@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,77 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { listFilesRecursive, pathExists } from './fs.js';
|
|
3
|
+
import { resolveRepoPath } from './paths.js';
|
|
4
|
+
function sortedUnique(values) {
|
|
5
|
+
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
6
|
+
}
|
|
7
|
+
async function readPackageJson(repoRoot) {
|
|
8
|
+
const packageJsonPath = resolveRepoPath(repoRoot, 'package.json');
|
|
9
|
+
if (!(await pathExists(packageJsonPath))) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function dependencyEntries(packageJson) {
|
|
20
|
+
if (!packageJson) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
const sections = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
|
|
24
|
+
const names = [];
|
|
25
|
+
for (const section of sections) {
|
|
26
|
+
const record = packageJson[section];
|
|
27
|
+
if (typeof record !== 'object' || record === null || Array.isArray(record)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
names.push(...Object.keys(record));
|
|
31
|
+
}
|
|
32
|
+
return sortedUnique(names);
|
|
33
|
+
}
|
|
34
|
+
export async function detectRepoContext(repoRoot) {
|
|
35
|
+
const packageJson = await readPackageJson(repoRoot);
|
|
36
|
+
const dependencies = dependencyEntries(packageJson);
|
|
37
|
+
const files = await listFilesRecursive(repoRoot);
|
|
38
|
+
const relativePaths = files.map((entry) => entry.relativePath.replaceAll('\\', '/'));
|
|
39
|
+
const hasTsConfig = relativePaths.includes('tsconfig.json');
|
|
40
|
+
const hasTypeScriptSources = relativePaths.some((relativePath) => relativePath.endsWith('.ts') || relativePath.endsWith('.tsx'));
|
|
41
|
+
const hasJavaScriptSources = relativePaths.some((relativePath) => relativePath.endsWith('.js') || relativePath.endsWith('.jsx'));
|
|
42
|
+
const hasTests = relativePaths.some((relativePath) => relativePath.startsWith('tests/') || relativePath.includes('.test.'));
|
|
43
|
+
const hasCliEntrypoint = relativePaths.includes('src/cli.ts')
|
|
44
|
+
|| relativePaths.includes('src/cli.js')
|
|
45
|
+
|| relativePaths.includes('bin/prodify');
|
|
46
|
+
const hasCoreDir = relativePaths.some((relativePath) => relativePath.startsWith('src/core/'));
|
|
47
|
+
const hasCommandsDir = relativePaths.some((relativePath) => relativePath.startsWith('src/commands/'));
|
|
48
|
+
const languages = sortedUnique([
|
|
49
|
+
...(hasTypeScriptSources || hasTsConfig ? ['typescript'] : []),
|
|
50
|
+
...(hasJavaScriptSources || packageJson ? ['javascript'] : [])
|
|
51
|
+
]);
|
|
52
|
+
const frameworks = sortedUnique([
|
|
53
|
+
...(dependencies.includes('react') || dependencies.includes('react-dom') ? ['react'] : [])
|
|
54
|
+
]);
|
|
55
|
+
const projectTypes = sortedUnique([
|
|
56
|
+
...(packageJson ? ['node-package'] : []),
|
|
57
|
+
...(hasCliEntrypoint ? ['cli'] : []),
|
|
58
|
+
...(frameworks.includes('react') ? ['frontend-app'] : []),
|
|
59
|
+
...(!frameworks.includes('react') && hasCoreDir ? ['backend-service'] : [])
|
|
60
|
+
]);
|
|
61
|
+
const architecturePatterns = sortedUnique([
|
|
62
|
+
...(hasCoreDir && hasCommandsDir ? ['layered-cli'] : []),
|
|
63
|
+
...(relativePaths.some((relativePath) => relativePath.startsWith('src/contracts/')) ? ['contract-driven-runtime'] : [])
|
|
64
|
+
]);
|
|
65
|
+
const riskSignals = sortedUnique([
|
|
66
|
+
...(dependencies.length > 0 ? ['external-dependencies'] : []),
|
|
67
|
+
...(hasCliEntrypoint ? ['cli-surface'] : []),
|
|
68
|
+
...(!hasTests ? ['missing-tests'] : [])
|
|
69
|
+
]);
|
|
70
|
+
return {
|
|
71
|
+
languages,
|
|
72
|
+
frameworks,
|
|
73
|
+
project_types: projectTypes,
|
|
74
|
+
architecture_patterns: architecturePatterns,
|
|
75
|
+
risk_signals: riskSignals
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { ProdifyError } from './errors.js';
|
|
4
|
+
async function isDirectory(targetPath) {
|
|
5
|
+
try {
|
|
6
|
+
return (await fs.stat(targetPath)).isDirectory();
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
async function directoryHas(directory, childName) {
|
|
13
|
+
return isDirectory(path.join(directory, childName));
|
|
14
|
+
}
|
|
15
|
+
async function searchUpwards(startDir, predicate) {
|
|
16
|
+
let current = path.resolve(startDir);
|
|
17
|
+
while (true) {
|
|
18
|
+
if (await predicate(current)) {
|
|
19
|
+
return current;
|
|
20
|
+
}
|
|
21
|
+
const parent = path.dirname(current);
|
|
22
|
+
if (parent === current) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
current = parent;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function resolveRepoRoot(options = {}) {
|
|
29
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
30
|
+
const explicitRepo = options.repoRoot ? path.resolve(options.repoRoot) : null;
|
|
31
|
+
const allowBootstrap = options.allowBootstrap ?? false;
|
|
32
|
+
if (explicitRepo) {
|
|
33
|
+
const hasProdify = await directoryHas(explicitRepo, '.prodify');
|
|
34
|
+
const hasGit = await directoryHas(explicitRepo, '.git');
|
|
35
|
+
if (!hasProdify && !(allowBootstrap && hasGit)) {
|
|
36
|
+
throw new ProdifyError(`Could not verify repository root at ${explicitRepo}.`, {
|
|
37
|
+
code: 'REPO_ROOT_NOT_FOUND'
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return explicitRepo;
|
|
41
|
+
}
|
|
42
|
+
const prodifyRoot = await searchUpwards(cwd, async (candidate) => directoryHas(candidate, '.prodify'));
|
|
43
|
+
if (prodifyRoot) {
|
|
44
|
+
return prodifyRoot;
|
|
45
|
+
}
|
|
46
|
+
if (allowBootstrap) {
|
|
47
|
+
const gitRoot = await searchUpwards(cwd, async (candidate) => directoryHas(candidate, '.git'));
|
|
48
|
+
if (gitRoot) {
|
|
49
|
+
return gitRoot;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
throw new ProdifyError('Could not resolve repository root from the current working directory.', {
|
|
53
|
+
code: 'REPO_ROOT_NOT_FOUND'
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { loadCompiledContract } from '../contracts/compiler.js';
|
|
2
|
+
import { ProdifyError } from './errors.js';
|
|
3
|
+
import { detectRepoContext } from './repo-context.js';
|
|
4
|
+
import { loadSkillRegistry } from '../skills/loader.js';
|
|
5
|
+
function factValues(context, fact) {
|
|
6
|
+
switch (fact) {
|
|
7
|
+
case 'language':
|
|
8
|
+
return context.languages;
|
|
9
|
+
case 'framework':
|
|
10
|
+
return context.frameworks;
|
|
11
|
+
case 'project_type':
|
|
12
|
+
return context.project_types;
|
|
13
|
+
case 'architecture_pattern':
|
|
14
|
+
return context.architecture_patterns;
|
|
15
|
+
case 'risk_signal':
|
|
16
|
+
return context.risk_signals;
|
|
17
|
+
default:
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function formatCondition(condition) {
|
|
22
|
+
return condition.all.map((predicate) => `${predicate.fact}=${predicate.includes}`).join(' and ');
|
|
23
|
+
}
|
|
24
|
+
function formatSkillActivationConditions(skill) {
|
|
25
|
+
return skill.activation_conditions.map((condition) => formatCondition(condition)).join(' or ');
|
|
26
|
+
}
|
|
27
|
+
function matchesCondition(context, condition) {
|
|
28
|
+
return condition.all.every((predicate) => factValues(context, predicate.fact).includes(predicate.includes));
|
|
29
|
+
}
|
|
30
|
+
function matchesSkillActivationConditions(context, skill) {
|
|
31
|
+
if (skill.activation_conditions.length === 0) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return skill.activation_conditions.some((condition) => matchesCondition(context, condition));
|
|
35
|
+
}
|
|
36
|
+
function assertStageCompatible(skill, stage) {
|
|
37
|
+
if (!skill.stage_compatibility.includes(stage)) {
|
|
38
|
+
throw new ProdifyError(`Skill "${skill.id}" is not compatible with stage "${stage}".`, {
|
|
39
|
+
code: 'SKILL_STAGE_INCOMPATIBLE'
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function resolveSkill(registry, skillId, stage) {
|
|
44
|
+
const skill = registry.get(skillId);
|
|
45
|
+
if (!skill) {
|
|
46
|
+
throw new ProdifyError(`Stage skill routing references unknown skill "${skillId}".`, {
|
|
47
|
+
code: 'SKILL_NOT_FOUND'
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
assertStageCompatible(skill, stage);
|
|
51
|
+
return skill;
|
|
52
|
+
}
|
|
53
|
+
function defaultRecord(skill, active) {
|
|
54
|
+
return {
|
|
55
|
+
id: skill.id,
|
|
56
|
+
name: skill.name,
|
|
57
|
+
category: skill.category,
|
|
58
|
+
source: 'default',
|
|
59
|
+
active,
|
|
60
|
+
reason: active ? 'default stage skill' : `inactive: ${formatSkillActivationConditions(skill)}`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function conditionalRecord(skill, conditionLabel, active) {
|
|
64
|
+
return {
|
|
65
|
+
id: skill.id,
|
|
66
|
+
name: skill.name,
|
|
67
|
+
category: skill.category,
|
|
68
|
+
source: 'conditional',
|
|
69
|
+
active,
|
|
70
|
+
reason: active ? conditionLabel : `inactive: ${conditionLabel}`
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function validateRouting(routing, registry, stage) {
|
|
74
|
+
for (const skillId of routing.allowed_skills) {
|
|
75
|
+
resolveSkill(registry, skillId, stage);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export async function resolveStageSkills(repoRoot, stage) {
|
|
79
|
+
const [contract, registry, context] = await Promise.all([
|
|
80
|
+
loadCompiledContract(repoRoot, stage),
|
|
81
|
+
loadSkillRegistry(repoRoot),
|
|
82
|
+
detectRepoContext(repoRoot)
|
|
83
|
+
]);
|
|
84
|
+
const routing = contract.skill_routing;
|
|
85
|
+
validateRouting(routing, registry, stage);
|
|
86
|
+
const consideredSkills = [];
|
|
87
|
+
const activeSkillIds = new Set();
|
|
88
|
+
for (const skillId of routing.default_skills) {
|
|
89
|
+
const skill = resolveSkill(registry, skillId, stage);
|
|
90
|
+
const active = matchesSkillActivationConditions(context, skill);
|
|
91
|
+
consideredSkills.push(defaultRecord(skill, active));
|
|
92
|
+
if (active) {
|
|
93
|
+
activeSkillIds.add(skill.id);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
for (const rule of routing.conditional_skills) {
|
|
97
|
+
const skill = resolveSkill(registry, rule.skill, stage);
|
|
98
|
+
const conditionLabel = rule.reason || formatCondition(rule.when);
|
|
99
|
+
const active = matchesCondition(context, rule.when) && matchesSkillActivationConditions(context, skill);
|
|
100
|
+
const existing = consideredSkills.find((entry) => entry.id === skill.id);
|
|
101
|
+
if (existing) {
|
|
102
|
+
if (active) {
|
|
103
|
+
existing.active = true;
|
|
104
|
+
existing.reason = `${existing.reason}; ${conditionLabel}`;
|
|
105
|
+
activeSkillIds.add(skill.id);
|
|
106
|
+
}
|
|
107
|
+
else if (!existing.active && skill.activation_conditions.length > 0) {
|
|
108
|
+
existing.reason = `inactive: ${formatSkillActivationConditions(skill)}`;
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
consideredSkills.push(conditionalRecord(skill, conditionLabel, active));
|
|
113
|
+
if (active) {
|
|
114
|
+
activeSkillIds.add(skill.id);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
stage,
|
|
119
|
+
context,
|
|
120
|
+
considered_skills: consideredSkills.sort((left, right) => left.id.localeCompare(right.id)),
|
|
121
|
+
active_skill_ids: [...activeSkillIds].sort((left, right) => left.localeCompare(right))
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { ProdifyError } from './errors.js';
|
|
3
|
+
import { pathExists, writeFileEnsuringDir } from './fs.js';
|
|
4
|
+
import { resolveCanonicalPath } from './paths.js';
|
|
5
|
+
export const RUNTIME_STATE_SCHEMA_VERSION = '2';
|
|
6
|
+
export const RUNTIME_STATUS = {
|
|
7
|
+
NOT_BOOTSTRAPPED: 'not_bootstrapped',
|
|
8
|
+
READY: 'ready',
|
|
9
|
+
RUNNING: 'running',
|
|
10
|
+
AWAITING_VALIDATION: 'awaiting_validation',
|
|
11
|
+
BLOCKED: 'blocked',
|
|
12
|
+
FAILED: 'failed',
|
|
13
|
+
COMPLETE: 'complete'
|
|
14
|
+
};
|
|
15
|
+
function isExecutionMode(value) {
|
|
16
|
+
return value === 'interactive' || value === 'auto';
|
|
17
|
+
}
|
|
18
|
+
function isFlowStage(value) {
|
|
19
|
+
return value === 'understand'
|
|
20
|
+
|| value === 'diagnose'
|
|
21
|
+
|| value === 'architecture'
|
|
22
|
+
|| value === 'plan'
|
|
23
|
+
|| value === 'refactor'
|
|
24
|
+
|| value === 'validate';
|
|
25
|
+
}
|
|
26
|
+
function isRuntimeStatus(value) {
|
|
27
|
+
return value === 'not_bootstrapped'
|
|
28
|
+
|| value === 'ready'
|
|
29
|
+
|| value === 'running'
|
|
30
|
+
|| value === 'awaiting_validation'
|
|
31
|
+
|| value === 'blocked'
|
|
32
|
+
|| value === 'failed'
|
|
33
|
+
|| value === 'complete';
|
|
34
|
+
}
|
|
35
|
+
function isValidationResult(value) {
|
|
36
|
+
return value === 'unknown' || value === 'pass' || value === 'fail' || value === 'inconclusive';
|
|
37
|
+
}
|
|
38
|
+
function isRuntimeContractState(value) {
|
|
39
|
+
return typeof value === 'string' && [
|
|
40
|
+
'not_bootstrapped',
|
|
41
|
+
'bootstrapped',
|
|
42
|
+
'understand_pending',
|
|
43
|
+
'understand_complete',
|
|
44
|
+
'diagnose_pending',
|
|
45
|
+
'diagnose_complete',
|
|
46
|
+
'architecture_pending',
|
|
47
|
+
'architecture_complete',
|
|
48
|
+
'plan_pending',
|
|
49
|
+
'plan_complete',
|
|
50
|
+
'refactor_pending',
|
|
51
|
+
'refactor_complete',
|
|
52
|
+
'validate_pending',
|
|
53
|
+
'validate_complete',
|
|
54
|
+
'blocked',
|
|
55
|
+
'failed',
|
|
56
|
+
'completed'
|
|
57
|
+
].includes(value);
|
|
58
|
+
}
|
|
59
|
+
function normalizeStageList(value) {
|
|
60
|
+
if (!Array.isArray(value)) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
return value.filter(isFlowStage);
|
|
64
|
+
}
|
|
65
|
+
function asRecord(value) {
|
|
66
|
+
return typeof value === 'object' && value !== null ? value : {};
|
|
67
|
+
}
|
|
68
|
+
function normalizeBootstrapMetadata(value) {
|
|
69
|
+
const record = asRecord(value);
|
|
70
|
+
return {
|
|
71
|
+
bootstrapped: Boolean(record.bootstrapped)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function normalizeFailureMetadata(value) {
|
|
75
|
+
if (!value) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const record = asRecord(value);
|
|
79
|
+
return {
|
|
80
|
+
stage: isFlowStage(record.stage) ? record.stage : null,
|
|
81
|
+
contract_version: typeof record.contract_version === 'string' ? record.contract_version : null,
|
|
82
|
+
reason: typeof record.reason === 'string' ? record.reason : 'unknown failure'
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function normalizeValidation(value) {
|
|
86
|
+
if (!value) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const record = asRecord(value);
|
|
90
|
+
return {
|
|
91
|
+
stage: isFlowStage(record.stage) ? record.stage : 'understand',
|
|
92
|
+
contract_version: typeof record.contract_version === 'string' ? record.contract_version : 'unknown',
|
|
93
|
+
passed: Boolean(record.passed),
|
|
94
|
+
violated_rules: Array.isArray(record.violated_rules)
|
|
95
|
+
? record.violated_rules
|
|
96
|
+
.map((entry) => asRecord(entry))
|
|
97
|
+
.map((entry) => ({
|
|
98
|
+
rule: typeof entry.rule === 'string' ? entry.rule : 'unknown',
|
|
99
|
+
message: typeof entry.message === 'string' ? entry.message : 'validation issue',
|
|
100
|
+
path: typeof entry.path === 'string' ? entry.path : undefined
|
|
101
|
+
}))
|
|
102
|
+
: [],
|
|
103
|
+
missing_artifacts: Array.isArray(record.missing_artifacts)
|
|
104
|
+
? record.missing_artifacts.filter((entry) => typeof entry === 'string')
|
|
105
|
+
: [],
|
|
106
|
+
warnings: Array.isArray(record.warnings)
|
|
107
|
+
? record.warnings.filter((entry) => typeof entry === 'string')
|
|
108
|
+
: [],
|
|
109
|
+
diagnostics: Array.isArray(record.diagnostics)
|
|
110
|
+
? record.diagnostics.filter((entry) => typeof entry === 'string')
|
|
111
|
+
: []
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function normalizeValidatedContractVersions(value) {
|
|
115
|
+
const record = asRecord(value);
|
|
116
|
+
const normalized = {};
|
|
117
|
+
for (const [key, rawValue] of Object.entries(record)) {
|
|
118
|
+
if (isFlowStage(key) && typeof rawValue === 'string') {
|
|
119
|
+
normalized[key] = rawValue;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return normalized;
|
|
123
|
+
}
|
|
124
|
+
export function createInitialRuntimeState({ presetMetadata }) {
|
|
125
|
+
return {
|
|
126
|
+
schema_version: RUNTIME_STATE_SCHEMA_VERSION,
|
|
127
|
+
preset_name: presetMetadata.name,
|
|
128
|
+
preset_version: presetMetadata.version,
|
|
129
|
+
runtime: {
|
|
130
|
+
status: RUNTIME_STATUS.NOT_BOOTSTRAPPED,
|
|
131
|
+
current_state: 'not_bootstrapped',
|
|
132
|
+
mode: null,
|
|
133
|
+
current_stage: null,
|
|
134
|
+
current_task_id: null,
|
|
135
|
+
pending_stage: null,
|
|
136
|
+
completed_stages: [],
|
|
137
|
+
awaiting_user_validation: false,
|
|
138
|
+
last_validation_result: 'unknown',
|
|
139
|
+
last_validation: null,
|
|
140
|
+
last_validated_contract_versions: {},
|
|
141
|
+
resumable: false,
|
|
142
|
+
blocked_reason: null,
|
|
143
|
+
failure_metadata: null,
|
|
144
|
+
bootstrap: {
|
|
145
|
+
bootstrapped: false
|
|
146
|
+
},
|
|
147
|
+
next_action: '$prodify-init',
|
|
148
|
+
timestamps: {
|
|
149
|
+
bootstrapped_at: null,
|
|
150
|
+
last_transition_at: null,
|
|
151
|
+
completed_at: null
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function normalizeRuntimeBlock(runtime) {
|
|
157
|
+
const record = asRecord(runtime);
|
|
158
|
+
const timestamps = asRecord(record.timestamps);
|
|
159
|
+
return {
|
|
160
|
+
status: isRuntimeStatus(record.status) ? record.status : RUNTIME_STATUS.NOT_BOOTSTRAPPED,
|
|
161
|
+
current_state: isRuntimeContractState(record.current_state) ? record.current_state : 'not_bootstrapped',
|
|
162
|
+
mode: isExecutionMode(record.mode) ? record.mode : null,
|
|
163
|
+
current_stage: isFlowStage(record.current_stage) ? record.current_stage : null,
|
|
164
|
+
current_task_id: typeof record.current_task_id === 'string' ? record.current_task_id : null,
|
|
165
|
+
pending_stage: isFlowStage(record.pending_stage) ? record.pending_stage : null,
|
|
166
|
+
completed_stages: normalizeStageList(record.completed_stages),
|
|
167
|
+
awaiting_user_validation: Boolean(record.awaiting_user_validation),
|
|
168
|
+
last_validation_result: isValidationResult(record.last_validation_result) ? record.last_validation_result : 'unknown',
|
|
169
|
+
last_validation: normalizeValidation(record.last_validation),
|
|
170
|
+
last_validated_contract_versions: normalizeValidatedContractVersions(record.last_validated_contract_versions),
|
|
171
|
+
resumable: Boolean(record.resumable),
|
|
172
|
+
blocked_reason: typeof record.blocked_reason === 'string' ? record.blocked_reason : null,
|
|
173
|
+
failure_metadata: normalizeFailureMetadata(record.failure_metadata),
|
|
174
|
+
bootstrap: normalizeBootstrapMetadata(record.bootstrap),
|
|
175
|
+
next_action: typeof record.next_action === 'string' ? record.next_action : '$prodify-init',
|
|
176
|
+
timestamps: {
|
|
177
|
+
bootstrapped_at: typeof timestamps.bootstrapped_at === 'string' ? timestamps.bootstrapped_at : null,
|
|
178
|
+
last_transition_at: typeof timestamps.last_transition_at === 'string' ? timestamps.last_transition_at : null,
|
|
179
|
+
completed_at: typeof timestamps.completed_at === 'string' ? timestamps.completed_at : null
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
export function normalizeRuntimeState(raw, { presetMetadata }) {
|
|
184
|
+
const record = asRecord(raw);
|
|
185
|
+
const base = createInitialRuntimeState({ presetMetadata });
|
|
186
|
+
return {
|
|
187
|
+
...base,
|
|
188
|
+
schema_version: base.schema_version,
|
|
189
|
+
preset_name: presetMetadata.name,
|
|
190
|
+
preset_version: presetMetadata.version,
|
|
191
|
+
runtime: normalizeRuntimeBlock(record.runtime)
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
export function serializeRuntimeState(state) {
|
|
195
|
+
return `${JSON.stringify(state, null, 2)}\n`;
|
|
196
|
+
}
|
|
197
|
+
export async function readRuntimeState(repoRoot, { allowMissing = false, presetMetadata }) {
|
|
198
|
+
const statePath = resolveCanonicalPath(repoRoot, '.prodify/state.json');
|
|
199
|
+
if (!(await pathExists(statePath))) {
|
|
200
|
+
if (allowMissing) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
throw new ProdifyError('Runtime state is missing: .prodify/state.json', {
|
|
204
|
+
code: 'RUNTIME_STATE_MISSING'
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const raw = JSON.parse(await fs.readFile(statePath, 'utf8'));
|
|
209
|
+
return normalizeRuntimeState(raw, { presetMetadata });
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
throw new ProdifyError('Runtime state is malformed: .prodify/state.json', {
|
|
213
|
+
code: 'RUNTIME_STATE_MALFORMED'
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
export async function writeRuntimeState(repoRoot, state) {
|
|
218
|
+
const statePath = resolveCanonicalPath(repoRoot, '.prodify/state.json');
|
|
219
|
+
await writeFileEnsuringDir(statePath, serializeRuntimeState(state));
|
|
220
|
+
}
|