mustard-claude 3.1.28 → 3.1.29

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.28",
3
+ "version": "3.1.29",
4
4
  "description": "Framework-agnostic CLI for Claude Code project setup",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1814,21 +1814,19 @@ describe("knowledge-extract prescriptions", () => {
1814
1814
  }];
1815
1815
 
1816
1816
  const patterns = extractPatternsFromStates(states);
1817
- // retries > 2 triggers the high-retry entry
1818
- const retryEntry = patterns.find(p => p.name === "high-retry-login-feature");
1819
- assert.ok(retryEntry, "Expected high-retry entry");
1817
+ // retries > 2 triggers the high-hook-retry entry
1818
+ const retryEntry = patterns.find(p => p.name === "high-hook-retry-login-feature");
1819
+ assert.ok(retryEntry, "Expected high-hook-retry entry");
1820
1820
  assert.ok(retryEntry.prescription, "Expected prescription field");
1821
1821
  assert.ok(
1822
1822
  /delegate investigation via Task\(general-purpose\)/.test(retryEntry.prescription),
1823
1823
  "Prescription should instruct delegation via Task(general-purpose)"
1824
1824
  );
1825
1825
  assert.ok(retryEntry.tags.includes("prescriptive"), "Tags should include 'prescriptive'");
1826
- // Back-compat: original tags preserved
1827
- assert.ok(retryEntry.tags.includes("retry"));
1826
+ assert.ok(retryEntry.tags.includes("hook-retry"));
1828
1827
  assert.ok(retryEntry.tags.includes("pipeline"));
1829
1828
  assert.ok(retryEntry.tags.includes("lesson"));
1830
- // Back-compat: description still present
1831
- assert.ok(retryEntry.description.includes("4 retries"));
1829
+ assert.ok(retryEntry.description.includes("4 hook-level retries"));
1832
1830
  });
1833
1831
 
1834
1832
  it("should emit fragmentation prescription when apiCalls > 50 AND retries > 3", () => {
@@ -1843,7 +1841,7 @@ describe("knowledge-extract prescriptions", () => {
1843
1841
  }];
1844
1842
 
1845
1843
  const patterns = extractPatternsFromStates(states);
1846
- // apiCalls > 50 triggers heavy-pipeline; retries > 2 also triggers high-retry.
1844
+ // apiCalls > 50 triggers heavy-pipeline; retries > 2 also triggers high-hook-retry.
1847
1845
  const heavyEntry = patterns.find(p => p.name === "heavy-pipeline-big-refactor");
1848
1846
  assert.ok(heavyEntry, "Expected heavy-pipeline entry");
1849
1847
  assert.ok(heavyEntry.prescription, "Expected prescription field");
@@ -1858,7 +1856,7 @@ describe("knowledge-extract prescriptions", () => {
1858
1856
  });
1859
1857
 
1860
1858
  it("should emit reactive-iteration prescription when Edit > 15 and Write < 3", () => {
1861
- // Edit=20 > 15, Write=1 < 3, retries=3 to trigger the high-retry entry
1859
+ // Edit=20 > 15, Write=1 < 3, retries=3 to trigger the high-hook-retry entry
1862
1860
  // (needs retries > 2 OR apiCalls > 50 to produce any entry at all).
1863
1861
  // Pick retries=3 and small Bash/Agent to avoid L0-violation heuristic dominance
1864
1862
  // but note: the heuristic checks order — L0 fires first if bash+edit>3*agent AND retries>2.
@@ -1873,8 +1871,8 @@ describe("knowledge-extract prescriptions", () => {
1873
1871
  }];
1874
1872
 
1875
1873
  const patterns = extractPatternsFromStates(states);
1876
- const retryEntry = patterns.find(p => p.name === "high-retry-tweak-hell");
1877
- assert.ok(retryEntry, "Expected high-retry entry");
1874
+ const retryEntry = patterns.find(p => p.name === "high-hook-retry-tweak-hell");
1875
+ assert.ok(retryEntry, "Expected high-hook-retry entry");
1878
1876
  assert.ok(retryEntry.prescription, "Expected prescription field");
1879
1877
  assert.ok(
1880
1878
  /investigate with Read\+Grep BEFORE editing/.test(retryEntry.prescription),
@@ -1884,7 +1882,7 @@ describe("knowledge-extract prescriptions", () => {
1884
1882
  });
1885
1883
 
1886
1884
  it("should NOT add prescription or prescriptive tag when no heuristic matches", () => {
1887
- // retries=3 to trigger high-retry entry, but balanced tools so none of the
1885
+ // retries=3 to trigger high-hook-retry entry, but balanced tools so none of the
1888
1886
  // heuristics fire (edit<=15, apiCalls<=50, bash+edit not >3*agent).
1889
1887
  const states = [{
1890
1888
  specName: "mild-case",
@@ -1896,13 +1894,12 @@ describe("knowledge-extract prescriptions", () => {
1896
1894
  }];
1897
1895
 
1898
1896
  const patterns = extractPatternsFromStates(states);
1899
- const retryEntry = patterns.find(p => p.name === "high-retry-mild-case");
1900
- assert.ok(retryEntry, "Expected high-retry entry");
1897
+ const retryEntry = patterns.find(p => p.name === "high-hook-retry-mild-case");
1898
+ assert.ok(retryEntry, "Expected high-hook-retry entry");
1901
1899
  assert.equal(retryEntry.prescription, undefined, "No prescription when no heuristic matches");
1902
1900
  assert.ok(!retryEntry.tags.includes("prescriptive"),
1903
1901
  "'prescriptive' tag must NOT be added when no prescription");
1904
- // Original schema preserved
1905
- assert.ok(retryEntry.tags.includes("retry"));
1902
+ assert.ok(retryEntry.tags.includes("hook-retry"));
1906
1903
  assert.ok(retryEntry.description);
1907
1904
  assert.equal(retryEntry.source, "session-knowledge");
1908
1905
  });
@@ -82,15 +82,17 @@ function extractPatternsFromStates(stateObjects) {
82
82
  var label = state.specName || state._file || 'unknown';
83
83
  var prescription = derivePrescription(metrics);
84
84
 
85
- // High retry count → lesson
85
+ // High hook-retry count → lesson. Counts hook/sandbox events, not agent
86
+ // redispatches — a clean Pass@1 pipeline can still accumulate dozens.
86
87
  if (metrics.retries && metrics.retries > 2) {
87
88
  var retryEntry = {
88
89
  type: 'convention',
89
- name: 'high-retry-' + label,
90
- description: 'Pipeline required ' + metrics.retries + ' retries. Tool breakdown: ' +
90
+ name: 'high-hook-retry-' + label,
91
+ description: 'Pipeline triggered ' + metrics.retries + ' hook-level retries ' +
92
+ '(sandbox/stash-pop/re-prompts — not agent redispatches). Tool breakdown: ' +
91
93
  JSON.stringify(metrics.toolBreakdown || {}),
92
94
  source: 'session-knowledge',
93
- tags: ['retry', 'pipeline', 'lesson'],
95
+ tags: ['hook-retry', 'pipeline', 'lesson'],
94
96
  };
95
97
  if (prescription) {
96
98
  retryEntry.prescription = prescription;
@@ -33,7 +33,7 @@ function main() {
33
33
  const statesDir = path.join(claudeDir, '.pipeline-states');
34
34
  const activeSpecDir = path.join(claudeDir, 'spec', 'active');
35
35
  if (fs.existsSync(statesDir)) {
36
- const files = fs.readdirSync(statesDir).filter(f => f.endsWith('.json'));
36
+ const files = fs.readdirSync(statesDir).filter(f => f.endsWith('.json') && !f.endsWith('.metrics.json'));
37
37
  const activeBuckets = [];
38
38
  const orphanedBuckets = [];
39
39
  for (const f of files) {
@@ -49,7 +49,7 @@ function main() {
49
49
  lines.push(`## ${isOrphaned ? 'Orphaned' : 'Active'}: ${name}`);
50
50
  lines.push(`- Duration: ${duration}`);
51
51
  lines.push(`- API calls: ${m.apiCalls || 0}`);
52
- lines.push(`- Retries: ${m.retries || 0}`);
52
+ lines.push(`- Hook retries: ${m.retries || 0}`);
53
53
  if (m.toolBreakdown && Object.keys(m.toolBreakdown).length > 0) {
54
54
  lines.push('- Tool breakdown:');
55
55
  for (const [tool, count] of Object.entries(m.toolBreakdown).sort((a, b) => b[1] - a[1])) {
@@ -102,7 +102,7 @@ function main() {
102
102
  parts.push(`### ${name}`);
103
103
  parts.push(`- Duration: ${duration}`);
104
104
  parts.push(`- API calls: ${m.apiCalls || 0}`);
105
- parts.push(`- Retries: ${m.retries || 0}`);
105
+ parts.push(`- Hook retries: ${m.retries || 0}`);
106
106
  if (m.rtkSavings) {
107
107
  parts.push(`- RTK savings: ${m.rtkSavings.pct}% (${Math.round((m.rtkSavings.saved || 0) / 1000)}k tokens)`);
108
108
  }
@@ -119,7 +119,7 @@ function main() {
119
119
  parts.push('## Averages (last ' + count + ' pipelines)');
120
120
  parts.push(`- Avg duration: ${formatMs(Math.round(totalDurationMs / count))}`);
121
121
  parts.push(`- Avg API calls: ${Math.round(totalCalls / count)}`);
122
- parts.push(`- Avg retries: ${Math.round(totalRetries / count)}`);
122
+ parts.push(`- Avg hook retries: ${Math.round(totalRetries / count)}`);
123
123
  parts.push('');
124
124
  }
125
125
 
@@ -184,8 +184,9 @@ function main() {
184
184
  var pass1Pct = Math.round((pass1Count / totalPipelines) * 100);
185
185
  var avgRetries = (totalRetrySum / totalPipelines).toFixed(1);
186
186
  parts.push('## Pass@1 Metrics');
187
- parts.push('- Pass@1: ' + pass1Pct + '% (' + pass1Count + '/' + totalPipelines + ' completed without retries)');
188
- parts.push('- Avg retries per pipeline: ' + avgRetries);
187
+ parts.push('- Pass@1 (hook-level): ' + pass1Pct + '% (' + pass1Count + '/' + totalPipelines + ' completed with zero hook retries)');
188
+ parts.push('- Avg hook retries per pipeline: ' + avgRetries);
189
+ parts.push('- Note: counts hook/sandbox events, not agent redispatches. True agent-level Pass@1 not yet tracked.');
189
190
  parts.push('');
190
191
  }
191
192
  }