worclaude 2.2.6 → 2.4.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.
- package/CHANGELOG.md +113 -0
- package/README.md +114 -37
- package/package.json +7 -2
- package/src/commands/doctor.js +507 -104
- package/src/commands/init.js +103 -19
- package/src/core/detector.js +34 -10
- package/src/core/merger.js +48 -3
- package/src/core/scaffolder.js +58 -1
- package/src/data/agent-registry.js +17 -2
- package/src/data/agents.js +7 -0
- package/src/index.js +2 -1
- package/src/prompts/claude-md-merge.js +5 -0
- package/templates/agents/universal/build-validator.md +23 -0
- package/templates/agents/universal/code-simplifier.md +11 -0
- package/templates/agents/universal/plan-reviewer.md +39 -0
- package/templates/agents/universal/test-writer.md +25 -0
- package/templates/agents/universal/upstream-watcher.md +120 -0
- package/templates/agents/universal/verify-app.md +12 -0
- package/templates/commands/build-fix.md +5 -0
- package/templates/commands/commit-push-pr.md +6 -0
- package/templates/commands/compact-safe.md +5 -0
- package/templates/commands/conflict-resolver.md +5 -0
- package/templates/commands/end.md +10 -0
- package/templates/commands/learn.md +33 -0
- package/templates/commands/refactor-clean.md +10 -0
- package/templates/commands/review-changes.md +5 -0
- package/templates/commands/review-plan.md +5 -0
- package/templates/commands/setup.md +5 -0
- package/templates/commands/start.md +10 -0
- package/templates/commands/status.md +5 -0
- package/templates/commands/sync.md +5 -0
- package/templates/commands/techdebt.md +5 -0
- package/templates/commands/test-coverage.md +5 -0
- package/templates/commands/update-claude-md.md +5 -0
- package/templates/commands/upstream-check.md +85 -0
- package/templates/commands/verify.md +11 -0
- package/templates/core/agents-md.md +25 -0
- package/templates/core/claude-md.md +17 -0
- package/templates/hooks/README.md +106 -0
- package/templates/hooks/correction-detect.cjs +48 -0
- package/templates/hooks/examples/prompt-hook-commit-validator.json +16 -0
- package/templates/hooks/learn-capture.cjs +179 -0
- package/templates/hooks/pre-compact-save.cjs +60 -0
- package/templates/hooks/skill-hint.cjs +66 -0
- package/templates/memory/decisions.md +11 -0
- package/templates/memory/preferences.md +17 -0
- package/templates/settings/base.json +56 -8
- package/templates/skills/templates/backend-conventions.md +1 -0
- package/templates/skills/templates/frontend-design-system.md +1 -0
- package/templates/skills/templates/project-patterns.md +1 -0
- package/templates/skills/universal/coding-principles.md +60 -0
- package/templates/skills/universal/context-management.md +21 -0
- package/templates/skills/universal/planning-with-files.md +11 -0
- package/templates/skills/universal/verification.md +18 -0
package/src/commands/init.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
275
|
-
|
|
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
|
|
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
|
|
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');
|
package/src/core/detector.js
CHANGED
|
@@ -19,8 +19,31 @@ export async function detectScenario(projectRoot) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export async function scanExistingSetup(projectRoot) {
|
|
22
|
-
const
|
|
23
|
-
|
|
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
|
|
36
|
-
hasMcpJson
|
|
37
|
-
existingSkills
|
|
38
|
-
existingSkillDirs
|
|
39
|
-
existingAgents
|
|
40
|
-
existingCommands
|
|
41
|
-
|
|
42
|
-
|
|
58
|
+
hasSettingsJson,
|
|
59
|
+
hasMcpJson,
|
|
60
|
+
existingSkills,
|
|
61
|
+
existingSkillDirs,
|
|
62
|
+
existingAgents,
|
|
63
|
+
existingCommands,
|
|
64
|
+
hasAgentsMd,
|
|
65
|
+
hasProgressMd,
|
|
66
|
+
hasSpecMd,
|
|
43
67
|
};
|
|
44
68
|
}
|
package/src/core/merger.js
CHANGED
|
@@ -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 {
|
|
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;
|
package/src/core/scaffolder.js
CHANGED
|
@@ -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 || [];
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Agent routing metadata for
|
|
2
|
+
* Agent routing metadata for every agent in the registry.
|
|
3
3
|
* Used by the agent-routing generator to produce the routing skill file.
|
|
4
4
|
* Separate from agents.js because this data is only consumed by the generator,
|
|
5
5
|
* not by CLI prompts or display logic.
|
|
6
6
|
*/
|
|
7
7
|
export const AGENT_REGISTRY = {
|
|
8
|
-
// --- Universal agents
|
|
8
|
+
// --- Universal agents ---
|
|
9
9
|
|
|
10
10
|
'plan-reviewer': {
|
|
11
11
|
category: 'universal',
|
|
@@ -73,6 +73,21 @@ export const AGENT_REGISTRY = {
|
|
|
73
73
|
expectBack: 'Detailed verification report. Blocking issues listed.',
|
|
74
74
|
situationLabel: 'Finished a task, ready for PR',
|
|
75
75
|
},
|
|
76
|
+
'upstream-watcher': {
|
|
77
|
+
category: 'universal',
|
|
78
|
+
model: 'Sonnet',
|
|
79
|
+
isolation: 'none',
|
|
80
|
+
pipelineStage: 'Stage 1: Context',
|
|
81
|
+
triggerType: 'manual',
|
|
82
|
+
triggerCommand: '/upstream-check',
|
|
83
|
+
whenToUse:
|
|
84
|
+
'At the start of a session to check for upstream Claude Code changes. After Claude Code updates. When behavior seems different from last session.',
|
|
85
|
+
whatItDoes:
|
|
86
|
+
"Fetches anthropic-watch feeds, cross-references upstream changes against the project's scaffolded agents/commands/hooks/skills, and produces an impact report.",
|
|
87
|
+
expectBack:
|
|
88
|
+
'Impact report: which upstream changes affect this project, which are informational, and recommended actions.',
|
|
89
|
+
situationLabel: 'Want to check for upstream Claude Code changes',
|
|
90
|
+
},
|
|
76
91
|
|
|
77
92
|
// --- Frontend agents (2) ---
|
|
78
93
|
|
package/src/data/agents.js
CHANGED
|
@@ -4,6 +4,7 @@ export const UNIVERSAL_AGENTS = [
|
|
|
4
4
|
'test-writer',
|
|
5
5
|
'build-validator',
|
|
6
6
|
'verify-app',
|
|
7
|
+
'upstream-watcher',
|
|
7
8
|
];
|
|
8
9
|
|
|
9
10
|
export const AGENT_CATALOG = {
|
|
@@ -194,6 +195,8 @@ export const COMMAND_FILES = [
|
|
|
194
195
|
'build-fix',
|
|
195
196
|
'refactor-clean',
|
|
196
197
|
'test-coverage',
|
|
198
|
+
'learn',
|
|
199
|
+
'upstream-check',
|
|
197
200
|
];
|
|
198
201
|
|
|
199
202
|
export const UNIVERSAL_SKILLS = [
|
|
@@ -205,6 +208,7 @@ export const UNIVERSAL_SKILLS = [
|
|
|
205
208
|
'verification',
|
|
206
209
|
'testing',
|
|
207
210
|
'claude-md-maintenance',
|
|
211
|
+
'coding-principles',
|
|
208
212
|
'subagent-usage',
|
|
209
213
|
'security-checklist',
|
|
210
214
|
'coordinator-mode',
|
|
@@ -216,6 +220,8 @@ export const TEMPLATE_SKILLS = [
|
|
|
216
220
|
'project-patterns',
|
|
217
221
|
];
|
|
218
222
|
|
|
223
|
+
export const HOOK_FILES = ['pre-compact-save', 'correction-detect', 'learn-capture', 'skill-hint'];
|
|
224
|
+
|
|
219
225
|
export const PROJECT_TYPES = [
|
|
220
226
|
'Full-stack web application',
|
|
221
227
|
'Backend / API',
|
|
@@ -315,6 +321,7 @@ export const CONFIRMATION_STEPS = [
|
|
|
315
321
|
{ name: 'Project type', value: 'projectType' },
|
|
316
322
|
{ name: 'Tech stack', value: 'techStack' },
|
|
317
323
|
{ name: 'Agent selection', value: 'agents' },
|
|
324
|
+
{ name: 'Optional extras (plugin.json, memory)', value: 'optionalExtras' },
|
|
318
325
|
];
|
|
319
326
|
|
|
320
327
|
export const NOTIFICATION_COMMANDS = {
|
package/src/index.js
CHANGED
|
@@ -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
|