@xenonbyte/da-vinci-workflow 0.1.26 → 0.2.2
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 +31 -0
- package/README.md +28 -65
- package/README.zh-CN.md +28 -65
- package/bin/da-vinci-tui.js +8 -0
- package/commands/claude/dv/continue.md +5 -0
- package/commands/codex/prompts/dv-continue.md +6 -1
- package/commands/gemini/dv/continue.toml +5 -0
- package/commands/templates/dv-continue.shared.md +33 -0
- package/docs/dv-command-reference.md +35 -0
- package/docs/execution-chain-migration.md +46 -0
- package/docs/execution-chain-plan.md +125 -0
- package/docs/prompt-entrypoints.md +8 -0
- package/docs/skill-usage.md +217 -0
- package/docs/workflow-examples.md +10 -0
- package/docs/workflow-overview.md +26 -0
- package/docs/zh-CN/dv-command-reference.md +35 -0
- package/docs/zh-CN/execution-chain-migration.md +46 -0
- package/docs/zh-CN/prompt-entrypoints.md +8 -0
- package/docs/zh-CN/skill-usage.md +217 -0
- package/docs/zh-CN/workflow-examples.md +10 -0
- package/docs/zh-CN/workflow-overview.md +26 -0
- package/lib/artifact-parsers.js +120 -0
- package/lib/audit.js +61 -0
- package/lib/cli.js +351 -13
- package/lib/diff-spec.js +242 -0
- package/lib/execution-signals.js +136 -0
- package/lib/lint-bindings.js +143 -0
- package/lib/lint-spec.js +408 -0
- package/lib/lint-tasks.js +176 -0
- package/lib/planning-parsers.js +567 -0
- package/lib/scaffold.js +193 -0
- package/lib/scope-check.js +603 -0
- package/lib/sidecars.js +369 -0
- package/lib/supervisor-review.js +28 -3
- package/lib/utils.js +10 -2
- package/lib/verify.js +652 -0
- package/lib/workflow-contract.js +107 -0
- package/lib/workflow-persisted-state.js +297 -0
- package/lib/workflow-state.js +785 -0
- package/package.json +13 -3
- package/references/artifact-templates.md +26 -0
- package/references/checkpoints.md +14 -0
- package/references/modes.md +10 -0
- package/tui/catalog.js +1190 -0
- package/tui/index.js +727 -0
package/lib/scaffold.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { STATUS } = require("./workflow-contract");
|
|
4
|
+
const { writeFileAtomic } = require("./utils");
|
|
5
|
+
const {
|
|
6
|
+
unique,
|
|
7
|
+
resolveChangeDir,
|
|
8
|
+
parseBindingsArtifact,
|
|
9
|
+
readChangeArtifacts,
|
|
10
|
+
readArtifactTexts
|
|
11
|
+
} = require("./planning-parsers");
|
|
12
|
+
|
|
13
|
+
function sanitizeRouteToFileName(route) {
|
|
14
|
+
const normalized = String(route || "")
|
|
15
|
+
.trim()
|
|
16
|
+
.split(/[?#]/, 1)[0]
|
|
17
|
+
.replace(/\\/g, "/");
|
|
18
|
+
if (!normalized || normalized === "/") {
|
|
19
|
+
return "index.html";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const rawSegments = normalized.replace(/^\/+/, "").split("/").filter(Boolean);
|
|
23
|
+
const safeSegments = [];
|
|
24
|
+
let hasTraversal = false;
|
|
25
|
+
|
|
26
|
+
for (const segment of rawSegments) {
|
|
27
|
+
if (segment === "." || segment === "..") {
|
|
28
|
+
hasTraversal = true;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const safeSegment = String(segment)
|
|
32
|
+
.replace(/[^\w.-]+/g, "-")
|
|
33
|
+
.replace(/^-+|-+$/g, "");
|
|
34
|
+
safeSegments.push(safeSegment || "index");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (safeSegments.length === 0) {
|
|
38
|
+
if (hasTraversal) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return "index.html";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let relativePath = safeSegments.join("/");
|
|
45
|
+
if (!path.extname(relativePath)) {
|
|
46
|
+
relativePath = `${relativePath}.html`;
|
|
47
|
+
}
|
|
48
|
+
if (hasTraversal) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return relativePath;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function buildSkeletonDocument(mapping) {
|
|
55
|
+
return [
|
|
56
|
+
"<!doctype html>",
|
|
57
|
+
'<html lang="en">',
|
|
58
|
+
"<head>",
|
|
59
|
+
' <meta charset="utf-8" />',
|
|
60
|
+
' <meta name="viewport" content="width=device-width,initial-scale=1" />',
|
|
61
|
+
` <title>${mapping.designPage || "Scaffold Page"}</title>`,
|
|
62
|
+
"</head>",
|
|
63
|
+
"<body>",
|
|
64
|
+
" <!-- TODO(scaffold): Replace placeholder skeleton with production implementation. -->",
|
|
65
|
+
` <!-- mapping: ${mapping.implementation} -> ${mapping.designPage}${mapping.screenId ? ` (${mapping.screenId})` : ""} -->`,
|
|
66
|
+
" <main>",
|
|
67
|
+
" <section data-scaffold-region=\"hero\">TODO: implement hero region</section>",
|
|
68
|
+
" <section data-scaffold-region=\"content\">TODO: implement content regions</section>",
|
|
69
|
+
" <section data-scaffold-region=\"actions\">TODO: implement action regions</section>",
|
|
70
|
+
" </main>",
|
|
71
|
+
"</body>",
|
|
72
|
+
"</html>",
|
|
73
|
+
""
|
|
74
|
+
].join("\n");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function scaffoldFromBindings(projectPathInput, options = {}) {
|
|
78
|
+
const projectRoot = path.resolve(projectPathInput || process.cwd());
|
|
79
|
+
const requestedChangeId = options.changeId ? String(options.changeId).trim() : "";
|
|
80
|
+
const outputDir = path.resolve(options.outputDir || path.join(projectRoot, ".da-vinci", "scaffold"));
|
|
81
|
+
const result = {
|
|
82
|
+
status: STATUS.PASS,
|
|
83
|
+
failures: [],
|
|
84
|
+
warnings: [],
|
|
85
|
+
notes: [],
|
|
86
|
+
projectRoot,
|
|
87
|
+
changeId: null,
|
|
88
|
+
outputDir,
|
|
89
|
+
files: [],
|
|
90
|
+
mvpScope: "single static HTML skeleton per binding; multi-framework expansion is intentionally out of scope."
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const resolved = resolveChangeDir(projectRoot, requestedChangeId);
|
|
94
|
+
result.failures.push(...resolved.failures);
|
|
95
|
+
result.notes.push(...resolved.notes);
|
|
96
|
+
if (!resolved.changeDir) {
|
|
97
|
+
result.status = STATUS.BLOCK;
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
result.changeId = resolved.changeId;
|
|
101
|
+
|
|
102
|
+
const artifacts = readArtifactTexts(readChangeArtifacts(projectRoot, resolved.changeId));
|
|
103
|
+
if (!artifacts.bindings) {
|
|
104
|
+
result.failures.push("Missing `pencil-bindings.md`; scaffold requires explicit bindings.");
|
|
105
|
+
result.status = STATUS.BLOCK;
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const bindings = parseBindingsArtifact(artifacts.bindings);
|
|
110
|
+
if (bindings.mappings.length === 0) {
|
|
111
|
+
result.failures.push("No bindings parsed from `pencil-bindings.md`; scaffold output would be ambiguous.");
|
|
112
|
+
result.status = STATUS.BLOCK;
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
117
|
+
const outputRoot = path.resolve(outputDir);
|
|
118
|
+
const outputRootPrefix = outputRoot.endsWith(path.sep) ? outputRoot : `${outputRoot}${path.sep}`;
|
|
119
|
+
for (const mapping of bindings.mappings) {
|
|
120
|
+
const targetRelativePath = sanitizeRouteToFileName(mapping.implementation);
|
|
121
|
+
if (!targetRelativePath) {
|
|
122
|
+
result.failures.push(
|
|
123
|
+
`Blocked scaffold route \`${mapping.implementation}\`: traversal segments are not allowed.`
|
|
124
|
+
);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const targetPath = path.resolve(outputRoot, targetRelativePath);
|
|
129
|
+
if (targetPath !== outputRoot && !targetPath.startsWith(outputRootPrefix)) {
|
|
130
|
+
result.failures.push(
|
|
131
|
+
`Blocked scaffold route \`${mapping.implementation}\`: target path escapes output directory.`
|
|
132
|
+
);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
136
|
+
writeFileAtomic(targetPath, buildSkeletonDocument(mapping));
|
|
137
|
+
result.files.push({
|
|
138
|
+
mapping: mapping.implementation,
|
|
139
|
+
designPage: mapping.designPage,
|
|
140
|
+
path: targetPath
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (result.failures.length > 0) {
|
|
145
|
+
result.status = STATUS.BLOCK;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
result.files = result.files.sort((left, right) => left.path.localeCompare(right.path));
|
|
149
|
+
result.notes.push("Scaffold output is TODO-marked and reviewable only; it is not final code generation.");
|
|
150
|
+
result.notes.push(
|
|
151
|
+
"Run `da-vinci verify-bindings`, `da-vinci verify-implementation`, and `da-vinci verify-structure` before accepting scaffold output."
|
|
152
|
+
);
|
|
153
|
+
result.notes.push(
|
|
154
|
+
"Scaffold boundaries are derived from `pencil-bindings.md` implementation-to-design mappings; no framework-specific expansion is performed."
|
|
155
|
+
);
|
|
156
|
+
result.warnings = unique(result.warnings);
|
|
157
|
+
result.notes = unique(result.notes);
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function formatScaffoldReport(result) {
|
|
162
|
+
const lines = [
|
|
163
|
+
"Da Vinci scaffold",
|
|
164
|
+
`Project: ${result.projectRoot}`,
|
|
165
|
+
`Change: ${result.changeId || "(not selected)"}`,
|
|
166
|
+
`Status: ${result.status}`,
|
|
167
|
+
`Output dir: ${result.outputDir}`
|
|
168
|
+
];
|
|
169
|
+
if (result.failures.length > 0) {
|
|
170
|
+
lines.push("", "Failures:");
|
|
171
|
+
for (const failure of result.failures) {
|
|
172
|
+
lines.push(`- ${failure}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (result.files.length > 0) {
|
|
176
|
+
lines.push("", "Generated files:");
|
|
177
|
+
for (const file of result.files) {
|
|
178
|
+
lines.push(`- ${file.path} (${file.mapping} -> ${file.designPage})`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (result.notes.length > 0) {
|
|
182
|
+
lines.push("", "Notes:");
|
|
183
|
+
for (const note of result.notes) {
|
|
184
|
+
lines.push(`- ${note}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return lines.join("\n");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = {
|
|
191
|
+
scaffoldFromBindings,
|
|
192
|
+
formatScaffoldReport
|
|
193
|
+
};
|