@shepai/cli 1.40.1 → 1.41.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 (73) hide show
  1. package/dist/packages/core/src/domain/factories/spec-yaml-parser.d.ts.map +1 -1
  2. package/dist/packages/core/src/domain/factories/spec-yaml-parser.js +2 -0
  3. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts +1 -1
  4. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts.map +1 -1
  5. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.js +6 -8
  6. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
  7. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +1 -3
  8. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge-output-parser.d.ts +22 -0
  9. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge-output-parser.d.ts.map +1 -0
  10. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge-output-parser.js +32 -0
  11. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.d.ts +8 -9
  12. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.d.ts.map +1 -1
  13. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.js +34 -64
  14. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/merge-prompts.d.ts +24 -0
  15. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/merge-prompts.d.ts.map +1 -0
  16. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/merge-prompts.js +128 -0
  17. package/dist/tsconfig.build.tsbuildinfo +1 -1
  18. package/package.json +2 -1
  19. package/web/.next/BUILD_ID +1 -1
  20. package/web/.next/build-manifest.json +2 -2
  21. package/web/.next/cache/.previewinfo +1 -1
  22. package/web/.next/cache/.rscinfo +1 -1
  23. package/web/.next/cache/.tsbuildinfo +1 -1
  24. package/web/.next/cache/config.json +3 -3
  25. package/web/.next/fallback-build-manifest.json +2 -2
  26. package/web/.next/prerender-manifest.json +3 -3
  27. package/web/.next/required-server-files.js +1 -1
  28. package/web/.next/required-server-files.json +1 -1
  29. package/web/.next/server/app/_global-error.html +2 -2
  30. package/web/.next/server/app/_global-error.rsc +1 -1
  31. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  32. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  33. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  34. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  35. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  36. package/web/.next/server/app/page/server-reference-manifest.json +9 -9
  37. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  38. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +1 -1
  39. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js.map +1 -1
  40. package/web/.next/server/chunks/ssr/[root-of-the-server]__19354aea._.js +2 -2
  41. package/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
  42. package/web/.next/server/pages/500.html +2 -2
  43. package/web/.next/server/server-reference-manifest.js +1 -1
  44. package/web/.next/server/server-reference-manifest.json +10 -10
  45. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  46. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  47. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  48. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  49. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  50. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  51. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  52. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  53. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  54. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  55. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  56. package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +9 -9
  57. package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
  58. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +1 -1
  59. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__19354aea._.js +2 -2
  60. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
  61. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  62. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  63. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +10 -10
  64. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  65. package/web/.next/static/chunks/{5e089c6e9ef54c86.js → be3b7ed5ace25bc5.js} +1 -1
  66. package/web/.next/trace +1 -1
  67. package/web/.next/trace-build +1 -1
  68. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/pr-yaml-generator.d.ts +0 -17
  69. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/pr-yaml-generator.d.ts.map +0 -1
  70. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/pr-yaml-generator.js +0 -43
  71. /package/web/.next/static/{01NlX2KFq1kSxa1SFgZTQ → GPVcc9EGdqEYO3zHqy-pA}/_buildManifest.js +0 -0
  72. /package/web/.next/static/{01NlX2KFq1kSxa1SFgZTQ → GPVcc9EGdqEYO3zHqy-pA}/_clientMiddlewareManifest.json +0 -0
  73. /package/web/.next/static/{01NlX2KFq1kSxa1SFgZTQ → GPVcc9EGdqEYO3zHqy-pA}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"spec-yaml-parser.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/domain/factories/spec-yaml-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,EACb,aAAa,EACd,MAAM,qBAAqB,CAAC;AAgF7B;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAI9D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAInE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAIpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAI7D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAIrE"}
1
+ {"version":3,"file":"spec-yaml-parser.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/domain/factories/spec-yaml-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,EACb,aAAa,EACd,MAAM,qBAAqB,CAAC;AAiF7B;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAI9D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAInE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAIpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAI7D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAIrE"}
@@ -6,6 +6,7 @@
6
6
  * to the TypeScript types — no manual field-by-field extraction needed.
7
7
  */
8
8
  import Ajv2020 from 'ajv/dist/2020.js';
9
+ import addFormats from 'ajv-formats';
9
10
  import yaml from 'js-yaml';
10
11
  import { readdirSync, readFileSync } from 'node:fs';
11
12
  import { randomUUID } from 'node:crypto';
@@ -19,6 +20,7 @@ function resolveSchemaDir() {
19
20
  }
20
21
  function createValidator() {
21
22
  const ajv = new Ajv2020({ allErrors: true, strict: false });
23
+ addFormats(ajv);
22
24
  const schemaDir = resolveSchemaDir();
23
25
  const files = readdirSync(schemaDir).filter((f) => f.endsWith('.yaml'));
24
26
  for (const file of files) {
@@ -8,7 +8,7 @@ export { FeatureAgentAnnotation, type FeatureAgentState } from './state.js';
8
8
  */
9
9
  export interface FeatureAgentGraphDeps {
10
10
  executor: IAgentExecutor;
11
- mergeNodeDeps: MergeNodeDeps;
11
+ mergeNodeDeps?: Omit<MergeNodeDeps, 'executor'>;
12
12
  }
13
13
  /**
14
14
  * Factory function that creates and compiles the feature-agent LangGraph.
@@ -1 +1 @@
1
- {"version":3,"file":"feature-agent-graph.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AAOpG,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAS5E,OAAO,EAAE,sBAAsB,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE5E;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,cAAc,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;CAC9B;AAyHD;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,cAAc,EAAE,qBAAqB,GAAG,cAAc,EACtD,YAAY,CAAC,EAAE,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA0EnC"}
1
+ {"version":3,"file":"feature-agent-graph.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AAOpG,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAS5E,OAAO,EAAE,sBAAsB,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE5E;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,cAAc,CAAC;IACzB,aAAa,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;CACjD;AAyHD;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,cAAc,EAAE,qBAAqB,GAAG,cAAc,EACtD,YAAY,CAAC,EAAE,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAwEnC"}
@@ -134,9 +134,7 @@ function createPlanTasksValidator() {
134
134
  */
135
135
  export function createFeatureAgentGraph(depsOrExecutor, checkpointer) {
136
136
  // Support legacy signature: createFeatureAgentGraph(executor, checkpointer)
137
- const deps = 'execute' in depsOrExecutor
138
- ? { executor: depsOrExecutor, mergeNodeDeps: undefined }
139
- : depsOrExecutor;
137
+ const deps = 'execute' in depsOrExecutor ? { executor: depsOrExecutor } : depsOrExecutor;
140
138
  const { executor } = deps;
141
139
  const graph = new StateGraph(FeatureAgentAnnotation)
142
140
  // --- Producer nodes ---
@@ -156,10 +154,6 @@ export function createFeatureAgentGraph(depsOrExecutor, checkpointer) {
156
154
  .addNode('repair_research', createRepairNode('research.yaml', executor))
157
155
  .addNode('repair_plan_tasks', createRepairNode(['plan.yaml', 'tasks.yaml'], executor))
158
156
  // --- Edges: linear flow with validation gates ---
159
- // Interruptible nodes (requirements, plan) use conditional edges that
160
- // check _needsReexecution to loop back on rejection. This ensures each
161
- // re-execution is a fresh node invocation with a clean interrupt index,
162
- // avoiding the stale interrupt replay bug with dual interrupt() calls.
163
157
  .addEdge(START, 'analyze')
164
158
  .addEdge('analyze', 'validate_spec_analyze')
165
159
  .addConditionalEdges('validate_spec_analyze', routeValidation('requirements', 'repair_spec_analyze'))
@@ -175,8 +169,12 @@ export function createFeatureAgentGraph(depsOrExecutor, checkpointer) {
175
169
  .addEdge('repair_plan_tasks', 'validate_plan_tasks');
176
170
  // --- Merge node: wired when deps are provided ---
177
171
  if (deps.mergeNodeDeps) {
172
+ const mergeNodeDeps = {
173
+ executor,
174
+ ...deps.mergeNodeDeps,
175
+ };
178
176
  graph
179
- .addNode('merge', createMergeNode(deps.mergeNodeDeps))
177
+ .addNode('merge', createMergeNode(mergeNodeDeps))
180
178
  .addEdge('implement', 'merge')
181
179
  .addEdge('merge', END);
182
180
  }
@@ -1 +1 @@
1
- {"version":3,"file":"feature-agent-worker.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AAEH,OAAO,kBAAkB,CAAC;AAqB1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAoD1D;AAgCD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAkM/D"}
1
+ {"version":3,"file":"feature-agent-worker.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AAEH,OAAO,kBAAkB,CAAC;AAoB1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAoD1D;AAgCD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAkM/D"}
@@ -21,7 +21,6 @@ import { InitializeSettingsUseCase } from '../../../../application/use-cases/set
21
21
  import { setHeartbeatContext } from './heartbeat.js';
22
22
  import { setPhaseTimingContext, recordLifecycleEvent } from './phase-timing-context.js';
23
23
  import { setLifecycleContext } from './lifecycle-context.js';
24
- import { generatePrYaml } from './nodes/pr-yaml-generator.js';
25
24
  /**
26
25
  * Parse CLI arguments into a WorkerArgs object.
27
26
  * Throws if any required argument is missing.
@@ -128,8 +127,7 @@ export async function runWorker(args) {
128
127
  const graphDeps = {
129
128
  executor,
130
129
  mergeNodeDeps: {
131
- gitPrService,
132
- generatePrYaml,
130
+ getDiffSummary: (cwd, baseBranch) => gitPrService.getPrDiffSummary(cwd, baseBranch),
133
131
  featureRepository,
134
132
  },
135
133
  };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Merge Output Parser
3
+ *
4
+ * Extracts structured data (commit SHA, PR URL/number) from agent text output
5
+ * using regex patterns. Falls back gracefully to null when patterns don't match.
6
+ */
7
+ export interface PrParseResult {
8
+ url: string;
9
+ number: number;
10
+ }
11
+ /**
12
+ * Extract the first commit SHA from agent output text.
13
+ * Looks for git commit output format `[branch SHA]` or `commit SHA`.
14
+ * Returns null if no SHA found.
15
+ */
16
+ export declare function parseCommitHash(output: string): string | null;
17
+ /**
18
+ * Extract the first GitHub PR URL and number from agent output text.
19
+ * Returns null if no PR URL found.
20
+ */
21
+ export declare function parsePrUrl(output: string): PrParseResult | null;
22
+ //# sourceMappingURL=merge-output-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-output-parser.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge-output-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI7D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAI/D"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Merge Output Parser
3
+ *
4
+ * Extracts structured data (commit SHA, PR URL/number) from agent text output
5
+ * using regex patterns. Falls back gracefully to null when patterns don't match.
6
+ */
7
+ // Matches git commit output: [branch SHA] message
8
+ // Also matches "Created commit SHA" or "commit SHA" patterns
9
+ const COMMIT_SHA_RE = /\[[\w/.-]+\s+([0-9a-f]{7,40})\]|(?:commit\s+)([0-9a-f]{7,40})/i;
10
+ // Matches GitHub PR URL: https://github.com/owner/repo/pull/123
11
+ const PR_URL_RE = /(https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/pull\/(\d+))/;
12
+ /**
13
+ * Extract the first commit SHA from agent output text.
14
+ * Looks for git commit output format `[branch SHA]` or `commit SHA`.
15
+ * Returns null if no SHA found.
16
+ */
17
+ export function parseCommitHash(output) {
18
+ const match = output.match(COMMIT_SHA_RE);
19
+ if (!match)
20
+ return null;
21
+ return match[1] ?? match[2] ?? null;
22
+ }
23
+ /**
24
+ * Extract the first GitHub PR URL and number from agent output text.
25
+ * Returns null if no PR URL found.
26
+ */
27
+ export function parsePrUrl(output) {
28
+ const match = output.match(PR_URL_RE);
29
+ if (!match)
30
+ return null;
31
+ return { url: match[1], number: parseInt(match[2], 10) };
32
+ }
@@ -1,20 +1,19 @@
1
1
  /**
2
- * Merge Node — Post-Implementation Merge Flow
2
+ * Merge Node — Agent-Driven Post-Implementation Merge Flow
3
3
  *
4
- * Handles the complete merge workflow after implementation:
5
- * 1. Commit uncommitted changes and push
6
- * 2. Generate pr.yaml (always) and optionally create PR
7
- * 3. Optionally auto-merge (via PR or direct branch merge)
8
- * 4. Update feature lifecycle and clean up
4
+ * Handles the complete merge workflow after implementation using two agent calls:
5
+ * 1. Commit + push + create PR (via agent executor)
6
+ * 2. Merge/squash (via agent executor, after approval gate)
9
7
  *
10
8
  * Uses interrupt() for the merge approval gate when allowMerge=false.
11
9
  */
10
+ import type { IAgentExecutor } from '../../../../../application/ports/output/agents/agent-executor.interface.js';
12
11
  import type { FeatureAgentState } from '../state.js';
13
- import type { IGitPrService } from '../../../../../application/ports/output/services/git-pr-service.interface.js';
14
12
  import type { IFeatureRepository } from '../../../../../application/ports/output/repositories/feature-repository.interface.js';
13
+ import type { DiffSummary } from '../../../../../application/ports/output/services/git-pr-service.interface.js';
15
14
  export interface MergeNodeDeps {
16
- gitPrService: IGitPrService;
17
- generatePrYaml: (specDir: string, branch: string, baseBranch: string) => string;
15
+ executor: IAgentExecutor;
16
+ getDiffSummary: (cwd: string, baseBranch: string) => Promise<DiffSummary>;
18
17
  featureRepository: Pick<IFeatureRepository, 'findById' | 'update'>;
19
18
  }
20
19
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"merge.node.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iEAAiE,CAAC;AACrG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yEAAyE,CAAC;AAWlH,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,aAAa,CAAC;IAC5B,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IAChF,iBAAiB,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAC;CACpE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,IAGnC,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAwJ7E"}
1
+ {"version":3,"file":"merge.node.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AACpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yEAAyE,CAAC;AAClH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iEAAiE,CAAC;AAkBnG,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,CAAC;IACzB,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1E,iBAAiB,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAC;CACpE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,IAGnC,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CA2H7E"}
@@ -1,20 +1,20 @@
1
1
  /**
2
- * Merge Node — Post-Implementation Merge Flow
2
+ * Merge Node — Agent-Driven Post-Implementation Merge Flow
3
3
  *
4
- * Handles the complete merge workflow after implementation:
5
- * 1. Commit uncommitted changes and push
6
- * 2. Generate pr.yaml (always) and optionally create PR
7
- * 3. Optionally auto-merge (via PR or direct branch merge)
8
- * 4. Update feature lifecycle and clean up
4
+ * Handles the complete merge workflow after implementation using two agent calls:
5
+ * 1. Commit + push + create PR (via agent executor)
6
+ * 2. Merge/squash (via agent executor, after approval gate)
9
7
  *
10
8
  * Uses interrupt() for the merge approval gate when allowMerge=false.
11
9
  */
12
10
  import { interrupt, isGraphBubbleUp } from '@langchain/langgraph';
13
11
  import { SdlcLifecycle, PrStatus } from '../../../../../domain/generated/output.js';
14
- import { createNodeLogger, shouldInterrupt } from './node-helpers.js';
12
+ import { createNodeLogger, shouldInterrupt, retryExecute, buildExecutorOptions, } from './node-helpers.js';
15
13
  import { reportNodeStart } from '../heartbeat.js';
16
14
  import { recordPhaseStart, recordPhaseEnd, recordApprovalWaitStart, } from '../phase-timing-context.js';
17
15
  import { updateNodeLifecycle } from '../lifecycle-context.js';
16
+ import { buildCommitPushPrPrompt, buildMergeSquashPrompt } from './prompts/merge-prompts.js';
17
+ import { parseCommitHash, parsePrUrl } from './merge-output-parser.js';
18
18
  /**
19
19
  * Factory that creates the merge node function.
20
20
  *
@@ -32,60 +32,39 @@ export function createMergeNode(deps) {
32
32
  // Record merge phase timing
33
33
  const mergeTimingId = await recordPhaseStart('merge');
34
34
  try {
35
- const { gitPrService } = deps;
35
+ const { executor } = deps;
36
36
  const cwd = state.worktreePath;
37
37
  // Resolve branch name from feature
38
38
  const feature = await deps.featureRepository.findById(state.featureId);
39
39
  const branch = feature?.branch ?? `feat/${state.featureId}`;
40
40
  const baseBranch = 'main';
41
- // --- Step 1: Commit and Push ---
42
- let commitHash = state.commitHash;
43
- const hasChanges = await gitPrService.hasUncommittedChanges(cwd);
44
- if (hasChanges) {
45
- log.info('Uncommitted changes detected, committing...');
46
- commitHash = await gitPrService.commitAll(cwd, `feat: implementation complete for ${state.featureId}`);
47
- messages.push(`[merge] Committed changes: ${commitHash}`);
48
- }
49
- else {
50
- log.info('No uncommitted changes');
51
- messages.push('[merge] No uncommitted changes to commit');
52
- }
53
- // Push is controlled by the push flag (--push, or implied by --pr)
54
- const shouldPush = state.push || state.openPr;
55
- if (shouldPush) {
56
- log.info('Pushing to remote...');
57
- await gitPrService.push(cwd, branch, true);
58
- messages.push(`[merge] Pushed branch ${branch}`);
59
- }
60
- else {
61
- log.info('Push skipped (--push not set)');
62
- messages.push('[merge] Push skipped (use --push to push to remote)');
63
- }
64
- // --- Step 2: Generate pr.yaml (always) ---
65
- log.info('Generating pr.yaml...');
66
- const prYamlPath = deps.generatePrYaml(state.specDir, branch, baseBranch);
67
- messages.push(`[merge] Generated pr.yaml at ${prYamlPath}`);
68
- // --- Step 3: Create PR (if openPr=true and not already created) ---
41
+ const options = buildExecutorOptions(state);
42
+ // --- Agent Call 1: Commit + Push + PR ---
43
+ log.info('Agent call 1: commit + push + PR');
44
+ const commitPushPrPrompt = buildCommitPushPrPrompt(state, branch, baseBranch);
45
+ const commitResult = await retryExecute(executor, commitPushPrPrompt, options, {
46
+ logger: log,
47
+ });
48
+ // Parse structured data from agent output
49
+ const commitHash = parseCommitHash(commitResult.result) ?? state.commitHash;
50
+ messages.push(`[merge] Agent completed commit/push/PR operations`);
69
51
  let prUrl = state.prUrl;
70
52
  let prNumber = state.prNumber;
71
53
  const ciStatus = state.ciStatus;
72
- if (state.openPr && !state.prUrl) {
73
- log.info('Creating pull request...');
74
- const prResult = await gitPrService.createPr(cwd, prYamlPath);
75
- prUrl = prResult.url;
76
- prNumber = prResult.number;
77
- messages.push(`[merge] PR created: ${prUrl}`);
78
- // CI watching disabled — let the user check CI status externally
79
- log.info('Skipping CI watch (disabled)');
80
- messages.push('[merge] CI watch skipped');
54
+ if (state.openPr) {
55
+ const prResult = parsePrUrl(commitResult.result);
56
+ if (prResult) {
57
+ prUrl = prResult.url;
58
+ prNumber = prResult.number;
59
+ messages.push(`[merge] PR created: ${prUrl}`);
60
+ }
81
61
  }
82
- // --- Step 4: Merge approval gate ---
62
+ // --- Merge approval gate ---
83
63
  if (shouldInterrupt('merge', state.approvalGates)) {
84
64
  log.info('Interrupting for merge approval');
85
- // Close timing before interrupt so the pre-approval work has a duration
86
65
  await recordPhaseEnd(mergeTimingId, Date.now() - startTime);
87
66
  await recordApprovalWaitStart(mergeTimingId);
88
- const diffSummary = await gitPrService.getPrDiffSummary(cwd, baseBranch);
67
+ const diffSummary = await deps.getDiffSummary(cwd, baseBranch);
89
68
  interrupt({
90
69
  node: 'merge',
91
70
  message: 'Merge approval required. Review the changes and approve to continue.',
@@ -94,23 +73,16 @@ export function createMergeNode(deps) {
94
73
  prNumber,
95
74
  });
96
75
  }
97
- // --- Step 5: Auto-merge (if enabled) ---
76
+ // --- Agent Call 2: Merge (if enabled) ---
98
77
  let merged = false;
99
78
  if (state.approvalGates?.allowMerge) {
100
- if (state.openPr && prNumber) {
101
- log.info(`Auto-merging PR #${prNumber}...`);
102
- await gitPrService.mergePr(cwd, prNumber, 'squash');
103
- messages.push(`[merge] PR #${prNumber} merged via squash`);
104
- merged = true;
105
- }
106
- else {
107
- log.info(`Auto-merging branch ${branch} into ${baseBranch}...`);
108
- await gitPrService.mergeBranch(cwd, branch, baseBranch);
109
- messages.push(`[merge] Branch ${branch} merged into ${baseBranch}`);
110
- merged = true;
111
- }
79
+ log.info('Agent call 2: merge/squash');
80
+ const mergePrompt = buildMergeSquashPrompt({ ...state, prUrl, prNumber, commitHash }, branch, baseBranch);
81
+ await retryExecute(executor, mergePrompt, options, { logger: log });
82
+ messages.push(`[merge] Agent completed merge operation`);
83
+ merged = true;
112
84
  }
113
- // --- Step 6: Update feature lifecycle ---
85
+ // --- Update feature lifecycle ---
114
86
  const newLifecycle = merged ? SdlcLifecycle.Maintain : SdlcLifecycle.Review;
115
87
  if (feature) {
116
88
  await deps.featureRepository.update({
@@ -154,8 +126,6 @@ export function createMergeNode(deps) {
154
126
  // Record phase end even on failure so timing shows duration, not "running"
155
127
  await recordPhaseEnd(mergeTimingId, Date.now() - startTime);
156
128
  // Re-throw so LangGraph does NOT checkpoint this node as completed.
157
- // This allows `feat resume` to retry the merge node instead of
158
- // restarting the entire graph from scratch.
159
129
  throw err;
160
130
  }
161
131
  };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Merge Node Prompt Builders
3
+ *
4
+ * Builds prompts for the two agent calls in the merge node:
5
+ * 1. buildCommitPushPrPrompt — commit (always), push (conditional), PR create (conditional)
6
+ * 2. buildMergeSquashPrompt — merge/squash PR or branch
7
+ */
8
+ import type { FeatureAgentState } from '../../state.js';
9
+ /**
10
+ * Build a prompt for the commit + push + PR agent call.
11
+ *
12
+ * The agent always commits. Push and PR creation are conditional based
13
+ * on state.push and state.openPr flags.
14
+ */
15
+ export declare function buildCommitPushPrPrompt(state: FeatureAgentState, branch: string, baseBranch: string): string;
16
+ /**
17
+ * Build a prompt for the merge/squash agent call.
18
+ *
19
+ * When a PR exists, uses `gh pr merge` (remote operation — no local merge needed).
20
+ * When no PR exists, performs a local merge in the ORIGINAL repo directory
21
+ * (not the worktree, which IS the feature branch).
22
+ */
23
+ export declare function buildMergeSquashPrompt(state: FeatureAgentState, branch: string, baseBranch: string): string;
24
+ //# sourceMappingURL=merge-prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-prompts.d.ts","sourceRoot":"","sources":["../../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/merge-prompts.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,MAAM,CAwDR;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,MAAM,CAwDR"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Merge Node Prompt Builders
3
+ *
4
+ * Builds prompts for the two agent calls in the merge node:
5
+ * 1. buildCommitPushPrPrompt — commit (always), push (conditional), PR create (conditional)
6
+ * 2. buildMergeSquashPrompt — merge/squash PR or branch
7
+ */
8
+ import { readSpecFile } from '../node-helpers.js';
9
+ /**
10
+ * Build a prompt for the commit + push + PR agent call.
11
+ *
12
+ * The agent always commits. Push and PR creation are conditional based
13
+ * on state.push and state.openPr flags.
14
+ */
15
+ export function buildCommitPushPrPrompt(state, branch, baseBranch) {
16
+ const specContent = readSpecFile(state.specDir, 'spec.yaml');
17
+ const cwd = state.worktreePath || state.repositoryPath;
18
+ const shouldPush = state.push || state.openPr;
19
+ const steps = [];
20
+ // Step 1: Commit (always)
21
+ steps.push(`1. Review the current changes using \`git diff\` and \`git status\`
22
+ 2. Stage all changes with \`git add -A\`
23
+ 3. Write a conventional commit message based on the actual diff content
24
+ - Use the format: \`feat(<scope>): <description>\` or \`fix(<scope>): <description>\`
25
+ - The commit message should summarize what actually changed, not be generic
26
+ - Run \`git commit -m "<your message>"\``);
27
+ // Step 2: Push (conditional)
28
+ if (shouldPush) {
29
+ steps.push(`4. Push the branch to remote: \`git push -u origin ${branch}\``);
30
+ }
31
+ // Step 3: PR creation (conditional)
32
+ if (state.openPr) {
33
+ steps.push(`${shouldPush ? '5' : '4'}. Create a pull request:
34
+ - Run \`gh pr create --base ${baseBranch} --head ${branch} --title "<title>" --body "<body>"\`
35
+ - Write a descriptive PR title using conventional commit format
36
+ - Write a rich PR body that summarizes the changes using the spec context below
37
+ - After creating the PR, update \`${state.specDir}/feature.yaml\` to set the prUrl field with the PR URL`);
38
+ }
39
+ return `You are performing git operations in a feature worktree.
40
+
41
+ ## Feature Specification Context
42
+
43
+ \`\`\`yaml
44
+ ${specContent}
45
+ \`\`\`
46
+
47
+ ## Branch Information
48
+
49
+ - Feature branch: \`${branch}\`
50
+ - Base branch: \`${baseBranch}\`
51
+
52
+ ## Working Directory
53
+
54
+ ${cwd}
55
+
56
+ ## Instructions
57
+
58
+ ${steps.join('\n')}
59
+
60
+ ## Constraints
61
+
62
+ - Write a meaningful conventional commit message derived from the actual diff — do NOT use generic messages
63
+ - Do NOT modify any source code files — only perform git operations
64
+ - Do NOT amend existing commits
65
+ - If there are no changes to commit, skip the commit step and report that no changes were found`;
66
+ }
67
+ /**
68
+ * Build a prompt for the merge/squash agent call.
69
+ *
70
+ * When a PR exists, uses `gh pr merge` (remote operation — no local merge needed).
71
+ * When no PR exists, performs a local merge in the ORIGINAL repo directory
72
+ * (not the worktree, which IS the feature branch).
73
+ */
74
+ export function buildMergeSquashPrompt(state, branch, baseBranch) {
75
+ if (state.prUrl && state.prNumber) {
76
+ // PR path: remote merge via GitHub CLI — no local merge needed
77
+ return `You are merging a pull request via the GitHub CLI.
78
+
79
+ ## PR to Merge
80
+
81
+ - PR URL: ${state.prUrl}
82
+ - PR Number: #${state.prNumber}
83
+
84
+ ## Instructions
85
+
86
+ 1. Merge the PR using squash merge: \`gh pr merge ${state.prNumber} --squash --delete-branch\`
87
+ 2. If merge conflicts are encountered, resolve them and retry
88
+ 3. If the merge fails, report the error clearly
89
+
90
+ ## Constraints
91
+
92
+ - Use squash merge strategy to keep history clean
93
+ - Report the merge result clearly`;
94
+ }
95
+ // Non-PR path: local merge in the ORIGINAL repo (not the worktree)
96
+ const originalRepo = state.repositoryPath;
97
+ return `You are performing a local merge in the original repository directory.
98
+
99
+ IMPORTANT: You MUST run all git commands in the original repository directory, NOT in any worktree.
100
+
101
+ ## Branch to Merge
102
+
103
+ - Feature branch: \`${branch}\`
104
+ - Base branch: \`${baseBranch}\`
105
+
106
+ ## Working Directory
107
+
108
+ ${originalRepo}
109
+
110
+ ## Instructions
111
+
112
+ 1. Change to the original repository: \`cd ${originalRepo}\`
113
+ 2. Fetch latest: \`git fetch origin\`
114
+ 3. Checkout the base branch: \`git checkout ${baseBranch}\`
115
+ 4. Pull latest base: \`git pull origin ${baseBranch}\`
116
+ 5. Merge the feature branch: \`git merge --squash ${branch}\`
117
+ 6. If merge conflicts are encountered, resolve them manually and complete the merge
118
+ 7. Commit the squash merge with a descriptive conventional commit message
119
+ 8. Delete the feature branch after successful merge: \`git branch -d ${branch}\`
120
+
121
+ ## Constraints
122
+
123
+ - Use squash merge strategy to keep history clean
124
+ - All commands MUST run in \`${originalRepo}\` (the original repo), NOT in the worktree
125
+ - If conflicts arise during merge, attempt to resolve them intelligently
126
+ - Do NOT modify any source code beyond what is needed for conflict resolution
127
+ - Report the merge result clearly`;
128
+ }