@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,202 +0,0 @@
|
|
|
1
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
-
import readline from "node:readline";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* OpenCodeAdapter - Real runtime integration with the OpenCode CLI.
|
|
6
|
-
*/
|
|
7
|
-
export class OpenCodeAdapter {
|
|
8
|
-
constructor({ cwd = process.cwd() } = {}) {
|
|
9
|
-
this.cwd = cwd;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Inspects the availability and capability of the OpenCode CLI.
|
|
14
|
-
* @returns {Promise<{available: boolean, supports: {run: boolean, formatJson: boolean, agent: boolean, model: boolean}}>}
|
|
15
|
-
*/
|
|
16
|
-
async inspect() {
|
|
17
|
-
try {
|
|
18
|
-
const cliHelp = spawnSync("opencode", ["--help"], { encoding: "utf8" });
|
|
19
|
-
const runHelp = spawnSync("opencode", ["run", "--help"], { encoding: "utf8" });
|
|
20
|
-
|
|
21
|
-
const available = cliHelp.status === 0 && runHelp.status === 0;
|
|
22
|
-
if (!available) {
|
|
23
|
-
return {
|
|
24
|
-
available: false,
|
|
25
|
-
supports: { run: false, formatJson: false, agent: false, model: false }
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
available: true,
|
|
31
|
-
supports: {
|
|
32
|
-
run: /\brun\b/.test(cliHelp.stdout),
|
|
33
|
-
formatJson: /--format/.test(runHelp.stdout),
|
|
34
|
-
agent: /--agent/.test(runHelp.stdout),
|
|
35
|
-
model: /--model/.test(runHelp.stdout),
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
} catch {
|
|
39
|
-
return {
|
|
40
|
-
available: false,
|
|
41
|
-
supports: { run: false, formatJson: false, agent: false, model: false }
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Runs opencode with a prompt and options.
|
|
48
|
-
* @param {string} message - The message prompt.
|
|
49
|
-
* @param {Object} options - CLI options (e.g. agent, model, readOnly).
|
|
50
|
-
* @returns {Promise<{success: boolean, commandsRun: string[], eventCount: number, error?: string}>}
|
|
51
|
-
*/
|
|
52
|
-
async execute(message, { agent = null, model = null, readOnly = false } = {}) {
|
|
53
|
-
const inspection = await this.inspect();
|
|
54
|
-
const isSpecializedAgent = agent && ["Nexus", "Orion", "Astra", "Sage", "Phoenix"].includes(agent);
|
|
55
|
-
|
|
56
|
-
if (!inspection.available) {
|
|
57
|
-
return {
|
|
58
|
-
success: false,
|
|
59
|
-
status: isSpecializedAgent ? "BLOCKED" : "FAILED",
|
|
60
|
-
commandsRun: [],
|
|
61
|
-
eventCount: 0,
|
|
62
|
-
error: "OpenCode CLI is not installed or not available in the system PATH.",
|
|
63
|
-
runtimeAgentRequested: agent || null,
|
|
64
|
-
runtimeAgentApplied: null,
|
|
65
|
-
runtimeAgentSupported: false,
|
|
66
|
-
runtimeAgentSelectionMode: isSpecializedAgent ? "explicit" : "default"
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (isSpecializedAgent && !inspection.supports.agent) {
|
|
71
|
-
return {
|
|
72
|
-
success: false,
|
|
73
|
-
status: "BLOCKED",
|
|
74
|
-
error: `Requested runtime actor '${agent}' cannot be applied because this OpenCode runtime does not support --agent.`,
|
|
75
|
-
runtimeAgentRequested: agent,
|
|
76
|
-
runtimeAgentApplied: null,
|
|
77
|
-
runtimeAgentSupported: false,
|
|
78
|
-
runtimeAgentSelectionMode: "unsupported",
|
|
79
|
-
commandsRun: [],
|
|
80
|
-
eventCount: 0
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const runInWorkspace = async (targetCwd) => {
|
|
85
|
-
return new Promise((resolve) => {
|
|
86
|
-
const args = ["run", message];
|
|
87
|
-
if (inspection.supports.formatJson) {
|
|
88
|
-
args.push("--format", "json");
|
|
89
|
-
}
|
|
90
|
-
if (isSpecializedAgent && inspection.supports.agent) {
|
|
91
|
-
args.push("--agent", agent);
|
|
92
|
-
}
|
|
93
|
-
if (model && inspection.supports.model) {
|
|
94
|
-
args.push("--model", model);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
console.log(`[RUNTIME] Delegating to OpenCode: opencode ${args.map(a => a.includes(" ") ? `"${a}"` : a).join(" ")}`);
|
|
98
|
-
|
|
99
|
-
const child = spawn("opencode", args, {
|
|
100
|
-
cwd: targetCwd,
|
|
101
|
-
stdio: ["ignore", "pipe", "inherit"] // Inherit stderr to show warnings directly
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
const rl = readline.createInterface({
|
|
105
|
-
input: child.stdout,
|
|
106
|
-
terminal: false
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
const commandsRun = [];
|
|
110
|
-
let eventCount = 0;
|
|
111
|
-
|
|
112
|
-
rl.on("line", (line) => {
|
|
113
|
-
const trimmed = line.trim();
|
|
114
|
-
if (!trimmed) return;
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
const event = JSON.parse(trimmed);
|
|
118
|
-
eventCount++;
|
|
119
|
-
|
|
120
|
-
// 1. Stream agent output text in real-time to user
|
|
121
|
-
if (event.type === "text" && event.part?.text) {
|
|
122
|
-
process.stdout.write(event.part.text);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 2. Capture tool executions/commands
|
|
126
|
-
if (event.type === "step_start" && event.part?.toolCalls) {
|
|
127
|
-
for (const call of event.part.toolCalls) {
|
|
128
|
-
if (call.name === "run_command" && call.args?.CommandLine) {
|
|
129
|
-
commandsRun.push(call.args.CommandLine);
|
|
130
|
-
}
|
|
131
|
-
if (readOnly) {
|
|
132
|
-
const name = call.name || "";
|
|
133
|
-
const isWriteTool = name.startsWith("write_") || name.includes("write") || name.includes("replace") || name.includes("edit") || name.includes("delete") || name.includes("remove");
|
|
134
|
-
let isDestructiveGit = false;
|
|
135
|
-
if (name === "run_command" && call.args?.CommandLine) {
|
|
136
|
-
const cmd = call.args.CommandLine;
|
|
137
|
-
const destPattern = /\b(git\s+(commit|add|push|merge|reset|rm|branch|checkout|switch|stash|init)|touch|rm|mkdir|cp|mv|chmod|chown|tee|sed|awk)\b|>>|>/;
|
|
138
|
-
if (destPattern.test(cmd)) {
|
|
139
|
-
isDestructiveGit = true;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (isWriteTool || isDestructiveGit) {
|
|
143
|
-
console.error(`\n[SECURITY WARNING] Read-only execution blocked attempt to write/modify workspace: tool '${name}' or command detected.`);
|
|
144
|
-
child.kill();
|
|
145
|
-
resolve({
|
|
146
|
-
success: false,
|
|
147
|
-
commandsRun,
|
|
148
|
-
eventCount,
|
|
149
|
-
error: `Read-only confinement violation: tool '${name}' or command attempted write/modify operation during read-only task.`,
|
|
150
|
-
runtimeAgentRequested: agent || null,
|
|
151
|
-
runtimeAgentApplied: null,
|
|
152
|
-
runtimeAgentSupported: inspection.supports.agent,
|
|
153
|
-
runtimeAgentSelectionMode: isSpecializedAgent ? "explicit" : "default"
|
|
154
|
-
});
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
} catch {
|
|
161
|
-
// If a line is not valid JSON (e.g. starting/finishing logs), print it directly
|
|
162
|
-
console.log(trimmed);
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
child.on("close", (code) => {
|
|
167
|
-
console.log("\n[RUNTIME] OpenCode finished execution.");
|
|
168
|
-
resolve({
|
|
169
|
-
success: code === 0,
|
|
170
|
-
commandsRun,
|
|
171
|
-
eventCount,
|
|
172
|
-
runtimeAgentRequested: agent || null,
|
|
173
|
-
runtimeAgentApplied: isSpecializedAgent ? agent : null,
|
|
174
|
-
runtimeAgentSupported: inspection.supports.agent,
|
|
175
|
-
runtimeAgentSelectionMode: isSpecializedAgent ? "explicit" : "default"
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
child.on("error", (err) => {
|
|
180
|
-
console.error(`\n[RUNTIME] Failed to launch opencode: ${err.message}`);
|
|
181
|
-
resolve({
|
|
182
|
-
success: false,
|
|
183
|
-
commandsRun,
|
|
184
|
-
eventCount: 0,
|
|
185
|
-
error: err.message,
|
|
186
|
-
runtimeAgentRequested: agent || null,
|
|
187
|
-
runtimeAgentApplied: null,
|
|
188
|
-
runtimeAgentSupported: inspection.supports.agent,
|
|
189
|
-
runtimeAgentSelectionMode: isSpecializedAgent ? "explicit" : "default"
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
if (readOnly) {
|
|
196
|
-
const { runInReadOnlyWorkspace } = await import("../workspace/read-only-workspace.js");
|
|
197
|
-
return runInReadOnlyWorkspace(this.cwd, runInWorkspace);
|
|
198
|
-
} else {
|
|
199
|
-
return runInWorkspace(this.cwd);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* SpecValidator - Enforces "No Spec, No Code" rule.
|
|
5
|
-
* Validates existence, structure, and approval status of specifications.
|
|
6
|
-
*/
|
|
7
|
-
export class SpecValidator {
|
|
8
|
-
/**
|
|
9
|
-
* Validates a specification file.
|
|
10
|
-
* @param {string} filePath - Path to the .md specification file.
|
|
11
|
-
* @returns {Promise<{valid: boolean, reason?: string, tier?: string}>}
|
|
12
|
-
*/
|
|
13
|
-
async validate(filePath) {
|
|
14
|
-
try {
|
|
15
|
-
const content = await fs.readFile(filePath, "utf8");
|
|
16
|
-
|
|
17
|
-
// Check for tiered tier identification
|
|
18
|
-
let tier = "unknown";
|
|
19
|
-
if (content.includes("[DEEP]")) tier = "deep";
|
|
20
|
-
else if (content.includes("[STANDARD]")) tier = "standard";
|
|
21
|
-
else if (content.includes("[TINY]")) tier = "tiny";
|
|
22
|
-
|
|
23
|
-
if (tier === "unknown") {
|
|
24
|
-
return { valid: false, reason: "Specification tier [DEEP|STANDARD|TINY] not identified in title." };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Check for Metadata section
|
|
28
|
-
if (!content.includes("## Metadata")) {
|
|
29
|
-
return { valid: false, reason: "Missing '## Metadata' section.", tier };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Check for Acceptance Criteria section
|
|
33
|
-
if (!content.includes("## Acceptance Criteria")) {
|
|
34
|
-
return { valid: false, reason: "Missing '## Acceptance Criteria' section.", tier };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Check for Approved status
|
|
38
|
-
const lines = content.split("\n");
|
|
39
|
-
const statusLine = lines.find(l => l.includes("Status:"));
|
|
40
|
-
|
|
41
|
-
if (!statusLine) {
|
|
42
|
-
return { valid: false, reason: "Missing Status field in Metadata section.", tier };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const statusPart = statusLine.split(":")[1]
|
|
46
|
-
.replace(/[\*_]/g, "") // Remove bold/italic markers
|
|
47
|
-
.trim()
|
|
48
|
-
.toUpperCase();
|
|
49
|
-
|
|
50
|
-
// If it contains multiple options separated by |, it's not approved yet
|
|
51
|
-
if (statusPart.includes("|")) {
|
|
52
|
-
return { valid: false, reason: "Specification status is a template list. Must be explicitly set to 'APPROVED'.", tier };
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (statusPart !== "APPROVED") {
|
|
56
|
-
return { valid: false, reason: `Specification status is '${statusPart}', but must be 'APPROVED' to proceed.`, tier };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return { valid: true, tier };
|
|
60
|
-
} catch (error) {
|
|
61
|
-
if (error.code === "ENOENT") {
|
|
62
|
-
return { valid: false, reason: `Specification file not found: ${filePath}` };
|
|
63
|
-
}
|
|
64
|
-
return { valid: false, reason: `Error reading specification: ${error.message}` };
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
package/src/core/statuses.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export const COMPLETION_STATUSES = Object.freeze([
|
|
2
|
-
"PASS",
|
|
3
|
-
"PASS_WITH_NOTES",
|
|
4
|
-
"FAIL_DELEGATION_GATE",
|
|
5
|
-
"FAIL_QUALITY_GATE",
|
|
6
|
-
"FAIL",
|
|
7
|
-
"BLOCKED",
|
|
8
|
-
"NOT_RUN"
|
|
9
|
-
]);
|
|
10
|
-
|
|
11
|
-
export const RECOVERABLE_GATE_STATUSES = Object.freeze([
|
|
12
|
-
"FAIL_DELEGATION_GATE",
|
|
13
|
-
"FAIL_QUALITY_GATE"
|
|
14
|
-
]);
|
|
15
|
-
|
|
16
|
-
export const TERMINAL_FAILURE_STATUSES = Object.freeze([
|
|
17
|
-
"FAIL",
|
|
18
|
-
"BLOCKED"
|
|
19
|
-
]);
|
|
20
|
-
|
|
21
|
-
export function isRecoverableGateFailure(status) {
|
|
22
|
-
return RECOVERABLE_GATE_STATUSES.includes(status);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function isTerminalFailure(status) {
|
|
26
|
-
return TERMINAL_FAILURE_STATUSES.includes(status);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const COMPLETION_STATUS_TEXT = COMPLETION_STATUSES.join(" | ");
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
|
|
4
|
-
function normalize(p) {
|
|
5
|
-
return p.split(path.sep).join("/");
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* For v3.0.0 "Elite Governance", we want an ULTRA-CLEAN root.
|
|
10
|
-
* Only "opencode" is symlinked to the root (required for OpenCode discovery).
|
|
11
|
-
* All other assets stay inside .ai-workflow/.
|
|
12
|
-
*/
|
|
13
|
-
const LINKABLE_ROOTS = new Set([
|
|
14
|
-
"opencode",
|
|
15
|
-
".agents"
|
|
16
|
-
]);
|
|
17
|
-
|
|
18
|
-
const LINKABLE_FILES = new Set([]);
|
|
19
|
-
|
|
20
|
-
export function buildSymlinkEntries({ templateFiles, installRoot = ".ai-workflow" }) {
|
|
21
|
-
const entries = new Map();
|
|
22
|
-
|
|
23
|
-
for (const relativePath of Object.keys(templateFiles)) {
|
|
24
|
-
if (relativePath === "opencode.jsonc") continue;
|
|
25
|
-
|
|
26
|
-
const normalized = normalize(relativePath);
|
|
27
|
-
|
|
28
|
-
if (LINKABLE_FILES.has(normalized)) {
|
|
29
|
-
entries.set(normalized, `${installRoot}/${normalized}`);
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Match composite paths in LINKABLE_ROOTS (e.g., "docs/policies")
|
|
34
|
-
for (const rootPath of LINKABLE_ROOTS) {
|
|
35
|
-
if (normalized === rootPath || normalized.startsWith(`${rootPath}/`)) {
|
|
36
|
-
entries.set(rootPath, `${installRoot}/${rootPath}`);
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return Array.from(entries.entries()).map(([linkPath, targetPath]) => ({ linkPath, targetPath }));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export async function isSymlinkTo(absoluteLinkPath, absoluteTargetPath) {
|
|
46
|
-
try {
|
|
47
|
-
const stat = await fs.lstat(absoluteLinkPath);
|
|
48
|
-
if (!stat.isSymbolicLink()) return false;
|
|
49
|
-
const currentTarget = await fs.readlink(absoluteLinkPath);
|
|
50
|
-
const resolved = path.resolve(path.dirname(absoluteLinkPath), currentTarget);
|
|
51
|
-
return resolved === absoluteTargetPath;
|
|
52
|
-
} catch {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Internal symlink to satisfy opencode.jsonc paths without polluting the root.
|
|
59
|
-
* Creates .ai-workflow/opencode/skills -> ../.agents/skills
|
|
60
|
-
*/
|
|
61
|
-
export async function setupInternalSymlinks(cwd, installRoot = ".ai-workflow") {
|
|
62
|
-
const skillsLink = path.join(cwd, installRoot, "opencode/skills");
|
|
63
|
-
const skillsTarget = path.join(cwd, installRoot, ".agents/skills");
|
|
64
|
-
|
|
65
|
-
if (!(await exists(skillsTarget))) return;
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
const stat = await fs.lstat(skillsLink).catch(() => null);
|
|
69
|
-
if (stat) {
|
|
70
|
-
if (stat.isSymbolicLink()) {
|
|
71
|
-
const current = await fs.readlink(skillsLink);
|
|
72
|
-
if (current === "../.agents/skills") return;
|
|
73
|
-
await fs.unlink(skillsLink);
|
|
74
|
-
} else {
|
|
75
|
-
return; // File exists, skip
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const type = process.platform === "win32" ? "junction" : "dir";
|
|
80
|
-
await fs.symlink("../.agents/skills", skillsLink, type);
|
|
81
|
-
} catch {
|
|
82
|
-
// Silent fail for internal symlink
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function exists(p) {
|
|
87
|
-
try {
|
|
88
|
-
await fs.access(p);
|
|
89
|
-
return true;
|
|
90
|
-
} catch {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
}
|
package/src/core/templates.js
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import { getFullAgentContent, getFullSkillFiles, discoverPackageFiles, readPackageFile, getPackageVersion } from "./package-assets.js";
|
|
2
|
-
import { getCanonicalAgentName } from "./identity.js";
|
|
3
|
-
|
|
4
|
-
const COMMON_FILES = {
|
|
5
|
-
"opencode/README.md": `# OpenCode Setup\n\nThis directory is managed by \`ai-workflow\` init.\nAdd agent and command files required by your project workflow.\n`
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
const FULL_PRIMARY_AGENTS = [
|
|
9
|
-
"atlas",
|
|
10
|
-
"nexus",
|
|
11
|
-
"orion",
|
|
12
|
-
"astra",
|
|
13
|
-
"sage",
|
|
14
|
-
"phoenix"
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
const FULL_SKILLS = [
|
|
18
|
-
"project-memory",
|
|
19
|
-
"optimize-tokens",
|
|
20
|
-
"documentation",
|
|
21
|
-
"architecture",
|
|
22
|
-
"technical-leadership",
|
|
23
|
-
"product-discovery",
|
|
24
|
-
"product-planning",
|
|
25
|
-
"spec-driven-development",
|
|
26
|
-
"pr-workflow",
|
|
27
|
-
"qa-workflow",
|
|
28
|
-
"release-workflow",
|
|
29
|
-
"deployment",
|
|
30
|
-
"ui-ux-design",
|
|
31
|
-
"design-principles",
|
|
32
|
-
"frontend-development",
|
|
33
|
-
"backend-development",
|
|
34
|
-
"full-stack-development",
|
|
35
|
-
"refactoring",
|
|
36
|
-
"prompt-engineer"
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
const KIT_VERSION = getPackageVersion() ?? "unknown";
|
|
40
|
-
|
|
41
|
-
function buildRuntimeFiles({ includeFormalEvidence = false } = {}) {
|
|
42
|
-
const files = {};
|
|
43
|
-
|
|
44
|
-
for (const agent of FULL_PRIMARY_AGENTS) {
|
|
45
|
-
const content = getFullAgentContent(agent);
|
|
46
|
-
if (content === null) {
|
|
47
|
-
throw new Error(`CRITICAL FAILURE: Missing required agent asset: dist-assets/agents/${agent}.md`);
|
|
48
|
-
}
|
|
49
|
-
files[`opencode/agents/${agent}.md`] = content;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
for (const skill of FULL_SKILLS) {
|
|
53
|
-
const skillFiles = getFullSkillFiles(skill);
|
|
54
|
-
if (Object.keys(skillFiles).length === 0) {
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
for (const [relPath, content] of Object.entries(skillFiles)) {
|
|
58
|
-
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
59
|
-
if (!files[targetPath]) {
|
|
60
|
-
files[targetPath] = content;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Copy governance policy into opencode/docs/policies/ so the relative link
|
|
66
|
-
// ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md resolves correctly from
|
|
67
|
-
// opencode/skills/{skill}/SKILL.md for OpenCode and all platform adapters.
|
|
68
|
-
const opencodeGovernancePolicy = readPackageFile("dist-assets/docs/policies/SKILLS_COMMON_GOVERNANCE.md");
|
|
69
|
-
if (opencodeGovernancePolicy !== null) {
|
|
70
|
-
files["opencode/docs/policies/SKILLS_COMMON_GOVERNANCE.md"] = opencodeGovernancePolicy;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const commandFiles = discoverPackageFiles("dist-assets/commands");
|
|
74
|
-
for (const [relPath, content] of Object.entries(commandFiles)) {
|
|
75
|
-
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
76
|
-
if (!files[targetPath]) {
|
|
77
|
-
files[targetPath] = content;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const opencodeSkillFiles = discoverPackageFiles("dist-assets/skills");
|
|
82
|
-
for (const [relPath, content] of Object.entries(opencodeSkillFiles)) {
|
|
83
|
-
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
84
|
-
if (!files[targetPath]) {
|
|
85
|
-
files[targetPath] = content;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const harnessWorkflowFiles = discoverPackageFiles("dist-assets/harness/workflows");
|
|
90
|
-
for (const [relPath, content] of Object.entries(harnessWorkflowFiles)) {
|
|
91
|
-
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
92
|
-
files[targetPath] = content;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const harnessHandoffFiles = discoverPackageFiles("dist-assets/harness/handoffs");
|
|
96
|
-
for (const [relPath, content] of Object.entries(harnessHandoffFiles)) {
|
|
97
|
-
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
98
|
-
files[targetPath] = content;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const agentsContent = readPackageFile("dist-assets/AGENTS.md");
|
|
102
|
-
if (agentsContent !== null) files["AGENTS.md"] = agentsContent;
|
|
103
|
-
|
|
104
|
-
const consumerQuickstart = readPackageFile("dist-assets/docs/QUICKSTART.md");
|
|
105
|
-
if (consumerQuickstart !== null) files["QUICKSTART.md"] = consumerQuickstart;
|
|
106
|
-
|
|
107
|
-
const policyContent = readPackageFile("dist-assets/docs/architecture-policy.md");
|
|
108
|
-
if (policyContent !== null) files["opencode/docs/architecture-policy.md"] = policyContent;
|
|
109
|
-
|
|
110
|
-
const dpContent = readPackageFile("dist-assets/docs/design-patterns-policy.md");
|
|
111
|
-
if (dpContent !== null) files["opencode/docs/design-patterns-policy.md"] = dpContent;
|
|
112
|
-
|
|
113
|
-
const visualValidationGuide = readPackageFile("dist-assets/docs/visual-validation-guide.md");
|
|
114
|
-
if (visualValidationGuide !== null) files["opencode/docs/visual-validation-guide.md"] = visualValidationGuide;
|
|
115
|
-
|
|
116
|
-
const governancePolicies = discoverPackageFiles("dist-assets/docs/policies");
|
|
117
|
-
for (const [relPath, content] of Object.entries(governancePolicies)) {
|
|
118
|
-
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
119
|
-
files[targetPath] = content;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const compatibilityDocs = discoverPackageFiles("dist-assets/docs/compatibility");
|
|
123
|
-
for (const [relPath, content] of Object.entries(compatibilityDocs)) {
|
|
124
|
-
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
125
|
-
files[targetPath] = content;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const qualityReferenceFiles = discoverPackageFiles("dist-assets/docs/references");
|
|
129
|
-
for (const [relPath, content] of Object.entries(qualityReferenceFiles)) {
|
|
130
|
-
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
131
|
-
files[targetPath] = content;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const workflowProfileFiles = discoverPackageFiles("dist-assets/docs/profiles");
|
|
135
|
-
for (const [relPath, content] of Object.entries(workflowProfileFiles)) {
|
|
136
|
-
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
137
|
-
files[targetPath] = content;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const templateFiles = discoverPackageFiles("dist-assets/templates");
|
|
141
|
-
for (const [relPath, content] of Object.entries(templateFiles)) {
|
|
142
|
-
if (!includeFormalEvidence && relPath.includes("/owner-evidence/")) continue;
|
|
143
|
-
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
144
|
-
files[targetPath] = content;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const schemaFiles = discoverPackageFiles("dist-assets/schemas");
|
|
148
|
-
for (const [relPath, content] of Object.entries(schemaFiles)) {
|
|
149
|
-
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
150
|
-
files[targetPath] = content;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return files;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function buildExtraFiles() {
|
|
157
|
-
const files = {};
|
|
158
|
-
const exampleFiles = discoverPackageFiles("dist-assets/examples");
|
|
159
|
-
for (const [relPath, content] of Object.entries(exampleFiles)) {
|
|
160
|
-
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
161
|
-
files[targetPath] = content;
|
|
162
|
-
}
|
|
163
|
-
return files;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const STANDARD_FILES = buildRuntimeFiles();
|
|
167
|
-
const FULL_FILES = { ...buildRuntimeFiles({ includeFormalEvidence: true }), ...buildExtraFiles() };
|
|
168
|
-
|
|
169
|
-
export const PROFILE_FILES = {
|
|
170
|
-
standard: {
|
|
171
|
-
...COMMON_FILES,
|
|
172
|
-
...STANDARD_FILES
|
|
173
|
-
},
|
|
174
|
-
full: {
|
|
175
|
-
...COMMON_FILES,
|
|
176
|
-
...FULL_FILES
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
export function isValidProfile(profile) {
|
|
181
|
-
return profile === "standard" || profile === "full";
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function getTemplateFiles(profile = "standard") {
|
|
185
|
-
return PROFILE_FILES[profile] ?? PROFILE_FILES.standard;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export function getManagedBlocks() {
|
|
189
|
-
return [
|
|
190
|
-
...FULL_PRIMARY_AGENTS.map((agent) => `opencode.jsonc:agent.${getCanonicalAgentName(agent)}`),
|
|
191
|
-
...FULL_SKILLS.map((skill) => `opencode.jsonc:agent.${getCanonicalAgentName(skill)}`),
|
|
192
|
-
"opencode.jsonc:command.atlas",
|
|
193
|
-
"opencode.jsonc:command.run",
|
|
194
|
-
"opencode.jsonc:command.discover",
|
|
195
|
-
"opencode.jsonc:command.spec-create",
|
|
196
|
-
"opencode.jsonc:command.spec-review",
|
|
197
|
-
"opencode.jsonc:command.spec-implement",
|
|
198
|
-
"opencode.jsonc:command.plan",
|
|
199
|
-
"opencode.jsonc:command.implement",
|
|
200
|
-
"opencode.jsonc:command.validate",
|
|
201
|
-
"opencode.jsonc:command.audit",
|
|
202
|
-
"opencode.jsonc:command.optimize-tokens",
|
|
203
|
-
"opencode.jsonc:command.update-memory",
|
|
204
|
-
"opencode.jsonc:command.release",
|
|
205
|
-
"opencode.jsonc:command.deploy"
|
|
206
|
-
];
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export function buildAiWorkflowConfig({ profile, managedFiles, managedLinks = [] }) {
|
|
210
|
-
const version = getPackageVersion();
|
|
211
|
-
const now = new Date().toISOString();
|
|
212
|
-
|
|
213
|
-
return {
|
|
214
|
-
package: "@williambeto/ai-workflow",
|
|
215
|
-
version,
|
|
216
|
-
runtime: "opencode",
|
|
217
|
-
profile,
|
|
218
|
-
installMode: "project-local",
|
|
219
|
-
generatedAt: now,
|
|
220
|
-
paths: {
|
|
221
|
-
agents: ".ai-workflow/opencode/agents",
|
|
222
|
-
commands: ".ai-workflow/opencode/commands",
|
|
223
|
-
skills: ".ai-workflow/opencode/skills",
|
|
224
|
-
policies: ".ai-workflow/opencode/docs/policies",
|
|
225
|
-
harness: ".ai-workflow/harness",
|
|
226
|
-
schemas: ".ai-workflow/schemas"
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
}
|