@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
package/src/types.ts
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
export type RuntimeProfileName = 'codex' | 'claude' | 'copilot' | 'opencode';
|
|
2
|
+
export type ExecutionMode = 'interactive' | 'auto';
|
|
3
|
+
export type FlowStage = 'understand' | 'diagnose' | 'architecture' | 'plan' | 'refactor' | 'validate';
|
|
4
|
+
export type RuntimeStatus = 'not_bootstrapped' | 'ready' | 'running' | 'awaiting_validation' | 'blocked' | 'failed' | 'complete';
|
|
5
|
+
export type ValidationResult = 'unknown' | 'pass' | 'fail' | 'inconclusive';
|
|
6
|
+
export type ContractArtifactFormat = 'markdown' | 'json';
|
|
7
|
+
export type SkillCategory = 'stage-method' | 'domain' | 'quality-policy';
|
|
8
|
+
export type RepoContextFact = 'language' | 'framework' | 'project_type' | 'architecture_pattern' | 'risk_signal';
|
|
9
|
+
export type ContractRuntimeState =
|
|
10
|
+
| 'not_bootstrapped'
|
|
11
|
+
| 'bootstrapped'
|
|
12
|
+
| 'understand_pending'
|
|
13
|
+
| 'understand_complete'
|
|
14
|
+
| 'diagnose_pending'
|
|
15
|
+
| 'diagnose_complete'
|
|
16
|
+
| 'architecture_pending'
|
|
17
|
+
| 'architecture_complete'
|
|
18
|
+
| 'plan_pending'
|
|
19
|
+
| 'plan_complete'
|
|
20
|
+
| 'refactor_pending'
|
|
21
|
+
| 'refactor_complete'
|
|
22
|
+
| 'validate_pending'
|
|
23
|
+
| 'validate_complete'
|
|
24
|
+
| 'blocked'
|
|
25
|
+
| 'failed'
|
|
26
|
+
| 'completed';
|
|
27
|
+
export type ScoreSnapshotKind = 'baseline' | 'final';
|
|
28
|
+
export type ScoreMetricStatus = 'pass' | 'partial' | 'fail' | 'unavailable';
|
|
29
|
+
|
|
30
|
+
export interface OutputWriter {
|
|
31
|
+
write(chunk: string): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface CommandContext {
|
|
35
|
+
cwd: string;
|
|
36
|
+
stdout: OutputWriter;
|
|
37
|
+
stderr: OutputWriter;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type CommandHandler = (args: string[], context: CommandContext) => Promise<number>;
|
|
41
|
+
|
|
42
|
+
export interface VersionMetadata {
|
|
43
|
+
name: string;
|
|
44
|
+
version: string;
|
|
45
|
+
schemaVersion: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ParsedVersionMetadata {
|
|
49
|
+
presetName: string;
|
|
50
|
+
presetVersion: string;
|
|
51
|
+
schemaVersion: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface PresetEntry {
|
|
55
|
+
relativePath: string;
|
|
56
|
+
content: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface LoadedPreset {
|
|
60
|
+
metadata: VersionMetadata;
|
|
61
|
+
entries: PresetEntry[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface FileEntry {
|
|
65
|
+
fullPath: string;
|
|
66
|
+
relativePath: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface RuntimeTimestamps {
|
|
70
|
+
bootstrapped_at: string | null;
|
|
71
|
+
last_transition_at: string | null;
|
|
72
|
+
completed_at: string | null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface CompiledContractArtifactRule {
|
|
76
|
+
path: string;
|
|
77
|
+
format: ContractArtifactFormat;
|
|
78
|
+
required_sections: string[];
|
|
79
|
+
required_json_keys: string[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface SkillConditionPredicate {
|
|
83
|
+
fact: RepoContextFact;
|
|
84
|
+
includes: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface SkillCondition {
|
|
88
|
+
all: SkillConditionPredicate[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface SkillDefinition {
|
|
92
|
+
schema_version: string;
|
|
93
|
+
id: string;
|
|
94
|
+
name: string;
|
|
95
|
+
version: string;
|
|
96
|
+
category: SkillCategory;
|
|
97
|
+
description: string;
|
|
98
|
+
intended_use: string[];
|
|
99
|
+
stage_compatibility: FlowStage[];
|
|
100
|
+
activation_conditions: SkillCondition[];
|
|
101
|
+
execution_guidance: string[];
|
|
102
|
+
caution_guidance: string[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface SkillRegistryManifest {
|
|
106
|
+
schema_version: string;
|
|
107
|
+
skills: string[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface StageSkillRoutingRule {
|
|
111
|
+
skill: string;
|
|
112
|
+
when: SkillCondition;
|
|
113
|
+
reason: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface StageSkillRouting {
|
|
117
|
+
default_skills: string[];
|
|
118
|
+
allowed_skills: string[];
|
|
119
|
+
conditional_skills: StageSkillRoutingRule[];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface CompiledStageContract {
|
|
123
|
+
schema_version: string;
|
|
124
|
+
contract_version: string;
|
|
125
|
+
stage: FlowStage;
|
|
126
|
+
task_id: string;
|
|
127
|
+
source_path: string;
|
|
128
|
+
source_hash: string;
|
|
129
|
+
required_artifacts: CompiledContractArtifactRule[];
|
|
130
|
+
allowed_write_roots: string[];
|
|
131
|
+
forbidden_writes: string[];
|
|
132
|
+
policy_rules: string[];
|
|
133
|
+
success_criteria: string[];
|
|
134
|
+
skill_routing: StageSkillRouting;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface ContractSourceDocument {
|
|
138
|
+
frontmatter: Record<string, unknown>;
|
|
139
|
+
body: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface CompiledContractInventory {
|
|
143
|
+
ok: boolean;
|
|
144
|
+
sourceCount: number;
|
|
145
|
+
compiledCount: number;
|
|
146
|
+
staleStages: FlowStage[];
|
|
147
|
+
missingCompiledStages: FlowStage[];
|
|
148
|
+
missingSourceStages: FlowStage[];
|
|
149
|
+
invalidStages: string[];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface ValidationIssue {
|
|
153
|
+
rule: string;
|
|
154
|
+
message: string;
|
|
155
|
+
path?: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface StageValidationResult {
|
|
159
|
+
stage: FlowStage;
|
|
160
|
+
contract_version: string;
|
|
161
|
+
passed: boolean;
|
|
162
|
+
violated_rules: ValidationIssue[];
|
|
163
|
+
missing_artifacts: string[];
|
|
164
|
+
warnings: string[];
|
|
165
|
+
diagnostics: string[];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface RuntimeFailureMetadata {
|
|
169
|
+
stage: FlowStage | null;
|
|
170
|
+
contract_version: string | null;
|
|
171
|
+
reason: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface RuntimeBootstrapMetadata {
|
|
175
|
+
bootstrapped: boolean;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface RuntimeStateBlock {
|
|
179
|
+
status: RuntimeStatus;
|
|
180
|
+
current_state: ContractRuntimeState;
|
|
181
|
+
mode: ExecutionMode | null;
|
|
182
|
+
current_stage: FlowStage | null;
|
|
183
|
+
current_task_id: string | null;
|
|
184
|
+
pending_stage: FlowStage | null;
|
|
185
|
+
completed_stages: FlowStage[];
|
|
186
|
+
awaiting_user_validation: boolean;
|
|
187
|
+
last_validation_result: ValidationResult;
|
|
188
|
+
last_validation: StageValidationResult | null;
|
|
189
|
+
last_validated_contract_versions: Partial<Record<FlowStage, string>>;
|
|
190
|
+
resumable: boolean;
|
|
191
|
+
blocked_reason: string | null;
|
|
192
|
+
failure_metadata: RuntimeFailureMetadata | null;
|
|
193
|
+
bootstrap: RuntimeBootstrapMetadata;
|
|
194
|
+
next_action: string;
|
|
195
|
+
timestamps: RuntimeTimestamps;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface ProdifyState {
|
|
199
|
+
schema_version: string;
|
|
200
|
+
preset_name: string;
|
|
201
|
+
preset_version: string;
|
|
202
|
+
runtime: RuntimeStateBlock;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export interface ResumeDecision {
|
|
206
|
+
resumable: boolean;
|
|
207
|
+
command: string | null;
|
|
208
|
+
reason: string;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export interface RuntimeProfile {
|
|
212
|
+
name: RuntimeProfileName;
|
|
213
|
+
displayName: string;
|
|
214
|
+
bootstrapPrompt: string;
|
|
215
|
+
bootstrapSummary: string;
|
|
216
|
+
executeCommand: '$prodify-execute' | '$prodify-execute --auto';
|
|
217
|
+
resumeCommand: '$prodify-resume';
|
|
218
|
+
nuances: string[];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export interface RepoContextSnapshot {
|
|
222
|
+
languages: string[];
|
|
223
|
+
frameworks: string[];
|
|
224
|
+
project_types: string[];
|
|
225
|
+
architecture_patterns: string[];
|
|
226
|
+
risk_signals: string[];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface SkillActivationRecord {
|
|
230
|
+
id: string;
|
|
231
|
+
name: string;
|
|
232
|
+
category: SkillCategory;
|
|
233
|
+
source: 'default' | 'conditional';
|
|
234
|
+
active: boolean;
|
|
235
|
+
reason: string;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export interface StageSkillResolution {
|
|
239
|
+
stage: FlowStage;
|
|
240
|
+
context: RepoContextSnapshot;
|
|
241
|
+
considered_skills: SkillActivationRecord[];
|
|
242
|
+
active_skill_ids: string[];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export interface VersionInspection {
|
|
246
|
+
status: 'missing' | 'current' | 'outdated' | 'malformed';
|
|
247
|
+
current: ParsedVersionMetadata | null;
|
|
248
|
+
expected: VersionMetadata;
|
|
249
|
+
schemaMigrationRequired: boolean;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export interface StatusReport {
|
|
253
|
+
ok: boolean;
|
|
254
|
+
initialized: boolean;
|
|
255
|
+
canonicalOk: boolean;
|
|
256
|
+
canonicalMissing: string[];
|
|
257
|
+
contractsOk: boolean;
|
|
258
|
+
contractInventory: CompiledContractInventory | null;
|
|
259
|
+
versionStatus: VersionInspection;
|
|
260
|
+
configuredAgents: RuntimeProfileName[];
|
|
261
|
+
runtimeState: ProdifyState | null;
|
|
262
|
+
runtimeStateError: Error | null;
|
|
263
|
+
resumable: boolean;
|
|
264
|
+
manualBootstrapReady: boolean;
|
|
265
|
+
bootstrapProfile: RuntimeProfileName;
|
|
266
|
+
bootstrapPrompt: string;
|
|
267
|
+
stageSkillResolution: StageSkillResolution | null;
|
|
268
|
+
recommendedNextAction: string;
|
|
269
|
+
presetMetadata: VersionMetadata;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export interface DoctorCheck {
|
|
273
|
+
label: string;
|
|
274
|
+
ok: boolean;
|
|
275
|
+
skipped?: boolean;
|
|
276
|
+
details?: string;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export interface DoctorResult {
|
|
280
|
+
ok: boolean;
|
|
281
|
+
checks: DoctorCheck[];
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export interface UpdateSummary {
|
|
285
|
+
versionStatus: VersionInspection['status'];
|
|
286
|
+
schemaMigrationRequired: boolean;
|
|
287
|
+
writtenCanonicalCount: number;
|
|
288
|
+
preservedCanonicalCount: number;
|
|
289
|
+
compiledContractCount: number;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export interface ScoreMetric {
|
|
293
|
+
id: string;
|
|
294
|
+
label: string;
|
|
295
|
+
tool: string;
|
|
296
|
+
weight: number;
|
|
297
|
+
max_points: number;
|
|
298
|
+
points: number;
|
|
299
|
+
status: ScoreMetricStatus;
|
|
300
|
+
details: string;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export interface ScoreSnapshot {
|
|
304
|
+
schema_version: string;
|
|
305
|
+
kind: ScoreSnapshotKind;
|
|
306
|
+
ecosystems: string[];
|
|
307
|
+
total_score: number;
|
|
308
|
+
max_score: number;
|
|
309
|
+
metrics: ScoreMetric[];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export interface ScoreDelta {
|
|
313
|
+
schema_version: string;
|
|
314
|
+
baseline_score: number;
|
|
315
|
+
final_score: number;
|
|
316
|
+
delta: number;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export interface AgentSetupRecord {
|
|
320
|
+
agent: RuntimeProfileName;
|
|
321
|
+
display_name: string;
|
|
322
|
+
configured_at: string;
|
|
323
|
+
commands: string[];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export interface GlobalAgentSetupState {
|
|
327
|
+
schema_version: string;
|
|
328
|
+
configured_agents: Partial<Record<RuntimeProfileName, AgentSetupRecord>>;
|
|
329
|
+
}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { runCli } from '../../dist/cli.js';
|
|
7
|
+
import { bootstrapFlowState, failFlowStage, startFlowExecution } from '../../dist/core/flow-state.js';
|
|
8
|
+
import { readRuntimeState, writeRuntimeState } from '../../dist/core/state.js';
|
|
9
|
+
import { createTempDir, createTempRepo, memoryStream } from '../unit/helpers.js';
|
|
10
|
+
|
|
11
|
+
async function execCli(repoRoot, args) {
|
|
12
|
+
const stdout = memoryStream();
|
|
13
|
+
const stderr = memoryStream();
|
|
14
|
+
const exitCode = await runCli(args, { cwd: repoRoot, stdout, stderr });
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
exitCode,
|
|
18
|
+
stdout: stdout.toString(),
|
|
19
|
+
stderr: stderr.toString()
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function readRepoFile(repoRoot, relativePath) {
|
|
24
|
+
return fs.readFile(path.join(repoRoot, relativePath), 'utf8');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function assertMissing(repoRoot, relativePath) {
|
|
28
|
+
await assert.rejects(fs.access(path.join(repoRoot, relativePath)));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
test('status reports an uninitialized repo cleanly', async () => {
|
|
32
|
+
const repoRoot = await createTempRepo();
|
|
33
|
+
|
|
34
|
+
const result = await execCli(repoRoot, ['status']);
|
|
35
|
+
|
|
36
|
+
assert.equal(result.exitCode, 1);
|
|
37
|
+
assert.match(result.stdout, /Repository: not initialized/);
|
|
38
|
+
assert.match(result.stdout, /Recommended next action: prodify init/);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('init creates only .prodify-owned runtime scaffolding', async () => {
|
|
42
|
+
const repoRoot = await createTempRepo();
|
|
43
|
+
|
|
44
|
+
const result = await execCli(repoRoot, ['init']);
|
|
45
|
+
|
|
46
|
+
assert.equal(result.exitCode, 0);
|
|
47
|
+
assert.match(result.stdout, /Initialized Prodify/);
|
|
48
|
+
assert.match(result.stdout, /prodify setup-agent <agent>/);
|
|
49
|
+
assert.match(result.stdout, /Manual bootstrap starts by telling your agent to read \.prodify\/AGENTS\.md/);
|
|
50
|
+
assert.match(result.stdout, /Compiled runtime contracts were generated under \.prodify\/contracts\//);
|
|
51
|
+
await fs.access(path.join(repoRoot, '.prodify', 'state.json'));
|
|
52
|
+
await fs.access(path.join(repoRoot, '.prodify', 'runtime-commands.md'));
|
|
53
|
+
await fs.access(path.join(repoRoot, '.prodify', 'contracts-src', 'understand.contract.md'));
|
|
54
|
+
await fs.access(path.join(repoRoot, '.prodify', 'contracts', 'understand.contract.json'));
|
|
55
|
+
await fs.access(path.join(repoRoot, '.prodify', 'artifacts', 'README.md'));
|
|
56
|
+
await fs.access(path.join(repoRoot, '.prodify', 'metrics', 'README.md'));
|
|
57
|
+
await assertMissing(repoRoot, 'AGENTS.md');
|
|
58
|
+
await assertMissing(repoRoot, 'CLAUDE.md');
|
|
59
|
+
await assertMissing(repoRoot, '.github/copilot-instructions.md');
|
|
60
|
+
await assertMissing(repoRoot, '.opencode/AGENTS.md');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('status becomes the primary user-facing summary after init', async () => {
|
|
64
|
+
const repoRoot = await createTempRepo();
|
|
65
|
+
await execCli(repoRoot, ['init']);
|
|
66
|
+
|
|
67
|
+
const result = await execCli(repoRoot, ['status']);
|
|
68
|
+
|
|
69
|
+
assert.equal(result.exitCode, 0);
|
|
70
|
+
assert.match(result.stdout, /Workspace health: healthy/);
|
|
71
|
+
assert.match(result.stdout, /Canonical files: healthy/);
|
|
72
|
+
assert.match(result.stdout, /Contract freshness: 6 compiled, synchronized/);
|
|
73
|
+
assert.match(result.stdout, /Version\/schema: current/);
|
|
74
|
+
assert.match(result.stdout, /Repo runtime binding: agent-agnostic/);
|
|
75
|
+
assert.match(result.stdout, /Global agent setup: none configured/);
|
|
76
|
+
assert.match(result.stdout, /Skill routing stage: understand/);
|
|
77
|
+
assert.match(result.stdout, /Skills considered: codebase-scanning/);
|
|
78
|
+
assert.match(result.stdout, /Skills active: codebase-scanning/);
|
|
79
|
+
assert.match(result.stdout, /Execution state: not bootstrapped/);
|
|
80
|
+
assert.match(result.stdout, /Stage validation: not run yet/);
|
|
81
|
+
assert.match(result.stdout, /Manual bootstrap: ready/);
|
|
82
|
+
assert.match(result.stdout, /Bootstrap prompt: Read \.prodify\/AGENTS\.md/);
|
|
83
|
+
assert.match(result.stdout, /Recommended next action: prodify setup-agent <agent>/);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('setup-agent configures a supported agent globally without repo-local writes', async () => {
|
|
87
|
+
const cwd = await createTempDir();
|
|
88
|
+
|
|
89
|
+
const result = await execCli(cwd, ['setup-agent', 'codex']);
|
|
90
|
+
|
|
91
|
+
assert.equal(result.exitCode, 0);
|
|
92
|
+
assert.match(result.stdout, /Prodify Agent Setup/);
|
|
93
|
+
assert.match(result.stdout, /Agent: codex/);
|
|
94
|
+
assert.match(result.stdout, /Repo impact: none/);
|
|
95
|
+
await fs.access(path.join(cwd, '.prodify-home', 'agent-setup.json'));
|
|
96
|
+
await assertMissing(cwd, '.prodify');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('multiple agents can be set up independently and status uses configured setup for bootstrap guidance', async () => {
|
|
100
|
+
const repoRoot = await createTempRepo();
|
|
101
|
+
|
|
102
|
+
await execCli(repoRoot, ['setup-agent', 'codex']);
|
|
103
|
+
await execCli(repoRoot, ['setup-agent', 'claude']);
|
|
104
|
+
await execCli(repoRoot, ['init']);
|
|
105
|
+
|
|
106
|
+
const result = await execCli(repoRoot, ['status', '--agent', 'claude']);
|
|
107
|
+
|
|
108
|
+
assert.equal(result.exitCode, 0);
|
|
109
|
+
assert.match(result.stdout, /Global agent setup: claude, codex/);
|
|
110
|
+
assert.match(result.stdout, /Skill routing stage: understand/);
|
|
111
|
+
assert.match(result.stdout, /Bootstrap profile: claude/);
|
|
112
|
+
assert.match(result.stdout, /tell your agent:/);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('doctor validates healthy setup after init', async () => {
|
|
116
|
+
const repoRoot = await createTempRepo();
|
|
117
|
+
await execCli(repoRoot, ['init']);
|
|
118
|
+
|
|
119
|
+
const result = await execCli(repoRoot, ['doctor']);
|
|
120
|
+
|
|
121
|
+
assert.equal(result.exitCode, 0);
|
|
122
|
+
assert.match(result.stdout, /canonical: PASS/);
|
|
123
|
+
assert.match(result.stdout, /contracts\/source: PASS/);
|
|
124
|
+
assert.match(result.stdout, /contracts\/compiled: PASS/);
|
|
125
|
+
assert.match(result.stdout, /canonical\/schema: PASS/);
|
|
126
|
+
assert.match(result.stdout, /runtime\/state: PASS/);
|
|
127
|
+
assert.match(result.stdout, /gitignore\/prodify: PASS/);
|
|
128
|
+
assert.match(result.stdout, /bootstrap\/guidance: PASS/);
|
|
129
|
+
assert.doesNotMatch(result.stdout, /compatibility\//);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('update is a no-op refresh on a current repo', async () => {
|
|
133
|
+
const repoRoot = await createTempRepo();
|
|
134
|
+
await execCli(repoRoot, ['init']);
|
|
135
|
+
|
|
136
|
+
const result = await execCli(repoRoot, ['update']);
|
|
137
|
+
|
|
138
|
+
assert.equal(result.exitCode, 0);
|
|
139
|
+
assert.match(result.stdout, /Prodify Update/);
|
|
140
|
+
assert.match(result.stdout, /Version\/schema: current/);
|
|
141
|
+
assert.match(result.stdout, /Canonical assets: /);
|
|
142
|
+
assert.match(result.stdout, /Compiled contracts: 6/);
|
|
143
|
+
assert.match(result.stdout, /Legacy compatibility adapters: not part of the default flow/);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('update repairs outdated version metadata and restores .prodify runtime assets only', async () => {
|
|
147
|
+
const repoRoot = await createTempRepo();
|
|
148
|
+
await execCli(repoRoot, ['init']);
|
|
149
|
+
|
|
150
|
+
await fs.writeFile(path.join(repoRoot, '.prodify', 'version.json'), '{\n "schema_version": "1",\n "preset_name": "default",\n "preset_version": "1.0.0"\n}\n', 'utf8');
|
|
151
|
+
await fs.rm(path.join(repoRoot, '.prodify', 'runtime-commands.md'));
|
|
152
|
+
|
|
153
|
+
const result = await execCli(repoRoot, ['update']);
|
|
154
|
+
|
|
155
|
+
assert.equal(result.exitCode, 0);
|
|
156
|
+
assert.match(result.stdout, /Version\/schema: outdated/);
|
|
157
|
+
await fs.access(path.join(repoRoot, '.prodify', 'runtime-commands.md'));
|
|
158
|
+
await assertMissing(repoRoot, 'AGENTS.md');
|
|
159
|
+
await assertMissing(repoRoot, 'CLAUDE.md');
|
|
160
|
+
await assertMissing(repoRoot, '.github/copilot-instructions.md');
|
|
161
|
+
await assertMissing(repoRoot, '.opencode/AGENTS.md');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('update preserves user-owned canonical files', async () => {
|
|
165
|
+
const repoRoot = await createTempRepo();
|
|
166
|
+
await execCli(repoRoot, ['init']);
|
|
167
|
+
|
|
168
|
+
await fs.writeFile(path.join(repoRoot, '.prodify', 'AGENTS.md'), '# Canonical\n\nCustom operator guidance.\n', 'utf8');
|
|
169
|
+
await fs.writeFile(path.join(repoRoot, '.prodify', 'project.md'), '# Project\n\nCustom project context.\n', 'utf8');
|
|
170
|
+
|
|
171
|
+
const result = await execCli(repoRoot, ['update']);
|
|
172
|
+
|
|
173
|
+
assert.equal(result.exitCode, 0);
|
|
174
|
+
assert.match(await readRepoFile(repoRoot, '.prodify/AGENTS.md'), /Custom operator guidance/);
|
|
175
|
+
assert.match(await readRepoFile(repoRoot, '.prodify/project.md'), /Custom project context/);
|
|
176
|
+
await assertMissing(repoRoot, 'AGENTS.md');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('source-only contract edits are detected until compiled contracts are refreshed', async () => {
|
|
180
|
+
const repoRoot = await createTempRepo();
|
|
181
|
+
await execCli(repoRoot, ['init']);
|
|
182
|
+
|
|
183
|
+
await fs.appendFile(path.join(repoRoot, '.prodify', 'contracts-src', 'understand.contract.md'), '\nAdditional editing note.\n', 'utf8');
|
|
184
|
+
|
|
185
|
+
const result = await execCli(repoRoot, ['status']);
|
|
186
|
+
|
|
187
|
+
assert.equal(result.exitCode, 1);
|
|
188
|
+
assert.match(result.stdout, /Contract freshness: stale: understand/);
|
|
189
|
+
assert.match(result.stdout, /Recommended next action: prodify update/);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('status reports malformed runtime state and recommends update', async () => {
|
|
193
|
+
const repoRoot = await createTempRepo();
|
|
194
|
+
await execCli(repoRoot, ['init']);
|
|
195
|
+
await fs.writeFile(path.join(repoRoot, '.prodify', 'state.json'), '{bad json\n', 'utf8');
|
|
196
|
+
|
|
197
|
+
const result = await execCli(repoRoot, ['status']);
|
|
198
|
+
|
|
199
|
+
assert.equal(result.exitCode, 1);
|
|
200
|
+
assert.match(result.stdout, /Runtime state: Runtime state is malformed/);
|
|
201
|
+
assert.match(result.stdout, /Recommended next action: prodify update/);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test('doctor fails clearly when .gitignore hides .prodify', async () => {
|
|
205
|
+
const repoRoot = await createTempRepo();
|
|
206
|
+
await execCli(repoRoot, ['init']);
|
|
207
|
+
await fs.writeFile(path.join(repoRoot, '.gitignore'), '.prodify/\n', 'utf8');
|
|
208
|
+
|
|
209
|
+
const result = await execCli(repoRoot, ['doctor']);
|
|
210
|
+
|
|
211
|
+
assert.equal(result.exitCode, 1);
|
|
212
|
+
assert.match(result.stdout, /gitignore\/prodify: FAIL/);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('runtime bootstrap and resume readiness are reflected in status', async () => {
|
|
216
|
+
const repoRoot = await createTempRepo();
|
|
217
|
+
await execCli(repoRoot, ['setup-agent', 'codex']);
|
|
218
|
+
await execCli(repoRoot, ['init']);
|
|
219
|
+
|
|
220
|
+
const initial = await readRuntimeState(repoRoot, {
|
|
221
|
+
presetMetadata: {
|
|
222
|
+
name: 'default',
|
|
223
|
+
version: '4.0.0',
|
|
224
|
+
schemaVersion: '4'
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
const bootstrapped = bootstrapFlowState(initial, {
|
|
228
|
+
agent: 'codex',
|
|
229
|
+
mode: 'interactive'
|
|
230
|
+
});
|
|
231
|
+
const running = startFlowExecution(bootstrapped);
|
|
232
|
+
await writeRuntimeState(repoRoot, running);
|
|
233
|
+
|
|
234
|
+
const result = await execCli(repoRoot, ['status']);
|
|
235
|
+
|
|
236
|
+
assert.equal(result.exitCode, 0);
|
|
237
|
+
assert.match(result.stdout, /Workspace health: healthy/);
|
|
238
|
+
assert.match(result.stdout, /Repo runtime binding: agent-agnostic/);
|
|
239
|
+
assert.match(result.stdout, /Global agent setup: codex/);
|
|
240
|
+
assert.match(result.stdout, /Skill routing stage: understand/);
|
|
241
|
+
assert.match(result.stdout, /Skills active: codebase-scanning/);
|
|
242
|
+
assert.match(result.stdout, /Execution state: understand_pending at understand \(01-understand\)/);
|
|
243
|
+
assert.match(result.stdout, /Stage validation: not run yet/);
|
|
244
|
+
assert.match(result.stdout, /Resumable: yes/);
|
|
245
|
+
assert.match(result.stdout, /Recommended next action: \$prodify-resume/);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('status distinguishes stage-validation failure from workspace health issues', async () => {
|
|
249
|
+
const repoRoot = await createTempRepo();
|
|
250
|
+
await execCli(repoRoot, ['setup-agent', 'codex']);
|
|
251
|
+
await execCli(repoRoot, ['init']);
|
|
252
|
+
|
|
253
|
+
const initial = await readRuntimeState(repoRoot, {
|
|
254
|
+
presetMetadata: {
|
|
255
|
+
name: 'default',
|
|
256
|
+
version: '4.0.0',
|
|
257
|
+
schemaVersion: '4'
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
const bootstrapped = bootstrapFlowState(initial, {
|
|
261
|
+
agent: 'codex',
|
|
262
|
+
mode: 'interactive'
|
|
263
|
+
});
|
|
264
|
+
const running = startFlowExecution(bootstrapped);
|
|
265
|
+
const failed = failFlowStage(running, {
|
|
266
|
+
reason: 'Required artifact .prodify/artifacts/01-understand.md is missing.'
|
|
267
|
+
});
|
|
268
|
+
await writeRuntimeState(repoRoot, failed);
|
|
269
|
+
|
|
270
|
+
const result = await execCli(repoRoot, ['status']);
|
|
271
|
+
|
|
272
|
+
assert.equal(result.exitCode, 1);
|
|
273
|
+
assert.match(result.stdout, /Workspace health: healthy/);
|
|
274
|
+
assert.match(result.stdout, /Stage validation: failed at understand:/);
|
|
275
|
+
assert.match(result.stdout, /Recommended next action: rerun or remediate stage outputs/);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('status can render a deterministic bootstrap prompt for a requested profile', async () => {
|
|
279
|
+
const repoRoot = await createTempRepo();
|
|
280
|
+
await execCli(repoRoot, ['setup-agent', 'claude']);
|
|
281
|
+
await execCli(repoRoot, ['init']);
|
|
282
|
+
|
|
283
|
+
const result = await execCli(repoRoot, ['status', '--agent', 'claude']);
|
|
284
|
+
|
|
285
|
+
assert.equal(result.exitCode, 0);
|
|
286
|
+
assert.match(result.stdout, /Bootstrap profile: claude/);
|
|
287
|
+
assert.match(result.stdout, /Bootstrap prompt: Read \.prodify\/AGENTS\.md/);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test('canonical runtime instructions live inside .prodify guidance', async () => {
|
|
291
|
+
const repoRoot = await createTempRepo();
|
|
292
|
+
await execCli(repoRoot, ['init']);
|
|
293
|
+
|
|
294
|
+
const guidance = await readRepoFile(repoRoot, '.prodify/AGENTS.md');
|
|
295
|
+
const runtimeCommands = await readRepoFile(repoRoot, '.prodify/runtime-commands.md');
|
|
296
|
+
|
|
297
|
+
assert.match(guidance, /Read \.prodify\/AGENTS\.md and bootstrap Prodify/);
|
|
298
|
+
assert.match(guidance, /\.prodify\/contracts-src\//);
|
|
299
|
+
assert.match(guidance, /prodify setup-agent/);
|
|
300
|
+
assert.match(guidance, /\$prodify-init/);
|
|
301
|
+
assert.match(runtimeCommands, /\$prodify-execute/);
|
|
302
|
+
assert.match(runtimeCommands, /prodify setup-agent/);
|
|
303
|
+
assert.match(runtimeCommands, /compiled-contract validation/i);
|
|
304
|
+
assert.match(runtimeCommands, /\$prodify-resume/);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test('no command path can create root-level legacy adapter files', async () => {
|
|
308
|
+
const repoRoot = await createTempRepo();
|
|
309
|
+
await execCli(repoRoot, ['init']);
|
|
310
|
+
await execCli(repoRoot, ['update']);
|
|
311
|
+
|
|
312
|
+
await assertMissing(repoRoot, 'AGENTS.md');
|
|
313
|
+
await assertMissing(repoRoot, 'CLAUDE.md');
|
|
314
|
+
await assertMissing(repoRoot, '.github/copilot-instructions.md');
|
|
315
|
+
await assertMissing(repoRoot, '.opencode/AGENTS.md');
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test('removed legacy commands are not available anymore', async () => {
|
|
319
|
+
const repoRoot = await createTempRepo();
|
|
320
|
+
const result = await execCli(repoRoot, ['install', '--agent', 'codex']);
|
|
321
|
+
|
|
322
|
+
assert.equal(result.exitCode, 1);
|
|
323
|
+
assert.match(result.stderr, /Unknown command: install/);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test('README and help output match the lifecycle model', async () => {
|
|
327
|
+
const repoRoot = await createTempRepo();
|
|
328
|
+
const readme = await fs.readFile(path.join(process.cwd(), 'README.md'), 'utf8');
|
|
329
|
+
const compatibilityTargets = await fs.readFile(path.join(process.cwd(), 'docs', 'compatibility-targets.md'), 'utf8');
|
|
330
|
+
const codexSupport = await fs.readFile(path.join(process.cwd(), 'docs', 'codex-support.md'), 'utf8');
|
|
331
|
+
const help = await execCli(repoRoot, ['--help']);
|
|
332
|
+
|
|
333
|
+
assert.match(readme, /prodify setup-agent codex/);
|
|
334
|
+
assert.match(readme, /prodify status/);
|
|
335
|
+
assert.match(readme, /read `\.prodify\/AGENTS\.md`/i);
|
|
336
|
+
assert.match(readme, /`\.prodify\/skills\/`/);
|
|
337
|
+
assert.match(readme, /No root-level agent files are required/i);
|
|
338
|
+
assert.match(readme, /Repo initialization stays agent-agnostic/i);
|
|
339
|
+
assert.match(readme, /root `AGENTS\.md`.*repository-local contributor guidance/i);
|
|
340
|
+
assert.doesNotMatch(readme, /prodify install --agent/);
|
|
341
|
+
assert.match(compatibilityTargets, /do not create root-level compatibility files/i);
|
|
342
|
+
assert.match(codexSupport, /not part of the default lifecycle/i);
|
|
343
|
+
assert.match(help.stdout, /prodify setup-agent/);
|
|
344
|
+
assert.match(help.stdout, /prodify status/);
|
|
345
|
+
assert.match(help.stdout, /prodify update/);
|
|
346
|
+
assert.doesNotMatch(help.stdout, /prodify install/);
|
|
347
|
+
});
|