@ikunin/sprintpilot 2.0.9 → 2.0.10

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.
@@ -1,6 +1,6 @@
1
1
  addon:
2
2
  name: sprintpilot
3
- version: 2.0.9
3
+ version: 2.0.10
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:
@@ -15,6 +15,10 @@
15
15
  * Flags: --include, --exclude, --root
16
16
  * extensions Extension frequency histogram, descending.
17
17
  * Flags: --exclude, --root, --limit (default 20)
18
+ *
19
+ * Ignore files: .gitignore and .aiexclude at the project root are parsed and
20
+ * applied as additional excludes by default. Pass --no-respect-ignore-files
21
+ * to disable. Negation patterns (`!pattern`) are logged to stderr and skipped.
18
22
  */
19
23
 
20
24
  const fs = require('fs');
@@ -41,9 +45,74 @@ const DEFAULT_EXCLUDES = [
41
45
  '.worktrees',
42
46
  ];
43
47
 
48
+ const IGNORE_FILES = ['.gitignore', '.aiexclude'];
49
+
50
+ // Translate one .gitignore / .aiexclude line into zero or more scan.js exclude
51
+ // patterns. Blank lines and `#` comments → []. Negation (`!`) is unsupported
52
+ // and returns []; the caller reports a stderr note. Trailing `/` marks a
53
+ // directory; we expand to both `dir` and `dir/**` so descendant files are also
54
+ // excluded. Leading `/` anchors to the ignore file's directory; we strip it
55
+ // and rely on scan.js's path-anchored exclude semantics for patterns that
56
+ // contain a slash.
57
+ function parseIgnorePattern(line) {
58
+ let p = line.trim();
59
+ if (!p || p.startsWith('#')) return { patterns: [], negation: false };
60
+ if (p.startsWith('!')) return { patterns: [], negation: true };
61
+ // Unescape leading `\#` and `\!` (gitignore literal escapes).
62
+ if (p.startsWith('\\#') || p.startsWith('\\!')) p = p.slice(1);
63
+
64
+ const isDir = p.endsWith('/');
65
+ if (isDir) p = p.slice(0, -1);
66
+
67
+ const anchored = p.startsWith('/');
68
+ const body = anchored ? p.slice(1) : p;
69
+ if (!body) return { patterns: [], negation: false };
70
+ // compilePatterns handles a leading '/' by anchoring the pattern to the
71
+ // root, so we keep it intact for anchored patterns.
72
+ const prefix = anchored ? '/' : '';
73
+
74
+ const patterns = [];
75
+ if (isDir) {
76
+ patterns.push(`${prefix}${body}`);
77
+ patterns.push(`${prefix}${body}/**`);
78
+ } else {
79
+ patterns.push(`${prefix}${body}`);
80
+ // A non-anchored pattern that has no slash matches files at any depth as
81
+ // a basename, which scan.js's matcher already does. If the same name is
82
+ // also a directory anywhere in the tree, exclude its descendants too.
83
+ if (!anchored && !body.includes('/')) patterns.push(`**/${body}/**`);
84
+ }
85
+ return { patterns, negation: false };
86
+ }
87
+
88
+ function loadIgnoreFilePatterns(root) {
89
+ const out = [];
90
+ let negationCount = 0;
91
+ for (const name of IGNORE_FILES) {
92
+ const full = path.join(root, name);
93
+ let content;
94
+ try {
95
+ content = fs.readFileSync(full, 'utf8');
96
+ } catch {
97
+ continue;
98
+ }
99
+ for (const raw of content.split(/\r?\n/)) {
100
+ const { patterns, negation } = parseIgnorePattern(raw);
101
+ if (negation) negationCount++;
102
+ for (const p of patterns) out.push(p);
103
+ }
104
+ }
105
+ if (negationCount > 0) {
106
+ log.error(
107
+ `scan.js: ignored ${negationCount} negation pattern(s) from .gitignore/.aiexclude (not supported)`,
108
+ );
109
+ }
110
+ return out;
111
+ }
112
+
44
113
  function help() {
45
114
  log.out(
46
- 'Usage: scan.js <files|largest|loc|extensions> [--include <globs>] [--exclude <globs>] [--root <path>] [--limit <N>] [--count]',
115
+ 'Usage: scan.js <files|largest|loc|extensions> [--include <globs>] [--exclude <globs>] [--root <path>] [--limit <N>] [--count] [--no-respect-ignore-files]',
47
116
  );
48
117
  }
49
118
 
@@ -183,12 +252,22 @@ function toPosix(p) {
183
252
  // pathAnchored = true if the pattern contains a path separator; such patterns
184
253
  // only match the full relative path. Basename-only patterns (no '/') match
185
254
  // both the full path and the basename, so "*.ts" works at any depth.
255
+ // A leading '/' anchors the pattern to the root (relative paths have no
256
+ // leading slash, so we strip it but keep pathAnchored=true).
186
257
  function compilePatterns(patterns) {
187
- return patterns.map((p) => ({
188
- raw: p,
189
- re: globToRegex(p),
190
- pathAnchored: p.includes('/'),
191
- }));
258
+ return patterns.map((p) => {
259
+ let raw = p;
260
+ let leadingSlash = false;
261
+ if (raw.startsWith('/')) {
262
+ raw = raw.slice(1);
263
+ leadingSlash = true;
264
+ }
265
+ return {
266
+ raw,
267
+ re: globToRegex(raw),
268
+ pathAnchored: leadingSlash || raw.includes('/'),
269
+ };
270
+ });
192
271
  }
193
272
 
194
273
  function matchesAny(relPath, compiled) {
@@ -335,8 +414,8 @@ function resolveRoot(opts) {
335
414
  return root;
336
415
  }
337
416
 
338
- function buildExcludes(extra) {
339
- const list = [...DEFAULT_EXCLUDES, ...extra];
417
+ function buildExcludes(extra, ignoreFromFiles) {
418
+ const list = [...DEFAULT_EXCLUDES, ...extra, ...ignoreFromFiles];
340
419
  // Patterns: match the basename of a directory OR any path containing it.
341
420
  const patterns = [];
342
421
  const basenames = new Set();
@@ -352,10 +431,18 @@ function buildExcludes(extra) {
352
431
  return { compiled: compilePatterns(patterns), basenames };
353
432
  }
354
433
 
434
+ function ignoreFilePatternsFor(root, opts) {
435
+ if (opts['no-respect-ignore-files'] === true) return [];
436
+ return loadIgnoreFilePatterns(root);
437
+ }
438
+
355
439
  function cmdFiles(opts) {
356
440
  const root = resolveRoot(opts);
357
441
  const includes = compilePatterns(splitList(opts.include));
358
- const { compiled: excludes, basenames } = buildExcludes(splitList(opts.exclude));
442
+ const { compiled: excludes, basenames } = buildExcludes(
443
+ splitList(opts.exclude),
444
+ ignoreFilePatternsFor(root, opts),
445
+ );
359
446
  const limit = opts.limit ? Number(opts.limit) : 0;
360
447
  const count = opts.count === true || opts.count === 'true';
361
448
 
@@ -378,7 +465,10 @@ function cmdFiles(opts) {
378
465
  function cmdLargest(opts) {
379
466
  const root = resolveRoot(opts);
380
467
  const includes = compilePatterns(splitList(opts.include));
381
- const { compiled: excludes, basenames } = buildExcludes(splitList(opts.exclude));
468
+ const { compiled: excludes, basenames } = buildExcludes(
469
+ splitList(opts.exclude),
470
+ ignoreFilePatternsFor(root, opts),
471
+ );
382
472
  const limit = opts.limit ? Number(opts.limit) : 10;
383
473
 
384
474
  const heap = []; // simple array; N is small so O(files * log N) is fine
@@ -396,7 +486,10 @@ function cmdLargest(opts) {
396
486
  function cmdLoc(opts) {
397
487
  const root = resolveRoot(opts);
398
488
  const includes = compilePatterns(splitList(opts.include));
399
- const { compiled: excludes, basenames } = buildExcludes(splitList(opts.exclude));
489
+ const { compiled: excludes, basenames } = buildExcludes(
490
+ splitList(opts.exclude),
491
+ ignoreFilePatternsFor(root, opts),
492
+ );
400
493
 
401
494
  let total = 0;
402
495
  let fileCount = 0;
@@ -409,7 +502,10 @@ function cmdLoc(opts) {
409
502
 
410
503
  function cmdExtensions(opts) {
411
504
  const root = resolveRoot(opts);
412
- const { compiled: excludes, basenames } = buildExcludes(splitList(opts.exclude));
505
+ const { compiled: excludes, basenames } = buildExcludes(
506
+ splitList(opts.exclude),
507
+ ignoreFilePatternsFor(root, opts),
508
+ );
413
509
  const limit = opts.limit ? Number(opts.limit) : 20;
414
510
 
415
511
  const counts = new Map();
@@ -427,7 +523,7 @@ function cmdExtensions(opts) {
427
523
 
428
524
  function main() {
429
525
  const { opts, positional } = parseArgs(process.argv.slice(2), {
430
- booleanFlags: ['count'],
526
+ booleanFlags: ['count', 'no-respect-ignore-files'],
431
527
  });
432
528
  if (opts.help || positional.length === 0) {
433
529
  help();
@@ -19,6 +19,15 @@ Scan the project at `{{project_root}}` and write your findings to `{{output_file
19
19
  - `*.key`, `*.pem`, `*.p12` (private keys)
20
20
  - `credentials.json`, `service-account.json`
21
21
 
22
+ ## Ignore-file Awareness
23
+
24
+ Before any Glob or Grep, read `{{project_root}}/.gitignore` and
25
+ `{{project_root}}/.aiexclude` if they exist. Treat every non-comment,
26
+ non-negation pattern as an additional excluded path: skip those files and
27
+ directories entirely, do not Read them, and filter them out of pattern search
28
+ results. `scan.js` applies these patterns automatically. Skip negation (`!`)
29
+ lines.
30
+
22
31
  ## Exploration
23
32
 
24
33
  Use your native file tools (Read, Glob, Grep). The lists below describe what data to collect; pick the appropriate tool for your CLI.
@@ -20,6 +20,15 @@ Scan the project at `{{project_root}}` and write your findings to `{{output_file
20
20
  - `credentials.json`, `service-account.json`
21
21
  - Files in `.git/` directory
22
22
 
23
+ ## Ignore-file Awareness
24
+
25
+ Before any Glob or Grep, read `{{project_root}}/.gitignore` and
26
+ `{{project_root}}/.aiexclude` if they exist. Treat every non-comment,
27
+ non-negation pattern as an additional excluded path: skip those files and
28
+ directories entirely, do not Read them, and filter them out of pattern search
29
+ results. `scan.js` applies these patterns automatically. Skip negation (`!`)
30
+ lines.
31
+
23
32
  ## Exploration
24
33
 
25
34
  Use Grep for pattern searches and `scan.js` for aggregations. All Grep calls below should filter to code file types (e.g., `*.ts`, `*.js`, `*.py`, `*.java`, `*.go`, `*.rs`, `*.rb`, `*.cs`, `*.sql`, `*.sps`, `*.spb`, `*.xml`, `*.sh`, `*.c`, `*.h`, `*.cpp`, `*.hpp`, `*.cc`, `*.cxx`, `*.hxx`) and cap each result set (~20-50).
@@ -21,6 +21,15 @@ Scan the project at `{{project_root}}` and write your findings to `{{output_file
21
21
 
22
22
  **DO read**: `.env.example`, `.env.sample`, `.env.template` (safe — contain variable names only)
23
23
 
24
+ ## Ignore-file Awareness
25
+
26
+ Before any Glob or Grep, read `{{project_root}}/.gitignore` and
27
+ `{{project_root}}/.aiexclude` if they exist. Treat every non-comment,
28
+ non-negation pattern as an additional excluded path: skip those files and
29
+ directories entirely, do not Read them, and filter them out of pattern search
30
+ results. `scan.js` applies these patterns automatically. Skip negation (`!`)
31
+ lines.
32
+
24
33
  ## Exploration
25
34
 
26
35
  Use Grep and Read. Below are the patterns to search for — file-type filters match the original language coverage (`*.ts`, `*.js`, `*.py`, `*.rb`, `*.go`, `*.rs`, `*.java`, `*.sh`, `*.c`, `*.h`, `*.cpp`, `*.hpp`, `*.cc`, `*.cxx`, `*.hxx`, `*.sql`, `*.sps`, `*.spb`, `*.xml`). Cap each result set (~15-30 matches).
@@ -19,6 +19,15 @@ Scan the project at `{{project_root}}` and write your findings to `{{output_file
19
19
  - `*.key`, `*.pem`, `*.p12` (private keys)
20
20
  - `credentials.json`, `service-account.json`
21
21
 
22
+ ## Ignore-file Awareness
23
+
24
+ Before any Glob or Grep, read `{{project_root}}/.gitignore` and
25
+ `{{project_root}}/.aiexclude` if they exist. Treat every non-comment,
26
+ non-negation pattern as an additional excluded path: skip those files and
27
+ directories entirely, do not Read them, and filter them out of pattern search
28
+ results. `scan.js` applies these patterns automatically. Skip negation (`!`)
29
+ lines.
30
+
22
31
  ## Exploration
23
32
 
24
33
  Use your native file tools (Read, Glob, Grep) plus the `scan.js` helper for aggregations.
@@ -20,6 +20,16 @@ Scan the project at `{{project_root}}` and write your findings to `{{output_file
20
20
  - `credentials.json`, `service-account.json`
21
21
  - `*.secret`, `*password*`, `*token*` (in filenames)
22
22
 
23
+ ## Ignore-file Awareness
24
+
25
+ Before any Glob or Grep, read `{{project_root}}/.gitignore` and
26
+ `{{project_root}}/.aiexclude` if they exist. Treat every non-comment,
27
+ non-negation pattern as an additional excluded path: skip those files and
28
+ directories entirely, do not Read them, and filter them out of any pattern
29
+ search results. `scan.js` already applies these patterns automatically — pass
30
+ `--no-respect-ignore-files` only if you have a deliberate reason. Skip negation
31
+ (`!`) lines.
32
+
23
33
  ## Exploration
24
34
 
25
35
  Gather data using your native file tools (Read, Glob, Grep). The commands below are illustrative — use the equivalent tool from your CLI. Skip files that don't exist; do not fail the task on missing manifests.
@@ -21,6 +21,8 @@ Complementary, not a replacement. `bmad-document-project` generates comprehensiv
21
21
  <action>Create output directory `{output_folder}/codebase-analysis` (use your native file-create tool; it will create parent directories as needed). The first `Write` tool call targeting a file inside this directory will auto-create it, so no explicit mkdir is required in practice.</action>
22
22
  <action>Determine project root absolute path: `{{project_root}}`</action>
23
23
 
24
+ <action>Note: each agent reads `{{project_root}}/.gitignore` and `{{project_root}}/.aiexclude` (if present) and treats every listed file/directory as off-limits. The `scan.js` helper applies these patterns automatically; agents apply them to their Glob/Grep operations as well. This prevents inclusion of build artifacts, vendored code, secrets, and explicitly AI-excluded paths in the analysis.</action>
25
+
24
26
  ---
25
27
 
26
28
  ## Step 2 — Launch 5 Analysis Agents in Parallel
@@ -6,6 +6,13 @@ You are extracting module boundaries and component contracts from an existing co
6
6
 
7
7
  Using the architecture-analysis.md analysis as a starting point, go deeper: trace actual imports, identify public APIs, and map the internal dependency graph.
8
8
 
9
+ ## Ignore-file Awareness
10
+
11
+ Before any Glob or Grep, read the project's `.gitignore` and `.aiexclude` if
12
+ they exist. Treat every non-comment, non-negation pattern as an additional
13
+ excluded path: skip those files and directories entirely, do not Read them,
14
+ and filter them out of pattern search results. Skip negation (`!`) lines.
15
+
9
16
  ## Method
10
17
 
11
18
  1. For each module/directory identified in architecture-analysis.md:
@@ -6,6 +6,13 @@ You are tracing how data flows through the system — from entry points to stora
6
6
 
7
7
  Using architecture-analysis.md and integrations-analysis.md as context, trace the actual request/data paths through the code.
8
8
 
9
+ ## Ignore-file Awareness
10
+
11
+ Before any Glob or Grep, read the project's `.gitignore` and `.aiexclude` if
12
+ they exist. Treat every non-comment, non-negation pattern as an additional
13
+ excluded path: skip those files and directories entirely, do not Read them,
14
+ and filter them out of pattern search results. Skip negation (`!`) lines.
15
+
9
16
  ## Method
10
17
 
11
18
  1. Start from entry points (routes, CLI handlers, event listeners)
@@ -6,6 +6,13 @@ You are identifying design patterns, conventions, and architectural decisions em
6
6
 
7
7
  Using architecture-analysis.md and stack-analysis.md as context, identify the actual patterns the codebase follows (not what it claims to follow).
8
8
 
9
+ ## Ignore-file Awareness
10
+
11
+ Before any Glob or Grep, read the project's `.gitignore` and `.aiexclude` if
12
+ they exist. Treat every non-comment, non-negation pattern as an additional
13
+ excluded path: skip those files and directories entirely, do not Read them,
14
+ and filter them out of pattern search results. Skip negation (`!`) lines.
15
+
9
16
  ## Method
10
17
 
11
18
  1. Look for structural patterns: Factory, Repository, Observer, Middleware, Decorator
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikunin/sprintpilot",
3
- "version": "2.0.9",
3
+ "version": "2.0.10",
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": {