@shepai/cli 1.35.1 → 1.35.3

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 (57) hide show
  1. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts +1 -1
  2. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
  3. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +3 -0
  4. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/lifecycle-context.d.ts +26 -0
  5. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/lifecycle-context.d.ts.map +1 -0
  6. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/lifecycle-context.js +61 -0
  7. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/implement.node.d.ts.map +1 -1
  8. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/implement.node.js +8 -0
  9. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.d.ts.map +1 -1
  10. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.js +12 -8
  11. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts.map +1 -1
  12. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.js +5 -1
  13. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.d.ts.map +1 -1
  14. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.js +21 -1
  15. package/dist/tsconfig.build.tsbuildinfo +1 -1
  16. package/package.json +1 -1
  17. package/web/.next/BUILD_ID +1 -1
  18. package/web/.next/build-manifest.json +2 -2
  19. package/web/.next/cache/.previewinfo +1 -1
  20. package/web/.next/cache/.rscinfo +1 -1
  21. package/web/.next/cache/config.json +3 -3
  22. package/web/.next/fallback-build-manifest.json +2 -2
  23. package/web/.next/prerender-manifest.json +3 -3
  24. package/web/.next/required-server-files.js +1 -1
  25. package/web/.next/required-server-files.json +1 -1
  26. package/web/.next/server/app/_global-error.html +2 -2
  27. package/web/.next/server/app/_global-error.rsc +1 -1
  28. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  29. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  30. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  31. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  32. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  33. package/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
  34. package/web/.next/server/pages/500.html +2 -2
  35. package/web/.next/server/server-reference-manifest.js +1 -1
  36. package/web/.next/server/server-reference-manifest.json +1 -1
  37. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  38. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  39. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  40. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  41. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  42. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  43. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  44. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  45. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  46. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  47. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  48. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
  49. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  50. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  51. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +1 -1
  52. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  53. package/web/.next/trace +1 -1
  54. package/web/.next/trace-build +1 -1
  55. /package/web/.next/static/{_6QZh55r5rZlPal4MMGV8 → aMt5PeCCyLYG-zTyUFrpm}/_buildManifest.js +0 -0
  56. /package/web/.next/static/{_6QZh55r5rZlPal4MMGV8 → aMt5PeCCyLYG-zTyUFrpm}/_clientMiddlewareManifest.json +0 -0
  57. /package/web/.next/static/{_6QZh55r5rZlPal4MMGV8 → aMt5PeCCyLYG-zTyUFrpm}/_ssgManifest.js +0 -0
@@ -59,7 +59,7 @@ export declare function createFeatureAgentGraph(depsOrExecutor: FeatureAgentGrap
59
59
  ciStatus?: string | null | undefined;
60
60
  push?: boolean | undefined;
61
61
  openPr?: boolean | undefined;
62
- }, "__start__" | "analyze" | "requirements" | "plan" | "implement" | "research" | "validate_spec_analyze" | "validate_spec_requirements" | "validate_research" | "validate_plan_tasks" | "repair_spec_analyze" | "repair_spec_requirements" | "repair_research" | "repair_plan_tasks", {
62
+ }, "__start__" | "analyze" | "requirements" | "research" | "plan" | "implement" | "validate_spec_analyze" | "validate_spec_requirements" | "validate_research" | "validate_plan_tasks" | "repair_spec_analyze" | "repair_spec_requirements" | "repair_research" | "repair_plan_tasks", {
63
63
  featureId: {
64
64
  (): import("@langchain/langgraph").LastValue<string>;
65
65
  (annotation: import("@langchain/langgraph").SingleReducer<string, string>): import("@langchain/langgraph").BinaryOperatorAggregate<string, string>;
@@ -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;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;CAClB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CA6C1D;AAgCD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAuJ/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;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;CAClB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CA6C1D;AAgCD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA0J/D"}
@@ -20,6 +20,7 @@ import { initializeSettings } from '../../../services/settings.service.js';
20
20
  import { InitializeSettingsUseCase } from '../../../../application/use-cases/settings/initialize-settings.use-case.js';
21
21
  import { setHeartbeatContext } from './heartbeat.js';
22
22
  import { setPhaseTimingContext } from './phase-timing-context.js';
23
+ import { setLifecycleContext } from './lifecycle-context.js';
23
24
  import { generatePrYaml } from './nodes/pr-yaml-generator.js';
24
25
  /**
25
26
  * Parse CLI arguments into a WorkerArgs object.
@@ -150,6 +151,8 @@ export async function runWorker(args) {
150
151
  // Set phase timing context so executeNode() records per-phase durations
151
152
  const timingRepository = container.resolve('IPhaseTimingRepository');
152
153
  setPhaseTimingContext(args.runId, timingRepository);
154
+ // Set lifecycle context so nodes update the feature lifecycle as they execute
155
+ setLifecycleContext(args.featureId, featureRepository);
153
156
  try {
154
157
  const graphConfig = { configurable: { thread_id: checkpointId } };
155
158
  let result;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Lifecycle Context
3
+ *
4
+ * Module-level singleton that allows graph nodes to update the feature's
5
+ * SDLC lifecycle as they execute, following the same pattern as
6
+ * phase-timing-context.ts.
7
+ *
8
+ * The worker calls setLifecycleContext() once after DI init.
9
+ * Nodes call updateNodeLifecycle() at the start of execution.
10
+ * Errors are swallowed so lifecycle updates never block graph execution.
11
+ */
12
+ import type { IFeatureRepository } from '../../../../application/ports/output/repositories/feature-repository.interface.js';
13
+ /**
14
+ * Set the lifecycle context. Called once by the worker after DI init.
15
+ */
16
+ export declare function setLifecycleContext(featureId: string, repository: IFeatureRepository): void;
17
+ /**
18
+ * Clear the lifecycle context. Useful for testing.
19
+ */
20
+ export declare function clearLifecycleContext(): void;
21
+ /**
22
+ * Update the feature's lifecycle to match the current graph node.
23
+ * No-op if context is not set or the node name has no lifecycle mapping.
24
+ */
25
+ export declare function updateNodeLifecycle(nodeName: string): Promise<void>;
26
+ //# sourceMappingURL=lifecycle-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-context.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/lifecycle-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yEAAyE,CAAC;AAgBlH;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAG3F;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBzE"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Lifecycle Context
3
+ *
4
+ * Module-level singleton that allows graph nodes to update the feature's
5
+ * SDLC lifecycle as they execute, following the same pattern as
6
+ * phase-timing-context.ts.
7
+ *
8
+ * The worker calls setLifecycleContext() once after DI init.
9
+ * Nodes call updateNodeLifecycle() at the start of execution.
10
+ * Errors are swallowed so lifecycle updates never block graph execution.
11
+ */
12
+ import { SdlcLifecycle } from '../../../../domain/generated/output.js';
13
+ let contextFeatureId;
14
+ let contextRepository;
15
+ /** Map graph node names to their corresponding SDLC lifecycle stage. */
16
+ const NODE_TO_LIFECYCLE = {
17
+ analyze: SdlcLifecycle.Analyze,
18
+ requirements: SdlcLifecycle.Requirements,
19
+ research: SdlcLifecycle.Research,
20
+ plan: SdlcLifecycle.Planning,
21
+ implement: SdlcLifecycle.Implementation,
22
+ merge: SdlcLifecycle.Review,
23
+ };
24
+ /**
25
+ * Set the lifecycle context. Called once by the worker after DI init.
26
+ */
27
+ export function setLifecycleContext(featureId, repository) {
28
+ contextFeatureId = featureId;
29
+ contextRepository = repository;
30
+ }
31
+ /**
32
+ * Clear the lifecycle context. Useful for testing.
33
+ */
34
+ export function clearLifecycleContext() {
35
+ contextFeatureId = undefined;
36
+ contextRepository = undefined;
37
+ }
38
+ /**
39
+ * Update the feature's lifecycle to match the current graph node.
40
+ * No-op if context is not set or the node name has no lifecycle mapping.
41
+ */
42
+ export async function updateNodeLifecycle(nodeName) {
43
+ if (!contextFeatureId || !contextRepository)
44
+ return;
45
+ const lifecycle = NODE_TO_LIFECYCLE[nodeName];
46
+ if (!lifecycle)
47
+ return;
48
+ try {
49
+ const feature = await contextRepository.findById(contextFeatureId);
50
+ if (!feature)
51
+ return;
52
+ await contextRepository.update({
53
+ ...feature,
54
+ lifecycle,
55
+ updatedAt: new Date(),
56
+ });
57
+ }
58
+ catch {
59
+ // Swallow — lifecycle update failure is non-fatal
60
+ }
61
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"implement.node.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/implement.node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AACpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAgErD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,IAG5C,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAqK7E"}
1
+ {"version":3,"file":"implement.node.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/implement.node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AACpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAiErD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,IAG5C,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CA+K7E"}
@@ -13,6 +13,7 @@ import { interrupt, isGraphBubbleUp } from '@langchain/langgraph';
13
13
  import { createNodeLogger, readSpecFile, buildExecutorOptions, shouldInterrupt, safeYamlLoad, retryExecute, getCompletedPhases, markPhaseComplete, } from './node-helpers.js';
14
14
  import { reportNodeStart } from '../heartbeat.js';
15
15
  import { recordPhaseStart, recordPhaseEnd } from '../phase-timing-context.js';
16
+ import { updateNodeLifecycle } from '../lifecycle-context.js';
16
17
  import { buildImplementPhasePrompt, } from './prompts/implement.prompt.js';
17
18
  /**
18
19
  * Update feature.yaml with current implementation progress.
@@ -50,8 +51,11 @@ export function createImplementNode(executor) {
50
51
  return async (state) => {
51
52
  log.info('Starting implementation phase orchestration');
52
53
  reportNodeStart('implement');
54
+ await updateNodeLifecycle('implement');
53
55
  const startTime = Date.now();
54
56
  const messages = [];
57
+ // Record top-level implement phase timing (sub-phases are recorded individually below)
58
+ const implementTimingId = await recordPhaseStart('implement');
55
59
  try {
56
60
  // --- Parse plan & tasks ---
57
61
  const planContent = readSpecFile(state.specDir, 'plan.yaml');
@@ -137,6 +141,8 @@ export function createImplementNode(executor) {
137
141
  log.info(`All phases complete — ${completedTasks}/${totalTasks} tasks (${elapsed}s)`);
138
142
  updateFeatureProgress(state.specDir, totalTasks, totalTasks, 'implementation-complete', null, log);
139
143
  messages.push(`[implement] Complete: ${totalTasks} tasks across ${totalPhases} phases (${elapsed}s)`);
144
+ // Record top-level implement phase completion
145
+ await recordPhaseEnd(implementTimingId, Date.now() - startTime);
140
146
  if (shouldInterrupt('implement', state.approvalGates)) {
141
147
  log.info('Interrupting for human approval');
142
148
  interrupt({
@@ -152,6 +158,8 @@ export function createImplementNode(executor) {
152
158
  const message = err instanceof Error ? err.message : String(err);
153
159
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
154
160
  log.error(`${message} (after ${elapsed}s)`);
161
+ // Record phase end even on failure so timing shows duration, not "running"
162
+ await recordPhaseEnd(implementTimingId, Date.now() - startTime);
155
163
  // Throw so LangGraph does NOT checkpoint this node as "completed".
156
164
  // The worker catch block marks the run as failed, and on resume
157
165
  // LangGraph re-executes from the last successfully checkpointed node.
@@ -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;AAKlH,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,CAiJ7E"}
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;AAOlH,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,CAqJ7E"}
@@ -13,6 +13,8 @@ import { interrupt, isGraphBubbleUp } from '@langchain/langgraph';
13
13
  import { SdlcLifecycle, PrStatus } from '../../../../../domain/generated/output.js';
14
14
  import { createNodeLogger, shouldInterrupt } from './node-helpers.js';
15
15
  import { reportNodeStart } from '../heartbeat.js';
16
+ import { recordPhaseStart, recordPhaseEnd } from '../phase-timing-context.js';
17
+ import { updateNodeLifecycle } from '../lifecycle-context.js';
16
18
  /**
17
19
  * Factory that creates the merge node function.
18
20
  *
@@ -24,8 +26,11 @@ export function createMergeNode(deps) {
24
26
  return async (state) => {
25
27
  log.info('Starting merge flow');
26
28
  reportNodeStart('merge');
29
+ await updateNodeLifecycle('merge');
27
30
  const messages = [];
28
31
  const startTime = Date.now();
32
+ // Record merge phase timing
33
+ const mergeTimingId = await recordPhaseStart('merge');
29
34
  try {
30
35
  const { gitPrService } = deps;
31
36
  const cwd = state.worktreePath;
@@ -63,20 +68,16 @@ export function createMergeNode(deps) {
63
68
  // --- Step 3: Create PR (if openPr=true and not already created) ---
64
69
  let prUrl = state.prUrl;
65
70
  let prNumber = state.prNumber;
66
- let ciStatus = state.ciStatus;
71
+ const ciStatus = state.ciStatus;
67
72
  if (state.openPr && !state.prUrl) {
68
73
  log.info('Creating pull request...');
69
74
  const prResult = await gitPrService.createPr(cwd, prYamlPath);
70
75
  prUrl = prResult.url;
71
76
  prNumber = prResult.number;
72
77
  messages.push(`[merge] PR created: ${prUrl}`);
73
- // Watch CI if we're going to auto-merge
74
- if (state.approvalGates?.allowMerge) {
75
- log.info('Watching CI...');
76
- const ciResult = await gitPrService.watchCi(cwd, branch);
77
- ciStatus = ciResult.status;
78
- messages.push(`[merge] CI status: ${ciStatus}`);
79
- }
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');
80
81
  }
81
82
  // --- Step 4: Merge approval gate ---
82
83
  if (shouldInterrupt('merge', state.approvalGates)) {
@@ -128,6 +129,7 @@ export function createMergeNode(deps) {
128
129
  messages.push(`[merge] Feature lifecycle → ${newLifecycle}`);
129
130
  }
130
131
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
132
+ await recordPhaseEnd(mergeTimingId, Date.now() - startTime);
131
133
  messages.push(`[merge] Complete (${elapsed}s)`);
132
134
  log.info(`Merge flow complete (${elapsed}s)`);
133
135
  return {
@@ -146,6 +148,8 @@ export function createMergeNode(deps) {
146
148
  const message = err instanceof Error ? err.message : String(err);
147
149
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
148
150
  log.error(`Merge failed: ${message} (${elapsed}s)`);
151
+ // Record phase end even on failure so timing shows duration, not "running"
152
+ await recordPhaseEnd(mergeTimingId, Date.now() - startTime);
149
153
  // Re-throw so LangGraph does NOT checkpoint this node as completed.
150
154
  // This allows `feat resume` to retry the merge node instead of
151
155
  // restarting the entire graph from scratch.
@@ -1 +1 @@
1
- {"version":3,"file":"node-helpers.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,+DAA+D,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIrD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM;kBAE/B,MAAM,GAAG,IAAI;mBAIZ,MAAM,GAAG,IAAI;EAK/B;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE7D;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMtE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,iBAAiB,GAAG,qBAAqB,CAMpF;AAeD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQrD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,OAAO,CAS3F;AAMD,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG,mBAAmB,GAAG,eAAe,GAAG,SAAS,CAAC;AAMhG;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,CAKjE;AAED,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,qBAAqB,EAC9B,SAAS,CAAC,EAAE,YAAY,GACvB,OAAO,CAAC,oBAAoB,CAAC,CA6B/B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAW5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,IAAI,CAwB1F;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,UAAU,KAAK,MAAM,GACjE,CAAC,KAAK,EAAE,iBAAiB,KAAK,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CA4EnE"}
1
+ {"version":3,"file":"node-helpers.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,+DAA+D,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAKrD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM;kBAE/B,MAAM,GAAG,IAAI;mBAIZ,MAAM,GAAG,IAAI;EAK/B;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE7D;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMtE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,iBAAiB,GAAG,qBAAqB,CAMpF;AAeD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQrD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,OAAO,CAS3F;AAMD,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG,mBAAmB,GAAG,eAAe,GAAG,SAAS,CAAC;AAMhG;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,CAKjE;AAED,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,qBAAqB,EAC9B,SAAS,CAAC,EAAE,YAAY,GACvB,OAAO,CAAC,oBAAoB,CAAC,CA6B/B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAW5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,IAAI,CAwB1F;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,UAAU,KAAK,MAAM,GACjE,CAAC,KAAK,EAAE,iBAAiB,KAAK,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAgFnE"}
@@ -10,6 +10,7 @@ import { join } from 'node:path';
10
10
  import { interrupt, isGraphBubbleUp } from '@langchain/langgraph';
11
11
  import { reportNodeStart } from '../heartbeat.js';
12
12
  import { recordPhaseStart, recordPhaseEnd } from '../phase-timing-context.js';
13
+ import { updateNodeLifecycle } from '../lifecycle-context.js';
13
14
  /**
14
15
  * Create a scoped logger that prefixes messages with the node name.
15
16
  * Output goes to stdout which the worker redirects to the log file.
@@ -206,6 +207,8 @@ export function executeNode(nodeName, executor, buildPrompt) {
206
207
  return async (state) => {
207
208
  log.info('Starting...');
208
209
  reportNodeStart(nodeName);
210
+ // Update feature lifecycle to reflect the current phase
211
+ await updateNodeLifecycle(nodeName);
209
212
  // On resume from interrupt, LangGraph re-executes the node function from
210
213
  // the top. Skip the expensive executor call if this phase already completed
211
214
  // (markPhaseComplete is called before interrupt, so completed = done but
@@ -258,7 +261,8 @@ export function executeNode(nodeName, executor, buildPrompt) {
258
261
  const durationMs = Date.now() - startTime;
259
262
  const elapsed = (durationMs / 1000).toFixed(1);
260
263
  log.error(`${message} (after ${elapsed}s)`);
261
- // Leave timingId without completedAt (partial timing for failed nodes)
264
+ // Record phase end even on failure so timing shows duration, not "running"
265
+ await recordPhaseEnd(timingId, durationMs);
262
266
  // Throw so LangGraph does NOT checkpoint this node as "completed".
263
267
  // The worker catch block marks the run as failed, and on resume
264
268
  // LangGraph re-executes from the last successfully checkpointed node.
@@ -1 +1 @@
1
- {"version":3,"file":"git-pr.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/git-pr.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wEAAwE,CAAC;AAC5G,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,aAAa,EACb,cAAc,EACf,MAAM,wEAAwE,CAAC;AAKhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,qBACa,YAAa,YAAW,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAErE,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKpD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAWxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAelE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,aAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUzF,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnF,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAkBjE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAuBjF,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhF,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAe7E,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,aAAa;CAiBtB"}
1
+ {"version":3,"file":"git-pr.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/git-pr.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wEAAwE,CAAC;AAC5G,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,aAAa,EACb,cAAc,EACf,MAAM,wEAAwE,CAAC;AAOhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,qBACa,YAAa,YAAW,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAErE,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKpD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAWxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAuClE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,aAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUzF,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnF,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAkBjE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAuBjF,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhF,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAe7E,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,aAAa;CAiBtB"}
@@ -18,6 +18,8 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
18
18
  };
19
19
  import { injectable, inject } from 'tsyringe';
20
20
  import { GitPrError, GitPrErrorCode, } from '../../../application/ports/output/services/git-pr-service.interface.js';
21
+ import { readFileSync } from 'node:fs';
22
+ import yaml from 'js-yaml';
21
23
  let GitPrService = class GitPrService {
22
24
  execFile;
23
25
  constructor(execFile) {
@@ -52,7 +54,25 @@ let GitPrService = class GitPrService {
52
54
  }
53
55
  async createPr(cwd, prYamlPath) {
54
56
  try {
55
- const { stdout } = await this.execFile('gh', ['pr', 'create', '--title', prYamlPath, '--body-file', prYamlPath], { cwd });
57
+ // Parse pr.yaml to extract PR metadata
58
+ const prYamlContent = readFileSync(prYamlPath, 'utf-8');
59
+ const prData = yaml.load(prYamlContent);
60
+ const title = prData.title ?? 'Untitled PR';
61
+ const body = prData.body ?? '';
62
+ const args = ['pr', 'create', '--title', title, '--body', body];
63
+ if (prData.baseBranch) {
64
+ args.push('--base', prData.baseBranch);
65
+ }
66
+ if (prData.headBranch) {
67
+ args.push('--head', prData.headBranch);
68
+ }
69
+ if (prData.labels?.length) {
70
+ args.push('--label', prData.labels.join(','));
71
+ }
72
+ if (prData.draft) {
73
+ args.push('--draft');
74
+ }
75
+ const { stdout } = await this.execFile('gh', args, { cwd });
56
76
  const url = stdout.trim();
57
77
  const number = this.parsePrNumberFromUrl(url);
58
78
  return { url, number };