@xenonbyte/da-vinci-workflow 0.2.1 → 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/tui/catalog.js ADDED
@@ -0,0 +1,1190 @@
1
+ const path = require("path");
2
+
3
+ const STAGES = [
4
+ {
5
+ id: "setup",
6
+ title: {
7
+ en: "Setup & Tooling",
8
+ zh: "初始化与工具"
9
+ },
10
+ summary: {
11
+ en: "Install, validate, bootstrap, and inspect the local Da Vinci environment.",
12
+ zh: "安装、校验、初始化并检查本地 Da Vinci 环境。"
13
+ }
14
+ },
15
+ {
16
+ id: "routing",
17
+ title: {
18
+ en: "Routing & Resume",
19
+ zh: "选路与续跑"
20
+ },
21
+ summary: {
22
+ en: "Detect the current workflow phase before deciding the next prompt or CLI action.",
23
+ zh: "在决定下一步提示词或命令前,先判断当前工作流阶段。"
24
+ }
25
+ },
26
+ {
27
+ id: "planning",
28
+ title: {
29
+ en: "Planning & Drift",
30
+ zh: "规划与漂移检查"
31
+ },
32
+ summary: {
33
+ en: "Lint planning artifacts, generate sidecars, and inspect requirement drift before implementation.",
34
+ zh: "在实现前校验规划工件、生成 sidecar,并检查需求漂移。"
35
+ }
36
+ },
37
+ {
38
+ id: "visual",
39
+ title: {
40
+ en: "Visual & Review",
41
+ zh: "视觉与审查"
42
+ },
43
+ summary: {
44
+ en: "Prepare icon catalogs and run design-supervisor review when visual gates matter.",
45
+ zh: "在视觉门禁重要时准备图标目录并执行 design-supervisor 审查。"
46
+ }
47
+ },
48
+ {
49
+ id: "pen",
50
+ title: {
51
+ en: "Pen Files & Sync",
52
+ zh: "Pen 文件与同步"
53
+ },
54
+ summary: {
55
+ en: "Seed, write, diff, and sync project-local `.pen` sources.",
56
+ zh: "初始化、写入、比对并同步项目内 `.pen` 设计源。"
57
+ }
58
+ },
59
+ {
60
+ id: "pencil",
61
+ title: {
62
+ en: "Pencil Session",
63
+ zh: "Pencil 会话"
64
+ },
65
+ summary: {
66
+ en: "Control lock ownership and live Pencil session lifecycle safely.",
67
+ zh: "安全管理锁、会话和 live Pencil 生命周期。"
68
+ }
69
+ },
70
+ {
71
+ id: "verification",
72
+ title: {
73
+ en: "Verification & Completion",
74
+ zh: "验证与完成"
75
+ },
76
+ summary: {
77
+ en: "Verify bindings, implementation coverage, structural alignment, and completion readiness.",
78
+ zh: "验证 bindings、实现覆盖、结构一致性以及完成就绪度。"
79
+ }
80
+ }
81
+ ];
82
+
83
+ function normalizeLanguage(value) {
84
+ return value === "zh" ? "zh" : "en";
85
+ }
86
+
87
+ function resolveLocalizedText(record, lang) {
88
+ if (record == null) {
89
+ return "";
90
+ }
91
+ if (typeof record === "string") {
92
+ return record;
93
+ }
94
+ const normalized = normalizeLanguage(lang);
95
+ return record[normalized] || record.en || "";
96
+ }
97
+
98
+ function defaultValue(value, placeholder) {
99
+ return value ? String(value).trim() : placeholder;
100
+ }
101
+
102
+ function maybePush(args, flag, value) {
103
+ const normalized = String(value || "").trim();
104
+ if (!normalized) {
105
+ return;
106
+ }
107
+ args.push(flag, normalized);
108
+ }
109
+
110
+ function maybeProject(args, context, options = {}) {
111
+ const requested = String(context.projectPath || "").trim();
112
+ if (!requested) {
113
+ return;
114
+ }
115
+ const currentDir = path.resolve(process.cwd());
116
+ const resolved = path.resolve(requested);
117
+ if (!options.force && resolved === currentDir) {
118
+ return;
119
+ }
120
+ args.push("--project", requested);
121
+ }
122
+
123
+ function maybeChange(args, context) {
124
+ maybePush(args, "--change", context.changeId);
125
+ }
126
+
127
+ function maybeStrict(args, context, enabled) {
128
+ if (enabled && context.strict) {
129
+ args.push("--strict");
130
+ }
131
+ }
132
+
133
+ function maybeJson(args, context, enabled) {
134
+ if (enabled && context.jsonOutput) {
135
+ args.push("--json");
136
+ }
137
+ }
138
+
139
+ function maybeContinueOnError(args, context, enabled) {
140
+ if (enabled && context.continueOnError) {
141
+ args.push("--continue-on-error");
142
+ }
143
+ }
144
+
145
+ function createCommand(definition) {
146
+ return {
147
+ supportsProject: true,
148
+ supportsChange: false,
149
+ supportsStrict: false,
150
+ supportsJson: false,
151
+ supportsContinueOnError: false,
152
+ ...definition
153
+ };
154
+ }
155
+
156
+ function createParameter(flag, value, description, options = {}) {
157
+ return {
158
+ flag,
159
+ value,
160
+ description,
161
+ required: options.required === true
162
+ };
163
+ }
164
+
165
+ const COMMON_PARAMETER_HINTS = {
166
+ project: createParameter("--project", "<path>", {
167
+ en: "Project root used as the execution cwd when you are not already inside the target repository.",
168
+ zh: "当你当前不在目标仓库目录里时,用它指定执行时的项目根目录。"
169
+ }),
170
+ change: createParameter("--change", "<id>", {
171
+ en: "Active change slice. Leave it blank only when the CLI can safely infer a single active change.",
172
+ zh: "当前活跃的 change 切片。只有 CLI 能安全推断唯一 active change 时才建议留空。"
173
+ }),
174
+ strict: createParameter("--strict", "", {
175
+ en: "Upgrade advisory findings into blocking failures where the command supports strict mode.",
176
+ zh: "在命令支持 strict 时,把 advisory 发现升级为阻断失败。"
177
+ }),
178
+ json: createParameter("--json", "", {
179
+ en: "Emit machine-readable JSON output instead of the default text report.",
180
+ zh: "输出机器可读的 JSON,而不是默认的文本报告。"
181
+ }),
182
+ continueOnError: createParameter("--continue-on-error", "", {
183
+ en: "Keep running supported checks after the first finding instead of stopping immediately.",
184
+ zh: "遇到第一个问题后仍继续执行支持的检查,而不是立即停止。"
185
+ })
186
+ };
187
+
188
+ const COMMANDS = [
189
+ createCommand({
190
+ id: "install",
191
+ stageId: "setup",
192
+ title: { en: "install", zh: "install" },
193
+ summary: {
194
+ en: "Install prompt and command assets into local Codex, Claude, and Gemini homes.",
195
+ zh: "把提示词和命令资产安装到本地 Codex、Claude、Gemini 目录。"
196
+ },
197
+ details: {
198
+ en: "Use this when the workflow skill is not installed yet or needs reinstalling for local testing.",
199
+ zh: "当 workflow skill 还未安装,或需要本地重装测试时使用。"
200
+ },
201
+ buildArgs() {
202
+ return ["install", "--platform", "codex,claude,gemini"];
203
+ }
204
+ }),
205
+ createCommand({
206
+ id: "uninstall",
207
+ stageId: "setup",
208
+ title: { en: "uninstall", zh: "uninstall" },
209
+ summary: {
210
+ en: "Remove previously installed Da Vinci prompt and command assets from local homes.",
211
+ zh: "从本地目录移除已安装的 Da Vinci 提示词和命令资产。"
212
+ },
213
+ details: {
214
+ en: "Use this when validating install drift or resetting the local prompt surfaces.",
215
+ zh: "当你要验证安装漂移或重置本地提示词面时使用。"
216
+ },
217
+ buildArgs() {
218
+ return ["uninstall", "--platform", "codex,claude,gemini"];
219
+ }
220
+ }),
221
+ createCommand({
222
+ id: "status",
223
+ stageId: "setup",
224
+ title: { en: "status", zh: "status" },
225
+ summary: {
226
+ en: "Inspect whether local prompt and command assets are installed and still match the repo.",
227
+ zh: "检查本地提示词和命令资产是否已安装且仍与仓库一致。"
228
+ },
229
+ details: {
230
+ en: "Useful before debugging a platform-specific prompt issue.",
231
+ zh: "在排查平台特定提示词问题前很有用。"
232
+ },
233
+ supportsProject: false,
234
+ buildArgs() {
235
+ return ["status"];
236
+ }
237
+ }),
238
+ createCommand({
239
+ id: "validate-assets",
240
+ stageId: "setup",
241
+ title: { en: "validate-assets", zh: "validate-assets" },
242
+ summary: {
243
+ en: "Check that the packaged Da Vinci assets are complete.",
244
+ zh: "检查打包后的 Da Vinci 资产是否完整。"
245
+ },
246
+ details: {
247
+ en: "Use this before publishing or when pack/install output looks incomplete.",
248
+ zh: "在发布前或怀疑打包/安装资产不完整时使用。"
249
+ },
250
+ supportsProject: false,
251
+ buildArgs() {
252
+ return ["validate-assets"];
253
+ }
254
+ }),
255
+ createCommand({
256
+ id: "bootstrap-project",
257
+ stageId: "setup",
258
+ title: { en: "bootstrap-project", zh: "bootstrap-project" },
259
+ summary: {
260
+ en: "Create the minimum `.da-vinci/` project spine and optionally seed one change slice.",
261
+ zh: "创建最小 `.da-vinci/` 项目骨架,并可顺带初始化一个 change 切片。"
262
+ },
263
+ details: {
264
+ en: "Start here when the repository has no usable workflow artifacts yet.",
265
+ zh: "当仓库里还没有可用工作流工件时,从这里开始。"
266
+ },
267
+ supportsChange: true,
268
+ buildArgs(context) {
269
+ const args = ["bootstrap-project"];
270
+ maybeProject(args, context, { force: true });
271
+ maybeChange(args, context);
272
+ return args;
273
+ }
274
+ }),
275
+ createCommand({
276
+ id: "workflow-status",
277
+ stageId: "routing",
278
+ title: { en: "workflow-status", zh: "workflow-status" },
279
+ summary: {
280
+ en: "Inspect the current stage, blockers, and handoff gates from artifact truth.",
281
+ zh: "根据工件真相检查当前阶段、阻塞项和交接门禁。"
282
+ },
283
+ details: {
284
+ en: "Run this first whenever you resume a paused project or are unsure which stage is active.",
285
+ zh: "每次恢复暂停项目,或不确定当前阶段时,先运行它。"
286
+ },
287
+ supportsChange: true,
288
+ supportsJson: true,
289
+ buildArgs(context) {
290
+ const args = ["workflow-status"];
291
+ maybeProject(args, context);
292
+ maybeChange(args, context);
293
+ maybeJson(args, context, true);
294
+ return args;
295
+ }
296
+ }),
297
+ createCommand({
298
+ id: "next-step",
299
+ stageId: "routing",
300
+ title: { en: "next-step", zh: "next-step" },
301
+ summary: {
302
+ en: "Derive the first recommended continuation action from the current workflow state.",
303
+ zh: "根据当前工作流状态给出第一优先的续跑动作。"
304
+ },
305
+ details: {
306
+ en: "Use it after `workflow-status` to decide whether to keep planning, bind pages, generate tasks, or implement.",
307
+ zh: "通常在 `workflow-status` 之后运行,用来判断接下来是补规划、做绑定、生成任务还是实现。"
308
+ },
309
+ supportsChange: true,
310
+ supportsJson: true,
311
+ buildArgs(context) {
312
+ const args = ["next-step"];
313
+ maybeProject(args, context);
314
+ maybeChange(args, context);
315
+ maybeJson(args, context, true);
316
+ return args;
317
+ }
318
+ }),
319
+ createCommand({
320
+ id: "lint-spec",
321
+ stageId: "planning",
322
+ title: { en: "lint-spec", zh: "lint-spec" },
323
+ summary: {
324
+ en: "Lint runtime `spec.md` files before implementation planning or code generation.",
325
+ zh: "在实现规划或代码落地前校验运行时 `spec.md`。"
326
+ },
327
+ details: {
328
+ en: "Use it when spec headings, acceptance quality, or state semantics may still be unstable.",
329
+ zh: "当 spec 标题结构、验收标准质量或状态语义还不够稳定时使用。"
330
+ },
331
+ supportsChange: true,
332
+ supportsStrict: true,
333
+ supportsJson: true,
334
+ supportsContinueOnError: true,
335
+ buildArgs(context) {
336
+ const args = ["lint-spec"];
337
+ maybeProject(args, context);
338
+ maybeChange(args, context);
339
+ maybeStrict(args, context, true);
340
+ maybeJson(args, context, true);
341
+ maybeContinueOnError(args, context, true);
342
+ return args;
343
+ }
344
+ }),
345
+ createCommand({
346
+ id: "scope-check",
347
+ stageId: "planning",
348
+ title: { en: "scope-check", zh: "scope-check" },
349
+ summary: {
350
+ en: "Check page/state propagation across proposal, page-map, runtime specs, Pencil design, and tasks.",
351
+ zh: "检查 proposal、page-map、运行时 spec、Pencil 设计和任务之间的页面/状态传播。"
352
+ },
353
+ details: {
354
+ en: "Use this before implementation when planning artifacts disagree or the active pages are ambiguous.",
355
+ zh: "当规划工件有分歧,或活跃页面不明确时,在实现前运行。"
356
+ },
357
+ supportsChange: true,
358
+ supportsStrict: true,
359
+ supportsJson: true,
360
+ supportsContinueOnError: true,
361
+ buildArgs(context) {
362
+ const args = ["scope-check"];
363
+ maybeProject(args, context);
364
+ maybeChange(args, context);
365
+ maybeStrict(args, context, true);
366
+ maybeJson(args, context, true);
367
+ maybeContinueOnError(args, context, true);
368
+ return args;
369
+ }
370
+ }),
371
+ createCommand({
372
+ id: "lint-tasks",
373
+ stageId: "planning",
374
+ title: { en: "lint-tasks", zh: "lint-tasks" },
375
+ summary: {
376
+ en: "Validate task groups, ordering, and verification coverage inside `tasks.md`.",
377
+ zh: "校验 `tasks.md` 里的任务分组、顺序和验证覆盖。"
378
+ },
379
+ details: {
380
+ en: "Use it after task generation and before build handoff.",
381
+ zh: "在任务生成后、实现交接前使用。"
382
+ },
383
+ supportsChange: true,
384
+ supportsStrict: true,
385
+ supportsJson: true,
386
+ supportsContinueOnError: true,
387
+ buildArgs(context) {
388
+ const args = ["lint-tasks"];
389
+ maybeProject(args, context);
390
+ maybeChange(args, context);
391
+ maybeStrict(args, context, true);
392
+ maybeJson(args, context, true);
393
+ maybeContinueOnError(args, context, true);
394
+ return args;
395
+ }
396
+ }),
397
+ createCommand({
398
+ id: "lint-bindings",
399
+ stageId: "planning",
400
+ title: { en: "lint-bindings", zh: "lint-bindings" },
401
+ summary: {
402
+ en: "Validate implementation-to-Pencil mappings before scaffold or verify steps.",
403
+ zh: "在 scaffold 或 verify 前校验实现到 Pencil 的映射。"
404
+ },
405
+ details: {
406
+ en: "Run this after editing `pencil-bindings.md` or when implementation landing paths changed.",
407
+ zh: "修改了 `pencil-bindings.md`,或实现落点路径改变后运行。"
408
+ },
409
+ supportsChange: true,
410
+ supportsStrict: true,
411
+ supportsJson: true,
412
+ supportsContinueOnError: true,
413
+ buildArgs(context) {
414
+ const args = ["lint-bindings"];
415
+ maybeProject(args, context);
416
+ maybeChange(args, context);
417
+ maybeStrict(args, context, true);
418
+ maybeJson(args, context, true);
419
+ maybeContinueOnError(args, context, true);
420
+ return args;
421
+ }
422
+ }),
423
+ createCommand({
424
+ id: "generate-sidecars",
425
+ stageId: "planning",
426
+ title: { en: "generate-sidecars", zh: "generate-sidecars" },
427
+ summary: {
428
+ en: "Write deterministic planning sidecars for spec, tasks, page-map, and bindings.",
429
+ zh: "为 spec、tasks、page-map 和 bindings 写入确定性的 planning sidecar。"
430
+ },
431
+ details: {
432
+ en: "Use it after planning edits when you need explicit diffable state for continuation.",
433
+ zh: "当规划工件改动后需要显式、可比对的续跑状态时使用。"
434
+ },
435
+ supportsChange: true,
436
+ supportsJson: true,
437
+ supportsContinueOnError: true,
438
+ buildArgs(context) {
439
+ const args = ["generate-sidecars"];
440
+ maybeProject(args, context);
441
+ maybeChange(args, context);
442
+ maybeJson(args, context, true);
443
+ maybeContinueOnError(args, context, true);
444
+ return args;
445
+ }
446
+ }),
447
+ createCommand({
448
+ id: "diff-spec",
449
+ stageId: "planning",
450
+ title: { en: "diff-spec", zh: "diff-spec" },
451
+ summary: {
452
+ en: "Compare current planning artifacts with saved sidecars to inspect drift safely.",
453
+ zh: "把当前规划工件与已保存的 sidecar 做比对,安全检查漂移。"
454
+ },
455
+ details: {
456
+ en: "Useful after spec, task, or bindings edits when a paused workflow needs fresh route decisions.",
457
+ zh: "当 spec、tasks 或 bindings 发生变化,而暂停中的工作流需要重新判断下一步时很有用。"
458
+ },
459
+ supportsChange: true,
460
+ supportsJson: true,
461
+ supportsContinueOnError: true,
462
+ buildArgs(context) {
463
+ const args = ["diff-spec"];
464
+ maybeProject(args, context);
465
+ maybeChange(args, context);
466
+ maybeJson(args, context, true);
467
+ maybeContinueOnError(args, context, true);
468
+ return args;
469
+ }
470
+ }),
471
+ createCommand({
472
+ id: "scaffold",
473
+ stageId: "planning",
474
+ title: { en: "scaffold", zh: "scaffold" },
475
+ summary: {
476
+ en: "Generate a constrained TODO skeleton from `pencil-bindings.md` mappings.",
477
+ zh: "根据 `pencil-bindings.md` 映射生成受约束的 TODO 骨架。"
478
+ },
479
+ details: {
480
+ en: "This is an MVP skeleton only, not autonomous production code generation.",
481
+ zh: "它只是 MVP 级骨架,不是自动生成生产代码。"
482
+ },
483
+ supportsChange: true,
484
+ supportsJson: true,
485
+ supportsContinueOnError: true,
486
+ buildArgs(context) {
487
+ const args = ["scaffold"];
488
+ maybeProject(args, context);
489
+ maybeChange(args, context);
490
+ maybeJson(args, context, true);
491
+ maybeContinueOnError(args, context, true);
492
+ return args;
493
+ }
494
+ }),
495
+ createCommand({
496
+ id: "icon-sync",
497
+ stageId: "visual",
498
+ title: { en: "icon-sync", zh: "icon-sync" },
499
+ summary: {
500
+ en: "Fetch official icon catalogs for supported libraries into the local cache.",
501
+ zh: "把支持库的官方图标目录拉到本地缓存。"
502
+ },
503
+ details: {
504
+ en: "Run this when the design depends on newer icon names than the local cache already knows.",
505
+ zh: "当设计依赖本地图标缓存还不认识的新图标名时运行。"
506
+ },
507
+ supportsProject: false,
508
+ supportsStrict: true,
509
+ buildArgs(context) {
510
+ const args = ["icon-sync"];
511
+ maybeStrict(args, context, true);
512
+ return args;
513
+ }
514
+ }),
515
+ createCommand({
516
+ id: "icon-search",
517
+ stageId: "visual",
518
+ title: { en: "icon-search", zh: "icon-search" },
519
+ summary: {
520
+ en: "Search synced icon catalogs for buildable icon names before Pencil work.",
521
+ zh: "在 Pencil 工作前搜索已同步图标目录中的可落地图标名。"
522
+ },
523
+ details: {
524
+ en: "The preview keeps a query placeholder so you can fill it before running.",
525
+ zh: "预览里会保留查询占位符,运行前填入关键词即可。"
526
+ },
527
+ supportsProject: false,
528
+ supportsJson: true,
529
+ buildArgs(context) {
530
+ const args = ["icon-search", "--query", "<query>"];
531
+ maybeJson(args, context, true);
532
+ return args;
533
+ }
534
+ }),
535
+ createCommand({
536
+ id: "supervisor-review",
537
+ stageId: "visual",
538
+ title: { en: "supervisor-review", zh: "supervisor-review" },
539
+ summary: {
540
+ en: "Run or record Design-Supervisor review for the active change slice.",
541
+ zh: "为当前 change 切片执行或记录 Design-Supervisor 审查。"
542
+ },
543
+ details: {
544
+ en: "Use it after anchor screenshots are ready and reviewer-backed style signoff matters.",
545
+ zh: "当 anchor 截图已准备好,且需要 reviewer 风格签收时使用。"
546
+ },
547
+ supportsChange: true,
548
+ supportsJson: true,
549
+ buildArgs(context) {
550
+ const args = ["supervisor-review"];
551
+ maybeProject(args, context);
552
+ maybeChange(args, context);
553
+ if (!String(context.changeId || "").trim()) {
554
+ args.push("--change", "<change-id>");
555
+ }
556
+ maybeJson(args, context, true);
557
+ return args;
558
+ }
559
+ }),
560
+ createCommand({
561
+ id: "preflight-pencil",
562
+ stageId: "pen",
563
+ title: { en: "preflight-pencil", zh: "preflight-pencil" },
564
+ summary: {
565
+ en: "Statically check a Pencil `batch_design` payload before sending it to MCP.",
566
+ zh: "在发送给 MCP 前静态校验 Pencil `batch_design` 载荷。"
567
+ },
568
+ details: {
569
+ en: "Use it when a non-trivial Pencil operation list may fail due to syntax or schema drift.",
570
+ zh: "当较复杂的 Pencil 操作列表可能因为语法或 schema 漂移失败时使用。"
571
+ },
572
+ buildArgs() {
573
+ return ["preflight-pencil", "--ops-file", "<ops-file>"];
574
+ }
575
+ }),
576
+ createCommand({
577
+ id: "ensure-pen",
578
+ stageId: "pen",
579
+ title: { en: "ensure-pen", zh: "ensure-pen" },
580
+ summary: {
581
+ en: "Seed or verify a project-local `.pen` file before live editing begins.",
582
+ zh: "在 live 编辑开始前初始化或校验项目内 `.pen` 文件。"
583
+ },
584
+ details: {
585
+ en: "Use it to make sure the workflow-owned `.pen` exists before Pencil work starts.",
586
+ zh: "用它确保工作流管理的 `.pen` 在开始 Pencil 工作前已经存在。"
587
+ },
588
+ supportsProject: false,
589
+ buildArgs() {
590
+ return ["ensure-pen", "--output", "<pen-output-path>"];
591
+ }
592
+ }),
593
+ createCommand({
594
+ id: "write-pen",
595
+ stageId: "pen",
596
+ title: { en: "write-pen", zh: "write-pen" },
597
+ summary: {
598
+ en: "Write a `.pen` file from node and variable payloads captured from MCP.",
599
+ zh: "根据 MCP 抓取到的节点和变量载荷写入 `.pen` 文件。"
600
+ },
601
+ details: {
602
+ en: "Useful when persisting a live Pencil snapshot back into the project-owned source.",
603
+ zh: "当你要把 live Pencil 快照持久化回项目自有设计源时很有用。"
604
+ },
605
+ supportsProject: false,
606
+ buildArgs() {
607
+ return ["write-pen", "--output", "<pen-output-path>", "--nodes-file", "<nodes-json>"];
608
+ }
609
+ }),
610
+ createCommand({
611
+ id: "check-pen-sync",
612
+ stageId: "pen",
613
+ title: { en: "check-pen-sync", zh: "check-pen-sync" },
614
+ summary: {
615
+ en: "Compare the current live payload with a persisted project-local `.pen` file.",
616
+ zh: "比较当前 live 载荷与已持久化的项目内 `.pen` 文件。"
617
+ },
618
+ details: {
619
+ en: "Run this before closing a session when you need explicit sync evidence.",
620
+ zh: "当你在结束会话前需要显式同步证据时运行。"
621
+ },
622
+ supportsProject: false,
623
+ buildArgs() {
624
+ return ["check-pen-sync", "--pen", "<pen-path>", "--nodes-file", "<nodes-json>"];
625
+ }
626
+ }),
627
+ createCommand({
628
+ id: "check-pen-baseline",
629
+ stageId: "pen",
630
+ title: { en: "check-pen-baseline", zh: "check-pen-baseline" },
631
+ summary: {
632
+ en: "Compare the project-local `.pen` file with one or more external baseline sources.",
633
+ zh: "把项目内 `.pen` 与一个或多个外部 baseline 源做比对。"
634
+ },
635
+ details: {
636
+ en: "Use it before a new write phase when backups or mirrored sources may have drifted.",
637
+ zh: "当备份或镜像源可能漂移时,在新的写阶段前运行。"
638
+ },
639
+ supportsProject: false,
640
+ buildArgs() {
641
+ return ["check-pen-baseline", "--pen", "<pen-path>", "--baseline", "<baseline-path>"];
642
+ }
643
+ }),
644
+ createCommand({
645
+ id: "sync-pen-source",
646
+ stageId: "pen",
647
+ title: { en: "sync-pen-source", zh: "sync-pen-source" },
648
+ summary: {
649
+ en: "Copy the selected latest `.pen` source back into the project baseline path.",
650
+ zh: "把选定的最新 `.pen` 源同步回项目 baseline 路径。"
651
+ },
652
+ details: {
653
+ en: "Use it after manually choosing the winner between divergent `.pen` sources.",
654
+ zh: "当你在多个分歧 `.pen` 源之间手动选定胜出版本后使用。"
655
+ },
656
+ supportsProject: false,
657
+ buildArgs() {
658
+ return ["sync-pen-source", "--from", "<source-pen>", "--to", "<target-pen>"];
659
+ }
660
+ }),
661
+ createCommand({
662
+ id: "snapshot-pen",
663
+ stageId: "pen",
664
+ title: { en: "snapshot-pen", zh: "snapshot-pen" },
665
+ summary: {
666
+ en: "Copy a `.pen` file to a new location while recording snapshot metadata.",
667
+ zh: "把 `.pen` 文件复制到新位置,并记录快照元数据。"
668
+ },
669
+ details: {
670
+ en: "This is a disk-to-disk utility rather than a live-edit persistence step.",
671
+ zh: "它是磁盘到磁盘的工具,不是 live 编辑持久化步骤。"
672
+ },
673
+ supportsProject: false,
674
+ buildArgs() {
675
+ return ["snapshot-pen", "--input", "<source-pen>", "--output", "<snapshot-pen>"];
676
+ }
677
+ }),
678
+ createCommand({
679
+ id: "pencil-lock-acquire",
680
+ stageId: "pencil",
681
+ title: { en: "pencil-lock acquire", zh: "pencil-lock acquire" },
682
+ summary: {
683
+ en: "Acquire the global Pencil lock for the active project before write operations.",
684
+ zh: "在执行写操作前为当前项目获取全局 Pencil 锁。"
685
+ },
686
+ details: {
687
+ en: "Use it when orchestrating Pencil writes manually or outside `pencil-session begin`.",
688
+ zh: "当你手动编排 Pencil 写操作,或不通过 `pencil-session begin` 时使用。"
689
+ },
690
+ buildArgs(context) {
691
+ const args = ["pencil-lock", "acquire"];
692
+ maybeProject(args, context, { force: true });
693
+ return args;
694
+ }
695
+ }),
696
+ createCommand({
697
+ id: "pencil-lock-release",
698
+ stageId: "pencil",
699
+ title: { en: "pencil-lock release", zh: "pencil-lock release" },
700
+ summary: {
701
+ en: "Release the global Pencil lock for the active project.",
702
+ zh: "释放当前项目持有的全局 Pencil 锁。"
703
+ },
704
+ details: {
705
+ en: "Use it after emergency recovery or when you managed lock ownership manually.",
706
+ zh: "在应急恢复后,或你手动管理锁时使用。"
707
+ },
708
+ buildArgs(context) {
709
+ const args = ["pencil-lock", "release"];
710
+ maybeProject(args, context, { force: true });
711
+ return args;
712
+ }
713
+ }),
714
+ createCommand({
715
+ id: "pencil-lock-status",
716
+ stageId: "pencil",
717
+ title: { en: "pencil-lock status", zh: "pencil-lock status" },
718
+ summary: {
719
+ en: "Inspect current global Pencil lock ownership.",
720
+ zh: "检查当前全局 Pencil 锁的持有状态。"
721
+ },
722
+ details: {
723
+ en: "Run it when another workflow appears to be holding Pencil write access.",
724
+ zh: "当你怀疑别的工作流占用了 Pencil 写权限时运行。"
725
+ },
726
+ buildArgs() {
727
+ return ["pencil-lock", "status"];
728
+ }
729
+ }),
730
+ createCommand({
731
+ id: "pencil-session-begin",
732
+ stageId: "pencil",
733
+ title: { en: "pencil-session begin", zh: "pencil-session begin" },
734
+ summary: {
735
+ en: "Begin a workflow-owned Pencil session on the registered project-local `.pen` source.",
736
+ zh: "在已登记的项目内 `.pen` 源上开始工作流管理的 Pencil 会话。"
737
+ },
738
+ details: {
739
+ en: "Use it before the first live write in a design phase.",
740
+ zh: "在设计阶段首次 live 写入前使用。"
741
+ },
742
+ buildArgs(context) {
743
+ const args = ["pencil-session", "begin"];
744
+ maybeProject(args, context, { force: true });
745
+ args.push("--pen", "<pen-path>");
746
+ return args;
747
+ }
748
+ }),
749
+ createCommand({
750
+ id: "pencil-session-persist",
751
+ stageId: "pencil",
752
+ title: { en: "pencil-session persist", zh: "pencil-session persist" },
753
+ summary: {
754
+ en: "Persist the current live Pencil payload back into the workflow-owned `.pen` source.",
755
+ zh: "把当前 live Pencil 载荷持久化回工作流管理的 `.pen` 源。"
756
+ },
757
+ details: {
758
+ en: "Use it after material design edits during an open session.",
759
+ zh: "在打开的会话中完成实质性设计修改后使用。"
760
+ },
761
+ buildArgs(context) {
762
+ const args = ["pencil-session", "persist"];
763
+ maybeProject(args, context, { force: true });
764
+ args.push("--pen", "<pen-path>", "--nodes-file", "<nodes-json>");
765
+ return args;
766
+ }
767
+ }),
768
+ createCommand({
769
+ id: "pencil-session-end",
770
+ stageId: "pencil",
771
+ title: { en: "pencil-session end", zh: "pencil-session end" },
772
+ summary: {
773
+ en: "End the active Pencil session after final sync verification.",
774
+ zh: "在完成最终同步校验后结束当前 Pencil 会话。"
775
+ },
776
+ details: {
777
+ en: "Use it only after the final live snapshot has been persisted or explicitly force-close if recovery is needed.",
778
+ zh: "应在最终 live 快照已持久化后使用;只有需要恢复时才显式强制关闭。"
779
+ },
780
+ buildArgs(context) {
781
+ const args = ["pencil-session", "end"];
782
+ maybeProject(args, context, { force: true });
783
+ args.push("--pen", "<pen-path>", "--nodes-file", "<nodes-json>");
784
+ return args;
785
+ }
786
+ }),
787
+ createCommand({
788
+ id: "pencil-session-status",
789
+ stageId: "pencil",
790
+ title: { en: "pencil-session status", zh: "pencil-session status" },
791
+ summary: {
792
+ en: "Inspect the active project Pencil session and sync metadata.",
793
+ zh: "检查当前项目的 Pencil 会话与同步元数据。"
794
+ },
795
+ details: {
796
+ en: "Run it before resuming design work when you are unsure whether the previous session ended cleanly.",
797
+ zh: "当你不确定上一次会话是否正常结束时,在恢复设计工作前运行。"
798
+ },
799
+ buildArgs(context) {
800
+ const args = ["pencil-session", "status"];
801
+ maybeProject(args, context, { force: true });
802
+ return args;
803
+ }
804
+ }),
805
+ createCommand({
806
+ id: "verify-bindings",
807
+ stageId: "verification",
808
+ title: { en: "verify-bindings", zh: "verify-bindings" },
809
+ summary: {
810
+ en: "Verify that implementation routes are still traceable to Pencil bindings.",
811
+ zh: "验证实现路由仍然能追溯到 Pencil bindings。"
812
+ },
813
+ details: {
814
+ en: "Run it before terminal claims or after bindings / routing edits.",
815
+ zh: "在终态声明前,或 bindings / 路由改动后运行。"
816
+ },
817
+ supportsChange: true,
818
+ supportsStrict: true,
819
+ supportsJson: true,
820
+ supportsContinueOnError: true,
821
+ buildArgs(context) {
822
+ const args = ["verify-bindings"];
823
+ maybeProject(args, context);
824
+ maybeChange(args, context);
825
+ maybeStrict(args, context, true);
826
+ maybeJson(args, context, true);
827
+ maybeContinueOnError(args, context, true);
828
+ return args;
829
+ }
830
+ }),
831
+ createCommand({
832
+ id: "verify-implementation",
833
+ stageId: "verification",
834
+ title: { en: "verify-implementation", zh: "verify-implementation" },
835
+ summary: {
836
+ en: "Check whether real implementation files cover the active requirement and task surfaces.",
837
+ zh: "检查真实实现文件是否覆盖当前需求与任务面。"
838
+ },
839
+ details: {
840
+ en: "Use it after code changes to validate that test files or fixtures are not being mistaken for the implementation.",
841
+ zh: "在代码修改后运行,确认不会把测试文件或 fixture 误当成真实实现。"
842
+ },
843
+ supportsChange: true,
844
+ supportsStrict: true,
845
+ supportsJson: true,
846
+ supportsContinueOnError: true,
847
+ buildArgs(context) {
848
+ const args = ["verify-implementation"];
849
+ maybeProject(args, context);
850
+ maybeChange(args, context);
851
+ maybeStrict(args, context, true);
852
+ maybeJson(args, context, true);
853
+ maybeContinueOnError(args, context, true);
854
+ return args;
855
+ }
856
+ }),
857
+ createCommand({
858
+ id: "verify-structure",
859
+ stageId: "verification",
860
+ title: { en: "verify-structure", zh: "verify-structure" },
861
+ summary: {
862
+ en: "Check structural alignment between bindings and implementation markup.",
863
+ zh: "检查 bindings 与实现结构之间的一致性。"
864
+ },
865
+ details: {
866
+ en: "Useful after markup-heavy changes or when you need heuristic fallback visibility.",
867
+ zh: "在大量结构变动后,或你需要看 heuristic fallback 情况时很有用。"
868
+ },
869
+ supportsChange: true,
870
+ supportsStrict: true,
871
+ supportsJson: true,
872
+ supportsContinueOnError: true,
873
+ buildArgs(context) {
874
+ const args = ["verify-structure"];
875
+ maybeProject(args, context);
876
+ maybeChange(args, context);
877
+ maybeStrict(args, context, true);
878
+ maybeJson(args, context, true);
879
+ maybeContinueOnError(args, context, true);
880
+ return args;
881
+ }
882
+ }),
883
+ createCommand({
884
+ id: "verify-coverage",
885
+ stageId: "verification",
886
+ title: { en: "verify-coverage", zh: "verify-coverage" },
887
+ summary: {
888
+ en: "Aggregate requirement, design, bindings, implementation, and test evidence into one signal.",
889
+ zh: "把需求、设计、bindings、实现和测试证据聚合成一个总信号。"
890
+ },
891
+ details: {
892
+ en: "Use it before completion audit or terminal status claims.",
893
+ zh: "在 completion audit 或终态声明前使用。"
894
+ },
895
+ supportsChange: true,
896
+ supportsStrict: true,
897
+ supportsJson: true,
898
+ supportsContinueOnError: true,
899
+ buildArgs(context) {
900
+ const args = ["verify-coverage"];
901
+ maybeProject(args, context);
902
+ maybeChange(args, context);
903
+ maybeStrict(args, context, true);
904
+ maybeJson(args, context, true);
905
+ maybeContinueOnError(args, context, true);
906
+ return args;
907
+ }
908
+ }),
909
+ createCommand({
910
+ id: "audit",
911
+ stageId: "verification",
912
+ title: { en: "audit", zh: "audit" },
913
+ summary: {
914
+ en: "Run integrity or completion audit once the workflow is ready for that gate.",
915
+ zh: "在工作流准备好后执行 integrity 或 completion audit。"
916
+ },
917
+ details: {
918
+ en: "The preview defaults to integrity mode; edit it to `completion` before terminal completion claims.",
919
+ zh: "预览默认是 integrity 模式;在终态完成声明前,把它改成 `completion`。"
920
+ },
921
+ supportsChange: true,
922
+ supportsContinueOnError: true,
923
+ buildArgs(context) {
924
+ const args = ["audit", "--mode", "integrity"];
925
+ maybeProject(args, context);
926
+ maybeChange(args, context);
927
+ maybeContinueOnError(args, context, true);
928
+ return args;
929
+ }
930
+ })
931
+ ];
932
+
933
+ const COMMAND_PARAMETER_HINTS = {
934
+ install: [
935
+ createParameter("--platform", "codex,claude,gemini", {
936
+ en: "Fixed in the TUI to install assets for all supported local prompt surfaces.",
937
+ zh: "TUI 里固定为所有支持的平台一起安装资产。"
938
+ })
939
+ ],
940
+ uninstall: [
941
+ createParameter("--platform", "codex,claude,gemini", {
942
+ en: "Fixed in the TUI to remove assets from every supported local prompt surface.",
943
+ zh: "TUI 里固定为从所有支持的平台一起移除资产。"
944
+ })
945
+ ],
946
+ "icon-search": [
947
+ createParameter("--query", "<query>", {
948
+ en: "Keyword or phrase to search inside the synced icon catalogs.",
949
+ zh: "用于在已同步图标目录里搜索的关键词或短语。"
950
+ }, { required: true })
951
+ ],
952
+ "preflight-pencil": [
953
+ createParameter("--ops-file", "<ops-file>", {
954
+ en: "Path to the Pencil operations file that should be statically checked before MCP submission.",
955
+ zh: "在提交给 MCP 前要做静态校验的 Pencil 操作文件路径。"
956
+ }, { required: true })
957
+ ],
958
+ "ensure-pen": [
959
+ createParameter("--output", "<pen-output-path>", {
960
+ en: "Destination path for the workflow-owned `.pen` source that should exist locally.",
961
+ zh: "工作流管理的 `.pen` 设计源在本地应写入或校验的目标路径。"
962
+ }, { required: true })
963
+ ],
964
+ "write-pen": [
965
+ createParameter("--output", "<pen-output-path>", {
966
+ en: "Destination `.pen` file path to write from the captured MCP payload.",
967
+ zh: "把 MCP 抓取载荷写成 `.pen` 文件时的输出路径。"
968
+ }, { required: true }),
969
+ createParameter("--nodes-file", "<nodes-json>", {
970
+ en: "Path to the captured nodes JSON payload exported from Pencil MCP.",
971
+ zh: "从 Pencil MCP 导出的节点 JSON 载荷路径。"
972
+ }, { required: true })
973
+ ],
974
+ "check-pen-sync": [
975
+ createParameter("--pen", "<pen-path>", {
976
+ en: "Project-local `.pen` file to compare against the current live payload.",
977
+ zh: "要与当前 live 载荷做比对的项目内 `.pen` 文件。"
978
+ }, { required: true }),
979
+ createParameter("--nodes-file", "<nodes-json>", {
980
+ en: "Path to the live nodes JSON payload captured from the current session.",
981
+ zh: "当前会话抓取到的 live nodes JSON 载荷路径。"
982
+ }, { required: true })
983
+ ],
984
+ "check-pen-baseline": [
985
+ createParameter("--pen", "<pen-path>", {
986
+ en: "Project-local `.pen` file that acts as the workflow-owned source of truth.",
987
+ zh: "作为工作流自有真相源的项目内 `.pen` 文件。"
988
+ }, { required: true }),
989
+ createParameter("--baseline", "<baseline-path>", {
990
+ en: "External baseline `.pen` path that should be compared with the project-local source.",
991
+ zh: "要与项目内设计源做比较的外部 baseline `.pen` 路径。"
992
+ }, { required: true })
993
+ ],
994
+ "sync-pen-source": [
995
+ createParameter("--from", "<source-pen>", {
996
+ en: "Source `.pen` file that won the manual comparison and should be copied forward.",
997
+ zh: "手动比对后选中的胜出 `.pen` 源路径。"
998
+ }, { required: true }),
999
+ createParameter("--to", "<target-pen>", {
1000
+ en: "Destination `.pen` path that should receive the chosen source file.",
1001
+ zh: "接收最终胜出设计源的目标 `.pen` 路径。"
1002
+ }, { required: true })
1003
+ ],
1004
+ "snapshot-pen": [
1005
+ createParameter("--input", "<source-pen>", {
1006
+ en: "Source `.pen` file to snapshot.",
1007
+ zh: "要制作快照的源 `.pen` 文件。"
1008
+ }, { required: true }),
1009
+ createParameter("--output", "<snapshot-pen>", {
1010
+ en: "Destination path for the recorded `.pen` snapshot.",
1011
+ zh: "记录 `.pen` 快照的输出路径。"
1012
+ }, { required: true })
1013
+ ],
1014
+ "pencil-session-begin": [
1015
+ createParameter("--pen", "<pen-path>", {
1016
+ en: "Registered project-local `.pen` source that the live Pencil session should open against.",
1017
+ zh: "live Pencil 会话要基于其开启的已登记项目内 `.pen` 设计源。"
1018
+ }, { required: true })
1019
+ ],
1020
+ "pencil-session-persist": [
1021
+ createParameter("--pen", "<pen-path>", {
1022
+ en: "Registered project-local `.pen` source that should receive the persisted live changes.",
1023
+ zh: "接收 live 设计改动持久化结果的已登记项目内 `.pen` 设计源。"
1024
+ }, { required: true }),
1025
+ createParameter("--nodes-file", "<nodes-json>", {
1026
+ en: "Captured live nodes JSON payload that should be written back into the `.pen` source.",
1027
+ zh: "需要写回 `.pen` 设计源的 live nodes JSON 载荷。"
1028
+ }, { required: true })
1029
+ ],
1030
+ "pencil-session-end": [
1031
+ createParameter("--pen", "<pen-path>", {
1032
+ en: "Registered project-local `.pen` source associated with the session being closed.",
1033
+ zh: "当前要关闭会话所关联的已登记项目内 `.pen` 设计源。"
1034
+ }, { required: true }),
1035
+ createParameter("--nodes-file", "<nodes-json>", {
1036
+ en: "Final live nodes JSON payload used for the closing sync verification.",
1037
+ zh: "结束前做同步校验所使用的最终 live nodes JSON 载荷。"
1038
+ }, { required: true })
1039
+ ],
1040
+ audit: [
1041
+ createParameter("--mode", "integrity|completion", {
1042
+ en: "Audit mode. The preview defaults to `integrity`; switch to `completion` before final completion claims.",
1043
+ zh: "审查模式。预览默认使用 `integrity`;在做最终完成声明前改成 `completion`。"
1044
+ })
1045
+ ]
1046
+ };
1047
+
1048
+ function getCommandParameterItems(command) {
1049
+ if (!command) {
1050
+ return [];
1051
+ }
1052
+ const items = [];
1053
+ if (command.supportsProject) {
1054
+ items.push(COMMON_PARAMETER_HINTS.project);
1055
+ }
1056
+ if (command.supportsChange) {
1057
+ items.push(COMMON_PARAMETER_HINTS.change);
1058
+ }
1059
+ if (command.supportsStrict) {
1060
+ items.push(COMMON_PARAMETER_HINTS.strict);
1061
+ }
1062
+ if (command.supportsJson) {
1063
+ items.push(COMMON_PARAMETER_HINTS.json);
1064
+ }
1065
+ if (command.supportsContinueOnError) {
1066
+ items.push(COMMON_PARAMETER_HINTS.continueOnError);
1067
+ }
1068
+ for (const item of COMMAND_PARAMETER_HINTS[command.id] || []) {
1069
+ items.push(item);
1070
+ }
1071
+
1072
+ const deduped = [];
1073
+ const seen = new Set();
1074
+ for (const item of items) {
1075
+ const key = `${item.flag} ${item.value || ""}`.trim();
1076
+ if (seen.has(key)) {
1077
+ continue;
1078
+ }
1079
+ seen.add(key);
1080
+ deduped.push({ ...item });
1081
+ }
1082
+ return deduped;
1083
+ }
1084
+
1085
+ function getStageById(stageId) {
1086
+ return STAGES.find((stage) => stage.id === stageId) || null;
1087
+ }
1088
+
1089
+ function getCommandById(commandId) {
1090
+ return COMMANDS.find((command) => command.id === commandId) || null;
1091
+ }
1092
+
1093
+ function buildCommandArgs(command, context = {}) {
1094
+ if (!command || typeof command.buildArgs !== "function") {
1095
+ throw new Error("Command definition is missing a buildArgs function.");
1096
+ }
1097
+ return command.buildArgs(context);
1098
+ }
1099
+
1100
+ function shellQuote(value) {
1101
+ const text = String(value || "");
1102
+ if (/^[A-Za-z0-9_./:@=+-]+$/.test(text)) {
1103
+ return text;
1104
+ }
1105
+ return `'${text.replace(/'/g, `'\"'\"'`)}'`;
1106
+ }
1107
+
1108
+ function buildDisplayCommand(command, context = {}) {
1109
+ const args = buildCommandArgs(command, context);
1110
+ return ["da-vinci", ...args].map(shellQuote).join(" ");
1111
+ }
1112
+
1113
+ function hasPlaceholders(args) {
1114
+ return (args || []).some((token) => /^<.+>$/.test(String(token || "").trim()));
1115
+ }
1116
+
1117
+ function getDefaultLanguage(localeValue) {
1118
+ const locale = String(localeValue || process.env.LANG || "").toLowerCase();
1119
+ if (locale.startsWith("zh")) {
1120
+ return "zh";
1121
+ }
1122
+ return "en";
1123
+ }
1124
+
1125
+ function tokenizeCommandLine(input) {
1126
+ const source = String(input || "").trim();
1127
+ const tokens = [];
1128
+ let current = "";
1129
+ let quote = "";
1130
+ let escaping = false;
1131
+
1132
+ for (let index = 0; index < source.length; index += 1) {
1133
+ const char = source[index];
1134
+ if (escaping) {
1135
+ current += char;
1136
+ escaping = false;
1137
+ continue;
1138
+ }
1139
+ if (char === "\\") {
1140
+ escaping = true;
1141
+ continue;
1142
+ }
1143
+ if (quote) {
1144
+ if (char === quote) {
1145
+ quote = "";
1146
+ } else {
1147
+ current += char;
1148
+ }
1149
+ continue;
1150
+ }
1151
+ if (char === '"' || char === "'") {
1152
+ quote = char;
1153
+ continue;
1154
+ }
1155
+ if (/\s/.test(char)) {
1156
+ if (current) {
1157
+ tokens.push(current);
1158
+ current = "";
1159
+ }
1160
+ continue;
1161
+ }
1162
+ current += char;
1163
+ }
1164
+
1165
+ if (escaping) {
1166
+ current += "\\";
1167
+ }
1168
+ if (quote) {
1169
+ throw new Error("Unclosed quote in command preview.");
1170
+ }
1171
+ if (current) {
1172
+ tokens.push(current);
1173
+ }
1174
+ return tokens;
1175
+ }
1176
+
1177
+ module.exports = {
1178
+ STAGES,
1179
+ COMMANDS,
1180
+ buildCommandArgs,
1181
+ buildDisplayCommand,
1182
+ getCommandParameterItems,
1183
+ getCommandById,
1184
+ getDefaultLanguage,
1185
+ getStageById,
1186
+ hasPlaceholders,
1187
+ normalizeLanguage,
1188
+ resolveLocalizedText,
1189
+ tokenizeCommandLine
1190
+ };