mustard-claude 3.1.27 → 3.1.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mustard-claude",
3
- "version": "3.1.27",
3
+ "version": "3.1.28",
4
4
  "description": "Framework-agnostic CLI for Claude Code project setup",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,6 @@
1
+ {
2
+ "bashFailStreak": {
3
+ "count": 0,
4
+ "lastTs": 1776746668631
5
+ }
6
+ }
@@ -13,3 +13,4 @@
13
13
  {"ts":"2026-04-14T19:07:59.760Z","event":"bash-native-redirect","tokens_affected":3,"tokens_saved":3,"note":"redirected","from":"cat","to":"Read","command_head":"cat file.txt"}
14
14
  {"ts":"2026-04-14T23:33:26.496Z","event":"bash-native-redirect","tokens_affected":8,"tokens_saved":8,"note":"redirected","from":"cat","to":"Read","command_head":"cat /c/Atiz/Mustard/mustard.json"}
15
15
  {"ts":"2026-04-16T11:43:50.380Z","event":"bash-native-redirect","tokens_affected":4,"tokens_saved":4,"note":"redirected","from":"ls","to":"Glob","command_head":"ls src/commands/"}
16
+ {"ts":"2026-04-21T04:49:05.072Z","event":"bash-native-redirect","tokens_affected":27,"tokens_saved":27,"note":"redirected","from":"ls","to":"Glob","command_head":"ls \"C:/Atiz/Competi/projetos/sialia/.claude/spec/planned/2026-04-20-wave1-02-con"}
@@ -2,3 +2,7 @@
2
2
  {"ts":"2026-04-16T11:31:25.015Z","event":"rtk-rewrite","tokens_affected":17,"tokens_saved":85,"note":"rewritten via rtk","command_head":"cd templates && grep -A3 \"debug-loop-guard\" settings.json | ","savings_rate":0.5}
3
3
  {"ts":"2026-04-16T11:43:50.502Z","event":"rtk-rewrite","tokens_affected":4,"tokens_saved":26,"note":"rewritten via rtk","command_head":"ls src/commands/","savings_rate":0.65}
4
4
  {"ts":"2026-04-16T11:49:34.177Z","event":"rtk-rewrite","tokens_affected":37,"tokens_saved":222,"note":"rewritten via rtk","command_head":"cat mustard.json 2>/dev/null | head -30 && echo \"---\" && rtk","savings_rate":0.6}
5
+ {"ts":"2026-04-21T04:44:22.627Z","event":"rtk-rewrite","tokens_affected":18,"tokens_saved":0,"note":"rewritten via rtk","command_head":"ls -la \"C:/Atiz/Competi/projetos/sialia/.claude/spec/\" 2>&1 ","rewritten_head":"rtk ls -la \"C:/Atiz/Competi/projetos/sialia/.claude/spec/\" 2"}
6
+ {"ts":"2026-04-21T04:44:28.194Z","event":"rtk-rewrite","tokens_affected":41,"tokens_saved":0,"note":"rewritten via rtk","command_head":"ls \"C:/Atiz/Competi/projetos/sialia/.claude/spec/planned/\" 2","rewritten_head":"rtk ls \"C:/Atiz/Competi/projetos/sialia/.claude/spec/planned"}
7
+ {"ts":"2026-04-21T04:49:05.191Z","event":"rtk-rewrite","tokens_affected":29,"tokens_saved":0,"note":"rewritten via rtk","command_head":"ls \"C:/Atiz/Competi/projetos/sialia/.claude/spec/planned/202","rewritten_head":"rtk ls \"C:/Atiz/Competi/projetos/sialia/.claude/spec/planned"}
8
+ {"ts":"2026-04-21T04:51:12.069Z","event":"rtk-rewrite","tokens_affected":104,"tokens_saved":0,"note":"rewritten via rtk","command_head":"cd C:/Atiz/Mustard && cat mustard.json 2>/dev/null; echo '--","rewritten_head":"cd C:/Atiz/Mustard && rtk read mustard.json 2>/dev/null; ech"}
@@ -8,20 +8,34 @@
8
8
 
9
9
  Shows consolidated project status.
10
10
 
11
- ## Information
11
+ ## Action
12
12
 
13
- 1. **Git Status**
14
- - Current branch
15
- - Modified files
16
- - Commits pending push
13
+ 1. **Git Status** — run `rtk git status` and `rtk git log -1 --format=%H %s` for current branch, modified files, and last commit.
14
+ 2. **Pipeline** — run `node .claude/scripts/metrics-collect.js` and surface the `## Active:` and any `## Orphaned:` sections. Do NOT attempt to read `.claude/pipeline-state.json` (legacy singular path, no longer written). The canonical source is `.claude/.pipeline-states/*.json`, read via the script above.
15
+ 3. **Build** — if `.claude/.last-build.json` exists, report timestamp and pass/fail; otherwise note "no build state persisted".
16
+ 4. **Entity Registry** — read `.claude/entity-registry.json` and report `_meta.version`, `_meta.generatedAt`, and total entity count (length of `entities` or equivalent top-level collection).
17
17
 
18
- 2. **Pipeline**
19
- - Active pipeline (if any)
20
- - Current phase
18
+ ## Rules
21
19
 
22
- 3. **Build**
23
- - Last validation result
20
+ - Always delegate pipeline state reading to `metrics-collect.js` — never parse `.pipeline-states/` directly. This keeps `/status` and `/stats` consistent.
21
+ - If `metrics-collect.js` reports `## Orphaned:` pipelines, include them under the Pipeline section and suggest the user run `/mustard:complete {spec-name}` for each, or `/mustard:maint` for bulk cleanup.
22
+ - If no `.claude/` directory exists, inform user that project is not initialized and suggest `mustard init`.
24
23
 
25
- 4. **Entity Registry**
26
- - Number of entities
27
- - Last update
24
+ ## Information Layout
25
+
26
+ ```
27
+ 1. Git
28
+ - Branch, modified files, last commit
29
+
30
+ 2. Pipeline
31
+ - Active: {name} (phase: {phase}) ← if any
32
+ - Orphaned: {name} (spec no longer in active/) ← if any
33
+ - (or "none" if both empty)
34
+
35
+ 3. Build
36
+ - Last validation: {timestamp} — {pass|fail}
37
+ - (or "no build state persisted")
38
+
39
+ 4. Entity Registry
40
+ - Version, generated at, total entities
41
+ ```
@@ -24,42 +24,61 @@ function main() {
24
24
  parts.push('# Pipeline Metrics');
25
25
  parts.push('');
26
26
 
27
- // Active pipelines
27
+ // Active & Orphaned pipelines
28
+ //
29
+ // A pipeline-state file is "Active" iff its spec still lives in
30
+ // .claude/spec/active/{name}/. If the spec has moved to completed/ or no
31
+ // longer exists, the state file is an orphan — reported under a separate
32
+ // heading so users can reconcile it via /mustard:complete or /mustard:maint.
28
33
  const statesDir = path.join(claudeDir, '.pipeline-states');
34
+ const activeSpecDir = path.join(claudeDir, 'spec', 'active');
29
35
  if (fs.existsSync(statesDir)) {
30
36
  const files = fs.readdirSync(statesDir).filter(f => f.endsWith('.json'));
37
+ const activeBuckets = [];
38
+ const orphanedBuckets = [];
31
39
  for (const f of files) {
32
40
  try {
33
41
  const state = JSON.parse(fs.readFileSync(path.join(statesDir, f), 'utf8'));
34
- if (state.metrics) {
35
- const name = f.replace('.json', '');
36
- const m = state.metrics;
37
- const duration = m.startedAt ? formatDuration(new Date(m.startedAt), new Date()) : 'unknown';
38
- parts.push(`## Active: ${name}`);
39
- parts.push(`- Duration: ${duration}`);
40
- parts.push(`- API calls: ${m.apiCalls || 0}`);
41
- parts.push(`- Retries: ${m.retries || 0}`);
42
- if (m.toolBreakdown && Object.keys(m.toolBreakdown).length > 0) {
43
- parts.push('- Tool breakdown:');
44
- for (const [tool, count] of Object.entries(m.toolBreakdown).sort((a, b) => b[1] - a[1])) {
45
- parts.push(` - ${tool}: ${count}`);
46
- }
42
+ if (!state.metrics) continue;
43
+ const name = f.replace('.json', '');
44
+ const specPath = path.join(activeSpecDir, name);
45
+ const isOrphaned = !fs.existsSync(specPath);
46
+ const m = state.metrics;
47
+ const duration = m.startedAt ? formatDuration(new Date(m.startedAt), new Date()) : 'unknown';
48
+ const lines = [];
49
+ lines.push(`## ${isOrphaned ? 'Orphaned' : 'Active'}: ${name}`);
50
+ lines.push(`- Duration: ${duration}`);
51
+ lines.push(`- API calls: ${m.apiCalls || 0}`);
52
+ lines.push(`- Retries: ${m.retries || 0}`);
53
+ if (m.toolBreakdown && Object.keys(m.toolBreakdown).length > 0) {
54
+ lines.push('- Tool breakdown:');
55
+ for (const [tool, count] of Object.entries(m.toolBreakdown).sort((a, b) => b[1] - a[1])) {
56
+ lines.push(` - ${tool}: ${count}`);
47
57
  }
48
- if (m.gate_saves !== undefined) parts.push(`- Gate saves: ${m.gate_saves}`);
49
- if (m.wave_reentry !== undefined) parts.push(`- Wave reentries: ${m.wave_reentry}`);
50
- if (m.skillHits && Object.keys(m.skillHits).length > 0) {
51
- parts.push('- Skill hits:');
52
- for (const [agent, hits] of Object.entries(m.skillHits).sort()) {
53
- const pct = hits.loaded > 0 ? Math.round((hits.read / hits.loaded) * 100) + '%' : '\u2014';
54
- parts.push(` - ${agent}: ${hits.read}/${hits.loaded} (${pct})`);
55
- }
58
+ }
59
+ if (m.gate_saves !== undefined) lines.push(`- Gate saves: ${m.gate_saves}`);
60
+ if (m.wave_reentry !== undefined) lines.push(`- Wave reentries: ${m.wave_reentry}`);
61
+ if (m.skillHits && Object.keys(m.skillHits).length > 0) {
62
+ lines.push('- Skill hits:');
63
+ for (const [agent, hits] of Object.entries(m.skillHits).sort()) {
64
+ const pct = hits.loaded > 0 ? Math.round((hits.read / hits.loaded) * 100) + '%' : '\u2014';
65
+ lines.push(` - ${agent}: ${hits.read}/${hits.loaded} (${pct})`);
56
66
  }
57
- parts.push('');
58
67
  }
68
+ if (isOrphaned) {
69
+ lines.push('- Spec: not in spec/active/ (likely completed without /mustard:complete)');
70
+ }
71
+ lines.push('');
72
+ (isOrphaned ? orphanedBuckets : activeBuckets).push(lines);
59
73
  } catch {}
60
74
  }
75
+ for (const block of activeBuckets) for (const line of block) parts.push(line);
76
+ for (const block of orphanedBuckets) for (const line of block) parts.push(line);
77
+ if (orphanedBuckets.length > 0) {
78
+ parts.push(`> ${orphanedBuckets.length} orphaned pipeline state(s) detected. Run \`/mustard:complete {spec-name}\` or \`/mustard:maint\` to reconcile.`);
79
+ parts.push('');
80
+ }
61
81
  }
62
-
63
82
  // Completed pipelines (archived metrics)
64
83
  const metricsDir = path.join(claudeDir, 'metrics');
65
84
  if (fs.existsSync(metricsDir)) {