@urielsh/prodify 0.1.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/.prodify/AGENTS.md +56 -0
- package/.prodify/README.md +10 -0
- package/.prodify/artifacts/01-understand.md +28 -0
- package/.prodify/artifacts/02-diagnose.md +26 -0
- package/.prodify/artifacts/03-architecture.md +30 -0
- package/.prodify/artifacts/04-plan.md +35 -0
- package/.prodify/artifacts/05-refactor.md +24 -0
- package/.prodify/artifacts/06-validate.md +17 -0
- package/.prodify/artifacts/README.md +6 -0
- package/.prodify/artifacts/architecture_spec.md +29 -0
- package/.prodify/artifacts/artifact-validation-design.md +276 -0
- package/.prodify/artifacts/cli-command-design.md +299 -0
- package/.prodify/artifacts/completed-steps-tracking.md +201 -0
- package/.prodify/artifacts/diagnostic_report.md +45 -0
- package/.prodify/artifacts/hardening-patch-summary.md +57 -0
- package/.prodify/artifacts/hardening-verification-report.md +48 -0
- package/.prodify/artifacts/implementation_summary.md +19 -0
- package/.prodify/artifacts/improvement-evaluation-spec.md +148 -0
- package/.prodify/artifacts/next-step-resolver-design.md +570 -0
- package/.prodify/artifacts/orientation_map.md +32 -0
- package/.prodify/artifacts/persona-removal-audit.md +106 -0
- package/.prodify/artifacts/planning-alignment-report.md +189 -0
- package/.prodify/artifacts/refactor-plan-template-hardening.md +83 -0
- package/.prodify/artifacts/refactor-validate-loop-design.md +231 -0
- package/.prodify/artifacts/refactor_plan.md +21 -0
- package/.prodify/artifacts/refactor_plan.strict-example.md +31 -0
- package/.prodify/artifacts/run-state-design.md +292 -0
- package/.prodify/artifacts/run-summary-spec.md +149 -0
- package/.prodify/artifacts/run_state.json +14 -0
- package/.prodify/artifacts/sample-repo-test-plan.md +129 -0
- package/.prodify/artifacts/status-output-spec.md +349 -0
- package/.prodify/artifacts/step-selection-design.md +251 -0
- package/.prodify/artifacts/task-dispatcher-design.md +266 -0
- package/.prodify/artifacts/task-header-audit.md +42 -0
- package/.prodify/artifacts/task-log-enhancement-spec.md +264 -0
- package/.prodify/artifacts/task-protocol-hardening-plan.md +68 -0
- package/.prodify/artifacts/task-self-validation-spec.md +171 -0
- package/.prodify/artifacts/task_log.json +160 -0
- package/.prodify/artifacts/template-usage-audit.md +20 -0
- package/.prodify/artifacts/validation_report.md +22 -0
- package/.prodify/contracts/README.md +3 -0
- package/.prodify/contracts/architecture.contract.json +42 -0
- package/.prodify/contracts/diagnose.contract.json +42 -0
- package/.prodify/contracts/plan.contract.json +42 -0
- package/.prodify/contracts/refactor.contract.json +45 -0
- package/.prodify/contracts/understand.contract.json +42 -0
- package/.prodify/contracts/validate.contract.json +52 -0
- package/.prodify/contracts-src/README.md +4 -0
- package/.prodify/contracts-src/architecture.contract.md +29 -0
- package/.prodify/contracts-src/diagnose.contract.md +29 -0
- package/.prodify/contracts-src/plan.contract.md +29 -0
- package/.prodify/contracts-src/refactor.contract.md +32 -0
- package/.prodify/contracts-src/understand.contract.md +29 -0
- package/.prodify/contracts-src/validate.contract.md +35 -0
- package/.prodify/metrics/README.md +4 -0
- package/.prodify/planning.md +13 -0
- package/.prodify/project.md +15 -0
- package/.prodify/rules/01-global-operating-rules.md +21 -0
- package/.prodify/rules/02-analysis-discipline.md +17 -0
- package/.prodify/rules/03-diagnosis-severity-rules.md +42 -0
- package/.prodify/rules/04-architecture-rules.md +27 -0
- package/.prodify/rules/05-planning-rules.md +23 -0
- package/.prodify/rules/06-refactor-rules.md +20 -0
- package/.prodify/rules/07-validation-rules.md +25 -0
- package/.prodify/rules/08-output-format-rules.md +20 -0
- package/.prodify/rules/09-template-usage-rules.md +11 -0
- package/.prodify/rules/10-artifact-flow-and-orchestration-rules.md +40 -0
- package/.prodify/rules/README.md +3 -0
- package/.prodify/rules/example-rule.md +4 -0
- package/.prodify/runtime-commands.md +57 -0
- package/.prodify/skills/README.md +8 -0
- package/.prodify/skills/domain/react-frontend.skill.json +34 -0
- package/.prodify/skills/domain/typescript-backend.skill.json +34 -0
- package/.prodify/skills/quality-policy/maintainability-review.skill.json +25 -0
- package/.prodify/skills/quality-policy/security-hardening.skill.json +32 -0
- package/.prodify/skills/quality-policy/test-hardening.skill.json +23 -0
- package/.prodify/skills/registry.json +16 -0
- package/.prodify/skills/stage-method/architecture-method.skill.json +22 -0
- package/.prodify/skills/stage-method/codebase-scanning.skill.json +22 -0
- package/.prodify/skills/stage-method/diagnosis-method.skill.json +22 -0
- package/.prodify/skills/stage-method/planning-method.skill.json +22 -0
- package/.prodify/skills/stage-method/refactoring-method.skill.json +22 -0
- package/.prodify/skills/stage-method/validation-method.skill.json +22 -0
- package/.prodify/state.json +30 -0
- package/.prodify/tasks/01-understand.md +83 -0
- package/.prodify/tasks/02-diagnose.md +67 -0
- package/.prodify/tasks/03-architecture.md +70 -0
- package/.prodify/tasks/04-plan.md +71 -0
- package/.prodify/tasks/05-refactor.md +61 -0
- package/.prodify/tasks/06-validate.md +69 -0
- package/.prodify/tasks/README.md +3 -0
- package/.prodify/tasks/example-task.md +13 -0
- package/.prodify/templates/01-understand.template.md +18 -0
- package/.prodify/templates/02-diagnose.template.md +18 -0
- package/.prodify/templates/03-architecture.template.md +18 -0
- package/.prodify/templates/04-plan.template.md +22 -0
- package/.prodify/templates/05-refactor.template.md +19 -0
- package/.prodify/templates/06-validate.template.md +16 -0
- package/.prodify/templates/README.md +3 -0
- package/.prodify/templates/architecture_spec.template.md +29 -0
- package/.prodify/templates/diagnostic_report.template.md +45 -0
- package/.prodify/templates/example.template.md +5 -0
- package/.prodify/templates/implementation_summary.template.md +19 -0
- package/.prodify/templates/orientation_map.template.md +32 -0
- package/.prodify/templates/refactor_plan.template.md +24 -0
- package/.prodify/templates/validation_report.template.md +22 -0
- package/.prodify/version.json +5 -0
- package/AGENTS.md +305 -0
- package/LICENSE +201 -0
- package/README.md +118 -0
- package/assets/presets/default/canonical/AGENTS.md +54 -0
- package/assets/presets/default/canonical/artifacts/README.md +6 -0
- package/assets/presets/default/canonical/contracts-src/README.md +4 -0
- package/assets/presets/default/canonical/contracts-src/architecture.contract.md +56 -0
- package/assets/presets/default/canonical/contracts-src/diagnose.contract.md +49 -0
- package/assets/presets/default/canonical/contracts-src/plan.contract.md +42 -0
- package/assets/presets/default/canonical/contracts-src/refactor.contract.md +54 -0
- package/assets/presets/default/canonical/contracts-src/understand.contract.md +56 -0
- package/assets/presets/default/canonical/contracts-src/validate.contract.md +64 -0
- package/assets/presets/default/canonical/metrics/README.md +4 -0
- package/assets/presets/default/canonical/planning.md +13 -0
- package/assets/presets/default/canonical/project.md +15 -0
- package/assets/presets/default/canonical/rules/README.md +3 -0
- package/assets/presets/default/canonical/rules/example-rule.md +4 -0
- package/assets/presets/default/canonical/runtime-commands.md +57 -0
- package/assets/presets/default/canonical/skills/README.md +8 -0
- package/assets/presets/default/canonical/skills/domain/react-frontend.skill.json +34 -0
- package/assets/presets/default/canonical/skills/domain/typescript-backend.skill.json +34 -0
- package/assets/presets/default/canonical/skills/quality-policy/maintainability-review.skill.json +25 -0
- package/assets/presets/default/canonical/skills/quality-policy/security-hardening.skill.json +32 -0
- package/assets/presets/default/canonical/skills/quality-policy/test-hardening.skill.json +23 -0
- package/assets/presets/default/canonical/skills/registry.json +16 -0
- package/assets/presets/default/canonical/skills/stage-method/architecture-method.skill.json +22 -0
- package/assets/presets/default/canonical/skills/stage-method/codebase-scanning.skill.json +22 -0
- package/assets/presets/default/canonical/skills/stage-method/diagnosis-method.skill.json +22 -0
- package/assets/presets/default/canonical/skills/stage-method/planning-method.skill.json +22 -0
- package/assets/presets/default/canonical/skills/stage-method/refactoring-method.skill.json +22 -0
- package/assets/presets/default/canonical/skills/stage-method/validation-method.skill.json +22 -0
- package/assets/presets/default/canonical/state.json +30 -0
- package/assets/presets/default/canonical/tasks/README.md +3 -0
- package/assets/presets/default/canonical/tasks/example-task.md +13 -0
- package/assets/presets/default/canonical/templates/README.md +3 -0
- package/assets/presets/default/canonical/templates/example.template.md +5 -0
- package/assets/presets/default/preset.json +5 -0
- package/dist/cli.js +53 -0
- package/dist/commands/doctor.js +16 -0
- package/dist/commands/init.js +30 -0
- package/dist/commands/install.js +27 -0
- package/dist/commands/setup-agent.js +23 -0
- package/dist/commands/status.js +28 -0
- package/dist/commands/sync.js +29 -0
- package/dist/commands/update.js +18 -0
- package/dist/contracts/compiled-schema.js +37 -0
- package/dist/contracts/compiler.js +83 -0
- package/dist/contracts/freshness.js +52 -0
- package/dist/contracts/index.js +5 -0
- package/dist/contracts/parser.js +201 -0
- package/dist/contracts/schema.js +138 -0
- package/dist/contracts/source-schema.js +111 -0
- package/dist/core/agent-runtime.js +36 -0
- package/dist/core/agent-setup.js +111 -0
- package/dist/core/doctor.js +155 -0
- package/dist/core/errors.js +19 -0
- package/dist/core/flow-state.js +262 -0
- package/dist/core/fs.js +50 -0
- package/dist/core/install.js +44 -0
- package/dist/core/managed-files.js +106 -0
- package/dist/core/paths.js +56 -0
- package/dist/core/preset-validation.js +43 -0
- package/dist/core/prompt-builder.js +63 -0
- package/dist/core/repo-context.js +77 -0
- package/dist/core/repo-root.js +55 -0
- package/dist/core/skill-resolution.js +123 -0
- package/dist/core/state.js +220 -0
- package/dist/core/status.js +293 -0
- package/dist/core/sync.js +63 -0
- package/dist/core/targets.js +48 -0
- package/dist/core/upgrade.js +57 -0
- package/dist/core/validation.js +138 -0
- package/dist/core/version-checks.js +35 -0
- package/dist/generators/claude.js +14 -0
- package/dist/generators/codex.js +14 -0
- package/dist/generators/copilot.js +29 -0
- package/dist/generators/header.js +13 -0
- package/dist/generators/opencode.js +14 -0
- package/dist/generators/shared.js +37 -0
- package/dist/index.js +9 -0
- package/dist/legacy/targets.js +55 -0
- package/dist/presets/default.js +3 -0
- package/dist/presets/loader.js +34 -0
- package/dist/presets/version.js +18 -0
- package/dist/scoring/model.js +262 -0
- package/dist/skills/loader.js +40 -0
- package/dist/skills/schema.js +159 -0
- package/dist/types.js +1 -0
- package/docs/canonical-prodify-layout.md +87 -0
- package/docs/claude-support.md +22 -0
- package/docs/cli-doctor-spec.md +52 -0
- package/docs/cli-init-spec.md +70 -0
- package/docs/cli-install-agent-spec.md +48 -0
- package/docs/cli-sync-spec.md +40 -0
- package/docs/codex-support.md +24 -0
- package/docs/compatibility-targets.md +59 -0
- package/docs/copilot-support.md +30 -0
- package/docs/default-preset.md +47 -0
- package/docs/generated-file-headers.md +45 -0
- package/docs/generation-rules.md +94 -0
- package/docs/idempotency-guarantees.md +31 -0
- package/docs/managed-file-detection.md +45 -0
- package/docs/manual-edit-conflicts.md +37 -0
- package/docs/opencode-support.md +27 -0
- package/docs/ownership-rules.md +39 -0
- package/docs/path-resolution-rules.md +40 -0
- package/docs/preset-structure.md +49 -0
- package/docs/skill-system.md +67 -0
- package/docs/versioning-and-upgrade-strategy.md +40 -0
- package/package.json +22 -0
- package/src/README.md +11 -0
- package/src/cli.ts +61 -0
- package/src/commands/doctor.ts +20 -0
- package/src/commands/init.ts +37 -0
- package/src/commands/setup-agent.ts +28 -0
- package/src/commands/status.ts +34 -0
- package/src/commands/update.ts +23 -0
- package/src/contracts/README.md +10 -0
- package/src/contracts/compiled-schema.ts +42 -0
- package/src/contracts/compiler.ts +111 -0
- package/src/contracts/freshness.ts +58 -0
- package/src/contracts/index.ts +11 -0
- package/src/contracts/parser.ts +253 -0
- package/src/contracts/source-schema.ts +141 -0
- package/src/core/agent-runtime.ts +53 -0
- package/src/core/agent-setup.ts +147 -0
- package/src/core/doctor.ts +171 -0
- package/src/core/errors.ts +28 -0
- package/src/core/flow-state.ts +333 -0
- package/src/core/fs.ts +59 -0
- package/src/core/paths.ts +63 -0
- package/src/core/preset-validation.ts +47 -0
- package/src/core/prompt-builder.ts +73 -0
- package/src/core/repo-context.ts +93 -0
- package/src/core/repo-root.ts +74 -0
- package/src/core/skill-resolution.ts +151 -0
- package/src/core/state.ts +264 -0
- package/src/core/status.ts +372 -0
- package/src/core/targets.ts +53 -0
- package/src/core/upgrade.ts +66 -0
- package/src/core/validation.ts +233 -0
- package/src/core/version-checks.ts +40 -0
- package/src/index.ts +13 -0
- package/src/presets/default.ts +7 -0
- package/src/presets/loader.ts +46 -0
- package/src/presets/version.ts +31 -0
- package/src/scoring/model.ts +332 -0
- package/src/skills/loader.ts +58 -0
- package/src/skills/schema.ts +197 -0
- package/src/types.ts +329 -0
- package/tests/integration/cli-flows.test.js +347 -0
- package/tests/unit/agent-setup.test.js +81 -0
- package/tests/unit/cli.test.js +28 -0
- package/tests/unit/contracts.test.js +61 -0
- package/tests/unit/helpers.js +28 -0
- package/tests/unit/paths.test.js +52 -0
- package/tests/unit/preset-loader.test.js +37 -0
- package/tests/unit/scoring.test.js +65 -0
- package/tests/unit/self-hosted-workspace.test.js +22 -0
- package/tests/unit/skills.test.js +115 -0
- package/tests/unit/state-and-flow.test.js +120 -0
- package/tests/unit/targets.test.js +13 -0
- package/tests/unit/validation.test.js +73 -0
- package/tests/unit/version.test.js +24 -0
- package/tsconfig.json +23 -0
- package/urielsh-prodify-0.1.0.tgz +0 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { ProdifyError } from './errors.js';
|
|
2
|
+
import { isRuntimeProfileName } from './paths.js';
|
|
3
|
+
import { RUNTIME_STATUS } from './state.js';
|
|
4
|
+
import type {
|
|
5
|
+
ExecutionMode,
|
|
6
|
+
FlowStage,
|
|
7
|
+
ProdifyState,
|
|
8
|
+
ResumeDecision,
|
|
9
|
+
RuntimeProfileName,
|
|
10
|
+
StageValidationResult
|
|
11
|
+
} from '../types.js';
|
|
12
|
+
|
|
13
|
+
export const STAGE_ORDER: FlowStage[] = ['understand', 'diagnose', 'architecture', 'plan', 'refactor', 'validate'];
|
|
14
|
+
|
|
15
|
+
const STAGE_TASK_IDS: Record<FlowStage, string> = {
|
|
16
|
+
understand: '01-understand',
|
|
17
|
+
diagnose: '02-diagnose',
|
|
18
|
+
architecture: '03-architecture',
|
|
19
|
+
plan: '04-plan',
|
|
20
|
+
refactor: '05-refactor',
|
|
21
|
+
validate: '06-validate'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function cloneState(state: ProdifyState): ProdifyState {
|
|
25
|
+
return {
|
|
26
|
+
...state,
|
|
27
|
+
runtime: {
|
|
28
|
+
...state.runtime,
|
|
29
|
+
completed_stages: [...state.runtime.completed_stages],
|
|
30
|
+
last_validated_contract_versions: {
|
|
31
|
+
...state.runtime.last_validated_contract_versions
|
|
32
|
+
},
|
|
33
|
+
last_validation: state.runtime.last_validation
|
|
34
|
+
? {
|
|
35
|
+
...state.runtime.last_validation,
|
|
36
|
+
violated_rules: [...state.runtime.last_validation.violated_rules],
|
|
37
|
+
missing_artifacts: [...state.runtime.last_validation.missing_artifacts],
|
|
38
|
+
warnings: [...state.runtime.last_validation.warnings],
|
|
39
|
+
diagnostics: [...state.runtime.last_validation.diagnostics]
|
|
40
|
+
}
|
|
41
|
+
: null,
|
|
42
|
+
failure_metadata: state.runtime.failure_metadata
|
|
43
|
+
? { ...state.runtime.failure_metadata }
|
|
44
|
+
: null,
|
|
45
|
+
bootstrap: {
|
|
46
|
+
...state.runtime.bootstrap
|
|
47
|
+
},
|
|
48
|
+
timestamps: {
|
|
49
|
+
...state.runtime.timestamps
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function nextStage(currentStage: FlowStage): FlowStage | null {
|
|
56
|
+
const index = STAGE_ORDER.indexOf(currentStage);
|
|
57
|
+
if (index === -1) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return STAGE_ORDER[index + 1] ?? null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function pendingState(stage: FlowStage): ProdifyState['runtime']['current_state'] {
|
|
65
|
+
return `${stage}_pending` as ProdifyState['runtime']['current_state'];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function completeState(stage: FlowStage): ProdifyState['runtime']['current_state'] {
|
|
69
|
+
return `${stage}_complete` as ProdifyState['runtime']['current_state'];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function assertAgent(agent: RuntimeProfileName): void {
|
|
73
|
+
if (!isRuntimeProfileName(agent)) {
|
|
74
|
+
throw new ProdifyError(`Unknown target agent: ${agent}`, {
|
|
75
|
+
code: 'UNKNOWN_TARGET'
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function assertMode(mode: ExecutionMode): void {
|
|
81
|
+
if (!['interactive', 'auto'].includes(mode)) {
|
|
82
|
+
throw new ProdifyError(`Unsupported runtime mode: ${mode}`, {
|
|
83
|
+
code: 'INVALID_RUNTIME_MODE'
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function ensureStageCheckpoint(state: ProdifyState): FlowStage {
|
|
89
|
+
if (!state.runtime.current_stage) {
|
|
90
|
+
throw new ProdifyError('Cannot complete a runtime stage when no stage is active.', {
|
|
91
|
+
code: 'RUNTIME_STAGE_MISSING'
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return state.runtime.current_stage;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function stageToTaskId(stage: FlowStage): string {
|
|
99
|
+
return STAGE_TASK_IDS[stage];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function bootstrapFlowState(
|
|
103
|
+
state: ProdifyState,
|
|
104
|
+
{ agent, mode = 'interactive', now = null }: { agent: RuntimeProfileName; mode?: ExecutionMode; now?: string | null }
|
|
105
|
+
): ProdifyState {
|
|
106
|
+
assertAgent(agent);
|
|
107
|
+
assertMode(mode);
|
|
108
|
+
|
|
109
|
+
const nextState = cloneState(state);
|
|
110
|
+
nextState.runtime.status = RUNTIME_STATUS.READY;
|
|
111
|
+
nextState.runtime.current_state = 'bootstrapped';
|
|
112
|
+
nextState.runtime.mode = mode;
|
|
113
|
+
nextState.runtime.current_stage = null;
|
|
114
|
+
nextState.runtime.current_task_id = null;
|
|
115
|
+
nextState.runtime.pending_stage = STAGE_ORDER[0];
|
|
116
|
+
nextState.runtime.completed_stages = [];
|
|
117
|
+
nextState.runtime.awaiting_user_validation = false;
|
|
118
|
+
nextState.runtime.last_validation_result = 'unknown';
|
|
119
|
+
nextState.runtime.last_validation = null;
|
|
120
|
+
nextState.runtime.last_validated_contract_versions = {};
|
|
121
|
+
nextState.runtime.resumable = true;
|
|
122
|
+
nextState.runtime.blocked_reason = null;
|
|
123
|
+
nextState.runtime.failure_metadata = null;
|
|
124
|
+
nextState.runtime.bootstrap = {
|
|
125
|
+
bootstrapped: true
|
|
126
|
+
};
|
|
127
|
+
nextState.runtime.next_action = mode === 'auto' ? '$prodify-execute --auto' : '$prodify-execute';
|
|
128
|
+
nextState.runtime.timestamps.bootstrapped_at = nextState.runtime.timestamps.bootstrapped_at ?? now;
|
|
129
|
+
nextState.runtime.timestamps.last_transition_at = now;
|
|
130
|
+
nextState.runtime.timestamps.completed_at = null;
|
|
131
|
+
|
|
132
|
+
return nextState;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function startFlowExecution(
|
|
136
|
+
state: ProdifyState,
|
|
137
|
+
{ mode = state.runtime.mode ?? 'interactive', now = null }: { mode?: ExecutionMode; now?: string | null } = {}
|
|
138
|
+
): ProdifyState {
|
|
139
|
+
assertMode(mode);
|
|
140
|
+
|
|
141
|
+
const nextState = cloneState(state);
|
|
142
|
+
nextState.runtime.mode = mode;
|
|
143
|
+
|
|
144
|
+
if (nextState.runtime.current_state === 'validate_complete') {
|
|
145
|
+
nextState.runtime.status = RUNTIME_STATUS.COMPLETE;
|
|
146
|
+
nextState.runtime.current_state = 'completed';
|
|
147
|
+
nextState.runtime.current_stage = null;
|
|
148
|
+
nextState.runtime.current_task_id = null;
|
|
149
|
+
nextState.runtime.pending_stage = null;
|
|
150
|
+
nextState.runtime.awaiting_user_validation = false;
|
|
151
|
+
nextState.runtime.resumable = false;
|
|
152
|
+
nextState.runtime.next_action = 'none';
|
|
153
|
+
nextState.runtime.timestamps.completed_at = now;
|
|
154
|
+
nextState.runtime.timestamps.last_transition_at = now;
|
|
155
|
+
return nextState;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const stage = nextState.runtime.pending_stage;
|
|
159
|
+
if (!stage) {
|
|
160
|
+
throw new ProdifyError('Cannot start flow execution without a pending stage.', {
|
|
161
|
+
code: 'RUNTIME_STAGE_MISSING'
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
nextState.runtime.status = RUNTIME_STATUS.RUNNING;
|
|
166
|
+
nextState.runtime.current_state = pendingState(stage);
|
|
167
|
+
nextState.runtime.current_stage = stage;
|
|
168
|
+
nextState.runtime.current_task_id = stageToTaskId(stage);
|
|
169
|
+
nextState.runtime.pending_stage = null;
|
|
170
|
+
nextState.runtime.awaiting_user_validation = false;
|
|
171
|
+
nextState.runtime.blocked_reason = null;
|
|
172
|
+
nextState.runtime.failure_metadata = null;
|
|
173
|
+
nextState.runtime.next_action = '$prodify-resume';
|
|
174
|
+
nextState.runtime.resumable = true;
|
|
175
|
+
nextState.runtime.timestamps.last_transition_at = now;
|
|
176
|
+
|
|
177
|
+
return nextState;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function completeFlowStage(
|
|
181
|
+
state: ProdifyState,
|
|
182
|
+
{
|
|
183
|
+
validation,
|
|
184
|
+
now = null
|
|
185
|
+
}: {
|
|
186
|
+
validation: StageValidationResult;
|
|
187
|
+
now?: string | null;
|
|
188
|
+
}
|
|
189
|
+
): ProdifyState {
|
|
190
|
+
const currentStage = ensureStageCheckpoint(state);
|
|
191
|
+
if (validation.stage !== currentStage) {
|
|
192
|
+
throw new ProdifyError(`Validation result stage ${validation.stage} does not match active stage ${currentStage}.`, {
|
|
193
|
+
code: 'VALIDATION_STAGE_MISMATCH'
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!validation.passed) {
|
|
198
|
+
return failFlowStage(state, {
|
|
199
|
+
reason: validation.violated_rules.map((issue) => issue.message).join('; ') || 'stage validation failed',
|
|
200
|
+
validation,
|
|
201
|
+
now
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const nextState = cloneState(state);
|
|
206
|
+
if (!nextState.runtime.completed_stages.includes(currentStage)) {
|
|
207
|
+
nextState.runtime.completed_stages.push(currentStage);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
nextState.runtime.last_validation_result = 'pass';
|
|
211
|
+
nextState.runtime.last_validation = validation;
|
|
212
|
+
nextState.runtime.last_validated_contract_versions[currentStage] = validation.contract_version;
|
|
213
|
+
nextState.runtime.blocked_reason = null;
|
|
214
|
+
nextState.runtime.failure_metadata = null;
|
|
215
|
+
nextState.runtime.timestamps.last_transition_at = now;
|
|
216
|
+
|
|
217
|
+
const upcomingStage = nextStage(currentStage);
|
|
218
|
+
if (!upcomingStage) {
|
|
219
|
+
nextState.runtime.current_state = 'validate_complete';
|
|
220
|
+
nextState.runtime.current_stage = currentStage;
|
|
221
|
+
nextState.runtime.current_task_id = stageToTaskId(currentStage);
|
|
222
|
+
nextState.runtime.pending_stage = null;
|
|
223
|
+
|
|
224
|
+
if (nextState.runtime.mode === 'interactive') {
|
|
225
|
+
nextState.runtime.status = RUNTIME_STATUS.AWAITING_VALIDATION;
|
|
226
|
+
nextState.runtime.awaiting_user_validation = true;
|
|
227
|
+
nextState.runtime.resumable = true;
|
|
228
|
+
nextState.runtime.next_action = '$prodify-resume';
|
|
229
|
+
return nextState;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return startFlowExecution(nextState, {
|
|
233
|
+
mode: nextState.runtime.mode ?? 'auto',
|
|
234
|
+
now
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (nextState.runtime.mode === 'interactive') {
|
|
239
|
+
nextState.runtime.status = RUNTIME_STATUS.AWAITING_VALIDATION;
|
|
240
|
+
nextState.runtime.current_state = completeState(currentStage);
|
|
241
|
+
nextState.runtime.current_stage = currentStage;
|
|
242
|
+
nextState.runtime.current_task_id = stageToTaskId(currentStage);
|
|
243
|
+
nextState.runtime.pending_stage = upcomingStage;
|
|
244
|
+
nextState.runtime.awaiting_user_validation = true;
|
|
245
|
+
nextState.runtime.resumable = true;
|
|
246
|
+
nextState.runtime.next_action = '$prodify-resume';
|
|
247
|
+
return nextState;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
nextState.runtime.status = RUNTIME_STATUS.READY;
|
|
251
|
+
nextState.runtime.current_state = pendingState(upcomingStage);
|
|
252
|
+
nextState.runtime.current_stage = upcomingStage;
|
|
253
|
+
nextState.runtime.current_task_id = stageToTaskId(upcomingStage);
|
|
254
|
+
nextState.runtime.pending_stage = null;
|
|
255
|
+
nextState.runtime.awaiting_user_validation = false;
|
|
256
|
+
nextState.runtime.resumable = true;
|
|
257
|
+
nextState.runtime.next_action = '$prodify-execute --auto';
|
|
258
|
+
return nextState;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function failFlowStage(
|
|
262
|
+
state: ProdifyState,
|
|
263
|
+
{
|
|
264
|
+
reason,
|
|
265
|
+
validation = null,
|
|
266
|
+
now = null
|
|
267
|
+
}: {
|
|
268
|
+
reason: string;
|
|
269
|
+
validation?: StageValidationResult | null;
|
|
270
|
+
now?: string | null;
|
|
271
|
+
}
|
|
272
|
+
): ProdifyState {
|
|
273
|
+
const nextState = cloneState(state);
|
|
274
|
+
nextState.runtime.status = RUNTIME_STATUS.FAILED;
|
|
275
|
+
nextState.runtime.current_state = 'failed';
|
|
276
|
+
nextState.runtime.blocked_reason = reason;
|
|
277
|
+
nextState.runtime.awaiting_user_validation = false;
|
|
278
|
+
nextState.runtime.resumable = false;
|
|
279
|
+
nextState.runtime.next_action = 'repair runtime state';
|
|
280
|
+
nextState.runtime.last_validation_result = validation ? 'fail' : nextState.runtime.last_validation_result;
|
|
281
|
+
nextState.runtime.last_validation = validation;
|
|
282
|
+
nextState.runtime.failure_metadata = {
|
|
283
|
+
stage: validation?.stage ?? nextState.runtime.current_stage,
|
|
284
|
+
contract_version: validation?.contract_version ?? null,
|
|
285
|
+
reason
|
|
286
|
+
};
|
|
287
|
+
nextState.runtime.timestamps.last_transition_at = now;
|
|
288
|
+
return nextState;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function getResumeDecision(state: ProdifyState): ResumeDecision {
|
|
292
|
+
const runtime = state.runtime;
|
|
293
|
+
|
|
294
|
+
if (runtime.current_state === 'completed' || runtime.status === RUNTIME_STATUS.COMPLETE) {
|
|
295
|
+
return {
|
|
296
|
+
resumable: false,
|
|
297
|
+
command: null,
|
|
298
|
+
reason: 'flow complete'
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (runtime.current_state === 'failed' || runtime.current_state === 'blocked') {
|
|
303
|
+
return {
|
|
304
|
+
resumable: false,
|
|
305
|
+
command: null,
|
|
306
|
+
reason: runtime.blocked_reason ?? 'runtime is blocked'
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (!runtime.resumable) {
|
|
311
|
+
return {
|
|
312
|
+
resumable: false,
|
|
313
|
+
command: null,
|
|
314
|
+
reason: runtime.blocked_reason ?? 'runtime not bootstrapped'
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (runtime.status === RUNTIME_STATUS.READY) {
|
|
319
|
+
return {
|
|
320
|
+
resumable: true,
|
|
321
|
+
command: runtime.mode === 'auto' ? '$prodify-execute --auto' : '$prodify-execute',
|
|
322
|
+
reason: runtime.pending_stage
|
|
323
|
+
? `ready for ${runtime.pending_stage}`
|
|
324
|
+
: runtime.current_state
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
resumable: true,
|
|
330
|
+
command: '$prodify-resume',
|
|
331
|
+
reason: runtime.current_state
|
|
332
|
+
};
|
|
333
|
+
}
|
package/src/core/fs.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import type { FileEntry } from '../types.js';
|
|
4
|
+
|
|
5
|
+
export async function pathExists(targetPath: string): Promise<boolean> {
|
|
6
|
+
try {
|
|
7
|
+
await fs.access(targetPath);
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function ensureDir(targetPath: string): Promise<void> {
|
|
15
|
+
await fs.mkdir(targetPath, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function writeFileEnsuringDir(targetPath: string, content: string): Promise<void> {
|
|
19
|
+
await ensureDir(path.dirname(targetPath));
|
|
20
|
+
await fs.writeFile(targetPath, content, 'utf8');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function copyTree(sourceDir: string, destinationDir: string): Promise<void> {
|
|
24
|
+
const entries = await fs.readdir(sourceDir, { withFileTypes: true });
|
|
25
|
+
|
|
26
|
+
await ensureDir(destinationDir);
|
|
27
|
+
|
|
28
|
+
for (const entry of entries) {
|
|
29
|
+
const sourcePath = path.join(sourceDir, entry.name);
|
|
30
|
+
const destinationPath = path.join(destinationDir, entry.name);
|
|
31
|
+
|
|
32
|
+
if (entry.isDirectory()) {
|
|
33
|
+
await copyTree(sourcePath, destinationPath);
|
|
34
|
+
} else if (entry.isFile()) {
|
|
35
|
+
await writeFileEnsuringDir(destinationPath, await fs.readFile(sourcePath, 'utf8'));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function listFilesRecursive(rootDir: string, prefix = ''): Promise<FileEntry[]> {
|
|
41
|
+
const entries = await fs.readdir(rootDir, { withFileTypes: true });
|
|
42
|
+
const files: FileEntry[] = [];
|
|
43
|
+
|
|
44
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
45
|
+
const nextPrefix = prefix ? path.posix.join(prefix, entry.name) : entry.name;
|
|
46
|
+
const fullPath = path.join(rootDir, entry.name);
|
|
47
|
+
|
|
48
|
+
if (entry.isDirectory()) {
|
|
49
|
+
files.push(...(await listFilesRecursive(fullPath, nextPrefix)));
|
|
50
|
+
} else if (entry.isFile()) {
|
|
51
|
+
files.push({
|
|
52
|
+
fullPath,
|
|
53
|
+
relativePath: nextPrefix
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return files;
|
|
59
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import type { RuntimeProfileName } from '../types.js';
|
|
3
|
+
|
|
4
|
+
export const RUNTIME_PROFILE_NAMES = ['codex', 'claude', 'copilot', 'opencode'] as const satisfies readonly RuntimeProfileName[];
|
|
5
|
+
export const USER_OWNED_CANONICAL_PATHS = [
|
|
6
|
+
'.prodify/AGENTS.md',
|
|
7
|
+
'.prodify/project.md',
|
|
8
|
+
'.prodify/planning.md'
|
|
9
|
+
] as const;
|
|
10
|
+
export const USER_OWNED_CANONICAL_PREFIXES = [
|
|
11
|
+
'.prodify/contracts-src/'
|
|
12
|
+
] as const;
|
|
13
|
+
|
|
14
|
+
export const REQUIRED_CANONICAL_PATHS = [
|
|
15
|
+
'.prodify/AGENTS.md',
|
|
16
|
+
'.prodify/artifacts/README.md',
|
|
17
|
+
'.prodify/contracts-src/README.md',
|
|
18
|
+
'.prodify/contracts-src/architecture.contract.md',
|
|
19
|
+
'.prodify/contracts-src/diagnose.contract.md',
|
|
20
|
+
'.prodify/contracts-src/plan.contract.md',
|
|
21
|
+
'.prodify/contracts-src/refactor.contract.md',
|
|
22
|
+
'.prodify/contracts-src/understand.contract.md',
|
|
23
|
+
'.prodify/contracts-src/validate.contract.md',
|
|
24
|
+
'.prodify/metrics/README.md',
|
|
25
|
+
'.prodify/project.md',
|
|
26
|
+
'.prodify/planning.md',
|
|
27
|
+
'.prodify/runtime-commands.md',
|
|
28
|
+
'.prodify/skills/README.md',
|
|
29
|
+
'.prodify/skills/registry.json',
|
|
30
|
+
'.prodify/skills/domain/react-frontend.skill.json',
|
|
31
|
+
'.prodify/skills/domain/typescript-backend.skill.json',
|
|
32
|
+
'.prodify/skills/quality-policy/maintainability-review.skill.json',
|
|
33
|
+
'.prodify/skills/quality-policy/security-hardening.skill.json',
|
|
34
|
+
'.prodify/skills/quality-policy/test-hardening.skill.json',
|
|
35
|
+
'.prodify/skills/stage-method/architecture-method.skill.json',
|
|
36
|
+
'.prodify/skills/stage-method/codebase-scanning.skill.json',
|
|
37
|
+
'.prodify/skills/stage-method/diagnosis-method.skill.json',
|
|
38
|
+
'.prodify/skills/stage-method/planning-method.skill.json',
|
|
39
|
+
'.prodify/skills/stage-method/refactoring-method.skill.json',
|
|
40
|
+
'.prodify/skills/stage-method/validation-method.skill.json',
|
|
41
|
+
'.prodify/state.json',
|
|
42
|
+
'.prodify/tasks/README.md',
|
|
43
|
+
'.prodify/rules/README.md',
|
|
44
|
+
'.prodify/templates/README.md',
|
|
45
|
+
'.prodify/version.json'
|
|
46
|
+
] as const;
|
|
47
|
+
|
|
48
|
+
export function isRuntimeProfileName(value: unknown): value is RuntimeProfileName {
|
|
49
|
+
return typeof value === 'string' && (RUNTIME_PROFILE_NAMES as readonly string[]).includes(value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function normalizeRepoRelativePath(relativePath: string): string {
|
|
53
|
+
const normalized = relativePath.replaceAll('\\', '/');
|
|
54
|
+
return path.posix.normalize(normalized).replace(/^\/+/, '');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function resolveRepoPath(repoRoot: string, relativePath: string): string {
|
|
58
|
+
return path.join(repoRoot, ...normalizeRepoRelativePath(relativePath).split('/'));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function resolveCanonicalPath(repoRoot: string, relativePath: string): string {
|
|
62
|
+
return resolveRepoPath(repoRoot, relativePath);
|
|
63
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ProdifyError } from './errors.js';
|
|
2
|
+
import type { PresetEntry } from '../types.js';
|
|
3
|
+
|
|
4
|
+
const REQUIRED_PRESET_ENTRIES = [
|
|
5
|
+
'.prodify/AGENTS.md',
|
|
6
|
+
'.prodify/artifacts/README.md',
|
|
7
|
+
'.prodify/contracts-src/README.md',
|
|
8
|
+
'.prodify/contracts-src/architecture.contract.md',
|
|
9
|
+
'.prodify/contracts-src/diagnose.contract.md',
|
|
10
|
+
'.prodify/contracts-src/plan.contract.md',
|
|
11
|
+
'.prodify/contracts-src/refactor.contract.md',
|
|
12
|
+
'.prodify/contracts-src/understand.contract.md',
|
|
13
|
+
'.prodify/contracts-src/validate.contract.md',
|
|
14
|
+
'.prodify/metrics/README.md',
|
|
15
|
+
'.prodify/project.md',
|
|
16
|
+
'.prodify/planning.md',
|
|
17
|
+
'.prodify/runtime-commands.md',
|
|
18
|
+
'.prodify/skills/README.md',
|
|
19
|
+
'.prodify/skills/registry.json',
|
|
20
|
+
'.prodify/skills/domain/react-frontend.skill.json',
|
|
21
|
+
'.prodify/skills/domain/typescript-backend.skill.json',
|
|
22
|
+
'.prodify/skills/quality-policy/maintainability-review.skill.json',
|
|
23
|
+
'.prodify/skills/quality-policy/security-hardening.skill.json',
|
|
24
|
+
'.prodify/skills/quality-policy/test-hardening.skill.json',
|
|
25
|
+
'.prodify/skills/stage-method/architecture-method.skill.json',
|
|
26
|
+
'.prodify/skills/stage-method/codebase-scanning.skill.json',
|
|
27
|
+
'.prodify/skills/stage-method/diagnosis-method.skill.json',
|
|
28
|
+
'.prodify/skills/stage-method/planning-method.skill.json',
|
|
29
|
+
'.prodify/skills/stage-method/refactoring-method.skill.json',
|
|
30
|
+
'.prodify/skills/stage-method/validation-method.skill.json',
|
|
31
|
+
'.prodify/state.json',
|
|
32
|
+
'.prodify/version.json',
|
|
33
|
+
'.prodify/tasks/README.md',
|
|
34
|
+
'.prodify/rules/README.md',
|
|
35
|
+
'.prodify/templates/README.md'
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
export function validatePresetEntries(entries: PresetEntry[]): void {
|
|
39
|
+
const entryPaths = new Set(entries.map((entry) => entry.relativePath));
|
|
40
|
+
const missing = REQUIRED_PRESET_ENTRIES.filter((requiredPath) => !entryPaths.has(requiredPath));
|
|
41
|
+
|
|
42
|
+
if (missing.length > 0) {
|
|
43
|
+
throw new ProdifyError(`Default preset is missing required canonical files: ${missing.join(', ')}`, {
|
|
44
|
+
code: 'PRESET_INVALID'
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { STAGE_ORDER, stageToTaskId } from './flow-state.js';
|
|
2
|
+
import { getRuntimeProfile } from './targets.js';
|
|
3
|
+
import type { ProdifyState, RuntimeProfileName } from '../types.js';
|
|
4
|
+
|
|
5
|
+
export function buildBootstrapPrompt(profileName: RuntimeProfileName | null | undefined = 'codex'): string {
|
|
6
|
+
return (getRuntimeProfile(profileName ?? 'codex') ?? getRuntimeProfile('codex'))!.bootstrapPrompt;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function hasManualBootstrapGuidance(markdown: string): boolean {
|
|
10
|
+
return markdown.includes('.prodify/AGENTS.md') && markdown.includes('$prodify-init');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function buildRuntimeCommandReference(options: {
|
|
14
|
+
concise?: boolean;
|
|
15
|
+
profileName?: RuntimeProfileName | null;
|
|
16
|
+
} = {}): string {
|
|
17
|
+
const concise = options.concise ?? false;
|
|
18
|
+
const profile = getRuntimeProfile(options.profileName ?? 'codex') ?? getRuntimeProfile('codex');
|
|
19
|
+
const bootstrapPrompt = profile?.bootstrapPrompt ?? buildBootstrapPrompt('codex');
|
|
20
|
+
const lines = [
|
|
21
|
+
'# Runtime Commands',
|
|
22
|
+
'',
|
|
23
|
+
'Manual bootstrap:',
|
|
24
|
+
`- First tell the agent: "${bootstrapPrompt}"`,
|
|
25
|
+
'- Before using `$prodify-*` in a new agent environment, run `prodify setup-agent <agent>` once outside the repo.',
|
|
26
|
+
'- Keep the workflow anchored to `.prodify/` files only.',
|
|
27
|
+
'- Humans edit `.prodify/contracts-src/`; runtime reads only `.prodify/contracts/*.contract.json`.',
|
|
28
|
+
'- Stage skill definitions live under `.prodify/skills/` and can assist a stage without overriding its contract or validator.',
|
|
29
|
+
'',
|
|
30
|
+
'Run these commands inside your coding agent after `prodify init` has created the repo scaffolding.',
|
|
31
|
+
'',
|
|
32
|
+
'- `$prodify-init`: inspect `.prodify/`, detect or resolve the active agent runtime, initialize `.prodify/state.json`, and prepare the bootstrapped state without locking the repo to one agent.',
|
|
33
|
+
'- `$prodify-execute`: run one stage, write stage artifacts, validate them against compiled contracts, then pause in interactive mode.',
|
|
34
|
+
'- `$prodify-execute --auto`: continue through the full workflow without pausing unless there is a hard failure, policy block, required approval threshold, or invalid state.',
|
|
35
|
+
'- `$prodify-resume`: continue from `.prodify/state.json` after a pause, interruption, or validation checkpoint.',
|
|
36
|
+
'',
|
|
37
|
+
'Stage order:',
|
|
38
|
+
`- ${STAGE_ORDER.map((stage) => `${stage} (${stageToTaskId(stage)})`).join(' -> ')}`,
|
|
39
|
+
'',
|
|
40
|
+
'Resume rules:',
|
|
41
|
+
'- interactive mode pauses between stages and expects validation before resume',
|
|
42
|
+
'- auto mode keeps advancing while state remains valid',
|
|
43
|
+
'- failed or corrupt state must stop with a clear reason'
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
if (concise) {
|
|
47
|
+
return [
|
|
48
|
+
'# Runtime Commands',
|
|
49
|
+
'',
|
|
50
|
+
`- First prompt: "${bootstrapPrompt}"`,
|
|
51
|
+
'- Run `prodify setup-agent <agent>` once per machine before using agent runtime commands.',
|
|
52
|
+
'- `$prodify-init`: bootstrap `.prodify/state.json` for the active agent runtime without repo-level locking.',
|
|
53
|
+
'- `$prodify-execute`: run one stage, then pause.',
|
|
54
|
+
'- `$prodify-execute --auto`: continue until a hard stop.',
|
|
55
|
+
'- `$prodify-resume`: continue from saved state.'
|
|
56
|
+
].join('\n') + '\n';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return `${lines.join('\n')}\n`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function buildExecutionPrompt(state: ProdifyState): string {
|
|
63
|
+
const stage = state.runtime.current_stage ?? state.runtime.pending_stage ?? 'none';
|
|
64
|
+
const task = state.runtime.current_task_id ?? (state.runtime.pending_stage ? stageToTaskId(state.runtime.pending_stage) : 'none');
|
|
65
|
+
return [
|
|
66
|
+
`Current stage: ${stage}`,
|
|
67
|
+
`Current task: ${task}`,
|
|
68
|
+
`Current state: ${state.runtime.current_state}`,
|
|
69
|
+
`Mode: ${state.runtime.mode ?? 'unset'}`,
|
|
70
|
+
`Status: ${state.runtime.status}`,
|
|
71
|
+
`Next action: ${state.runtime.next_action}`
|
|
72
|
+
].join('\n');
|
|
73
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
import { listFilesRecursive, pathExists } from './fs.js';
|
|
4
|
+
import { resolveRepoPath } from './paths.js';
|
|
5
|
+
import type { RepoContextSnapshot } from '../types.js';
|
|
6
|
+
|
|
7
|
+
function sortedUnique(values: Iterable<string>): string[] {
|
|
8
|
+
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function readPackageJson(repoRoot: string): Promise<Record<string, unknown> | null> {
|
|
12
|
+
const packageJsonPath = resolveRepoPath(repoRoot, 'package.json');
|
|
13
|
+
if (!(await pathExists(packageJsonPath))) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(await fs.readFile(packageJsonPath, 'utf8')) as Record<string, unknown>;
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function dependencyEntries(packageJson: Record<string, unknown> | null): string[] {
|
|
25
|
+
if (!packageJson) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const sections = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
|
|
30
|
+
const names = [];
|
|
31
|
+
for (const section of sections) {
|
|
32
|
+
const record = packageJson[section];
|
|
33
|
+
if (typeof record !== 'object' || record === null || Array.isArray(record)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
names.push(...Object.keys(record as Record<string, unknown>));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return sortedUnique(names);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function detectRepoContext(repoRoot: string): Promise<RepoContextSnapshot> {
|
|
44
|
+
const packageJson = await readPackageJson(repoRoot);
|
|
45
|
+
const dependencies = dependencyEntries(packageJson);
|
|
46
|
+
const files = await listFilesRecursive(repoRoot);
|
|
47
|
+
const relativePaths = files.map((entry) => entry.relativePath.replaceAll('\\', '/'));
|
|
48
|
+
|
|
49
|
+
const hasTsConfig = relativePaths.includes('tsconfig.json');
|
|
50
|
+
const hasTypeScriptSources = relativePaths.some((relativePath) => relativePath.endsWith('.ts') || relativePath.endsWith('.tsx'));
|
|
51
|
+
const hasJavaScriptSources = relativePaths.some((relativePath) => relativePath.endsWith('.js') || relativePath.endsWith('.jsx'));
|
|
52
|
+
const hasTests = relativePaths.some((relativePath) => relativePath.startsWith('tests/') || relativePath.includes('.test.'));
|
|
53
|
+
const hasCliEntrypoint = relativePaths.includes('src/cli.ts')
|
|
54
|
+
|| relativePaths.includes('src/cli.js')
|
|
55
|
+
|| relativePaths.includes('bin/prodify');
|
|
56
|
+
const hasCoreDir = relativePaths.some((relativePath) => relativePath.startsWith('src/core/'));
|
|
57
|
+
const hasCommandsDir = relativePaths.some((relativePath) => relativePath.startsWith('src/commands/'));
|
|
58
|
+
|
|
59
|
+
const languages = sortedUnique([
|
|
60
|
+
...(hasTypeScriptSources || hasTsConfig ? ['typescript'] : []),
|
|
61
|
+
...(hasJavaScriptSources || packageJson ? ['javascript'] : [])
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
const frameworks = sortedUnique([
|
|
65
|
+
...(dependencies.includes('react') || dependencies.includes('react-dom') ? ['react'] : [])
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
const projectTypes = sortedUnique([
|
|
69
|
+
...(packageJson ? ['node-package'] : []),
|
|
70
|
+
...(hasCliEntrypoint ? ['cli'] : []),
|
|
71
|
+
...(frameworks.includes('react') ? ['frontend-app'] : []),
|
|
72
|
+
...(!frameworks.includes('react') && hasCoreDir ? ['backend-service'] : [])
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
const architecturePatterns = sortedUnique([
|
|
76
|
+
...(hasCoreDir && hasCommandsDir ? ['layered-cli'] : []),
|
|
77
|
+
...(relativePaths.some((relativePath) => relativePath.startsWith('src/contracts/')) ? ['contract-driven-runtime'] : [])
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
const riskSignals = sortedUnique([
|
|
81
|
+
...(dependencies.length > 0 ? ['external-dependencies'] : []),
|
|
82
|
+
...(hasCliEntrypoint ? ['cli-surface'] : []),
|
|
83
|
+
...(!hasTests ? ['missing-tests'] : [])
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
languages,
|
|
88
|
+
frameworks,
|
|
89
|
+
project_types: projectTypes,
|
|
90
|
+
architecture_patterns: architecturePatterns,
|
|
91
|
+
risk_signals: riskSignals
|
|
92
|
+
};
|
|
93
|
+
}
|