worclaude 2.0.0 → 2.1.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/README.md +42 -3
- package/package.json +1 -1
- package/src/commands/delete.js +6 -9
- package/src/commands/diff.js +4 -5
- package/src/commands/doctor.js +159 -41
- package/src/commands/init.js +128 -102
- package/src/commands/status.js +4 -5
- package/src/commands/upgrade.js +6 -10
- package/src/core/config.js +11 -0
- package/src/utils/display.js +0 -9
- package/templates/agents/optional/quality/security-reviewer.md +3 -0
- package/templates/agents/universal/plan-reviewer.md +1 -0
- package/templates/agents/universal/test-writer.md +2 -0
- package/templates/agents/universal/verify-app.md +2 -0
- package/templates/core/memory-md.md +28 -0
- package/templates/skills/universal/claude-md-maintenance.md +1 -0
- package/templates/skills/universal/context-management.md +1 -0
- package/templates/skills/universal/coordinator-mode.md +1 -0
- package/templates/skills/universal/git-conventions.md +1 -0
- package/templates/skills/universal/planning-with-files.md +1 -0
- package/templates/skills/universal/prompt-engineering.md +1 -0
- package/templates/skills/universal/review-and-handoff.md +1 -0
- package/templates/skills/universal/security-checklist.md +1 -0
- package/templates/skills/universal/subagent-usage.md +1 -0
- package/templates/skills/universal/testing.md +1 -0
- package/templates/skills/universal/verification.md +1 -0
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
[Full Documentation](https://sefaertunc.github.io/Worclaude/) · [Interactive Demo](https://sefaertunc.github.io/Worclaude/demo/) · [npm](https://www.npmjs.com/package/worclaude)
|
|
12
12
|
|
|
13
|
-
Worclaude scaffolds a complete Claude Code workflow into any project in seconds. It implements all [tips by Boris Cherny](https://www.howborisusesclaudecode.com/) — the creator of Claude Code at Anthropic — as a reusable, upgradable scaffold. One `init` command gives you 25 agents, 16 slash commands,
|
|
13
|
+
Worclaude scaffolds a complete Claude Code workflow into any project in seconds. It implements all [tips by Boris Cherny](https://www.howborisusesclaudecode.com/) — the creator of Claude Code at Anthropic — as a reusable, upgradable scaffold. One `init` command gives you 25 agents, 16 slash commands, 15 skills, hooks, permissions, and a CLAUDE.md template tuned for your tech stack. Whether you're starting fresh or adding structure to an existing project, Worclaude handles the setup so you can focus on building.
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -26,9 +26,9 @@ Worclaude scaffolds a complete Claude Code workflow into any project in seconds.
|
|
|
26
26
|
**Slash Commands (16)**
|
|
27
27
|
`/start` `/end` `/commit-push-pr` `/review-plan` `/techdebt` `/verify` `/compact-safe` `/status` `/update-claude-md` `/setup` `/sync` `/conflict-resolver` `/review-changes` `/build-fix` `/refactor-clean` `/test-coverage`
|
|
28
28
|
|
|
29
|
-
**Skills (
|
|
29
|
+
**Skills (15)**
|
|
30
30
|
|
|
31
|
-
-
|
|
31
|
+
- 11 universal knowledge files (testing, git conventions, context management, security, coordinator mode, and more)
|
|
32
32
|
- 3 project-specific templates filled in by `/setup`
|
|
33
33
|
- 1 generated agent routing guide (dynamically built from your agent selection)
|
|
34
34
|
|
|
@@ -48,6 +48,45 @@ Worclaude scaffolds a complete Claude Code workflow into any project in seconds.
|
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
51
|
+
## What's New in v2.0.0
|
|
52
|
+
|
|
53
|
+
v2.0.0 is a major integration release. Skills and agents now register with Claude Code's runtime systems — they show up in `/skills` and `/agents`, not just as files on disk.
|
|
54
|
+
|
|
55
|
+
**Claude Code Runtime Integration**
|
|
56
|
+
|
|
57
|
+
- Skills use directory format (`skill-name/SKILL.md`) and register with `/skills`
|
|
58
|
+
- Agents include `description` frontmatter and register with `/agents`
|
|
59
|
+
- Run `worclaude doctor` to verify everything is wired up correctly
|
|
60
|
+
|
|
61
|
+
**Conditional Skills**
|
|
62
|
+
|
|
63
|
+
- 6 skills activate only when relevant files are touched (testing, security, frontend, backend, verification, project-patterns)
|
|
64
|
+
- 8 skills stay always-loaded for cross-cutting guidance
|
|
65
|
+
- Saves context window budget by keeping domain-specific guidance out of unrelated work
|
|
66
|
+
|
|
67
|
+
**Agent Enhancements**
|
|
68
|
+
|
|
69
|
+
- Read-only agents blocked from file modifications via `disallowedTools`
|
|
70
|
+
- Background execution for long-running agents (verify-app, build-validator, e2e-runner)
|
|
71
|
+
- Turn limits (`maxTurns`) prevent runaway token consumption
|
|
72
|
+
- Persistent memory for agents that learn across sessions (test-writer, security-reviewer, doc-writer)
|
|
73
|
+
|
|
74
|
+
**New Content**
|
|
75
|
+
|
|
76
|
+
- Coordinator-mode skill for multi-agent orchestration patterns
|
|
77
|
+
- Optional MEMORY.md template for Claude Code's memory system
|
|
78
|
+
- Enhanced verify-app agent with structured verdicts and adversarial probe requirements
|
|
79
|
+
- Per-tech-stack permission presets (16 languages)
|
|
80
|
+
|
|
81
|
+
**Upgrade Migration**
|
|
82
|
+
|
|
83
|
+
- `worclaude upgrade` auto-migrates v1.x projects: flat skills → directory format, agents get required frontmatter
|
|
84
|
+
- `worclaude doctor` detects old formats and missing fields
|
|
85
|
+
|
|
86
|
+
See the [Claude Code Integration guide](https://sefaertunc.github.io/Worclaude/guide/claude-code-integration) for technical details.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
51
90
|
## Quick Start
|
|
52
91
|
|
|
53
92
|
```bash
|
package/package.json
CHANGED
package/src/commands/delete.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
3
|
import ora from 'ora';
|
|
4
|
-
import {
|
|
4
|
+
import { requireWorkflowMeta } from '../core/config.js';
|
|
5
5
|
import { createBackup } from '../core/backup.js';
|
|
6
6
|
import {
|
|
7
7
|
classifyClaudeFiles,
|
|
@@ -16,16 +16,13 @@ export async function deleteCommand() {
|
|
|
16
16
|
const projectRoot = process.cwd();
|
|
17
17
|
|
|
18
18
|
// Pre-flight: ensure worclaude is installed
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
display.info('Run `worclaude init` to set up
|
|
19
|
+
const { meta, error } = await requireWorkflowMeta(projectRoot);
|
|
20
|
+
if (error === 'not-installed') {
|
|
21
|
+
display.info('Workflow is not installed. Run `worclaude init` to set up.');
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (!meta) {
|
|
27
|
-
display.error('workflow-meta.json is corrupted or unreadable.');
|
|
28
|
-
display.info('You may need to manually remove the .claude/ directory.');
|
|
24
|
+
if (error === 'corrupted') {
|
|
25
|
+
display.error('workflow-meta.json is corrupted. Run `worclaude init` to reinstall.');
|
|
29
26
|
return;
|
|
30
27
|
}
|
|
31
28
|
|
package/src/commands/diff.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requireWorkflowMeta, getPackageVersion } from '../core/config.js';
|
|
2
2
|
import { categorizeFiles } from '../core/file-categorizer.js';
|
|
3
3
|
import * as display from '../utils/display.js';
|
|
4
4
|
|
|
5
5
|
export async function diffCommand() {
|
|
6
6
|
const projectRoot = process.cwd();
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const { meta, error } = await requireWorkflowMeta(projectRoot);
|
|
9
|
+
if (error === 'not-installed') {
|
|
9
10
|
display.info('Workflow is not installed. Run `worclaude init` to set up.');
|
|
10
11
|
return;
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
-
const meta = await readWorkflowMeta(projectRoot);
|
|
14
|
-
if (!meta) {
|
|
13
|
+
if (error === 'corrupted') {
|
|
15
14
|
display.error('workflow-meta.json is corrupted. Run `worclaude init` to reinstall.');
|
|
16
15
|
return;
|
|
17
16
|
}
|
package/src/commands/doctor.js
CHANGED
|
@@ -177,59 +177,57 @@ async function checkAgents(projectRoot, meta) {
|
|
|
177
177
|
return results;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
async function
|
|
180
|
+
async function readAgentFrontmatters(projectRoot) {
|
|
181
181
|
const agentsDir = path.join(projectRoot, '.claude', 'agents');
|
|
182
|
-
const results = [];
|
|
183
|
-
|
|
184
182
|
try {
|
|
185
183
|
const entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
|
186
184
|
const mdFiles = entries.filter((e) => e.isFile() && e.name.endsWith('.md'));
|
|
187
|
-
|
|
185
|
+
const agents = [];
|
|
188
186
|
for (const file of mdFiles) {
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
`agents/${file.name}`,
|
|
199
|
-
'No YAML frontmatter — agent is invisible to Claude Code'
|
|
200
|
-
)
|
|
201
|
-
);
|
|
202
|
-
continue;
|
|
203
|
-
}
|
|
187
|
+
const content = await readFile(path.join(agentsDir, file.name));
|
|
188
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
189
|
+
agents.push({ name: file.name, frontmatter: match ? match[1] : null });
|
|
190
|
+
}
|
|
191
|
+
return agents;
|
|
192
|
+
} catch {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
204
196
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
FAIL,
|
|
217
|
-
`agents/${file.name}`,
|
|
218
|
-
'Missing required "description" field — agent is invisible to Claude Code\'s /agents and routing'
|
|
219
|
-
)
|
|
220
|
-
);
|
|
221
|
-
}
|
|
197
|
+
async function checkAgentDescription(projectRoot) {
|
|
198
|
+
const agents = await readAgentFrontmatters(projectRoot);
|
|
199
|
+
if (agents.length === 0) return [];
|
|
200
|
+
|
|
201
|
+
const results = [];
|
|
202
|
+
for (const { name, frontmatter } of agents) {
|
|
203
|
+
if (!frontmatter) {
|
|
204
|
+
results.push(
|
|
205
|
+
result(FAIL, `agents/${name}`, 'No YAML frontmatter — agent is invisible to Claude Code')
|
|
206
|
+
);
|
|
207
|
+
continue;
|
|
222
208
|
}
|
|
223
209
|
|
|
224
|
-
|
|
210
|
+
const hasName = /^name:\s*.+/m.test(frontmatter);
|
|
211
|
+
const hasDescription = /^description:\s*.+/m.test(frontmatter);
|
|
212
|
+
|
|
213
|
+
if (!hasName) {
|
|
214
|
+
results.push(result(FAIL, `agents/${name}`, 'Missing required "name" field in frontmatter'));
|
|
215
|
+
} else if (!hasDescription) {
|
|
225
216
|
results.push(
|
|
226
|
-
result(
|
|
217
|
+
result(
|
|
218
|
+
FAIL,
|
|
219
|
+
`agents/${name}`,
|
|
220
|
+
'Missing required "description" field — agent is invisible to Claude Code\'s /agents and routing'
|
|
221
|
+
)
|
|
227
222
|
);
|
|
228
223
|
}
|
|
229
|
-
} catch {
|
|
230
|
-
// agents/ doesn't exist — covered by existing component checks
|
|
231
224
|
}
|
|
232
225
|
|
|
226
|
+
if (results.length === 0) {
|
|
227
|
+
results.push(
|
|
228
|
+
result(PASS, `agents/ frontmatter (${agents.length} agents have required fields)`, null)
|
|
229
|
+
);
|
|
230
|
+
}
|
|
233
231
|
return results;
|
|
234
232
|
}
|
|
235
233
|
|
|
@@ -385,6 +383,124 @@ async function checkDocSpecs(projectRoot) {
|
|
|
385
383
|
return results;
|
|
386
384
|
}
|
|
387
385
|
|
|
386
|
+
const AGENT_OPTIONAL_FIELDS = [
|
|
387
|
+
'model',
|
|
388
|
+
'isolation',
|
|
389
|
+
'maxTurns',
|
|
390
|
+
'disallowedTools',
|
|
391
|
+
'background',
|
|
392
|
+
'memory',
|
|
393
|
+
'skills',
|
|
394
|
+
'initialPrompt',
|
|
395
|
+
'criticalSystemReminder',
|
|
396
|
+
'omitClaudeMd',
|
|
397
|
+
];
|
|
398
|
+
|
|
399
|
+
async function checkAgentCompleteness(projectRoot) {
|
|
400
|
+
const agents = await readAgentFrontmatters(projectRoot);
|
|
401
|
+
const withFrontmatter = agents.filter((a) => a.frontmatter);
|
|
402
|
+
if (withFrontmatter.length === 0) return [];
|
|
403
|
+
|
|
404
|
+
let totalFields = 0;
|
|
405
|
+
const suggestions = [];
|
|
406
|
+
|
|
407
|
+
for (const { name, frontmatter } of withFrontmatter) {
|
|
408
|
+
let fieldCount = 0;
|
|
409
|
+
for (const field of AGENT_OPTIONAL_FIELDS) {
|
|
410
|
+
if (new RegExp(`^${field}:`, 'm').test(frontmatter)) fieldCount++;
|
|
411
|
+
}
|
|
412
|
+
totalFields += fieldCount;
|
|
413
|
+
|
|
414
|
+
const hasDisallowed = /^disallowedTools:/m.test(frontmatter);
|
|
415
|
+
const hasReminder = /^criticalSystemReminder:/m.test(frontmatter);
|
|
416
|
+
if (hasDisallowed && !hasReminder) {
|
|
417
|
+
suggestions.push(`${name}: read-only agent could benefit from criticalSystemReminder`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const totalPossible = withFrontmatter.length * AGENT_OPTIONAL_FIELDS.length;
|
|
422
|
+
const pct = Math.round((totalFields / totalPossible) * 100);
|
|
423
|
+
const results = [];
|
|
424
|
+
|
|
425
|
+
if (pct >= 50) {
|
|
426
|
+
results.push(
|
|
427
|
+
result(
|
|
428
|
+
PASS,
|
|
429
|
+
`Agent enrichment: ${pct}% optional fields used across ${withFrontmatter.length} agents`,
|
|
430
|
+
null
|
|
431
|
+
)
|
|
432
|
+
);
|
|
433
|
+
} else {
|
|
434
|
+
results.push(
|
|
435
|
+
result(
|
|
436
|
+
WARN,
|
|
437
|
+
`Agent enrichment: ${pct}% optional fields used across ${withFrontmatter.length} agents`,
|
|
438
|
+
'Run `worclaude upgrade` to add model, maxTurns, skills, and other metadata'
|
|
439
|
+
)
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
for (const s of suggestions) {
|
|
444
|
+
results.push(result(WARN, s, null));
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return results;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
async function checkClaudeMdSections(projectRoot) {
|
|
451
|
+
const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
|
|
452
|
+
const SECTION_THRESHOLD = 20000; // Only analyze sections if file > 20KB
|
|
453
|
+
const results = [];
|
|
454
|
+
|
|
455
|
+
try {
|
|
456
|
+
const content = await readFile(claudeMdPath);
|
|
457
|
+
if (content.length < SECTION_THRESHOLD) return results;
|
|
458
|
+
|
|
459
|
+
// Split into ## sections
|
|
460
|
+
const sections = [];
|
|
461
|
+
const lines = content.split('\n');
|
|
462
|
+
let currentHeading = '(top-level)';
|
|
463
|
+
let currentLines = [];
|
|
464
|
+
|
|
465
|
+
for (const line of lines) {
|
|
466
|
+
const headingMatch = line.match(/^##\s+(.+)/);
|
|
467
|
+
if (headingMatch) {
|
|
468
|
+
if (currentLines.length > 0) {
|
|
469
|
+
sections.push({ heading: currentHeading, size: currentLines.join('\n').length });
|
|
470
|
+
}
|
|
471
|
+
currentHeading = headingMatch[1];
|
|
472
|
+
currentLines = [];
|
|
473
|
+
} else {
|
|
474
|
+
currentLines.push(line);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (currentLines.length > 0) {
|
|
478
|
+
sections.push({ heading: currentHeading, size: currentLines.join('\n').length });
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Sort by size, suggest extracting the top 3 sections > 2KB
|
|
482
|
+
const large = sections.filter((s) => s.size > 2000).sort((a, b) => b.size - a.size);
|
|
483
|
+
|
|
484
|
+
if (large.length > 0) {
|
|
485
|
+
const top = large.slice(0, 3);
|
|
486
|
+
const sectionList = top
|
|
487
|
+
.map((s) => `"${s.heading}" (${(s.size / 1024).toFixed(1)}KB)`)
|
|
488
|
+
.join(', ');
|
|
489
|
+
results.push(
|
|
490
|
+
result(
|
|
491
|
+
WARN,
|
|
492
|
+
`CLAUDE.md has large sections: ${sectionList}`,
|
|
493
|
+
'Consider extracting to conditional skills with paths frontmatter to save context budget'
|
|
494
|
+
)
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
} catch {
|
|
498
|
+
// Already covered by checkClaudeMd
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return results;
|
|
502
|
+
}
|
|
503
|
+
|
|
388
504
|
async function checkPendingReviewFiles(projectRoot) {
|
|
389
505
|
const pending = [];
|
|
390
506
|
try {
|
|
@@ -429,6 +545,7 @@ export async function doctorCommand() {
|
|
|
429
545
|
|
|
430
546
|
printResult(await checkClaudeMd(projectRoot));
|
|
431
547
|
for (const r of await checkClaudeMdSize(projectRoot)) printResult(r);
|
|
548
|
+
for (const r of await checkClaudeMdSections(projectRoot)) printResult(r);
|
|
432
549
|
printResult(await checkSettingsJson(projectRoot));
|
|
433
550
|
printResult(await checkSessions(projectRoot));
|
|
434
551
|
display.newline();
|
|
@@ -440,6 +557,7 @@ export async function doctorCommand() {
|
|
|
440
557
|
for (const r of await checkCommands(projectRoot)) printResult(r);
|
|
441
558
|
for (const r of await checkSkills(projectRoot)) printResult(r);
|
|
442
559
|
for (const r of await checkSkillFormat(projectRoot)) printResult(r);
|
|
560
|
+
for (const r of await checkAgentCompleteness(projectRoot)) printResult(r);
|
|
443
561
|
display.newline();
|
|
444
562
|
|
|
445
563
|
// Docs
|
|
@@ -455,7 +573,7 @@ export async function doctorCommand() {
|
|
|
455
573
|
|
|
456
574
|
// Summary
|
|
457
575
|
if (metaResult.status === FAIL) {
|
|
458
|
-
display.
|
|
576
|
+
display.info('Workflow is not installed. Run `worclaude init` to set up.');
|
|
459
577
|
} else {
|
|
460
578
|
display.success('Doctor complete. Review any warnings above.');
|
|
461
579
|
}
|
package/src/commands/init.js
CHANGED
|
@@ -31,111 +31,137 @@ import { buildAgentRoutingSkill } from '../generators/agent-routing.js';
|
|
|
31
31
|
|
|
32
32
|
// --- Helper functions ---
|
|
33
33
|
|
|
34
|
+
const LANGUAGE_COMMANDS = {
|
|
35
|
+
python: {
|
|
36
|
+
heading: 'Python',
|
|
37
|
+
commands: [
|
|
38
|
+
'python -m pytest # Run tests',
|
|
39
|
+
'ruff check . # Lint',
|
|
40
|
+
'ruff format . # Format',
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
node: {
|
|
44
|
+
heading: 'Node.js / TypeScript',
|
|
45
|
+
commands: [
|
|
46
|
+
'npm test # Run tests',
|
|
47
|
+
'npx eslint . # Lint',
|
|
48
|
+
'npx prettier --write . # Format',
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
java: {
|
|
52
|
+
heading: 'Java',
|
|
53
|
+
commands: [
|
|
54
|
+
'mvn test # Run tests',
|
|
55
|
+
'mvn checkstyle:check # Lint',
|
|
56
|
+
'mvn spotless:apply # Format',
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
csharp: {
|
|
60
|
+
heading: 'C# / .NET',
|
|
61
|
+
commands: [
|
|
62
|
+
'dotnet test # Run tests',
|
|
63
|
+
'dotnet format --verify-no-changes # Lint',
|
|
64
|
+
'dotnet format # Format',
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
cpp: {
|
|
68
|
+
heading: 'C / C++',
|
|
69
|
+
commands: [
|
|
70
|
+
'cmake --build build && ctest # Build & test',
|
|
71
|
+
'clang-tidy src/*.cpp # Lint',
|
|
72
|
+
'clang-format -i src/*.[ch]pp # Format',
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
go: {
|
|
76
|
+
heading: 'Go',
|
|
77
|
+
commands: [
|
|
78
|
+
'go test ./... # Run tests',
|
|
79
|
+
'golangci-lint run # Lint',
|
|
80
|
+
'gofmt -w . # Format',
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
php: {
|
|
84
|
+
heading: 'PHP',
|
|
85
|
+
commands: [
|
|
86
|
+
'vendor/bin/phpunit # Run tests',
|
|
87
|
+
'vendor/bin/phpstan analyse # Lint',
|
|
88
|
+
'vendor/bin/php-cs-fixer fix . # Format',
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
ruby: {
|
|
92
|
+
heading: 'Ruby',
|
|
93
|
+
commands: [
|
|
94
|
+
'bundle exec rspec # Run tests',
|
|
95
|
+
'rubocop # Lint',
|
|
96
|
+
'rubocop -A # Format',
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
kotlin: {
|
|
100
|
+
heading: 'Kotlin',
|
|
101
|
+
commands: [
|
|
102
|
+
'gradle test # Run tests',
|
|
103
|
+
'detekt # Lint',
|
|
104
|
+
'ktlint -F # Format',
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
swift: {
|
|
108
|
+
heading: 'Swift',
|
|
109
|
+
commands: [
|
|
110
|
+
'swift test # Run tests',
|
|
111
|
+
'swiftlint # Lint',
|
|
112
|
+
'swift-format format -r . -i # Format',
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
rust: {
|
|
116
|
+
heading: 'Rust',
|
|
117
|
+
commands: [
|
|
118
|
+
'cargo test # Run tests',
|
|
119
|
+
'cargo clippy # Lint',
|
|
120
|
+
'cargo fmt # Format',
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
dart: {
|
|
124
|
+
heading: 'Dart / Flutter',
|
|
125
|
+
commands: [
|
|
126
|
+
'dart test # Run tests',
|
|
127
|
+
'dart analyze # Lint',
|
|
128
|
+
'dart format . # Format',
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
scala: {
|
|
132
|
+
heading: 'Scala',
|
|
133
|
+
commands: [
|
|
134
|
+
'sbt test # Run tests',
|
|
135
|
+
'sbt scalafix # Lint',
|
|
136
|
+
'scalafmt # Format',
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
elixir: {
|
|
140
|
+
heading: 'Elixir',
|
|
141
|
+
commands: [
|
|
142
|
+
'mix test # Run tests',
|
|
143
|
+
'mix credo # Lint',
|
|
144
|
+
'mix format # Format',
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
zig: {
|
|
148
|
+
heading: 'Zig',
|
|
149
|
+
commands: [
|
|
150
|
+
'zig build test # Run tests',
|
|
151
|
+
'zig build # Build (lint via compiler)',
|
|
152
|
+
'zig fmt . # Format',
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
|
|
34
157
|
function buildCommandsBlock(languages, useDocker) {
|
|
35
158
|
const lines = ['```bash'];
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
lines.push('ruff check . # Lint');
|
|
40
|
-
lines.push('ruff format . # Format');
|
|
41
|
-
}
|
|
42
|
-
if (languages.includes('node')) {
|
|
43
|
-
if (lines.length > 1) lines.push('');
|
|
44
|
-
lines.push('# Node.js / TypeScript');
|
|
45
|
-
lines.push('npm test # Run tests');
|
|
46
|
-
lines.push('npx eslint . # Lint');
|
|
47
|
-
lines.push('npx prettier --write . # Format');
|
|
48
|
-
}
|
|
49
|
-
if (languages.includes('java')) {
|
|
50
|
-
if (lines.length > 1) lines.push('');
|
|
51
|
-
lines.push('# Java');
|
|
52
|
-
lines.push('mvn test # Run tests');
|
|
53
|
-
lines.push('mvn checkstyle:check # Lint');
|
|
54
|
-
lines.push('mvn spotless:apply # Format');
|
|
55
|
-
}
|
|
56
|
-
if (languages.includes('csharp')) {
|
|
57
|
-
if (lines.length > 1) lines.push('');
|
|
58
|
-
lines.push('# C# / .NET');
|
|
59
|
-
lines.push('dotnet test # Run tests');
|
|
60
|
-
lines.push('dotnet format --verify-no-changes # Lint');
|
|
61
|
-
lines.push('dotnet format # Format');
|
|
62
|
-
}
|
|
63
|
-
if (languages.includes('cpp')) {
|
|
64
|
-
if (lines.length > 1) lines.push('');
|
|
65
|
-
lines.push('# C / C++');
|
|
66
|
-
lines.push('cmake --build build && ctest # Build & test');
|
|
67
|
-
lines.push('clang-tidy src/*.cpp # Lint');
|
|
68
|
-
lines.push('clang-format -i src/*.[ch]pp # Format');
|
|
69
|
-
}
|
|
70
|
-
if (languages.includes('go')) {
|
|
71
|
-
if (lines.length > 1) lines.push('');
|
|
72
|
-
lines.push('# Go');
|
|
73
|
-
lines.push('go test ./... # Run tests');
|
|
74
|
-
lines.push('golangci-lint run # Lint');
|
|
75
|
-
lines.push('gofmt -w . # Format');
|
|
76
|
-
}
|
|
77
|
-
if (languages.includes('php')) {
|
|
78
|
-
if (lines.length > 1) lines.push('');
|
|
79
|
-
lines.push('# PHP');
|
|
80
|
-
lines.push('vendor/bin/phpunit # Run tests');
|
|
81
|
-
lines.push('vendor/bin/phpstan analyse # Lint');
|
|
82
|
-
lines.push('vendor/bin/php-cs-fixer fix . # Format');
|
|
83
|
-
}
|
|
84
|
-
if (languages.includes('ruby')) {
|
|
85
|
-
if (lines.length > 1) lines.push('');
|
|
86
|
-
lines.push('# Ruby');
|
|
87
|
-
lines.push('bundle exec rspec # Run tests');
|
|
88
|
-
lines.push('rubocop # Lint');
|
|
89
|
-
lines.push('rubocop -A # Format');
|
|
90
|
-
}
|
|
91
|
-
if (languages.includes('kotlin')) {
|
|
92
|
-
if (lines.length > 1) lines.push('');
|
|
93
|
-
lines.push('# Kotlin');
|
|
94
|
-
lines.push('gradle test # Run tests');
|
|
95
|
-
lines.push('detekt # Lint');
|
|
96
|
-
lines.push('ktlint -F # Format');
|
|
97
|
-
}
|
|
98
|
-
if (languages.includes('swift')) {
|
|
99
|
-
if (lines.length > 1) lines.push('');
|
|
100
|
-
lines.push('# Swift');
|
|
101
|
-
lines.push('swift test # Run tests');
|
|
102
|
-
lines.push('swiftlint # Lint');
|
|
103
|
-
lines.push('swift-format format -r . -i # Format');
|
|
104
|
-
}
|
|
105
|
-
if (languages.includes('rust')) {
|
|
106
|
-
if (lines.length > 1) lines.push('');
|
|
107
|
-
lines.push('# Rust');
|
|
108
|
-
lines.push('cargo test # Run tests');
|
|
109
|
-
lines.push('cargo clippy # Lint');
|
|
110
|
-
lines.push('cargo fmt # Format');
|
|
111
|
-
}
|
|
112
|
-
if (languages.includes('dart')) {
|
|
113
|
-
if (lines.length > 1) lines.push('');
|
|
114
|
-
lines.push('# Dart / Flutter');
|
|
115
|
-
lines.push('dart test # Run tests');
|
|
116
|
-
lines.push('dart analyze # Lint');
|
|
117
|
-
lines.push('dart format . # Format');
|
|
118
|
-
}
|
|
119
|
-
if (languages.includes('scala')) {
|
|
120
|
-
if (lines.length > 1) lines.push('');
|
|
121
|
-
lines.push('# Scala');
|
|
122
|
-
lines.push('sbt test # Run tests');
|
|
123
|
-
lines.push('sbt scalafix # Lint');
|
|
124
|
-
lines.push('scalafmt # Format');
|
|
125
|
-
}
|
|
126
|
-
if (languages.includes('elixir')) {
|
|
127
|
-
if (lines.length > 1) lines.push('');
|
|
128
|
-
lines.push('# Elixir');
|
|
129
|
-
lines.push('mix test # Run tests');
|
|
130
|
-
lines.push('mix credo # Lint');
|
|
131
|
-
lines.push('mix format # Format');
|
|
132
|
-
}
|
|
133
|
-
if (languages.includes('zig')) {
|
|
159
|
+
for (const lang of languages) {
|
|
160
|
+
const entry = LANGUAGE_COMMANDS[lang];
|
|
161
|
+
if (!entry) continue;
|
|
134
162
|
if (lines.length > 1) lines.push('');
|
|
135
|
-
lines.push(
|
|
136
|
-
lines.push(
|
|
137
|
-
lines.push('zig build # Build (lint via compiler)');
|
|
138
|
-
lines.push('zig fmt . # Format');
|
|
163
|
+
lines.push(`# ${entry.heading}`);
|
|
164
|
+
lines.push(...entry.commands);
|
|
139
165
|
}
|
|
140
166
|
if (useDocker) {
|
|
141
167
|
if (lines.length > 1) lines.push('');
|
package/src/commands/status.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { requireWorkflowMeta, getPackageVersion } from '../core/config.js';
|
|
3
3
|
import { hashFile } from '../utils/hash.js';
|
|
4
4
|
import { fileExists, readFile, listFilesRecursive } from '../utils/file.js';
|
|
5
5
|
import { getLatestNpmVersion } from '../utils/npm.js';
|
|
@@ -15,13 +15,12 @@ function countByPrefix(fileHashes, prefix) {
|
|
|
15
15
|
export async function statusCommand() {
|
|
16
16
|
const projectRoot = process.cwd();
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
const { meta, error } = await requireWorkflowMeta(projectRoot);
|
|
19
|
+
if (error === 'not-installed') {
|
|
19
20
|
display.info('Workflow is not installed. Run `worclaude init` to set up.');
|
|
20
21
|
return;
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
-
const meta = await readWorkflowMeta(projectRoot);
|
|
24
|
-
if (!meta) {
|
|
23
|
+
if (error === 'corrupted') {
|
|
25
24
|
display.error('workflow-meta.json is corrupted. Run `worclaude init` to reinstall.');
|
|
26
25
|
return;
|
|
27
26
|
}
|
package/src/commands/upgrade.js
CHANGED
|
@@ -4,8 +4,7 @@ import inquirer from 'inquirer';
|
|
|
4
4
|
import ora from 'ora';
|
|
5
5
|
import {
|
|
6
6
|
computeFileHashes,
|
|
7
|
-
|
|
8
|
-
workflowMetaExists,
|
|
7
|
+
requireWorkflowMeta,
|
|
9
8
|
writeWorkflowMeta,
|
|
10
9
|
getPackageVersion,
|
|
11
10
|
} from '../core/config.js';
|
|
@@ -70,16 +69,13 @@ export async function upgradeCommand() {
|
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
// 2. Check prerequisite
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
display.info('Run `worclaude init` to set up
|
|
72
|
+
const { meta, error } = await requireWorkflowMeta(projectRoot);
|
|
73
|
+
if (error === 'not-installed') {
|
|
74
|
+
display.info('Workflow is not installed. Run `worclaude init` to set up.');
|
|
76
75
|
return;
|
|
77
76
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (!meta) {
|
|
81
|
-
display.error('workflow-meta.json is corrupted or invalid.');
|
|
82
|
-
display.info('Run `worclaude init` to reinstall (a backup will be created first).');
|
|
77
|
+
if (error === 'corrupted') {
|
|
78
|
+
display.error('workflow-meta.json is corrupted. Run `worclaude init` to reinstall.');
|
|
83
79
|
return;
|
|
84
80
|
}
|
|
85
81
|
|
package/src/core/config.js
CHANGED
|
@@ -59,6 +59,17 @@ export async function writeWorkflowMeta(projectRoot, meta) {
|
|
|
59
59
|
await writeFile(metaPath, JSON.stringify(meta, null, 2));
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
export async function requireWorkflowMeta(projectRoot) {
|
|
63
|
+
if (!(await workflowMetaExists(projectRoot))) {
|
|
64
|
+
return { meta: null, error: 'not-installed' };
|
|
65
|
+
}
|
|
66
|
+
const meta = await readWorkflowMeta(projectRoot);
|
|
67
|
+
if (!meta) {
|
|
68
|
+
return { meta: null, error: 'corrupted' };
|
|
69
|
+
}
|
|
70
|
+
return { meta, error: null };
|
|
71
|
+
}
|
|
72
|
+
|
|
62
73
|
export async function computeFileHashes(projectRoot) {
|
|
63
74
|
const claudeDir = path.join(projectRoot, '.claude');
|
|
64
75
|
const allFiles = await listFilesRecursive(claudeDir);
|
package/src/utils/display.js
CHANGED
|
@@ -64,15 +64,6 @@ export const MODEL_BADGES = {
|
|
|
64
64
|
haiku: badges.haiku,
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
export const CATEGORY_BADGES = {
|
|
68
|
-
Backend: badges.backend,
|
|
69
|
-
Frontend: badges.frontend,
|
|
70
|
-
DevOps: badges.devops,
|
|
71
|
-
Quality: badges.quality,
|
|
72
|
-
Documentation: badges.docs,
|
|
73
|
-
'Data / AI': badges.dataai,
|
|
74
|
-
};
|
|
75
|
-
|
|
76
67
|
export const STACK_BADGES = {
|
|
77
68
|
Python: badges.python,
|
|
78
69
|
'Node.js / TypeScript': badges.node,
|
|
@@ -11,6 +11,9 @@ disallowedTools:
|
|
|
11
11
|
maxTurns: 40
|
|
12
12
|
omitClaudeMd: true
|
|
13
13
|
memory: project
|
|
14
|
+
skills:
|
|
15
|
+
- security-checklist
|
|
16
|
+
criticalSystemReminder: "CRITICAL: You CANNOT edit files. Report vulnerabilities with remediation guidance only."
|
|
14
17
|
---
|
|
15
18
|
|
|
16
19
|
You are a senior application security engineer performing a code
|
|
@@ -5,6 +5,8 @@ model: sonnet
|
|
|
5
5
|
isolation: worktree
|
|
6
6
|
background: true
|
|
7
7
|
maxTurns: 50
|
|
8
|
+
initialPrompt: "/start"
|
|
9
|
+
criticalSystemReminder: "CRITICAL: You are verification-only. Do NOT edit or fix code. Report findings with exact reproduction steps."
|
|
8
10
|
---
|
|
9
11
|
|
|
10
12
|
You are a verification specialist. You test the actual running
|
|
@@ -7,23 +7,51 @@
|
|
|
7
7
|
## User
|
|
8
8
|
|
|
9
9
|
<!-- Role, preferences, expertise — helps Claude tailor responses -->
|
|
10
|
+
<!-- Example: - [Backend lead](user_role.md) — 8 years Python, new to this frontend -->
|
|
10
11
|
|
|
11
12
|
## Feedback
|
|
12
13
|
|
|
13
14
|
<!-- What to do and what to avoid — both corrections AND confirmed approaches -->
|
|
14
15
|
<!-- Format: rule, then **Why:** and **How to apply:** -->
|
|
16
|
+
<!-- Example: - [No mocks in integration tests](feedback_testing.md) — burned by mock/prod divergence -->
|
|
15
17
|
|
|
16
18
|
## Project
|
|
17
19
|
|
|
18
20
|
<!-- Ongoing work, goals, deadlines — convert relative dates to absolute -->
|
|
19
21
|
<!-- Format: fact/decision, then **Why:** and **How to apply:** -->
|
|
22
|
+
<!-- Example: - [Merge freeze 2026-03-05](project_release.md) — mobile team cutting release branch -->
|
|
20
23
|
|
|
21
24
|
## Reference
|
|
22
25
|
|
|
23
26
|
<!-- Pointers to external systems — Linear boards, Slack channels, dashboards -->
|
|
27
|
+
<!-- Example: - [Pipeline bugs](reference_linear.md) — tracked in Linear project "INGEST" -->
|
|
24
28
|
|
|
25
29
|
---
|
|
26
30
|
|
|
31
|
+
## Memory File Format
|
|
32
|
+
|
|
33
|
+
Each memory file in `.claude/memory/` uses this frontmatter:
|
|
34
|
+
|
|
35
|
+
```markdown
|
|
36
|
+
---
|
|
37
|
+
name: descriptive-name
|
|
38
|
+
description: one-line summary used to decide relevance in future sessions
|
|
39
|
+
type: user | feedback | project | reference
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
Content here. For feedback/project types, structure as:
|
|
43
|
+
Rule or fact.
|
|
44
|
+
**Why:** the reason or context.
|
|
45
|
+
**How to apply:** when and where this guidance applies.
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Drift and Verification
|
|
49
|
+
|
|
50
|
+
- Memory records become stale over time. Before acting on a memory, verify it against the current state of the codebase.
|
|
51
|
+
- If a memory names a file path, check the file still exists. If it names a function or flag, grep for it.
|
|
52
|
+
- If a recalled memory conflicts with current information, trust what you observe now — update or remove the stale memory.
|
|
53
|
+
- "The memory says X exists" is not the same as "X exists now."
|
|
54
|
+
|
|
27
55
|
## What NOT to save here
|
|
28
56
|
|
|
29
57
|
- Code patterns, architecture, file paths — derivable from current project state
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "How Claude writes rules for itself, when to update CLAUDE.md, keeping it lean and effective"
|
|
3
3
|
when_to_use: "When considering updates to CLAUDE.md, when the same mistake has happened twice, when CLAUDE.md is getting too long"
|
|
4
|
+
version: "1.0.0"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# CLAUDE.md Maintenance
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Context budget awareness, when to compact, when to clear, subagent offloading"
|
|
3
3
|
when_to_use: "When context is running low, before compaction decisions, when deciding whether to use subagents for context hygiene"
|
|
4
|
+
version: "1.0.0"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Context Management
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: 'Multi-agent orchestration patterns: when to use coordinator mode, worker prompts, parallel execution'
|
|
3
3
|
when_to_use: 'When working with multiple agents or terminals in parallel, or when deciding how to break a large task into coordinated sub-tasks'
|
|
4
|
+
version: "1.0.0"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Coordinator Mode
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Branch naming, commit message format, PR workflow, worktree conventions, versioning policy"
|
|
3
3
|
when_to_use: "When creating branches, writing commit messages, creating PRs, or making versioning decisions"
|
|
4
|
+
version: "1.0.0"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Git Conventions
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "How to structure implementation plans as files, progressive implementation, plan review process"
|
|
3
3
|
when_to_use: "When starting a multi-step task that needs a written plan, or when reviewing an existing implementation plan"
|
|
4
|
+
version: "1.0.0"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Planning with Files
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "OWASP-based security checklist any agent can reference when reviewing or writing code"
|
|
3
3
|
when_to_use: "When writing code that handles user input, authentication, authorization, file uploads, or external data"
|
|
4
|
+
version: "1.0.0"
|
|
4
5
|
paths:
|
|
5
6
|
- "**/auth/**"
|
|
6
7
|
- "**/security/**"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "When to use subagents, how many, context hygiene, worktree isolation patterns"
|
|
3
3
|
when_to_use: "When deciding whether to spawn a subagent, choosing between parallel and sequential execution, or giving subagent instructions"
|
|
4
|
+
version: "1.0.0"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Subagent Usage
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Test philosophy, coverage strategy, test-first patterns, what to test and what not to"
|
|
3
3
|
when_to_use: "When writing, modifying, or reviewing tests, or when making decisions about test strategy and coverage"
|
|
4
|
+
version: "1.0.0"
|
|
4
5
|
paths:
|
|
5
6
|
- "test/**"
|
|
6
7
|
- "tests/**"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Domain-specific verification beyond tests, closing the feedback loop for web, API, CLI, data"
|
|
3
3
|
when_to_use: "When verifying that implemented changes work correctly, after running automated tests, before committing"
|
|
4
|
+
version: "1.0.0"
|
|
4
5
|
paths:
|
|
5
6
|
- "test/**"
|
|
6
7
|
- "tests/**"
|