suggest-headings
Generate structured headings for Things 3 projects based on project goals and work type to organize tasks effectively.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_uuid | No | Optional project UUID to inspect existing headings first | |
| project_title | No | Project title if project_uuid is not available | |
| goal | No | Short description of what the project is trying to achieve | |
| work_type | No | Optional category like design, software, content, workshop, personal | |
| preferred_count | No | Preferred number of headings to suggest |
Implementation Reference
- src/index.ts:1016-1113 (handler)The "suggest-headings" tool implementation, which generates suggested headings for a project based on its title, goal, and work type, while taking existing headings into account if a project UUID is provided.
"suggest-headings", { project_uuid: z .string() .optional() .describe("Optional project UUID to inspect existing headings first"), project_title: z .string() .optional() .describe("Project title if project_uuid is not available"), goal: z .string() .optional() .describe("Short description of what the project is trying to achieve"), work_type: z .string() .optional() .describe("Optional category like design, software, content, workshop, personal"), preferred_count: z .number() .int() .min(3) .max(8) .optional() .describe("Preferred number of headings to suggest"), }, async ({ project_uuid, project_title, goal, work_type, preferred_count }) => { const data = await withDatabase((db) => { const tasks = getAllTasks(db); const project = project_uuid ? tasks.find((task) => task.type === "project" && task.id === project_uuid) : undefined; const existingHeadings = project_uuid ? tasks .filter( (task) => task.type === "heading" && task.projectId === project_uuid && !task.trashed ) .map((task) => task.title) : []; return { projectTitle: project?.title ?? project_title ?? "", existingHeadings, }; }).catch(() => ({ projectTitle: project_title ?? "", existingHeadings: [] as string[], })); const baseSuggestions = inferHeadingTemplate({ projectTitle: data.projectTitle, goal, workType: work_type, }); const suggestedHeadings = uniqueHeadingNames( preferred_count ? baseSuggestions.slice(0, preferred_count) : baseSuggestions ); const existingNormalized = new Set( data.existingHeadings.map((heading) => normalizeHeadingName(heading)) ); const missingHeadings = suggestedHeadings.filter( (heading) => !existingNormalized.has(normalizeHeadingName(heading)) ); const isExistingProject = Boolean(project_uuid); return buildTextResponse("Suggested heading structure for the project", { projectUuid: project_uuid ?? null, projectTitle: data.projectTitle || project_title || null, goal: goal ?? null, workType: work_type ?? null, existingHeadings: data.existingHeadings, suggestedHeadings, missingHeadings, creationMode: isExistingProject ? "existing-project" : "new-project", requiresManualCreation: isExistingProject && missingHeadings.length > 0, preferredCreationTool: isExistingProject ? null : "create-project-with-headings", userInstruction: isExistingProject ? (missingHeadings.length === 0 ? "The existing project already has the suggested heading structure. You can continue creating or moving tasks into those headings." : buildHeadingCreationPrompt({ projectTitle: data.projectTitle || project_title, suggestedHeadings, missingHeadings, })) : buildNewProjectStructurePrompt({ projectTitle: data.projectTitle || project_title, suggestedHeadings, }), followUpInstruction: isExistingProject ? "If headings are missing in an existing project, ask the user to create them manually, then call get-headings or validate-headings before creating or moving tasks into them." : "For a new project, prefer create-project-with-headings or the JSON tool to create the project, headings, and optional to-dos in one operation.", }); } );