@slope-dev/slope 1.17.0 → 1.20.0

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.
Files changed (83) hide show
  1. package/dist/cli/commands/guard.d.ts.map +1 -1
  2. package/dist/cli/commands/guard.js +36 -11
  3. package/dist/cli/commands/guard.js.map +1 -1
  4. package/dist/cli/commands/loop.js +1 -1
  5. package/dist/cli/commands/review-state.d.ts.map +1 -1
  6. package/dist/cli/commands/review-state.js +6 -45
  7. package/dist/cli/commands/review-state.js.map +1 -1
  8. package/dist/cli/commands/review.d.ts.map +1 -1
  9. package/dist/cli/commands/review.js +3 -0
  10. package/dist/cli/commands/review.js.map +1 -1
  11. package/dist/cli/commands/sprint.d.ts +2 -0
  12. package/dist/cli/commands/sprint.d.ts.map +1 -0
  13. package/dist/cli/commands/sprint.js +96 -0
  14. package/dist/cli/commands/sprint.js.map +1 -0
  15. package/dist/cli/commands/validate.d.ts.map +1 -1
  16. package/dist/cli/commands/validate.js +7 -1
  17. package/dist/cli/commands/validate.js.map +1 -1
  18. package/dist/cli/guards/next-action.d.ts.map +1 -1
  19. package/dist/cli/guards/next-action.js +15 -0
  20. package/dist/cli/guards/next-action.js.map +1 -1
  21. package/dist/cli/guards/plan-analysis.d.ts +34 -0
  22. package/dist/cli/guards/plan-analysis.d.ts.map +1 -0
  23. package/dist/cli/guards/plan-analysis.js +102 -0
  24. package/dist/cli/guards/plan-analysis.js.map +1 -0
  25. package/dist/cli/guards/pr-review.d.ts.map +1 -1
  26. package/dist/cli/guards/pr-review.js +7 -0
  27. package/dist/cli/guards/pr-review.js.map +1 -1
  28. package/dist/cli/guards/review-tier.d.ts +4 -2
  29. package/dist/cli/guards/review-tier.d.ts.map +1 -1
  30. package/dist/cli/guards/review-tier.js +104 -60
  31. package/dist/cli/guards/review-tier.js.map +1 -1
  32. package/dist/cli/guards/sprint-completion.d.ts +11 -0
  33. package/dist/cli/guards/sprint-completion.d.ts.map +1 -0
  34. package/dist/cli/guards/sprint-completion.js +121 -0
  35. package/dist/cli/guards/sprint-completion.js.map +1 -0
  36. package/dist/cli/guards/workflow-gate.d.ts.map +1 -1
  37. package/dist/cli/guards/workflow-gate.js +9 -1
  38. package/dist/cli/guards/workflow-gate.js.map +1 -1
  39. package/dist/cli/index.js +8 -0
  40. package/dist/cli/index.js.map +1 -1
  41. package/dist/cli/loop/continuous.d.ts.map +1 -1
  42. package/dist/cli/loop/continuous.js +36 -0
  43. package/dist/cli/loop/continuous.js.map +1 -1
  44. package/dist/cli/loop/executor.d.ts.map +1 -1
  45. package/dist/cli/loop/executor.js +92 -50
  46. package/dist/cli/loop/executor.js.map +1 -1
  47. package/dist/cli/loop/guard-runner.d.ts +19 -3
  48. package/dist/cli/loop/guard-runner.d.ts.map +1 -1
  49. package/dist/cli/loop/guard-runner.js +108 -1
  50. package/dist/cli/loop/guard-runner.js.map +1 -1
  51. package/dist/cli/loop/model-selector.d.ts.map +1 -1
  52. package/dist/cli/loop/model-selector.js +3 -2
  53. package/dist/cli/loop/model-selector.js.map +1 -1
  54. package/dist/cli/loop/planner.d.ts +21 -0
  55. package/dist/cli/loop/planner.d.ts.map +1 -0
  56. package/dist/cli/loop/planner.js +298 -0
  57. package/dist/cli/loop/planner.js.map +1 -0
  58. package/dist/cli/loop/pr-lifecycle.d.ts +6 -4
  59. package/dist/cli/loop/pr-lifecycle.d.ts.map +1 -1
  60. package/dist/cli/loop/pr-lifecycle.js +19 -17
  61. package/dist/cli/loop/pr-lifecycle.js.map +1 -1
  62. package/dist/cli/loop/staging.d.ts +19 -0
  63. package/dist/cli/loop/staging.d.ts.map +1 -0
  64. package/dist/cli/loop/staging.js +125 -0
  65. package/dist/cli/loop/staging.js.map +1 -0
  66. package/dist/cli/loop/types.d.ts +31 -1
  67. package/dist/cli/loop/types.d.ts.map +1 -1
  68. package/dist/cli/loop/worktree.d.ts +1 -1
  69. package/dist/cli/loop/worktree.d.ts.map +1 -1
  70. package/dist/cli/loop/worktree.js +5 -2
  71. package/dist/cli/loop/worktree.js.map +1 -1
  72. package/dist/cli/registry.d.ts.map +1 -1
  73. package/dist/cli/registry.js +1 -0
  74. package/dist/cli/registry.js.map +1 -1
  75. package/dist/cli/sprint-state.d.ts +27 -0
  76. package/dist/cli/sprint-state.d.ts.map +1 -0
  77. package/dist/cli/sprint-state.js +88 -0
  78. package/dist/cli/sprint-state.js.map +1 -0
  79. package/dist/core/guard.d.ts +1 -1
  80. package/dist/core/guard.d.ts.map +1 -1
  81. package/dist/core/guard.js +26 -4
  82. package/dist/core/guard.js.map +1 -1
  83. package/package.json +1 -1
@@ -1,12 +1,83 @@
1
1
  import { execSync, execFileSync } from 'node:child_process';
2
2
  const SHA_PATTERN = /^[0-9a-f]{40}$/;
3
+ /**
4
+ * Check if the diff since preSha is within the ticket's module scope.
5
+ * A file is in scope if it matches a module path, is under a module directory,
6
+ * or is a test file for an in-scope module.
7
+ */
8
+ export function isDiffInScope(preSha, modules, cwd) {
9
+ if (modules.length === 0)
10
+ return { inScope: true, outOfScopeFiles: [] };
11
+ let changedFiles;
12
+ try {
13
+ const output = execFileSync('git', ['diff', '--name-only', preSha, 'HEAD'], {
14
+ cwd,
15
+ encoding: 'utf8',
16
+ });
17
+ changedFiles = output.split('\n').map(f => f.trim()).filter(Boolean);
18
+ }
19
+ catch {
20
+ // If diff fails, assume in scope (don't block on errors)
21
+ return { inScope: true, outOfScopeFiles: [] };
22
+ }
23
+ if (changedFiles.length === 0)
24
+ return { inScope: true, outOfScopeFiles: [] };
25
+ const outOfScopeFiles = [];
26
+ for (const file of changedFiles) {
27
+ const fileInScope = modules.some(mod => {
28
+ // Direct path match
29
+ if (file === mod)
30
+ return true;
31
+ // File is under a module directory
32
+ if (file.startsWith(mod.endsWith('/') ? mod : mod + '/'))
33
+ return true;
34
+ // Test file for an in-scope module: test path mirrors src path
35
+ // e.g., "tests/cli/loop/planner.test.ts" is in scope for "src/cli/loop/planner.ts"
36
+ if (file.includes('.test.')) {
37
+ const srcEquiv = file
38
+ .replace(/^tests\//, 'src/')
39
+ .replace(/\.test\.(ts|js)$/, '.$1');
40
+ if (srcEquiv === mod)
41
+ return true;
42
+ if (srcEquiv.startsWith(mod + '/'))
43
+ return true;
44
+ // Also check if the test is under a module directory in tests/
45
+ const testDir = mod.replace(/^src\//, 'tests/');
46
+ if (file.startsWith(testDir + '/') || file.startsWith(testDir))
47
+ return true;
48
+ }
49
+ return false;
50
+ });
51
+ if (!fileInScope) {
52
+ outOfScopeFiles.push(file);
53
+ }
54
+ }
55
+ // Threshold: more than half of files must be out of scope to flag (warn-only)
56
+ const SCOPE_THRESHOLD = 0.5;
57
+ const inScope = outOfScopeFiles.length / changedFiles.length <= SCOPE_THRESHOLD;
58
+ return { inScope, outOfScopeFiles };
59
+ }
3
60
  /**
4
61
  * Run post-ticket guards: typecheck + tests.
5
62
  * If either fails, auto-revert to preSha.
6
63
  *
7
64
  * @returns Guard result with pass/fail status
8
65
  */
9
- export function runGuards(preSha, config, cwd, log) {
66
+ export function runGuards(preSha, config, cwd, log, ticket) {
67
+ // Guard 0a: Diff scope — warn if changes are outside ticket modules
68
+ if (ticket && ticket.modules.length > 0) {
69
+ const scopeResult = isDiffInScope(preSha, ticket.modules, cwd);
70
+ if (!scopeResult.inScope) {
71
+ log.warn(`Diff scope warning: ${scopeResult.outOfScopeFiles.length} file(s) outside ticket modules: ${scopeResult.outOfScopeFiles.join(', ')}`);
72
+ // Warn only — do NOT return failure
73
+ }
74
+ }
75
+ // Guard 0b: Substantiveness — detect stub/whitespace-only changes
76
+ if (!isSubstantive(preSha, cwd)) {
77
+ log.warn('Changes are not substantive (comments/whitespace only) — reverting');
78
+ revert(preSha, cwd);
79
+ return { passed: false, failedGuard: 'substantiveness' };
80
+ }
10
81
  // Guard 1: Typecheck
11
82
  try {
12
83
  execSync('pnpm typecheck', { cwd, stdio: 'pipe', timeout: 120_000 });
@@ -39,6 +110,42 @@ function revert(sha, cwd) {
39
110
  }
40
111
  catch { /* ok */ }
41
112
  }
113
+ /**
114
+ * Check if changes since preSha are substantive (not just comments/whitespace).
115
+ * Uses `git diff --stat` to count insertions/deletions, then checks
116
+ * if the non-comment/non-whitespace diff is meaningful.
117
+ */
118
+ export function isSubstantive(preSha, cwd) {
119
+ try {
120
+ // Get the actual code diff (ignore whitespace changes)
121
+ const diff = execFileSync('git', ['diff', '-w', '--no-color', preSha, 'HEAD'], {
122
+ cwd,
123
+ encoding: 'utf8',
124
+ maxBuffer: 1024 * 1024,
125
+ });
126
+ // Extract only added/removed lines (not diff headers)
127
+ const changedLines = diff.split('\n').filter(line => (line.startsWith('+') || line.startsWith('-')) &&
128
+ !line.startsWith('+++') && !line.startsWith('---'));
129
+ // Filter out comment-only and empty lines
130
+ const substantiveLines = changedLines.filter(line => {
131
+ const content = line.slice(1).trim(); // Remove +/- prefix
132
+ if (content === '')
133
+ return false;
134
+ // Skip pure comment lines
135
+ if (content.startsWith('//') || content.startsWith('/*') || content.startsWith('*') || content.startsWith('#'))
136
+ return false;
137
+ // Skip JSDoc-only lines
138
+ if (content.startsWith('/**') || content === '*/')
139
+ return false;
140
+ return true;
141
+ });
142
+ return substantiveLines.length >= 3;
143
+ }
144
+ catch {
145
+ // If diff fails, assume substantive (don't block on errors)
146
+ return true;
147
+ }
148
+ }
42
149
  /** Count commits that would be reverted */
43
150
  function countRevertable(preSha, cwd) {
44
151
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"guard-runner.js","sourceRoot":"","sources":["../../../src/cli/loop/guard-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAS5D,MAAM,WAAW,GAAG,gBAAgB,CAAC;AAErC;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CACvB,MAAc,EACd,MAAkB,EAClB,GAAW,EACX,GAAW;IAEX,qBAAqB;IACrB,IAAI,CAAC;QACH,QAAQ,CAAC,gBAAgB,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACjD,GAAG,CAAC,KAAK,CAAC,yCAAyC,WAAW,YAAY,CAAC,CAAC;QAC5E,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACpB,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;IACrD,CAAC;IAED,wCAAwC;IACxC,IAAI,CAAC;QACH,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACjD,GAAG,CAAC,KAAK,CAAC,qCAAqC,WAAW,YAAY,CAAC,CAAC;QACxE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACpB,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;IACjD,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED,iDAAiD;AACjD,SAAS,MAAM,CAAC,GAAW,EAAE,GAAW;IACtC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,GAAG,EAAE,CAAC,CAAC;IACnE,YAAY,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACtE,IAAI,CAAC;QACH,YAAY,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC;AACtB,CAAC;AAED,2CAA2C;AAC3C,SAAS,eAAe,CAAC,MAAc,EAAE,GAAW;IAClD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1F,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,KAAK,IAAI,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACnH,OAAO,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"guard-runner.js","sourceRoot":"","sources":["../../../src/cli/loop/guard-runner.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../src/cli/loop/model-selector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAe,aAAa,EAAE,MAAM,YAAY,CAAC;AAE/E;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,GACnC,MAAM,CA8BR;AAED,iDAAiD;AACjD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAEvE;AAED,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD"}
1
+ {"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../src/cli/loop/model-selector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAe,aAAa,EAAE,MAAM,YAAY,CAAC;AAE/E;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,GACnC,MAAM,CA+BR;AAED,iDAAiD;AACjD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAEvE;AAED,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD"}
@@ -15,8 +15,9 @@ export function selectModel(club, maxFiles, estTokens, config, cwd, strategy) {
15
15
  // 2. Multi-file routing: 2+ files → API
16
16
  if (maxFiles >= 2)
17
17
  return config.modelApi;
18
- // 3. Strategy-based: documentation tickets need API model
19
- if (strategy === 'documentation')
18
+ // 3. Strategy-based: documentation and roadmap tickets need API model
19
+ // (local models struggle with prose; roadmap tickets are curated features)
20
+ if (strategy === 'documentation' || strategy === 'roadmap')
20
21
  return config.modelApi;
21
22
  // 4. Data-driven overrides from model-config.json
22
23
  const modelConfig = loadModelConfig(cwd);
@@ -1 +1 @@
1
- {"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../../src/cli/loop/model-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,IAAU,EACV,QAAgB,EAChB,SAAiB,EACjB,MAAkB,EAClB,GAAW,EACX,QAAoC;IAEpC,2DAA2D;IAC3D,IAAI,SAAS,GAAG,KAAK;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC;IAE9C,wCAAwC;IACxC,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC;IAE1C,0DAA0D;IAC1D,IAAI,QAAQ,KAAK,eAAe;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC;IAEzD,kDAAkD;IAClD,MAAM,WAAW,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,GAAG,GAAG,WAAW,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,GAAG,EAAE,KAAK,KAAK,KAAK;YAAE,OAAO,MAAM,CAAC,QAAQ,CAAC;QACjD,IAAI,GAAG,EAAE,KAAK,KAAK,OAAO;YAAE,OAAO,MAAM,CAAC,UAAU,CAAC;IACvD,CAAC;IAED,mBAAmB;IACnB,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ,CAAC;QACd,KAAK,OAAO,CAAC;QACb,KAAK,YAAY;YACf,OAAO,MAAM,CAAC,UAAU,CAAC;QAC3B,KAAK,WAAW,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC,QAAQ,CAAC;QACzB;YACE,OAAO,MAAM,CAAC,UAAU,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,MAAkB;IAC7D,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC;AACjF,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,0CAA0C;AAC1C,SAAS,eAAe,CAAC,GAAW;IAClC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,8BAA8B,CAAC,CAAC;IAC7D,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAgB,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../../src/cli/loop/model-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,IAAU,EACV,QAAgB,EAChB,SAAiB,EACjB,MAAkB,EAClB,GAAW,EACX,QAAoC;IAEpC,2DAA2D;IAC3D,IAAI,SAAS,GAAG,KAAK;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC;IAE9C,wCAAwC;IACxC,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC;IAE1C,sEAAsE;IACtE,8EAA8E;IAC9E,IAAI,QAAQ,KAAK,eAAe,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC;IAEnF,kDAAkD;IAClD,MAAM,WAAW,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,GAAG,GAAG,WAAW,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,GAAG,EAAE,KAAK,KAAK,KAAK;YAAE,OAAO,MAAM,CAAC,QAAQ,CAAC;QACjD,IAAI,GAAG,EAAE,KAAK,KAAK,OAAO;YAAE,OAAO,MAAM,CAAC,UAAU,CAAC;IACvD,CAAC;IAED,mBAAmB;IACnB,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ,CAAC;QACd,KAAK,OAAO,CAAC;QACb,KAAK,YAAY;YACf,OAAO,MAAM,CAAC,UAAU,CAAC;QAC3B,KAAK,WAAW,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC,QAAQ,CAAC;QACzB;YACE,OAAO,MAAM,CAAC,UAAU,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,MAAkB;IAC7D,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC;AACjF,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,0CAA0C;AAC1C,SAAS,eAAe,CAAC,GAAW;IAClC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,8BAA8B,CAAC,CAAC;IAC7D,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAgB,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { BacklogTicket, ExecutionPlan, PlanFileEntry } from './types.js';
2
+ import type { Logger } from './logger.js';
3
+ /**
4
+ * Extract top keywords from text for grep discovery.
5
+ */
6
+ export declare function extractKeywords(text: string, max?: number): string[];
7
+ /**
8
+ * Tier 1.5: Use ticket.modules for scoped file discovery.
9
+ * Includes module files themselves (if they exist on disk) plus grep within module paths.
10
+ */
11
+ export declare function tier15Modules(ticket: BacklogTicket, cwd: string): PlanFileEntry[] | null;
12
+ /**
13
+ * Generate a concrete execution plan for a ticket using 4-tier file discovery.
14
+ * No embeddings required.
15
+ */
16
+ export declare function generatePlan(ticket: BacklogTicket, model: string, cwd: string, log: Logger): ExecutionPlan;
17
+ /**
18
+ * Format an ExecutionPlan as a structured Aider --message prompt.
19
+ */
20
+ export declare function formatPlanAsPrompt(plan: ExecutionPlan, ticket: BacklogTicket): string;
21
+ //# sourceMappingURL=planner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner.d.ts","sourceRoot":"","sources":["../../../src/cli/loop/planner.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAY1C;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAI,GAAG,MAAM,EAAE,CAgB/D;AA8CD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,EAAE,GAAG,IAAI,CAoDxF;AA8ED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,aAAa,CAqDf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM,CA2CrF"}
@@ -0,0 +1,298 @@
1
+ // Planner — generates concrete execution plans for Aider without embeddings.
2
+ // 4-tier file discovery: enriched → modules → grep → generic.
3
+ import { execFileSync } from 'node:child_process';
4
+ import { existsSync, readFileSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { collectTestFiles } from '../../core/prep.js';
7
+ import { isLocalModel } from './model-selector.js';
8
+ // Stop words for keyword extraction
9
+ const STOP_WORDS = new Set([
10
+ 'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
11
+ 'of', 'with', 'by', 'from', 'is', 'are', 'was', 'were', 'be', 'been',
12
+ 'has', 'have', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
13
+ 'should', 'may', 'might', 'can', 'this', 'that', 'these', 'those',
14
+ 'it', 'its', 'as', 'if', 'not', 'no', 'so', 'up', 'out', 'all',
15
+ 'add', 'update', 'fix', 'remove', 'change', 'make', 'use', 'new',
16
+ ]);
17
+ /**
18
+ * Extract top keywords from text for grep discovery.
19
+ */
20
+ export function extractKeywords(text, max = 3) {
21
+ const words = text.toLowerCase()
22
+ .replace(/[^a-z0-9\s_-]/g, ' ')
23
+ .split(/\s+/)
24
+ .filter(w => w.length > 2 && !STOP_WORDS.has(w));
25
+ // Count frequency
26
+ const freq = new Map();
27
+ for (const w of words) {
28
+ freq.set(w, (freq.get(w) ?? 0) + 1);
29
+ }
30
+ return [...freq.entries()]
31
+ .sort((a, b) => b[1] - a[1])
32
+ .slice(0, max)
33
+ .map(([w]) => w);
34
+ }
35
+ /**
36
+ * Read the first N lines of a file and match against acceptance criteria
37
+ * to produce a specific action description.
38
+ */
39
+ function inferAction(filePath, ticket, cwd) {
40
+ const fullPath = join(cwd, filePath);
41
+ let header = '';
42
+ try {
43
+ const content = readFileSync(fullPath, 'utf8');
44
+ header = content.split('\n').slice(0, 50).join('\n');
45
+ }
46
+ catch {
47
+ return { action: 'Modify as described in ticket', reason: ticket.acceptance_criteria[0] ?? ticket.title };
48
+ }
49
+ // Try to match acceptance criteria keywords against file content
50
+ for (const ac of ticket.acceptance_criteria) {
51
+ const acWords = ac.toLowerCase().split(/\s+/).filter(w => w.length > 3);
52
+ for (const word of acWords) {
53
+ if (header.toLowerCase().includes(word)) {
54
+ return { action: `Modify to satisfy: ${ac}`, reason: ac };
55
+ }
56
+ }
57
+ }
58
+ // Fallback: use ticket description keywords
59
+ return { action: `Update per ticket requirements`, reason: ticket.acceptance_criteria[0] ?? ticket.title };
60
+ }
61
+ /**
62
+ * Tier 1: Use enriched primary files from the ticket.
63
+ */
64
+ function tier1Enriched(ticket, cwd) {
65
+ if (!ticket.files?.primary || ticket.files.primary.length === 0)
66
+ return null;
67
+ const entries = [];
68
+ for (const f of ticket.files.primary) {
69
+ if (!f || !existsSync(join(cwd, f)))
70
+ continue;
71
+ const { action, reason } = inferAction(f, ticket, cwd);
72
+ entries.push({ path: f, action, reason });
73
+ }
74
+ return entries.length > 0 ? entries : null;
75
+ }
76
+ /**
77
+ * Tier 1.5: Use ticket.modules for scoped file discovery.
78
+ * Includes module files themselves (if they exist on disk) plus grep within module paths.
79
+ */
80
+ export function tier15Modules(ticket, cwd) {
81
+ if (!ticket.modules || ticket.modules.length === 0)
82
+ return null;
83
+ const matchedFiles = new Set();
84
+ for (const mod of ticket.modules) {
85
+ const fullPath = join(cwd, mod);
86
+ // Include the module file itself if it exists
87
+ if (existsSync(fullPath) && !mod.endsWith('/')) {
88
+ matchedFiles.add(mod);
89
+ }
90
+ // Grep keywords within the module path (file's parent dir or directory itself)
91
+ const keywords = extractKeywords(`${ticket.title} ${ticket.description}`);
92
+ // Use relative path for grep so output is relative to cwd
93
+ const relSearchDir = existsSync(fullPath) && !mod.endsWith('/')
94
+ ? join(mod, '..') // parent dir of file (relative)
95
+ : mod; // directory itself (relative)
96
+ if (!existsSync(join(cwd, relSearchDir)))
97
+ continue;
98
+ for (const keyword of keywords) {
99
+ if (matchedFiles.size >= 5)
100
+ break;
101
+ try {
102
+ const output = execFileSync('grep', [
103
+ '-rl', '--include=*.ts', '--include=*.js',
104
+ keyword,
105
+ relSearchDir,
106
+ ], { cwd, encoding: 'utf8', timeout: 5000 });
107
+ for (const line of output.split('\n')) {
108
+ const trimmed = line.trim();
109
+ if (!trimmed || trimmed.includes('.test.') || trimmed.includes('node_modules'))
110
+ continue;
111
+ matchedFiles.add(trimmed);
112
+ if (matchedFiles.size >= 5)
113
+ break;
114
+ }
115
+ }
116
+ catch {
117
+ // grep returns exit code 1 when no matches — not an error
118
+ }
119
+ }
120
+ }
121
+ if (matchedFiles.size === 0)
122
+ return null;
123
+ const entries = [];
124
+ for (const f of matchedFiles) {
125
+ const { action, reason } = inferAction(f, ticket, cwd);
126
+ entries.push({ path: f, action, reason });
127
+ }
128
+ return entries;
129
+ }
130
+ /**
131
+ * Tier 2: Grep for keywords from ticket title/description.
132
+ */
133
+ function tier2Grep(ticket, cwd) {
134
+ const keywords = extractKeywords(`${ticket.title} ${ticket.description}`);
135
+ if (keywords.length === 0)
136
+ return null;
137
+ const matchedFiles = new Set();
138
+ for (const keyword of keywords) {
139
+ if (matchedFiles.size >= 5)
140
+ break;
141
+ try {
142
+ const output = execFileSync('grep', [
143
+ '-rl', '--include=*.ts', '--include=*.js',
144
+ keyword,
145
+ 'src/', 'packages/',
146
+ ], { cwd, encoding: 'utf8', timeout: 5000 });
147
+ for (const line of output.split('\n')) {
148
+ const trimmed = line.trim();
149
+ if (trimmed && !trimmed.includes('.test.') && !trimmed.includes('node_modules')) {
150
+ matchedFiles.add(trimmed);
151
+ }
152
+ if (matchedFiles.size >= 5)
153
+ break;
154
+ }
155
+ }
156
+ catch {
157
+ // grep returns exit code 1 when no matches — not an error
158
+ }
159
+ }
160
+ if (matchedFiles.size === 0)
161
+ return null;
162
+ const entries = [];
163
+ for (const f of matchedFiles) {
164
+ const { action, reason } = inferAction(f, ticket, cwd);
165
+ entries.push({ path: f, action, reason });
166
+ }
167
+ return entries;
168
+ }
169
+ /**
170
+ * Tier 3: Generic fallback using modules or description.
171
+ */
172
+ function tier3Generic(ticket) {
173
+ if (ticket.modules.length > 0) {
174
+ return ticket.modules.slice(0, 5).map(m => ({
175
+ path: m,
176
+ action: 'Identify and modify relevant files in this module',
177
+ reason: ticket.acceptance_criteria[0] ?? ticket.title,
178
+ }));
179
+ }
180
+ return [{
181
+ path: '(read description to identify target files)',
182
+ action: ticket.title,
183
+ reason: ticket.acceptance_criteria[0] ?? ticket.title,
184
+ }];
185
+ }
186
+ /**
187
+ * Build model-specific approach text.
188
+ */
189
+ function buildApproach(model) {
190
+ if (isLocalModel(model)) {
191
+ return `## Approach (local model — keep it simple)
192
+ - Focus on ONE file at a time
193
+ - Make the smallest possible change that satisfies the criteria
194
+ - Prefer editing existing code over adding new files`;
195
+ }
196
+ return `## Approach (plan then execute)
197
+ 1. List the specific changes needed per file
198
+ 2. For each file: read it, make the change, verify
199
+ 3. Run verification commands after all changes`;
200
+ }
201
+ /**
202
+ * Generate a concrete execution plan for a ticket using 4-tier file discovery.
203
+ * No embeddings required.
204
+ */
205
+ export function generatePlan(ticket, model, cwd, log) {
206
+ // Tier 1: enriched files
207
+ const enriched = tier1Enriched(ticket, cwd);
208
+ if (enriched) {
209
+ log.info(`Plan tier: enriched (${enriched.length} files)`);
210
+ return {
211
+ ticket: ticket.key,
212
+ title: ticket.title,
213
+ files: enriched,
214
+ testFiles: collectTestFiles(enriched.map(e => e.path), cwd),
215
+ approach: buildApproach(model),
216
+ generated: 'enriched',
217
+ };
218
+ }
219
+ // Tier 1.5: module-scoped discovery
220
+ const modules = tier15Modules(ticket, cwd);
221
+ if (modules) {
222
+ log.info(`Plan tier: modules (${modules.length} files)`);
223
+ return {
224
+ ticket: ticket.key,
225
+ title: ticket.title,
226
+ files: modules,
227
+ testFiles: collectTestFiles(modules.map(e => e.path), cwd),
228
+ approach: buildApproach(model),
229
+ generated: 'modules',
230
+ };
231
+ }
232
+ // Tier 2: grep discovery
233
+ const grepped = tier2Grep(ticket, cwd);
234
+ if (grepped) {
235
+ log.info(`Plan tier: grep (${grepped.length} files)`);
236
+ return {
237
+ ticket: ticket.key,
238
+ title: ticket.title,
239
+ files: grepped,
240
+ testFiles: collectTestFiles(grepped.map(e => e.path), cwd),
241
+ approach: buildApproach(model),
242
+ generated: 'grep',
243
+ };
244
+ }
245
+ // Tier 3: generic
246
+ log.info('Plan tier: generic');
247
+ return {
248
+ ticket: ticket.key,
249
+ title: ticket.title,
250
+ files: tier3Generic(ticket),
251
+ testFiles: [],
252
+ approach: buildApproach(model),
253
+ generated: 'generic',
254
+ };
255
+ }
256
+ /**
257
+ * Format an ExecutionPlan as a structured Aider --message prompt.
258
+ */
259
+ export function formatPlanAsPrompt(plan, ticket) {
260
+ const fileSection = plan.files.map(f => `### ${f.path}\n**Action:** ${f.action}\n**Reason:** ${f.reason}`).join('\n\n');
261
+ const testSection = plan.testFiles.length > 0
262
+ ? plan.testFiles.map(t => `- ${t}`).join('\n')
263
+ : '- (run full test suite)';
264
+ const acSection = ticket.acceptance_criteria
265
+ .map(ac => ` [ ] ${ac}`)
266
+ .join('\n');
267
+ return `You are working on the SLOPE project (TypeScript monorepo, pnpm, vitest).
268
+
269
+ ## Task
270
+ ${ticket.key}: ${ticket.title}
271
+
272
+ ## Description
273
+ ${ticket.description}
274
+
275
+ ## Execution Plan
276
+ For each file below, make the specified change:
277
+
278
+ ${fileSection}
279
+
280
+ ## Test Files to Verify
281
+ ${testSection}
282
+
283
+ ## Acceptance Criteria (ALL must pass)
284
+ ${acSection}
285
+
286
+ ## Verification
287
+ 1. pnpm typecheck
288
+ 2. pnpm test
289
+
290
+ ## Rules
291
+ - Read each file BEFORE modifying — understand existing patterns
292
+ - Make real, substantive changes — no comment-only or whitespace edits
293
+ - If a file already satisfies criteria, skip it
294
+ - Commit: '${ticket.key}: <what you changed>'
295
+
296
+ ${plan.approach}`;
297
+ }
298
+ //# sourceMappingURL=planner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner.js","sourceRoot":"","sources":["../../../src/cli/loop/planner.ts"],"names":[],"mappings":"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"}
@@ -10,10 +10,10 @@ export interface MergeResult {
10
10
  }
11
11
  /** Check if gh CLI is available and authenticated */
12
12
  export declare function checkGhCli(log: Logger): boolean;
13
- /** Check if branch has commits ahead of main */
14
- export declare function hasCommitsAhead(branch: string, cwd: string): boolean;
13
+ /** Check if branch has commits ahead of base (default: main) */
14
+ export declare function hasCommitsAhead(branch: string, cwd: string, baseBranch?: string): boolean;
15
15
  /** Create a PR for the sprint branch */
16
- export declare function createPr(branch: string, sprintId: string, title: string, strategy: string, ticketResults: TicketResult[], cwd: string, log: Logger): PrInfo | null;
16
+ export declare function createPr(branch: string, sprintId: string, title: string, strategy: string, ticketResults: TicketResult[], cwd: string, log: Logger, baseBranch?: string): PrInfo | null;
17
17
  /**
18
18
  * Run structural review on a PR diff.
19
19
  * Returns number of findings added.
@@ -22,5 +22,7 @@ export declare function runStructuralReview(prNumber: number, sprintId: string,
22
22
  /**
23
23
  * Run auto-merge safeguards and merge if all pass.
24
24
  */
25
- export declare function autoMerge(prNumber: number, findingCount: number, passingCount: number, config: LoopConfig, cwd: string, log: Logger): MergeResult;
25
+ export declare function autoMerge(prNumber: number, findingCount: number, passingCount: number, config: LoopConfig, cwd: string, log: Logger, opts?: {
26
+ isStagingMerge?: boolean;
27
+ }): MergeResult;
26
28
  //# sourceMappingURL=pr-lifecycle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pr-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/cli/loop/pr-lifecycle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAc/C;AAED,gDAAgD;AAChD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAUpE;AAED,wCAAwC;AACxC,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,YAAY,EAAE,EAC7B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,IAAI,CAqCf;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,CAqFR;AAyBD;;GAEG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,WAAW,CA8Db"}
1
+ {"version":3,"file":"pr-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/cli/loop/pr-lifecycle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAc/C;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,SAAS,GAAG,OAAO,CAUzF;AAED,wCAAwC;AACxC,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,YAAY,EAAE,EAC7B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,UAAU,SAAS,GAClB,MAAM,GAAG,IAAI,CAqCf;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,CAqFR;AAyBD;;GAEG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,GAClC,WAAW,CAgEb"}
@@ -17,10 +17,10 @@ export function checkGhCli(log) {
17
17
  }
18
18
  return true;
19
19
  }
20
- /** Check if branch has commits ahead of main */
21
- export function hasCommitsAhead(branch, cwd) {
20
+ /** Check if branch has commits ahead of base (default: main) */
21
+ export function hasCommitsAhead(branch, cwd, baseBranch = 'main') {
22
22
  try {
23
- const count = execFileSync('git', ['rev-list', '--count', `main..${branch}`], {
23
+ const count = execFileSync('git', ['rev-list', '--count', `${baseBranch}..${branch}`], {
24
24
  cwd,
25
25
  encoding: 'utf8',
26
26
  }).trim();
@@ -31,7 +31,7 @@ export function hasCommitsAhead(branch, cwd) {
31
31
  }
32
32
  }
33
33
  /** Create a PR for the sprint branch */
34
- export function createPr(branch, sprintId, title, strategy, ticketResults, cwd, log) {
34
+ export function createPr(branch, sprintId, title, strategy, ticketResults, cwd, log, baseBranch = 'main') {
35
35
  const passingCount = ticketResults.filter(t => t.tests_passing && !t.noop).length;
36
36
  const noopCount = ticketResults.filter(t => t.noop).length;
37
37
  const totalCount = ticketResults.length;
@@ -53,7 +53,7 @@ ${ticketLines}
53
53
  try {
54
54
  const url = execFileSync('gh', [
55
55
  'pr', 'create',
56
- '--base', 'main',
56
+ '--base', baseBranch,
57
57
  '--head', branch,
58
58
  '--title', `feat(${sprintId}): ${title}`,
59
59
  '--body', body,
@@ -167,7 +167,7 @@ function addFinding(type, sprintId, sprintNum, severity, description, cwd, log)
167
167
  /**
168
168
  * Run auto-merge safeguards and merge if all pass.
169
169
  */
170
- export function autoMerge(prNumber, findingCount, passingCount, config, cwd, log) {
170
+ export function autoMerge(prNumber, findingCount, passingCount, config, cwd, log, opts) {
171
171
  // Safeguard 1: No critical/major findings
172
172
  if (findingCount > 0) {
173
173
  try {
@@ -199,19 +199,21 @@ export function autoMerge(prNumber, findingCount, passingCount, config, cwd, log
199
199
  catch {
200
200
  return block(prNumber, 'typecheck failing', cwd, log);
201
201
  }
202
- // Safeguard 4: PR not too large (<20 files changed)
203
- try {
204
- const filesChanged = execFileSync('gh', [
205
- 'pr', 'view', String(prNumber),
206
- '--json', 'changedFiles',
207
- '--jq', '.changedFiles',
208
- ], { cwd, encoding: 'utf8' }).trim();
209
- const count = parseInt(filesChanged, 10) || 0;
210
- if (count > 20) {
211
- return block(prNumber, `${count} files changed (threshold: 20)`, cwd, log);
202
+ // Safeguard 4: PR not too large (<20 files changed) — skip for staging merges
203
+ if (!opts?.isStagingMerge) {
204
+ try {
205
+ const filesChanged = execFileSync('gh', [
206
+ 'pr', 'view', String(prNumber),
207
+ '--json', 'changedFiles',
208
+ '--jq', '.changedFiles',
209
+ ], { cwd, encoding: 'utf8' }).trim();
210
+ const count = parseInt(filesChanged, 10) || 0;
211
+ if (count > 20) {
212
+ return block(prNumber, `${count} files changed (threshold: 20)`, cwd, log);
213
+ }
212
214
  }
215
+ catch { /* ok — proceed if check fails */ }
213
216
  }
214
- catch { /* ok — proceed if check fails */ }
215
217
  // Safeguard 5: At least one passing non-noop ticket
216
218
  if (passingCount <= 0) {
217
219
  return block(prNumber, 'no passing tickets', cwd, log);