@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
@@ -28,11 +28,14 @@ export default tseslint.config(
28
28
  // Build outputs
29
29
  'dist/**',
30
30
  'build/**',
31
+ 'web/**',
31
32
  '**/.next/**',
32
33
  'out/**',
33
34
  '**/storybook-static/**',
34
35
  // Dependencies
35
36
  'node_modules/**',
37
+ // Git worktrees (parallel development checkouts)
38
+ '.worktrees/**',
36
39
  // Generated files
37
40
  'apis/**',
38
41
  '*.generated.*',
@@ -50,6 +53,7 @@ export default tseslint.config(
50
53
  '*.config.js',
51
54
  '*.config.mjs',
52
55
  '*.config.cjs',
56
+ '*.config.ts',
53
57
  ],
54
58
  },
55
59
  // =============================================================================
@@ -9,6 +9,7 @@
9
9
  * - Application layer depends on this interface
10
10
  * - Infrastructure layer provides concrete implementation
11
11
  */
12
+ import type { ApprovalGates } from '../../../../domain/generated/output.js';
12
13
  /**
13
14
  * Service interface for feature agent background process management.
14
15
  */
@@ -23,7 +24,7 @@ export interface IFeatureAgentProcessService {
23
24
  * @returns The PID of the spawned process
24
25
  */
25
26
  spawn(featureId: string, runId: string, repoPath: string, specDir: string, worktreePath?: string, options?: {
26
- approvalMode?: string;
27
+ approvalGates?: ApprovalGates;
27
28
  resume?: boolean;
28
29
  threadId?: string;
29
30
  resumeFromInterrupt?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"feature-agent-process.interface.d.ts","sourceRoot":"","sources":["../../../../../../src/application/ports/output/agents/feature-agent-process.interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;;OAQG;IACH,KAAK,CACH,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;QACR,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;KAC/B,GACA,MAAM,CAAC;IAEV;;;;;OAKG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAE9B;;;;;OAKG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD"}
1
+ {"version":3,"file":"feature-agent-process.interface.d.ts","sourceRoot":"","sources":["../../../../../../src/application/ports/output/agents/feature-agent-process.interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;;OAQG;IACH,KAAK,CACH,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;QACR,aAAa,CAAC,EAAE,aAAa,CAAC;QAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;KAC/B,GACA,MAAM,CAAC;IAEV;;;;;OAKG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAE9B;;;;;OAKG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD"}
@@ -9,6 +9,7 @@ export type { IAgentExecutorProvider } from './agent-executor-provider.interface
9
9
  export type { IAgentRegistry, AgentDefinitionWithFactory } from './agent-registry.interface.js';
10
10
  export type { IAgentRunner, AgentRunOptions } from './agent-runner.interface.js';
11
11
  export type { IAgentRunRepository } from './agent-run-repository.interface.js';
12
+ export type { IPhaseTimingRepository } from './phase-timing-repository.interface.js';
12
13
  export type { IAgentValidator, AgentValidationResult } from './agent-validator.interface.js';
13
14
  export type { IFeatureAgentProcessService } from './feature-agent-process.interface.js';
14
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/application/ports/output/agents/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AACnF,YAAY,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,YAAY,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChG,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACjF,YAAY,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,YAAY,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAC7F,YAAY,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/application/ports/output/agents/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AACnF,YAAY,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,YAAY,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChG,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACjF,YAAY,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,YAAY,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,YAAY,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAC7F,YAAY,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Phase Timing Repository Interface
3
+ *
4
+ * Output port for PhaseTiming persistence operations.
5
+ * Records timing data for each agent graph node execution.
6
+ *
7
+ * Following Clean Architecture:
8
+ * - Domain and Application layers depend on this interface
9
+ * - Infrastructure layer provides concrete implementations
10
+ */
11
+ import type { PhaseTiming } from '../../../../domain/generated/output.js';
12
+ /**
13
+ * Repository interface for PhaseTiming entity persistence.
14
+ */
15
+ export interface IPhaseTimingRepository {
16
+ /**
17
+ * Save a new phase timing record.
18
+ *
19
+ * @param phaseTiming - The phase timing to persist
20
+ */
21
+ save(phaseTiming: PhaseTiming): Promise<void>;
22
+ /**
23
+ * Update a phase timing record (typically to set completedAt and durationMs).
24
+ *
25
+ * @param id - The phase timing ID
26
+ * @param updates - Fields to update
27
+ */
28
+ update(id: string, updates: Partial<Pick<PhaseTiming, 'completedAt' | 'durationMs'>>): Promise<void>;
29
+ /**
30
+ * Find all phase timings for an agent run.
31
+ *
32
+ * @param agentRunId - The agent run ID
33
+ * @returns Array of phase timings ordered by creation
34
+ */
35
+ findByRunId(agentRunId: string): Promise<PhaseTiming[]>;
36
+ /**
37
+ * Find all phase timings for a feature (via agent_runs join).
38
+ *
39
+ * @param featureId - The feature ID
40
+ * @returns Array of phase timings ordered by creation
41
+ */
42
+ findByFeatureId(featureId: string): Promise<PhaseTiming[]>;
43
+ }
44
+ //# sourceMappingURL=phase-timing-repository.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase-timing-repository.interface.d.ts","sourceRoot":"","sources":["../../../../../../src/application/ports/output/agents/phase-timing-repository.interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C;;;;;OAKG;IACH,MAAM,CACJ,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,GAAG,YAAY,CAAC,CAAC,GAChE,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;OAKG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAExD;;;;;OAKG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;CAC5D"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Phase Timing Repository Interface
3
+ *
4
+ * Output port for PhaseTiming persistence operations.
5
+ * Records timing data for each agent graph node execution.
6
+ *
7
+ * Following Clean Architecture:
8
+ * - Domain and Application layers depend on this interface
9
+ * - Infrastructure layer provides concrete implementations
10
+ */
11
+ export {};
@@ -1 +1 @@
1
- {"version":3,"file":"approve-agent-run.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/application/use-cases/agents/approve-agent-run.use-case.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6DAA6D,CAAC;AACvG,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,8DAA8D,CAAC;AAChH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iEAAiE,CAAC;AAC1G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAGlG,qBACa,sBAAsB;IAG/B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IAEnC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAE/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAElC,OAAO,CAAC,QAAQ,CAAC,eAAe;gBANf,kBAAkB,EAAE,mBAAmB,EAEvC,cAAc,EAAE,2BAA2B,EAE3C,iBAAiB,EAAE,kBAAkB,EAErC,eAAe,EAAE,gBAAgB;IAG9C,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAsC1E"}
1
+ {"version":3,"file":"approve-agent-run.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/application/use-cases/agents/approve-agent-run.use-case.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6DAA6D,CAAC;AACvG,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,8DAA8D,CAAC;AAChH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iEAAiE,CAAC;AAC1G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAGlG,qBACa,sBAAsB;IAG/B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IAEnC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAE/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAElC,OAAO,CAAC,QAAQ,CAAC,eAAe;gBANf,kBAAkB,EAAE,mBAAmB,EAEvC,cAAc,EAAE,2BAA2B,EAE3C,iBAAiB,EAAE,kBAAkB,EAErC,eAAe,EAAE,gBAAgB;IAG9C,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CA0C1E"}
@@ -48,11 +48,15 @@ let ApproveAgentRunUseCase = class ApproveAgentRunUseCase {
48
48
  : repoPath;
49
49
  const now = new Date();
50
50
  await this.agentRunRepository.updateStatus(id, AgentRunStatus.running, {
51
- approvalStatus: 'approved',
52
51
  updatedAt: now,
53
52
  });
54
53
  this.processService.spawn(run.featureId ?? '', id, repoPath, worktreePath, // specDir = worktree path (same as initial spawn)
55
- worktreePath, { resume: true, approvalMode: run.approvalMode });
54
+ worktreePath, {
55
+ resume: true,
56
+ approvalGates: run.approvalGates,
57
+ threadId: run.threadId,
58
+ resumeFromInterrupt: true,
59
+ });
56
60
  return { approved: true, reason: 'Approved and resumed' };
57
61
  }
58
62
  };
@@ -1 +1 @@
1
- {"version":3,"file":"reject-agent-run.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/application/use-cases/agents/reject-agent-run.use-case.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6DAA6D,CAAC;AAGvG,qBACa,qBAAqB;IAG9B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBAAlB,kBAAkB,EAAE,mBAAmB;IAGpD,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAuB3F"}
1
+ {"version":3,"file":"reject-agent-run.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/application/use-cases/agents/reject-agent-run.use-case.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6DAA6D,CAAC;AAGvG,qBACa,qBAAqB;IAG9B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBAAlB,kBAAkB,EAAE,mBAAmB;IAGpD,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAsB3F"}
@@ -36,7 +36,6 @@ let RejectAgentRunUseCase = class RejectAgentRunUseCase {
36
36
  }
37
37
  const now = new Date();
38
38
  await this.agentRunRepository.updateStatus(id, AgentRunStatus.cancelled, {
39
- approvalStatus: 'rejected',
40
39
  error: reason ?? 'Rejected by user',
41
40
  completedAt: now,
42
41
  updatedAt: now,
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Create Feature Use Case
3
+ *
4
+ * Creates a new feature entity with a git worktree for isolated development,
5
+ * initializes the spec directory with YAML templates, then spawns the
6
+ * feature agent to begin autonomous SDLC processing.
7
+ *
8
+ * Business Rules:
9
+ * - Metadata generation delegated to MetadataGenerator service
10
+ * - Slug uniqueness delegated to SlugResolver service
11
+ * - Full user input is preserved as-is in spec.yaml
12
+ * - A git worktree is created at ~/.shep/repos/HASH/wt/SLUG/
13
+ * - Spec YAML files are scaffolded at WORKTREE/specs/NNN-SLUG/
14
+ * - Feature agent is spawned with the spec dir path
15
+ * - specPath is persisted on the Feature record
16
+ * - Initial lifecycle is Requirements
17
+ */
18
+ import type { IFeatureRepository } from '../../../ports/output/repositories/feature-repository.interface.js';
19
+ import type { IWorktreeService } from '../../../ports/output/services/worktree-service.interface.js';
20
+ import type { IFeatureAgentProcessService } from '../../../ports/output/agents/feature-agent-process.interface.js';
21
+ import type { IAgentRunRepository } from '../../../ports/output/agents/agent-run-repository.interface.js';
22
+ import type { ISpecInitializerService } from '../../../ports/output/services/spec-initializer.interface.js';
23
+ import { MetadataGenerator } from './metadata-generator.js';
24
+ import { SlugResolver } from './slug-resolver.js';
25
+ import type { CreateFeatureInput, CreateFeatureResult } from './types.js';
26
+ export declare class CreateFeatureUseCase {
27
+ private readonly featureRepo;
28
+ private readonly worktreeService;
29
+ private readonly agentProcess;
30
+ private readonly runRepository;
31
+ private readonly specInitializer;
32
+ private readonly metadataGenerator;
33
+ private readonly slugResolver;
34
+ constructor(featureRepo: IFeatureRepository, worktreeService: IWorktreeService, agentProcess: IFeatureAgentProcessService, runRepository: IAgentRunRepository, specInitializer: ISpecInitializerService, metadataGenerator: MetadataGenerator, slugResolver: SlugResolver);
35
+ execute(input: CreateFeatureInput): Promise<CreateFeatureResult>;
36
+ }
37
+ //# sourceMappingURL=create-feature.use-case.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-feature.use-case.d.ts","sourceRoot":"","sources":["../../../../../../src/application/use-cases/features/create/create-feature.use-case.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oEAAoE,CAAC;AAC7G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8DAA8D,CAAC;AACrG,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,iEAAiE,CAAC;AACnH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gEAAgE,CAAC;AAC1G,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8DAA8D,CAAC;AAE5G,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE1E,qBACa,oBAAoB;IAG7B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAE5B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAEhC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAE7B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAE9B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAEhC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAElC,OAAO,CAAC,QAAQ,CAAC,YAAY;gBAZZ,WAAW,EAAE,kBAAkB,EAE/B,eAAe,EAAE,gBAAgB,EAEjC,YAAY,EAAE,2BAA2B,EAEzC,aAAa,EAAE,mBAAmB,EAElC,eAAe,EAAE,uBAAuB,EAExC,iBAAiB,EAAE,iBAAiB,EAEpC,YAAY,EAAE,YAAY;IAGvC,OAAO,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;CAyEvE"}
@@ -6,10 +6,9 @@
6
6
  * feature agent to begin autonomous SDLC processing.
7
7
  *
8
8
  * Business Rules:
9
- * - AI generates slug/name/description from user's free-text input
10
- * - Falls back to regex-based slug if AI fails
9
+ * - Metadata generation delegated to MetadataGenerator service
10
+ * - Slug uniqueness delegated to SlugResolver service
11
11
  * - Full user input is preserved as-is in spec.yaml
12
- * - Slug must be unique within the repository
13
12
  * - A git worktree is created at ~/.shep/repos/HASH/wt/SLUG/
14
13
  * - Spec YAML files are scaffolded at WORKTREE/specs/NNN-SLUG/
15
14
  * - Feature agent is spawned with the spec dir path
@@ -30,34 +29,32 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
30
29
  };
31
30
  import { injectable, inject } from 'tsyringe';
32
31
  import { randomUUID } from 'node:crypto';
33
- import { SdlcLifecycle } from '../../../domain/generated/output.js';
34
- import { AgentRunStatus } from '../../../domain/generated/output.js';
35
- import { getSettings } from '../../../infrastructure/services/settings.service.js';
36
- /** Maximum characters of user input sent to the AI for metadata generation. */
37
- const MAX_INPUT_FOR_AI = 500;
32
+ import { SdlcLifecycle, AgentRunStatus } from '../../../../domain/generated/output.js';
33
+ import { getSettings } from '../../../../infrastructure/services/settings.service.js';
34
+ import { MetadataGenerator } from './metadata-generator.js';
35
+ import { SlugResolver } from './slug-resolver.js';
38
36
  let CreateFeatureUseCase = class CreateFeatureUseCase {
39
37
  featureRepo;
40
38
  worktreeService;
41
39
  agentProcess;
42
40
  runRepository;
43
41
  specInitializer;
44
- executorProvider;
45
- constructor(featureRepo, worktreeService, agentProcess, runRepository, specInitializer, executorProvider) {
42
+ metadataGenerator;
43
+ slugResolver;
44
+ constructor(featureRepo, worktreeService, agentProcess, runRepository, specInitializer, metadataGenerator, slugResolver) {
46
45
  this.featureRepo = featureRepo;
47
46
  this.worktreeService = worktreeService;
48
47
  this.agentProcess = agentProcess;
49
48
  this.runRepository = runRepository;
50
49
  this.specInitializer = specInitializer;
51
- this.executorProvider = executorProvider;
50
+ this.metadataGenerator = metadataGenerator;
51
+ this.slugResolver = slugResolver;
52
52
  }
53
53
  async execute(input) {
54
- const metadata = await this.generateMetadata(input.userInput);
55
- const slug = metadata.slug;
56
- const branch = `feat/${slug}`;
57
- const existing = await this.featureRepo.findBySlug(slug, input.repositoryPath);
58
- if (existing) {
59
- throw new Error(`Feature with slug "${slug}" already exists in this repository`);
60
- }
54
+ const metadata = await this.metadataGenerator.generateMetadata(input.userInput);
55
+ const originalSlug = metadata.slug;
56
+ // Find a unique slug — the branch may exist from a previous (deleted) feature
57
+ const { slug, branch, warning } = await this.slugResolver.resolveUniqueSlug(originalSlug, input.repositoryPath);
61
58
  // Determine next feature number for this repo
62
59
  const existingFeatures = await this.featureRepo.list({
63
60
  repositoryPath: input.repositoryPath,
@@ -97,63 +94,16 @@ let CreateFeatureUseCase = class CreateFeatureUseCase {
97
94
  threadId: randomUUID(),
98
95
  featureId: feature.id,
99
96
  repositoryPath: input.repositoryPath,
100
- ...(input.approvalMode ? { approvalMode: input.approvalMode } : {}),
97
+ ...(input.approvalGates ? { approvalGates: input.approvalGates } : {}),
101
98
  createdAt: now.toISOString(),
102
99
  updatedAt: now.toISOString(),
103
100
  };
104
101
  await this.runRepository.create(agentRun);
105
102
  this.agentProcess.spawn(feature.id, runId, input.repositoryPath, specDir, worktreePath, {
106
- ...(input.approvalMode ? { approvalMode: input.approvalMode } : {}),
103
+ ...(input.approvalGates ? { approvalGates: input.approvalGates } : {}),
107
104
  threadId: agentRun.threadId,
108
105
  });
109
- return feature;
110
- }
111
- async generateMetadata(userInput) {
112
- try {
113
- const executor = this.executorProvider.getExecutor();
114
- const truncated = userInput.length > MAX_INPUT_FOR_AI
115
- ? `${userInput.slice(0, MAX_INPUT_FOR_AI)}...`
116
- : userInput;
117
- const prompt = `Generate feature metadata from this user request:
118
- "${truncated}"
119
-
120
- Return ONLY a JSON object with these fields:
121
- - slug: kebab-case identifier, 2-4 words max (e.g., "github-oauth-login")
122
- - name: short human-readable title (e.g., "GitHub OAuth Login")
123
- - description: refined 1-2 sentence description
124
-
125
- JSON only, no markdown fences.`;
126
- const result = await executor.execute(prompt, {
127
- maxTurns: 1,
128
- allowedTools: [],
129
- silent: true,
130
- });
131
- const parsed = JSON.parse(result.result);
132
- if (!parsed.slug || !parsed.name || !parsed.description) {
133
- throw new Error('Missing required fields in AI response');
134
- }
135
- return {
136
- slug: this.toSlug(parsed.slug),
137
- name: parsed.name,
138
- description: parsed.description,
139
- };
140
- }
141
- catch {
142
- // Fallback to regex-based slug generation
143
- return {
144
- slug: this.toSlug(userInput),
145
- name: userInput,
146
- description: userInput,
147
- };
148
- }
149
- }
150
- toSlug(text) {
151
- return text
152
- .toLowerCase()
153
- .replace(/[^a-z0-9\s-]/g, '')
154
- .replace(/\s+/g, '-')
155
- .replace(/-+/g, '-')
156
- .replace(/^-|-$/g, '');
106
+ return { feature, warning };
157
107
  }
158
108
  };
159
109
  CreateFeatureUseCase = __decorate([
@@ -163,7 +113,9 @@ CreateFeatureUseCase = __decorate([
163
113
  __param(2, inject('IFeatureAgentProcessService')),
164
114
  __param(3, inject('IAgentRunRepository')),
165
115
  __param(4, inject('ISpecInitializerService')),
166
- __param(5, inject('IAgentExecutorProvider')),
167
- __metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object])
116
+ __param(5, inject(MetadataGenerator)),
117
+ __param(6, inject(SlugResolver)),
118
+ __metadata("design:paramtypes", [Object, Object, Object, Object, Object, MetadataGenerator,
119
+ SlugResolver])
168
120
  ], CreateFeatureUseCase);
169
121
  export { CreateFeatureUseCase };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * MetadataGenerator
3
+ *
4
+ * Generates feature metadata (slug, name, description) from user input
5
+ * via AI call through IAgentExecutorProvider.
6
+ * Errors are propagated to the caller.
7
+ */
8
+ import type { IAgentExecutorProvider } from '../../../ports/output/agents/agent-executor-provider.interface.js';
9
+ import type { FeatureMetadata } from './types.js';
10
+ export declare class MetadataGenerator {
11
+ private readonly executorProvider;
12
+ constructor(executorProvider: IAgentExecutorProvider);
13
+ /**
14
+ * Generate feature metadata from user input via AI.
15
+ * Errors are propagated to the caller.
16
+ */
17
+ generateMetadata(userInput: string): Promise<FeatureMetadata>;
18
+ /**
19
+ * Strip markdown code fence wrappers (```lang ... ```) if present at start/end.
20
+ */
21
+ private stripCodeFence;
22
+ /**
23
+ * Convert text to kebab-case slug with 50-char limit.
24
+ * Removes special characters, collapses spaces, limits length.
25
+ */
26
+ private toSlug;
27
+ }
28
+ //# sourceMappingURL=metadata-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata-generator.d.ts","sourceRoot":"","sources":["../../../../../../src/application/use-cases/features/create/metadata-generator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mEAAmE,CAAC;AAChH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKlD,qBACa,iBAAiB;IAG1B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBAAhB,gBAAgB,EAAE,sBAAsB;IAG3D;;;OAGG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAyCnE;;OAEG;IACH,OAAO,CAAC,cAAc;IAgBtB;;;OAGG;IACH,OAAO,CAAC,MAAM;CAgBf"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * MetadataGenerator
3
+ *
4
+ * Generates feature metadata (slug, name, description) from user input
5
+ * via AI call through IAgentExecutorProvider.
6
+ * Errors are propagated to the caller.
7
+ */
8
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
9
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
10
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
11
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
12
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
13
+ };
14
+ var __metadata = (this && this.__metadata) || function (k, v) {
15
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
16
+ };
17
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
18
+ return function (target, key) { decorator(target, key, paramIndex); }
19
+ };
20
+ import { injectable, inject } from 'tsyringe';
21
+ /** Maximum characters of user input sent to the AI for metadata generation. */
22
+ const MAX_INPUT_FOR_AI = 500;
23
+ let MetadataGenerator = class MetadataGenerator {
24
+ executorProvider;
25
+ constructor(executorProvider) {
26
+ this.executorProvider = executorProvider;
27
+ }
28
+ /**
29
+ * Generate feature metadata from user input via AI.
30
+ * Errors are propagated to the caller.
31
+ */
32
+ async generateMetadata(userInput) {
33
+ const executor = this.executorProvider.getExecutor();
34
+ const truncated = userInput.length > MAX_INPUT_FOR_AI
35
+ ? `${userInput.slice(0, MAX_INPUT_FOR_AI)}...`
36
+ : userInput;
37
+ const prompt = `Analyze this user request and extract the CORE feature intent. Condense to essential concepts.
38
+
39
+ User request:
40
+ "${truncated}"
41
+
42
+ IMPORTANT: Don't use the request as-is. Extract the key feature being requested and improve the wording.
43
+
44
+ Return ONLY a JSON object with these fields:
45
+ - slug: kebab-case identifier, 2-4 words MAX. Extract the core feature name (e.g., "github-oauth-login" from "add github oauth")
46
+ - name: polished, professional title (improve upon user's wording)
47
+ - description: refined 1-2 sentence description that captures the feature essence (not the request verbatim)
48
+
49
+ JSON only, no markdown fences.`;
50
+ const result = await executor.execute(prompt, {
51
+ maxTurns: 1,
52
+ allowedTools: [],
53
+ silent: true,
54
+ });
55
+ const cleaned = this.stripCodeFence(result.result);
56
+ const parsed = JSON.parse(cleaned);
57
+ if (!parsed.slug || !parsed.name || !parsed.description) {
58
+ throw new Error('Missing required fields in AI response');
59
+ }
60
+ return {
61
+ slug: this.toSlug(parsed.slug),
62
+ name: parsed.name,
63
+ description: parsed.description,
64
+ };
65
+ }
66
+ /**
67
+ * Strip markdown code fence wrappers (```lang ... ```) if present at start/end.
68
+ */
69
+ stripCodeFence(text) {
70
+ const trimmed = text.trim();
71
+ if (!trimmed.startsWith('```')) {
72
+ return trimmed;
73
+ }
74
+ // Find end of first line (```json or ```anything)
75
+ const firstNewline = trimmed.indexOf('\n');
76
+ if (firstNewline === -1) {
77
+ return trimmed;
78
+ }
79
+ if (!trimmed.endsWith('```')) {
80
+ return trimmed;
81
+ }
82
+ return trimmed.slice(firstNewline + 1, trimmed.length - 3).trim();
83
+ }
84
+ /**
85
+ * Convert text to kebab-case slug with 50-char limit.
86
+ * Removes special characters, collapses spaces, limits length.
87
+ */
88
+ toSlug(text) {
89
+ const slug = text
90
+ .toLowerCase()
91
+ .replace(/[^a-z0-9\s-]/g, '')
92
+ .replace(/\s+/g, '-')
93
+ .replace(/-+/g, '-')
94
+ .replace(/^-|-$/g, '');
95
+ // Limit to ~50 chars, cutting at a word boundary
96
+ if (slug.length <= 50) {
97
+ return slug;
98
+ }
99
+ const truncated = slug.slice(0, 50);
100
+ const lastDash = truncated.lastIndexOf('-');
101
+ return lastDash > 10 ? truncated.slice(0, lastDash) : truncated;
102
+ }
103
+ };
104
+ MetadataGenerator = __decorate([
105
+ injectable(),
106
+ __param(0, inject('IAgentExecutorProvider')),
107
+ __metadata("design:paramtypes", [Object])
108
+ ], MetadataGenerator);
109
+ export { MetadataGenerator };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * SlugResolver
3
+ *
4
+ * Resolves unique slugs by checking against database and git branches.
5
+ * Ensures no slug collision by appending numeric suffixes (-2, -3, etc.)
6
+ */
7
+ import type { IFeatureRepository } from '../../../ports/output/repositories/feature-repository.interface.js';
8
+ import type { IWorktreeService } from '../../../ports/output/services/worktree-service.interface.js';
9
+ export interface SlugResolutionResult {
10
+ slug: string;
11
+ branch: string;
12
+ warning?: string;
13
+ }
14
+ export declare class SlugResolver {
15
+ private readonly featureRepo;
16
+ private readonly worktreeService;
17
+ constructor(featureRepo: IFeatureRepository, worktreeService: IWorktreeService);
18
+ /**
19
+ * Find a unique slug by checking both the feature DB and git branches.
20
+ * If the original slug conflicts, appends -2, -3, etc. until a free one is found.
21
+ */
22
+ resolveUniqueSlug(originalSlug: string, repositoryPath: string): Promise<SlugResolutionResult>;
23
+ }
24
+ //# sourceMappingURL=slug-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug-resolver.d.ts","sourceRoot":"","sources":["../../../../../../src/application/use-cases/features/create/slug-resolver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oEAAoE,CAAC;AAC7G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8DAA8D,CAAC;AAKrG,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBACa,YAAY;IAGrB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAE5B,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAFf,WAAW,EAAE,kBAAkB,EAE/B,eAAe,EAAE,gBAAgB;IAGpD;;;OAGG;IACG,iBAAiB,CACrB,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,oBAAoB,CAAC;CA+BjC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * SlugResolver
3
+ *
4
+ * Resolves unique slugs by checking against database and git branches.
5
+ * Ensures no slug collision by appending numeric suffixes (-2, -3, etc.)
6
+ */
7
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
8
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
9
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
10
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
11
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
12
+ };
13
+ var __metadata = (this && this.__metadata) || function (k, v) {
14
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
15
+ };
16
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
17
+ return function (target, key) { decorator(target, key, paramIndex); }
18
+ };
19
+ import { injectable, inject } from 'tsyringe';
20
+ /** Maximum number of suffix attempts before giving up */
21
+ const MAX_SUFFIX = 10;
22
+ let SlugResolver = class SlugResolver {
23
+ featureRepo;
24
+ worktreeService;
25
+ constructor(featureRepo, worktreeService) {
26
+ this.featureRepo = featureRepo;
27
+ this.worktreeService = worktreeService;
28
+ }
29
+ /**
30
+ * Find a unique slug by checking both the feature DB and git branches.
31
+ * If the original slug conflicts, appends -2, -3, etc. until a free one is found.
32
+ */
33
+ async resolveUniqueSlug(originalSlug, repositoryPath) {
34
+ for (let suffix = 0; suffix <= MAX_SUFFIX; suffix++) {
35
+ const slug = suffix === 0 ? originalSlug : `${originalSlug}-${suffix + 1}`;
36
+ const branch = `feat/${slug}`;
37
+ // Check if a feature with this slug already exists in the DB
38
+ const existing = await this.featureRepo.findBySlug(slug, repositoryPath);
39
+ if (existing) {
40
+ if (suffix === 0)
41
+ continue; // try suffixed version
42
+ continue;
43
+ }
44
+ // Check if the git branch already exists (leftover from deleted feature)
45
+ const branchInUse = await this.worktreeService.exists(repositoryPath, branch);
46
+ if (branchInUse)
47
+ continue;
48
+ if (suffix > 0) {
49
+ return {
50
+ slug,
51
+ branch,
52
+ warning: `Branch "feat/${originalSlug}" already exists, using "${branch}" instead`,
53
+ };
54
+ }
55
+ return { slug, branch };
56
+ }
57
+ throw new Error(`Could not find a unique slug for "${originalSlug}" after ${MAX_SUFFIX} attempts`);
58
+ }
59
+ };
60
+ SlugResolver = __decorate([
61
+ injectable(),
62
+ __param(0, inject('IFeatureRepository')),
63
+ __param(1, inject('IWorktreeService')),
64
+ __metadata("design:paramtypes", [Object, Object])
65
+ ], SlugResolver);
66
+ export { SlugResolver };
@@ -0,0 +1,16 @@
1
+ import type { ApprovalGates, Feature } from '../../../../domain/generated/output.js';
2
+ export interface CreateFeatureInput {
3
+ userInput: string;
4
+ repositoryPath: string;
5
+ approvalGates?: ApprovalGates;
6
+ }
7
+ export interface CreateFeatureResult {
8
+ feature: Feature;
9
+ warning?: string;
10
+ }
11
+ export interface FeatureMetadata {
12
+ slug: string;
13
+ name: string;
14
+ description: string;
15
+ }
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/application/use-cases/features/create/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,wCAAwC,CAAC;AAErF,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB"}
@@ -72,7 +72,7 @@ let ResumeFeatureUseCase = class ResumeFeatureUseCase {
72
72
  threadId: lastRun.threadId, // Same thread for checkpoint continuity
73
73
  featureId: feature.id,
74
74
  repositoryPath: feature.repositoryPath,
75
- approvalMode: lastRun.approvalMode,
75
+ approvalGates: lastRun.approvalGates,
76
76
  createdAt: now,
77
77
  updatedAt: now,
78
78
  };
@@ -87,7 +87,7 @@ let ResumeFeatureUseCase = class ResumeFeatureUseCase {
87
87
  const worktreePath = this.worktreeService.getWorktreePath(feature.repositoryPath, feature.branch);
88
88
  this.processService.spawn(feature.id, newRunId, feature.repositoryPath, worktreePath, worktreePath, {
89
89
  resume: true,
90
- approvalMode: lastRun.approvalMode,
90
+ approvalGates: lastRun.approvalGates,
91
91
  threadId: lastRun.threadId,
92
92
  resumeFromInterrupt: lastRun.status === AgentRunStatus.waitingApproval,
93
93
  });