@shepai/cli 1.19.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/eslint.config.mjs +3 -0
- package/dist/src/application/ports/output/agents/feature-agent-process.interface.d.ts +2 -1
- package/dist/src/application/ports/output/agents/feature-agent-process.interface.d.ts.map +1 -1
- package/dist/src/application/ports/output/agents/index.d.ts +1 -0
- package/dist/src/application/ports/output/agents/index.d.ts.map +1 -1
- package/dist/src/application/ports/output/agents/phase-timing-repository.interface.d.ts +44 -0
- package/dist/src/application/ports/output/agents/phase-timing-repository.interface.d.ts.map +1 -0
- package/dist/src/application/ports/output/agents/phase-timing-repository.interface.js +11 -0
- package/dist/src/application/use-cases/agents/approve-agent-run.use-case.d.ts.map +1 -1
- package/dist/src/application/use-cases/agents/approve-agent-run.use-case.js +6 -2
- package/dist/src/application/use-cases/agents/reject-agent-run.use-case.d.ts.map +1 -1
- package/dist/src/application/use-cases/agents/reject-agent-run.use-case.js +0 -1
- package/dist/src/application/use-cases/features/create-feature.use-case.d.ts +12 -2
- package/dist/src/application/use-cases/features/create-feature.use-case.d.ts.map +1 -1
- package/dist/src/application/use-cases/features/create-feature.use-case.js +45 -10
- package/dist/src/application/use-cases/features/resume-feature.use-case.js +2 -2
- package/dist/src/domain/generated/output.d.ts +40 -6
- package/dist/src/domain/generated/output.d.ts.map +1 -1
- package/dist/src/infrastructure/di/container.d.ts.map +1 -1
- package/dist/src/infrastructure/di/container.js +7 -0
- package/dist/src/infrastructure/persistence/sqlite/mappers/agent-run.mapper.d.ts +1 -2
- package/dist/src/infrastructure/persistence/sqlite/mappers/agent-run.mapper.d.ts.map +1 -1
- package/dist/src/infrastructure/persistence/sqlite/mappers/agent-run.mapper.js +4 -4
- package/dist/src/infrastructure/persistence/sqlite/migrations.d.ts.map +1 -1
- package/dist/src/infrastructure/persistence/sqlite/migrations.js +31 -0
- package/dist/src/infrastructure/repositories/agent-run.repository.d.ts.map +1 -1
- package/dist/src/infrastructure/repositories/agent-run.repository.js +5 -9
- package/dist/src/infrastructure/repositories/sqlite-phase-timing.repository.d.ts +21 -0
- package/dist/src/infrastructure/repositories/sqlite-phase-timing.repository.d.ts.map +1 -0
- package/dist/src/infrastructure/repositories/sqlite-phase-timing.repository.js +100 -0
- package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts +305 -13
- package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts.map +1 -1
- package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-graph.js +124 -7
- package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.d.ts +2 -1
- package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.d.ts.map +1 -1
- package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.js +2 -2
- package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts +2 -1
- package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
- package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +19 -7
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/implement.node.js +1 -1
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts +11 -2
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts.map +1 -1
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.js +46 -15
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/repair.node.d.ts +23 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/repair.node.d.ts.map +1 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/repair.node.js +77 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/plan.schema.d.ts +4 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/plan.schema.d.ts.map +1 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/plan.schema.js +54 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/research.schema.d.ts +3 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/research.schema.d.ts.map +1 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/research.schema.js +24 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/spec.schema.d.ts +4 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/spec.schema.d.ts.map +1 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/spec.schema.js +50 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/validation.d.ts +8 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/validation.d.ts.map +1 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/validation.js +36 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/validate.node.d.ts +12 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/validate.node.d.ts.map +1 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/validate.node.js +58 -0
- package/dist/src/infrastructure/services/agents/feature-agent/phase-timing-context.d.ts +30 -0
- package/dist/src/infrastructure/services/agents/feature-agent/phase-timing-context.d.ts.map +1 -0
- package/dist/src/infrastructure/services/agents/feature-agent/phase-timing-context.js +68 -0
- package/dist/src/infrastructure/services/agents/feature-agent/state.d.ts +5 -1
- package/dist/src/infrastructure/services/agents/feature-agent/state.d.ts.map +1 -1
- package/dist/src/infrastructure/services/agents/feature-agent/state.js +14 -1
- package/dist/src/presentation/cli/commands/feat/approve.command.d.ts +12 -0
- package/dist/src/presentation/cli/commands/feat/approve.command.d.ts.map +1 -0
- package/dist/src/presentation/cli/commands/feat/approve.command.js +48 -0
- package/dist/src/presentation/cli/commands/feat/index.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/feat/index.js +7 -1
- package/dist/src/presentation/cli/commands/feat/ls.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/feat/ls.command.js +13 -5
- package/dist/src/presentation/cli/commands/feat/new.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/feat/new.command.js +34 -15
- package/dist/src/presentation/cli/commands/feat/reject.command.d.ts +12 -0
- package/dist/src/presentation/cli/commands/feat/reject.command.d.ts.map +1 -0
- package/dist/src/presentation/cli/commands/feat/reject.command.js +50 -0
- package/dist/src/presentation/cli/commands/feat/resolve-waiting-feature.d.ts +30 -0
- package/dist/src/presentation/cli/commands/feat/resolve-waiting-feature.d.ts.map +1 -0
- package/dist/src/presentation/cli/commands/feat/resolve-waiting-feature.js +57 -0
- package/dist/src/presentation/cli/commands/feat/review.command.d.ts +12 -0
- package/dist/src/presentation/cli/commands/feat/review.command.d.ts.map +1 -0
- package/dist/src/presentation/cli/commands/feat/review.command.js +49 -0
- package/dist/src/presentation/cli/commands/feat/show.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/feat/show.command.js +51 -6
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/cache/.previewinfo +1 -1
- package/web/.next/cache/.rscinfo +1 -1
- package/web/.next/cache/config.json +3 -3
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +1 -1
- package/web/.next/required-server-files.json +1 -1
- package/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.html +2 -2
- package/web/.next/server/app/_not-found.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/index.html +2 -2
- package/web/.next/server/app/index.rsc +1 -1
- package/web/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/version.html +2 -2
- package/web/.next/server/app/version.rsc +1 -1
- package/web/.next/server/app/version.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/version/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/version.segment.rsc +1 -1
- package/web/.next/server/chunks/ssr/_19be0743._.js +1 -1
- package/web/.next/server/pages/404.html +2 -2
- package/web/.next/server/pages/500.html +2 -2
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- /package/web/.next/static/{nw4IT0rr_mE5Tx22X9OgO → u-tyZUMnsIne6K_0rPmPO}/_buildManifest.js +0 -0
- /package/web/.next/static/{nw4IT0rr_mE5Tx22X9OgO → u-tyZUMnsIne6K_0rPmPO}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{nw4IT0rr_mE5Tx22X9OgO → u-tyZUMnsIne6K_0rPmPO}/_ssgManifest.js +0 -0
package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/research.schema.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { requireString, requireArrayOfShape, requireNonEmptyArray, } from './validation.js';
|
|
2
|
+
export function validateResearch(data) {
|
|
3
|
+
const errors = [];
|
|
4
|
+
if (!data || typeof data !== 'object') {
|
|
5
|
+
errors.push('YAML parsed to null or non-object');
|
|
6
|
+
return { valid: false, errors };
|
|
7
|
+
}
|
|
8
|
+
const d = data;
|
|
9
|
+
requireString(d, 'name', errors);
|
|
10
|
+
requireString(d, 'summary', errors);
|
|
11
|
+
requireString(d, 'content', errors);
|
|
12
|
+
requireArrayOfShape(d, 'decisions', errors, (item, idx, errs) => {
|
|
13
|
+
const decision = item;
|
|
14
|
+
if (!decision || typeof decision !== 'object') {
|
|
15
|
+
errs.push(`decisions[${idx}] must be an object`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
requireString(decision, 'title', errs, `decisions[${idx}].`);
|
|
19
|
+
requireString(decision, 'chosen', errs, `decisions[${idx}].`);
|
|
20
|
+
requireNonEmptyArray(decision, 'rejected', errs, `decisions[${idx}].`);
|
|
21
|
+
requireString(decision, 'rationale', errs, `decisions[${idx}].`);
|
|
22
|
+
});
|
|
23
|
+
return { valid: errors.length === 0, errors };
|
|
24
|
+
}
|
package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/spec.schema.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec.schema.d.ts","sourceRoot":"","sources":["../../../../../../../../src/infrastructure/services/agents/feature-agent/nodes/schemas/spec.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AA4B7F,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,gBAAgB,CAInE;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,GAAG,gBAAgB,CAqBxE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { requireString, requireNonEmptyArray } from './validation.js';
|
|
2
|
+
const VALID_SIZE_ESTIMATES = ['S', 'M', 'L', 'XL'];
|
|
3
|
+
function validateBaseSpec(data, errors) {
|
|
4
|
+
if (!data || typeof data !== 'object') {
|
|
5
|
+
errors.push('YAML parsed to null or non-object');
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
const d = data;
|
|
9
|
+
requireString(d, 'name', errors);
|
|
10
|
+
requireString(d, 'oneLiner', errors);
|
|
11
|
+
requireString(d, 'summary', errors);
|
|
12
|
+
requireString(d, 'phase', errors);
|
|
13
|
+
requireString(d, 'content', errors);
|
|
14
|
+
requireNonEmptyArray(d, 'technologies', errors);
|
|
15
|
+
if (typeof d.sizeEstimate === 'string' && !VALID_SIZE_ESTIMATES.includes(d.sizeEstimate)) {
|
|
16
|
+
errors.push(`Field 'sizeEstimate' must be one of ${VALID_SIZE_ESTIMATES.join(', ')}, got '${d.sizeEstimate}'`);
|
|
17
|
+
}
|
|
18
|
+
else if (typeof d.sizeEstimate !== 'string') {
|
|
19
|
+
requireString(d, 'sizeEstimate', errors);
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
export function validateSpecAnalyze(data) {
|
|
24
|
+
const errors = [];
|
|
25
|
+
validateBaseSpec(data, errors);
|
|
26
|
+
return { valid: errors.length === 0, errors };
|
|
27
|
+
}
|
|
28
|
+
export function validateSpecRequirements(data) {
|
|
29
|
+
const errors = [];
|
|
30
|
+
if (!validateBaseSpec(data, errors))
|
|
31
|
+
return { valid: false, errors };
|
|
32
|
+
const d = data;
|
|
33
|
+
if (!('openQuestions' in d)) {
|
|
34
|
+
errors.push("Missing required field 'openQuestions'");
|
|
35
|
+
}
|
|
36
|
+
else if (Array.isArray(d.openQuestions)) {
|
|
37
|
+
for (let i = 0; i < d.openQuestions.length; i++) {
|
|
38
|
+
const q = d.openQuestions[i];
|
|
39
|
+
if (!q || typeof q !== 'object') {
|
|
40
|
+
errors.push(`openQuestions[${i}] must be an object`);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
requireString(q, 'question', errors, `openQuestions[${i}].`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
errors.push("Field 'openQuestions' must be an array");
|
|
48
|
+
}
|
|
49
|
+
return { valid: errors.length === 0, errors };
|
|
50
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ValidationResult {
|
|
2
|
+
valid: boolean;
|
|
3
|
+
errors: string[];
|
|
4
|
+
}
|
|
5
|
+
export declare function requireString(data: Record<string, unknown>, field: string, errors: string[], prefix?: string): void;
|
|
6
|
+
export declare function requireNonEmptyArray(data: Record<string, unknown>, field: string, errors: string[], prefix?: string): unknown[] | null;
|
|
7
|
+
export declare function requireArrayOfShape(data: Record<string, unknown>, field: string, errors: string[], itemChecker: (item: unknown, index: number, errors: string[]) => void, prefix?: string): void;
|
|
8
|
+
//# sourceMappingURL=validation.d.ts.map
|
package/dist/src/infrastructure/services/agents/feature-agent/nodes/schemas/validation.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../../../../../../src/infrastructure/services/agents/feature-agent/nodes/schemas/validation.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EAAE,EAChB,MAAM,SAAK,GACV,IAAI,CASN;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EAAE,EAChB,MAAM,SAAK,GACV,OAAO,EAAE,GAAG,IAAI,CAelB;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EAAE,EAChB,WAAW,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,EACrE,MAAM,SAAK,GACV,IAAI,CAMN"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function requireString(data, field, errors, prefix = '') {
|
|
2
|
+
const val = data[field];
|
|
3
|
+
if (val === undefined || val === null) {
|
|
4
|
+
errors.push(`Missing required string field '${prefix}${field}'`);
|
|
5
|
+
}
|
|
6
|
+
else if (typeof val !== 'string') {
|
|
7
|
+
errors.push(`Field '${prefix}${field}' must be a string, got ${typeof val}`);
|
|
8
|
+
}
|
|
9
|
+
else if (val.trim() === '') {
|
|
10
|
+
errors.push(`Field '${prefix}${field}' must not be empty`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function requireNonEmptyArray(data, field, errors, prefix = '') {
|
|
14
|
+
const val = data[field];
|
|
15
|
+
if (val === undefined || val === null) {
|
|
16
|
+
errors.push(`Missing required array field '${prefix}${field}'`);
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
if (!Array.isArray(val)) {
|
|
20
|
+
errors.push(`Field '${prefix}${field}' must be an array, got ${typeof val}`);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (val.length === 0) {
|
|
24
|
+
errors.push(`Field '${prefix}${field}' must not be empty`);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return val;
|
|
28
|
+
}
|
|
29
|
+
export function requireArrayOfShape(data, field, errors, itemChecker, prefix = '') {
|
|
30
|
+
const arr = requireNonEmptyArray(data, field, errors, prefix);
|
|
31
|
+
if (!arr)
|
|
32
|
+
return;
|
|
33
|
+
for (let i = 0; i < arr.length; i++) {
|
|
34
|
+
itemChecker(arr[i], i, errors);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FeatureAgentState } from '../state.js';
|
|
2
|
+
import type { ValidationResult } from './schemas/validation.js';
|
|
3
|
+
type SchemaValidator = (data: unknown) => ValidationResult;
|
|
4
|
+
/**
|
|
5
|
+
* Factory that creates a validate node for a specific YAML file.
|
|
6
|
+
*
|
|
7
|
+
* On success: resets validationRetries to 0, clears errors.
|
|
8
|
+
* On failure: increments validationRetries, sets errors for repair node.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createValidateNode(filename: string, schema: SchemaValidator): (state: FeatureAgentState) => Promise<Partial<FeatureAgentState>>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=validate.node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.node.d.ts","sourceRoot":"","sources":["../../../../../../../src/infrastructure/services/agents/feature-agent/nodes/validate.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,KAAK,eAAe,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,gBAAgB,CAAC;AAE3D;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,eAAe,GACtB,CAAC,KAAK,EAAE,iBAAiB,KAAK,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAsDnE"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { readSpecFile, safeYamlLoad, createNodeLogger } from './node-helpers.js';
|
|
2
|
+
/**
|
|
3
|
+
* Factory that creates a validate node for a specific YAML file.
|
|
4
|
+
*
|
|
5
|
+
* On success: resets validationRetries to 0, clears errors.
|
|
6
|
+
* On failure: increments validationRetries, sets errors for repair node.
|
|
7
|
+
*/
|
|
8
|
+
export function createValidateNode(filename, schema) {
|
|
9
|
+
const log = createNodeLogger(`validate:${filename}`);
|
|
10
|
+
return async (state) => {
|
|
11
|
+
log.info(`Validating ${filename}`);
|
|
12
|
+
const content = readSpecFile(state.specDir, filename);
|
|
13
|
+
if (!content) {
|
|
14
|
+
const errors = [`File '${filename}' not found or empty in ${state.specDir}`];
|
|
15
|
+
log.error(errors[0]);
|
|
16
|
+
return {
|
|
17
|
+
lastValidationTarget: filename,
|
|
18
|
+
lastValidationErrors: errors,
|
|
19
|
+
validationRetries: state.validationRetries + 1,
|
|
20
|
+
messages: [`[validate:${filename}] FAIL: ${errors[0]}`],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// Try to parse
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = safeYamlLoad(content);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
30
|
+
const errors = [`YAML parse error in ${filename}: ${msg}`];
|
|
31
|
+
log.error(errors[0]);
|
|
32
|
+
return {
|
|
33
|
+
lastValidationTarget: filename,
|
|
34
|
+
lastValidationErrors: errors,
|
|
35
|
+
validationRetries: state.validationRetries + 1,
|
|
36
|
+
messages: [`[validate:${filename}] FAIL: parse error`],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Run schema validation
|
|
40
|
+
const result = schema(parsed);
|
|
41
|
+
if (result.valid) {
|
|
42
|
+
log.info('Validation passed');
|
|
43
|
+
return {
|
|
44
|
+
lastValidationTarget: filename,
|
|
45
|
+
lastValidationErrors: [],
|
|
46
|
+
validationRetries: 0,
|
|
47
|
+
messages: [`[validate:${filename}] PASS`],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
log.error(`Validation failed: ${result.errors.join('; ')}`);
|
|
51
|
+
return {
|
|
52
|
+
lastValidationTarget: filename,
|
|
53
|
+
lastValidationErrors: result.errors,
|
|
54
|
+
validationRetries: state.validationRetries + 1,
|
|
55
|
+
messages: [`[validate:${filename}] FAIL: ${result.errors.length} error(s)`],
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -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
|
-
|
|
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":"
|
|
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
|
-
|
|
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;
|
|
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;
|
|
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
|
|
37
|
-
|
|
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
|
-
|
|
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;
|
|
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
16
|
import { CreateFeatureUseCase } from '../../../../application/use-cases/features/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
|
-
//
|
|
33
|
-
let
|
|
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
|
-
|
|
37
|
+
approvalGates = { ...approvalGates, allowPrd: true };
|
|
38
38
|
if (options.allowPlan)
|
|
39
|
-
|
|
39
|
+
approvalGates = { allowPrd: true, allowPlan: true };
|
|
40
40
|
if (options.allowAll)
|
|
41
|
-
|
|
42
|
-
const
|
|
41
|
+
approvalGates = undefined; // no gates = fully autonomous
|
|
42
|
+
const result = await spinner('Thinking', () => useCase.execute({
|
|
43
43
|
userInput: description,
|
|
44
44
|
repositoryPath: repoPath,
|
|
45
|
-
|
|
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:')}
|
|
50
|
-
console.log(` ${colors.muted('Name:')}
|
|
51
|
-
console.log(` ${colors.muted('Branch:')}
|
|
52
|
-
console.log(` ${colors.muted('Status:')}
|
|
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:')}
|
|
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"}
|