@kmgeon/taskflow 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +374 -0
  2. package/bin/task-mcp.mjs +19 -0
  3. package/bin/task.mjs +19 -0
  4. package/docs/clean-code.md +29 -0
  5. package/docs/git.md +36 -0
  6. package/docs/guideline.md +25 -0
  7. package/docs/security.md +32 -0
  8. package/docs/step-by-step.md +29 -0
  9. package/docs/superpowers/specs/2026-03-21-cli-advisor-design.md +383 -0
  10. package/docs/superpowers/specs/2026-03-21-init-redesign-design.md +429 -0
  11. package/docs/superpowers/specs/2026-03-21-skill-architecture-design.md +362 -0
  12. package/docs/superpowers/specs/2026-03-23-t-create-task-run-design.md +40 -0
  13. package/docs/superpowers/specs/2026-03-23-task-run-design.md +44 -0
  14. package/docs/tdd.md +41 -0
  15. package/package.json +114 -0
  16. package/src/app/(protected)/dashboard/page.tsx +7 -0
  17. package/src/app/(protected)/layout.tsx +10 -0
  18. package/src/app/api/[[...hono]]/route.ts +13 -0
  19. package/src/app/example/page.tsx +11 -0
  20. package/src/app/favicon.ico +0 -0
  21. package/src/app/globals.css +168 -0
  22. package/src/app/layout.tsx +35 -0
  23. package/src/app/page.tsx +5 -0
  24. package/src/app/providers.tsx +57 -0
  25. package/src/backend/config/index.ts +36 -0
  26. package/src/backend/hono/app.ts +32 -0
  27. package/src/backend/hono/context.ts +38 -0
  28. package/src/backend/http/response.ts +64 -0
  29. package/src/backend/middleware/context.ts +23 -0
  30. package/src/backend/middleware/error.ts +31 -0
  31. package/src/backend/middleware/supabase.ts +23 -0
  32. package/src/backend/supabase/client.ts +17 -0
  33. package/src/cli/commands/__tests__/task-commands.test.ts +170 -0
  34. package/src/cli/commands/advisor.ts +45 -0
  35. package/src/cli/commands/ask.ts +50 -0
  36. package/src/cli/commands/board.ts +72 -0
  37. package/src/cli/commands/init.ts +184 -0
  38. package/src/cli/commands/list.ts +138 -0
  39. package/src/cli/commands/run.ts +143 -0
  40. package/src/cli/commands/set-status.ts +50 -0
  41. package/src/cli/commands/show.ts +28 -0
  42. package/src/cli/commands/tree.ts +72 -0
  43. package/src/cli/index.ts +38 -0
  44. package/src/cli/lib/__tests__/formatter.test.ts +123 -0
  45. package/src/cli/lib/error-boundary.test.ts +135 -0
  46. package/src/cli/lib/error-boundary.ts +70 -0
  47. package/src/cli/lib/formatter.ts +764 -0
  48. package/src/cli/lib/trd.ts +33 -0
  49. package/src/cli/lib/validate.test.ts +89 -0
  50. package/src/cli/lib/validate.ts +43 -0
  51. package/src/cli/prompts/task-run.md +25 -0
  52. package/src/components/layout/AppLayout.tsx +15 -0
  53. package/src/components/layout/Sidebar.tsx +124 -0
  54. package/src/components/ui/accordion.tsx +58 -0
  55. package/src/components/ui/avatar.tsx +50 -0
  56. package/src/components/ui/badge.tsx +36 -0
  57. package/src/components/ui/button.tsx +56 -0
  58. package/src/components/ui/card.tsx +79 -0
  59. package/src/components/ui/checkbox.tsx +30 -0
  60. package/src/components/ui/dialog.tsx +122 -0
  61. package/src/components/ui/dropdown-menu.tsx +200 -0
  62. package/src/components/ui/file-upload.tsx +50 -0
  63. package/src/components/ui/form.tsx +179 -0
  64. package/src/components/ui/input.tsx +25 -0
  65. package/src/components/ui/label.tsx +26 -0
  66. package/src/components/ui/scroll-area.tsx +48 -0
  67. package/src/components/ui/select.tsx +160 -0
  68. package/src/components/ui/separator.tsx +31 -0
  69. package/src/components/ui/sheet.tsx +140 -0
  70. package/src/components/ui/textarea.tsx +22 -0
  71. package/src/components/ui/toast.tsx +129 -0
  72. package/src/components/ui/toaster.tsx +35 -0
  73. package/src/core/ai/claude-client.ts +79 -0
  74. package/src/core/claude-runner/flag-builder.ts +57 -0
  75. package/src/core/claude-runner/index.ts +2 -0
  76. package/src/core/claude-runner/spawner.ts +86 -0
  77. package/src/core/prd/__tests__/auto-analyzer.test.ts +35 -0
  78. package/src/core/prd/__tests__/generator.test.ts +26 -0
  79. package/src/core/prd/__tests__/scanner.test.ts +35 -0
  80. package/src/core/prd/auto-analyzer.ts +9 -0
  81. package/src/core/prd/generator.ts +8 -0
  82. package/src/core/prd/scanner.ts +117 -0
  83. package/src/core/project/__tests__/claude-setup.test.ts +133 -0
  84. package/src/core/project/__tests__/config.test.ts +30 -0
  85. package/src/core/project/__tests__/init.test.ts +37 -0
  86. package/src/core/project/__tests__/skill-setup.test.ts +62 -0
  87. package/src/core/project/claude-setup.ts +224 -0
  88. package/src/core/project/config.ts +34 -0
  89. package/src/core/project/docs-setup.ts +26 -0
  90. package/src/core/project/docs-templates.ts +205 -0
  91. package/src/core/project/init.ts +40 -0
  92. package/src/core/project/skill-setup.ts +32 -0
  93. package/src/core/project/skill-templates.ts +277 -0
  94. package/src/core/task/index.ts +16 -0
  95. package/src/core/types.ts +58 -0
  96. package/src/features/example/backend/error.ts +9 -0
  97. package/src/features/example/backend/route.ts +52 -0
  98. package/src/features/example/backend/schema.ts +25 -0
  99. package/src/features/example/backend/service.ts +73 -0
  100. package/src/features/example/components/example-status.test.tsx +97 -0
  101. package/src/features/example/components/example-status.tsx +160 -0
  102. package/src/features/example/hooks/useExampleQuery.ts +23 -0
  103. package/src/features/example/lib/dto.test.ts +57 -0
  104. package/src/features/example/lib/dto.ts +5 -0
  105. package/src/features/kanban/backend/__tests__/sse-broadcaster.test.ts +137 -0
  106. package/src/features/kanban/backend/__tests__/sse-event-format.test.ts +55 -0
  107. package/src/features/kanban/backend/route.ts +55 -0
  108. package/src/features/kanban/backend/sse-broadcaster.ts +142 -0
  109. package/src/features/kanban/backend/sse-route.ts +43 -0
  110. package/src/features/kanban/components/KanbanBoard.tsx +105 -0
  111. package/src/features/kanban/components/KanbanColumn.tsx +51 -0
  112. package/src/features/kanban/components/KanbanError.tsx +29 -0
  113. package/src/features/kanban/components/KanbanSkeleton.tsx +46 -0
  114. package/src/features/kanban/components/ProgressCard.tsx +42 -0
  115. package/src/features/kanban/components/TaskCard.tsx +76 -0
  116. package/src/features/kanban/components/__tests__/kanban-components.test.tsx +86 -0
  117. package/src/features/kanban/hooks/useTaskSse.ts +66 -0
  118. package/src/features/kanban/hooks/useTasksQuery.ts +52 -0
  119. package/src/features/kanban/lib/__tests__/kanban-utils.test.ts +97 -0
  120. package/src/features/kanban/lib/kanban-utils.ts +37 -0
  121. package/src/features/taskflow/constants.ts +54 -0
  122. package/src/features/taskflow/index.ts +27 -0
  123. package/src/features/taskflow/lib/__tests__/filter.test.ts +89 -0
  124. package/src/features/taskflow/lib/__tests__/graph.test.ts +247 -0
  125. package/src/features/taskflow/lib/__tests__/repository.test.ts +233 -0
  126. package/src/features/taskflow/lib/__tests__/serializer.test.ts +98 -0
  127. package/src/features/taskflow/lib/advisor/__tests__/advisor-integration.test.ts +98 -0
  128. package/src/features/taskflow/lib/advisor/ai-advisor.test.ts +40 -0
  129. package/src/features/taskflow/lib/advisor/ai-advisor.ts +20 -0
  130. package/src/features/taskflow/lib/advisor/context-builder.test.ts +73 -0
  131. package/src/features/taskflow/lib/advisor/context-builder.ts +151 -0
  132. package/src/features/taskflow/lib/advisor/db.test.ts +106 -0
  133. package/src/features/taskflow/lib/advisor/db.ts +185 -0
  134. package/src/features/taskflow/lib/advisor/local-summary.test.ts +53 -0
  135. package/src/features/taskflow/lib/advisor/local-summary.ts +72 -0
  136. package/src/features/taskflow/lib/advisor/prompts.ts +86 -0
  137. package/src/features/taskflow/lib/filter.ts +54 -0
  138. package/src/features/taskflow/lib/fs-utils.ts +50 -0
  139. package/src/features/taskflow/lib/graph.ts +148 -0
  140. package/src/features/taskflow/lib/index-builder.ts +42 -0
  141. package/src/features/taskflow/lib/repository.ts +168 -0
  142. package/src/features/taskflow/lib/serializer.ts +62 -0
  143. package/src/features/taskflow/lib/watcher.ts +40 -0
  144. package/src/features/taskflow/types.ts +71 -0
  145. package/src/hooks/use-toast.ts +194 -0
  146. package/src/lib/remote/api-client.ts +40 -0
  147. package/src/lib/supabase/client.ts +8 -0
  148. package/src/lib/supabase/server.ts +46 -0
  149. package/src/lib/supabase/types.ts +3 -0
  150. package/src/lib/utils.ts +6 -0
  151. package/src/mcp/index.ts +7 -0
  152. package/src/mcp/server.ts +21 -0
  153. package/src/mcp/tools/brainstorm.ts +48 -0
  154. package/src/mcp/tools/prd.ts +71 -0
  155. package/src/mcp/tools/project.ts +39 -0
  156. package/src/mcp/tools/task-status.ts +40 -0
  157. package/src/mcp/tools/task.ts +82 -0
  158. package/src/mcp/util.ts +6 -0
@@ -0,0 +1,224 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { DOCS_NAMES } from "./docs-setup.js";
4
+
5
+ const TASKFLOW_DIR = ".taskflow";
6
+ const CLAUDE_MD = "CLAUDE.md";
7
+ const MCP_JSON = ".mcp.json";
8
+ const IMPORT_LINE = "@./.taskflow/CLAUDE.md";
9
+ const DOCS_SECTION_MARKER = "<!-- docs-reference -->";
10
+
11
+ export interface ClaudeMdContext {
12
+ projectName: string;
13
+ summary?: string;
14
+ stack?: string[];
15
+ }
16
+
17
+ export async function generateClaudeMd(
18
+ projectRoot: string,
19
+ ctx: ClaudeMdContext,
20
+ ): Promise<void> {
21
+ const date = new Date().toISOString().split("T")[0];
22
+ const stackLine = ctx.stack?.length ? ctx.stack.join(", ") : "N/A";
23
+
24
+ const content = `# TaskFlow — ${ctx.projectName} 개발 가이드
25
+
26
+ ## 프로젝트 정보
27
+ - 이름: ${ctx.projectName}
28
+ - 설명: ${ctx.summary ?? "N/A"}
29
+ - 기술 스택: ${stackLine}
30
+ - 생성일: ${date}
31
+
32
+ ## TaskFlow MCP 도구
33
+
34
+ 이 프로젝트는 TaskFlow로 태스크를 관리합니다.
35
+ 아래 MCP 도구를 사용하여 태스크를 조회하고 관리하세요.
36
+
37
+ ### 태스크 관리
38
+ - \`list_tasks\` — 태스크 목록 조회 (필터: status, priority)
39
+ - \`read_task\` — 태스크 상세 조회
40
+ - \`create_task\` — 새 태스크 생성
41
+ - \`update_task\` — 태스크 수정
42
+ - \`delete_task\` — 태스크 삭제
43
+ - \`set_task_status\` — 상태 변경
44
+ - \`get_next_task\` — 의존성/우선순위 기반 다음 태스크 추천
45
+ - \`expand_subtasks\` — 서브태스크 파일 생성
46
+
47
+ ### PRD & 코드베이스
48
+ - \`scan_codebase\` — 코드베이스 파일 목록/시그니처 스캔
49
+ - \`save_prd\` — PRD 마크다운 저장
50
+ - \`read_prd\` — PRD 읽기
51
+
52
+ ### 프로젝트
53
+ - \`initialize_project\` — 프로젝트 초기화
54
+ - \`generate_claude_md\` — CLAUDE.md 재생성
55
+
56
+ ## Claude Code 스킬
57
+
58
+ 다음 스킬 커맨드를 사용하여 워크플로우를 실행할 수 있습니다:
59
+ - \`/prd\` — PRD 대화형 생성
60
+ - \`/trd\` — PRD 기반 TRD 생성
61
+ - \`/parse-prd\` — PRD → 태스크 분해
62
+ - \`/brainstorm\` — 태스크 서브태스크 분해
63
+ - \`/refine\` — 요구사항 변경 영향 분석
64
+ - \`/next\` — 다음 작업할 태스크 추천
65
+ - \`/task-status\` — 진행 상황 요약
66
+
67
+ ## 워크플로우
68
+
69
+ ### 새 기능 구현 시
70
+ 1. \`/next\`로 다음 태스크 확인
71
+ 2. \`set_task_status\` → in-progress
72
+ 3. 구현 완료 후 \`set_task_status\` → done
73
+
74
+ ### PRD → 태스크 생성
75
+ 1. \`/prd\`로 PRD 대화형 생성
76
+ 2. \`/trd\`로 구현 계획 작성
77
+ 3. \`/parse-prd\`로 태스크 분해
78
+
79
+ ### 요구사항 변경 시
80
+ 1. \`/refine\`으로 영향 받는 태스크 분석
81
+ 2. 영향 받는 태스크 확인 및 수정
82
+
83
+ ## 파일 구조
84
+ - \`.taskflow/config.json\` — 프로젝트 설정
85
+ - \`.taskflow/prd.md\` — PRD 문서
86
+ - \`.taskflow/trd.md\` — TRD 문서
87
+ - \`.taskflow/tasks/task-{NNN}.md\` — 개별 태스크 파일
88
+ `;
89
+
90
+ const filePath = path.join(projectRoot, TASKFLOW_DIR, CLAUDE_MD);
91
+ await fs.writeFile(filePath, content, "utf-8");
92
+ }
93
+
94
+ export async function generateMcpJson(projectRoot: string): Promise<void> {
95
+ const filePath = path.join(projectRoot, MCP_JSON);
96
+
97
+ let existing: Record<string, unknown> = {};
98
+ try {
99
+ const raw = await fs.readFile(filePath, "utf-8");
100
+ existing = JSON.parse(raw);
101
+ } catch {
102
+ // file does not exist or invalid JSON
103
+ }
104
+
105
+ const mcpServers = (existing.mcpServers as Record<string, unknown>) ?? {};
106
+ mcpServers.taskflow = {
107
+ type: "stdio",
108
+ command: "node",
109
+ args: ["./bin/task-mcp.mjs"],
110
+ env: {},
111
+ };
112
+
113
+ const merged = { ...existing, mcpServers };
114
+ await fs.writeFile(filePath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
115
+ }
116
+
117
+ export async function appendClaudeImport(projectRoot: string): Promise<void> {
118
+ const filePath = path.join(projectRoot, CLAUDE_MD);
119
+
120
+ let existing = "";
121
+ try {
122
+ existing = await fs.readFile(filePath, "utf-8");
123
+ } catch {
124
+ // file does not exist
125
+ }
126
+
127
+ if (existing.includes(IMPORT_LINE)) {
128
+ return;
129
+ }
130
+
131
+ const importBlock = `\n## TaskFlow\n${IMPORT_LINE}\n`;
132
+
133
+ if (existing.trim()) {
134
+ const content = existing.endsWith("\n") ? existing + importBlock : existing + "\n" + importBlock;
135
+ await fs.writeFile(filePath, content, "utf-8");
136
+ } else {
137
+ const content = `# Claude Code Instructions\n${importBlock}`;
138
+ await fs.writeFile(filePath, content, "utf-8");
139
+ }
140
+ }
141
+
142
+ const DEFAULT_PLUGINS: Record<string, boolean> = {
143
+ "superpowers@claude-plugins-official": true,
144
+ "ralph-loop@claude-plugins-official": true,
145
+ };
146
+
147
+ /**
148
+ * .claude/settings.local.json에 enabledPlugins를 추가한다.
149
+ * 이미 설정된 플러그인은 덮어쓰지 않는다.
150
+ */
151
+ export async function setupPlugins(projectRoot: string): Promise<void> {
152
+ const settingsPath = path.join(projectRoot, ".claude", "settings.local.json");
153
+
154
+ let existing: Record<string, unknown> = {};
155
+ try {
156
+ const raw = await fs.readFile(settingsPath, "utf-8");
157
+ existing = JSON.parse(raw);
158
+ } catch {
159
+ // file does not exist or invalid JSON
160
+ }
161
+
162
+ const enabledPlugins = (existing.enabledPlugins as Record<string, boolean>) ?? {};
163
+
164
+ for (const [plugin, enabled] of Object.entries(DEFAULT_PLUGINS)) {
165
+ if (!(plugin in enabledPlugins)) {
166
+ enabledPlugins[plugin] = enabled;
167
+ }
168
+ }
169
+
170
+ const merged = { ...existing, enabledPlugins };
171
+
172
+ await fs.mkdir(path.dirname(settingsPath), { recursive: true });
173
+ await fs.writeFile(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
174
+ }
175
+
176
+ export async function appendDocsReference(projectRoot: string): Promise<void> {
177
+ const filePath = path.join(projectRoot, CLAUDE_MD);
178
+
179
+ let existing = "";
180
+ try {
181
+ existing = await fs.readFile(filePath, "utf-8");
182
+ } catch {
183
+ // file does not exist
184
+ }
185
+
186
+ if (existing.includes(DOCS_SECTION_MARKER)) {
187
+ return;
188
+ }
189
+
190
+ const docsList = DOCS_NAMES.map((name) => `- ${name}.md: ./docs/${name}.md`).join("\n");
191
+
192
+ const docsBlock = `
193
+ ${DOCS_SECTION_MARKER}
194
+ For every request:
195
+
196
+ 1. Always refer to the documentation files stored under \`/docs\`:
197
+ <docs>
198
+ ${docsList}
199
+ </docs>
200
+
201
+ 2. Use the relevant file(s) depending on the context of the request.
202
+ 3. Never ignore these files. Consider them for:
203
+ - Providing accurate information
204
+ - Ensuring consistency
205
+ - Following documented guidelines
206
+ - Making decisions or generating content
207
+ `;
208
+
209
+ if (existing.trim()) {
210
+ // Insert docs reference at the top, after the first heading line
211
+ const lines = existing.split("\n");
212
+ const headingIndex = lines.findIndex((l) => l.startsWith("# "));
213
+ if (headingIndex >= 0) {
214
+ lines.splice(headingIndex + 1, 0, docsBlock);
215
+ await fs.writeFile(filePath, lines.join("\n"), "utf-8");
216
+ } else {
217
+ const content = docsBlock + "\n" + existing;
218
+ await fs.writeFile(filePath, content, "utf-8");
219
+ }
220
+ } else {
221
+ const content = `# Claude Code Instructions\n${docsBlock}`;
222
+ await fs.writeFile(filePath, content, "utf-8");
223
+ }
224
+ }
@@ -0,0 +1,34 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import type { TaskFlowConfig } from "../types.js";
4
+
5
+ const CONFIG_FILE = "config.json";
6
+ const TASKFLOW_DIR = ".taskflow";
7
+
8
+ export const DEFAULT_CONFIG: TaskFlowConfig = {
9
+ version: "1.0",
10
+ project: {
11
+ name: "",
12
+ },
13
+ tasks: {
14
+ statusFlow: ["pending", "in-progress", "blocked", "done"],
15
+ },
16
+ };
17
+
18
+ export async function readConfig(projectRoot: string): Promise<TaskFlowConfig> {
19
+ const configPath = path.join(projectRoot, TASKFLOW_DIR, CONFIG_FILE);
20
+ try {
21
+ const raw = await fs.readFile(configPath, "utf-8");
22
+ return JSON.parse(raw) as TaskFlowConfig;
23
+ } catch {
24
+ return { ...DEFAULT_CONFIG };
25
+ }
26
+ }
27
+
28
+ export async function writeConfig(
29
+ projectRoot: string,
30
+ config: TaskFlowConfig,
31
+ ): Promise<void> {
32
+ const configPath = path.join(projectRoot, TASKFLOW_DIR, CONFIG_FILE);
33
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
34
+ }
@@ -0,0 +1,26 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { DOCS_TEMPLATES } from "./docs-templates.js";
4
+
5
+ const DOCS_DIR = "docs";
6
+
7
+ export const DOCS_NAMES = Object.keys(DOCS_TEMPLATES);
8
+
9
+ export async function installDocs(projectRoot: string): Promise<void> {
10
+ const docsDir = path.join(projectRoot, DOCS_DIR);
11
+ await fs.mkdir(docsDir, { recursive: true });
12
+
13
+ for (const [name, content] of Object.entries(DOCS_TEMPLATES)) {
14
+ const filePath = path.join(docsDir, `${name}.md`);
15
+
16
+ // Don't overwrite existing docs (user may have customized them)
17
+ try {
18
+ await fs.access(filePath);
19
+ continue;
20
+ } catch {
21
+ // File doesn't exist — create it
22
+ }
23
+
24
+ await fs.writeFile(filePath, content, "utf-8");
25
+ }
26
+ }
@@ -0,0 +1,205 @@
1
+ export const DOCS_TEMPLATES: Record<string, string> = {
2
+ guideline: `# Project Guideline
3
+
4
+ ## Naming Conventions
5
+ - **Files**: kebab-case (\`parse-prd-flow.ts\`)
6
+ - **Components**: PascalCase (\`TaskCard.tsx\`)
7
+ - **Functions/Variables**: camelCase
8
+ - **Types/Interfaces**: PascalCase
9
+ - **Constants**: UPPER_SNAKE_CASE
10
+ - **Test files**: \`*.test.ts\` or \`__tests__/*.test.ts\` (co-located)
11
+
12
+ ## Import Conventions
13
+ - Use path alias (\`@/*\` etc.) when available — avoid deep relative paths
14
+ - Group imports: external → internal → relative
15
+ - No circular imports between modules/features
16
+
17
+ ## Module Structure
18
+ - Keep modules self-contained: co-locate related code (logic, types, tests)
19
+ - Separate concerns by layer (API, UI, business logic)
20
+ - Shared code belongs in \`lib/\` or \`utils/\` — not inside a feature module
21
+
22
+ ## General Principles
23
+ - Prefer composition over inheritance
24
+ - Keep public API surface small — export only what's needed
25
+ - Avoid god files (>300 lines) — split by responsibility
26
+ - Configuration and constants belong in dedicated files, not scattered inline
27
+ `,
28
+
29
+ "clean-code": `# Clean Code Guidelines
30
+
31
+ ## Principles
32
+ - **Single Responsibility**: One function/module = one job
33
+ - **DRY**: Extract only when duplicated 3+ times. Premature abstraction is worse than duplication
34
+ - **KISS**: Simplest solution that works. No over-engineering
35
+ - **YAGNI**: Don't build what you don't need yet
36
+
37
+ ## Functions
38
+ - Max 20 lines per function (guideline, not hard rule)
39
+ - Max 3 parameters. Use an options object for more
40
+ - Name functions with verbs: \`createTask\`, \`parseMarkdown\`, \`validateInput\`
41
+ - Early return over nested conditionals
42
+
43
+ ## Error Handling
44
+ - Use custom error classes per domain
45
+ - Let errors bubble up — catch only at boundaries (API routes, CLI commands)
46
+ - Never swallow errors silently
47
+
48
+ ## Types
49
+ - Prefer \`type\` over \`interface\` for consistency (unless extending)
50
+ - Use schema-first approach (Zod, etc.); derive types from schemas
51
+ - Avoid \`any\` — use \`unknown\` and narrow
52
+
53
+ ## Code Smells to Avoid
54
+ - God files (>300 lines) — split by responsibility
55
+ - Boolean parameters — use named options or separate functions
56
+ - Magic strings/numbers — use constants or enums
57
+ - Commented-out code — delete it, git has history
58
+ `,
59
+
60
+ git: `# Git Conventions
61
+
62
+ ## Branch Naming
63
+ \`\`\`
64
+ feat/{description} # New feature
65
+ fix/{description} # Bug fix
66
+ refactor/{description} # Refactoring
67
+ docs/{description} # Documentation
68
+ chore/{description} # Maintenance
69
+ \`\`\`
70
+
71
+ ## Commit Messages
72
+ Follow Conventional Commits:
73
+ \`\`\`
74
+ type(scope): description
75
+
76
+ feat(kanban): add drag-and-drop task reordering
77
+ fix(cli): handle missing config file gracefully
78
+ refactor(core): extract task validation logic
79
+ test(prd): add parser edge case coverage
80
+ \`\`\`
81
+
82
+ ### Types
83
+ - \`feat\` — new feature
84
+ - \`fix\` — bug fix
85
+ - \`refactor\` — code restructuring (no behavior change)
86
+ - \`test\` — adding/updating tests
87
+ - \`docs\` — documentation only
88
+ - \`chore\` — build, deps, config changes
89
+ - \`style\` — formatting (no logic change)
90
+
91
+ ## Rules
92
+ - Commit small, atomic changes
93
+ - Never commit secrets, \`.env\` files, or credentials
94
+ - Keep commits buildable — don't break the build mid-branch
95
+ - Squash WIP commits before PR
96
+ `,
97
+
98
+ tdd: `# Test-Driven Development
99
+
100
+ ## Philosophy
101
+ - Write tests first for new features and bug fixes
102
+ - Tests document behavior — they are living specifications
103
+ - Fast feedback: unit tests should run in <1s per file
104
+
105
+ ## Test Pyramid
106
+ 1. **Unit tests** — core logic, utils, schemas (most coverage)
107
+ 2. **Integration tests** — API routes, database queries (moderate)
108
+ 3. **E2E tests** — critical user flows only (fewest)
109
+
110
+ ## Conventions
111
+ - Co-locate tests: \`*.test.ts\` next to source or in \`__tests__/\`
112
+ - Use \`describe\` blocks to group by function/scenario
113
+ - Test names: \`it('should {expected behavior} when {condition}')\`
114
+ - One assertion concept per test
115
+
116
+ ## Patterns
117
+ \`\`\`typescript
118
+ // Arrange
119
+ const input = createMockTask({ status: 'todo' });
120
+
121
+ // Act
122
+ const result = updateTaskStatus(input, 'in-progress');
123
+
124
+ // Assert
125
+ expect(result.status).toBe('in-progress');
126
+ \`\`\`
127
+
128
+ ## What to Test
129
+ - Business logic and transformations
130
+ - Edge cases and error paths
131
+ - Schema validation
132
+ - API response shapes
133
+
134
+ ## What NOT to Test
135
+ - Framework internals (routing, rendering)
136
+ - Third-party library behavior
137
+ - Implementation details (private functions, internal state)
138
+ - Trivial getters/setters
139
+ `,
140
+
141
+ "step-by-step": `# Step-by-Step Development Workflow
142
+
143
+ ## Before Writing Code
144
+ 1. **Understand the requirement** — Read the task/PRD thoroughly
145
+ 2. **Check existing code** — Search for related implementations before creating new ones
146
+ 3. **Plan the approach** — Identify which files to modify and what tests to write
147
+
148
+ ## Implementation Flow
149
+ 1. **Write the test** — Define expected behavior first
150
+ 2. **Write minimal code** — Just enough to pass the test
151
+ 3. **Refactor** — Clean up while tests stay green
152
+ 4. **Verify** — Run full test suite
153
+ 5. **Type check** — Run type checker
154
+ 6. **Lint** — Run linter
155
+
156
+ ## PR Checklist
157
+ - [ ] Tests pass
158
+ - [ ] Type check passes
159
+ - [ ] No lint errors
160
+ - [ ] New code has test coverage
161
+ - [ ] Commit messages follow conventions
162
+ - [ ] No secrets or .env files committed
163
+
164
+ ## Debugging
165
+ 1. Read the error message carefully
166
+ 2. Reproduce the issue with a test
167
+ 3. Identify root cause (don't just patch symptoms)
168
+ 4. Fix and verify the test passes
169
+ 5. Check for similar issues elsewhere
170
+ `,
171
+
172
+ security: `# Security Guidelines
173
+
174
+ ## Authentication & Authorization
175
+ - All API routes must verify authentication
176
+ - Validate user session on every request — never trust client-side auth state
177
+ - Use row-level or resource-level access control for data isolation
178
+
179
+ ## Input Validation
180
+ - Validate ALL external input with schemas at the boundary
181
+ - Never trust query params, request bodies, or URL params directly
182
+ - Sanitize user-generated content before rendering (XSS prevention)
183
+
184
+ ## Secrets Management
185
+ - Store secrets in environment variables only
186
+ - Never commit \`.env\`, API keys, or credentials
187
+ - Use \`.env.local\` for local development (ensure it's in \`.gitignore\`)
188
+
189
+ ## API Security
190
+ - Use parameterized queries — never concatenate user input into queries
191
+ - Rate limit public endpoints
192
+ - Return generic error messages to clients — log details server-side
193
+
194
+ ## Dependencies
195
+ - Keep dependencies up to date
196
+ - Review new dependencies before adding (check maintenance, size, security)
197
+ - Run security audits periodically
198
+
199
+ ## What to Never Do
200
+ - Expose internal error stacks to clients
201
+ - Store passwords in plain text
202
+ - Use \`eval()\` or dynamic code execution with user input
203
+ - Disable strict type checks for convenience
204
+ `,
205
+ };
@@ -0,0 +1,40 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { writeConfig, DEFAULT_CONFIG } from "./config.js";
4
+
5
+ const TASKFLOW_DIR = ".taskflow";
6
+
7
+ export interface InitResult {
8
+ created: boolean;
9
+ alreadyExists: boolean;
10
+ projectRoot: string;
11
+ }
12
+
13
+ export async function initProject(projectRoot: string, projectName?: string): Promise<InitResult> {
14
+ const taskflowDir = path.join(projectRoot, TASKFLOW_DIR);
15
+
16
+ let alreadyExists = false;
17
+ try {
18
+ await fs.access(taskflowDir);
19
+ alreadyExists = true;
20
+ } catch {
21
+ // directory does not exist
22
+ }
23
+
24
+ if (alreadyExists) {
25
+ return { created: false, alreadyExists: true, projectRoot };
26
+ }
27
+
28
+ await fs.mkdir(path.join(taskflowDir, "tasks"), { recursive: true });
29
+ await fs.mkdir(path.join(taskflowDir, "index"), { recursive: true });
30
+ await fs.mkdir(path.join(taskflowDir, "logs"), { recursive: true });
31
+ await fs.mkdir(path.join(taskflowDir, "cache"), { recursive: true });
32
+
33
+ const config = {
34
+ ...DEFAULT_CONFIG,
35
+ project: { ...DEFAULT_CONFIG.project, name: projectName ?? "" },
36
+ };
37
+ await writeConfig(projectRoot, config);
38
+
39
+ return { created: true, alreadyExists: false, projectRoot };
40
+ }
@@ -0,0 +1,32 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { SKILL_TEMPLATES } from "./skill-templates.js";
4
+
5
+ export const SKILL_NAMES = Object.keys(SKILL_TEMPLATES);
6
+
7
+ export async function installSkills(projectRoot: string): Promise<void> {
8
+ const srcDir = path.join(projectRoot, ".taskflow", ".claude", "commands");
9
+ const destDir = path.join(projectRoot, ".claude", "commands");
10
+
11
+ await fs.mkdir(srcDir, { recursive: true });
12
+ await fs.mkdir(destDir, { recursive: true });
13
+
14
+ for (const [name, content] of Object.entries(SKILL_TEMPLATES)) {
15
+ const srcFile = path.join(srcDir, `${name}.md`);
16
+ const destFile = path.join(destDir, `${name}.md`);
17
+
18
+ // 원본 스킬 파일 생성 (항상 최신 템플릿으로 덮어쓰기)
19
+ await fs.writeFile(srcFile, content, "utf-8");
20
+
21
+ // 기존 링크/파일이 있으면 심볼릭 링크 스킵
22
+ try {
23
+ await fs.lstat(destFile);
24
+ continue;
25
+ } catch {
26
+ // 없으면 생성
27
+ }
28
+
29
+ const relativePath = path.relative(destDir, srcFile);
30
+ await fs.symlink(relativePath, destFile);
31
+ }
32
+ }