mustard-claude 3.0.11 → 3.0.12
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 +88 -5
- package/dist/cli.js +13 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.d.ts +8 -0
- package/dist/commands/add.js +191 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/review.d.ts +6 -0
- package/dist/commands/review.js +143 -0
- package/dist/commands/review.js.map +1 -0
- package/package.json +1 -1
- package/templates/commands/mustard/bugfix/SKILL.md +4 -0
- package/templates/commands/mustard/complete/SKILL.md +26 -1
- package/templates/commands/mustard/feature/SKILL.md +8 -1
- package/templates/commands/mustard/knowledge/SKILL.md +82 -4
- package/templates/commands/mustard/resume/SKILL.md +6 -2
- package/templates/commands/mustard/stats/SKILL.md +34 -0
- package/templates/hooks/metrics-tracker.js +80 -0
- package/templates/hooks/pre-compact.js +17 -0
- package/templates/hooks/review-gate.js +129 -0
- package/templates/hooks/session-cleanup.js +2 -1
- package/templates/hooks/session-memory.js +62 -0
- package/templates/hooks/subagent-tracker.js +39 -0
- package/templates/pipeline-config.md +19 -0
- package/templates/scripts/diff-context.js +137 -0
- package/templates/scripts/knowledge-update.js +133 -0
- package/templates/scripts/memory-persist.js +118 -0
- package/templates/scripts/metrics-collect.js +141 -0
- package/templates/settings.json +31 -0
package/README.md
CHANGED
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
|
|
27
27
|
Mustard sets up a `.claude/` folder that turns Claude Code into a structured development pipeline:
|
|
28
28
|
|
|
29
|
-
- **
|
|
30
|
-
- **
|
|
29
|
+
- **15 pipeline skills** — feature, bugfix, scan, resume, approve, complete, git, maint, task, knowledge, skill, status, scan-format, agent-prompt template, stats
|
|
30
|
+
- **12 enforcement hooks** — bash safety, file guard, registry validation, guard verification, auto-format, pre-compact, session cleanup, subagent tracking, RTK rewrite, session memory, review gate, metrics tracker
|
|
31
31
|
- **6 bundled skills** — design-craft, react-best-practices, senior-architect, skill-creator, commit-workflow, pipeline-execution
|
|
32
|
-
- **
|
|
32
|
+
- **8 utility scripts** — subproject detection, entity registry sync, statusline, memory persistence, diff context, knowledge base, metrics collection
|
|
33
33
|
- **Token economy** — auto-installs [RTK](https://github.com/rtk-ai/rtk) to reduce token consumption by 60-90% on CLI outputs
|
|
34
34
|
- **Monorepo + single repo** — works with any project structure
|
|
35
35
|
|
|
@@ -101,6 +101,8 @@ The CLI is a **one-time setup tool**. All intelligence lives in the skills and h
|
|
|
101
101
|
| `mustard init` | Copy `.claude/` structure into current project |
|
|
102
102
|
| `mustard update` | Update core files (preserves user customizations) |
|
|
103
103
|
| `mustard auto-update` | Check npm for newer version and install |
|
|
104
|
+
| `mustard add <template>` | Install a community template |
|
|
105
|
+
| `mustard review --pr <N>` | Review a pull request (local or CI mode) |
|
|
104
106
|
| `mustard --version` | Show installed version |
|
|
105
107
|
| `mustard --help` | Show help |
|
|
106
108
|
|
|
@@ -144,6 +146,40 @@ The CLI is a **one-time setup tool**. All intelligence lives in the skills and h
|
|
|
144
146
|
| `--check-only` | Only check for updates, do not install |
|
|
145
147
|
| `-y, --yes` | Skip confirmation prompts |
|
|
146
148
|
|
|
149
|
+
### `mustard add`
|
|
150
|
+
|
|
151
|
+
| Option | Description |
|
|
152
|
+
|--------|-------------|
|
|
153
|
+
| `-f, --force` | Overwrite existing files |
|
|
154
|
+
|
|
155
|
+
**Sources:**
|
|
156
|
+
- GitHub: `github.com/mustard-templates/{name}`
|
|
157
|
+
- npm: `mustard-template-{name}`
|
|
158
|
+
|
|
159
|
+
**Usage:**
|
|
160
|
+
```bash
|
|
161
|
+
mustard add template:dotnet-clean-arch
|
|
162
|
+
mustard add template:nextjs-app-router
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### `mustard review`
|
|
166
|
+
|
|
167
|
+
| Option | Description |
|
|
168
|
+
|--------|-------------|
|
|
169
|
+
| `--pr <number>` | PR number to review (required) |
|
|
170
|
+
| `--ci` | CI mode: post as PR comment, exit 1 on critical issues |
|
|
171
|
+
|
|
172
|
+
**Requirements:** `gh` (GitHub CLI) and `claude` CLI must be installed.
|
|
173
|
+
|
|
174
|
+
**Usage:**
|
|
175
|
+
```bash
|
|
176
|
+
# Interactive review
|
|
177
|
+
mustard review --pr 42
|
|
178
|
+
|
|
179
|
+
# CI mode (for GitHub Actions)
|
|
180
|
+
mustard review --ci --pr 42
|
|
181
|
+
```
|
|
182
|
+
|
|
147
183
|
## What Gets Installed
|
|
148
184
|
|
|
149
185
|
```
|
|
@@ -166,6 +202,7 @@ The CLI is a **one-time setup tool**. All intelligence lives in the skills and h
|
|
|
166
202
|
│ ├── knowledge/SKILL.md # /knowledge — notes, audit, reports
|
|
167
203
|
│ ├── skill/SKILL.md # /skill — manage skills
|
|
168
204
|
│ ├── status/SKILL.md # /status — project status
|
|
205
|
+
│ ├── stats/SKILL.md # /stats — pipeline metrics
|
|
169
206
|
│ └── templates/agent-prompt/SKILL.md # Agent prompt template
|
|
170
207
|
├── hooks/ # Enforcement hooks
|
|
171
208
|
│ ├── rtk-rewrite.js # Rewrites Bash commands through RTK
|
|
@@ -177,11 +214,24 @@ The CLI is a **one-time setup tool**. All intelligence lives in the skills and h
|
|
|
177
214
|
│ ├── pre-compact.js # Saves state before compaction
|
|
178
215
|
│ ├── session-cleanup.js # Cleans up on session end
|
|
179
216
|
│ ├── subagent-tracker.js # Tracks agent lifecycle
|
|
217
|
+
│ ├── session-memory.js # Injects persistent memory on session start
|
|
218
|
+
│ ├── review-gate.js # Pre-commit validation (fail-open)
|
|
219
|
+
│ ├── metrics-tracker.js # Tracks pipeline API calls and retries
|
|
180
220
|
│ └── __tests__/hooks.test.js # Hook tests
|
|
181
221
|
├── scripts/
|
|
182
222
|
│ ├── sync-detect.js # Detects subprojects + roles
|
|
183
223
|
│ ├── sync-registry.js # Generates entity-registry.json
|
|
184
|
-
│
|
|
224
|
+
│ ├── statusline.js # Claude Code statusline
|
|
225
|
+
│ ├── memory-persist.js # Persists decisions/lessons across sessions
|
|
226
|
+
│ ├── memory-write.js # Writes agent memory entries
|
|
227
|
+
│ ├── diff-context.js # Generates git diff summary for agents
|
|
228
|
+
│ ├── knowledge-update.js # Updates project knowledge base
|
|
229
|
+
│ └── metrics-collect.js # Collects and displays pipeline metrics
|
|
230
|
+
├── memory/ # Persistent memory (auto-created)
|
|
231
|
+
│ ├── decisions.json # Decisions across pipelines
|
|
232
|
+
│ └── lessons.json # Lessons learned
|
|
233
|
+
├── knowledge.json # Evolutionary knowledge base
|
|
234
|
+
├── metrics/ # Pipeline metrics archive
|
|
185
235
|
└── skills/ # Bundled skills
|
|
186
236
|
├── design-craft/ # UI design methodology
|
|
187
237
|
├── react-best-practices/ # React/Next.js optimization (40+ rules)
|
|
@@ -203,6 +253,7 @@ The CLI is a **one-time setup tool**. All intelligence lives in the skills and h
|
|
|
203
253
|
| `/approve` | Approve spec for implementation |
|
|
204
254
|
| `/resume` | Resume interrupted pipeline |
|
|
205
255
|
| `/complete` | Finalize or cancel pipeline |
|
|
256
|
+
| `/stats` | Show pipeline metrics and token savings |
|
|
206
257
|
|
|
207
258
|
### Operations
|
|
208
259
|
|
|
@@ -269,7 +320,7 @@ After `/scan`, the pipeline commands (`/feature`, `/bugfix`) have full context t
|
|
|
269
320
|
PLAN — create spec with tasks per agent (Light: inline, Full: /approve)
|
|
270
321
|
│
|
|
271
322
|
▼
|
|
272
|
-
EXECUTE — dispatch agents per wave (DB+Backend ∥, Frontend after)
|
|
323
|
+
EXECUTE — dispatch agents per wave (DB+Backend ∥, Frontend after or parallel if safe)
|
|
273
324
|
│
|
|
274
325
|
▼
|
|
275
326
|
REVIEW — mandatory review per subproject (SOLID, patterns, i18n, ...)
|
|
@@ -301,6 +352,38 @@ Mustard integrates [RTK (Rust Token Killer)](https://github.com/rtk-ai/rtk) as c
|
|
|
301
352
|
|
|
302
353
|
RTK only applies to Bash tool calls. Claude Code's built-in tools (Read, Grep, Glob) are already optimized and bypass the hook.
|
|
303
354
|
|
|
355
|
+
## Persistent Memory
|
|
356
|
+
|
|
357
|
+
Mustard maintains lightweight persistent memory across sessions:
|
|
358
|
+
|
|
359
|
+
- **Decisions** — architectural and implementation decisions are saved to `.claude/memory/decisions.json`
|
|
360
|
+
- **Lessons** — what went wrong and the correction applied, saved to `.claude/memory/lessons.json`
|
|
361
|
+
- **Knowledge base** — patterns, conventions, and entities discovered across pipelines, saved to `.claude/knowledge.json`
|
|
362
|
+
|
|
363
|
+
Memory is automatically:
|
|
364
|
+
- **Injected** into each new session and every dispatched agent
|
|
365
|
+
- **Capped** at 50 entries (memory) / 200 entries (knowledge) — oldest pruned
|
|
366
|
+
- **Never cleaned** by session cleanup — persists until manually removed
|
|
367
|
+
|
|
368
|
+
This gives 80% of the benefit of a database-backed system with zero infrastructure.
|
|
369
|
+
|
|
370
|
+
## CI Integration
|
|
371
|
+
|
|
372
|
+
Review PRs automatically in your CI pipeline:
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
# In GitHub Actions
|
|
376
|
+
mustard review --ci --pr ${{ github.event.pull_request.number }}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
CI mode:
|
|
380
|
+
- Fetches PR diff via `gh pr diff`
|
|
381
|
+
- Runs Claude review with project guards and rules
|
|
382
|
+
- Posts review as PR comment
|
|
383
|
+
- Exits with code 1 if CRITICAL issues found (fails the build)
|
|
384
|
+
|
|
385
|
+
Requires `gh` and `claude` CLI in the CI environment.
|
|
386
|
+
|
|
304
387
|
## Supported Projects
|
|
305
388
|
|
|
306
389
|
Mustard is **framework-agnostic**. The CLI just copies templates. `/scan` handles detection:
|
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,8 @@ import { createRequire } from 'node:module';
|
|
|
3
3
|
import { initCommand } from './commands/init.js';
|
|
4
4
|
import { updateCommand } from './commands/update.js';
|
|
5
5
|
import { autoUpdateCommand } from './commands/auto-update.js';
|
|
6
|
+
import { addCommand } from './commands/add.js';
|
|
7
|
+
import { reviewCommand } from './commands/review.js';
|
|
6
8
|
const require = createRequire(import.meta.url);
|
|
7
9
|
const { version } = require('../package.json');
|
|
8
10
|
export function run() {
|
|
@@ -28,6 +30,17 @@ export function run() {
|
|
|
28
30
|
.option('--check-only', 'Only check for updates, do not install')
|
|
29
31
|
.option('-y, --yes', 'Skip confirmation prompts')
|
|
30
32
|
.action(autoUpdateCommand);
|
|
33
|
+
program
|
|
34
|
+
.command('add <template>')
|
|
35
|
+
.description('Install a community template (e.g., mustard add template:dotnet-clean-arch)')
|
|
36
|
+
.option('-f, --force', 'Overwrite existing files')
|
|
37
|
+
.action(addCommand);
|
|
38
|
+
program
|
|
39
|
+
.command('review')
|
|
40
|
+
.description('Review a pull request (local or CI mode)')
|
|
41
|
+
.option('--ci', 'CI mode: post review as PR comment, exit 1 on critical issues')
|
|
42
|
+
.option('--pr <number>', 'PR number to review')
|
|
43
|
+
.action(reviewCommand);
|
|
31
44
|
program.parse();
|
|
32
45
|
}
|
|
33
46
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAE/C,MAAM,UAAU,GAAG;IACjB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,SAAS,CAAC;SACf,WAAW,CAAC,sDAAsD,CAAC;SACnE,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,kDAAkD,CAAC;SAC/D,MAAM,CAAC,aAAa,EAAE,sDAAsD,CAAC;SAC7E,MAAM,CAAC,WAAW,EAAE,2BAA2B,CAAC;SAChD,MAAM,CAAC,WAAW,CAAC,CAAC;IAEvB,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,2DAA2D,CAAC;SACxE,MAAM,CAAC,aAAa,EAAE,8BAA8B,CAAC;SACrD,MAAM,CAAC,aAAa,CAAC,CAAC;IAEzB,OAAO;SACJ,OAAO,CAAC,aAAa,CAAC;SACtB,WAAW,CAAC,uDAAuD,CAAC;SACpE,MAAM,CAAC,cAAc,EAAE,wCAAwC,CAAC;SAChE,MAAM,CAAC,WAAW,EAAE,2BAA2B,CAAC;SAChD,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAE7B,OAAO;SACJ,OAAO,CAAC,gBAAgB,CAAC;SACzB,WAAW,CAAC,6EAA6E,CAAC;SAC1F,MAAM,CAAC,aAAa,EAAE,0BAA0B,CAAC;SACjD,MAAM,CAAC,UAAU,CAAC,CAAC;IAEtB,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,0CAA0C,CAAC;SACvD,MAAM,CAAC,MAAM,EAAE,+DAA+D,CAAC;SAC/E,MAAM,CAAC,eAAe,EAAE,qBAAqB,CAAC;SAC9C,MAAM,CAAC,aAAa,CAAC,CAAC;IAEzB,OAAO,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface AddOptions {
|
|
2
|
+
force?: boolean;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Add command - installs a community template into the current project's .claude/ directory.
|
|
6
|
+
* Sources: GitHub (mustard-templates/{name}) or npm (mustard-template-{name}).
|
|
7
|
+
*/
|
|
8
|
+
export declare function addCommand(templateSpec: string, options: AddOptions): Promise<void>;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { join, basename, relative, dirname } from 'node:path';
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
/**
|
|
6
|
+
* Add command - installs a community template into the current project's .claude/ directory.
|
|
7
|
+
* Sources: GitHub (mustard-templates/{name}) or npm (mustard-template-{name}).
|
|
8
|
+
*/
|
|
9
|
+
export async function addCommand(templateSpec, options) {
|
|
10
|
+
// Parse template spec: "template:name" or just "name"
|
|
11
|
+
const name = templateSpec.replace(/^template:/, '');
|
|
12
|
+
if (!name || name.includes('..') || /[^a-zA-Z0-9_-]/.test(name)) {
|
|
13
|
+
console.error(`❌ Invalid template name: "${name}". Use alphanumeric, hyphens, underscores only.`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const cwd = process.cwd();
|
|
17
|
+
const claudeDir = join(cwd, '.claude');
|
|
18
|
+
if (!existsSync(claudeDir)) {
|
|
19
|
+
console.error('❌ No .claude/ directory found. Run `mustard init` first.');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
console.log(`📦 Installing template: ${name}`);
|
|
23
|
+
const tmpDir = join(tmpdir(), `mustard-template-${name}-${Date.now()}`);
|
|
24
|
+
try {
|
|
25
|
+
// Attempt 1: clone from GitHub
|
|
26
|
+
const repoUrl = `https://github.com/mustard-templates/${name}.git`;
|
|
27
|
+
console.log(`📥 Fetching from ${repoUrl}...`);
|
|
28
|
+
let fetchedDir = tmpDir;
|
|
29
|
+
try {
|
|
30
|
+
execSync(`git clone --depth 1 "${repoUrl}" "${tmpDir}"`, {
|
|
31
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
32
|
+
timeout: 30000,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Attempt 2: npm package
|
|
37
|
+
console.log(` GitHub repo not found. Trying npm: mustard-template-${name}...`);
|
|
38
|
+
try {
|
|
39
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
40
|
+
execSync(`npm pack mustard-template-${name} --pack-destination "${tmpDir}"`, {
|
|
41
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
42
|
+
timeout: 30000,
|
|
43
|
+
});
|
|
44
|
+
const tgz = readdirSync(tmpDir).find(f => f.endsWith('.tgz'));
|
|
45
|
+
if (tgz) {
|
|
46
|
+
execSync(`tar -xzf "${join(tmpDir, tgz)}" -C "${tmpDir}"`, {
|
|
47
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
48
|
+
});
|
|
49
|
+
// npm pack extracts to a "package" subdirectory
|
|
50
|
+
const packageDir = join(tmpDir, 'package');
|
|
51
|
+
if (existsSync(packageDir)) {
|
|
52
|
+
fetchedDir = packageDir;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
console.error(`❌ Template "${name}" not found on GitHub or npm.`);
|
|
58
|
+
console.log('\nAvailable sources:');
|
|
59
|
+
console.log(` GitHub: github.com/mustard-templates/${name}`);
|
|
60
|
+
console.log(` npm: mustard-template-${name}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Read manifest
|
|
65
|
+
const manifestPath = join(fetchedDir, 'mustard-template.json');
|
|
66
|
+
let manifest;
|
|
67
|
+
if (existsSync(manifestPath)) {
|
|
68
|
+
manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Auto-detect known .claude/ subdirectories
|
|
72
|
+
manifest = {
|
|
73
|
+
name,
|
|
74
|
+
version: '0.0.0',
|
|
75
|
+
files: detectFiles(fetchedDir),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
console.log(`📋 Template: ${manifest.name} v${manifest.version}`);
|
|
79
|
+
if (manifest.description)
|
|
80
|
+
console.log(` ${manifest.description}`);
|
|
81
|
+
// Copy files
|
|
82
|
+
let copied = 0;
|
|
83
|
+
let skipped = 0;
|
|
84
|
+
for (const filePattern of manifest.files) {
|
|
85
|
+
const src = join(fetchedDir, filePattern);
|
|
86
|
+
if (!existsSync(src))
|
|
87
|
+
continue;
|
|
88
|
+
const destBase = join(claudeDir, filePattern);
|
|
89
|
+
if (statSync(src).isDirectory()) {
|
|
90
|
+
const files = walkDir(src);
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
const rel = relative(src, file);
|
|
93
|
+
const dest = join(destBase, rel);
|
|
94
|
+
if (existsSync(dest) && !options.force) {
|
|
95
|
+
console.log(` Skipping existing: ${join(filePattern, rel)}`);
|
|
96
|
+
skipped++;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
100
|
+
writeFileSync(dest, readFileSync(file));
|
|
101
|
+
console.log(` Copied: ${join(filePattern, rel)}`);
|
|
102
|
+
copied++;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
if (existsSync(destBase) && !options.force) {
|
|
107
|
+
console.log(` Skipping existing: ${filePattern}`);
|
|
108
|
+
skipped++;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
mkdirSync(dirname(destBase), { recursive: true });
|
|
112
|
+
writeFileSync(destBase, readFileSync(src));
|
|
113
|
+
console.log(` Copied: ${filePattern}`);
|
|
114
|
+
copied++;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Merge hook additions into settings.json
|
|
118
|
+
if (manifest.hooks_additions && manifest.hooks_additions.length > 0) {
|
|
119
|
+
const settingsPath = join(claudeDir, 'settings.json');
|
|
120
|
+
if (existsSync(settingsPath)) {
|
|
121
|
+
try {
|
|
122
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
123
|
+
const hooks = (settings.hooks ?? {});
|
|
124
|
+
settings.hooks = hooks;
|
|
125
|
+
for (const hook of manifest.hooks_additions) {
|
|
126
|
+
const event = hook.event;
|
|
127
|
+
if (!hooks[event])
|
|
128
|
+
hooks[event] = [];
|
|
129
|
+
// Skip if hook command already registered
|
|
130
|
+
const alreadyExists = hooks[event].some(h => h.hooks?.some(hh => hh.command === hook.command));
|
|
131
|
+
if (!alreadyExists) {
|
|
132
|
+
hooks[event].push({
|
|
133
|
+
matcher: hook.matcher,
|
|
134
|
+
hooks: [
|
|
135
|
+
{
|
|
136
|
+
type: 'command',
|
|
137
|
+
command: hook.command,
|
|
138
|
+
timeout: hook.timeout ?? 5,
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
});
|
|
142
|
+
console.log(` Registered hook: ${event} -> ${basename(hook.command)}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
process.stderr.write(` Could not merge hooks into settings.json: ${err.message}\n`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
console.log(`\nTemplate installed: ${copied} file(s) copied, ${skipped} skipped.`);
|
|
153
|
+
if (skipped > 0)
|
|
154
|
+
console.log('Use --force to overwrite existing files.');
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
// Cleanup tmp directory
|
|
158
|
+
try {
|
|
159
|
+
execSync(`rm -rf "${tmpDir}"`, { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
try {
|
|
163
|
+
execSync(`rmdir /s /q "${tmpDir}"`, { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
// Best-effort cleanup — ignore failure
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function detectFiles(dir) {
|
|
172
|
+
const knownSubdirs = ['commands', 'skills', 'hooks', 'context', 'scripts'];
|
|
173
|
+
return knownSubdirs.filter(p => existsSync(join(dir, p)));
|
|
174
|
+
}
|
|
175
|
+
function walkDir(dir) {
|
|
176
|
+
const results = [];
|
|
177
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
178
|
+
for (const entry of entries) {
|
|
179
|
+
const full = join(dir, entry.name);
|
|
180
|
+
if (entry.isDirectory()) {
|
|
181
|
+
if (entry.name === '.git' || entry.name === 'node_modules')
|
|
182
|
+
continue;
|
|
183
|
+
results.push(...walkDir(full));
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
results.push(full);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return results;
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=add.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
export async function reviewCommand(options) {
|
|
5
|
+
const prNumber = options.pr;
|
|
6
|
+
if (!prNumber) {
|
|
7
|
+
console.error('Error: PR number required. Usage: mustard review --pr <number>');
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
// Verify gh CLI is available
|
|
11
|
+
try {
|
|
12
|
+
execSync('gh --version', { stdio: 'ignore' });
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
console.error('Error: GitHub CLI (gh) is required. Install from https://cli.github.com/');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
// Verify claude CLI is available
|
|
19
|
+
try {
|
|
20
|
+
execSync('claude --version', { stdio: 'ignore' });
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
console.error('Error: Claude CLI is required. Install from https://docs.anthropic.com/');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
console.log(`Reviewing PR #${prNumber}${options.ci ? ' (CI mode)' : ''}...`);
|
|
27
|
+
try {
|
|
28
|
+
// Fetch PR info
|
|
29
|
+
const prInfo = execSync(`gh pr view ${prNumber} --json title,body,additions,deletions,changedFiles,baseRefName,headRefName`, {
|
|
30
|
+
encoding: 'utf8',
|
|
31
|
+
timeout: 15000,
|
|
32
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
33
|
+
windowsHide: true,
|
|
34
|
+
});
|
|
35
|
+
const pr = JSON.parse(prInfo);
|
|
36
|
+
// Fetch PR diff
|
|
37
|
+
const diff = execSync(`gh pr diff ${prNumber}`, {
|
|
38
|
+
encoding: 'utf8',
|
|
39
|
+
timeout: 30000,
|
|
40
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
41
|
+
windowsHide: true,
|
|
42
|
+
});
|
|
43
|
+
// Truncate diff if too large (max ~50k chars for Claude context)
|
|
44
|
+
const maxDiffChars = 50000;
|
|
45
|
+
let truncatedDiff = diff;
|
|
46
|
+
if (diff.length > maxDiffChars) {
|
|
47
|
+
truncatedDiff = diff.slice(0, maxDiffChars) + '\n\n... (diff truncated, showing first 50k chars)';
|
|
48
|
+
}
|
|
49
|
+
// Load guards if available
|
|
50
|
+
let guards = '';
|
|
51
|
+
const cwd = process.cwd();
|
|
52
|
+
const guardsPath = join(cwd, '.claude', 'commands', 'guards.md');
|
|
53
|
+
if (existsSync(guardsPath)) {
|
|
54
|
+
try {
|
|
55
|
+
guards = readFileSync(guardsPath, 'utf8').slice(0, 3000);
|
|
56
|
+
}
|
|
57
|
+
catch { /* non-critical — proceed without guards */ }
|
|
58
|
+
}
|
|
59
|
+
// Load CLAUDE.md for project rules
|
|
60
|
+
let projectRules = '';
|
|
61
|
+
const claudeMdPath = join(cwd, 'CLAUDE.md');
|
|
62
|
+
if (existsSync(claudeMdPath)) {
|
|
63
|
+
try {
|
|
64
|
+
projectRules = readFileSync(claudeMdPath, 'utf8').slice(0, 2000);
|
|
65
|
+
}
|
|
66
|
+
catch { /* non-critical — proceed without project rules */ }
|
|
67
|
+
}
|
|
68
|
+
// Build review prompt
|
|
69
|
+
const prompt = buildReviewPrompt(pr, truncatedDiff, guards, projectRules);
|
|
70
|
+
// Run Claude for review (both modes use --print for non-interactive output)
|
|
71
|
+
const reviewResult = execSync(`claude --print "${escapeForShell(prompt)}"`, {
|
|
72
|
+
encoding: 'utf8',
|
|
73
|
+
timeout: 120000,
|
|
74
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
75
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
76
|
+
windowsHide: true,
|
|
77
|
+
});
|
|
78
|
+
console.log('\nReview Result:\n');
|
|
79
|
+
console.log(reviewResult);
|
|
80
|
+
// In CI mode, post review as PR comment
|
|
81
|
+
if (options.ci && reviewResult.trim()) {
|
|
82
|
+
try {
|
|
83
|
+
const commentBody = `## Automated Review (Mustard)\n\n${reviewResult}`;
|
|
84
|
+
execSync(`gh pr comment ${prNumber} --body "${escapeForShell(commentBody)}"`, {
|
|
85
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
86
|
+
timeout: 15000,
|
|
87
|
+
windowsHide: true,
|
|
88
|
+
});
|
|
89
|
+
console.log(`\nReview posted as comment on PR #${prNumber}`);
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
process.stderr.write(`Warning: Could not post review comment: ${err.message}\n`);
|
|
93
|
+
}
|
|
94
|
+
// Exit with code based on review severity
|
|
95
|
+
if (/\bCRITICAL\b/i.test(reviewResult)) {
|
|
96
|
+
console.log('Critical issues found.');
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
console.log('Review complete.');
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
console.error(`Error: Review failed: ${err.message}`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function buildReviewPrompt(pr, diff, guards, projectRules) {
|
|
108
|
+
const additions = pr.additions;
|
|
109
|
+
const deletions = pr.deletions;
|
|
110
|
+
const changedFiles = pr.changedFiles;
|
|
111
|
+
const title = pr.title;
|
|
112
|
+
const body = pr.body;
|
|
113
|
+
const baseRefName = pr.baseRefName;
|
|
114
|
+
const headRefName = pr.headRefName;
|
|
115
|
+
const parts = [
|
|
116
|
+
'Review this pull request for code quality, security, and correctness.',
|
|
117
|
+
'',
|
|
118
|
+
`## PR: ${title}`,
|
|
119
|
+
`Base: ${baseRefName} <- Head: ${headRefName}`,
|
|
120
|
+
`Changes: +${additions} -${deletions} (${changedFiles} files)`,
|
|
121
|
+
'',
|
|
122
|
+
];
|
|
123
|
+
if (body) {
|
|
124
|
+
parts.push('## Description', body, '');
|
|
125
|
+
}
|
|
126
|
+
if (projectRules) {
|
|
127
|
+
parts.push('## Project Rules', projectRules, '');
|
|
128
|
+
}
|
|
129
|
+
if (guards) {
|
|
130
|
+
parts.push("## Guards (DO/DON'T rules)", guards, '');
|
|
131
|
+
}
|
|
132
|
+
parts.push('## Review Checklist', '- [ ] No security vulnerabilities (injection, XSS, secrets)', '- [ ] Code follows project conventions', '- [ ] No unnecessary complexity', '- [ ] Error handling is appropriate', '- [ ] No breaking changes without migration', '', '## Diff', '```diff', diff, '```', '', 'Provide a structured review with:', '1. **Summary**: What this PR does (1-2 sentences)', '2. **Issues**: List of issues found (CRITICAL / WARNING / INFO)', '3. **Suggestions**: Improvements (optional)', '4. **Verdict**: APPROVE / REQUEST_CHANGES / COMMENT');
|
|
133
|
+
return parts.join('\n');
|
|
134
|
+
}
|
|
135
|
+
function escapeForShell(str) {
|
|
136
|
+
// Escape for double-quoted shell string
|
|
137
|
+
return str
|
|
138
|
+
.replace(/\\/g, '\\\\')
|
|
139
|
+
.replace(/"/g, '\\"')
|
|
140
|
+
.replace(/\$/g, '\\$')
|
|
141
|
+
.replace(/`/g, '\\`');
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -15,6 +15,10 @@ Autonomous pipeline to diagnose and fix bugs. Zero context-switch — never ask
|
|
|
15
15
|
### ANALYZE (diagnose + assess)
|
|
16
16
|
|
|
17
17
|
1. **AUTO-SYNC:** `node .claude/scripts/sync-registry.js`
|
|
18
|
+
|
|
19
|
+
### Diff Context (automatic)
|
|
20
|
+
Run `node .claude/scripts/diff-context.js` to capture the current git state. Include the output in the agent prompt as `{diff_context}` so agents know what has already changed.
|
|
21
|
+
|
|
18
22
|
2. **DIAGNOSE:** Dispatch Explore agent:
|
|
19
23
|
- Scoped Grep searches with specific path + pattern for the error/symptom
|
|
20
24
|
- Trace callers/callees via Grep in relevant directories
|
|
@@ -33,10 +33,35 @@ If ANY gate fails: do NOT mark complete → report what failed + suggest fix. If
|
|
|
33
33
|
6. **Pipeline State — cleanup:**
|
|
34
34
|
- Extract `spec-name` from the spec directory (e.g. `2026-02-26-linked-services-card`)
|
|
35
35
|
- **Delete** `.claude/.pipeline-states/{spec-name}.json` (removes from statusline)
|
|
36
|
-
6b. **
|
|
36
|
+
6b. **Knowledge Capture:**
|
|
37
|
+
- Review patterns discovered during this pipeline
|
|
38
|
+
- For each significant pattern/convention/entity discovered:
|
|
39
|
+
```bash
|
|
40
|
+
echo '{"type":"pattern","name":"...","description":"...","source":"{spec-name}"}' | node .claude/scripts/knowledge-update.js
|
|
41
|
+
```
|
|
42
|
+
- Focus on: naming conventions used, architectural decisions, integration patterns
|
|
43
|
+
- Skip trivial or already-known patterns
|
|
44
|
+
|
|
45
|
+
6c. **Token Economy — RTK report (if available):**
|
|
37
46
|
- Run `rtk gain --all --format json` via Bash
|
|
38
47
|
- If RTK available: extract `saved_tokens` and `savings_pct`
|
|
39
48
|
- Include in output block below
|
|
49
|
+
6d. **Metrics Archive:**
|
|
50
|
+
- Read metrics from `.claude/.pipeline-states/{spec-name}.json`
|
|
51
|
+
- If metrics exist, ensure `.claude/metrics/` directory exists
|
|
52
|
+
- Save to `.claude/metrics/{spec-name}.json`:
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"name": "{spec-name}",
|
|
56
|
+
"completedAt": "{ISO timestamp}",
|
|
57
|
+
"durationMs": "{calculated from startedAt to now}",
|
|
58
|
+
"apiCalls": "{from metrics}",
|
|
59
|
+
"retries": "{from metrics}",
|
|
60
|
+
"toolBreakdown": "{from metrics}",
|
|
61
|
+
"rtkSavings": { "saved": N, "pct": N }
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
- If no metrics in state file, skip silently
|
|
40
65
|
7. **Output — visual feedback:**
|
|
41
66
|
|
|
42
67
|
```
|
|
@@ -15,6 +15,10 @@ Starts the pipeline to implement a feature or enhancement. Self-contained: ANALY
|
|
|
15
15
|
### ANALYZE Phase
|
|
16
16
|
|
|
17
17
|
**Auto-sync (silent):** `node .claude/scripts/sync-registry.js`
|
|
18
|
+
|
|
19
|
+
### Diff Context (automatic)
|
|
20
|
+
Run `node .claude/scripts/diff-context.js` to capture the current git state. Include the output in the agent prompt as `{diff_context}` so agents know what has already changed.
|
|
21
|
+
|
|
18
22
|
1. Read `pipeline-config.md` — agents, wave transitions, model selection
|
|
19
23
|
2. Read `entity-registry.json` via Grep for the specific entity name (e.g. `"Contract":`) — NEVER read the full JSON. Entity found? infer layers. Not found? all layers.
|
|
20
24
|
3. Determine layers from signals:
|
|
@@ -65,6 +69,9 @@ Record scope for PLAN phase branching.
|
|
|
65
69
|
- Summary, Entity Info, Files, Tasks, Dependencies
|
|
66
70
|
- Tasks organized by `### {Agent} Agent (Wave {N})`
|
|
67
71
|
- 3-8 checkboxed steps per agent, decomposed by operation type (NOT by file)
|
|
72
|
+
- If a frontend task has NO dependency on new backend endpoints or types, mark it as `(parallel-safe)` in the spec header:
|
|
73
|
+
`### Frontend Agent (Wave 1, parallel-safe)`
|
|
74
|
+
This allows the orchestrator to dispatch it alongside backend in Wave 1.
|
|
68
75
|
2. Add checkpoint fields: `Status: draft`, `Phase: PLAN`, `Scope: full`, `Checkpoint: {now}`
|
|
69
76
|
3. Create `.claude/.pipeline-states/{spec-name}.json`: `specName`, `status: "active"`, `phase: 2`, `phaseName: "PLAN"`, `scope: "full"`
|
|
70
77
|
4. Elegance Check: 3+ files or complex logic → "Is there a more elegant approach?"
|
|
@@ -103,7 +110,7 @@ When user chooses "Approve and implement now":
|
|
|
103
110
|
3. Read `pipeline-config.md` for agent config. For `entity-registry.json`: Grep for specific entity block only
|
|
104
111
|
4. Match recipes by title via Grep on `{subproject}/.claude/commands/recipes.md` — do NOT read full file. Extract recipe number + pattern refs
|
|
105
112
|
5. Identify relevant skills for `{recommended_skills}`: list skill names most relevant to the task (e.g., `api-endpoint-wiring, api-dto-validation`). Agents use these as hints — Claude natively decides which to load based on descriptions
|
|
106
|
-
6. Dispatch agents (wave rules: DB+Backend parallel, Frontend after Backend). Agent prompt includes `{recommended_skills}` as skill hints — agents read SKILL.md of relevant skills before implementing
|
|
113
|
+
6. Dispatch agents (wave rules: DB+Backend parallel, Frontend after Backend UNLESS spec marks task as `(parallel-safe)` — see `pipeline-config.md` Parallel Rules). Agent prompt includes `{recommended_skills}` as skill hints — agents read SKILL.md of relevant skills before implementing
|
|
107
114
|
7. Wave transitions between waves (from `pipeline-config.md`)
|
|
108
115
|
8. On return: validate (build/type-check), update spec `[ ]` → `[x]` (line-by-line edits, NEVER copy entire spec blocks as old_string)
|
|
109
116
|
8b. **Agent Memory:** After agents return and spec is updated, write agent memory: `echo '{"agent_type":"{type}","wave":{N},"pipeline":"{spec-name}","summary":"{what agent did}","details":{...}}' | node .claude/scripts/memory-write.js` — one per agent. Skip if single-wave pipeline (no downstream agents to benefit).
|