@xenonbyte/da-vinci-workflow 0.1.25 → 0.2.1
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 +37 -0
- package/README.md +48 -67
- package/README.zh-CN.md +36 -66
- package/SKILL.md +3 -0
- package/commands/claude/dv/continue.md +5 -0
- package/commands/claude/dv/design.md +1 -0
- package/commands/codex/prompts/dv-continue.md +6 -1
- package/commands/codex/prompts/dv-design.md +1 -0
- package/commands/gemini/dv/continue.toml +5 -0
- package/commands/gemini/dv/design.toml +1 -0
- package/commands/templates/dv-continue.shared.md +33 -0
- package/docs/dv-command-reference.md +45 -2
- package/docs/execution-chain-migration.md +46 -0
- package/docs/execution-chain-plan.md +125 -0
- package/docs/pencil-rendering-workflow.md +9 -7
- package/docs/prompt-entrypoints.md +6 -0
- package/docs/prompt-presets/README.md +4 -0
- package/docs/visual-assist-presets/README.md +4 -0
- package/docs/workflow-examples.md +23 -11
- package/docs/workflow-overview.md +27 -0
- package/docs/zh-CN/dv-command-reference.md +45 -2
- package/docs/zh-CN/execution-chain-migration.md +46 -0
- package/docs/zh-CN/pencil-rendering-workflow.md +9 -7
- package/docs/zh-CN/prompt-entrypoints.md +6 -0
- package/docs/zh-CN/prompt-presets/README.md +5 -1
- package/docs/zh-CN/visual-assist-presets/README.md +5 -1
- package/docs/zh-CN/workflow-examples.md +23 -11
- package/docs/zh-CN/workflow-overview.md +27 -0
- package/examples/greenfield-spec-markupflow/README.md +6 -1
- package/lib/artifact-parsers.js +120 -0
- package/lib/async-offload-worker.js +26 -0
- package/lib/async-offload.js +82 -0
- package/lib/audit-parsers.js +152 -32
- package/lib/audit.js +145 -23
- package/lib/cli.js +1068 -437
- package/lib/diff-spec.js +242 -0
- package/lib/execution-signals.js +136 -0
- package/lib/fs-safety.js +1 -4
- package/lib/icon-aliases.js +7 -7
- package/lib/icon-search.js +21 -14
- package/lib/icon-sync.js +220 -41
- package/lib/install.js +128 -60
- package/lib/lint-bindings.js +143 -0
- package/lib/lint-spec.js +408 -0
- package/lib/lint-tasks.js +176 -0
- package/lib/mcp-runtime-gate.js +4 -7
- package/lib/pen-persistence.js +318 -46
- package/lib/pencil-lock.js +237 -25
- package/lib/pencil-preflight.js +233 -12
- package/lib/pencil-session.js +216 -36
- 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 +82 -35
- package/lib/utils.js +129 -0
- package/lib/verify.js +652 -0
- package/lib/workflow-bootstrap.js +255 -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 +21 -3
- package/references/artifact-templates.md +26 -0
- package/references/checkpoints.md +16 -0
- package/references/design-inputs.md +2 -0
- package/references/modes.md +10 -0
- package/references/pencil-design-to-code.md +2 -0
- package/scripts/fixtures/complex-sample.pen +0 -295
- package/scripts/fixtures/mock-pencil.js +0 -49
- package/scripts/test-audit-context-delta.js +0 -446
- package/scripts/test-audit-design-supervisor.js +0 -691
- package/scripts/test-audit-safety.js +0 -92
- package/scripts/test-icon-aliases.js +0 -96
- package/scripts/test-icon-search.js +0 -77
- package/scripts/test-icon-sync.js +0 -178
- package/scripts/test-mcp-runtime-gate.js +0 -287
- package/scripts/test-mode-consistency.js +0 -344
- package/scripts/test-pen-persistence.js +0 -403
- package/scripts/test-pencil-lock.js +0 -130
- package/scripts/test-pencil-preflight.js +0 -169
- package/scripts/test-pencil-session.js +0 -192
- package/scripts/test-persistence-flows.js +0 -345
- package/scripts/test-supervisor-review-cli.js +0 -619
- package/scripts/test-supervisor-review-integration.js +0 -115
package/lib/icon-sync.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require("fs");
|
|
|
2
2
|
const https = require("https");
|
|
3
3
|
const os = require("os");
|
|
4
4
|
const path = require("path");
|
|
5
|
+
const { parseJsonText, readJsonFile, writeFileAtomic } = require("./utils");
|
|
5
6
|
const {
|
|
6
7
|
MATERIAL_ROUNDED,
|
|
7
8
|
MATERIAL_OUTLINED,
|
|
@@ -13,6 +14,15 @@ const LUCIDE_TREE_URL = "https://api.github.com/repos/lucide-icons/lucide/git/tr
|
|
|
13
14
|
const FEATHER_TREE_URL = "https://api.github.com/repos/feathericons/feather/git/trees/main?recursive=1";
|
|
14
15
|
const PHOSPHOR_TREE_URL = "https://api.github.com/repos/phosphor-icons/core/git/trees/main?recursive=1";
|
|
15
16
|
const DEFAULT_TIMEOUT_MS = 20000;
|
|
17
|
+
const DEFAULT_MAX_REDIRECTS = 4;
|
|
18
|
+
const DEFAULT_MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
19
|
+
const DEFAULT_SOURCE_CONCURRENCY = 1;
|
|
20
|
+
const DEFAULT_ICON_SOURCE_URLS = Object.freeze({
|
|
21
|
+
material: MATERIAL_METADATA_URL,
|
|
22
|
+
lucide: LUCIDE_TREE_URL,
|
|
23
|
+
feather: FEATHER_TREE_URL,
|
|
24
|
+
phosphor: PHOSPHOR_TREE_URL
|
|
25
|
+
});
|
|
16
26
|
|
|
17
27
|
function toBoolean(value) {
|
|
18
28
|
if (value === true || value === false) {
|
|
@@ -57,7 +67,7 @@ function parseGoogleMaterialMetadata(raw) {
|
|
|
57
67
|
if (objectStart < 0) {
|
|
58
68
|
throw new Error("Material metadata payload is not valid JSON.");
|
|
59
69
|
}
|
|
60
|
-
const parsed =
|
|
70
|
+
const parsed = parseJsonText(text.slice(objectStart), "Google Material metadata JSON payload");
|
|
61
71
|
const icons = Array.isArray(parsed.icons) ? parsed.icons : [];
|
|
62
72
|
const roundedFamilyKey = "Material Icons Round";
|
|
63
73
|
const outlinedFamilyKey = "Material Icons Outlined";
|
|
@@ -87,7 +97,7 @@ function parseGoogleMaterialMetadata(raw) {
|
|
|
87
97
|
|
|
88
98
|
function parseGitHubTreeIcons(raw, options) {
|
|
89
99
|
const { family, prefix, suffix } = options;
|
|
90
|
-
const parsed =
|
|
100
|
+
const parsed = parseJsonText(String(raw || ""), `GitHub icon tree JSON payload for ${family}`);
|
|
91
101
|
const tree = Array.isArray(parsed.tree) ? parsed.tree : [];
|
|
92
102
|
|
|
93
103
|
return tree
|
|
@@ -132,14 +142,47 @@ function summarizeSourceResults(sourceResults = {}) {
|
|
|
132
142
|
};
|
|
133
143
|
}
|
|
134
144
|
|
|
145
|
+
function resolveRedirectUrl(fromUrl, location) {
|
|
146
|
+
if (!location) {
|
|
147
|
+
return "";
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
const resolved = new URL(String(location), fromUrl);
|
|
151
|
+
return resolved.protocol === "https:" ? resolved.toString() : "";
|
|
152
|
+
} catch (error) {
|
|
153
|
+
return "";
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
135
157
|
function fetchText(url, options = {}) {
|
|
136
158
|
const timeoutMs = Number.isFinite(Number(options.timeoutMs))
|
|
137
159
|
? Number(options.timeoutMs)
|
|
138
160
|
: DEFAULT_TIMEOUT_MS;
|
|
161
|
+
const maxResponseBytes = Number.isFinite(Number(options.maxResponseBytes))
|
|
162
|
+
? Math.max(1, Number(options.maxResponseBytes))
|
|
163
|
+
: DEFAULT_MAX_RESPONSE_BYTES;
|
|
164
|
+
const maxRedirects = Number.isFinite(Number(options.maxRedirects))
|
|
165
|
+
? Math.max(0, Number(options.maxRedirects))
|
|
166
|
+
: DEFAULT_MAX_REDIRECTS;
|
|
167
|
+
const redirectCount = Number.isFinite(Number(options._redirectCount))
|
|
168
|
+
? Number(options._redirectCount)
|
|
169
|
+
: 0;
|
|
170
|
+
const visited = options._visited instanceof Set ? options._visited : new Set();
|
|
171
|
+
const requestImpl = typeof options.httpsGet === "function" ? options.httpsGet : https.get;
|
|
172
|
+
const requestUrl = String(url || "");
|
|
139
173
|
|
|
140
174
|
return new Promise((resolve, reject) => {
|
|
141
|
-
|
|
142
|
-
|
|
175
|
+
let settled = false;
|
|
176
|
+
function finish(callback, value) {
|
|
177
|
+
if (settled) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
settled = true;
|
|
181
|
+
callback(value);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const request = requestImpl(
|
|
185
|
+
requestUrl,
|
|
143
186
|
{
|
|
144
187
|
headers: {
|
|
145
188
|
"User-Agent": "da-vinci-workflow/icon-sync",
|
|
@@ -148,26 +191,85 @@ function fetchText(url, options = {}) {
|
|
|
148
191
|
},
|
|
149
192
|
(response) => {
|
|
150
193
|
let data = "";
|
|
194
|
+
let receivedBytes = 0;
|
|
151
195
|
response.setEncoding("utf8");
|
|
152
196
|
response.on("data", (chunk) => {
|
|
197
|
+
if (settled) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
receivedBytes += Buffer.byteLength(chunk, "utf8");
|
|
201
|
+
if (receivedBytes > maxResponseBytes) {
|
|
202
|
+
const error = new Error(`Response body exceeded ${maxResponseBytes} bytes for ${requestUrl}.`);
|
|
203
|
+
finish(reject, error);
|
|
204
|
+
request.destroy(error);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
153
207
|
data += chunk;
|
|
154
208
|
});
|
|
209
|
+
response.on("error", (error) => {
|
|
210
|
+
finish(reject, error);
|
|
211
|
+
});
|
|
155
212
|
response.on("end", () => {
|
|
213
|
+
if (settled) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const statusCode = Number(response.statusCode || 0);
|
|
217
|
+
if (statusCode >= 300 && statusCode < 400) {
|
|
218
|
+
if (redirectCount >= maxRedirects) {
|
|
219
|
+
finish(
|
|
220
|
+
reject,
|
|
221
|
+
new Error(
|
|
222
|
+
`Too many redirects while fetching ${requestUrl} (limit ${maxRedirects}).`
|
|
223
|
+
)
|
|
224
|
+
);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const redirectUrl = resolveRedirectUrl(requestUrl, response.headers && response.headers.location);
|
|
229
|
+
if (!redirectUrl) {
|
|
230
|
+
finish(
|
|
231
|
+
reject,
|
|
232
|
+
new Error(`HTTP ${statusCode} redirect without valid location for ${requestUrl}`)
|
|
233
|
+
);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (visited.has(redirectUrl) || redirectUrl === requestUrl) {
|
|
238
|
+
finish(reject, new Error(`Redirect loop detected while fetching ${requestUrl}.`));
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const nextVisited = new Set(visited);
|
|
243
|
+
nextVisited.add(requestUrl);
|
|
244
|
+
finish(
|
|
245
|
+
resolve,
|
|
246
|
+
fetchText(redirectUrl, {
|
|
247
|
+
...options,
|
|
248
|
+
_redirectCount: redirectCount + 1,
|
|
249
|
+
_visited: nextVisited
|
|
250
|
+
})
|
|
251
|
+
);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
156
255
|
if (response.statusCode && response.statusCode >= 400) {
|
|
157
|
-
reject
|
|
256
|
+
finish(reject, new Error(`HTTP ${response.statusCode} for ${requestUrl}`));
|
|
158
257
|
return;
|
|
159
258
|
}
|
|
160
|
-
resolve
|
|
259
|
+
finish(resolve, data);
|
|
161
260
|
});
|
|
162
261
|
}
|
|
163
262
|
);
|
|
164
263
|
|
|
165
264
|
request.on("error", (error) => {
|
|
166
|
-
reject
|
|
265
|
+
finish(reject, error);
|
|
167
266
|
});
|
|
168
267
|
|
|
169
268
|
request.setTimeout(timeoutMs, () => {
|
|
170
|
-
|
|
269
|
+
if (settled) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
request.destroy(new Error(`Request timeout after ${timeoutMs}ms for ${requestUrl}`));
|
|
171
273
|
});
|
|
172
274
|
});
|
|
173
275
|
}
|
|
@@ -187,43 +289,46 @@ function resolveCatalogPath(options = {}) {
|
|
|
187
289
|
return path.resolve(getDefaultCatalogPath(options.homeDir));
|
|
188
290
|
}
|
|
189
291
|
|
|
190
|
-
function
|
|
191
|
-
const
|
|
192
|
-
if (!
|
|
193
|
-
return
|
|
194
|
-
catalogPath,
|
|
195
|
-
catalog: null
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const parsed = JSON.parse(fs.readFileSync(catalogPath, "utf8"));
|
|
200
|
-
if (!parsed || !Array.isArray(parsed.icons)) {
|
|
201
|
-
throw new Error(`Invalid icon catalog format: ${catalogPath}`);
|
|
292
|
+
function normalizePositiveInt(value, fallback, minimum = 1, maximum = Number.POSITIVE_INFINITY) {
|
|
293
|
+
const parsed = Number.parseInt(String(value || ""), 10);
|
|
294
|
+
if (!Number.isFinite(parsed)) {
|
|
295
|
+
return fallback;
|
|
202
296
|
}
|
|
297
|
+
return Math.max(minimum, Math.min(maximum, parsed));
|
|
298
|
+
}
|
|
203
299
|
|
|
300
|
+
function resolveSourceUrls(overrides = {}) {
|
|
204
301
|
return {
|
|
205
|
-
|
|
206
|
-
|
|
302
|
+
material:
|
|
303
|
+
typeof overrides.material === "string" && overrides.material.trim()
|
|
304
|
+
? overrides.material.trim()
|
|
305
|
+
: DEFAULT_ICON_SOURCE_URLS.material,
|
|
306
|
+
lucide:
|
|
307
|
+
typeof overrides.lucide === "string" && overrides.lucide.trim()
|
|
308
|
+
? overrides.lucide.trim()
|
|
309
|
+
: DEFAULT_ICON_SOURCE_URLS.lucide,
|
|
310
|
+
feather:
|
|
311
|
+
typeof overrides.feather === "string" && overrides.feather.trim()
|
|
312
|
+
? overrides.feather.trim()
|
|
313
|
+
: DEFAULT_ICON_SOURCE_URLS.feather,
|
|
314
|
+
phosphor:
|
|
315
|
+
typeof overrides.phosphor === "string" && overrides.phosphor.trim()
|
|
316
|
+
? overrides.phosphor.trim()
|
|
317
|
+
: DEFAULT_ICON_SOURCE_URLS.phosphor
|
|
207
318
|
};
|
|
208
319
|
}
|
|
209
320
|
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
? Number(options.timeoutMs)
|
|
214
|
-
: DEFAULT_TIMEOUT_MS;
|
|
215
|
-
const strict = toBoolean(options.strict);
|
|
216
|
-
const fetchTextImpl = typeof options.fetchText === "function" ? options.fetchText : fetchText;
|
|
217
|
-
|
|
218
|
-
const sourceSpecs = [
|
|
321
|
+
function buildSourceSpecs(options = {}) {
|
|
322
|
+
const sourceUrls = resolveSourceUrls(options.sourceUrls);
|
|
323
|
+
return [
|
|
219
324
|
{
|
|
220
325
|
key: "material",
|
|
221
|
-
url:
|
|
326
|
+
url: sourceUrls.material,
|
|
222
327
|
parse: parseGoogleMaterialMetadata
|
|
223
328
|
},
|
|
224
329
|
{
|
|
225
330
|
key: "lucide",
|
|
226
|
-
url:
|
|
331
|
+
url: sourceUrls.lucide,
|
|
227
332
|
parse: (raw) =>
|
|
228
333
|
parseGitHubTreeIcons(raw, {
|
|
229
334
|
family: "lucide",
|
|
@@ -233,7 +338,7 @@ async function syncIconCatalog(options = {}) {
|
|
|
233
338
|
},
|
|
234
339
|
{
|
|
235
340
|
key: "feather",
|
|
236
|
-
url:
|
|
341
|
+
url: sourceUrls.feather,
|
|
237
342
|
parse: (raw) =>
|
|
238
343
|
parseGitHubTreeIcons(raw, {
|
|
239
344
|
family: "feather",
|
|
@@ -243,7 +348,7 @@ async function syncIconCatalog(options = {}) {
|
|
|
243
348
|
},
|
|
244
349
|
{
|
|
245
350
|
key: "phosphor",
|
|
246
|
-
url:
|
|
351
|
+
url: sourceUrls.phosphor,
|
|
247
352
|
parse: (raw) =>
|
|
248
353
|
parseGitHubTreeIcons(raw, {
|
|
249
354
|
family: "phosphor",
|
|
@@ -252,15 +357,83 @@ async function syncIconCatalog(options = {}) {
|
|
|
252
357
|
})
|
|
253
358
|
}
|
|
254
359
|
];
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async function runSourcePool(items, worker, concurrency) {
|
|
363
|
+
const queue = Array.isArray(items) ? items.slice() : [];
|
|
364
|
+
if (queue.length === 0) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const workerCount = Math.min(
|
|
369
|
+
normalizePositiveInt(concurrency, DEFAULT_SOURCE_CONCURRENCY, 1, 8),
|
|
370
|
+
queue.length
|
|
371
|
+
);
|
|
372
|
+
let cursor = 0;
|
|
373
|
+
|
|
374
|
+
async function runWorker() {
|
|
375
|
+
while (true) {
|
|
376
|
+
const index = cursor;
|
|
377
|
+
cursor += 1;
|
|
378
|
+
if (index >= queue.length) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
await worker(queue[index], index);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
await Promise.all(Array.from({ length: workerCount }, () => runWorker()));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function loadIconCatalog(options = {}) {
|
|
389
|
+
const catalogPath = resolveCatalogPath(options);
|
|
390
|
+
if (!fs.existsSync(catalogPath)) {
|
|
391
|
+
return {
|
|
392
|
+
catalogPath,
|
|
393
|
+
catalog: null
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const parsed = readJsonFile(catalogPath, `icon catalog JSON at ${catalogPath}`);
|
|
398
|
+
if (!parsed || !Array.isArray(parsed.icons)) {
|
|
399
|
+
throw new Error(`Invalid icon catalog format: ${catalogPath}`);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
catalogPath,
|
|
404
|
+
catalog: parsed
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async function syncIconCatalog(options = {}) {
|
|
409
|
+
const catalogPath = resolveCatalogPath(options);
|
|
410
|
+
const timeoutMs = Number.isFinite(Number(options.timeoutMs))
|
|
411
|
+
? Number(options.timeoutMs)
|
|
412
|
+
: DEFAULT_TIMEOUT_MS;
|
|
413
|
+
const maxResponseBytes = Number.isFinite(Number(options.maxResponseBytes))
|
|
414
|
+
? Math.max(1, Number(options.maxResponseBytes))
|
|
415
|
+
: DEFAULT_MAX_RESPONSE_BYTES;
|
|
416
|
+
const strict = toBoolean(options.strict);
|
|
417
|
+
const fetchTextImpl = typeof options.fetchText === "function" ? options.fetchText : fetchText;
|
|
418
|
+
const sourceConcurrency = normalizePositiveInt(
|
|
419
|
+
options.sourceConcurrency,
|
|
420
|
+
DEFAULT_SOURCE_CONCURRENCY,
|
|
421
|
+
1,
|
|
422
|
+
4
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
const sourceSpecs = buildSourceSpecs(options);
|
|
255
426
|
|
|
256
427
|
const sourceResults = {};
|
|
257
428
|
const collected = [];
|
|
258
429
|
|
|
259
|
-
await
|
|
260
|
-
sourceSpecs
|
|
430
|
+
await runSourcePool(
|
|
431
|
+
sourceSpecs,
|
|
432
|
+
async (spec) => {
|
|
261
433
|
try {
|
|
262
434
|
const raw = await fetchTextImpl(spec.url, {
|
|
263
|
-
timeoutMs
|
|
435
|
+
timeoutMs,
|
|
436
|
+
maxResponseBytes
|
|
264
437
|
});
|
|
265
438
|
const records = spec.parse(raw);
|
|
266
439
|
sourceResults[spec.key] = {
|
|
@@ -277,7 +450,8 @@ async function syncIconCatalog(options = {}) {
|
|
|
277
450
|
error: error.message || String(error)
|
|
278
451
|
};
|
|
279
452
|
}
|
|
280
|
-
}
|
|
453
|
+
},
|
|
454
|
+
sourceConcurrency
|
|
281
455
|
);
|
|
282
456
|
|
|
283
457
|
const icons = dedupeIconRecords(collected);
|
|
@@ -310,7 +484,7 @@ async function syncIconCatalog(options = {}) {
|
|
|
310
484
|
};
|
|
311
485
|
|
|
312
486
|
ensureDir(catalogPath);
|
|
313
|
-
|
|
487
|
+
writeFileAtomic(catalogPath, JSON.stringify(catalog, null, 2));
|
|
314
488
|
|
|
315
489
|
return {
|
|
316
490
|
catalogPath,
|
|
@@ -349,13 +523,18 @@ module.exports = {
|
|
|
349
523
|
LUCIDE_TREE_URL,
|
|
350
524
|
FEATHER_TREE_URL,
|
|
351
525
|
PHOSPHOR_TREE_URL,
|
|
526
|
+
DEFAULT_ICON_SOURCE_URLS,
|
|
352
527
|
getDefaultCatalogPath,
|
|
353
528
|
resolveCatalogPath,
|
|
354
529
|
loadIconCatalog,
|
|
355
530
|
syncIconCatalog,
|
|
356
531
|
formatIconSyncReport,
|
|
532
|
+
buildSourceSpecs,
|
|
357
533
|
parseGoogleMaterialMetadata,
|
|
358
534
|
parseGitHubTreeIcons,
|
|
359
535
|
dedupeIconRecords,
|
|
360
|
-
summarizeSourceResults
|
|
536
|
+
summarizeSourceResults,
|
|
537
|
+
fetchText,
|
|
538
|
+
resolveRedirectUrl,
|
|
539
|
+
DEFAULT_SOURCE_CONCURRENCY
|
|
361
540
|
};
|
package/lib/install.js
CHANGED
|
@@ -19,62 +19,71 @@ const REQUIRED_FILES = [
|
|
|
19
19
|
...listFiles(path.join(REPO_ROOT, "examples")).map((filePath) => path.relative(REPO_ROOT, filePath))
|
|
20
20
|
];
|
|
21
21
|
|
|
22
|
-
const
|
|
23
|
-
(filePath) =>
|
|
22
|
+
const CODEX_PROMPT_TARGET_PAIRS = listFiles(path.join(REPO_ROOT, "commands", "codex", "prompts")).map(
|
|
23
|
+
(filePath) => ({
|
|
24
|
+
sourcePath: filePath,
|
|
25
|
+
targetPath: path.join(".codex", "prompts", path.basename(filePath))
|
|
26
|
+
})
|
|
24
27
|
);
|
|
25
28
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
path.join(".codex", "skills", "da-vinci", "
|
|
29
|
+
const CODEX_SKILL_TARGET_PAIRS = [
|
|
30
|
+
{
|
|
31
|
+
sourcePath: path.join(REPO_ROOT, "SKILL.md"),
|
|
32
|
+
targetPath: path.join(".codex", "skills", "da-vinci", "SKILL.md")
|
|
33
|
+
},
|
|
34
|
+
...buildTargetPairs(
|
|
35
|
+
path.join(REPO_ROOT, "agents"),
|
|
36
|
+
path.join(".codex", "skills", "da-vinci", "agents")
|
|
30
37
|
),
|
|
31
|
-
...
|
|
32
|
-
path.join(
|
|
33
|
-
|
|
34
|
-
"skills",
|
|
35
|
-
"da-vinci",
|
|
36
|
-
"references",
|
|
37
|
-
path.relative(path.join(REPO_ROOT, "references"), filePath)
|
|
38
|
-
)
|
|
38
|
+
...buildTargetPairs(
|
|
39
|
+
path.join(REPO_ROOT, "references"),
|
|
40
|
+
path.join(".codex", "skills", "da-vinci", "references")
|
|
39
41
|
),
|
|
40
|
-
...
|
|
41
|
-
path.join(
|
|
42
|
+
...buildTargetPairs(
|
|
43
|
+
path.join(REPO_ROOT, "docs"),
|
|
44
|
+
path.join(".codex", "skills", "da-vinci", "docs")
|
|
42
45
|
),
|
|
43
|
-
...
|
|
44
|
-
path.join(
|
|
45
|
-
|
|
46
|
-
"skills",
|
|
47
|
-
"da-vinci",
|
|
48
|
-
"examples",
|
|
49
|
-
path.relative(path.join(REPO_ROOT, "examples"), filePath)
|
|
50
|
-
)
|
|
46
|
+
...buildTargetPairs(
|
|
47
|
+
path.join(REPO_ROOT, "examples"),
|
|
48
|
+
path.join(".codex", "skills", "da-vinci", "examples")
|
|
51
49
|
)
|
|
52
50
|
];
|
|
53
51
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
path.join(".claude", "commands", "dv", "design.md"),
|
|
60
|
-
path.join(".claude", "commands", "dv", "intake.md"),
|
|
61
|
-
path.join(".claude", "commands", "dv", "prompt.md"),
|
|
62
|
-
path.join(".claude", "commands", "dv", "tasks.md"),
|
|
63
|
-
path.join(".claude", "commands", "dv", "verify.md")
|
|
52
|
+
const CLAUDE_COMMAND_TARGET_PAIRS = [
|
|
53
|
+
{
|
|
54
|
+
sourcePath: path.join(REPO_ROOT, "commands", "claude", "da-vinci.md"),
|
|
55
|
+
targetPath: path.join(".claude", "commands", "da-vinci.md")
|
|
56
|
+
}
|
|
64
57
|
];
|
|
65
58
|
|
|
66
|
-
const
|
|
67
|
-
path.join("
|
|
68
|
-
path.join(".
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
59
|
+
const CLAUDE_ACTION_TARGET_PAIRS = buildNamedTargetPairs(
|
|
60
|
+
path.join(REPO_ROOT, "commands", "claude", "dv"),
|
|
61
|
+
path.join(".claude", "commands", "dv"),
|
|
62
|
+
["breakdown.md", "build.md", "continue.md", "design.md", "intake.md", "prompt.md", "tasks.md", "verify.md"]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const GEMINI_COMMAND_TARGET_PAIRS = [
|
|
66
|
+
{
|
|
67
|
+
sourcePath: path.join(REPO_ROOT, "commands", "gemini", "da-vinci.toml"),
|
|
68
|
+
targetPath: path.join(".gemini", "commands", "da-vinci.toml")
|
|
69
|
+
}
|
|
76
70
|
];
|
|
77
71
|
|
|
72
|
+
const GEMINI_ACTION_TARGET_PAIRS = buildNamedTargetPairs(
|
|
73
|
+
path.join(REPO_ROOT, "commands", "gemini", "dv"),
|
|
74
|
+
path.join(".gemini", "commands", "dv"),
|
|
75
|
+
[
|
|
76
|
+
"breakdown.toml",
|
|
77
|
+
"build.toml",
|
|
78
|
+
"continue.toml",
|
|
79
|
+
"design.toml",
|
|
80
|
+
"intake.toml",
|
|
81
|
+
"prompt.toml",
|
|
82
|
+
"tasks.toml",
|
|
83
|
+
"verify.toml"
|
|
84
|
+
]
|
|
85
|
+
);
|
|
86
|
+
|
|
78
87
|
function resolveHome(homeDir) {
|
|
79
88
|
return homeDir || process.env.HOME || os.homedir();
|
|
80
89
|
}
|
|
@@ -156,8 +165,51 @@ function listFiles(dirPath) {
|
|
|
156
165
|
return scan.files;
|
|
157
166
|
}
|
|
158
167
|
|
|
159
|
-
function
|
|
160
|
-
return
|
|
168
|
+
function buildTargetPairs(sourceRoot, targetRoot) {
|
|
169
|
+
return listFiles(sourceRoot).map((filePath) => ({
|
|
170
|
+
sourcePath: filePath,
|
|
171
|
+
targetPath: path.join(targetRoot, path.relative(sourceRoot, filePath))
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function buildNamedTargetPairs(sourceDir, targetDir, fileNames) {
|
|
176
|
+
return fileNames.map((fileName) => ({
|
|
177
|
+
sourcePath: path.join(sourceDir, fileName),
|
|
178
|
+
targetPath: path.join(targetDir, fileName)
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function inspectInstalledTargets(homeDir, targetPairs) {
|
|
183
|
+
const missing = [];
|
|
184
|
+
const mismatched = [];
|
|
185
|
+
const unreadable = [];
|
|
186
|
+
|
|
187
|
+
for (const pair of targetPairs) {
|
|
188
|
+
const targetPath = path.join(homeDir, pair.targetPath);
|
|
189
|
+
if (!fs.existsSync(targetPath)) {
|
|
190
|
+
missing.push(pair.targetPath);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const sourceContent = fs.readFileSync(pair.sourcePath);
|
|
196
|
+
const targetContent = fs.readFileSync(targetPath);
|
|
197
|
+
if (!sourceContent.equals(targetContent)) {
|
|
198
|
+
mismatched.push(pair.targetPath);
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
unreadable.push(
|
|
202
|
+
`${pair.targetPath}${error && error.code ? ` (${error.code})` : error && error.message ? ` (${error.message})` : ""}`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
ok: missing.length === 0 && mismatched.length === 0 && unreadable.length === 0,
|
|
209
|
+
missing,
|
|
210
|
+
mismatched,
|
|
211
|
+
unreadable
|
|
212
|
+
};
|
|
161
213
|
}
|
|
162
214
|
|
|
163
215
|
function installCodex(homeDir) {
|
|
@@ -312,29 +364,45 @@ function uninstallPlatforms(platforms, options = {}) {
|
|
|
312
364
|
|
|
313
365
|
function getStatus(options = {}) {
|
|
314
366
|
const homeDir = resolveHome(options.homeDir);
|
|
315
|
-
const
|
|
316
|
-
const
|
|
317
|
-
const
|
|
318
|
-
const
|
|
367
|
+
const codexPrompt = inspectInstalledTargets(homeDir, CODEX_PROMPT_TARGET_PAIRS);
|
|
368
|
+
const codexSkill = inspectInstalledTargets(homeDir, CODEX_SKILL_TARGET_PAIRS);
|
|
369
|
+
const claudeCommand = inspectInstalledTargets(homeDir, CLAUDE_COMMAND_TARGET_PAIRS);
|
|
370
|
+
const claudeActionSet = inspectInstalledTargets(homeDir, CLAUDE_ACTION_TARGET_PAIRS);
|
|
371
|
+
const geminiCommand = inspectInstalledTargets(homeDir, GEMINI_COMMAND_TARGET_PAIRS);
|
|
372
|
+
const geminiActionSet = inspectInstalledTargets(homeDir, GEMINI_ACTION_TARGET_PAIRS);
|
|
319
373
|
|
|
320
374
|
return {
|
|
321
375
|
version: VERSION,
|
|
322
376
|
homeDir,
|
|
323
377
|
codex: {
|
|
324
|
-
prompt:
|
|
325
|
-
skill:
|
|
326
|
-
promptMissing:
|
|
327
|
-
|
|
378
|
+
prompt: codexPrompt.ok,
|
|
379
|
+
skill: codexSkill.ok,
|
|
380
|
+
promptMissing: codexPrompt.missing,
|
|
381
|
+
promptMismatched: codexPrompt.mismatched,
|
|
382
|
+
promptUnreadable: codexPrompt.unreadable,
|
|
383
|
+
skillMissing: codexSkill.missing,
|
|
384
|
+
skillMismatched: codexSkill.mismatched,
|
|
385
|
+
skillUnreadable: codexSkill.unreadable
|
|
328
386
|
},
|
|
329
387
|
claude: {
|
|
330
|
-
command:
|
|
331
|
-
actionSet:
|
|
332
|
-
|
|
388
|
+
command: claudeCommand.ok,
|
|
389
|
+
actionSet: claudeActionSet.ok,
|
|
390
|
+
commandMissing: claudeCommand.missing,
|
|
391
|
+
commandMismatched: claudeCommand.mismatched,
|
|
392
|
+
commandUnreadable: claudeCommand.unreadable,
|
|
393
|
+
actionSetMissing: claudeActionSet.missing,
|
|
394
|
+
actionSetMismatched: claudeActionSet.mismatched,
|
|
395
|
+
actionSetUnreadable: claudeActionSet.unreadable
|
|
333
396
|
},
|
|
334
397
|
gemini: {
|
|
335
|
-
command:
|
|
336
|
-
actionSet:
|
|
337
|
-
|
|
398
|
+
command: geminiCommand.ok,
|
|
399
|
+
actionSet: geminiActionSet.ok,
|
|
400
|
+
commandMissing: geminiCommand.missing,
|
|
401
|
+
commandMismatched: geminiCommand.mismatched,
|
|
402
|
+
commandUnreadable: geminiCommand.unreadable,
|
|
403
|
+
actionSetMissing: geminiActionSet.missing,
|
|
404
|
+
actionSetMismatched: geminiActionSet.mismatched,
|
|
405
|
+
actionSetUnreadable: geminiActionSet.unreadable
|
|
338
406
|
}
|
|
339
407
|
};
|
|
340
408
|
}
|