@xenonbyte/da-vinci-workflow 0.2.2 → 0.2.4
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 +28 -0
- package/README.md +49 -14
- package/README.zh-CN.md +169 -14
- package/commands/claude/dv/breakdown.md +8 -0
- package/commands/claude/dv/build.md +16 -0
- package/commands/claude/dv/continue.md +4 -0
- package/commands/claude/dv/design.md +5 -2
- package/commands/claude/dv/tasks.md +14 -0
- package/commands/claude/dv/verify.md +11 -0
- package/commands/codex/prompts/dv-breakdown.md +8 -0
- package/commands/codex/prompts/dv-build.md +16 -0
- package/commands/codex/prompts/dv-continue.md +4 -0
- package/commands/codex/prompts/dv-design.md +5 -2
- package/commands/codex/prompts/dv-tasks.md +14 -0
- package/commands/codex/prompts/dv-verify.md +10 -0
- package/commands/gemini/dv/breakdown.toml +8 -0
- package/commands/gemini/dv/build.toml +16 -0
- package/commands/gemini/dv/continue.toml +4 -0
- package/commands/gemini/dv/design.toml +5 -2
- package/commands/gemini/dv/tasks.toml +14 -0
- package/commands/gemini/dv/verify.toml +10 -0
- package/commands/templates/dv-continue.shared.md +4 -0
- package/docs/discipline-and-orchestration-upgrade.md +83 -0
- package/docs/dv-command-reference.md +61 -2
- package/docs/execution-chain-migration.md +23 -0
- package/docs/execution-chain-plan.md +10 -3
- package/docs/mode-use-cases.md +2 -1
- package/docs/pencil-rendering-workflow.md +15 -12
- package/docs/prompt-entrypoints.md +5 -0
- package/docs/prompt-presets/README.md +1 -1
- package/docs/prompt-presets/desktop-app.md +3 -3
- package/docs/prompt-presets/mobile-app.md +3 -3
- package/docs/prompt-presets/tablet-app.md +3 -3
- package/docs/prompt-presets/web-app.md +3 -3
- package/docs/skill-usage.md +61 -38
- package/docs/workflow-examples.md +16 -13
- package/docs/workflow-overview.md +19 -0
- package/docs/zh-CN/dv-command-reference.md +59 -2
- package/docs/zh-CN/execution-chain-migration.md +23 -0
- package/docs/zh-CN/mode-use-cases.md +2 -1
- package/docs/zh-CN/pencil-rendering-workflow.md +15 -12
- package/docs/zh-CN/prompt-entrypoints.md +5 -0
- package/docs/zh-CN/prompt-presets/README.md +1 -1
- package/docs/zh-CN/prompt-presets/desktop-app.md +3 -3
- package/docs/zh-CN/prompt-presets/mobile-app.md +3 -3
- package/docs/zh-CN/prompt-presets/tablet-app.md +3 -3
- package/docs/zh-CN/prompt-presets/web-app.md +3 -3
- package/docs/zh-CN/skill-usage.md +61 -38
- package/docs/zh-CN/workflow-examples.md +15 -13
- package/docs/zh-CN/workflow-overview.md +19 -0
- package/examples/greenfield-spec-markupflow/.da-vinci/state/execution-signals/demo__lint-tasks.json +16 -0
- package/lib/audit-parsers.js +166 -10
- package/lib/audit.js +3 -26
- package/lib/cli.js +156 -2
- package/lib/design-source-registry.js +146 -0
- package/lib/execution-profile.js +143 -0
- package/lib/execution-signals.js +19 -1
- package/lib/lint-tasks.js +86 -2
- package/lib/planning-parsers.js +255 -18
- package/lib/save-current-design.js +790 -0
- package/lib/supervisor-review.js +3 -2
- package/lib/task-execution.js +160 -0
- package/lib/task-review.js +197 -0
- package/lib/verify.js +152 -1
- package/lib/workflow-bootstrap.js +2 -13
- package/lib/workflow-persisted-state.js +3 -1
- package/lib/workflow-state.js +503 -33
- package/lib/worktree-preflight.js +214 -0
- package/package.json +1 -1
- package/references/artifact-templates.md +56 -6
- package/tui/catalog.js +103 -0
- package/tui/index.js +2274 -418
package/lib/audit-parsers.js
CHANGED
|
@@ -244,18 +244,27 @@ function normalizeCheckpointLabel(value) {
|
|
|
244
244
|
|
|
245
245
|
function parseCheckpointStatusMap(markdownText) {
|
|
246
246
|
const section = getMarkdownSection(markdownText, "Checkpoint Status");
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
const statuses = {};
|
|
248
|
+
|
|
249
|
+
if (section) {
|
|
250
|
+
const matches = section.matchAll(/(?:^|\n)\s*-\s*`?([^`:\n]+?)`?\s*:\s*(PASS|WARN|BLOCK)\b/gi);
|
|
251
|
+
for (const match of matches) {
|
|
252
|
+
const label = normalizeCheckpointLabel(match[1]);
|
|
253
|
+
if (!label) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
statuses[label] = String(match[2]).toUpperCase();
|
|
257
|
+
}
|
|
249
258
|
}
|
|
250
259
|
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
260
|
+
const runtimeGateSection = getMarkdownSection(markdownText, "MCP Runtime Gate");
|
|
261
|
+
if (runtimeGateSection) {
|
|
262
|
+
const runtimeGateMatch = String(runtimeGateSection).match(
|
|
263
|
+
/(?:^|\n)\s*-\s*(?:Final runtime gate status|Status|状态)\s*:\s*`?(PASS|WARN|BLOCK)`?\b/i
|
|
264
|
+
);
|
|
265
|
+
if (runtimeGateMatch) {
|
|
266
|
+
statuses[normalizeCheckpointLabel("mcp runtime gate")] = String(runtimeGateMatch[1]).toUpperCase();
|
|
257
267
|
}
|
|
258
|
-
statuses[label] = String(match[2]).toUpperCase();
|
|
259
268
|
}
|
|
260
269
|
|
|
261
270
|
return statuses;
|
|
@@ -751,6 +760,149 @@ function inspectDesignSupervisorReview(pencilDesignText) {
|
|
|
751
760
|
};
|
|
752
761
|
}
|
|
753
762
|
|
|
763
|
+
const DISCIPLINE_MARKER_NAMES = Object.freeze({
|
|
764
|
+
designApproval: "design_approval",
|
|
765
|
+
planSelfReview: "plan_self_review",
|
|
766
|
+
operatorReviewAck: "operator_review_ack"
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
const DISCIPLINE_MARKER_ALIASES = Object.freeze({
|
|
770
|
+
[DISCIPLINE_MARKER_NAMES.designApproval]: [
|
|
771
|
+
"design approval",
|
|
772
|
+
"design-approved",
|
|
773
|
+
"design approved",
|
|
774
|
+
"design_approval",
|
|
775
|
+
"design-approval"
|
|
776
|
+
],
|
|
777
|
+
[DISCIPLINE_MARKER_NAMES.planSelfReview]: [
|
|
778
|
+
"plan self review",
|
|
779
|
+
"plan_self_review",
|
|
780
|
+
"plan-self-review",
|
|
781
|
+
"plan review",
|
|
782
|
+
"plan-review"
|
|
783
|
+
],
|
|
784
|
+
[DISCIPLINE_MARKER_NAMES.operatorReviewAck]: [
|
|
785
|
+
"operator review ack",
|
|
786
|
+
"operator review acknowledgement",
|
|
787
|
+
"operator review acknowledgment",
|
|
788
|
+
"operator_ack",
|
|
789
|
+
"operator-review-ack",
|
|
790
|
+
"operator-review-acknowledgement",
|
|
791
|
+
"operator-review-acknowledgment"
|
|
792
|
+
]
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
const DISCIPLINE_MARKER_KEYWORDS = Object.freeze(
|
|
796
|
+
Object.values(DISCIPLINE_MARKER_ALIASES)
|
|
797
|
+
.flat()
|
|
798
|
+
.map((token) => String(token).replace(/[_-]+/g, " ").toLowerCase())
|
|
799
|
+
);
|
|
800
|
+
|
|
801
|
+
function normalizeDisciplineMarkerName(value) {
|
|
802
|
+
const normalized = String(value || "")
|
|
803
|
+
.toLowerCase()
|
|
804
|
+
.replace(/[`*_~]/g, "")
|
|
805
|
+
.replace(/[_-]+/g, " ")
|
|
806
|
+
.replace(/\s+/g, " ")
|
|
807
|
+
.trim();
|
|
808
|
+
if (!normalized) {
|
|
809
|
+
return "";
|
|
810
|
+
}
|
|
811
|
+
for (const [canonicalName, aliases] of Object.entries(DISCIPLINE_MARKER_ALIASES)) {
|
|
812
|
+
if (aliases.some((alias) => normalized === String(alias).replace(/[_-]+/g, " ").toLowerCase())) {
|
|
813
|
+
return canonicalName;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return "";
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function normalizeDisciplineMarkerStatus(value) {
|
|
820
|
+
return String(value || "")
|
|
821
|
+
.toUpperCase()
|
|
822
|
+
.replace(/[`*_~]/g, "")
|
|
823
|
+
.replace(/[()]/g, "")
|
|
824
|
+
.replace(/\s+/g, "_")
|
|
825
|
+
.replace(/[^A-Z0-9_]+/g, "_")
|
|
826
|
+
.replace(/^_+|_+$/g, "");
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function parseDisciplineMarkers(markdownText) {
|
|
830
|
+
const markers = {};
|
|
831
|
+
const ordered = [];
|
|
832
|
+
const malformed = [];
|
|
833
|
+
const lines = String(markdownText || "").replace(/\r\n?/g, "\n").split("\n");
|
|
834
|
+
const markerPattern =
|
|
835
|
+
/^\s*-\s*`?([A-Za-z][A-Za-z0-9 _-]{1,80})`?\s*:\s*`?([A-Za-z0-9 _-]{1,80})`?(?:\s*@\s*`?([^`]+?)`?)?\s*$/;
|
|
836
|
+
|
|
837
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
838
|
+
const rawLine = lines[index];
|
|
839
|
+
const match = rawLine.match(markerPattern);
|
|
840
|
+
if (!match) {
|
|
841
|
+
const normalizedLine = String(rawLine || "")
|
|
842
|
+
.toLowerCase()
|
|
843
|
+
.replace(/[_-]+/g, " ")
|
|
844
|
+
.replace(/\s+/g, " ")
|
|
845
|
+
.trim();
|
|
846
|
+
if (!normalizedLine.startsWith("-")) {
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
if (DISCIPLINE_MARKER_KEYWORDS.some((keyword) => normalizedLine.includes(keyword))) {
|
|
850
|
+
malformed.push({
|
|
851
|
+
line: index + 1,
|
|
852
|
+
reason: "Malformed discipline marker syntax.",
|
|
853
|
+
raw: rawLine
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
const markerName = normalizeDisciplineMarkerName(match[1]);
|
|
860
|
+
if (!markerName) {
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const status = normalizeDisciplineMarkerStatus(match[2]);
|
|
865
|
+
if (!status) {
|
|
866
|
+
malformed.push({
|
|
867
|
+
line: index + 1,
|
|
868
|
+
reason: "Missing marker status token.",
|
|
869
|
+
raw: rawLine
|
|
870
|
+
});
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
const rawTime = String(match[3] || "").trim();
|
|
875
|
+
let time = "";
|
|
876
|
+
if (rawTime) {
|
|
877
|
+
time = normalizeTimeToken(rawTime);
|
|
878
|
+
if (!time) {
|
|
879
|
+
malformed.push({
|
|
880
|
+
line: index + 1,
|
|
881
|
+
reason: "Invalid marker timestamp.",
|
|
882
|
+
raw: rawLine
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
const record = {
|
|
888
|
+
name: markerName,
|
|
889
|
+
status,
|
|
890
|
+
time,
|
|
891
|
+
rawTime,
|
|
892
|
+
line: index + 1,
|
|
893
|
+
raw: rawLine
|
|
894
|
+
};
|
|
895
|
+
ordered.push(record);
|
|
896
|
+
markers[markerName] = record;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
return {
|
|
900
|
+
markers,
|
|
901
|
+
ordered,
|
|
902
|
+
malformed
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
|
|
754
906
|
module.exports = {
|
|
755
907
|
hasMarkdownHeading,
|
|
756
908
|
getMarkdownSection,
|
|
@@ -766,5 +918,9 @@ module.exports = {
|
|
|
766
918
|
getConfiguredDesignSupervisorReviewers,
|
|
767
919
|
hasConfiguredDesignSupervisorReview,
|
|
768
920
|
isDesignSupervisorReviewRequired,
|
|
769
|
-
inspectDesignSupervisorReview
|
|
921
|
+
inspectDesignSupervisorReview,
|
|
922
|
+
DISCIPLINE_MARKER_NAMES,
|
|
923
|
+
normalizeDisciplineMarkerName,
|
|
924
|
+
normalizeDisciplineMarkerStatus,
|
|
925
|
+
parseDisciplineMarkers
|
|
770
926
|
};
|
package/lib/audit.js
CHANGED
|
@@ -8,7 +8,8 @@ const {
|
|
|
8
8
|
assertPenDocumentShape
|
|
9
9
|
} = require("./pen-persistence");
|
|
10
10
|
const { getSessionStatePath, readSessionState } = require("./pencil-session");
|
|
11
|
-
const {
|
|
11
|
+
const { listFilesRecursiveSafe } = require("./fs-safety");
|
|
12
|
+
const { collectRegisteredPenPaths } = require("./design-source-registry");
|
|
12
13
|
const { pathExists, readTextIfExists } = require("./utils");
|
|
13
14
|
const { runModuleExportInWorker } = require("./async-offload");
|
|
14
15
|
const { readExecutionSignals, summarizeSignalsBySurface } = require("./execution-signals");
|
|
@@ -69,30 +70,6 @@ function relativeTo(projectRoot, targetPath) {
|
|
|
69
70
|
return path.relative(projectRoot, targetPath) || ".";
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
function collectRegisteredPenPaths(projectRoot, designRegistryPath) {
|
|
73
|
-
const registryText = readTextIfExists(designRegistryPath);
|
|
74
|
-
const matches = registryText.match(/\.da-vinci\/designs\/[^\s`]+\.pen/g) || [];
|
|
75
|
-
const validPaths = [];
|
|
76
|
-
const escapedPaths = [];
|
|
77
|
-
|
|
78
|
-
for (const relativePath of [...new Set(matches)]) {
|
|
79
|
-
const resolvedPath = path.resolve(projectRoot, relativePath);
|
|
80
|
-
if (!isPathInside(projectRoot, resolvedPath)) {
|
|
81
|
-
escapedPaths.push({
|
|
82
|
-
relativePath,
|
|
83
|
-
resolvedPath
|
|
84
|
-
});
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
validPaths.push(resolvedPath);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
validPaths,
|
|
92
|
-
escapedPaths
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
73
|
function getNonEmptyChangeDirs(changesDir) {
|
|
97
74
|
return listChildDirs(changesDir).filter((changeDir) => {
|
|
98
75
|
const scan = listFilesRecursive(changeDir, CHANGE_SCAN_LIMITS);
|
|
@@ -368,7 +345,7 @@ function auditProject(projectPathInput, options = {}) {
|
|
|
368
345
|
const {
|
|
369
346
|
validPaths: registeredPenPaths,
|
|
370
347
|
escapedPaths: escapedRegisteredPenPaths
|
|
371
|
-
} = collectRegisteredPenPaths(projectRoot, designRegistryPath);
|
|
348
|
+
} = collectRegisteredPenPaths(projectRoot, readTextIfExists(designRegistryPath));
|
|
372
349
|
for (const escapedPath of escapedRegisteredPenPaths) {
|
|
373
350
|
const message =
|
|
374
351
|
`Registered design source escapes project root and will be ignored: ${escapedPath.relativePath} ` +
|
package/lib/cli.js
CHANGED
|
@@ -32,6 +32,11 @@ const {
|
|
|
32
32
|
endPencilSession,
|
|
33
33
|
getPencilSessionStatus
|
|
34
34
|
} = require("./pencil-session");
|
|
35
|
+
const {
|
|
36
|
+
SAVE_STATUS,
|
|
37
|
+
saveCurrentDesign,
|
|
38
|
+
formatSaveCurrentDesignReport
|
|
39
|
+
} = require("./save-current-design");
|
|
35
40
|
const {
|
|
36
41
|
searchIconLibrary,
|
|
37
42
|
formatIconSearchReport
|
|
@@ -74,6 +79,18 @@ const {
|
|
|
74
79
|
const { diffSpec, formatDiffSpecReport } = require("./diff-spec");
|
|
75
80
|
const { scaffoldFromBindings, formatScaffoldReport } = require("./scaffold");
|
|
76
81
|
const { writeExecutionSignal } = require("./execution-signals");
|
|
82
|
+
const {
|
|
83
|
+
writeTaskExecutionEnvelope,
|
|
84
|
+
formatTaskExecutionReport
|
|
85
|
+
} = require("./task-execution");
|
|
86
|
+
const {
|
|
87
|
+
writeTaskReviewEnvelope,
|
|
88
|
+
formatTaskReviewReport
|
|
89
|
+
} = require("./task-review");
|
|
90
|
+
const {
|
|
91
|
+
runWorktreePreflight,
|
|
92
|
+
formatWorktreePreflightReport
|
|
93
|
+
} = require("./worktree-preflight");
|
|
77
94
|
const { formatTuiHelp, launchTui } = require("../tui");
|
|
78
95
|
|
|
79
96
|
const DEFAULT_MAX_PREFLIGHT_STDIN_BYTES = 1024 * 1024;
|
|
@@ -83,6 +100,7 @@ const OPTION_FLAGS_WITH_VALUES = new Set([
|
|
|
83
100
|
"--home",
|
|
84
101
|
"--platform",
|
|
85
102
|
"--lang",
|
|
103
|
+
"--tui-width",
|
|
86
104
|
"--project",
|
|
87
105
|
"--mode",
|
|
88
106
|
"--change",
|
|
@@ -95,6 +113,15 @@ const OPTION_FLAGS_WITH_VALUES = new Set([
|
|
|
95
113
|
"--aliases",
|
|
96
114
|
"--pencil-design",
|
|
97
115
|
"--status",
|
|
116
|
+
"--stage",
|
|
117
|
+
"--summary",
|
|
118
|
+
"--task-group",
|
|
119
|
+
"--changed-files",
|
|
120
|
+
"--test-evidence",
|
|
121
|
+
"--concerns",
|
|
122
|
+
"--blockers",
|
|
123
|
+
"--issues",
|
|
124
|
+
"--reviewer",
|
|
98
125
|
"--source",
|
|
99
126
|
"--executed-reviewers",
|
|
100
127
|
"--codex-bin",
|
|
@@ -113,6 +140,7 @@ const OPTION_FLAGS_WITH_VALUES = new Set([
|
|
|
113
140
|
"--from",
|
|
114
141
|
"--to",
|
|
115
142
|
"--pen",
|
|
143
|
+
"--pencil-bin",
|
|
116
144
|
"--nodes-file",
|
|
117
145
|
"--variables-file",
|
|
118
146
|
"--version",
|
|
@@ -123,6 +151,7 @@ const OPTION_FLAGS_WITH_VALUES = new Set([
|
|
|
123
151
|
const HELP_OPTION_SPECS = [
|
|
124
152
|
{ flag: "--platform <value>", description: "codex, claude, gemini, or all" },
|
|
125
153
|
{ flag: "--lang <value>", description: "ui language for `da-vinci tui`: en or zh" },
|
|
154
|
+
{ flag: "--tui-width <cols>", description: "fixed layout width (columns) for `da-vinci tui`" },
|
|
126
155
|
{ flag: "--home <path>", description: "override HOME for installation targets" },
|
|
127
156
|
{ flag: "--project <path>", description: "override project path for audit" },
|
|
128
157
|
{
|
|
@@ -155,6 +184,16 @@ const HELP_OPTION_SPECS = [
|
|
|
155
184
|
description: "icon-search family filter: all, material, rounded, outlined, sharp, lucide, feather, phosphor"
|
|
156
185
|
},
|
|
157
186
|
{ flag: "--status <value>", description: "PASS, WARN, or BLOCK for supervisor-review" },
|
|
187
|
+
{ flag: "--stage <value>", description: "task-review stage: spec or quality" },
|
|
188
|
+
{ flag: "--summary <text>", description: "task-execution/task-review summary text" },
|
|
189
|
+
{ flag: "--task-group <id>", description: "task group identifier for task-execution/task-review" },
|
|
190
|
+
{ flag: "--changed-files <csv>", description: "comma-separated changed files for task-execution" },
|
|
191
|
+
{ flag: "--test-evidence <csv>", description: "comma-separated test evidence commands for task-execution" },
|
|
192
|
+
{ flag: "--concerns <csv>", description: "comma-separated concern text for task-execution" },
|
|
193
|
+
{ flag: "--blockers <csv>", description: "comma-separated blocker text for task-execution" },
|
|
194
|
+
{ flag: "--issues <csv>", description: "comma-separated issue text for task-review" },
|
|
195
|
+
{ flag: "--reviewer <name>", description: "reviewer identifier for task-review" },
|
|
196
|
+
{ flag: "--write-verification", description: "append task-review evidence into verification.md" },
|
|
158
197
|
{ flag: "--source <value>", description: "review source: skill, manual, inferred" },
|
|
159
198
|
{ flag: "--executed-reviewers <csv>", description: "reviewer skills that executed this review" },
|
|
160
199
|
{ flag: "--run-reviewers", description: "execute configured reviewer skills through codex exec" },
|
|
@@ -188,12 +227,15 @@ const HELP_OPTION_SPECS = [
|
|
|
188
227
|
flag: "--strict",
|
|
189
228
|
description: "enable strict failure mode for commands that support advisory defaults (for example icon-sync, lint-spec)"
|
|
190
229
|
},
|
|
230
|
+
{ flag: "--alt-screen", description: "enable alternate terminal screen buffer for `da-vinci tui`" },
|
|
231
|
+
{ flag: "--no-alt-screen", description: "disable alternate terminal screen buffer for `da-vinci tui`" },
|
|
191
232
|
{
|
|
192
233
|
flag: "--continue-on-error",
|
|
193
234
|
description: "print BLOCK/FAIL command results without throwing process errors"
|
|
194
235
|
},
|
|
195
236
|
{ flag: "--json", description: "print structured JSON output when supported by the command" },
|
|
196
237
|
{ flag: "--pen <path>", description: "registered .pen path for sync checks" },
|
|
238
|
+
{ flag: "--pencil-bin <path>", description: "override Pencil executable for interactive capture/verify flows" },
|
|
197
239
|
{
|
|
198
240
|
flag: "--from <path>",
|
|
199
241
|
description: "source path for sync-pen-source, or baseline sidecars directory for diff-spec"
|
|
@@ -475,7 +517,7 @@ function printHelp() {
|
|
|
475
517
|
" da-vinci install --platform codex,claude,gemini",
|
|
476
518
|
" da-vinci uninstall --platform codex,claude,gemini",
|
|
477
519
|
" da-vinci status",
|
|
478
|
-
" da-vinci tui [--project <path>] [--change <id>] [--lang en|zh] [--strict] [--json] [--continue-on-error]",
|
|
520
|
+
" da-vinci tui [--project <path>] [--change <id>] [--lang en|zh] [--strict] [--json] [--continue-on-error] [--tui-width <cols>] [--alt-screen|--no-alt-screen]",
|
|
479
521
|
" da-vinci workflow-status [--project <path>] [--change <id>] [--json]",
|
|
480
522
|
" da-vinci next-step [--project <path>] [--change <id>] [--json]",
|
|
481
523
|
" da-vinci lint-spec [--project <path>] [--change <id>] [--strict] [--json]",
|
|
@@ -487,6 +529,9 @@ function printHelp() {
|
|
|
487
529
|
" da-vinci verify-implementation [--project <path>] [--change <id>] [--strict] [--json]",
|
|
488
530
|
" da-vinci verify-structure [--project <path>] [--change <id>] [--strict] [--json]",
|
|
489
531
|
" da-vinci verify-coverage [--project <path>] [--change <id>] [--strict] [--json]",
|
|
532
|
+
" da-vinci task-execution --project <path> --change <id> --task-group <id> --status <DONE|DONE_WITH_CONCERNS|NEEDS_CONTEXT|BLOCKED> --summary <text> [--changed-files <csv>] [--test-evidence <csv>] [--concerns <csv>] [--blockers <csv>] [--json]",
|
|
533
|
+
" da-vinci task-review --project <path> --change <id> --task-group <id> --stage <spec|quality> --status <PASS|WARN|BLOCK> --summary <text> [--issues <csv>] [--reviewer <name>] [--write-verification] [--json]",
|
|
534
|
+
" da-vinci worktree-preflight --project <path> [--change <id>] [--json]",
|
|
490
535
|
" da-vinci diff-spec [--project <path>] [--change <id>] [--from <sidecars-dir>] [--json]",
|
|
491
536
|
" da-vinci scaffold [--project <path>] [--change <id>] [--output <path>] [--json]",
|
|
492
537
|
" da-vinci validate-assets",
|
|
@@ -502,6 +547,7 @@ function printHelp() {
|
|
|
502
547
|
" da-vinci check-pen-baseline --pen <path> --baseline <path>[,<path>...] [--baseline <path>] [--prefer-source <path>]",
|
|
503
548
|
" da-vinci sync-pen-source --from <path> --to <path>",
|
|
504
549
|
" da-vinci snapshot-pen --input <path> --output <path>",
|
|
550
|
+
" da-vinci save-current-design --project <path> [--json] [--continue-on-error]",
|
|
505
551
|
" da-vinci pencil-lock acquire --project <path>",
|
|
506
552
|
" da-vinci pencil-lock release --project <path>",
|
|
507
553
|
" da-vinci pencil-lock status",
|
|
@@ -945,6 +991,12 @@ async function runCli(argv) {
|
|
|
945
991
|
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
946
992
|
const changeId = getOption(argv, "--change");
|
|
947
993
|
const lang = getOption(argv, "--lang");
|
|
994
|
+
const tuiWidth = getOption(argv, "--tui-width");
|
|
995
|
+
const altScreen = argv.includes("--alt-screen")
|
|
996
|
+
? true
|
|
997
|
+
: argv.includes("--no-alt-screen")
|
|
998
|
+
? false
|
|
999
|
+
: undefined;
|
|
948
1000
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
949
1001
|
console.log(formatTuiHelp(lang));
|
|
950
1002
|
return;
|
|
@@ -953,6 +1005,8 @@ async function runCli(argv) {
|
|
|
953
1005
|
projectPath,
|
|
954
1006
|
changeId,
|
|
955
1007
|
lang,
|
|
1008
|
+
tuiWidth,
|
|
1009
|
+
altScreen,
|
|
956
1010
|
strict: argv.includes("--strict"),
|
|
957
1011
|
jsonOutput: argv.includes("--json"),
|
|
958
1012
|
continueOnError
|
|
@@ -980,7 +1034,21 @@ async function runCli(argv) {
|
|
|
980
1034
|
const result = deriveWorkflowStatus(projectPath, { changeId });
|
|
981
1035
|
|
|
982
1036
|
if (argv.includes("--json")) {
|
|
983
|
-
console.log(
|
|
1037
|
+
console.log(
|
|
1038
|
+
JSON.stringify(
|
|
1039
|
+
{
|
|
1040
|
+
stage: result.stage,
|
|
1041
|
+
checkpointState: result.checkpointState,
|
|
1042
|
+
nextStep: result.nextStep || null,
|
|
1043
|
+
discipline: result.discipline || null,
|
|
1044
|
+
executionProfile: result.executionProfile || null,
|
|
1045
|
+
worktreePreflight: result.worktreePreflight || null,
|
|
1046
|
+
verificationFreshness: result.verificationFreshness || null
|
|
1047
|
+
},
|
|
1048
|
+
null,
|
|
1049
|
+
2
|
|
1050
|
+
)
|
|
1051
|
+
);
|
|
984
1052
|
return;
|
|
985
1053
|
}
|
|
986
1054
|
|
|
@@ -1140,6 +1208,63 @@ async function runCli(argv) {
|
|
|
1140
1208
|
return;
|
|
1141
1209
|
}
|
|
1142
1210
|
|
|
1211
|
+
if (command === "task-execution") {
|
|
1212
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1213
|
+
const changeId = getOption(argv, "--change");
|
|
1214
|
+
const result = writeTaskExecutionEnvelope(projectPath, {
|
|
1215
|
+
changeId,
|
|
1216
|
+
taskGroupId: getOption(argv, "--task-group"),
|
|
1217
|
+
status: getOption(argv, "--status"),
|
|
1218
|
+
summary: getOption(argv, "--summary"),
|
|
1219
|
+
changedFiles: getCommaSeparatedOptionValues(argv, "--changed-files"),
|
|
1220
|
+
testEvidence: getCommaSeparatedOptionValues(argv, "--test-evidence"),
|
|
1221
|
+
concerns: getCommaSeparatedOptionValues(argv, "--concerns"),
|
|
1222
|
+
blockers: getCommaSeparatedOptionValues(argv, "--blockers")
|
|
1223
|
+
});
|
|
1224
|
+
const useJson = argv.includes("--json");
|
|
1225
|
+
const output = useJson ? JSON.stringify(result, null, 2) : formatTaskExecutionReport(result);
|
|
1226
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
console.log(output);
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
if (command === "task-review") {
|
|
1234
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1235
|
+
const changeId = getOption(argv, "--change");
|
|
1236
|
+
const result = writeTaskReviewEnvelope(projectPath, {
|
|
1237
|
+
changeId,
|
|
1238
|
+
taskGroupId: getOption(argv, "--task-group"),
|
|
1239
|
+
stage: getOption(argv, "--stage"),
|
|
1240
|
+
status: getOption(argv, "--status"),
|
|
1241
|
+
summary: getOption(argv, "--summary"),
|
|
1242
|
+
issues: getCommaSeparatedOptionValues(argv, "--issues"),
|
|
1243
|
+
reviewer: getOption(argv, "--reviewer"),
|
|
1244
|
+
writeVerification: argv.includes("--write-verification")
|
|
1245
|
+
});
|
|
1246
|
+
const useJson = argv.includes("--json");
|
|
1247
|
+
const output = useJson ? JSON.stringify(result, null, 2) : formatTaskReviewReport(result);
|
|
1248
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
console.log(output);
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
if (command === "worktree-preflight") {
|
|
1256
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1257
|
+
const changeId = getOption(argv, "--change");
|
|
1258
|
+
const result = runWorktreePreflight(projectPath);
|
|
1259
|
+
persistExecutionSignal(projectPath, changeId || "global", "worktree-preflight", result, false);
|
|
1260
|
+
if (argv.includes("--json")) {
|
|
1261
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
console.log(formatWorktreePreflightReport(result));
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1143
1268
|
if (command === "diff-spec") {
|
|
1144
1269
|
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1145
1270
|
const changeId = getOption(argv, "--change");
|
|
@@ -1425,6 +1550,35 @@ async function runCli(argv) {
|
|
|
1425
1550
|
return;
|
|
1426
1551
|
}
|
|
1427
1552
|
|
|
1553
|
+
if (command === "save-current-design") {
|
|
1554
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1555
|
+
const pencilBin = getOption(argv, "--pencil-bin");
|
|
1556
|
+
const result = await saveCurrentDesign({
|
|
1557
|
+
projectPath,
|
|
1558
|
+
homeDir,
|
|
1559
|
+
pencilBin,
|
|
1560
|
+
allowLocalBridge: true
|
|
1561
|
+
});
|
|
1562
|
+
const useJson = argv.includes("--json");
|
|
1563
|
+
const output = useJson
|
|
1564
|
+
? JSON.stringify(result, null, 2)
|
|
1565
|
+
: formatSaveCurrentDesignReport(result);
|
|
1566
|
+
|
|
1567
|
+
if (
|
|
1568
|
+
emitOrThrowOnStatus(
|
|
1569
|
+
result.status,
|
|
1570
|
+
[SAVE_STATUS.BLOCKED, SAVE_STATUS.UNAVAILABLE],
|
|
1571
|
+
output,
|
|
1572
|
+
continueOnError
|
|
1573
|
+
)
|
|
1574
|
+
) {
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
console.log(output);
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1428
1582
|
if (command === "pencil-lock") {
|
|
1429
1583
|
handlePencilLockCommand(argv, homeDir);
|
|
1430
1584
|
return;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const { isPathInside } = require("./fs-safety");
|
|
3
|
+
|
|
4
|
+
const REGISTERED_PEN_PATTERN = /\.da-vinci\/designs\/[^\s`]+\.pen/g;
|
|
5
|
+
|
|
6
|
+
function normalizeProjectRoot(projectRoot) {
|
|
7
|
+
return path.resolve(projectRoot || process.cwd());
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function collectRegisteredPenReferences(registryText) {
|
|
11
|
+
const matches = String(registryText || "").match(REGISTERED_PEN_PATTERN) || [];
|
|
12
|
+
const seen = new Set();
|
|
13
|
+
const unique = [];
|
|
14
|
+
|
|
15
|
+
for (const match of matches) {
|
|
16
|
+
const normalized = String(match || "").trim();
|
|
17
|
+
if (!normalized || seen.has(normalized)) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
seen.add(normalized);
|
|
21
|
+
unique.push(normalized);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return unique;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function normalizeBoundPenPath(projectRoot, penPath, options = {}) {
|
|
28
|
+
const root = normalizeProjectRoot(projectRoot);
|
|
29
|
+
const raw = typeof penPath === "string" ? penPath.trim() : "";
|
|
30
|
+
|
|
31
|
+
if (!raw) {
|
|
32
|
+
return {
|
|
33
|
+
ok: false,
|
|
34
|
+
code: options.missingCode || "missing_path",
|
|
35
|
+
input: raw,
|
|
36
|
+
resolvedPath: ""
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (options.rejectNew && raw.toLowerCase() === "new") {
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
code: options.newCode || "active_editor_new",
|
|
44
|
+
input: raw,
|
|
45
|
+
resolvedPath: ""
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const resolvedPath = path.normalize(path.isAbsolute(raw) ? raw : path.resolve(root, raw));
|
|
50
|
+
if (!isPathInside(root, resolvedPath)) {
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
code: options.outsideCode || "outside_project_root",
|
|
54
|
+
input: raw,
|
|
55
|
+
resolvedPath
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (options.requirePenExtension !== false && !/\.pen$/i.test(resolvedPath)) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
code: options.extensionCode || "not_pen_path",
|
|
63
|
+
input: raw,
|
|
64
|
+
resolvedPath
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
ok: true,
|
|
70
|
+
code: "ok",
|
|
71
|
+
input: raw,
|
|
72
|
+
resolvedPath
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function normalizeRegisteredPenPath(projectRoot, penPath) {
|
|
77
|
+
return normalizeBoundPenPath(projectRoot, penPath, {
|
|
78
|
+
missingCode: "registered_pen_missing",
|
|
79
|
+
outsideCode: "registered_pen_outside_project_root",
|
|
80
|
+
extensionCode: "registered_pen_not_pen_path"
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function normalizeSessionPenPath(projectRoot, penPath) {
|
|
85
|
+
return normalizeBoundPenPath(projectRoot, penPath, {
|
|
86
|
+
missingCode: "session_pen_missing",
|
|
87
|
+
outsideCode: "session_pen_outside_project_root",
|
|
88
|
+
extensionCode: "session_pen_not_pen_path"
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function normalizeActiveEditorPath(projectRoot, activeEditorPath) {
|
|
93
|
+
return normalizeBoundPenPath(projectRoot, activeEditorPath, {
|
|
94
|
+
rejectNew: true,
|
|
95
|
+
missingCode: "active_editor_missing",
|
|
96
|
+
newCode: "active_editor_new",
|
|
97
|
+
outsideCode: "active_editor_outside_project_root",
|
|
98
|
+
extensionCode: "active_editor_not_pen_path"
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function collectRegisteredPenPaths(projectRoot, registryText) {
|
|
103
|
+
const root = normalizeProjectRoot(projectRoot);
|
|
104
|
+
const references = collectRegisteredPenReferences(registryText);
|
|
105
|
+
const validPaths = [];
|
|
106
|
+
const escapedPaths = [];
|
|
107
|
+
const seenResolved = new Set();
|
|
108
|
+
|
|
109
|
+
for (const relativePath of references) {
|
|
110
|
+
const normalized = normalizeRegisteredPenPath(root, relativePath);
|
|
111
|
+
if (normalized.ok) {
|
|
112
|
+
if (!seenResolved.has(normalized.resolvedPath)) {
|
|
113
|
+
seenResolved.add(normalized.resolvedPath);
|
|
114
|
+
validPaths.push(normalized.resolvedPath);
|
|
115
|
+
}
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (normalized.code === "registered_pen_outside_project_root") {
|
|
120
|
+
escapedPaths.push({
|
|
121
|
+
relativePath,
|
|
122
|
+
resolvedPath: normalized.resolvedPath
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
validPaths,
|
|
129
|
+
escapedPaths
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function resolvePreferredRegisteredPenPath(projectRoot, registryText) {
|
|
134
|
+
const resolved = collectRegisteredPenPaths(projectRoot, registryText);
|
|
135
|
+
return resolved.validPaths[0] || "";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
module.exports = {
|
|
139
|
+
collectRegisteredPenReferences,
|
|
140
|
+
normalizeBoundPenPath,
|
|
141
|
+
normalizeRegisteredPenPath,
|
|
142
|
+
normalizeSessionPenPath,
|
|
143
|
+
normalizeActiveEditorPath,
|
|
144
|
+
collectRegisteredPenPaths,
|
|
145
|
+
resolvePreferredRegisteredPenPath
|
|
146
|
+
};
|