@williambeto/ai-workflow 2.4.8 → 2.6.3
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/bin/ai-workflow.d.ts +3 -0
- package/bin/ai-workflow.js +2669 -1
- package/bin/ai-workflow.js.map +1 -0
- package/chunk-2AOV2ATY.js +749 -0
- package/chunk-5WRI5ZAA.js +31 -0
- package/chunk-6EMG6FAO.js +734 -0
- package/chunk-UHLZX2EG.js +1827 -0
- package/core/index.d.ts +440 -0
- package/core/index.js +32 -0
- package/core/index.js.map +1 -0
- package/dist-assets/AGENTS.md +23 -6
- package/dist-assets/agents/atlas.md +8 -8
- package/dist-assets/agents/nexus.md +4 -4
- package/dist-assets/agents/orion.md +1 -1
- package/dist-assets/agents/phoenix.md +1 -1
- package/dist-assets/agents/sage.md +1 -1
- package/dist-assets/commands/implement.md +1 -1
- package/dist-assets/docs/QUICKSTART.md +8 -8
- package/dist-assets/docs/cli-reference.md +7 -7
- package/dist-assets/docs/compatibility/provider-usage.md +1 -9
- package/dist-assets/docs/compatibility/runtime-matrix.md +5 -6
- package/dist-assets/docs/consumer-onboarding.md +2 -2
- package/dist-assets/docs/design-patterns-policy.md +1 -1
- package/dist-assets/docs/full-documentation.md +0 -2
- package/dist-assets/docs/npm-consumer-quickstart.md +70 -109
- package/dist-assets/docs/policies/01-BRANCH_GATE.md +1 -1
- package/dist-assets/docs/policies/06-FINAL_EVIDENCE_CONTRACT.md +2 -2
- package/dist-assets/docs/policies/08-PRODUCT_TRUTHFULNESS_AND_PROJECT_DOCS.md +1 -1
- package/dist-assets/docs/policies/10-BEHAVIORAL_CONTRACT_HARDENING.md +1 -1
- package/dist-assets/docs/policies/ORCHESTRATION_PROTOCOL.md +8 -9
- package/dist-assets/docs/policies/PROCEDURE_DELIVERY_ARTIFACTS.md +4 -10
- package/dist-assets/docs/policies/SKILLS_COMMON_GOVERNANCE.md +1 -1
- package/dist-assets/docs/profiles/README.md +2 -2
- package/dist-assets/docs/specs/runtime-operational-contract.md +12 -12
- package/dist-assets/docs/troubleshooting-guide.md +2 -2
- package/dist-assets/docs/visual-validation-guide.md +65 -3
- package/dist-assets/prompts/01-create-requirement.md +2 -2
- package/dist-assets/prompts/02-create-spec.md +2 -2
- package/dist-assets/prompts/03-create-tech-plan.md +4 -4
- package/dist-assets/prompts/04-breakdown-prs.md +4 -4
- package/dist-assets/prompts/05-implement-pr.md +5 -5
- package/dist-assets/prompts/06-review-and-fix.md +3 -3
- package/dist-assets/prompts/07-apply-design.md +2 -2
- package/dist-assets/prompts/08-validate.md +5 -5
- package/dist-assets/prompts/09-deploy.md +5 -5
- package/dist-assets/prompts/commands/implement.md +1 -1
- package/dist-assets/prompts/commands/tech-plan.md +2 -2
- package/dist-assets/prompts/commands/validate.md +2 -2
- package/dist-assets/runbooks/deploy-checklist.md +5 -5
- package/dist-assets/runbooks/how-to-use-skills.md +10 -0
- package/dist-assets/runbooks/validation-checklist.md +5 -5
- package/dist-assets/schemas/evidence.schema.json +109 -0
- package/dist-assets/templates/ANTIGRAVITY.md.template +7 -5
- package/dist-assets/templates/CLAUDE.md.template +7 -5
- package/dist-assets/templates/CODEX.md.template +7 -5
- package/evidence-validator-76ZQQYDU.js +6661 -0
- package/package.json +7 -65
- package/{src/core/workspace/read-only-workspace.js → read-only-workspace-PZBE7KAX.js} +14 -31
- package/{src/commands/validate.js → validate-Q2NLLQ5G.js} +30 -22
- package/AGENTS.md +0 -34
- package/CHANGELOG.md +0 -237
- package/LICENSE +0 -21
- package/PUBLISH_MANIFEST.json +0 -34
- package/README.md +0 -129
- package/dist-assets/runbooks/use-linear-for-operational-planning.md +0 -45
- package/dist-assets/runbooks/use-napkin-project-memory.md +0 -33
- package/dist-assets/templates/.geminiignore.template +0 -8
- package/dist-assets/templates/GEMINI.md.template +0 -20
- package/docs/compatibility/provider-usage.md +0 -46
- package/docs/compatibility/runtime-matrix.md +0 -31
- package/docs/getting-started/DESKTOP_PROMPT.md +0 -52
- package/docs/getting-started/quickstart.md +0 -117
- package/docs/getting-started/troubleshooting.md +0 -123
- package/docs/getting-started/upgrading-to-v2.md +0 -62
- package/read_only_safety_verification.md +0 -48
- package/src/adapters/index.js +0 -4
- package/src/adapters/platforms/antigravity.js +0 -382
- package/src/adapters/platforms/claude.js +0 -126
- package/src/adapters/platforms/codex.js +0 -113
- package/src/adapters/platforms/gemini.js +0 -245
- package/src/cli.js +0 -155
- package/src/commands/clean.js +0 -106
- package/src/commands/collect-evidence.js +0 -25
- package/src/commands/doctor.js +0 -202
- package/src/commands/execute.js +0 -382
- package/src/commands/init.js +0 -359
- package/src/commands/run.js +0 -112
- package/src/core/backup.js +0 -65
- package/src/core/completion-contract.js +0 -35
- package/src/core/delegation-controller.js +0 -193
- package/src/core/evidence/evidence-ledger.js +0 -98
- package/src/core/execution-planner.js +0 -62
- package/src/core/filesystem.js +0 -141
- package/src/core/finalization/finalizer.js +0 -165
- package/src/core/finalization/workspace-snapshot.js +0 -110
- package/src/core/gates/branch-gate.js +0 -134
- package/src/core/gates/merge-gate.js +0 -74
- package/src/core/handoff/handoff-engine.js +0 -104
- package/src/core/healing/cli-remediation-executor.js +0 -153
- package/src/core/healing/healer-engine.js +0 -212
- package/src/core/healing/runtime-remediation-executor.js +0 -136
- package/src/core/identity.js +0 -43
- package/src/core/install-plan.js +0 -34
- package/src/core/opencode-merge.js +0 -150
- package/src/core/package-assets.js +0 -141
- package/src/core/request-classifier.js +0 -73
- package/src/core/request-router.js +0 -289
- package/src/core/runtime/opencode-adapter.js +0 -202
- package/src/core/sdd/validator.js +0 -67
- package/src/core/statuses.js +0 -29
- package/src/core/symlink-layout.js +0 -93
- package/src/core/templates.js +0 -229
- package/src/core/validation/artifact-fidelity-gate.js +0 -445
- package/src/core/validation/canonical-finalization.js +0 -43
- package/src/core/validation/delivery-decision-engine.js +0 -344
- package/src/core/validation/evidence-collector.js +0 -245
- package/src/core/validation/quality-guard.js +0 -251
- package/src/core/validation/stack-detector.js +0 -65
- package/src/core/validation/validation-planner.js +0 -134
- package/src/core/validation/visual-verifier.js +0 -175
- package/src/core/workflow-profiles.js +0 -107
- package/src/core/workflow-state-machine.js +0 -46
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import crypto from "node:crypto";
|
|
4
|
-
import { isRecoverableGateFailure } from "../statuses.js";
|
|
5
|
-
|
|
6
|
-
const MODE_ATTEMPTS = Object.freeze({ quick: 1, standard: 2, full: 3 });
|
|
7
|
-
|
|
8
|
-
function stableJson(value) {
|
|
9
|
-
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
10
|
-
if (value && typeof value === "object") {
|
|
11
|
-
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
|
|
12
|
-
}
|
|
13
|
-
return JSON.stringify(value);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function collectBlockingFindings(result = {}) {
|
|
17
|
-
const findings = [];
|
|
18
|
-
const add = (source, value) => {
|
|
19
|
-
if (!value || typeof value !== "object") return;
|
|
20
|
-
const status = value.status || value.overallStatus;
|
|
21
|
-
if (!["FAIL", "FAIL_QUALITY_GATE", "FAIL_DELEGATION_GATE", "BLOCKED"].includes(status)) return;
|
|
22
|
-
findings.push({
|
|
23
|
-
source,
|
|
24
|
-
status,
|
|
25
|
-
missing: Array.isArray(value.missing) ? [...value.missing].sort() : [],
|
|
26
|
-
reason: value.reason || value.summary || ""
|
|
27
|
-
});
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
add("result", result);
|
|
31
|
-
add("evidence", result.evidence);
|
|
32
|
-
add("quality", result.quality);
|
|
33
|
-
const checks = result.quality?.checks || result.policyValidation?.checks || result.checks || {};
|
|
34
|
-
for (const [name, check] of Object.entries(checks)) add(`check:${name}`, check);
|
|
35
|
-
return findings.sort((a, b) => a.source.localeCompare(b.source));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export class HealerEngine {
|
|
39
|
-
constructor({ cwd, mode = "standard", taskSlug = "implementation", maxAttempts = null, ledger = null } = {}) {
|
|
40
|
-
this.cwd = cwd;
|
|
41
|
-
this.mode = MODE_ATTEMPTS[mode] ? mode : "standard";
|
|
42
|
-
this.taskSlug = taskSlug;
|
|
43
|
-
this.maxAttempts = maxAttempts ?? MODE_ATTEMPTS[this.mode];
|
|
44
|
-
this.ledger = ledger;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
get statePath() {
|
|
48
|
-
return path.join(this.cwd, ".ai-workflow", `healing-state-${this.taskSlug}.json`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
fingerprint(result) {
|
|
52
|
-
return crypto.createHash("sha256").update(stableJson(collectBlockingFindings(result))).digest("hex");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
blockingCount(result) {
|
|
56
|
-
return collectBlockingFindings(result).length;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
affectedChecks(result) {
|
|
60
|
-
return collectBlockingFindings(result).map((finding) => finding.source);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
hasProgress(before, after, remediation = {}) {
|
|
64
|
-
if (!isRecoverableGateFailure(after.overallStatus)) return true;
|
|
65
|
-
const beforeCount = this.blockingCount(before);
|
|
66
|
-
const afterCount = this.blockingCount(after);
|
|
67
|
-
if (afterCount < beforeCount) return true;
|
|
68
|
-
if (this.fingerprint(before) !== this.fingerprint(after) && (remediation.changedFiles?.length || remediation.evidenceAdded?.length)) return true;
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async writeState(state) {
|
|
73
|
-
await fs.mkdir(path.dirname(this.statePath), { recursive: true });
|
|
74
|
-
await fs.writeFile(this.statePath, JSON.stringify(state, null, 2));
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async resetState() {
|
|
78
|
-
try { await fs.unlink(this.statePath); } catch {}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async writeRemediationRequest({ attempt, result }) {
|
|
82
|
-
const requestPath = path.join(this.cwd, ".ai-workflow", "remediation-request.json");
|
|
83
|
-
await fs.mkdir(path.dirname(requestPath), { recursive: true });
|
|
84
|
-
const request = {
|
|
85
|
-
taskSlug: this.taskSlug,
|
|
86
|
-
executionMode: this.mode,
|
|
87
|
-
attempt,
|
|
88
|
-
maxAttempts: this.maxAttempts,
|
|
89
|
-
status: result.overallStatus,
|
|
90
|
-
affectedChecks: this.affectedChecks(result),
|
|
91
|
-
findingsFingerprint: this.fingerprint(result),
|
|
92
|
-
instruction: "Apply the smallest safe correction for the listed gates, then rerun validation. Do not broaden scope."
|
|
93
|
-
};
|
|
94
|
-
await fs.writeFile(requestPath, JSON.stringify(request, null, 2));
|
|
95
|
-
return requestPath;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async run({ initialResult, validate, remediate }) {
|
|
99
|
-
let current = initialResult;
|
|
100
|
-
const history = [];
|
|
101
|
-
const seen = new Set();
|
|
102
|
-
|
|
103
|
-
if (!isRecoverableGateFailure(current.overallStatus)) {
|
|
104
|
-
await this.resetState();
|
|
105
|
-
return { ...current, remediation: { status: "NOT_REQUIRED", attempts: 0, history } };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
for (let attempt = 1; attempt <= this.maxAttempts; attempt += 1) {
|
|
109
|
-
const fingerprint = this.fingerprint(current);
|
|
110
|
-
if (seen.has(fingerprint)) {
|
|
111
|
-
const blocked = { overallStatus: "BLOCKED", reason: "Repeated unresolved finding", previousResult: current };
|
|
112
|
-
await this.writeState({ status: "BLOCKED", reason: blocked.reason, attempts: attempt - 1, history });
|
|
113
|
-
return { ...blocked, remediation: { status: "BLOCKED", attempts: attempt - 1, history } };
|
|
114
|
-
}
|
|
115
|
-
seen.add(fingerprint);
|
|
116
|
-
|
|
117
|
-
const requestPath = await this.writeRemediationRequest({ attempt, result: current });
|
|
118
|
-
if (typeof remediate !== "function") {
|
|
119
|
-
const blocked = { overallStatus: "BLOCKED", reason: "No remediation executor available", previousResult: current, requestPath };
|
|
120
|
-
await this.writeState({ status: "BLOCKED", reason: blocked.reason, attempts: attempt - 1, history, requestPath });
|
|
121
|
-
return { ...blocked, remediation: { status: "BLOCKED", attempts: attempt - 1, history, requestPath } };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const remediation = await remediate({
|
|
125
|
-
attempt,
|
|
126
|
-
maxAttempts: this.maxAttempts,
|
|
127
|
-
mode: this.mode,
|
|
128
|
-
taskSlug: this.taskSlug,
|
|
129
|
-
result: current,
|
|
130
|
-
affectedChecks: this.affectedChecks(current),
|
|
131
|
-
requestPath
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
if (!remediation?.applied) {
|
|
135
|
-
const blocked = { overallStatus: "BLOCKED", reason: remediation?.reason || "Remediation was not applied", previousResult: current, requestPath };
|
|
136
|
-
history.push({ attempt, before: current.overallStatus, remediation, after: "NOT_RUN", progress: false });
|
|
137
|
-
await this.writeState({ status: "BLOCKED", reason: blocked.reason, attempts: attempt, history });
|
|
138
|
-
return { ...blocked, remediation: { status: "BLOCKED", attempts: attempt, history } };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const next = await validate({ affectedChecks: this.affectedChecks(current), attempt });
|
|
142
|
-
const progress = this.hasProgress(current, next, remediation);
|
|
143
|
-
|
|
144
|
-
// Verify strict conditions to declare Phoenix remediation applied/completed
|
|
145
|
-
const findingsConcrete = this.affectedChecks(current).length > 0;
|
|
146
|
-
const hashAltered = remediation.changedFiles && remediation.changedFiles.length > 0;
|
|
147
|
-
const inScope = hashAltered && remediation.changedFiles.every(file => {
|
|
148
|
-
const norm = file.replaceAll("\\", "/");
|
|
149
|
-
if (norm.startsWith(".ai-workflow/")) return false;
|
|
150
|
-
if (norm === "package.json" || norm === "package-lock.json" || norm === "tsconfig.json" || norm === ".gitignore" || norm === "opencode.jsonc" || norm === "EVIDENCE.json") {
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
return true;
|
|
154
|
-
});
|
|
155
|
-
const validationPassed = next.overallStatus === "PASS" || next.overallStatus === "PASS_WITH_NOTES";
|
|
156
|
-
|
|
157
|
-
const phoenixSuccessful = findingsConcrete && hashAltered && inScope && validationPassed;
|
|
158
|
-
|
|
159
|
-
if (this.ledger && phoenixSuccessful) {
|
|
160
|
-
this.ledger.logEvent({
|
|
161
|
-
actor: "Phoenix",
|
|
162
|
-
actorType: "runtime-agent",
|
|
163
|
-
observed: true,
|
|
164
|
-
runtime: "opencode",
|
|
165
|
-
eventType: "remediation_complete",
|
|
166
|
-
provenance: "opencode-adapter",
|
|
167
|
-
data: {
|
|
168
|
-
success: true,
|
|
169
|
-
changedFiles: remediation.changedFiles,
|
|
170
|
-
event: "remediation.completed"
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
history.push({
|
|
176
|
-
attempt,
|
|
177
|
-
before: current.overallStatus,
|
|
178
|
-
after: next.overallStatus,
|
|
179
|
-
progress,
|
|
180
|
-
changedFiles: remediation.changedFiles || [],
|
|
181
|
-
evidenceAdded: remediation.evidenceAdded || [],
|
|
182
|
-
affectedChecks: this.affectedChecks(current)
|
|
183
|
-
});
|
|
184
|
-
await this.writeState({ status: next.overallStatus, attempts: attempt, history });
|
|
185
|
-
|
|
186
|
-
if (!isRecoverableGateFailure(next.overallStatus)) {
|
|
187
|
-
await this.resetState();
|
|
188
|
-
return { ...next, remediation: { status: "RESOLVED", attempts: attempt, history } };
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (!progress) {
|
|
192
|
-
return {
|
|
193
|
-
overallStatus: "BLOCKED",
|
|
194
|
-
reason: "Remediation made no measurable progress",
|
|
195
|
-
previousResult: next,
|
|
196
|
-
remediation: { status: "BLOCKED", attempts: attempt, history }
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
current = next;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
overallStatus: "BLOCKED",
|
|
205
|
-
reason: `Quality remediation attempts exhausted for ${this.mode} mode`,
|
|
206
|
-
previousResult: current,
|
|
207
|
-
remediation: { status: "BLOCKED", attempts: this.maxAttempts, history }
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export { MODE_ATTEMPTS, collectBlockingFindings };
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { OpenCodeAdapter } from "../runtime/opencode-adapter.js";
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { execSync } from "node:child_process";
|
|
5
|
-
|
|
6
|
-
export function createRuntimeRemediationExecutor(cwd, ledger = null) {
|
|
7
|
-
const adapter = new OpenCodeAdapter({ cwd });
|
|
8
|
-
|
|
9
|
-
function getChangedFiles() {
|
|
10
|
-
try {
|
|
11
|
-
const output = execSync("git status --short", { cwd, encoding: "utf8" });
|
|
12
|
-
return output
|
|
13
|
-
.split("\n")
|
|
14
|
-
.filter(Boolean)
|
|
15
|
-
.map((line) => line.slice(3).trim())
|
|
16
|
-
.filter(Boolean);
|
|
17
|
-
} catch {
|
|
18
|
-
return [];
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return async function runtimeRemediationExecutor({ attempt, maxAttempts, mode, affectedChecks, requestPath, result }) {
|
|
23
|
-
console.log(`\n[HEALER] Running Runtime/Phoenix Structural Remediation [Attempt ${attempt}/${maxAttempts}]`);
|
|
24
|
-
console.log(`[HEALER] Affected checks: ${affectedChecks.join(", ")}`);
|
|
25
|
-
|
|
26
|
-
if (ledger) {
|
|
27
|
-
ledger.logEvent({
|
|
28
|
-
actor: "Phoenix",
|
|
29
|
-
actorType: "runtime-agent",
|
|
30
|
-
observed: true,
|
|
31
|
-
runtime: "opencode",
|
|
32
|
-
eventType: "remediation_start",
|
|
33
|
-
provenance: "opencode-adapter",
|
|
34
|
-
data: {
|
|
35
|
-
attempt,
|
|
36
|
-
affectedChecks,
|
|
37
|
-
event: "remediation.started"
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let requestContent = {};
|
|
43
|
-
try {
|
|
44
|
-
requestContent = JSON.parse(await fs.readFile(requestPath, "utf8"));
|
|
45
|
-
} catch {
|
|
46
|
-
// ignore/fallback
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const filesBefore = getChangedFiles();
|
|
50
|
-
const fileContentsBefore = {};
|
|
51
|
-
for (const file of filesBefore) {
|
|
52
|
-
try {
|
|
53
|
-
fileContentsBefore[file] = await fs.readFile(path.join(cwd, file), "utf8");
|
|
54
|
-
} catch {
|
|
55
|
-
fileContentsBefore[file] = null;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Extract concrete command outputs for failing checks
|
|
60
|
-
const failedCommands = (result?.evidence?.commands || [])
|
|
61
|
-
.filter(cmd => ["FAIL", "BLOCKED"].includes(cmd.status));
|
|
62
|
-
|
|
63
|
-
let concreteOutput = "";
|
|
64
|
-
if (failedCommands.length > 0) {
|
|
65
|
-
concreteOutput = failedCommands.map(cmd => {
|
|
66
|
-
return `Command: ${cmd.name} (${Array.isArray(cmd.command) ? cmd.command.join(" ") : cmd.command})
|
|
67
|
-
Exit Code: ${cmd.exitCode}
|
|
68
|
-
Output:
|
|
69
|
-
${cmd.output}`;
|
|
70
|
-
}).join("\n\n");
|
|
71
|
-
} else {
|
|
72
|
-
concreteOutput = JSON.stringify(result?.quality || result || {}, null, 2);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const promptMsg = `We are in remediation mode (attempt ${attempt}/${maxAttempts}).
|
|
76
|
-
The following validation checks have failed:
|
|
77
|
-
${affectedChecks.join(", ")}
|
|
78
|
-
|
|
79
|
-
Details of the failure:
|
|
80
|
-
\`\`\`
|
|
81
|
-
${concreteOutput}
|
|
82
|
-
\`\`\`
|
|
83
|
-
|
|
84
|
-
Please inspect the code, identify the root cause of these failures (e.g. failing tests, syntax issues, build errors), and write a correction to the relevant files in the workspace to make the tests/validations pass.`;
|
|
85
|
-
|
|
86
|
-
console.log(`[HEALER] Sending structural healing request to Phoenix agent...`);
|
|
87
|
-
const runResult = await adapter.execute(promptMsg, {
|
|
88
|
-
agent: "Phoenix"
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const filesAfter = getChangedFiles();
|
|
92
|
-
const changedFiles = [];
|
|
93
|
-
for (const file of filesAfter) {
|
|
94
|
-
if (!filesBefore.includes(file)) {
|
|
95
|
-
changedFiles.push(file);
|
|
96
|
-
} else {
|
|
97
|
-
try {
|
|
98
|
-
const contentAfter = await fs.readFile(path.join(cwd, file), "utf8");
|
|
99
|
-
if (contentAfter !== fileContentsBefore[file]) {
|
|
100
|
-
changedFiles.push(file);
|
|
101
|
-
}
|
|
102
|
-
} catch {
|
|
103
|
-
if (fileContentsBefore[file] !== null) {
|
|
104
|
-
changedFiles.push(file);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const success = runResult.success && changedFiles.length > 0;
|
|
110
|
-
|
|
111
|
-
if (!runResult.success) {
|
|
112
|
-
console.log(`[HEALER] Phoenix remediation failed: ${runResult.error || "Unknown error"}`);
|
|
113
|
-
return {
|
|
114
|
-
applied: false,
|
|
115
|
-
reason: `Phoenix runtime execution failed: ${runResult.error || "Unknown error"}`
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (changedFiles.length === 0) {
|
|
120
|
-
console.log(`[HEALER] Phoenix execution succeeded but no files were modified.`);
|
|
121
|
-
return {
|
|
122
|
-
applied: false,
|
|
123
|
-
reason: "Phoenix runtime executed but did not modify any files in the workspace."
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
console.log(`[HEALER] Phoenix successfully executed remediation commands.`);
|
|
128
|
-
console.log(`[HEALER] Files modified: ${changedFiles.join(", ")}`);
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
applied: true,
|
|
132
|
-
changedFiles,
|
|
133
|
-
evidenceAdded: ["phoenix-healing-run"]
|
|
134
|
-
};
|
|
135
|
-
};
|
|
136
|
-
}
|
package/src/core/identity.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
export const AGENT_MAP = {
|
|
2
|
-
"atlas": "Atlas",
|
|
3
|
-
"nexus": "Nexus",
|
|
4
|
-
"orion": "Orion",
|
|
5
|
-
"astra": "Astra",
|
|
6
|
-
"sage": "Sage",
|
|
7
|
-
"phoenix": "Phoenix",
|
|
8
|
-
"architecture": "Architecture-Specialist",
|
|
9
|
-
"backend-development": "Backend-Engineer",
|
|
10
|
-
"deployment": "Deployment-Specialist",
|
|
11
|
-
"design-principles": "Design-Specialist",
|
|
12
|
-
"documentation": "Docs-Engineer",
|
|
13
|
-
"frontend-development": "Frontend-Engineer",
|
|
14
|
-
"full-stack-development": "Full-Stack-Engineer",
|
|
15
|
-
"optimize-tokens": "Token-Economist",
|
|
16
|
-
"pr-workflow": "PR-Manager",
|
|
17
|
-
"product-discovery": "Discovery-Analyst",
|
|
18
|
-
"product-planning": "Product-Planner",
|
|
19
|
-
"project-memory": "Memory-Guardian",
|
|
20
|
-
"prompt-engineer": "Prompt-Engineer",
|
|
21
|
-
"qa-workflow": "QA-Engineer",
|
|
22
|
-
"refactoring": "Refactoring-Specialist",
|
|
23
|
-
"release-workflow": "Release-Specialist",
|
|
24
|
-
"spec-driven-development": "SDD-Specialist",
|
|
25
|
-
"technical-leadership": "Technical-Leader",
|
|
26
|
-
"ui-ux-design": "UI-UX-Engineer"
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Returns the canonical (commercial) name for an agent given its internal slug.
|
|
31
|
-
* Falls back to PascalCase translation if no mapping exists.
|
|
32
|
-
* @param {string} slug
|
|
33
|
-
* @returns {string}
|
|
34
|
-
*/
|
|
35
|
-
export function getCanonicalAgentName(slug) {
|
|
36
|
-
if (AGENT_MAP[slug]) return AGENT_MAP[slug];
|
|
37
|
-
|
|
38
|
-
// Fallback: convert slug-case to PascalCase
|
|
39
|
-
return slug
|
|
40
|
-
.split("-")
|
|
41
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
42
|
-
.join("");
|
|
43
|
-
}
|
package/src/core/install-plan.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { getTemplateFiles } from "./templates.js";
|
|
3
|
-
|
|
4
|
-
export async function createInstallPlan({ cwd, exists, profile = "standard" }) {
|
|
5
|
-
const actions = [];
|
|
6
|
-
const templateFiles = getTemplateFiles(profile);
|
|
7
|
-
const installRoot = ".ai-workflow";
|
|
8
|
-
|
|
9
|
-
for (const [relativePath, content] of Object.entries(templateFiles)) {
|
|
10
|
-
if (relativePath === "opencode.jsonc") {
|
|
11
|
-
continue;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const installedRelativePath = path.join(installRoot, relativePath);
|
|
15
|
-
const absolutePath = path.join(cwd, installedRelativePath);
|
|
16
|
-
const action = (await exists(absolutePath)) ? "conflict" : "create";
|
|
17
|
-
actions.push({
|
|
18
|
-
type: action,
|
|
19
|
-
relativePath: installedRelativePath,
|
|
20
|
-
absolutePath,
|
|
21
|
-
content
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const configPath = path.join(cwd, ".ai-workflow/config.json");
|
|
26
|
-
actions.push({
|
|
27
|
-
type: (await exists(configPath)) ? "conflict" : "create",
|
|
28
|
-
relativePath: ".ai-workflow/config.json",
|
|
29
|
-
absolutePath: configPath,
|
|
30
|
-
content: null
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
return actions;
|
|
34
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { exists, readJsonc, writeFileSafe } from "./filesystem.js";
|
|
3
|
-
import { createManagedBackup } from "./backup.js";
|
|
4
|
-
import { getTemplateFiles } from "./templates.js";
|
|
5
|
-
|
|
6
|
-
const LEGACY_MARKER = "_aiWorkflowManaged";
|
|
7
|
-
|
|
8
|
-
const AGENTS = {
|
|
9
|
-
Atlas: { description: "Router and workflow coordinator for AI Workflow Kit" },
|
|
10
|
-
Nexus: { description: "Discovery, requirement, scope, and specification owner" },
|
|
11
|
-
Orion: { description: "Planning, PR sequencing, release, and deployment strategy owner" },
|
|
12
|
-
Astra: { description: "Implementation owner for incremental, scoped changes" },
|
|
13
|
-
Sage: { description: "Audit, validation, evidence, and quality gate owner" },
|
|
14
|
-
Phoenix: { description: "Remediation and regression recovery owner" }
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const SUBAGENTS = {
|
|
18
|
-
"Architecture-Specialist": { description: "Select the simplest architecture that safely satisfies current requirements", skill: "architecture" },
|
|
19
|
-
"Backend-Engineer": { description: "Support safe PHP/Node/Python backend changes for APIs and persistence", skill: "backend-development" },
|
|
20
|
-
"Deployment-Specialist": { description: "Plan, review, or validate deployment, release, and production-readiness", skill: "deployment" },
|
|
21
|
-
"Design-Specialist": { description: "Apply practical design principles to keep solutions simple and maintainable", skill: "design-principles" },
|
|
22
|
-
"Docs-Engineer": { description: "Write or improve project documentation, READMEs, guides, and prompts", skill: "documentation" },
|
|
23
|
-
"Frontend-Engineer": { description: "Implement or review frontend changes involving UI, state, routing, and accessibility", skill: "frontend-development" },
|
|
24
|
-
"Frontend-Design-System-Specialist": { description: "Enforce CSS/HTML tokens, semantic elements, typography, and premium atmosphere constraints for frontend quality", skill: "frontend-design-system" },
|
|
25
|
-
"Full-Stack-Engineer": { description: "Coordinate frontend and backend changes as one coherent, testable increment", skill: "full-stack-development" },
|
|
26
|
-
"Token-Economist": { description: "Reduce context size while preserving information required for safe execution", skill: "optimize-tokens" },
|
|
27
|
-
"PR-Manager": { description: "Plan, implement, review, or validate small pull requests while preserving scope", skill: "pr-workflow" },
|
|
28
|
-
"Discovery-Analyst": { description: "Clarify ambiguous requests into actionable problem statements and success outcomes", skill: "product-discovery" },
|
|
29
|
-
"Product-Planner": { description: "Turn clarified needs into scoped requirements with acceptance-ready outcomes", skill: "product-planning" },
|
|
30
|
-
"Memory-Guardian": { description: "Manage discoverable project memory for durable decisions and recurring corrections", skill: "project-memory" },
|
|
31
|
-
"Prompt-Engineer": { description: "Expert in creating and refining prompts for AI agents and workflows", skill: "prompt-engineer" },
|
|
32
|
-
"QA-Engineer": { description: "Design, review, or improve tests, acceptance criteria, and validation evidence", skill: "qa-workflow" },
|
|
33
|
-
"Refactoring-Specialist": { description: "Improve code/document structure safely while preserving behavior", skill: "refactoring" },
|
|
34
|
-
"Release-Specialist": { description: "Prepare release readiness decisions with explicit gates and evidence", skill: "release-workflow" },
|
|
35
|
-
"SDD-Specialist": { description: "Convert approved requirements into explicit, testable specification artifacts", skill: "spec-driven-development" },
|
|
36
|
-
"Technical-Leader": { description: "Make technical decisions, review architecture, and identify trade-offs", skill: "technical-leadership" },
|
|
37
|
-
"UI-UX-Engineer": { description: "Improve usability and interface clarity without introducing unnecessary complexity", skill: "ui-ux-design" }
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const COMMANDS = {
|
|
41
|
-
atlas: { description: "Decide safest next step and delegate to the appropriate workflow or agent", agent: "Atlas" },
|
|
42
|
-
run: { description: "Execute the master orchestration pipeline (Gates, SDD, Implementation, Validation, Healing)", agent: "Atlas" },
|
|
43
|
-
discover: { description: "Understand the project, gather context, and create initial documentation", agent: "Nexus" },
|
|
44
|
-
"spec-create": { description: "Create a clear specification before implementation", agent: "Nexus" },
|
|
45
|
-
"spec-review": { description: "Review a specification and validate scope, risks, criteria, and gaps", agent: "Nexus" },
|
|
46
|
-
"spec-implement": { description: "Implement an approved specification incrementally", agent: "Astra" },
|
|
47
|
-
plan: { description: "Plan a task or change, defining steps, risks, and validation", agent: "Orion" },
|
|
48
|
-
implement: { description: "Implement a small task with defined scope", agent: "Astra" },
|
|
49
|
-
validate: { description: "Run project validations and collect evidence", agent: "Sage" },
|
|
50
|
-
audit: { description: "Audit the project, identify issues, and suggest prioritized improvements", agent: "Sage" },
|
|
51
|
-
"optimize-tokens": { description: "Analyze workflow and context usage; suggest reductions without losing quality or evidence", agent: "Sage" },
|
|
52
|
-
"update-memory": { description: "Update durable project memory with relevant decisions, constraints, and reusable context", agent: "Nexus" },
|
|
53
|
-
release: { description: "Prepare delivery with commit, push, PR/merge readiness, changelog, and evidence", agent: "Orion" },
|
|
54
|
-
deploy: { description: "Publish the project to the defined environment after validation and release readiness", agent: "Orion" }
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
function buildManagedConfig() {
|
|
58
|
-
const agent = {};
|
|
59
|
-
for (const [name, def] of Object.entries(AGENTS)) {
|
|
60
|
-
agent[name] = {
|
|
61
|
-
mode: "primary",
|
|
62
|
-
description: def.description,
|
|
63
|
-
prompt: `{file:./.ai-workflow/opencode/agents/${name.toLowerCase()}.md}`
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
for (const [name, def] of Object.entries(SUBAGENTS)) {
|
|
68
|
-
agent[name] = {
|
|
69
|
-
mode: "subagent",
|
|
70
|
-
description: def.description,
|
|
71
|
-
prompt: `{file:./.ai-workflow/opencode/skills/${def.skill}/SKILL.md}`
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const command = {};
|
|
76
|
-
for (const [name, def] of Object.entries(COMMANDS)) {
|
|
77
|
-
command[name] = {
|
|
78
|
-
description: def.description,
|
|
79
|
-
agent: def.agent,
|
|
80
|
-
template: `{file:./.ai-workflow/opencode/commands/${name}.md}`
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
$schema: "https://opencode.ai/config.json",
|
|
86
|
-
default_agent: "Atlas",
|
|
87
|
-
agent,
|
|
88
|
-
skills: {
|
|
89
|
-
paths: ["./.ai-workflow/opencode/skills"]
|
|
90
|
-
},
|
|
91
|
-
command
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export async function mergeOpencodeConfig(cwd, { force = false, backupRoot = ".ai-workflow-backups" } = {}) {
|
|
96
|
-
const targetPath = path.join(cwd, "opencode.jsonc");
|
|
97
|
-
const managed = buildManagedConfig();
|
|
98
|
-
|
|
99
|
-
if (!(await exists(targetPath))) {
|
|
100
|
-
await writeFileSafe(targetPath, `${JSON.stringify(managed, null, 2)}\n`);
|
|
101
|
-
return { changed: true, reason: "created" };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
let current;
|
|
105
|
-
try {
|
|
106
|
-
current = await readJsonc(targetPath);
|
|
107
|
-
} catch {
|
|
108
|
-
if (!force) {
|
|
109
|
-
return { changed: false, reason: "invalid-json (skipped)" };
|
|
110
|
-
}
|
|
111
|
-
await createManagedBackup(targetPath, {
|
|
112
|
-
cwd,
|
|
113
|
-
backupRoot,
|
|
114
|
-
maxPerFile: 20
|
|
115
|
-
});
|
|
116
|
-
await writeFileSafe(targetPath, `${JSON.stringify(managed, null, 2)}\n`);
|
|
117
|
-
return { changed: true, reason: "invalid-json (replaced with --force)" };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const { [LEGACY_MARKER]: _legacyManaged, ...currentWithoutLegacy } = current;
|
|
121
|
-
|
|
122
|
-
const next = {
|
|
123
|
-
...currentWithoutLegacy,
|
|
124
|
-
$schema: current.$schema ?? managed.$schema,
|
|
125
|
-
...(managed.mcp
|
|
126
|
-
? {
|
|
127
|
-
mcp: {
|
|
128
|
-
...(current.mcp ?? {}),
|
|
129
|
-
...managed.mcp
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
: {}),
|
|
133
|
-
agent: {
|
|
134
|
-
...(current.agent ?? {}),
|
|
135
|
-
...managed.agent
|
|
136
|
-
},
|
|
137
|
-
command: {
|
|
138
|
-
...(current.command ?? {}),
|
|
139
|
-
...managed.command
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const changed = JSON.stringify(current) !== JSON.stringify(next);
|
|
144
|
-
if (!changed) {
|
|
145
|
-
return { changed: false, reason: "already-up-to-date" };
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
await writeFileSafe(targetPath, `${JSON.stringify(next, null, 2)}\n`);
|
|
149
|
-
return { changed: true, reason: "merged-managed-sections" };
|
|
150
|
-
}
|