@xenonbyte/da-vinci-workflow 0.1.24 → 0.1.26

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +35 -1
  2. package/README.md +41 -10
  3. package/README.zh-CN.md +30 -10
  4. package/SKILL.md +4 -0
  5. package/commands/claude/dv/design.md +2 -1
  6. package/commands/codex/prompts/dv-design.md +2 -1
  7. package/commands/gemini/dv/design.toml +2 -1
  8. package/docs/constraint-files.md +1 -0
  9. package/docs/dv-command-reference.md +14 -2
  10. package/docs/pencil-rendering-workflow.md +9 -7
  11. package/docs/prompt-presets/README.md +4 -0
  12. package/docs/visual-assist-presets/README.md +4 -0
  13. package/docs/workflow-examples.md +13 -11
  14. package/docs/workflow-overview.md +2 -0
  15. package/docs/zh-CN/constraint-files.md +1 -0
  16. package/docs/zh-CN/dv-command-reference.md +14 -2
  17. package/docs/zh-CN/pencil-rendering-workflow.md +9 -7
  18. package/docs/zh-CN/prompt-presets/README.md +5 -1
  19. package/docs/zh-CN/visual-assist-presets/README.md +5 -1
  20. package/docs/zh-CN/workflow-examples.md +13 -11
  21. package/docs/zh-CN/workflow-overview.md +2 -0
  22. package/examples/greenfield-spec-markupflow/README.md +6 -1
  23. package/lib/async-offload-worker.js +26 -0
  24. package/lib/async-offload.js +82 -0
  25. package/lib/audit-parsers.js +223 -51
  26. package/lib/audit.js +91 -23
  27. package/lib/cli.js +749 -433
  28. package/lib/fs-safety.js +1 -4
  29. package/lib/icon-aliases.js +7 -7
  30. package/lib/icon-search.js +21 -14
  31. package/lib/icon-sync.js +220 -41
  32. package/lib/install.js +128 -60
  33. package/lib/mcp-runtime-gate.js +4 -7
  34. package/lib/pen-persistence.js +365 -46
  35. package/lib/pencil-lock.js +237 -25
  36. package/lib/pencil-preflight.js +233 -12
  37. package/lib/pencil-session.js +216 -36
  38. package/lib/supervisor-review.js +56 -34
  39. package/lib/utils.js +121 -0
  40. package/lib/workflow-bootstrap.js +255 -0
  41. package/package.json +13 -3
  42. package/references/artifact-templates.md +1 -0
  43. package/references/checkpoints.md +2 -0
  44. package/references/design-inputs.md +2 -0
  45. package/references/pencil-design-to-code.md +2 -0
  46. package/scripts/fixtures/complex-sample.pen +0 -295
  47. package/scripts/fixtures/mock-pencil.js +0 -49
  48. package/scripts/test-audit-context-delta.js +0 -446
  49. package/scripts/test-audit-design-supervisor.js +0 -537
  50. package/scripts/test-audit-safety.js +0 -92
  51. package/scripts/test-icon-aliases.js +0 -96
  52. package/scripts/test-icon-search.js +0 -77
  53. package/scripts/test-icon-sync.js +0 -178
  54. package/scripts/test-mcp-runtime-gate.js +0 -287
  55. package/scripts/test-mode-consistency.js +0 -339
  56. package/scripts/test-pen-persistence.js +0 -254
  57. package/scripts/test-pencil-lock.js +0 -130
  58. package/scripts/test-pencil-preflight.js +0 -169
  59. package/scripts/test-pencil-session.js +0 -192
  60. package/scripts/test-persistence-flows.js +0 -345
  61. package/scripts/test-supervisor-review-cli.js +0 -619
  62. package/scripts/test-supervisor-review-integration.js +0 -115
@@ -1,537 +0,0 @@
1
- const assert = require("assert/strict");
2
- const fs = require("fs");
3
- const os = require("os");
4
- const path = require("path");
5
- const { spawnSync } = require("child_process");
6
-
7
- const { auditProject } = require("../lib/audit");
8
-
9
- const repo = path.resolve(__dirname, "..");
10
- const cli = path.join(repo, "bin", "da-vinci.js");
11
- const fixture = JSON.parse(
12
- fs.readFileSync(path.join(__dirname, "fixtures", "complex-sample.pen"), "utf8")
13
- );
14
- const changeId = "redesign-001";
15
-
16
- function runTest(name, fn) {
17
- try {
18
- fn();
19
- console.log(`PASS ${name}`);
20
- } catch (error) {
21
- console.error(`FAIL ${name}`);
22
- throw error;
23
- }
24
- }
25
-
26
- function createHarness() {
27
- const base = fs.mkdtempSync(path.join(os.tmpdir(), "da-vinci-audit-supervisor-"));
28
- return {
29
- base,
30
- home: path.join(base, "home")
31
- };
32
- }
33
-
34
- function runCli(harness, args) {
35
- const [command, ...rest] = args;
36
- const result = spawnSync(process.execPath, [cli, command, "--home", harness.home, ...rest], {
37
- cwd: repo,
38
- encoding: "utf8",
39
- maxBuffer: 8 * 1024 * 1024
40
- });
41
-
42
- return {
43
- code: result.status,
44
- stdout: (result.stdout || "").trim(),
45
- stderr: (result.stderr || "").trim()
46
- };
47
- }
48
-
49
- function expectOk(step, result) {
50
- assert.equal(result.code, 0, `${step} failed:\n${result.stderr || result.stdout}`);
51
- }
52
-
53
- function writeJson(filePath, payload) {
54
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
55
- fs.writeFileSync(filePath, JSON.stringify(payload, null, 2));
56
- }
57
-
58
- function writeText(filePath, text) {
59
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
60
- fs.writeFileSync(filePath, text);
61
- }
62
-
63
- function setupProject(harness, name, options = {}) {
64
- const root = path.join(harness.base, name);
65
- const daVinciDir = path.join(root, ".da-vinci");
66
- const changeDir = path.join(daVinciDir, "changes", changeId);
67
- const penPath = path.join(daVinciDir, "designs", `${name}.pen`);
68
- const nodesFile = path.join(root, `${name}-nodes.json`);
69
- const variablesFile = path.join(root, `${name}-variables.json`);
70
- const configureReviewers = options.configureReviewers !== false;
71
- const requireSupervisorReview = options.requireSupervisorReview === true;
72
-
73
- const visualAssistLines = [
74
- "# DA-VINCI",
75
- "",
76
- "## Visual Assist",
77
- "- Preferred adapters:",
78
- " - frontend-skill"
79
- ];
80
-
81
- if (configureReviewers) {
82
- visualAssistLines.push("- Design-supervisor reviewers:");
83
- visualAssistLines.push(" - frontend-skill");
84
- visualAssistLines.push("- Design-supervisor review mode:");
85
- visualAssistLines.push(" - screenshot-and-theme");
86
- visualAssistLines.push("- Design-supervisor review inputs:");
87
- visualAssistLines.push(" - screenshots");
88
- visualAssistLines.push(" - pencil variables");
89
- }
90
-
91
- visualAssistLines.push("- Require Supervisor Review:");
92
- visualAssistLines.push(` - ${requireSupervisorReview ? "true" : "false"}`);
93
- visualAssistLines.push("");
94
-
95
- writeText(
96
- path.join(root, "DA-VINCI.md"),
97
- visualAssistLines.join("\n")
98
- );
99
- writeText(path.join(daVinciDir, "project-inventory.md"), "# Inventory\n");
100
- writeText(path.join(daVinciDir, "page-map.md"), "# Page Map\n");
101
- writeText(
102
- path.join(daVinciDir, "design-registry.md"),
103
- `# Registry\n- Preferred .pen: .da-vinci/designs/${name}.pen\n`
104
- );
105
- writeText(path.join(changeDir, "design-brief.md"), "# Brief\n");
106
- writeText(path.join(changeDir, "design.md"), "# Design\n");
107
- writeText(path.join(changeDir, "pencil-bindings.md"), "# Bindings\n");
108
-
109
- writeJson(nodesFile, { nodes: fixture.children });
110
- writeJson(variablesFile, { variables: fixture.variables });
111
-
112
- expectOk(
113
- `${name} begin`,
114
- runCli(harness, ["pencil-session", "begin", "--project", root, "--pen", penPath])
115
- );
116
- expectOk(
117
- `${name} persist`,
118
- runCli(harness, [
119
- "pencil-session",
120
- "persist",
121
- "--project",
122
- root,
123
- "--pen",
124
- penPath,
125
- "--nodes-file",
126
- nodesFile,
127
- "--variables-file",
128
- variablesFile,
129
- "--version",
130
- fixture.version
131
- ])
132
- );
133
- expectOk(
134
- `${name} end`,
135
- runCli(harness, [
136
- "pencil-session",
137
- "end",
138
- "--project",
139
- root,
140
- "--pen",
141
- penPath,
142
- "--nodes-file",
143
- nodesFile,
144
- "--variables-file",
145
- variablesFile,
146
- "--version",
147
- fixture.version
148
- ])
149
- );
150
-
151
- return {
152
- root,
153
- changeDir,
154
- pencilDesignPath: path.join(changeDir, "pencil-design.md")
155
- };
156
- }
157
-
158
- runTest("completion audit warns when supervisor reviewers are configured but not required and no review is recorded", () => {
159
- const harness = createHarness();
160
- const project = setupProject(harness, "missing-supervisor-review");
161
-
162
- writeText(project.pencilDesignPath, "# Pencil Design\n");
163
-
164
- const result = auditProject(project.root, {
165
- mode: "completion",
166
- changeId
167
- });
168
-
169
- assert.equal(result.status, "WARN");
170
- assert.match(
171
- result.warnings.join("\n"),
172
- /Design-supervisor reviewers.*does not record a `## Design-Supervisor Review` section/i
173
- );
174
- });
175
-
176
- runTest("completion audit fails when supervisor review is required but no review is recorded", () => {
177
- const harness = createHarness();
178
- const project = setupProject(harness, "required-supervisor-review", {
179
- requireSupervisorReview: true
180
- });
181
-
182
- writeText(project.pencilDesignPath, "# Pencil Design\n");
183
-
184
- const result = auditProject(project.root, {
185
- mode: "completion",
186
- changeId
187
- });
188
-
189
- assert.equal(result.status, "FAIL");
190
- assert.match(
191
- result.failures.join("\n"),
192
- /Design-supervisor reviewers.*does not record a `## Design-Supervisor Review` section/i
193
- );
194
- });
195
-
196
- runTest("completion audit fails when supervisor review is required but no reviewers are configured", () => {
197
- const harness = createHarness();
198
- const project = setupProject(harness, "missing-supervisor-reviewers", {
199
- configureReviewers: false,
200
- requireSupervisorReview: true
201
- });
202
-
203
- writeText(project.pencilDesignPath, "# Pencil Design\n");
204
-
205
- const result = auditProject(project.root, {
206
- mode: "completion",
207
- changeId
208
- });
209
-
210
- assert.equal(result.status, "FAIL");
211
- assert.match(
212
- result.failures.join("\n"),
213
- /Require Supervisor Review: true.*no `Design-supervisor reviewers` are configured/i
214
- );
215
- });
216
-
217
- runTest("completion audit does not treat an empty reviewers field as configured", () => {
218
- const harness = createHarness();
219
- const project = setupProject(harness, "empty-supervisor-reviewers", {
220
- configureReviewers: false,
221
- requireSupervisorReview: false
222
- });
223
-
224
- writeText(
225
- path.join(project.root, "DA-VINCI.md"),
226
- [
227
- "# DA-VINCI",
228
- "",
229
- "## Visual Assist",
230
- "- Preferred adapters:",
231
- " - frontend-skill",
232
- "- Design-supervisor reviewers:",
233
- "- Design-supervisor review mode:",
234
- " - screenshot-and-theme",
235
- "- Design-supervisor review inputs:",
236
- " - screenshots",
237
- " - pencil variables",
238
- "- Require Supervisor Review:",
239
- " - false",
240
- ""
241
- ].join("\n")
242
- );
243
- writeText(project.pencilDesignPath, "# Pencil Design\n");
244
-
245
- const result = auditProject(project.root, {
246
- mode: "completion",
247
- changeId
248
- });
249
-
250
- assert.equal(result.status, "PASS");
251
- assert.doesNotMatch(
252
- result.warnings.join("\n"),
253
- /Design-supervisor reviewers.*does not record a `## Design-Supervisor Review` section/i
254
- );
255
- });
256
-
257
- runTest("integrity audit warns when supervisor review is required but no review is recorded", () => {
258
- const harness = createHarness();
259
- const project = setupProject(harness, "warn-supervisor-review", {
260
- requireSupervisorReview: true
261
- });
262
-
263
- writeText(project.pencilDesignPath, "# Pencil Design\n");
264
-
265
- const result = auditProject(project.root, {
266
- mode: "integrity"
267
- });
268
-
269
- assert.equal(result.status, "WARN");
270
- assert.match(
271
- result.warnings.join("\n"),
272
- /Design-supervisor reviewers.*does not record a `## Design-Supervisor Review` section/i
273
- );
274
- });
275
-
276
- runTest("completion audit fails when required supervisor review is missing Issue list or Revision outcome", () => {
277
- const harness = createHarness();
278
- const project = setupProject(harness, "malformed-supervisor-review", {
279
- requireSupervisorReview: true
280
- });
281
-
282
- writeText(
283
- project.pencilDesignPath,
284
- [
285
- "# Pencil Design",
286
- "",
287
- "## Design-Supervisor Review",
288
- "- Configured reviewers: frontend-skill",
289
- "- Review mode: screenshot-and-theme",
290
- "- Review inputs: screenshots, pencil variables",
291
- "- Status: PASS",
292
- ""
293
- ].join("\n")
294
- );
295
-
296
- const result = auditProject(project.root, {
297
- mode: "completion",
298
- changeId
299
- });
300
-
301
- assert.equal(result.status, "FAIL");
302
- assert.match(
303
- result.failures.join("\n"),
304
- /missing required field\(s\): Issue list and Revision outcome/i
305
- );
306
- });
307
-
308
- runTest("completion audit passes when required supervisor review is recorded as PASS", () => {
309
- const harness = createHarness();
310
- const project = setupProject(harness, "pass-supervisor-review", {
311
- requireSupervisorReview: true
312
- });
313
-
314
- writeText(
315
- project.pencilDesignPath,
316
- [
317
- "# Pencil Design",
318
- "",
319
- "## Design-Supervisor Review",
320
- "- Configured reviewers: frontend-skill",
321
- "- Executed reviewers: frontend-skill",
322
- "- Review source: skill",
323
- "- Review mode: screenshot-and-theme",
324
- "- Review inputs: screenshots, pencil variables, visual thesis, content plan, interaction thesis",
325
- "- Status: PASS",
326
- "- Issue list: none",
327
- "- Revision outcome: approved",
328
- "- Broad expansion approved: yes",
329
- "- Implementation-task handoff approved: yes",
330
- ""
331
- ].join("\n")
332
- );
333
-
334
- const result = auditProject(project.root, {
335
- mode: "completion",
336
- changeId
337
- });
338
-
339
- assert.equal(
340
- result.status,
341
- "PASS",
342
- `expected completion audit to pass:\n${JSON.stringify(result, null, 2)}`
343
- );
344
- assert.match(
345
- result.notes.join("\n"),
346
- /Detected design-supervisor review status PASS/i
347
- );
348
- });
349
-
350
- runTest("completion audit accepts the latest round section when multiple Design-Supervisor Review headings exist", () => {
351
- const harness = createHarness();
352
- const project = setupProject(harness, "latest-supervisor-round-wins", {
353
- requireSupervisorReview: true
354
- });
355
-
356
- writeText(
357
- project.pencilDesignPath,
358
- [
359
- "# Pencil Design",
360
- "",
361
- "## Design-Supervisor Review",
362
- "- Configured reviewers: frontend-skill",
363
- "- Executed reviewers: frontend-skill",
364
- "- Review source: skill",
365
- "- Status: BLOCK",
366
- "- Issue list: command not found",
367
- "- Revision outcome: blocked",
368
- "",
369
- "## Design-Supervisor Review (Round 2 Attempt)",
370
- "- Configured reviewers: frontend-skill",
371
- "- Executed reviewers: frontend-skill",
372
- "- Review source: skill",
373
- "- Status: PASS",
374
- "- Issue list: none",
375
- "- Revision outcome: accepted",
376
- ""
377
- ].join("\n")
378
- );
379
-
380
- const result = auditProject(project.root, {
381
- mode: "completion",
382
- changeId
383
- });
384
-
385
- assert.equal(
386
- result.status,
387
- "PASS",
388
- `expected completion audit to use latest review section:\n${JSON.stringify(result, null, 2)}`
389
- );
390
- assert.match(
391
- result.notes.join("\n"),
392
- /Detected design-supervisor review status PASS/i
393
- );
394
- });
395
-
396
- runTest("completion audit accepts multiline Issue list and Revision outcome fields", () => {
397
- const harness = createHarness();
398
- const project = setupProject(harness, "multiline-supervisor-fields", {
399
- requireSupervisorReview: true
400
- });
401
-
402
- writeText(
403
- project.pencilDesignPath,
404
- [
405
- "# Pencil Design",
406
- "",
407
- "## Design-Supervisor Review",
408
- "- Configured reviewers: frontend-skill",
409
- "- Executed reviewers: frontend-skill",
410
- "- Review source: skill",
411
- "- Status: WARN",
412
- "- Issue list:",
413
- " - icon alignment drift on top bar",
414
- " - hierarchy contrast needs one more pass",
415
- "- Revision outcome:",
416
- " - accepted with follow-up in next visual pass",
417
- ""
418
- ].join("\n")
419
- );
420
-
421
- const result = auditProject(project.root, {
422
- mode: "completion",
423
- changeId
424
- });
425
-
426
- assert.equal(
427
- result.status,
428
- "PASS",
429
- `expected accepted WARN with multiline fields to pass:\n${JSON.stringify(result, null, 2)}`
430
- );
431
- assert.match(
432
- result.notes.join("\n"),
433
- /Detected design-supervisor review status WARN/i
434
- );
435
- });
436
-
437
- runTest("completion audit fails when required supervisor review is not skill-backed", () => {
438
- const harness = createHarness();
439
- const project = setupProject(harness, "required-supervisor-skill-source", {
440
- requireSupervisorReview: true
441
- });
442
-
443
- writeText(
444
- project.pencilDesignPath,
445
- [
446
- "# Pencil Design",
447
- "",
448
- "## Design-Supervisor Review",
449
- "- Configured reviewers: frontend-skill",
450
- "- Review source: inferred",
451
- "- Status: PASS",
452
- "- Issue list: none",
453
- "- Revision outcome: approved",
454
- ""
455
- ].join("\n")
456
- );
457
-
458
- const result = auditProject(project.root, {
459
- mode: "completion",
460
- changeId
461
- });
462
-
463
- assert.equal(result.status, "FAIL");
464
- assert.match(
465
- result.failures.join("\n"),
466
- /must be skill-backed.*Require Supervisor Review: true/i
467
- );
468
- });
469
-
470
- runTest("completion audit fails when required supervisor review omits executed reviewers", () => {
471
- const harness = createHarness();
472
- const project = setupProject(harness, "required-supervisor-executed-missing", {
473
- requireSupervisorReview: true
474
- });
475
-
476
- writeText(
477
- project.pencilDesignPath,
478
- [
479
- "# Pencil Design",
480
- "",
481
- "## Design-Supervisor Review",
482
- "- Configured reviewers: frontend-skill",
483
- "- Review source: skill",
484
- "- Status: PASS",
485
- "- Issue list: none",
486
- "- Revision outcome: approved",
487
- ""
488
- ].join("\n")
489
- );
490
-
491
- const result = auditProject(project.root, {
492
- mode: "completion",
493
- changeId
494
- });
495
-
496
- assert.equal(result.status, "FAIL");
497
- assert.match(
498
- result.failures.join("\n"),
499
- /missing required field\(s\): Executed reviewers/i
500
- );
501
- });
502
-
503
- runTest("completion audit fails when required supervisor review did not execute all configured reviewers", () => {
504
- const harness = createHarness();
505
- const project = setupProject(harness, "required-supervisor-executed-incomplete", {
506
- requireSupervisorReview: true
507
- });
508
-
509
- writeText(
510
- project.pencilDesignPath,
511
- [
512
- "# Pencil Design",
513
- "",
514
- "## Design-Supervisor Review",
515
- "- Configured reviewers: frontend-skill",
516
- "- Executed reviewers: ui-ux-pro-max",
517
- "- Review source: skill",
518
- "- Status: PASS",
519
- "- Issue list: none",
520
- "- Revision outcome: approved",
521
- ""
522
- ].join("\n")
523
- );
524
-
525
- const result = auditProject(project.root, {
526
- mode: "completion",
527
- changeId
528
- });
529
-
530
- assert.equal(result.status, "FAIL");
531
- assert.match(
532
- result.failures.join("\n"),
533
- /did not execute configured reviewer skill\(s\): frontend-skill/i
534
- );
535
- });
536
-
537
- console.log("All design-supervisor audit tests passed.");
@@ -1,92 +0,0 @@
1
- const assert = require("assert/strict");
2
- const fs = require("fs");
3
- const os = require("os");
4
- const path = require("path");
5
-
6
- const { auditProject } = require("../lib/audit");
7
-
8
- function runTest(name, fn) {
9
- try {
10
- fn();
11
- console.log(`PASS ${name}`);
12
- } catch (error) {
13
- console.error(`FAIL ${name}`);
14
- throw error;
15
- }
16
- }
17
-
18
- function writeText(filePath, text) {
19
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
20
- fs.writeFileSync(filePath, text);
21
- }
22
-
23
- function setupProject(name) {
24
- const root = fs.mkdtempSync(path.join(os.tmpdir(), `da-vinci-audit-safety-${name}-`));
25
- const daVinciDir = path.join(root, ".da-vinci");
26
- const designsDir = path.join(daVinciDir, "designs");
27
- const designRegistryPath = path.join(daVinciDir, "design-registry.md");
28
-
29
- writeText(path.join(root, "DA-VINCI.md"), "# DA-VINCI\n");
30
- writeText(path.join(daVinciDir, "project-inventory.md"), "# Inventory\n");
31
- writeText(path.join(daVinciDir, "page-map.md"), "# Page Map\n");
32
- fs.mkdirSync(designsDir, { recursive: true });
33
-
34
- return {
35
- root,
36
- daVinciDir,
37
- designsDir,
38
- designRegistryPath
39
- };
40
- }
41
-
42
- runTest("integrity audit warns when registry contains out-of-root .pen reference", () => {
43
- const project = setupProject("escaped-registry");
44
- writeText(path.join(project.designsDir, "main.pen"), "{}");
45
- writeText(
46
- project.designRegistryPath,
47
- [
48
- "# Registry",
49
- "- Preferred .pen: .da-vinci/designs/main.pen",
50
- "- Legacy .pen: .da-vinci/designs/../../../outside.pen",
51
- ""
52
- ].join("\n")
53
- );
54
-
55
- const result = auditProject(project.root, {
56
- mode: "integrity"
57
- });
58
-
59
- assert.equal(result.failures.length, 0);
60
- assert.match(result.warnings.join("\n"), /escapes project root and will be ignored/i);
61
- });
62
-
63
- runTest("integrity audit surfaces traversal truncation warnings on deep trees", () => {
64
- const project = setupProject("truncated-scan");
65
- writeText(path.join(project.designsDir, "main.pen"), "{}");
66
- writeText(
67
- project.designRegistryPath,
68
- [
69
- "# Registry",
70
- "- Preferred .pen: .da-vinci/designs/main.pen",
71
- ""
72
- ].join("\n")
73
- );
74
-
75
- let current = project.designsDir;
76
- for (let depth = 0; depth < 30; depth += 1) {
77
- current = path.join(current, `deep-${depth}`);
78
- fs.mkdirSync(current, { recursive: true });
79
- }
80
- writeText(path.join(current, "deep-tree.pen"), "{}");
81
-
82
- const result = auditProject(project.root, {
83
- mode: "integrity"
84
- });
85
-
86
- assert.match(
87
- result.warnings.join("\n"),
88
- /File scan truncated under \.da-vinci[\/\\]designs/i
89
- );
90
- });
91
-
92
- console.log("All audit safety tests passed.");