worclaude 2.2.6 → 2.3.0

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 (51) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +114 -37
  3. package/package.json +7 -2
  4. package/src/commands/doctor.js +507 -104
  5. package/src/commands/init.js +103 -19
  6. package/src/core/detector.js +34 -10
  7. package/src/core/merger.js +48 -3
  8. package/src/core/scaffolder.js +58 -1
  9. package/src/data/agents.js +5 -0
  10. package/src/index.js +2 -1
  11. package/src/prompts/claude-md-merge.js +5 -0
  12. package/templates/agents/universal/build-validator.md +23 -0
  13. package/templates/agents/universal/code-simplifier.md +11 -0
  14. package/templates/agents/universal/plan-reviewer.md +39 -0
  15. package/templates/agents/universal/test-writer.md +25 -0
  16. package/templates/agents/universal/verify-app.md +12 -0
  17. package/templates/commands/build-fix.md +5 -0
  18. package/templates/commands/commit-push-pr.md +6 -0
  19. package/templates/commands/compact-safe.md +5 -0
  20. package/templates/commands/conflict-resolver.md +5 -0
  21. package/templates/commands/end.md +10 -0
  22. package/templates/commands/learn.md +33 -0
  23. package/templates/commands/refactor-clean.md +10 -0
  24. package/templates/commands/review-changes.md +5 -0
  25. package/templates/commands/review-plan.md +5 -0
  26. package/templates/commands/setup.md +5 -0
  27. package/templates/commands/start.md +10 -0
  28. package/templates/commands/status.md +5 -0
  29. package/templates/commands/sync.md +5 -0
  30. package/templates/commands/techdebt.md +5 -0
  31. package/templates/commands/test-coverage.md +5 -0
  32. package/templates/commands/update-claude-md.md +5 -0
  33. package/templates/commands/verify.md +11 -0
  34. package/templates/core/agents-md.md +25 -0
  35. package/templates/core/claude-md.md +17 -0
  36. package/templates/hooks/README.md +106 -0
  37. package/templates/hooks/correction-detect.cjs +48 -0
  38. package/templates/hooks/examples/prompt-hook-commit-validator.json +16 -0
  39. package/templates/hooks/learn-capture.cjs +179 -0
  40. package/templates/hooks/pre-compact-save.cjs +60 -0
  41. package/templates/hooks/skill-hint.cjs +66 -0
  42. package/templates/memory/decisions.md +11 -0
  43. package/templates/memory/preferences.md +17 -0
  44. package/templates/settings/base.json +56 -8
  45. package/templates/skills/templates/backend-conventions.md +1 -0
  46. package/templates/skills/templates/frontend-design-system.md +1 -0
  47. package/templates/skills/templates/project-patterns.md +1 -0
  48. package/templates/skills/universal/coding-principles.md +60 -0
  49. package/templates/skills/universal/context-management.md +21 -0
  50. package/templates/skills/universal/planning-with-files.md +11 -0
  51. package/templates/skills/universal/verification.md +18 -0
@@ -1,7 +1,13 @@
1
1
  import path from 'node:path';
2
2
  import inquirer from 'inquirer';
3
3
  import ora from 'ora';
4
- import { scaffoldFile, updateGitignore } from '../core/scaffolder.js';
4
+ import {
5
+ scaffoldFile,
6
+ updateGitignore,
7
+ scaffoldHooks,
8
+ scaffoldPluginJson,
9
+ scaffoldMemoryDocs,
10
+ } from '../core/scaffolder.js';
5
11
  import {
6
12
  computeFileHashes,
7
13
  createWorkflowMeta,
@@ -211,11 +217,30 @@ async function runAgents(selections) {
211
217
  return { ...selections, selectedAgents };
212
218
  }
213
219
 
220
+ async function runOptionalExtras(selections) {
221
+ const { generatePluginJson, scaffoldGtdMemory } = await inquirer.prompt([
222
+ {
223
+ type: 'confirm',
224
+ name: 'generatePluginJson',
225
+ message: 'Generate .claude-plugin/plugin.json for marketplace compatibility?',
226
+ default: selections.generatePluginJson || false,
227
+ },
228
+ {
229
+ type: 'confirm',
230
+ name: 'scaffoldGtdMemory',
231
+ message: 'Scaffold structured memory files (decisions.md, preferences.md)?',
232
+ default: selections.scaffoldGtdMemory || false,
233
+ },
234
+ ]);
235
+ return { ...selections, generatePluginJson, scaffoldGtdMemory };
236
+ }
237
+
214
238
  const STEP_RUNNERS = {
215
239
  projectInfo: runProjectInfo,
216
240
  projectType: runProjectType,
217
241
  techStack: runTechStack,
218
242
  agents: runAgents,
243
+ optionalExtras: runOptionalExtras,
219
244
  };
220
245
 
221
246
  // --- Confirmation ---
@@ -251,6 +276,14 @@ async function showConfirmation(selections) {
251
276
  console.log(
252
277
  ` ${'Agents'.padEnd(10)}${display.white(`${universalCount} universal + ${optionalCount} optional`)} ${display.dimColor(`(${totalCount} total)`)}`
253
278
  );
279
+
280
+ const extrasLabels = [];
281
+ if (selections.generatePluginJson) extrasLabels.push('plugin.json');
282
+ if (selections.scaffoldGtdMemory) extrasLabels.push('memory docs');
283
+ if (extrasLabels.length > 0) {
284
+ console.log(` ${'Extras'.padEnd(10)}${display.white(extrasLabels.join(', '))}`);
285
+ }
286
+
254
287
  display.newline();
255
288
 
256
289
  const { confirmation } = await inquirer.prompt([
@@ -271,25 +304,37 @@ async function showConfirmation(selections) {
271
304
 
272
305
  // --- Shared functions ---
273
306
 
274
- async function runInteractivePrompts(projectRoot) {
275
- let selections = {
307
+ function createInitialSelections(projectRoot) {
308
+ return {
276
309
  projectName: path.basename(projectRoot),
277
310
  description: '',
278
311
  projectTypes: [],
279
312
  languages: [],
280
313
  useDocker: false,
281
314
  selectedAgents: [],
315
+ generatePluginJson: false,
316
+ scaffoldGtdMemory: false,
282
317
  };
318
+ }
319
+
320
+ async function runAllSteps(selections) {
321
+ selections = await runProjectInfo(selections);
322
+ selections = await runProjectType(selections);
323
+ selections = await runTechStack(selections);
324
+ selections = await runAgents(selections);
325
+ selections = await runOptionalExtras(selections);
326
+ return selections;
327
+ }
328
+
329
+ async function runInteractivePrompts(projectRoot) {
330
+ let selections = createInitialSelections(projectRoot);
283
331
 
284
332
  let confirmed = false;
285
333
  let firstRun = true;
286
334
 
287
335
  while (!confirmed) {
288
336
  if (firstRun) {
289
- selections = await runProjectInfo(selections);
290
- selections = await runProjectType(selections);
291
- selections = await runTechStack(selections);
292
- selections = await runAgents(selections);
337
+ selections = await runAllSteps(selections);
293
338
  firstRun = false;
294
339
  }
295
340
 
@@ -298,21 +343,11 @@ async function runInteractivePrompts(projectRoot) {
298
343
  if (confirmation === 'yes') {
299
344
  confirmed = true;
300
345
  } else if (confirmation === 'restart') {
301
- selections = {
302
- projectName: path.basename(projectRoot),
303
- description: '',
304
- projectTypes: [],
305
- languages: [],
306
- useDocker: false,
307
- selectedAgents: [],
308
- };
346
+ selections = createInitialSelections(projectRoot);
309
347
  display.newline();
310
348
  display.info('Starting over...');
311
349
  display.newline();
312
- selections = await runProjectInfo(selections);
313
- selections = await runProjectType(selections);
314
- selections = await runTechStack(selections);
315
- selections = await runAgents(selections);
350
+ selections = await runAllSteps(selections);
316
351
  } else if (confirmation === 'adjust') {
317
352
  const { step } = await inquirer.prompt([
318
353
  {
@@ -363,6 +398,10 @@ function buildTemplateVariables(selections) {
363
398
  );
364
399
  const skillsText = skillsLines.join('\n');
365
400
 
401
+ const memoryArchitectureExtras = selections.scaffoldGtdMemory
402
+ ? '\n- Team decisions: `docs/memory/decisions.md` (version-controlled, shared).\n- Team preferences: `docs/memory/preferences.md` (version-controlled, shared).'
403
+ : '';
404
+
366
405
  return {
367
406
  project_name: projectName,
368
407
  description: description || 'A project scaffolded with Worclaude',
@@ -372,6 +411,7 @@ function buildTemplateVariables(selections) {
372
411
  docker_row: dockerRow,
373
412
  commands_filled_during_init: commandsText,
374
413
  project_specific_skills: skillsText,
414
+ memory_architecture_extras: memoryArchitectureExtras,
375
415
  timestamp: new Date().toISOString(),
376
416
  };
377
417
  }
@@ -401,6 +441,9 @@ async function scaffoldFresh(projectRoot, selections, variables, settingsStr, ve
401
441
  await scaffoldFile('core/claude-md.md', 'CLAUDE.md', variables, projectRoot);
402
442
  spinner.text = 'Created CLAUDE.md';
403
443
 
444
+ await scaffoldFile('core/agents-md.md', 'AGENTS.md', variables, projectRoot);
445
+ spinner.text = 'Created AGENTS.md';
446
+
404
447
  await writeFile(path.join(projectRoot, '.claude', 'settings.json'), settingsStr);
405
448
  spinner.text = 'Created .claude/settings.json';
406
449
 
@@ -500,6 +543,26 @@ async function scaffoldFresh(projectRoot, selections, variables, settingsStr, ve
500
543
  await writeFile(path.join(projectRoot, '.claude', 'sessions', '.gitkeep'), '');
501
544
  spinner.text = 'Created .claude/sessions/';
502
545
 
546
+ // Copy hook scripts (.claude/hooks/)
547
+ await scaffoldHooks(projectRoot);
548
+ spinner.text = 'Created .claude/hooks/';
549
+
550
+ // Create learnings directory for correction capture
551
+ await writeFile(path.join(projectRoot, '.claude', 'learnings', '.gitkeep'), '');
552
+ spinner.text = 'Created .claude/learnings/';
553
+
554
+ // Opt-in: plugin.json for Claude Code marketplace compatibility
555
+ if (selections.generatePluginJson) {
556
+ await scaffoldPluginJson(projectRoot, selections);
557
+ spinner.text = 'Created .claude-plugin/plugin.json';
558
+ }
559
+
560
+ // Opt-in: GTD memory scaffold (docs/memory/decisions.md, preferences.md)
561
+ if (selections.scaffoldGtdMemory) {
562
+ await scaffoldMemoryDocs(projectRoot);
563
+ spinner.text = 'Created docs/memory/';
564
+ }
565
+
503
566
  await computeAndWriteWorkflowMeta(projectRoot, selections, version);
504
567
  spinner.text = 'Created .claude/workflow-meta.json';
505
568
 
@@ -518,12 +581,20 @@ function displayFreshSuccess(selections, skipped) {
518
581
 
519
582
  display.newline();
520
583
  display.success('CLAUDE.md');
584
+ display.success('AGENTS.md');
521
585
  display.success('.claude/settings.json');
522
586
  display.success('.claude/workflow-meta.json');
523
587
  display.success(`.claude/agents/${display.dimColor(` ${totalAgents} agents`)}`);
524
588
  display.success(`.claude/commands/${display.dimColor(` ${COMMAND_FILES.length} commands`)}`);
525
589
  display.success(`.claude/skills/${display.dimColor(` ${totalSkills} skills`)}`);
526
590
  display.success('.claude/sessions/');
591
+ display.success('.claude/hooks/');
592
+ if (selections.generatePluginJson) {
593
+ display.success('.claude-plugin/plugin.json');
594
+ }
595
+ if (selections.scaffoldGtdMemory) {
596
+ display.success('docs/memory/' + display.dimColor(' decisions.md, preferences.md'));
597
+ }
527
598
  display.success('.mcp.json');
528
599
  display.success('.gitignore');
529
600
  if (skipped.progressMd) {
@@ -648,6 +719,19 @@ function displayMergeReport(report, backupPath) {
648
719
  display.success('CLAUDE.md created');
649
720
  }
650
721
 
722
+ // Memory Architecture Tier 3 notice
723
+ if (report.memoryArchitectureSectionExists) {
724
+ display.newline();
725
+ display.info(
726
+ 'docs/memory/ scaffolded, but your CLAUDE.md already has a Memory Architecture section.'
727
+ );
728
+ display.dim(' Add these lines manually if you want pointer bullets:');
729
+ display.dim(' - Team decisions: `docs/memory/decisions.md` (version-controlled, shared).');
730
+ display.dim(
731
+ ' - Team preferences: `docs/memory/preferences.md` (version-controlled, shared).'
732
+ );
733
+ }
734
+
651
735
  // Skipped
652
736
  if (report.skipped.progressMd) {
653
737
  display.dim(' docs/spec/PROGRESS.md — already exists, skipped');
@@ -19,8 +19,31 @@ export async function detectScenario(projectRoot) {
19
19
  }
20
20
 
21
21
  export async function scanExistingSetup(projectRoot) {
22
- const hasClaudeDir = await dirExists(path.join(projectRoot, '.claude'));
23
- const hasClaudeMd = await fileExists(path.join(projectRoot, 'CLAUDE.md'));
22
+ const [
23
+ hasClaudeDir,
24
+ hasClaudeMd,
25
+ hasSettingsJson,
26
+ hasMcpJson,
27
+ existingSkills,
28
+ existingSkillDirs,
29
+ existingAgents,
30
+ existingCommands,
31
+ hasAgentsMd,
32
+ hasProgressMd,
33
+ hasSpecMd,
34
+ ] = await Promise.all([
35
+ dirExists(path.join(projectRoot, '.claude')),
36
+ fileExists(path.join(projectRoot, 'CLAUDE.md')),
37
+ fileExists(path.join(projectRoot, '.claude', 'settings.json')),
38
+ fileExists(path.join(projectRoot, '.mcp.json')),
39
+ listFiles(path.join(projectRoot, '.claude', 'skills')),
40
+ listSkillDirs(path.join(projectRoot, '.claude', 'skills')),
41
+ listFiles(path.join(projectRoot, '.claude', 'agents')),
42
+ listFiles(path.join(projectRoot, '.claude', 'commands')),
43
+ fileExists(path.join(projectRoot, 'AGENTS.md')),
44
+ fileExists(path.join(projectRoot, 'docs', 'spec', 'PROGRESS.md')),
45
+ fileExists(path.join(projectRoot, 'docs', 'spec', 'SPEC.md')),
46
+ ]);
24
47
 
25
48
  let claudeMdLineCount = 0;
26
49
  if (hasClaudeMd) {
@@ -32,13 +55,14 @@ export async function scanExistingSetup(projectRoot) {
32
55
  hasClaudeDir,
33
56
  hasClaudeMd,
34
57
  claudeMdLineCount,
35
- hasSettingsJson: await fileExists(path.join(projectRoot, '.claude', 'settings.json')),
36
- hasMcpJson: await fileExists(path.join(projectRoot, '.mcp.json')),
37
- existingSkills: await listFiles(path.join(projectRoot, '.claude', 'skills')),
38
- existingSkillDirs: await listSkillDirs(path.join(projectRoot, '.claude', 'skills')),
39
- existingAgents: await listFiles(path.join(projectRoot, '.claude', 'agents')),
40
- existingCommands: await listFiles(path.join(projectRoot, '.claude', 'commands')),
41
- hasProgressMd: await fileExists(path.join(projectRoot, 'docs', 'spec', 'PROGRESS.md')),
42
- hasSpecMd: await fileExists(path.join(projectRoot, 'docs', 'spec', 'SPEC.md')),
58
+ hasSettingsJson,
59
+ hasMcpJson,
60
+ existingSkills,
61
+ existingSkillDirs,
62
+ existingAgents,
63
+ existingCommands,
64
+ hasAgentsMd,
65
+ hasProgressMd,
66
+ hasSpecMd,
43
67
  };
44
68
  }
@@ -1,7 +1,15 @@
1
1
  import path from 'node:path';
2
2
  import os from 'node:os';
3
3
  import { readFile, writeFile } from '../utils/file.js';
4
- import { readTemplate, substituteVariables, scaffoldFile, mergeSettings } from './scaffolder.js';
4
+ import {
5
+ readTemplate,
6
+ substituteVariables,
7
+ scaffoldFile,
8
+ mergeSettings,
9
+ scaffoldHooks,
10
+ scaffoldPluginJson,
11
+ scaffoldMemoryDocs,
12
+ } from './scaffolder.js';
5
13
  import { promptHookConflict } from '../prompts/conflict-resolution.js';
6
14
  import {
7
15
  detectMissingSections,
@@ -374,7 +382,7 @@ async function mergeDocSpecs(projectRoot, existingScan, variables, selections, r
374
382
  report.skipped.specMd = existingScan.hasSpecMd;
375
383
  }
376
384
 
377
- async function handleClaudeMd(projectRoot, existingScan, variables, report) {
385
+ async function handleClaudeMd(projectRoot, existingScan, variables, selections, report) {
378
386
  if (!existingScan.hasClaudeMd) {
379
387
  // No CLAUDE.md — scaffold fresh
380
388
  await scaffoldFile('core/claude-md.md', 'CLAUDE.md', variables, projectRoot);
@@ -386,6 +394,13 @@ async function handleClaudeMd(projectRoot, existingScan, variables, report) {
386
394
  const renderedTemplate = substituteVariables(await readTemplate('core/claude-md.md'), variables);
387
395
  const missingSections = detectMissingSections(existingContent);
388
396
 
397
+ // Tier 3 notice: if user opted into GTD memory AND their CLAUDE.md already has
398
+ // a Memory Architecture section, the pointer bullets from the rendered template
399
+ // won't be merged in automatically. Surface this so the user can add them manually.
400
+ if (selections.scaffoldGtdMemory && !missingSections.includes('Memory Architecture')) {
401
+ report.memoryArchitectureSectionExists = true;
402
+ }
403
+
389
404
  if (missingSections.length === 0) {
390
405
  display.newline();
391
406
  display.info(`Your CLAUDE.md (${existingContent.split(/\r?\n/).length} lines) was detected.`);
@@ -411,6 +426,16 @@ async function handleClaudeMd(projectRoot, existingScan, variables, report) {
411
426
  }
412
427
  }
413
428
 
429
+ async function mergeAgentsMd(projectRoot, existingScan, variables, report) {
430
+ if (!existingScan.hasAgentsMd) {
431
+ await scaffoldFile('core/agents-md.md', 'AGENTS.md', variables, projectRoot);
432
+ report.agentsMdHandling = 'created';
433
+ } else {
434
+ await scaffoldFile('core/agents-md.md', 'AGENTS.md.workflow-ref', variables, projectRoot);
435
+ report.agentsMdHandling = 'saved-alongside';
436
+ }
437
+ }
438
+
414
439
  // --- Main merge function ---
415
440
 
416
441
  export async function performMerge(
@@ -425,6 +450,7 @@ export async function performMerge(
425
450
  conflicts: { skills: [], agents: [], commands: [] },
426
451
  skipped: { progressMd: false, specMd: false },
427
452
  claudeMdHandling: 'kept',
453
+ agentsMdHandling: 'kept',
428
454
  hookConflicts: [],
429
455
  };
430
456
 
@@ -442,11 +468,30 @@ export async function performMerge(
442
468
  // Ensure sessions directory exists for session persistence
443
469
  await writeFile(path.join(projectRoot, '.claude', 'sessions', '.gitkeep'), '');
444
470
 
471
+ // Copy hook scripts (preserves existing user modifications)
472
+ await scaffoldHooks(projectRoot);
473
+
474
+ // Create learnings directory for correction capture
475
+ await writeFile(path.join(projectRoot, '.claude', 'learnings', '.gitkeep'), '');
476
+
477
+ // Opt-in: plugin.json (idempotent — scaffolder skips if file exists)
478
+ if (selections.generatePluginJson) {
479
+ await scaffoldPluginJson(projectRoot, selections);
480
+ }
481
+
482
+ // Opt-in: GTD memory scaffold (idempotent per-file). Tier 3 notice for
483
+ // existing Memory Architecture section is handled inside handleClaudeMd,
484
+ // which already reads CLAUDE.md and runs section detection.
485
+ if (selections.scaffoldGtdMemory) {
486
+ await scaffoldMemoryDocs(projectRoot);
487
+ }
488
+
445
489
  await mergeDocSpecs(projectRoot, existingScan, variables, selections, report);
446
490
 
447
491
  // Stop spinner before CLAUDE.md merge — interactive prompts for section selection
448
492
  if (spinner) spinner.stop();
449
- await handleClaudeMd(projectRoot, existingScan, variables, report);
493
+ await handleClaudeMd(projectRoot, existingScan, variables, selections, report);
494
+ await mergeAgentsMd(projectRoot, existingScan, variables, report);
450
495
  if (spinner) spinner.start();
451
496
 
452
497
  return report;
@@ -1,7 +1,8 @@
1
1
  import path from 'node:path';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import fs from 'fs-extra';
4
- import { readFile, writeFile } from '../utils/file.js';
4
+ import { fileExists, readFile, writeFile } from '../utils/file.js';
5
+ import { UNIVERSAL_AGENTS } from '../data/agents.js';
5
6
 
6
7
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
8
 
@@ -52,6 +53,7 @@ export async function updateGitignore(projectDir) {
52
53
  '.claude/workflow-meta.json',
53
54
  '.claude/worktrees/',
54
55
  '.claude-backup-*/',
56
+ '.claude/learnings/',
55
57
  ];
56
58
  const header = '# Worclaude (generated workflow files)';
57
59
 
@@ -86,6 +88,61 @@ export async function updateGitignore(projectDir) {
86
88
  return true;
87
89
  }
88
90
 
91
+ export async function scaffoldHooks(projectRoot) {
92
+ const hooksTemplateDir = path.join(getTemplatesDir(), 'hooks');
93
+ const destDir = path.join(projectRoot, '.claude', 'hooks');
94
+ await fs.ensureDir(destDir);
95
+
96
+ const entries = await fs.readdir(hooksTemplateDir);
97
+ for (const entry of entries) {
98
+ if (!entry.endsWith('.cjs') && !entry.endsWith('.js')) continue;
99
+ await fs.copy(path.join(hooksTemplateDir, entry), path.join(destDir, entry), {
100
+ overwrite: false,
101
+ errorOnExist: false,
102
+ });
103
+ }
104
+ }
105
+
106
+ export function slugifyPluginName(projectName) {
107
+ const slug = String(projectName || '')
108
+ .toLowerCase()
109
+ .replace(/[^a-z0-9]+/g, '-')
110
+ .replace(/^-+|-+$/g, '');
111
+ return slug || 'worclaude-plugin';
112
+ }
113
+
114
+ export async function scaffoldPluginJson(projectRoot, selections) {
115
+ const destPath = path.join(projectRoot, '.claude-plugin', 'plugin.json');
116
+ if (await fileExists(destPath)) return;
117
+
118
+ const agents = [...UNIVERSAL_AGENTS, ...(selections.selectedAgents || [])].map(
119
+ (a) => `./.claude/agents/${a}.md`
120
+ );
121
+
122
+ const plugin = {
123
+ name: `${slugifyPluginName(selections.projectName)}-workflow`,
124
+ version: '0.1.0',
125
+ description: selections.description || `Claude Code workflow for ${selections.projectName}`,
126
+ keywords: ['claude-code', 'workflow', 'worclaude'],
127
+ agents,
128
+ skills: ['./.claude/skills/'],
129
+ commands: ['./.claude/commands/'],
130
+ };
131
+
132
+ await writeFile(destPath, JSON.stringify(plugin, null, 2) + '\n');
133
+ }
134
+
135
+ export async function scaffoldMemoryDocs(projectRoot) {
136
+ const destDir = path.join(projectRoot, 'docs', 'memory');
137
+ await fs.ensureDir(destDir);
138
+ for (const file of ['decisions.md', 'preferences.md']) {
139
+ const destPath = path.join(destDir, file);
140
+ if (await fileExists(destPath)) continue;
141
+ const content = await readTemplate(`memory/${file}`);
142
+ await writeFile(destPath, content);
143
+ }
144
+ }
145
+
89
146
  export function mergeSettings(base, ...stacks) {
90
147
  const merged = JSON.parse(JSON.stringify(base));
91
148
  const baseAllow = merged.permissions?.allow || [];
@@ -194,6 +194,7 @@ export const COMMAND_FILES = [
194
194
  'build-fix',
195
195
  'refactor-clean',
196
196
  'test-coverage',
197
+ 'learn',
197
198
  ];
198
199
 
199
200
  export const UNIVERSAL_SKILLS = [
@@ -205,6 +206,7 @@ export const UNIVERSAL_SKILLS = [
205
206
  'verification',
206
207
  'testing',
207
208
  'claude-md-maintenance',
209
+ 'coding-principles',
208
210
  'subagent-usage',
209
211
  'security-checklist',
210
212
  'coordinator-mode',
@@ -216,6 +218,8 @@ export const TEMPLATE_SKILLS = [
216
218
  'project-patterns',
217
219
  ];
218
220
 
221
+ export const HOOK_FILES = ['pre-compact-save', 'correction-detect', 'learn-capture', 'skill-hint'];
222
+
219
223
  export const PROJECT_TYPES = [
220
224
  'Full-stack web application',
221
225
  'Backend / API',
@@ -315,6 +319,7 @@ export const CONFIRMATION_STEPS = [
315
319
  { name: 'Project type', value: 'projectType' },
316
320
  { name: 'Tech stack', value: 'techStack' },
317
321
  { name: 'Agent selection', value: 'agents' },
322
+ { name: 'Optional extras (plugin.json, memory)', value: 'optionalExtras' },
318
323
  ];
319
324
 
320
325
  export const NOTIFICATION_COMMANDS = {
package/src/index.js CHANGED
@@ -56,6 +56,7 @@ program
56
56
  program
57
57
  .command('doctor')
58
58
  .description('Validate workflow installation health')
59
- .action(doctorCommand);
59
+ .option('--json', 'Output results as JSON')
60
+ .action((options) => doctorCommand(options));
60
61
 
61
62
  program.parse();
@@ -28,6 +28,11 @@ const SECTION_DETECTORS = [
28
28
  heading: '## Gotchas',
29
29
  test: (c) => /Gotchas/i.test(c),
30
30
  },
31
+ {
32
+ name: 'Memory Architecture',
33
+ heading: '## Memory Architecture',
34
+ test: (c) => /Memory Architecture/i.test(c) || /\.claude\/learnings\//i.test(c),
35
+ },
31
36
  ];
32
37
 
33
38
  export function detectMissingSections(existingContent) {
@@ -48,6 +48,29 @@ End with a clear verdict:
48
48
  - **WARNINGS**: Non-blocking issues (formatting, deprecation warnings) — can commit with caution
49
49
  - **BLOCKED**: Tests fail or build broken — must fix before committing
50
50
 
51
+ ## Severity Classification
52
+
53
+ Classify each finding:
54
+
55
+ | Severity | Meaning | Action |
56
+ |----------|---------|--------|
57
+ | CRITICAL | Build fails, tests fail, app won't start | BLOCKED — must fix |
58
+ | WARNING | Deprecation warnings, formatting issues, lint warnings | Can commit with caution |
59
+ | INFO | Performance suggestions, optional improvements | Note for later |
60
+
61
+ Report CRITICAL issues first. If no CRITICAL but some WARNINGS, verdict is WARNINGS.
62
+
63
+ ## Common False Positives
64
+
65
+ Do not report these as failures:
66
+ - **Optional peer dependencies** missing — only a failure if the feature is used
67
+ - **Platform-specific warnings** — warnings for platforms not targeted
68
+ - **Dev dependency deprecation** — note but don't block
69
+ - **Test timeouts in CI vs local** — environmental, not bugs
70
+ - **Snapshot updates needed** — expected after intentional visual changes
71
+
72
+ When in doubt, report as INFO, not CRITICAL.
73
+
51
74
  ## Rules
52
75
  - Run checks in the listed order — if build fails, still run the rest
53
76
  - Report ALL failures, not just the first one
@@ -65,6 +65,17 @@ After completing improvements, provide a summary:
65
65
  | 2 | src/api.js | Early return for null check | Reduced nesting from 4→2 levels |
66
66
  | 3 | src/config.js | Removed 12 unused imports | Dead code |
67
67
 
68
+ ## When NOT to Simplify
69
+
70
+ Do not refactor:
71
+ - **Hot paths** with performance-sensitive code — profiling data overrules readability
72
+ - **Stable legacy code** that works and has no active bugs — risk exceeds benefit
73
+ - **Framework boilerplate** — generated patterns that look verbose but are idiomatic
74
+ - **Security-critical code** — verbose explicit validation is safer than clever abstraction
75
+ - **Code you don't fully understand** — if you can't explain what it does, don't touch it
76
+
77
+ When in doubt: leave it. A working system is more valuable than a clean system.
78
+
68
79
  ## Rules
69
80
  - Never change behavior — if tests break, you changed behavior, revert
70
81
  - Never refactor code you don't understand — read the full context first
@@ -74,6 +74,45 @@ Structure your review as:
74
74
 
75
75
  **What's Good** (1-2 sentences — acknowledge strengths briefly):
76
76
 
77
+ ## Confidence Threshold
78
+
79
+ Only flag issues you are >80% confident are real problems.
80
+ If uncertain, phrase it as a question, not a finding.
81
+ "This might cause issues with X" wastes the planner's time.
82
+ "Will this handle concurrent requests? I see no locking in step 3." is actionable.
83
+
84
+ ## Must-Haves Verification
85
+
86
+ Every plan must define must-haves:
87
+ - **Truths**: Observable behaviors that must be true when done
88
+ - **Artifacts**: Files that must exist with real implementation
89
+ - **Key Links**: Connections between artifacts (imports, routes, configs)
90
+
91
+ If must-haves are missing, verdict is NEEDS REVISION regardless of other quality.
92
+
93
+ ## Bounded Revision
94
+
95
+ Maximum 3 revision cycles between reviewer and author:
96
+ - Round 1: All issues surfaced. Author addresses them.
97
+ - Round 2: Verify fixes, catch anything missed. Author addresses.
98
+ - Round 3: Final check. If issues remain, escalate to user.
99
+
100
+ If critical issues do not decrease between rounds, stop reviewing
101
+ and escalate: "This plan needs human architectural input because..."
102
+
103
+ ## Worked Example
104
+
105
+ **Plan:** "Add password reset feature"
106
+ **Step 1:** "Create reset endpoint in src/routes/auth.js"
107
+
108
+ **Review finding:** NEEDS REVISION
109
+ - Missing: exact HTTP method and path (POST /api/auth/reset-password?)
110
+ - Missing: what parameters does the request body accept?
111
+ - Missing: verification step — how do you confirm the endpoint works?
112
+ - Missing: how does the reset token get sent to the user?
113
+
114
+ This level of specificity is what you're checking for.
115
+
77
116
  ## Review Principles
78
117
  - Be direct — flag problems, suggest solutions, don't hedge
79
118
  - Be specific — "this could fail" is useless; "step 3 will fail if the users table has existing rows because of the NOT NULL constraint" is actionable
@@ -91,6 +91,31 @@ After writing tests, report:
91
91
  | src/core/merger.js | 8 | 74% → 91% | Added edge cases for conflict resolution |
92
92
  | src/utils/hash.js | 3 | 100% | Empty input + large file + encoding |
93
93
 
94
+ ## Severity Classification
95
+
96
+ When reporting coverage gaps, classify by severity:
97
+
98
+ | Severity | When | Priority |
99
+ |----------|------|----------|
100
+ | CRITICAL | Auth, payment, data validation — user-facing correctness | Test immediately |
101
+ | HIGH | Core business logic, state transitions, error recovery | Test in this session |
102
+ | MEDIUM | Integration points, config variations, concurrent scenarios | Test if time allows |
103
+ | LOW | Formatting, logging, pure delegation | Skip unless requested |
104
+
105
+ Focus effort on CRITICAL and HIGH. Report MEDIUM and LOW without writing tests for them.
106
+
107
+ ## Edge Case Categories
108
+
109
+ When testing a function, systematically consider:
110
+ 1. **Empty/zero**: empty string, empty array, 0, null, undefined
111
+ 2. **Boundary**: first, last, max, min, off-by-one
112
+ 3. **Type**: wrong type, NaN, Infinity, negative where positive expected
113
+ 4. **State**: uninitialized, already-completed, concurrent modification
114
+ 5. **Format**: unicode, special characters, very long strings, whitespace-only
115
+ 6. **Environment**: missing file, no network, permission denied
116
+ 7. **Sequence**: out-of-order operations, duplicate calls, rapid succession
117
+ 8. **Scale**: single item, many items, items exceeding expected max
118
+
94
119
  ## Rules
95
120
  - Follow the project's existing test patterns — match file naming, framework, assertion style
96
121
  - Aim for meaningful coverage (>80% on changed code), not 100% everywhere
@@ -116,6 +116,18 @@ You will feel the urge to skip checks. These are the excuses — recognize them:
116
116
  - **Database migrations**: run migration up → verify schema matches intent → run migration down (reversibility) → test against existing data, not just empty DB
117
117
  - **Data/ML pipeline**: run with sample input → verify output shape/schema/types → test empty input and NaN/null handling → check row counts in vs out for silent data loss
118
118
 
119
+ ## Verification Depth Levels
120
+
121
+ Every check targets a depth level. Cover all 4 for critical features:
122
+
123
+ 1. **Exists** — file/function/endpoint is present
124
+ 2. **Substantive** — real implementation, not a stub or placeholder
125
+ 3. **Wired** — connected to the rest of the system (imported, routed, configured)
126
+ 4. **Functional** — actually works when invoked with real input
127
+
128
+ Before issuing PASS, scan for stubs: `grep -rn "TODO\|FIXME\|placeholder\|not.implemented" --include="*.js" --include="*.ts" --include="*.py" src/ || true`
129
+ Do not flag test fixtures, documentation examples, or planned future work in SPEC.md.
130
+
119
131
  ## Before Issuing PASS
120
132
 
121
133
  Your report must include at least one adversarial probe (boundary value, concurrent request,
@@ -38,3 +38,8 @@ for diagnosis and resolution.
38
38
  - Tests failing after dependency update
39
39
  - CI is red and you need to fix locally before pushing
40
40
  - After a large refactor that introduced errors
41
+
42
+ ## Trigger Phrases
43
+ - "fix the build"
44
+ - "build is broken"
45
+ - "tests are failing"
@@ -64,3 +64,9 @@ Only used for release merges after /sync has been run.
64
64
  Ask the user which base branch to target before creating a PR.
65
65
 
66
66
  Use gh pr create for PR creation.
67
+
68
+ ## Trigger Phrases
69
+ - "commit and push"
70
+ - "create a PR"
71
+ - "ship it"
72
+ - "push my changes"