@williambeto/ai-workflow 2.3.1 → 2.3.5
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/CHANGELOG.md +10 -0
- package/README.md +14 -10
- package/dist-assets/agents/astra.md +1 -0
- package/dist-assets/skills/architecture/SKILL.md +1 -1
- package/dist-assets/skills/backend-development/SKILL.md +1 -1
- package/dist-assets/skills/deployment/SKILL.md +1 -1
- package/dist-assets/skills/design-principles/SKILL.md +1 -1
- package/dist-assets/skills/documentation/SKILL.md +1 -1
- package/dist-assets/skills/frontend-development/SKILL.md +2 -1
- package/dist-assets/skills/full-stack-development/SKILL.md +1 -1
- package/dist-assets/skills/optimize-tokens/SKILL.md +1 -1
- package/dist-assets/skills/pr-workflow/SKILL.md +1 -1
- package/dist-assets/skills/product-discovery/SKILL.md +1 -1
- package/dist-assets/skills/product-planning/SKILL.md +1 -1
- package/dist-assets/skills/project-memory/SKILL.md +1 -1
- package/dist-assets/skills/prompt-engineer/SKILL.md +1 -1
- package/dist-assets/skills/qa-workflow/SKILL.md +1 -1
- package/dist-assets/skills/refactoring/SKILL.md +1 -1
- package/dist-assets/skills/release-workflow/SKILL.md +1 -1
- package/dist-assets/skills/spec-driven-development/SKILL.md +1 -1
- package/dist-assets/skills/technical-leadership/SKILL.md +1 -1
- package/dist-assets/skills/ui-ux-design/SKILL.md +1 -1
- package/docs/getting-started/upgrading-to-v2.md +10 -3
- package/package.json +3 -2
- package/src/commands/execute.js +8 -0
- package/src/core/finalization/finalizer.js +39 -0
- package/src/core/templates.js +8 -8
- package/src/core/validation/artifact-fidelity-gate.js +63 -0
- package/src/core/validation/delivery-decision-engine.js +69 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## [2.3.2] - 2026-06-19
|
|
2
|
+
|
|
3
|
+
### Documentation
|
|
4
|
+
- Clarified first-time usage with `npx @williambeto/ai-workflow init`.
|
|
5
|
+
- Clarified local installed-package alias usage with `npx aw init`, `npx aw doctor`, and `npx aw run`.
|
|
6
|
+
|
|
7
|
+
### Notes
|
|
8
|
+
- No CLI behavior changed.
|
|
9
|
+
- No runtime behavior changed.
|
|
10
|
+
|
|
1
11
|
## [2.3.1] - 2026-06-18
|
|
2
12
|
|
|
3
13
|
### Fixed
|
package/README.md
CHANGED
|
@@ -21,26 +21,30 @@ The workflow the product guarantees is:
|
|
|
21
21
|
|
|
22
22
|
In practice, the kit is neither a frontend framework nor a UI library. It is an operational layer for software agents: it installs instructions and guardrails so that the agent can work more effectively inside the user's repository.
|
|
23
23
|
|
|
24
|
-
## Installation
|
|
24
|
+
## Installation and Usage
|
|
25
25
|
|
|
26
|
-
###
|
|
26
|
+
### First-time usage
|
|
27
|
+
|
|
28
|
+
Use the full package command when running the kit before it is installed in a project:
|
|
27
29
|
|
|
28
30
|
```bash
|
|
29
|
-
|
|
31
|
+
npx @williambeto/ai-workflow init
|
|
30
32
|
```
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
### Short alias after installation
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
After installing AI Workflow Kit in a project, the shorter `aw` binary is available through `node_modules/.bin`:
|
|
35
37
|
|
|
36
38
|
```bash
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
npm install -D @williambeto/ai-workflow
|
|
40
|
+
|
|
41
|
+
npx aw init
|
|
42
|
+
npx aw doctor
|
|
43
|
+
npx aw run "Atlas, list all agents, subagents, skills, and commands in the project. Present them in a table with their name, description, and usage examples."
|
|
42
44
|
```
|
|
43
45
|
|
|
46
|
+
The `aw` binary is an alias for the same CLI entrypoint as `ai-workflow`.
|
|
47
|
+
|
|
44
48
|
The CLI receives the natural request, classifies the intent, plans the workflow, switch/creates branch safely, delegates coding tasks to the OpenCode runtime adapter, validates changes, and runs bounded remediation if necessary before outputting the handoff summary.
|
|
45
49
|
|
|
46
50
|
## Modes and profiles
|
|
@@ -38,6 +38,7 @@ Astra implements only after branch gate is satisfied. It keeps changes bounded t
|
|
|
38
38
|
* Run relevant validation after implementation.
|
|
39
39
|
* Preserve existing behavior and unrelated dirty work.
|
|
40
40
|
* Load `frontend-development` and `ui-ux-design` skills only when they apply. Use selected profile as quality guidance.
|
|
41
|
+
* Implement all frontend, backend, or full-stack tasks using framework-native or stack-native paradigms (e.g., framework-native React/Next.js components, Vue/Nuxt files, or project stack paradigms rather than generic standalone HTML/JS prototypes) unless explicitly asked otherwise by the user.
|
|
41
42
|
* Policies: `01-BRANCH_GATE.md`, `03-QUALITY_GATE.md`, `06-FINAL_EVIDENCE_CONTRACT.md`, `PROCEDURE_UI_CHECKLIST.md`.
|
|
42
43
|
|
|
43
44
|
## Allowed tools
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: architecture
|
|
3
3
|
description: Skill-backed capability for architectural design and modularity
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Architecture PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: backend-development
|
|
3
3
|
description: Skill-backed capability for backend APIs, routes, and data persistence
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Backend Development PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: deployment
|
|
3
3
|
description: Skill-backed capability for deployment planning, environments, and validation
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Deployment PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: design-principles
|
|
3
3
|
description: Skill-backed capability for clean, maintainable software design patterns
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Design Principles PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: documentation
|
|
3
3
|
description: Skill-backed capability for writing and verifying technical documentation
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Documentation PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: frontend-development
|
|
3
3
|
description: Skill-backed capability for frontend UI components and styles
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Frontend Development PromptContract
|
|
@@ -29,6 +29,7 @@ Do not trust:
|
|
|
29
29
|
## Constraints
|
|
30
30
|
- Follow accessibility (WCAG/ARIA) guidelines.
|
|
31
31
|
- Support responsive layouts.
|
|
32
|
+
- Deliver UI/frontend artifacts native to the detected or specified project stack (using framework-native or stack-native architectures like React, Vue, Nuxt, WordPress PHP, etc. rather than standalone HTML/JS files) unless explicitly specified otherwise.
|
|
32
33
|
- Do not claim execution without evidence.
|
|
33
34
|
|
|
34
35
|
## Allowed tools
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: full-stack-development
|
|
3
3
|
description: Skill-backed capability for coordinating frontend and backend updates
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Full-Stack Development PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: optimize-tokens
|
|
3
3
|
description: Skill-backed capability for reducing context size and optimizing token economy
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Optimize Tokens PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pr-workflow
|
|
3
3
|
description: Skill-backed capability for branch gates and pull request verification
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# PR Workflow PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: product-discovery
|
|
3
3
|
description: Skill-backed capability for project and product discovery work
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Product Discovery PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: product-planning
|
|
3
3
|
description: Skill-backed capability for translating needs into scoped requirements
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Product Planning PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: project-memory
|
|
3
3
|
description: Skill-backed capability for project memory, ADRs, and decision records
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Project Memory PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: prompt-engineer
|
|
3
3
|
description: Skill-backed capability for refining prompts and instructions
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Prompt Engineering PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: qa-workflow
|
|
3
3
|
description: Skill-backed capability for quality gates and validation tests
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Quality Assurance PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: refactoring
|
|
3
3
|
description: Skill-backed capability for code refactoring and bug-fixing
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Refactoring and Remediation PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: release-workflow
|
|
3
3
|
description: Skill-backed capability for release management and validation
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Release Workflow PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: spec-driven-development
|
|
3
3
|
description: Skill-backed capability for converting requirements to testable specs
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Spec-Driven Development PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: technical-leadership
|
|
3
3
|
description: Skill-backed capability for high-level technical decisions and reviews
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Technical Leadership PromptContract
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ui-ux-design
|
|
3
3
|
description: Skill-backed capability for user interface and experience design guidelines
|
|
4
|
-
governance:
|
|
4
|
+
governance: ../../docs/policies/SKILLS_COMMON_GOVERNANCE.md
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# UI-UX Design PromptContract
|
|
@@ -19,10 +19,17 @@ Version `2.x` is a breaking public line from the previous `1.x` line. Test upgra
|
|
|
19
19
|
|
|
20
20
|
4. Inspect initialization before applying it:
|
|
21
21
|
|
|
22
|
+
For first-time or pre-installation usage:
|
|
22
23
|
```bash
|
|
23
|
-
ai-workflow init --dry-run
|
|
24
|
-
ai-workflow init --yes
|
|
25
|
-
|
|
24
|
+
npx @williambeto/ai-workflow init --dry-run
|
|
25
|
+
npx @williambeto/ai-workflow init --yes
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
After installing AI Workflow Kit locally in your project:
|
|
29
|
+
```bash
|
|
30
|
+
npx aw init --dry-run
|
|
31
|
+
npx aw init --yes
|
|
32
|
+
npx aw doctor
|
|
26
33
|
```
|
|
27
34
|
|
|
28
35
|
## Main changes in version 2
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williambeto/ai-workflow",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.5",
|
|
4
4
|
"description": "AI Workflow Kit — OpenCode-first software delivery workflow with agents, commands, skills, validation, and evidence",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "José Willams",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"url": "https://github.com/williambeto/ai-workflow/issues"
|
|
30
30
|
},
|
|
31
31
|
"bin": {
|
|
32
|
-
"ai-workflow": "bin/ai-workflow.js"
|
|
32
|
+
"ai-workflow": "bin/ai-workflow.js",
|
|
33
|
+
"aw": "bin/ai-workflow.js"
|
|
33
34
|
},
|
|
34
35
|
"files": [
|
|
35
36
|
"bin",
|
package/src/commands/execute.js
CHANGED
|
@@ -327,6 +327,14 @@ export async function runExecute({ cwd, naturalRequest, taskSlug: taskSlugOverri
|
|
|
327
327
|
console.log(`\n[FINALIZER BLOCKED] BLOCKED: workspace did not stabilize`);
|
|
328
328
|
console.log(`Final changes detected:`, integrity.changes);
|
|
329
329
|
finalStatus = "FAIL_QUALITY_GATE";
|
|
330
|
+
} else {
|
|
331
|
+
// Workspace stabilized, now run Artifact Fidelity Gate
|
|
332
|
+
const fidelityResult = await finalizer.verifyFidelity(result.evidence?.changedFiles);
|
|
333
|
+
if (!fidelityResult.passed) {
|
|
334
|
+
console.log(`\n[FINALIZER BLOCKED] BLOCKED: Artifact fidelity check failed!`);
|
|
335
|
+
console.log(`Reason: ${fidelityResult.reason}`);
|
|
336
|
+
finalStatus = "FAIL_QUALITY_GATE";
|
|
337
|
+
}
|
|
330
338
|
}
|
|
331
339
|
|
|
332
340
|
const finalState = finalStatus === "PASS"
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { WorkspaceSnapshot } from "./workspace-snapshot.js";
|
|
2
|
+
import { DeliveryDecisionEngine } from "../validation/delivery-decision-engine.js";
|
|
3
|
+
import { ArtifactFidelityGate } from "../validation/artifact-fidelity-gate.js";
|
|
4
|
+
import { execSync } from "node:child_process";
|
|
2
5
|
|
|
3
6
|
export class Finalizer {
|
|
4
7
|
constructor({ cwd = process.cwd() } = {}) {
|
|
@@ -6,6 +9,42 @@ export class Finalizer {
|
|
|
6
9
|
this.snapshotManager = new WorkspaceSnapshot({ cwd });
|
|
7
10
|
}
|
|
8
11
|
|
|
12
|
+
getChangedFilesFromGit() {
|
|
13
|
+
try {
|
|
14
|
+
const files = new Set();
|
|
15
|
+
// Unstaged changes
|
|
16
|
+
const diff = execSync("git diff --name-only HEAD", { cwd: this.cwd, encoding: "utf8" });
|
|
17
|
+
diff.split("\n").map(f => f.trim()).filter(Boolean).forEach(f => files.add(f));
|
|
18
|
+
|
|
19
|
+
// Untracked/staged status
|
|
20
|
+
const status = execSync("git status --short --untracked-files=all", { cwd: this.cwd, encoding: "utf8" });
|
|
21
|
+
status.split("\n").forEach(line => {
|
|
22
|
+
const match = line.match(/^(?:[ MADRCU?!]{2}\s+|[MADRCU?!]\s+)(.+)$/);
|
|
23
|
+
if (match) {
|
|
24
|
+
const candidate = match[1].trim();
|
|
25
|
+
const renamed = candidate.includes(" -> ") ? candidate.split(" -> ").at(-1) : candidate;
|
|
26
|
+
if (renamed) files.add(renamed);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return [...files].sort();
|
|
30
|
+
} catch {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async verifyFidelity(changedFiles = null) {
|
|
36
|
+
const filesToCheck = changedFiles || this.getChangedFilesFromGit();
|
|
37
|
+
const decisionEngine = new DeliveryDecisionEngine({ cwd: this.cwd });
|
|
38
|
+
const deliveryClass = await decisionEngine.determineClass(filesToCheck);
|
|
39
|
+
const fidelityGate = new ArtifactFidelityGate({ cwd: this.cwd, deliveryClass });
|
|
40
|
+
const result = await fidelityGate.verify(filesToCheck);
|
|
41
|
+
return {
|
|
42
|
+
passed: result.passed,
|
|
43
|
+
reason: result.reason,
|
|
44
|
+
deliveryClass
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
9
48
|
/**
|
|
10
49
|
* Compares the current workspace state with a previously captured snapshot.
|
|
11
50
|
* @param {Object} previousSnapshot - The snapshot object captured earlier.
|
package/src/core/templates.js
CHANGED
|
@@ -105,35 +105,35 @@ function buildRuntimeFiles({ includeFormalEvidence = false } = {}) {
|
|
|
105
105
|
if (consumerQuickstart !== null) files["QUICKSTART.md"] = consumerQuickstart;
|
|
106
106
|
|
|
107
107
|
const policyContent = readPackageFile("dist-assets/docs/architecture-policy.md");
|
|
108
|
-
if (policyContent !== null) files["docs/architecture-policy.md"] = policyContent;
|
|
108
|
+
if (policyContent !== null) files["opencode/docs/architecture-policy.md"] = policyContent;
|
|
109
109
|
|
|
110
110
|
const dpContent = readPackageFile("dist-assets/docs/design-patterns-policy.md");
|
|
111
|
-
if (dpContent !== null) files["docs/design-patterns-policy.md"] = dpContent;
|
|
111
|
+
if (dpContent !== null) files["opencode/docs/design-patterns-policy.md"] = dpContent;
|
|
112
112
|
|
|
113
113
|
const visualValidationGuide = readPackageFile("dist-assets/docs/visual-validation-guide.md");
|
|
114
|
-
if (visualValidationGuide !== null) files["docs/visual-validation-guide.md"] = visualValidationGuide;
|
|
114
|
+
if (visualValidationGuide !== null) files["opencode/docs/visual-validation-guide.md"] = visualValidationGuide;
|
|
115
115
|
|
|
116
116
|
const governancePolicies = discoverPackageFiles("dist-assets/docs/policies");
|
|
117
117
|
for (const [relPath, content] of Object.entries(governancePolicies)) {
|
|
118
|
-
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
118
|
+
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
119
119
|
files[targetPath] = content;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
const compatibilityDocs = discoverPackageFiles("dist-assets/docs/compatibility");
|
|
123
123
|
for (const [relPath, content] of Object.entries(compatibilityDocs)) {
|
|
124
|
-
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
124
|
+
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
125
125
|
files[targetPath] = content;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
const qualityReferenceFiles = discoverPackageFiles("dist-assets/docs/references");
|
|
129
129
|
for (const [relPath, content] of Object.entries(qualityReferenceFiles)) {
|
|
130
|
-
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
130
|
+
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
131
131
|
files[targetPath] = content;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
const workflowProfileFiles = discoverPackageFiles("dist-assets/docs/profiles");
|
|
135
135
|
for (const [relPath, content] of Object.entries(workflowProfileFiles)) {
|
|
136
|
-
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
136
|
+
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
137
137
|
files[targetPath] = content;
|
|
138
138
|
}
|
|
139
139
|
|
|
@@ -221,7 +221,7 @@ export function buildAiWorkflowConfig({ profile, managedFiles, managedLinks = []
|
|
|
221
221
|
agents: ".ai-workflow/opencode/agents",
|
|
222
222
|
commands: ".ai-workflow/opencode/commands",
|
|
223
223
|
skills: ".ai-workflow/opencode/skills",
|
|
224
|
-
policies: ".ai-workflow/docs/policies",
|
|
224
|
+
policies: ".ai-workflow/opencode/docs/policies",
|
|
225
225
|
harness: ".ai-workflow/harness",
|
|
226
226
|
schemas: ".ai-workflow/schemas"
|
|
227
227
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export class ArtifactFidelityGate {
|
|
5
|
+
constructor({ cwd = process.cwd(), deliveryClass } = {}) {
|
|
6
|
+
this.cwd = cwd;
|
|
7
|
+
this.deliveryClass = deliveryClass;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async verify(changedFiles = []) {
|
|
11
|
+
if (changedFiles.length === 0) {
|
|
12
|
+
return { passed: true };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (this.deliveryClass === "framework-native-frontend") {
|
|
16
|
+
for (const file of changedFiles) {
|
|
17
|
+
const ext = path.extname(file).toLowerCase();
|
|
18
|
+
|
|
19
|
+
// 1. Forbid adding/modifying HTML files that are not SPA entry points
|
|
20
|
+
if (ext === ".html" || ext === ".htm") {
|
|
21
|
+
const basename = path.basename(file).toLowerCase();
|
|
22
|
+
const isSpaEntry = basename === "index.html" &&
|
|
23
|
+
(file === "index.html" || file === "public/index.html" || file === "src/index.html");
|
|
24
|
+
if (!isSpaEntry) {
|
|
25
|
+
return {
|
|
26
|
+
passed: false,
|
|
27
|
+
reason: `Fidelity Violation: standalone HTML file '${file}' is not permitted in a framework-native frontend project. UI must be implemented within framework components (e.g. React/Vue/Next/Nuxt files) instead of static prototypes.`
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check if SPA entry imports framework libraries via CDN script tags
|
|
32
|
+
try {
|
|
33
|
+
const content = await fs.readFile(path.join(this.cwd, file), "utf8");
|
|
34
|
+
if (content.includes("unpkg.com") || content.includes("cdnjs.cloudflare.com") || content.includes("cdn.jsdelivr.net")) {
|
|
35
|
+
if (content.includes("vue") || content.includes("react")) {
|
|
36
|
+
return {
|
|
37
|
+
passed: false,
|
|
38
|
+
reason: `Fidelity Violation: SPA entry point '${file}' imports framework library via CDN scripts. Framework dependencies must be installed and resolved locally via npm/package.json.`
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
// ignore
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (this.deliveryClass === "wordpress-php") {
|
|
50
|
+
for (const file of changedFiles) {
|
|
51
|
+
const ext = path.extname(file).toLowerCase();
|
|
52
|
+
if (ext === ".html" || ext === ".htm") {
|
|
53
|
+
return {
|
|
54
|
+
passed: false,
|
|
55
|
+
reason: `Fidelity Violation: standalone HTML file '${file}' is not permitted in a WordPress project. UI templates must be implemented in PHP files within theme or plugin structures.`
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { passed: true };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { StackDetector } from "./stack-detector.js";
|
|
4
|
+
|
|
5
|
+
export class DeliveryDecisionEngine {
|
|
6
|
+
constructor({ cwd = process.cwd() } = {}) {
|
|
7
|
+
this.cwd = cwd;
|
|
8
|
+
this.stackDetector = new StackDetector({ cwd });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async determineClass(changedFiles = []) {
|
|
12
|
+
if (changedFiles.length === 0) {
|
|
13
|
+
return "empty";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 1. Docs check
|
|
17
|
+
const isDocs = changedFiles.every(file =>
|
|
18
|
+
file.endsWith(".md") || file.endsWith(".txt") || file.startsWith("docs/")
|
|
19
|
+
);
|
|
20
|
+
if (isDocs) {
|
|
21
|
+
return "docs-only";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 2. Detect project stacks
|
|
25
|
+
const stacks = await this.stackDetector.detect();
|
|
26
|
+
|
|
27
|
+
// Check package.json for frontend frameworks
|
|
28
|
+
let usesJsFramework = false;
|
|
29
|
+
try {
|
|
30
|
+
const pkgPath = path.join(this.cwd, "package.json");
|
|
31
|
+
const pkgContent = await fs.readFile(pkgPath, "utf8");
|
|
32
|
+
const pkg = JSON.parse(pkgContent);
|
|
33
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
34
|
+
|
|
35
|
+
const frameworks = ["react", "next", "vue", "nuxt", "svelte", "solid-js", "preact", "quasar", "@angular/core"];
|
|
36
|
+
usesJsFramework = frameworks.some(fw => deps[fw] !== undefined);
|
|
37
|
+
} catch {
|
|
38
|
+
// package.json doesn't exist or is invalid
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check for WordPress
|
|
42
|
+
const isWordPress = await fs.access(path.join(this.cwd, "wp-config.php")).then(() => true).catch(() => false) ||
|
|
43
|
+
await fs.access(path.join(this.cwd, "wp-content")).then(() => true).catch(() => false);
|
|
44
|
+
|
|
45
|
+
// Analyze changed files for UI/frontend assets
|
|
46
|
+
const hasFrontendFiles = changedFiles.some(file =>
|
|
47
|
+
/\.(html?|css|scss|sass|less|jsx|tsx|vue|svelte)$/i.test(file) ||
|
|
48
|
+
(/\.js$/i.test(file) && !file.includes("config") && !file.includes("test"))
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (isWordPress) {
|
|
52
|
+
return "wordpress-php";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (usesJsFramework && hasFrontendFiles) {
|
|
56
|
+
return "framework-native-frontend";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (hasFrontendFiles) {
|
|
60
|
+
return "standalone-web";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (stacks.includes("python") || stacks.includes("php") || (stacks.includes("node") && !usesJsFramework)) {
|
|
64
|
+
return "backend-service";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return "generic-code";
|
|
68
|
+
}
|
|
69
|
+
}
|