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.
- package/CHANGELOG.md +97 -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/agents.js +5 -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/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/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 || [];
|
package/src/data/agents.js
CHANGED
|
@@ -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
|
@@ -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"
|