@shepai/cli 1.19.0 → 1.20.1

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 (150) hide show
  1. package/dist/eslint.config.mjs +4 -0
  2. package/dist/src/application/ports/output/agents/feature-agent-process.interface.d.ts +2 -1
  3. package/dist/src/application/ports/output/agents/feature-agent-process.interface.d.ts.map +1 -1
  4. package/dist/src/application/ports/output/agents/index.d.ts +1 -0
  5. package/dist/src/application/ports/output/agents/index.d.ts.map +1 -1
  6. package/dist/src/application/ports/output/agents/phase-timing-repository.interface.d.ts +44 -0
  7. package/dist/src/application/ports/output/agents/phase-timing-repository.interface.d.ts.map +1 -0
  8. package/dist/src/application/ports/output/agents/phase-timing-repository.interface.js +11 -0
  9. package/dist/src/application/use-cases/agents/approve-agent-run.use-case.d.ts.map +1 -1
  10. package/dist/src/application/use-cases/agents/approve-agent-run.use-case.js +6 -2
  11. package/dist/src/application/use-cases/agents/reject-agent-run.use-case.d.ts.map +1 -1
  12. package/dist/src/application/use-cases/agents/reject-agent-run.use-case.js +0 -1
  13. package/dist/src/application/use-cases/features/create/create-feature.use-case.d.ts +37 -0
  14. package/dist/src/application/use-cases/features/create/create-feature.use-case.d.ts.map +1 -0
  15. package/dist/src/application/use-cases/features/{create-feature.use-case.js → create/create-feature.use-case.js} +22 -70
  16. package/dist/src/application/use-cases/features/create/metadata-generator.d.ts +28 -0
  17. package/dist/src/application/use-cases/features/create/metadata-generator.d.ts.map +1 -0
  18. package/dist/src/application/use-cases/features/create/metadata-generator.js +109 -0
  19. package/dist/src/application/use-cases/features/create/slug-resolver.d.ts +24 -0
  20. package/dist/src/application/use-cases/features/create/slug-resolver.d.ts.map +1 -0
  21. package/dist/src/application/use-cases/features/create/slug-resolver.js +66 -0
  22. package/dist/src/application/use-cases/features/create/types.d.ts +16 -0
  23. package/dist/src/application/use-cases/features/create/types.d.ts.map +1 -0
  24. package/dist/src/application/use-cases/features/create/types.js +1 -0
  25. package/dist/src/application/use-cases/features/resume-feature.use-case.js +2 -2
  26. package/dist/src/domain/generated/output.d.ts +40 -6
  27. package/dist/src/domain/generated/output.d.ts.map +1 -1
  28. package/dist/src/infrastructure/di/container.d.ts.map +1 -1
  29. package/dist/src/infrastructure/di/container.js +12 -1
  30. package/dist/src/infrastructure/persistence/sqlite/mappers/agent-run.mapper.d.ts +1 -2
  31. package/dist/src/infrastructure/persistence/sqlite/mappers/agent-run.mapper.d.ts.map +1 -1
  32. package/dist/src/infrastructure/persistence/sqlite/mappers/agent-run.mapper.js +4 -4
  33. package/dist/src/infrastructure/persistence/sqlite/migrations.d.ts.map +1 -1
  34. package/dist/src/infrastructure/persistence/sqlite/migrations.js +31 -0
  35. package/dist/src/infrastructure/repositories/agent-run.repository.d.ts.map +1 -1
  36. package/dist/src/infrastructure/repositories/agent-run.repository.js +5 -9
  37. package/dist/src/infrastructure/repositories/sqlite-phase-timing.repository.d.ts +21 -0
  38. package/dist/src/infrastructure/repositories/sqlite-phase-timing.repository.d.ts.map +1 -0
  39. package/dist/src/infrastructure/repositories/sqlite-phase-timing.repository.js +100 -0
  40. package/dist/src/infrastructure/services/agents/common/executors/mock-executor.service.js +2 -2
  41. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts +305 -13
  42. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts.map +1 -1
  43. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-graph.js +124 -7
  44. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.d.ts +2 -1
  45. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.d.ts.map +1 -1
  46. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.js +2 -2
  47. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts +2 -1
  48. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
  49. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +19 -7
  50. package/dist/src/infrastructure/services/agents/feature-agent/nodes/implement.node.js +1 -1
  51. package/dist/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts +11 -2
  52. package/dist/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts.map +1 -1
  53. package/dist/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.js +46 -15
  54. package/dist/src/infrastructure/services/agents/feature-agent/nodes/repair.node.d.ts +23 -0
  55. package/dist/src/infrastructure/services/agents/feature-agent/nodes/repair.node.d.ts.map +1 -0
  56. package/dist/src/infrastructure/services/agents/feature-agent/nodes/repair.node.js +77 -0
  57. package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/plan.schema.d.ts +4 -0
  58. package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/plan.schema.d.ts.map +1 -0
  59. package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/plan.schema.js +54 -0
  60. package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/research.schema.d.ts +3 -0
  61. package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/research.schema.d.ts.map +1 -0
  62. package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/research.schema.js +24 -0
  63. package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/spec.schema.d.ts +4 -0
  64. package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/spec.schema.d.ts.map +1 -0
  65. package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/spec.schema.js +50 -0
  66. package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/validation.d.ts +8 -0
  67. package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/validation.d.ts.map +1 -0
  68. package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/validation.js +36 -0
  69. package/dist/src/infrastructure/services/agents/feature-agent/nodes/validate.node.d.ts +12 -0
  70. package/dist/src/infrastructure/services/agents/feature-agent/nodes/validate.node.d.ts.map +1 -0
  71. package/dist/src/infrastructure/services/agents/feature-agent/nodes/validate.node.js +58 -0
  72. package/dist/src/infrastructure/services/agents/feature-agent/phase-timing-context.d.ts +30 -0
  73. package/dist/src/infrastructure/services/agents/feature-agent/phase-timing-context.d.ts.map +1 -0
  74. package/dist/src/infrastructure/services/agents/feature-agent/phase-timing-context.js +68 -0
  75. package/dist/src/infrastructure/services/agents/feature-agent/state.d.ts +5 -1
  76. package/dist/src/infrastructure/services/agents/feature-agent/state.d.ts.map +1 -1
  77. package/dist/src/infrastructure/services/agents/feature-agent/state.js +14 -1
  78. package/dist/src/presentation/cli/commands/feat/approve.command.d.ts +12 -0
  79. package/dist/src/presentation/cli/commands/feat/approve.command.d.ts.map +1 -0
  80. package/dist/src/presentation/cli/commands/feat/approve.command.js +48 -0
  81. package/dist/src/presentation/cli/commands/feat/index.d.ts.map +1 -1
  82. package/dist/src/presentation/cli/commands/feat/index.js +7 -1
  83. package/dist/src/presentation/cli/commands/feat/ls.command.d.ts.map +1 -1
  84. package/dist/src/presentation/cli/commands/feat/ls.command.js +13 -5
  85. package/dist/src/presentation/cli/commands/feat/new.command.d.ts.map +1 -1
  86. package/dist/src/presentation/cli/commands/feat/new.command.js +35 -16
  87. package/dist/src/presentation/cli/commands/feat/reject.command.d.ts +12 -0
  88. package/dist/src/presentation/cli/commands/feat/reject.command.d.ts.map +1 -0
  89. package/dist/src/presentation/cli/commands/feat/reject.command.js +50 -0
  90. package/dist/src/presentation/cli/commands/feat/resolve-waiting-feature.d.ts +30 -0
  91. package/dist/src/presentation/cli/commands/feat/resolve-waiting-feature.d.ts.map +1 -0
  92. package/dist/src/presentation/cli/commands/feat/resolve-waiting-feature.js +57 -0
  93. package/dist/src/presentation/cli/commands/feat/review.command.d.ts +12 -0
  94. package/dist/src/presentation/cli/commands/feat/review.command.d.ts.map +1 -0
  95. package/dist/src/presentation/cli/commands/feat/review.command.js +49 -0
  96. package/dist/src/presentation/cli/commands/feat/show.command.d.ts.map +1 -1
  97. package/dist/src/presentation/cli/commands/feat/show.command.js +51 -6
  98. package/dist/tsconfig.build.tsbuildinfo +1 -1
  99. package/package.json +3 -1
  100. package/web/.next/BUILD_ID +1 -1
  101. package/web/.next/build-manifest.json +2 -2
  102. package/web/.next/cache/.previewinfo +1 -1
  103. package/web/.next/cache/.rscinfo +1 -1
  104. package/web/.next/cache/config.json +3 -3
  105. package/web/.next/fallback-build-manifest.json +2 -2
  106. package/web/.next/prerender-manifest.json +3 -3
  107. package/web/.next/required-server-files.js +1 -1
  108. package/web/.next/required-server-files.json +1 -1
  109. package/web/.next/server/app/_global-error.html +2 -2
  110. package/web/.next/server/app/_global-error.rsc +1 -1
  111. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  112. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  113. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  114. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  115. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  116. package/web/.next/server/app/_not-found.html +2 -2
  117. package/web/.next/server/app/_not-found.rsc +1 -1
  118. package/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  119. package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  120. package/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  121. package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  122. package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  123. package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  124. package/web/.next/server/app/index.html +2 -2
  125. package/web/.next/server/app/index.rsc +1 -1
  126. package/web/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  127. package/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
  128. package/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  129. package/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  130. package/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  131. package/web/.next/server/app/version.html +2 -2
  132. package/web/.next/server/app/version.rsc +1 -1
  133. package/web/.next/server/app/version.segments/_full.segment.rsc +1 -1
  134. package/web/.next/server/app/version.segments/_head.segment.rsc +1 -1
  135. package/web/.next/server/app/version.segments/_index.segment.rsc +1 -1
  136. package/web/.next/server/app/version.segments/_tree.segment.rsc +1 -1
  137. package/web/.next/server/app/version.segments/version/__PAGE__.segment.rsc +1 -1
  138. package/web/.next/server/app/version.segments/version.segment.rsc +1 -1
  139. package/web/.next/server/chunks/ssr/_19be0743._.js +1 -1
  140. package/web/.next/server/pages/404.html +2 -2
  141. package/web/.next/server/pages/500.html +2 -2
  142. package/web/.next/server/server-reference-manifest.js +1 -1
  143. package/web/.next/server/server-reference-manifest.json +1 -1
  144. package/web/.next/trace +1 -1
  145. package/web/.next/trace-build +1 -1
  146. package/dist/src/application/use-cases/features/create-feature.use-case.d.ts +0 -43
  147. package/dist/src/application/use-cases/features/create-feature.use-case.d.ts.map +0 -1
  148. /package/web/.next/static/{nw4IT0rr_mE5Tx22X9OgO → xKJulVMYO_ZRGoqa6eM7G}/_buildManifest.js +0 -0
  149. /package/web/.next/static/{nw4IT0rr_mE5Tx22X9OgO → xKJulVMYO_ZRGoqa6eM7G}/_clientMiddlewareManifest.json +0 -0
  150. /package/web/.next/static/{nw4IT0rr_mE5Tx22X9OgO → xKJulVMYO_ZRGoqa6eM7G}/_ssgManifest.js +0 -0
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Phase Timing Context
3
+ *
4
+ * Module-level singleton that provides phase timing recording
5
+ * to executeNode() without changing its public API.
6
+ *
7
+ * The worker calls setPhaseTimingContext() once after DI init.
8
+ * Node helpers call recordPhaseStart/End() during graph execution.
9
+ * Errors are swallowed so timing never blocks graph execution.
10
+ */
11
+ import type { IPhaseTimingRepository } from '../../../../application/ports/output/agents/phase-timing-repository.interface.js';
12
+ /**
13
+ * Set the phase timing context. Called once by the worker after DI init.
14
+ */
15
+ export declare function setPhaseTimingContext(runId: string, repository: IPhaseTimingRepository): void;
16
+ /**
17
+ * Clear the phase timing context. Useful for testing.
18
+ */
19
+ export declare function clearPhaseTimingContext(): void;
20
+ /**
21
+ * Record the start of a phase. Returns the timing record ID (for later update)
22
+ * or null if context is not set or save fails.
23
+ */
24
+ export declare function recordPhaseStart(phase: string): Promise<string | null>;
25
+ /**
26
+ * Record the end of a phase. Updates the timing record with completedAt and durationMs.
27
+ * No-op if timingId is null (phase start was skipped or failed).
28
+ */
29
+ export declare function recordPhaseEnd(timingId: string | null, durationMs: number): Promise<void>;
30
+ //# sourceMappingURL=phase-timing-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase-timing-context.d.ts","sourceRoot":"","sources":["../../../../../../src/infrastructure/services/agents/feature-agent/phase-timing-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wEAAwE,CAAC;AAKrH;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,sBAAsB,GAAG,IAAI,CAG7F;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAG9C;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmB5E;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAW/F"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Phase Timing Context
3
+ *
4
+ * Module-level singleton that provides phase timing recording
5
+ * to executeNode() without changing its public API.
6
+ *
7
+ * The worker calls setPhaseTimingContext() once after DI init.
8
+ * Node helpers call recordPhaseStart/End() during graph execution.
9
+ * Errors are swallowed so timing never blocks graph execution.
10
+ */
11
+ import { randomUUID } from 'node:crypto';
12
+ let contextRunId;
13
+ let contextRepository;
14
+ /**
15
+ * Set the phase timing context. Called once by the worker after DI init.
16
+ */
17
+ export function setPhaseTimingContext(runId, repository) {
18
+ contextRunId = runId;
19
+ contextRepository = repository;
20
+ }
21
+ /**
22
+ * Clear the phase timing context. Useful for testing.
23
+ */
24
+ export function clearPhaseTimingContext() {
25
+ contextRunId = undefined;
26
+ contextRepository = undefined;
27
+ }
28
+ /**
29
+ * Record the start of a phase. Returns the timing record ID (for later update)
30
+ * or null if context is not set or save fails.
31
+ */
32
+ export async function recordPhaseStart(phase) {
33
+ if (!contextRunId || !contextRepository)
34
+ return null;
35
+ const id = randomUUID();
36
+ const now = new Date();
37
+ try {
38
+ await contextRepository.save({
39
+ id,
40
+ agentRunId: contextRunId,
41
+ phase,
42
+ startedAt: now,
43
+ createdAt: now,
44
+ updatedAt: now,
45
+ });
46
+ return id;
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ /**
53
+ * Record the end of a phase. Updates the timing record with completedAt and durationMs.
54
+ * No-op if timingId is null (phase start was skipped or failed).
55
+ */
56
+ export async function recordPhaseEnd(timingId, durationMs) {
57
+ if (!timingId || !contextRepository)
58
+ return;
59
+ try {
60
+ await contextRepository.update(timingId, {
61
+ completedAt: new Date(),
62
+ durationMs: BigInt(durationMs),
63
+ });
64
+ }
65
+ catch {
66
+ // Swallow — timing update failure is non-fatal
67
+ }
68
+ }
@@ -1,3 +1,4 @@
1
+ import type { ApprovalGates } from '../../../../domain/generated/output.js';
1
2
  /**
2
3
  * State annotation for the feature-agent graph.
3
4
  *
@@ -32,8 +33,11 @@ export declare const FeatureAgentAnnotation: import("@langchain/langgraph").Anno
32
33
  Root: <S extends import("@langchain/langgraph").StateDefinition>(sd: S) => import("@langchain/langgraph").AnnotationRoot<S>;
33
34
  };
34
35
  error: import("@langchain/langgraph").BinaryOperatorAggregate<string | null, string | null>;
35
- approvalMode: import("@langchain/langgraph").BinaryOperatorAggregate<string | undefined, string | undefined>;
36
+ approvalGates: import("@langchain/langgraph").BinaryOperatorAggregate<ApprovalGates | undefined, ApprovalGates | undefined>;
36
37
  messages: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
38
+ validationRetries: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
39
+ lastValidationTarget: import("@langchain/langgraph").BinaryOperatorAggregate<string, string>;
40
+ lastValidationErrors: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
37
41
  }>;
38
42
  export type FeatureAgentState = typeof FeatureAgentAnnotation.State;
39
43
  //# sourceMappingURL=state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../../src/infrastructure/services/agents/feature-agent/state.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkBjC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,OAAO,sBAAsB,CAAC,KAAK,CAAC"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../../src/infrastructure/services/agents/feature-agent/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+BjC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,OAAO,sBAAsB,CAAC,KAAK,CAAC"}
@@ -16,7 +16,7 @@ export const FeatureAgentAnnotation = Annotation.Root({
16
16
  reducer: (prev, next) => (next !== undefined ? next : prev),
17
17
  default: () => null,
18
18
  }),
19
- approvalMode: Annotation({
19
+ approvalGates: Annotation({
20
20
  reducer: (prev, next) => next ?? prev,
21
21
  default: () => undefined,
22
22
  }),
@@ -24,4 +24,17 @@ export const FeatureAgentAnnotation = Annotation.Root({
24
24
  reducer: (prev, next) => [...prev, ...next],
25
25
  default: () => [],
26
26
  }),
27
+ // --- Validation state channels (for validate/repair loops) ---
28
+ validationRetries: Annotation({
29
+ reducer: (_prev, next) => next,
30
+ default: () => 0,
31
+ }),
32
+ lastValidationTarget: Annotation({
33
+ reducer: (_prev, next) => next,
34
+ default: () => '',
35
+ }),
36
+ lastValidationErrors: Annotation({
37
+ reducer: (_prev, next) => next,
38
+ default: () => [],
39
+ }),
27
40
  });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Feature Approve Command
3
+ *
4
+ * Approves a feature waiting for human approval, resuming
5
+ * the agent to continue with the next phase.
6
+ *
7
+ * Usage:
8
+ * shep feat approve [id]
9
+ */
10
+ import { Command } from 'commander';
11
+ export declare function createApproveCommand(): Command;
12
+ //# sourceMappingURL=approve.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approve.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/approve.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,oBAAoB,IAAI,OAAO,CAqC9C"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Feature Approve Command
3
+ *
4
+ * Approves a feature waiting for human approval, resuming
5
+ * the agent to continue with the next phase.
6
+ *
7
+ * Usage:
8
+ * shep feat approve [id]
9
+ */
10
+ import { Command } from 'commander';
11
+ import { container } from '../../../../infrastructure/di/container.js';
12
+ import { ApproveAgentRunUseCase } from '../../../../application/use-cases/agents/approve-agent-run.use-case.js';
13
+ import { resolveWaitingFeature } from './resolve-waiting-feature.js';
14
+ import { colors, messages } from '../../ui/index.js';
15
+ export function createApproveCommand() {
16
+ return new Command('approve')
17
+ .description('Approve a feature waiting for review')
18
+ .argument('[id]', 'Feature ID (auto-resolves if omitted)')
19
+ .action(async (featureId) => {
20
+ try {
21
+ const featureRepo = container.resolve('IFeatureRepository');
22
+ const runRepo = container.resolve('IAgentRunRepository');
23
+ const repoPath = process.cwd();
24
+ const { feature, run } = await resolveWaitingFeature({
25
+ featureId,
26
+ repoPath,
27
+ featureRepo,
28
+ runRepo,
29
+ });
30
+ const approveUseCase = container.resolve(ApproveAgentRunUseCase);
31
+ const result = await approveUseCase.execute(run.id);
32
+ if (!result.approved) {
33
+ throw new Error(result.reason);
34
+ }
35
+ const phase = run.result?.startsWith('node:') ? run.result.slice(5) : 'unknown';
36
+ messages.newline();
37
+ messages.success(`Approved: ${feature.name}`);
38
+ console.log(` ${colors.muted('Phase:')} ${colors.success(phase)} approved`);
39
+ console.log(` ${colors.muted('Agent:')} resumed`);
40
+ messages.newline();
41
+ }
42
+ catch (error) {
43
+ const err = error instanceof Error ? error : new Error(String(error));
44
+ messages.error('Failed to approve feature', err);
45
+ process.exitCode = 1;
46
+ }
47
+ });
48
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAQ3C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAW3C"}
@@ -15,6 +15,9 @@ import { createLsCommand } from './ls.command.js';
15
15
  import { createShowCommand } from './show.command.js';
16
16
  import { createDelCommand } from './del.command.js';
17
17
  import { createResumeCommand } from './resume.command.js';
18
+ import { createReviewCommand } from './review.command.js';
19
+ import { createApproveCommand } from './approve.command.js';
20
+ import { createRejectCommand } from './reject.command.js';
18
21
  /**
19
22
  * Create the feat command group
20
23
  */
@@ -25,5 +28,8 @@ export function createFeatCommand() {
25
28
  .addCommand(createLsCommand())
26
29
  .addCommand(createShowCommand())
27
30
  .addCommand(createDelCommand())
28
- .addCommand(createResumeCommand());
31
+ .addCommand(createResumeCommand())
32
+ .addCommand(createReviewCommand())
33
+ .addCommand(createApproveCommand())
34
+ .addCommand(createRejectCommand());
29
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ls.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/ls.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsGpC,wBAAgB,eAAe,IAAI,OAAO,CAqDzC"}
1
+ {"version":3,"file":"ls.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/ls.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+GpC,wBAAgB,eAAe,IAAI,OAAO,CAqDzC"}
@@ -15,7 +15,7 @@ import { Command } from 'commander';
15
15
  import { container } from '../../../../infrastructure/di/container.js';
16
16
  import { ListFeaturesUseCase } from '../../../../application/use-cases/features/list-features.use-case.js';
17
17
  import { colors, symbols, messages, renderListView } from '../../ui/index.js';
18
- /** Map graph node names to human-readable phase labels. */
18
+ /** Map graph node names to human-readable phase labels (active). */
19
19
  const NODE_TO_PHASE = {
20
20
  analyze: 'Analyzing',
21
21
  requirements: 'Requirements',
@@ -23,6 +23,14 @@ const NODE_TO_PHASE = {
23
23
  plan: 'Planning',
24
24
  implement: 'Implementing',
25
25
  };
26
+ /** Map graph node names to approval action labels. */
27
+ const NODE_TO_APPROVE = {
28
+ analyze: 'Approve Analysis',
29
+ requirements: 'Approve PRD',
30
+ research: 'Approve Research',
31
+ plan: 'Approve Plan',
32
+ implement: 'Approve Merge',
33
+ };
26
34
  /**
27
35
  * Derive the display status from the agent run.
28
36
  * Returns the current graph node as a phase label with an activity indicator.
@@ -33,9 +41,8 @@ function formatStatus(feature, run) {
33
41
  return `${colors.muted(symbols.dotEmpty)} ${colors.muted(feature.lifecycle)}`;
34
42
  }
35
43
  const isRunning = run.status === 'running' || run.status === 'pending';
36
- const phase = run.result?.startsWith('node:')
37
- ? (NODE_TO_PHASE[run.result.slice(5)] ?? run.result.slice(5))
38
- : feature.lifecycle;
44
+ const nodeName = run.result?.startsWith('node:') ? run.result.slice(5) : undefined;
45
+ const phase = nodeName ? (NODE_TO_PHASE[nodeName] ?? nodeName) : feature.lifecycle;
39
46
  if (isRunning) {
40
47
  // Check PID liveness for running agents
41
48
  if (run.pid && !isProcessAlive(run.pid)) {
@@ -50,7 +57,8 @@ function formatStatus(feature, run) {
50
57
  return `${colors.error(symbols.error)} ${colors.error('Failed')}`;
51
58
  }
52
59
  if (run.status === 'waiting_approval') {
53
- return `${colors.warning(symbols.warning)} ${colors.warning(phase)}`;
60
+ const action = nodeName ? (NODE_TO_APPROVE[nodeName] ?? phase) : phase;
61
+ return `${colors.warning(symbols.warning)} ${colors.warning(action)}`;
54
62
  }
55
63
  if (run.status === 'interrupted') {
56
64
  return `${colors.error(symbols.error)} ${colors.error('Interrupted')}`;
@@ -1 +1 @@
1
- {"version":3,"file":"new.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/new.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CA+C1C"}
1
+ {"version":3,"file":"new.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/new.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAiE1C"}
@@ -10,9 +10,12 @@
10
10
  * $ shep feat new "Add login page" --repo /path/to/project
11
11
  */
12
12
  import { Command } from 'commander';
13
+ import { createHash } from 'node:crypto';
14
+ import { join } from 'node:path';
13
15
  import { container } from '../../../../infrastructure/di/container.js';
14
- import { CreateFeatureUseCase } from '../../../../application/use-cases/features/create-feature.use-case.js';
16
+ import { CreateFeatureUseCase } from '../../../../application/use-cases/features/create/create-feature.use-case.js';
15
17
  import { colors, messages, spinner } from '../../ui/index.js';
18
+ import { SHEP_HOME_DIR } from '../../../../infrastructure/services/filesystem/shep-directory.service.js';
16
19
  /**
17
20
  * Create the feat new command
18
21
  */
@@ -21,7 +24,6 @@ export function createNewCommand() {
21
24
  .description('Create a new feature')
22
25
  .argument('<description>', 'Feature description')
23
26
  .option('-r, --repo <path>', 'Repository path (defaults to current directory)')
24
- .option('--interactive', 'Require approval after every agent step')
25
27
  .option('--allow-prd', 'Auto-approve through requirements, pause after')
26
28
  .option('--allow-plan', 'Auto-approve through planning, pause at implementation')
27
29
  .option('--allow-all', 'Run fully autonomous (no approval pauses)')
@@ -29,29 +31,46 @@ export function createNewCommand() {
29
31
  try {
30
32
  const useCase = container.resolve(CreateFeatureUseCase);
31
33
  const repoPath = options.repo ?? process.cwd();
32
- // Determine approval mode from flags (last one wins if multiple specified)
33
- let approvalMode;
34
- if (options.interactive)
35
- approvalMode = 'interactive';
34
+ // Build approval gates from flags (default: pause after every phase)
35
+ let approvalGates = { allowPrd: false, allowPlan: false };
36
36
  if (options.allowPrd)
37
- approvalMode = 'allow-prd';
37
+ approvalGates = { ...approvalGates, allowPrd: true };
38
38
  if (options.allowPlan)
39
- approvalMode = 'allow-plan';
39
+ approvalGates = { allowPrd: true, allowPlan: true };
40
40
  if (options.allowAll)
41
- approvalMode = 'allow-all';
42
- const feature = await spinner('Thinking', () => useCase.execute({
41
+ approvalGates = undefined; // no gates = fully autonomous
42
+ const result = await spinner('Thinking', () => useCase.execute({
43
43
  userInput: description,
44
44
  repositoryPath: repoPath,
45
- approvalMode,
45
+ approvalGates,
46
46
  }));
47
+ const { feature, warning } = result;
48
+ const repoHash = createHash('sha256').update(repoPath).digest('hex').slice(0, 16);
49
+ const wtSlug = feature.branch.replace(/\//g, '-');
50
+ const worktreePath = join(SHEP_HOME_DIR, 'repos', repoHash, 'wt', wtSlug);
47
51
  messages.newline();
52
+ if (warning) {
53
+ messages.warning(warning);
54
+ }
48
55
  messages.success('Feature created');
49
- console.log(` ${colors.muted('ID:')} ${colors.accent(feature.id)}`);
50
- console.log(` ${colors.muted('Name:')} ${feature.name}`);
51
- console.log(` ${colors.muted('Branch:')} ${colors.accent(feature.branch)}`);
52
- console.log(` ${colors.muted('Status:')} ${feature.lifecycle}`);
56
+ console.log(` ${colors.muted('ID:')} ${colors.accent(feature.id)}`);
57
+ console.log(` ${colors.muted('Name:')} ${feature.name}`);
58
+ console.log(` ${colors.muted('Branch:')} ${colors.accent(feature.branch)}`);
59
+ console.log(` ${colors.muted('Status:')} ${feature.lifecycle}`);
60
+ console.log(` ${colors.muted('Worktree:')} ${worktreePath}`);
61
+ if (feature.specPath) {
62
+ console.log(` ${colors.muted('Spec:')} ${feature.specPath}`);
63
+ }
53
64
  if (feature.agentRunId) {
54
- console.log(` ${colors.muted('Agent:')} ${colors.success('spawned')} (run ${feature.agentRunId.slice(0, 8)})`);
65
+ console.log(` ${colors.muted('Agent:')} ${colors.success('spawned')} (run ${feature.agentRunId.slice(0, 8)})`);
66
+ }
67
+ if (approvalGates) {
68
+ const hint = !approvalGates.allowPrd
69
+ ? 'pause after every phase'
70
+ : !approvalGates.allowPlan
71
+ ? 'auto-approve through requirements, pause after'
72
+ : 'auto-approve through planning, pause at implementation';
73
+ console.log(` ${colors.muted('Review:')} ${hint}`);
55
74
  }
56
75
  messages.newline();
57
76
  }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Feature Reject Command
3
+ *
4
+ * Rejects a feature waiting for human approval, cancelling
5
+ * the agent run with an optional reason.
6
+ *
7
+ * Usage:
8
+ * shep feat reject [id] [--reason <text>]
9
+ */
10
+ import { Command } from 'commander';
11
+ export declare function createRejectCommand(): Command;
12
+ //# sourceMappingURL=reject.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reject.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/reject.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,mBAAmB,IAAI,OAAO,CAsC7C"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Feature Reject Command
3
+ *
4
+ * Rejects a feature waiting for human approval, cancelling
5
+ * the agent run with an optional reason.
6
+ *
7
+ * Usage:
8
+ * shep feat reject [id] [--reason <text>]
9
+ */
10
+ import { Command } from 'commander';
11
+ import { container } from '../../../../infrastructure/di/container.js';
12
+ import { RejectAgentRunUseCase } from '../../../../application/use-cases/agents/reject-agent-run.use-case.js';
13
+ import { resolveWaitingFeature } from './resolve-waiting-feature.js';
14
+ import { colors, messages } from '../../ui/index.js';
15
+ export function createRejectCommand() {
16
+ return new Command('reject')
17
+ .description('Reject a feature waiting for review')
18
+ .argument('[id]', 'Feature ID (auto-resolves if omitted)')
19
+ .option('--reason <text>', 'Rejection reason')
20
+ .action(async (featureId, options) => {
21
+ try {
22
+ const featureRepo = container.resolve('IFeatureRepository');
23
+ const runRepo = container.resolve('IAgentRunRepository');
24
+ const repoPath = process.cwd();
25
+ const { feature, run } = await resolveWaitingFeature({
26
+ featureId,
27
+ repoPath,
28
+ featureRepo,
29
+ runRepo,
30
+ });
31
+ const rejectUseCase = container.resolve(RejectAgentRunUseCase);
32
+ const result = await rejectUseCase.execute(run.id, options.reason);
33
+ if (!result.rejected) {
34
+ throw new Error(result.reason);
35
+ }
36
+ messages.newline();
37
+ messages.warning(`Rejected: ${feature.name}`);
38
+ if (options.reason) {
39
+ console.log(` ${colors.muted('Reason:')} ${options.reason}`);
40
+ }
41
+ console.log(` ${colors.muted('Agent:')} cancelled`);
42
+ messages.newline();
43
+ }
44
+ catch (error) {
45
+ const err = error instanceof Error ? error : new Error(String(error));
46
+ messages.error('Failed to reject feature', err);
47
+ process.exitCode = 1;
48
+ }
49
+ });
50
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Resolve Waiting Feature Helper
3
+ *
4
+ * Shared helper for feat review/approve/reject commands.
5
+ * Given an optional feature ID and repo path, resolves the target
6
+ * feature that is currently waiting for human approval.
7
+ */
8
+ import type { Feature, AgentRun } from '../../../../domain/generated/output.js';
9
+ import type { IFeatureRepository } from '../../../../application/ports/output/repositories/feature-repository.interface.js';
10
+ import type { IAgentRunRepository } from '../../../../application/ports/output/agents/agent-run-repository.interface.js';
11
+ export interface ResolveWaitingFeatureOptions {
12
+ featureId?: string;
13
+ repoPath: string;
14
+ featureRepo: IFeatureRepository;
15
+ runRepo: IAgentRunRepository;
16
+ }
17
+ export interface ResolvedWaitingFeature {
18
+ feature: Feature;
19
+ run: AgentRun;
20
+ }
21
+ /**
22
+ * Resolve the target feature waiting for approval.
23
+ *
24
+ * If featureId is provided, looks it up directly.
25
+ * Otherwise, lists features for the repo and finds the single waiting one.
26
+ *
27
+ * @throws When feature not found, not waiting, or ambiguous (0 or 2+)
28
+ */
29
+ export declare function resolveWaitingFeature(options: ResolveWaitingFeatureOptions): Promise<ResolvedWaitingFeature>;
30
+ //# sourceMappingURL=resolve-waiting-feature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-waiting-feature.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/resolve-waiting-feature.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAC;AAEhF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mFAAmF,CAAC;AAC5H,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+EAA+E,CAAC;AAEzH,MAAM,WAAW,4BAA4B;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,kBAAkB,CAAC;IAChC,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,QAAQ,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CAOjC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Resolve Waiting Feature Helper
3
+ *
4
+ * Shared helper for feat review/approve/reject commands.
5
+ * Given an optional feature ID and repo path, resolves the target
6
+ * feature that is currently waiting for human approval.
7
+ */
8
+ import { AgentRunStatus } from '../../../../domain/generated/output.js';
9
+ /**
10
+ * Resolve the target feature waiting for approval.
11
+ *
12
+ * If featureId is provided, looks it up directly.
13
+ * Otherwise, lists features for the repo and finds the single waiting one.
14
+ *
15
+ * @throws When feature not found, not waiting, or ambiguous (0 or 2+)
16
+ */
17
+ export async function resolveWaitingFeature(options) {
18
+ const { featureId, repoPath, featureRepo, runRepo } = options;
19
+ if (featureId) {
20
+ return resolveExplicit(featureId, featureRepo, runRepo);
21
+ }
22
+ return resolveAuto(repoPath, featureRepo, runRepo);
23
+ }
24
+ async function resolveExplicit(featureId, featureRepo, runRepo) {
25
+ const feature = (await featureRepo.findById(featureId)) ?? (await featureRepo.findByIdPrefix(featureId));
26
+ if (!feature) {
27
+ throw new Error(`Feature not found: "${featureId}"`);
28
+ }
29
+ if (!feature.agentRunId) {
30
+ throw new Error(`Feature "${feature.name}" has no agent run`);
31
+ }
32
+ const run = await runRepo.findById(feature.agentRunId);
33
+ if (!run || run.status !== AgentRunStatus.waitingApproval) {
34
+ throw new Error(`Feature "${feature.name}" is not waiting for approval (status: ${run?.status ?? 'unknown'})`);
35
+ }
36
+ return { feature, run };
37
+ }
38
+ async function resolveAuto(repoPath, featureRepo, runRepo) {
39
+ const features = await featureRepo.list({ repositoryPath: repoPath });
40
+ const waiting = [];
41
+ for (const feature of features) {
42
+ if (!feature.agentRunId)
43
+ continue;
44
+ const run = await runRepo.findById(feature.agentRunId);
45
+ if (run && run.status === AgentRunStatus.waitingApproval) {
46
+ waiting.push({ feature, run });
47
+ }
48
+ }
49
+ if (waiting.length === 0) {
50
+ throw new Error('No features waiting for approval in this repository');
51
+ }
52
+ if (waiting.length > 1) {
53
+ const names = waiting.map((w) => ` - ${w.feature.name} (${w.feature.id.slice(0, 8)})`);
54
+ throw new Error(`Multiple features waiting for approval. Specify one:\n${names.join('\n')}`);
55
+ }
56
+ return waiting[0];
57
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Feature Review Command
3
+ *
4
+ * Shows the current state of a feature waiting for approval,
5
+ * including which phase triggered the interrupt and generated content.
6
+ *
7
+ * Usage:
8
+ * shep feat review [id]
9
+ */
10
+ import { Command } from 'commander';
11
+ export declare function createReviewCommand(): Command;
12
+ //# sourceMappingURL=review.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/review.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,mBAAmB,IAAI,OAAO,CAqC7C"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Feature Review Command
3
+ *
4
+ * Shows the current state of a feature waiting for approval,
5
+ * including which phase triggered the interrupt and generated content.
6
+ *
7
+ * Usage:
8
+ * shep feat review [id]
9
+ */
10
+ import { Command } from 'commander';
11
+ import { container } from '../../../../infrastructure/di/container.js';
12
+ import { resolveWaitingFeature } from './resolve-waiting-feature.js';
13
+ import { colors, messages } from '../../ui/index.js';
14
+ export function createReviewCommand() {
15
+ return new Command('review')
16
+ .description('Review a feature waiting for approval')
17
+ .argument('[id]', 'Feature ID (auto-resolves if omitted)')
18
+ .action(async (featureId) => {
19
+ try {
20
+ const featureRepo = container.resolve('IFeatureRepository');
21
+ const runRepo = container.resolve('IAgentRunRepository');
22
+ const repoPath = process.cwd();
23
+ const { feature, run } = await resolveWaitingFeature({
24
+ featureId,
25
+ repoPath,
26
+ featureRepo,
27
+ runRepo,
28
+ });
29
+ const phase = run.result?.startsWith('node:') ? run.result.slice(5) : 'unknown';
30
+ messages.newline();
31
+ messages.info(`Feature waiting for approval: ${colors.accent(feature.name)}`);
32
+ console.log(` ${colors.muted('ID:')} ${feature.id.slice(0, 8)}`);
33
+ console.log(` ${colors.muted('Phase:')} ${colors.warning(phase)}`);
34
+ console.log(` ${colors.muted('Branch:')} ${colors.accent(feature.branch)}`);
35
+ if (feature.specPath) {
36
+ console.log(` ${colors.muted('Specs:')} ${feature.specPath}`);
37
+ }
38
+ messages.newline();
39
+ console.log(` ${colors.muted('To approve:')} shep feat approve ${feature.id.slice(0, 8)}`);
40
+ console.log(` ${colors.muted('To reject:')} shep feat reject ${feature.id.slice(0, 8)}`);
41
+ messages.newline();
42
+ }
43
+ catch (error) {
44
+ const err = error instanceof Error ? error : new Error(String(error));
45
+ messages.error('Failed to review feature', err);
46
+ process.exitCode = 1;
47
+ }
48
+ });
49
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"show.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/show.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4DpC,wBAAgB,iBAAiB,IAAI,OAAO,CAyE3C"}
1
+ {"version":3,"file":"show.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/show.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuEpC,wBAAgB,iBAAiB,IAAI,OAAO,CAkH3C"}