@ikunin/sprintpilot 2.2.6 → 2.2.8

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.
@@ -56,7 +56,65 @@ function canonicalizeYaml(text) {
56
56
  return `${lines.join('\n')}\n`;
57
57
  }
58
58
 
59
+ // Directory names always pruned from fingerprint walks. These are
60
+ // regenerable build/cache artifacts that change between sessions for
61
+ // reasons unrelated to BMad state (Python bytecode, dependency
62
+ // installs, transpiler output, OS metadata). Without pruning, a single
63
+ // halt fingerprint can balloon to 100s of MB and a `.pyc` regen on
64
+ // resume produces spurious divergence prompts.
65
+ //
66
+ // Real-world trigger: a user's `_bmad-output/spikes/<name>/.venv/` was
67
+ // 794 MB; every halt entry captured every path inside it.
68
+ const FINGERPRINT_PRUNE_DIRS = new Set([
69
+ '.venv',
70
+ 'venv',
71
+ 'env',
72
+ 'node_modules',
73
+ '__pycache__',
74
+ '.pytest_cache',
75
+ '.mypy_cache',
76
+ '.ruff_cache',
77
+ '.tox',
78
+ '.gradle',
79
+ 'target',
80
+ 'dist',
81
+ 'build',
82
+ '.next',
83
+ '.nuxt',
84
+ '.cache',
85
+ '.parcel-cache',
86
+ '.turbo',
87
+ '.git',
88
+ '.svn',
89
+ '.hg',
90
+ '.idea',
91
+ '.vscode',
92
+ // BMad/Sprintpilot internal — worktrees aren't fingerprinted; they're
93
+ // captured separately via context.worktreeScanner.
94
+ '.worktrees',
95
+ ]);
96
+
97
+ // File suffixes always pruned. Generated / binary content that changes
98
+ // for non-state reasons.
99
+ const FINGERPRINT_PRUNE_SUFFIXES = [
100
+ '.pyc',
101
+ '.pyo',
102
+ '.pyd',
103
+ '.so',
104
+ '.o',
105
+ '.class',
106
+ '.DS_Store',
107
+ ];
108
+
109
+ // Hard cap on number of entries in the fingerprint tree. Defends against
110
+ // pathological cases where prune lists don't catch a large embedded
111
+ // dependency tree. When hit, the walk stops and `out[__truncated__]`
112
+ // is set so callers know the fingerprint is incomplete (treated as
113
+ // divergent on diff to avoid false-negative resume).
114
+ const FINGERPRINT_MAX_ENTRIES = 5000;
115
+
59
116
  function walkTree(fs, root, out, relBase) {
117
+ if (out.__truncated__) return;
60
118
  let entries;
61
119
  try {
62
120
  entries = fs.readdirSync(root, { withFileTypes: true });
@@ -64,14 +122,31 @@ function walkTree(fs, root, out, relBase) {
64
122
  return;
65
123
  }
66
124
  for (const ent of entries) {
125
+ if (out.__truncated__) return;
126
+ if (FINGERPRINT_PRUNE_DIRS.has(ent.name)) continue;
67
127
  const full = path.join(root, ent.name);
68
128
  const rel = path.join(relBase, ent.name);
69
129
  if (ent.isDirectory()) {
70
130
  walkTree(fs, full, out, rel);
71
131
  } else if (ent.isFile()) {
132
+ let prune = false;
133
+ for (const sfx of FINGERPRINT_PRUNE_SUFFIXES) {
134
+ if (ent.name.endsWith(sfx)) {
135
+ prune = true;
136
+ break;
137
+ }
138
+ }
139
+ if (prune) continue;
72
140
  try {
73
141
  const st = fs.statSync(full);
74
142
  out[rel.split(path.sep).join('/')] = st.size;
143
+ // -1 for the future __truncated__ marker; -2 below for the actual count
144
+ // (avoid counting the marker itself).
145
+ const count = Object.keys(out).length - (out.__truncated__ ? 1 : 0);
146
+ if (count >= FINGERPRINT_MAX_ENTRIES) {
147
+ out.__truncated__ = true;
148
+ return;
149
+ }
75
150
  } catch (_e) {
76
151
  // ignore unreadable
77
152
  }
@@ -157,7 +157,17 @@ function verifyCreateStory(state, _out, ctx) {
157
157
  else {
158
158
  const text = readFileSafe(ctx.fs, state.story_file_path);
159
159
  const fm = frontMatter(text);
160
- if (!fm) issues.push('story file missing YAML front-matter');
160
+ // Escape hatch: when the LLM sends verify_override with evidence
161
+ // {acknowledge_missing_front_matter: true, decision_log_ref: '...'},
162
+ // skip the front-matter check ONLY for this verification call. AC +
163
+ // Tasks checks still run. Auditable via the verify_override ledger
164
+ // entry which captures evidence verbatim. Used when bmad-create-story
165
+ // can't or won't regenerate front-matter (e.g., legacy story files
166
+ // in repos that pre-date the front-matter convention and have a
167
+ // body the skill wants to preserve).
168
+ const override = ctx.augmented || {};
169
+ const ackMissingFm = override && override.acknowledge_missing_front_matter === true;
170
+ if (!fm && !ackMissingFm) issues.push('story file missing YAML front-matter');
161
171
  // AC presence — look for "## Acceptance Criteria" section with at least one bullet.
162
172
  if (text && !/##\s+Acceptance Criteria[\s\S]*?\n-\s+/.test(text)) {
163
173
  issues.push('Acceptance Criteria section missing or empty');
@@ -1,6 +1,6 @@
1
1
  addon:
2
2
  name: sprintpilot
3
- version: 2.2.6
3
+ version: 2.2.8
4
4
  description: Sprintpilot — autopilot and multi-agent addon for BMad Method (git workflow, parallel agents, autonomous story execution)
5
5
  bmad_compatibility: ">=6.2.0"
6
6
  modules:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikunin/sprintpilot",
3
- "version": "2.2.6",
3
+ "version": "2.2.8",
4
4
  "description": "Sprintpilot — autopilot and multi-agent addon for BMad Method v6: git workflow, parallel agents, autonomous story execution",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {