@vpxa/aikit 0.1.73 → 0.1.75

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 (142) hide show
  1. package/package.json +9 -1
  2. package/packages/cli/dist/index.js +2 -2
  3. package/packages/cli/dist/{init-D_OGLUN1.js → init-CuRXmyD9.js} +4 -4
  4. package/packages/cli/dist/scaffold-WMQ2uQ48.js +2 -0
  5. package/packages/cli/dist/{templates-DJ7EC5vw.js → templates-ArdAVWoY.js} +13 -3
  6. package/packages/cli/dist/user-vbJwa7x2.js +5 -0
  7. package/packages/dashboard/dist/assets/index-C6D-PCp0.js.map +1 -1
  8. package/packages/flows/dist/index.d.ts +29 -0
  9. package/packages/flows/dist/index.js +1 -1
  10. package/packages/server/dist/index.js +1 -1
  11. package/packages/server/dist/{server-B9Mx1aK-.js → server-CVhVH5cT.js} +127 -127
  12. package/packages/tools/dist/index.d.ts +19 -1
  13. package/packages/tools/dist/index.js +39 -39
  14. package/scaffold/dist/adapters/claude-code.mjs +4 -0
  15. package/scaffold/dist/adapters/copilot.mjs +75 -0
  16. package/scaffold/dist/adapters/flows.mjs +1 -0
  17. package/scaffold/dist/adapters/skills.mjs +1 -0
  18. package/scaffold/dist/compiled/flows-data.mjs +1429 -0
  19. package/scaffold/dist/compiled/skills-data.mjs +9951 -0
  20. package/scaffold/dist/definitions/agents.mjs +9 -0
  21. package/scaffold/{definitions → dist/definitions}/bodies.mjs +6 -229
  22. package/scaffold/dist/definitions/exclusions.mjs +1 -0
  23. package/scaffold/dist/definitions/hooks.mjs +1 -0
  24. package/scaffold/dist/definitions/models.mjs +1 -0
  25. package/scaffold/dist/definitions/plugins.mjs +1 -0
  26. package/scaffold/{definitions → dist/definitions}/prompts.mjs +9 -149
  27. package/scaffold/{definitions → dist/definitions}/protocols.mjs +9 -37
  28. package/scaffold/dist/definitions/tools.mjs +1 -0
  29. package/packages/cli/dist/scaffold-CJwkHf-q.js +0 -2
  30. package/packages/cli/dist/user-BEmVW8Tp.js +0 -5
  31. package/scaffold/adapters/claude-code.mjs +0 -73
  32. package/scaffold/adapters/copilot.mjs +0 -292
  33. package/scaffold/definitions/agents.mjs +0 -266
  34. package/scaffold/definitions/hooks.mjs +0 -43
  35. package/scaffold/definitions/models.mjs +0 -84
  36. package/scaffold/definitions/plugins.mjs +0 -147
  37. package/scaffold/definitions/tools.mjs +0 -250
  38. package/scaffold/flows/_epilogue/steps/docs-sync/README.md +0 -120
  39. package/scaffold/flows/aikit-advanced/README.md +0 -70
  40. package/scaffold/flows/aikit-advanced/flow.json +0 -69
  41. package/scaffold/flows/aikit-advanced/steps/design/README.md +0 -178
  42. package/scaffold/flows/aikit-advanced/steps/execute/README.md +0 -145
  43. package/scaffold/flows/aikit-advanced/steps/plan/README.md +0 -122
  44. package/scaffold/flows/aikit-advanced/steps/spec/README.md +0 -121
  45. package/scaffold/flows/aikit-advanced/steps/task/README.md +0 -119
  46. package/scaffold/flows/aikit-advanced/steps/verify/README.md +0 -145
  47. package/scaffold/flows/aikit-basic/README.md +0 -51
  48. package/scaffold/flows/aikit-basic/flow.json +0 -45
  49. package/scaffold/flows/aikit-basic/steps/assess/README.md +0 -109
  50. package/scaffold/flows/aikit-basic/steps/design/README.md +0 -116
  51. package/scaffold/flows/aikit-basic/steps/implement/README.md +0 -131
  52. package/scaffold/flows/aikit-basic/steps/verify/README.md +0 -123
  53. package/scaffold/general/agents/Architect-Reviewer-Alpha.agent.md +0 -132
  54. package/scaffold/general/agents/Architect-Reviewer-Beta.agent.md +0 -132
  55. package/scaffold/general/agents/Code-Reviewer-Alpha.agent.md +0 -112
  56. package/scaffold/general/agents/Code-Reviewer-Beta.agent.md +0 -112
  57. package/scaffold/general/agents/Debugger.agent.md +0 -412
  58. package/scaffold/general/agents/Documenter.agent.md +0 -468
  59. package/scaffold/general/agents/Explorer.agent.md +0 -76
  60. package/scaffold/general/agents/Frontend.agent.md +0 -440
  61. package/scaffold/general/agents/Implementer.agent.md +0 -425
  62. package/scaffold/general/agents/Orchestrator.agent.md +0 -452
  63. package/scaffold/general/agents/Planner.agent.md +0 -481
  64. package/scaffold/general/agents/README.md +0 -57
  65. package/scaffold/general/agents/Refactor.agent.md +0 -435
  66. package/scaffold/general/agents/Researcher-Alpha.agent.md +0 -151
  67. package/scaffold/general/agents/Researcher-Beta.agent.md +0 -152
  68. package/scaffold/general/agents/Researcher-Delta.agent.md +0 -153
  69. package/scaffold/general/agents/Researcher-Gamma.agent.md +0 -152
  70. package/scaffold/general/agents/Security.agent.md +0 -433
  71. package/scaffold/general/agents/_shared/architect-reviewer-base.md +0 -104
  72. package/scaffold/general/agents/_shared/code-agent-base.md +0 -366
  73. package/scaffold/general/agents/_shared/code-reviewer-base.md +0 -87
  74. package/scaffold/general/agents/_shared/decision-protocol.md +0 -27
  75. package/scaffold/general/agents/_shared/forge-protocol.md +0 -90
  76. package/scaffold/general/agents/_shared/researcher-base.md +0 -114
  77. package/scaffold/general/agents/templates/adr-template.md +0 -28
  78. package/scaffold/general/agents/templates/execution-state.md +0 -26
  79. package/scaffold/general/prompts/aikit-ask.prompt.md +0 -13
  80. package/scaffold/general/prompts/aikit-debug.prompt.md +0 -15
  81. package/scaffold/general/prompts/aikit-design.prompt.md +0 -15
  82. package/scaffold/general/prompts/aikit-flow-add.prompt.md +0 -84
  83. package/scaffold/general/prompts/aikit-flow-create.prompt.md +0 -80
  84. package/scaffold/general/prompts/aikit-flow-manage.prompt.md +0 -24
  85. package/scaffold/general/prompts/aikit-implement.prompt.md +0 -17
  86. package/scaffold/general/prompts/aikit-plan.prompt.md +0 -15
  87. package/scaffold/general/prompts/aikit-review.prompt.md +0 -24
  88. package/scaffold/general/skills/adr-skill/SKILL.md +0 -335
  89. package/scaffold/general/skills/adr-skill/assets/templates/adr-madr.md +0 -89
  90. package/scaffold/general/skills/adr-skill/assets/templates/adr-readme.md +0 -20
  91. package/scaffold/general/skills/adr-skill/assets/templates/adr-simple.md +0 -46
  92. package/scaffold/general/skills/adr-skill/references/adr-conventions.md +0 -95
  93. package/scaffold/general/skills/adr-skill/references/examples.md +0 -193
  94. package/scaffold/general/skills/adr-skill/references/review-checklist.md +0 -77
  95. package/scaffold/general/skills/adr-skill/references/template-variants.md +0 -52
  96. package/scaffold/general/skills/adr-skill/scripts/bootstrap_adr.js +0 -259
  97. package/scaffold/general/skills/adr-skill/scripts/new_adr.js +0 -391
  98. package/scaffold/general/skills/adr-skill/scripts/set_adr_status.js +0 -169
  99. package/scaffold/general/skills/aikit/SKILL.md +0 -754
  100. package/scaffold/general/skills/brainstorming/SKILL.md +0 -265
  101. package/scaffold/general/skills/brainstorming/spec-document-reviewer-prompt.md +0 -49
  102. package/scaffold/general/skills/c4-architecture/SKILL.md +0 -389
  103. package/scaffold/general/skills/c4-architecture/references/advanced-patterns.md +0 -552
  104. package/scaffold/general/skills/c4-architecture/references/c4-syntax.md +0 -510
  105. package/scaffold/general/skills/c4-architecture/references/common-mistakes.md +0 -437
  106. package/scaffold/general/skills/c4-architecture/references/html-design-system.md +0 -337
  107. package/scaffold/general/skills/c4-architecture/references/html-template.html +0 -627
  108. package/scaffold/general/skills/docs/SKILL.md +0 -553
  109. package/scaffold/general/skills/docs/references/diataxis-anti-patterns.md +0 -147
  110. package/scaffold/general/skills/docs/references/diataxis-compass.md +0 -123
  111. package/scaffold/general/skills/docs/references/diataxis-quadrants.md +0 -192
  112. package/scaffold/general/skills/docs/references/diataxis-quality.md +0 -76
  113. package/scaffold/general/skills/docs/references/diataxis-templates.md +0 -120
  114. package/scaffold/general/skills/docs/references/flow-artifacts-guide.md +0 -70
  115. package/scaffold/general/skills/docs/references/project-knowledge-gotchas.md +0 -32
  116. package/scaffold/general/skills/docs/references/project-knowledge-templates.md +0 -281
  117. package/scaffold/general/skills/docs/references/project-knowledge-workflow.md +0 -80
  118. package/scaffold/general/skills/frontend-design/SKILL.md +0 -237
  119. package/scaffold/general/skills/lesson-learned/SKILL.md +0 -113
  120. package/scaffold/general/skills/lesson-learned/references/anti-patterns.md +0 -55
  121. package/scaffold/general/skills/lesson-learned/references/se-principles.md +0 -109
  122. package/scaffold/general/skills/multi-agents-development/SKILL.md +0 -448
  123. package/scaffold/general/skills/multi-agents-development/architecture-review-prompt.md +0 -81
  124. package/scaffold/general/skills/multi-agents-development/code-quality-review-prompt.md +0 -91
  125. package/scaffold/general/skills/multi-agents-development/implementer-prompt.md +0 -93
  126. package/scaffold/general/skills/multi-agents-development/parallel-dispatch-example.md +0 -167
  127. package/scaffold/general/skills/multi-agents-development/spec-review-prompt.md +0 -81
  128. package/scaffold/general/skills/present/SKILL.md +0 -616
  129. package/scaffold/general/skills/react/SKILL.md +0 -309
  130. package/scaffold/general/skills/repo-access/SKILL.md +0 -178
  131. package/scaffold/general/skills/repo-access/references/error-patterns.md +0 -116
  132. package/scaffold/general/skills/repo-access/references/platform-matrix.md +0 -142
  133. package/scaffold/general/skills/requirements-clarity/SKILL.md +0 -333
  134. package/scaffold/general/skills/session-handoff/SKILL.md +0 -199
  135. package/scaffold/general/skills/session-handoff/references/handoff-template.md +0 -139
  136. package/scaffold/general/skills/session-handoff/references/resume-checklist.md +0 -80
  137. package/scaffold/general/skills/session-handoff/scripts/check_staleness.js +0 -269
  138. package/scaffold/general/skills/session-handoff/scripts/create_handoff.js +0 -299
  139. package/scaffold/general/skills/session-handoff/scripts/list_handoffs.js +0 -113
  140. package/scaffold/general/skills/session-handoff/scripts/validate_handoff.js +0 -241
  141. package/scaffold/general/skills/typescript/SKILL.md +0 -405
  142. package/scaffold/generate.mjs +0 -82
@@ -1,299 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Smart scaffold generator for handoff documents.
4
- *
5
- * Creates a new handoff document with auto-detected metadata:
6
- * - Current timestamp
7
- * - Project path
8
- * - Git branch (if available)
9
- * - Recent git log
10
- * - Modified/staged files
11
- * - Handoff chain linking
12
- *
13
- * Usage:
14
- * node create_handoff.js [task-slug]
15
- * node create_handoff.js "implementing-auth"
16
- * node create_handoff.js "auth-part-2" --continues-from 2024-01-15-auth.md
17
- * node create_handoff.js # auto-generates slug from timestamp
18
- */
19
-
20
- const fs = require('node:fs');
21
- const path = require('node:path');
22
- const { execSync } = require('node:child_process');
23
-
24
- function _die(msg) {
25
- process.stderr.write(`${msg}\n`);
26
- process.exit(1);
27
- }
28
-
29
- function runCmd(cmd, cwd) {
30
- try {
31
- return { ok: true, out: execSync(cmd, { cwd, timeout: 10_000, encoding: 'utf-8' }).trim() };
32
- } catch {
33
- return { ok: false, out: '' };
34
- }
35
- }
36
-
37
- function getGitInfo(projectPath) {
38
- const info = {
39
- isGitRepo: false,
40
- branch: null,
41
- recentCommits: [],
42
- modifiedFiles: [],
43
- stagedFiles: [],
44
- };
45
-
46
- if (!runCmd('git rev-parse --git-dir', projectPath).ok) return info;
47
- info.isGitRepo = true;
48
-
49
- const { ok: bOk, out: branch } = runCmd('git branch --show-current', projectPath);
50
- if (bOk && branch) info.branch = branch;
51
-
52
- const { ok: lOk, out: log } = runCmd('git log --oneline -5 --no-decorate', projectPath);
53
- if (lOk && log) info.recentCommits = log.split('\n');
54
-
55
- const { ok: mOk, out: modified } = runCmd('git diff --name-only', projectPath);
56
- if (mOk && modified) info.modifiedFiles = modified.split('\n');
57
-
58
- const { ok: sOk, out: staged } = runCmd('git diff --name-only --cached', projectPath);
59
- if (sOk && staged) info.stagedFiles = staged.split('\n');
60
-
61
- return info;
62
- }
63
-
64
- function findPreviousHandoffs(projectPath) {
65
- const dir = path.join(projectPath, '.handoffs');
66
- if (!fs.existsSync(dir)) return [];
67
-
68
- const handoffs = [];
69
- for (const name of fs.readdirSync(dir)) {
70
- if (!name.endsWith('.md')) continue;
71
- const fp = path.join(dir, name);
72
-
73
- let title = name;
74
- try {
75
- const content = fs.readFileSync(fp, 'utf-8');
76
- const m = content.match(/^#\s+(?:Handoff:\s*)?(.+)$/m);
77
- if (m) title = m[1].trim();
78
- } catch {
79
- /* ignore */
80
- }
81
-
82
- let date = null;
83
- const dm = name.match(/^(\d{4}-\d{2}-\d{2})-(\d{6})/);
84
- if (dm) {
85
- try {
86
- date = new Date(`${dm[1]}T${dm[2].slice(0, 2)}:${dm[2].slice(2, 4)}:${dm[2].slice(4, 6)}`);
87
- } catch {
88
- /* ignore */
89
- }
90
- }
91
-
92
- handoffs.push({ filename: name, path: fp, title, date });
93
- }
94
-
95
- handoffs.sort((a, b) => (b.date || 0) - (a.date || 0));
96
- return handoffs;
97
- }
98
-
99
- function getPreviousHandoffInfo(projectPath, continuesFrom) {
100
- const handoffs = findPreviousHandoffs(projectPath);
101
-
102
- if (continuesFrom) {
103
- const found = handoffs.find((h) => h.filename.includes(continuesFrom));
104
- return found
105
- ? { exists: true, filename: found.filename, title: found.title }
106
- : { exists: false, filename: continuesFrom, title: 'Not found' };
107
- }
108
-
109
- if (handoffs.length > 0) {
110
- return {
111
- exists: true,
112
- filename: handoffs[0].filename,
113
- title: handoffs[0].title,
114
- suggested: true,
115
- };
116
- }
117
-
118
- return { exists: false };
119
- }
120
-
121
- function sanitizeSlug(slug) {
122
- return slug
123
- .toLowerCase()
124
- .replace(/[\s_]/g, '-')
125
- .replace(/[^a-z0-9-]/g, '');
126
- }
127
-
128
- function generateHandoff(projectPath, slug, continuesFrom) {
129
- const now = new Date();
130
- const timestamp = now.toISOString().replace('T', ' ').slice(0, 19);
131
- const fileTs = `${now.toISOString().slice(0, 10)}-${String(now.getHours()).padStart(2, '0')}${String(now.getMinutes()).padStart(2, '0')}${String(now.getSeconds()).padStart(2, '0')}`;
132
-
133
- if (!slug) slug = 'handoff';
134
- slug = sanitizeSlug(slug);
135
- const filename = `${fileTs}-${slug}.md`;
136
-
137
- const handoffsDir = path.join(projectPath, '.handoffs');
138
- fs.mkdirSync(handoffsDir, { recursive: true });
139
- const filepath = path.join(handoffsDir, filename);
140
-
141
- const git = getGitInfo(projectPath);
142
- const prev = getPreviousHandoffInfo(projectPath, continuesFrom);
143
-
144
- const branchLine = git.branch || '[not a git repo or detached HEAD]';
145
-
146
- const commitsSection =
147
- git.recentCommits.length > 0
148
- ? git.recentCommits.map((c) => ` - ${c}`).join('\n')
149
- : ' - [no recent commits or not a git repo]';
150
-
151
- const allModified = [...new Set([...git.modifiedFiles, ...git.stagedFiles])];
152
- let modifiedSection;
153
- if (allModified.length > 0) {
154
- modifiedSection = allModified
155
- .slice(0, 10)
156
- .map((f) => `| ${f} | [describe changes] | [why changed] |`)
157
- .join('\n');
158
- if (allModified.length > 10)
159
- modifiedSection += `\n| ... and ${allModified.length - 10} more files | | |`;
160
- } else {
161
- modifiedSection = '| [no modified files detected] | | |';
162
- }
163
-
164
- let chainSection;
165
- if (prev.exists) {
166
- chainSection = `## Handoff Chain
167
-
168
- - **Continues from**: [${prev.filename}](./${prev.filename})
169
- - Previous title: ${prev.title || 'Unknown'}
170
- - **Supersedes**: [list any older handoffs this replaces, or "None"]
171
-
172
- > Review the previous handoff for full context before filling this one.`;
173
- } else {
174
- chainSection = `## Handoff Chain
175
-
176
- - **Continues from**: None (fresh start)
177
- - **Supersedes**: None
178
-
179
- > This is the first handoff for this task.`;
180
- }
181
-
182
- const content = `# Handoff: [TASK_TITLE - replace this]
183
-
184
- ## Session Metadata
185
- - Created: ${timestamp}
186
- - Project: ${projectPath}
187
- - Branch: ${branchLine}
188
- - Session duration: [estimate how long you worked]
189
-
190
- ### Recent Commits (for context)
191
- ${commitsSection}
192
-
193
- ${chainSection}
194
-
195
- ## Current State Summary
196
-
197
- [TODO: Write one paragraph describing what was being worked on, current status, and where things left off]
198
-
199
- ## Codebase Understanding
200
-
201
- ### Architecture Overview
202
-
203
- [TODO: Document key architectural insights discovered during this session]
204
-
205
- ### Critical Files
206
-
207
- | File | Purpose | Relevance |
208
- |------|---------|-----------|
209
- | [TODO: Add critical files] | | |
210
-
211
- ### Key Patterns Discovered
212
-
213
- [TODO: Document important patterns, conventions, or idioms found in this codebase]
214
-
215
- ## Work Completed
216
-
217
- ### Tasks Finished
218
-
219
- - [ ] [TODO: List completed tasks]
220
-
221
- ### Files Modified
222
-
223
- | File | Changes | Rationale |
224
- |------|---------|-----------|
225
- ${modifiedSection}
226
-
227
- ### Decisions Made
228
-
229
- | Decision | Options Considered | Rationale |
230
- |----------|-------------------|-----------|
231
- | [TODO: Document key decisions] | | |
232
-
233
- ## Pending Work
234
-
235
- ### Immediate Next Steps
236
-
237
- 1. [TODO: Most critical next action]
238
- 2. [TODO: Second priority]
239
- 3. [TODO: Third priority]
240
-
241
- ### Blockers/Open Questions
242
-
243
- - [ ] [TODO: List blockers or open questions]
244
-
245
- ### Deferred Items
246
-
247
- - [TODO: Items deferred and why]
248
-
249
- ## Context for Resuming Agent
250
-
251
- ### Important Context
252
-
253
- [TODO: Critical information the next agent MUST know]
254
-
255
- ### Assumptions Made
256
-
257
- [TODO: List assumptions that might not be obvious]
258
-
259
- ### Potential Gotchas
260
-
261
- [TODO: Things that could trip up the next agent]
262
-
263
- ## Environment State
264
-
265
- [TODO: Required env vars, running processes, DB state]
266
-
267
- ## Related Resources
268
-
269
- [TODO: Links to docs, PRs, issues, Slack threads]
270
- `;
271
-
272
- fs.writeFileSync(filepath, content, 'utf-8');
273
- return { filepath, filename };
274
- }
275
-
276
- // --- Main ---
277
- const args = process.argv.slice(2);
278
- if (args.includes('--help') || args.includes('-h')) {
279
- console.log('Usage: node create_handoff.js [task-slug] [--continues-from <previous>]');
280
- console.log(' node create_handoff.js "implementing-auth"');
281
- console.log(' node create_handoff.js "auth-part-2" --continues-from 2024-01-15-auth.md');
282
- process.exit(0);
283
- }
284
-
285
- let slug = null;
286
- let continuesFrom = null;
287
- for (let i = 0; i < args.length; i++) {
288
- if (args[i] === '--continues-from' && i + 1 < args.length) {
289
- continuesFrom = args[++i];
290
- } else if (!args[i].startsWith('-')) {
291
- slug = args[i];
292
- }
293
- }
294
-
295
- const cwd = process.cwd();
296
- const { filepath } = generateHandoff(cwd, slug, continuesFrom);
297
- console.log(`Created handoff: ${filepath}`);
298
- console.log(`\nNext: Open the file and fill in all [TODO: ...] sections.`);
299
- console.log(`Then validate: node scripts/validate_handoff.js ${filepath}`);
@@ -1,113 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * List available handoff documents in the current project.
4
- *
5
- * Searches for handoff documents in .handoffs/ and displays:
6
- * - Filename with date
7
- * - Title extracted from document
8
- * - Status (complete / in progress / needs work)
9
- *
10
- * Usage:
11
- * node list_handoffs.js # List handoffs in current project
12
- * node list_handoffs.js /path # List handoffs in specified path
13
- */
14
-
15
- const fs = require('node:fs');
16
- const path = require('node:path');
17
-
18
- function extractTitle(filepath) {
19
- try {
20
- const content = fs.readFileSync(filepath, 'utf-8');
21
- const m = content.match(/^#\s+(?:Handoff:\s*)?(.+)$/m);
22
- if (m) {
23
- const title = m[1].trim();
24
- if (title.startsWith('[') && title.endsWith(']')) return '[Untitled - needs completion]';
25
- return title.length > 50 ? `${title.slice(0, 50)}...` : title;
26
- }
27
- } catch {
28
- /* ignore */
29
- }
30
- return '[Unable to read title]';
31
- }
32
-
33
- function checkCompletion(filepath) {
34
- try {
35
- const content = fs.readFileSync(filepath, 'utf-8');
36
- const count = (content.match(/\[TODO:/g) || []).length;
37
- if (count === 0) return 'Complete';
38
- if (count <= 3) return `In Progress (${count} TODOs)`;
39
- return `Needs Work (${count} TODOs)`;
40
- } catch {
41
- return 'Unknown';
42
- }
43
- }
44
-
45
- function parseDateFromFilename(filename) {
46
- const m = filename.match(/^(\d{4}-\d{2}-\d{2})-(\d{6})/);
47
- if (m) {
48
- try {
49
- return new Date(`${m[1]}T${m[2].slice(0, 2)}:${m[2].slice(2, 4)}:${m[2].slice(4, 6)}`);
50
- } catch {
51
- /* ignore */
52
- }
53
- }
54
- return null;
55
- }
56
-
57
- function listHandoffs(projectPath) {
58
- const dir = path.join(projectPath, '.handoffs');
59
- if (!fs.existsSync(dir)) return [];
60
-
61
- const handoffs = [];
62
- for (const name of fs.readdirSync(dir)) {
63
- if (!name.endsWith('.md')) continue;
64
- const fp = path.join(dir, name);
65
- const stat = fs.statSync(fp);
66
-
67
- handoffs.push({
68
- path: fp,
69
- filename: name,
70
- title: extractTitle(fp),
71
- status: checkCompletion(fp),
72
- date: parseDateFromFilename(name),
73
- size: stat.size,
74
- });
75
- }
76
-
77
- handoffs.sort((a, b) => (b.date || 0) - (a.date || 0));
78
- return handoffs;
79
- }
80
-
81
- function formatDate(dt) {
82
- if (!dt) return 'Unknown date';
83
- return dt.toISOString().replace('T', ' ').slice(0, 16);
84
- }
85
-
86
- // --- Main ---
87
- const args = process.argv.slice(2);
88
- if (args.includes('--help') || args.includes('-h')) {
89
- console.log('Usage: node list_handoffs.js [project-path]');
90
- process.exit(0);
91
- }
92
-
93
- const projectPath = args[0] || process.cwd();
94
- const handoffs = listHandoffs(projectPath);
95
-
96
- if (handoffs.length === 0) {
97
- console.log('No handoff documents found.');
98
- console.log(`Looked in: ${path.join(projectPath, '.handoffs')}`);
99
- console.log('\nCreate one with: node scripts/create_handoff.js [task-slug]');
100
- process.exit(0);
101
- }
102
-
103
- console.log(`\nFound ${handoffs.length} handoff(s) in ${projectPath}:\n`);
104
- console.log(`${'Date'.padEnd(18)} ${'Status'.padEnd(25)} Title`);
105
- console.log(`${'-'.repeat(18)} ${'-'.repeat(25)} ${'-'.repeat(40)}`);
106
-
107
- for (const h of handoffs) {
108
- console.log(`${formatDate(h.date).padEnd(18)} ${h.status.padEnd(25)} ${h.title}`);
109
- }
110
-
111
- if (args.includes('--json')) {
112
- console.log(`\n${JSON.stringify(handoffs, null, 2)}`);
113
- }
@@ -1,241 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Validate a handoff document for completeness and quality.
4
- *
5
- * Checks:
6
- * - No TODO placeholders remaining
7
- * - Required sections present and populated
8
- * - No potential secrets detected
9
- * - Referenced files exist
10
- * - Quality scoring (0-100)
11
- *
12
- * Usage:
13
- * node validate_handoff.js <handoff-file>
14
- * node validate_handoff.js .handoffs/2024-01-15-143022-auth.md
15
- */
16
-
17
- const fs = require('node:fs');
18
- const path = require('node:path');
19
-
20
- // Secret detection patterns
21
- const SECRET_PATTERNS = [
22
- [/["']?[a-zA-Z_]*api[_-]?key["']?\s*[:=]\s*["'][^"']{10,}["']/gi, 'API key'],
23
- [/["']?[a-zA-Z_]*password["']?\s*[:=]\s*["'][^"']+["']/gi, 'Password'],
24
- [/["']?[a-zA-Z_]*secret["']?\s*[:=]\s*["'][^"']{10,}["']/gi, 'Secret'],
25
- [/["']?[a-zA-Z_]*token["']?\s*[:=]\s*["'][^"']{20,}["']/gi, 'Token'],
26
- [/["']?[a-zA-Z_]*private[_-]?key["']?\s*[:=]/gi, 'Private key'],
27
- [/-----BEGIN [A-Z]+ PRIVATE KEY-----/g, 'PEM private key'],
28
- [/mongodb(\+srv)?:\/\/[^/\s]+:[^@\s]+@/gi, 'MongoDB connection string with password'],
29
- [/postgres:\/\/[^/\s]+:[^@\s]+@/gi, 'PostgreSQL connection string with password'],
30
- [/mysql:\/\/[^/\s]+:[^@\s]+@/gi, 'MySQL connection string with password'],
31
- [/Bearer\s+[a-zA-Z0-9_\-.]+/g, 'Bearer token'],
32
- [/ghp_[a-zA-Z0-9]{36}/g, 'GitHub personal access token'],
33
- [/sk-[a-zA-Z0-9]{48}/g, 'OpenAI API key'],
34
- [/xox[baprs]-[a-zA-Z0-9-]+/g, 'Slack token'],
35
- ];
36
-
37
- const REQUIRED_SECTIONS = ['Current State Summary', 'Important Context', 'Immediate Next Steps'];
38
- const RECOMMENDED_SECTIONS = [
39
- 'Architecture Overview',
40
- 'Critical Files',
41
- 'Files Modified',
42
- 'Decisions Made',
43
- 'Assumptions Made',
44
- 'Potential Gotchas',
45
- ];
46
-
47
- function checkTodos(content) {
48
- const todos = content.match(/\[TODO:[^\]]*\]/g) || [];
49
- return { clear: todos.length === 0, todos };
50
- }
51
-
52
- function checkRequiredSections(content) {
53
- const missing = [];
54
- for (const section of REQUIRED_SECTIONS) {
55
- const pattern = new RegExp(
56
- `(?:^|\\n)##?\\s*${section.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`,
57
- 'i',
58
- );
59
- const match = pattern.exec(content);
60
- if (!match) {
61
- missing.push(`${section} (missing)`);
62
- } else {
63
- const start = match.index + match[0].length;
64
- const nextSection = content.slice(start).search(/\n##?\s+/);
65
- const end = nextSection >= 0 ? start + nextSection : content.length;
66
- const sectionContent = content.slice(start, end).trim();
67
- if (sectionContent.length < 50 || sectionContent.includes('[TODO')) {
68
- missing.push(`${section} (incomplete)`);
69
- }
70
- }
71
- }
72
- return { complete: missing.length === 0, missing };
73
- }
74
-
75
- function checkRecommendedSections(content) {
76
- const missing = [];
77
- for (const section of RECOMMENDED_SECTIONS) {
78
- const pattern = new RegExp(
79
- `(?:^|\\n)##?\\s*${section.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`,
80
- 'i',
81
- );
82
- if (!pattern.test(content)) missing.push(section);
83
- }
84
- return missing;
85
- }
86
-
87
- function scanForSecrets(content) {
88
- const findings = [];
89
- for (const [pattern, description] of SECRET_PATTERNS) {
90
- const matches = content.match(pattern);
91
- if (matches) findings.push({ description, count: matches.length });
92
- }
93
- return findings;
94
- }
95
-
96
- function checkFileReferences(content, basePath) {
97
- const patterns = [
98
- /\|\s*([a-zA-Z0-9_\-./]+\.[a-zA-Z]+)\s*\|/g,
99
- /`([a-zA-Z0-9_\-./]+\.[a-zA-Z]+(?::\d+)?)`/g,
100
- /(?:^|\s)([a-zA-Z0-9_\-./]+\.[a-zA-Z]+:\d+)/gm,
101
- ];
102
-
103
- const foundFiles = new Set();
104
- for (const pattern of patterns) {
105
- for (const m of content.matchAll(pattern)) {
106
- const filepath = m[1].split(':')[0];
107
- if (filepath && !filepath.startsWith('http') && filepath.includes('/')) {
108
- foundFiles.add(filepath);
109
- }
110
- }
111
- }
112
-
113
- const existing = [];
114
- const missing = [];
115
- for (const f of foundFiles) {
116
- if (fs.existsSync(path.join(basePath, f))) existing.push(f);
117
- else missing.push(f);
118
- }
119
- return { existing, missing };
120
- }
121
-
122
- function calculateScore(todosClear, reqComplete, missingReq, missingRec, secrets, filesMissing) {
123
- let score = 100;
124
-
125
- if (!todosClear) score -= 30;
126
- if (!reqComplete) score -= 10 * missingReq.length;
127
- if (secrets.length > 0) score -= 20;
128
- if (filesMissing.length > 0) score -= 5 * Math.min(filesMissing.length, 4);
129
- score -= 2 * missingRec.length;
130
-
131
- score = Math.max(0, score);
132
-
133
- let rating;
134
- if (score >= 90) rating = 'Excellent - Ready for handoff';
135
- else if (score >= 70) rating = 'Good - Minor improvements suggested';
136
- else if (score >= 50) rating = 'Fair - Needs attention before handoff';
137
- else rating = 'Poor - Significant work needed';
138
-
139
- return { score, rating };
140
- }
141
-
142
- function validateHandoff(filepath) {
143
- if (!fs.existsSync(filepath)) return { error: `File not found: ${filepath}` };
144
-
145
- const content = fs.readFileSync(filepath, 'utf-8');
146
- const basePath = path.resolve(path.dirname(filepath), '..'); // Up from .handoffs/
147
-
148
- const { clear: todosClear, todos } = checkTodos(content);
149
- const { complete: reqComplete, missing: missingReq } = checkRequiredSections(content);
150
- const missingRec = checkRecommendedSections(content);
151
- const secrets = scanForSecrets(content);
152
- const { existing: existingFiles, missing: missingFiles } = checkFileReferences(content, basePath);
153
-
154
- const { score, rating } = calculateScore(
155
- todosClear,
156
- reqComplete,
157
- missingReq,
158
- missingRec,
159
- secrets,
160
- missingFiles,
161
- );
162
-
163
- return {
164
- filepath,
165
- score,
166
- rating,
167
- todosClear,
168
- remainingTodos: todos.slice(0, 5),
169
- todoCount: todos.length,
170
- requiredComplete: reqComplete,
171
- missingRequired: missingReq,
172
- missingRecommended: missingRec,
173
- secretsFound: secrets,
174
- filesVerified: existingFiles.length,
175
- filesMissing: missingFiles.slice(0, 5),
176
- };
177
- }
178
-
179
- function printReport(result) {
180
- if (result.error) {
181
- console.log(`Error: ${result.error}`);
182
- return;
183
- }
184
-
185
- console.log(`\n${'='.repeat(60)}`);
186
- console.log('Handoff Validation Report');
187
- console.log('='.repeat(60));
188
- console.log(`File: ${result.filepath}`);
189
- console.log(`\nQuality Score: ${result.score}/100 - ${result.rating}`);
190
- console.log('='.repeat(60));
191
-
192
- if (result.todosClear) {
193
- console.log('\n[PASS] No TODO placeholders remaining');
194
- } else {
195
- console.log(`\n[FAIL] ${result.todoCount} TODO placeholders found:`);
196
- for (const t of result.remainingTodos) console.log(` - ${t.slice(0, 50)}...`);
197
- }
198
-
199
- if (result.requiredComplete) {
200
- console.log('\n[PASS] All required sections complete');
201
- } else {
202
- console.log('\n[FAIL] Missing/incomplete required sections:');
203
- for (const s of result.missingRequired) console.log(` - ${s}`);
204
- }
205
-
206
- if (result.secretsFound.length === 0) {
207
- console.log('\n[PASS] No potential secrets detected');
208
- } else {
209
- console.log('\n[WARN] Potential secrets detected:');
210
- for (const s of result.secretsFound)
211
- console.log(` - ${s.description} (${s.count} match(es))`);
212
- }
213
-
214
- console.log(
215
- `\n[INFO] File references: ${result.filesVerified} verified, ${result.filesMissing.length} missing`,
216
- );
217
- if (result.filesMissing.length > 0) {
218
- for (const f of result.filesMissing) console.log(` - ${f}`);
219
- }
220
-
221
- if (result.missingRecommended.length > 0) {
222
- console.log('\n[INFO] Missing recommended sections:');
223
- for (const s of result.missingRecommended) console.log(` - ${s}`);
224
- }
225
- }
226
-
227
- // --- Main ---
228
- const args = process.argv.slice(2);
229
- if (args.includes('--help') || args.includes('-h') || args.length === 0) {
230
- console.log('Usage: node validate_handoff.js <handoff-file>');
231
- console.log(' node validate_handoff.js .handoffs/2024-01-15-143022-auth.md');
232
- process.exit(args.length === 0 ? 1 : 0);
233
- }
234
-
235
- if (args.includes('--json')) {
236
- const result = validateHandoff(args.find((a) => !a.startsWith('-')));
237
- console.log(JSON.stringify(result, null, 2));
238
- } else {
239
- const result = validateHandoff(args[0]);
240
- printReport(result);
241
- }