@uoyo/mvtt 2.2.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -7397,12 +7397,15 @@ function parseArgs(argv) {
|
|
|
7397
7397
|
}
|
|
7398
7398
|
return args2;
|
|
7399
7399
|
}
|
|
7400
|
+
function hasValue(value) {
|
|
7401
|
+
return value !== void 0 && value !== true && String(value).trim() !== "";
|
|
7402
|
+
}
|
|
7400
7403
|
function validateArgs(args2) {
|
|
7401
7404
|
if (args2.validate) return null;
|
|
7402
|
-
if (!args2.plan
|
|
7403
|
-
if (!args2.task
|
|
7404
|
-
const hasStatus = args2.status
|
|
7405
|
-
const hasMutation = hasStatus || args2.artifacts
|
|
7405
|
+
if (!hasValue(args2.plan)) return ERRORS.MISSING_PLAN();
|
|
7406
|
+
if (!hasValue(args2.task)) return ERRORS.MISSING_TASK();
|
|
7407
|
+
const hasStatus = hasValue(args2.status);
|
|
7408
|
+
const hasMutation = hasStatus || hasValue(args2.artifacts) || hasValue(args2.notes) || hasValue(args2["deliverables-pointer"]) || hasValue(args2["mark-deliverable-stale"]);
|
|
7406
7409
|
if (!hasMutation) return ERRORS.MISSING_STATUS();
|
|
7407
7410
|
if (args2.status === true) return ERRORS.MISSING_STATUS();
|
|
7408
7411
|
if (hasStatus && !VALID_STATUSES.includes(args2.status)) return ERRORS.INVALID_STATUS(args2.status);
|
|
@@ -7411,10 +7414,10 @@ function validateArgs(args2) {
|
|
|
7411
7414
|
function applyUpdate(plan, args2, now) {
|
|
7412
7415
|
const task = plan.tasks.find((t) => t.id === args2.task);
|
|
7413
7416
|
const oldStatus = task.status;
|
|
7414
|
-
if (args2.status
|
|
7417
|
+
if (hasValue(args2.status)) {
|
|
7415
7418
|
task.status = args2.status;
|
|
7416
7419
|
}
|
|
7417
|
-
if (args2.artifacts
|
|
7420
|
+
if (hasValue(args2.artifacts)) {
|
|
7418
7421
|
const incoming = args2.artifacts.split(",").map((s) => s.trim()).filter(Boolean);
|
|
7419
7422
|
if (incoming.length) {
|
|
7420
7423
|
if (!task.artifacts || typeof task.artifacts !== "object") {
|
|
@@ -7432,23 +7435,23 @@ function applyUpdate(plan, args2, now) {
|
|
|
7432
7435
|
}
|
|
7433
7436
|
}
|
|
7434
7437
|
}
|
|
7435
|
-
if (args2.notes
|
|
7438
|
+
if (hasValue(args2.notes)) {
|
|
7436
7439
|
task.notes = args2.notes;
|
|
7437
7440
|
}
|
|
7438
|
-
if (args2.status
|
|
7441
|
+
if (hasValue(args2.status)) {
|
|
7439
7442
|
if (args2.status === "done" && !task.completed_at) {
|
|
7440
7443
|
task.completed_at = now;
|
|
7441
7444
|
} else if (args2.status !== "done") {
|
|
7442
7445
|
task.completed_at = null;
|
|
7443
7446
|
}
|
|
7444
7447
|
}
|
|
7445
|
-
if (args2["deliverables-pointer"]
|
|
7448
|
+
if (hasValue(args2["deliverables-pointer"])) {
|
|
7446
7449
|
if (args2["deliverables-pointer"] !== "current") {
|
|
7447
7450
|
return { error: ERRORS.INVALID_DELIVERABLES_POINTER(args2["deliverables-pointer"]) };
|
|
7448
7451
|
}
|
|
7449
7452
|
task.deliverables = { freshness: "current" };
|
|
7450
7453
|
}
|
|
7451
|
-
if (args2["mark-deliverable-stale"]
|
|
7454
|
+
if (hasValue(args2["mark-deliverable-stale"])) {
|
|
7452
7455
|
const staleIds = args2["mark-deliverable-stale"].split(",").map((s) => s.trim()).filter(Boolean);
|
|
7453
7456
|
for (const staleTaskId of staleIds) {
|
|
7454
7457
|
const staleTask = plan.tasks.find((t) => t.id === staleTaskId);
|
|
@@ -7706,7 +7709,7 @@ function main() {
|
|
|
7706
7709
|
process.exit(1);
|
|
7707
7710
|
}
|
|
7708
7711
|
let projectList = null;
|
|
7709
|
-
if (args2.projects
|
|
7712
|
+
if (hasValue(args2.projects)) {
|
|
7710
7713
|
projectList = args2.projects.split(",").map((s) => s.trim()).filter(Boolean);
|
|
7711
7714
|
} else {
|
|
7712
7715
|
projectList = deriveProjectList(plan.tasks);
|
|
@@ -7349,7 +7349,8 @@ var ERRORS = {
|
|
|
7349
7349
|
CLOSE_NEW_EPIC_CONFLICT: () => "--close-epic and --new-epic are mutually exclusive",
|
|
7350
7350
|
NO_ACTIVE_EPIC: (flag) => `${flag} requires an active epic (active_epic.id is empty)`,
|
|
7351
7351
|
EPIC_ID_ORPHAN: () => "--epic-id (for sub-change) requires --new-change",
|
|
7352
|
-
MISSING_REMOVE_VALUE: () => "--remove-change / --remove-epic requires a non-empty value"
|
|
7352
|
+
MISSING_REMOVE_VALUE: () => "--remove-change / --remove-epic requires a non-empty value",
|
|
7353
|
+
MISSING_FLAG_VALUE: (flag) => `${flag} requires a non-empty value`
|
|
7353
7354
|
};
|
|
7354
7355
|
var DEFAULT_LIMITS = {
|
|
7355
7356
|
history: 20,
|
|
@@ -7388,6 +7389,9 @@ function parseIdList(value) {
|
|
|
7388
7389
|
if (value == null) return [];
|
|
7389
7390
|
return String(value).split(",").map((s) => s.trim()).filter(Boolean);
|
|
7390
7391
|
}
|
|
7392
|
+
function hasValue(value) {
|
|
7393
|
+
return value !== void 0 && value !== true && String(value).trim() !== "";
|
|
7394
|
+
}
|
|
7391
7395
|
function loadHistoryLimits(configPath) {
|
|
7392
7396
|
const limits = { ...DEFAULT_LIMITS };
|
|
7393
7397
|
if (!(0, import_node_fs.existsSync)(configPath)) return limits;
|
|
@@ -7413,11 +7417,22 @@ function loadHistoryLimits(configPath) {
|
|
|
7413
7417
|
}
|
|
7414
7418
|
function validate(args) {
|
|
7415
7419
|
if (!args.skill) return ERRORS.MISSING_SKILL();
|
|
7420
|
+
if (args.skill === true) return ERRORS.MISSING_FLAG_VALUE("--skill");
|
|
7416
7421
|
if (!args.summary) return ERRORS.MISSING_SUMMARY();
|
|
7422
|
+
if (args.summary === true) return ERRORS.MISSING_FLAG_VALUE("--summary");
|
|
7423
|
+
if (args["new-change"] !== void 0 && !hasValue(args["new-change"])) return ERRORS.MISSING_FLAG_VALUE("--new-change");
|
|
7417
7424
|
if (args["new-change"] && !args["change-id"]) return ERRORS.CHANGE_ID_REQUIRED();
|
|
7425
|
+
if (args["change-id"] !== void 0 && !hasValue(args["change-id"])) return ERRORS.MISSING_FLAG_VALUE("--change-id");
|
|
7418
7426
|
if (args["new-epic"] && !args["epic-id"]) return ERRORS.EPIC_ID_REQUIRED();
|
|
7427
|
+
if (args["new-epic"] !== void 0 && !hasValue(args["new-epic"])) return ERRORS.MISSING_FLAG_VALUE("--new-epic");
|
|
7428
|
+
if (args["epic-id"] !== void 0 && !hasValue(args["epic-id"])) return ERRORS.MISSING_FLAG_VALUE("--epic-id");
|
|
7419
7429
|
if (args["close-epic"] && args["new-epic"]) return ERRORS.CLOSE_NEW_EPIC_CONFLICT();
|
|
7420
7430
|
if (args["epic-id"] && !args["new-change"] && !args["new-epic"]) return ERRORS.EPIC_ID_ORPHAN();
|
|
7431
|
+
for (const flag of ["set-plan-path", "set-change-status", "truncate-history", "set-epic-path", "set-epic-status"]) {
|
|
7432
|
+
if (args[flag] !== void 0 && !hasValue(args[flag])) {
|
|
7433
|
+
return ERRORS.MISSING_FLAG_VALUE(`--${flag}`);
|
|
7434
|
+
}
|
|
7435
|
+
}
|
|
7421
7436
|
if (args["remove-change"] !== void 0 && (args["remove-change"] === true || !String(args["remove-change"]).trim())) {
|
|
7422
7437
|
return ERRORS.MISSING_REMOVE_VALUE();
|
|
7423
7438
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uoyo/mvtt",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "My Virtual Tech Team - AI-guided prompt orchestration framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^25.6.0",
|
|
46
|
-
"@vitest/coverage-v8": "^
|
|
46
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
47
47
|
"esbuild": "^0.28.0",
|
|
48
48
|
"typescript": "^5.4.0",
|
|
49
|
-
"vitest": "^
|
|
49
|
+
"vitest": "^4.1.9"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@clack/prompts": "^1.5.1",
|
|
@@ -116,16 +116,20 @@ function parseArgs(argv) {
|
|
|
116
116
|
return args;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
function hasValue(value) {
|
|
120
|
+
return value !== undefined && value !== true && String(value).trim() !== "";
|
|
121
|
+
}
|
|
122
|
+
|
|
119
123
|
function validateArgs(args) {
|
|
120
124
|
if (args.validate) return null;
|
|
121
|
-
if (!args.plan
|
|
122
|
-
if (!args.task
|
|
123
|
-
const hasStatus = args.status
|
|
125
|
+
if (!hasValue(args.plan)) return ERRORS.MISSING_PLAN();
|
|
126
|
+
if (!hasValue(args.task)) return ERRORS.MISSING_TASK();
|
|
127
|
+
const hasStatus = hasValue(args.status);
|
|
124
128
|
const hasMutation = hasStatus ||
|
|
125
|
-
(args.artifacts
|
|
126
|
-
(args.notes
|
|
127
|
-
(args["deliverables-pointer"]
|
|
128
|
-
(args["mark-deliverable-stale"]
|
|
129
|
+
hasValue(args.artifacts) ||
|
|
130
|
+
hasValue(args.notes) ||
|
|
131
|
+
hasValue(args["deliverables-pointer"]) ||
|
|
132
|
+
hasValue(args["mark-deliverable-stale"]);
|
|
129
133
|
if (!hasMutation) return ERRORS.MISSING_STATUS();
|
|
130
134
|
if (args.status === true) return ERRORS.MISSING_STATUS();
|
|
131
135
|
if (hasStatus && !VALID_STATUSES.includes(args.status)) return ERRORS.INVALID_STATUS(args.status);
|
|
@@ -137,11 +141,11 @@ function applyUpdate(plan, args, now) {
|
|
|
137
141
|
const task = plan.tasks.find((t) => t.id === args.task);
|
|
138
142
|
|
|
139
143
|
const oldStatus = task.status;
|
|
140
|
-
if (args.status
|
|
144
|
+
if (hasValue(args.status)) {
|
|
141
145
|
task.status = args.status;
|
|
142
146
|
}
|
|
143
147
|
|
|
144
|
-
if (args.artifacts
|
|
148
|
+
if (hasValue(args.artifacts)) {
|
|
145
149
|
const incoming = args.artifacts
|
|
146
150
|
.split(",")
|
|
147
151
|
.map((s) => s.trim())
|
|
@@ -163,12 +167,12 @@ function applyUpdate(plan, args, now) {
|
|
|
163
167
|
}
|
|
164
168
|
}
|
|
165
169
|
|
|
166
|
-
if (args.notes
|
|
170
|
+
if (hasValue(args.notes)) {
|
|
167
171
|
task.notes = args.notes;
|
|
168
172
|
}
|
|
169
173
|
|
|
170
174
|
// completed_at consistency: set only on status updates.
|
|
171
|
-
if (args.status
|
|
175
|
+
if (hasValue(args.status)) {
|
|
172
176
|
if (args.status === "done" && !task.completed_at) {
|
|
173
177
|
task.completed_at = now;
|
|
174
178
|
} else if (args.status !== "done") {
|
|
@@ -177,7 +181,7 @@ function applyUpdate(plan, args, now) {
|
|
|
177
181
|
}
|
|
178
182
|
|
|
179
183
|
// --deliverables-pointer current
|
|
180
|
-
if (args["deliverables-pointer"]
|
|
184
|
+
if (hasValue(args["deliverables-pointer"])) {
|
|
181
185
|
if (args["deliverables-pointer"] !== "current") {
|
|
182
186
|
return { error: ERRORS.INVALID_DELIVERABLES_POINTER(args["deliverables-pointer"]) };
|
|
183
187
|
}
|
|
@@ -186,7 +190,7 @@ function applyUpdate(plan, args, now) {
|
|
|
186
190
|
|
|
187
191
|
// --mark-deliverable-stale <task_id>[,task_id2,...]
|
|
188
192
|
// Supports comma-separated list of downstream task ids.
|
|
189
|
-
if (args["mark-deliverable-stale"]
|
|
193
|
+
if (hasValue(args["mark-deliverable-stale"])) {
|
|
190
194
|
const staleIds = args["mark-deliverable-stale"]
|
|
191
195
|
.split(",")
|
|
192
196
|
.map((s) => s.trim())
|
|
@@ -551,7 +555,7 @@ function main() {
|
|
|
551
555
|
|
|
552
556
|
// Parse --projects; if not provided, derive from tasks
|
|
553
557
|
let projectList = null;
|
|
554
|
-
if (args.projects
|
|
558
|
+
if (hasValue(args.projects)) {
|
|
555
559
|
projectList = args.projects.split(",").map((s) => s.trim()).filter(Boolean);
|
|
556
560
|
} else {
|
|
557
561
|
// Derive project list from task.project arrays so that validation
|
|
@@ -41,6 +41,7 @@ const ERRORS = {
|
|
|
41
41
|
NO_ACTIVE_EPIC: (flag) => `${flag} requires an active epic (active_epic.id is empty)`,
|
|
42
42
|
EPIC_ID_ORPHAN: () => "--epic-id (for sub-change) requires --new-change",
|
|
43
43
|
MISSING_REMOVE_VALUE: () => "--remove-change / --remove-epic requires a non-empty value",
|
|
44
|
+
MISSING_FLAG_VALUE: (flag) => `${flag} requires a non-empty value`,
|
|
44
45
|
};
|
|
45
46
|
|
|
46
47
|
// ── Defaults ────────────────────────────────────────────────────────────────
|
|
@@ -94,6 +95,10 @@ function parseIdList(value) {
|
|
|
94
95
|
.filter(Boolean);
|
|
95
96
|
}
|
|
96
97
|
|
|
98
|
+
function hasValue(value) {
|
|
99
|
+
return value !== undefined && value !== true && String(value).trim() !== "";
|
|
100
|
+
}
|
|
101
|
+
|
|
97
102
|
// ── Config Loading ──────────────────────────────────────────────────────────
|
|
98
103
|
function loadHistoryLimits(configPath) {
|
|
99
104
|
const limits = { ...DEFAULT_LIMITS };
|
|
@@ -126,14 +131,26 @@ function loadHistoryLimits(configPath) {
|
|
|
126
131
|
// ── Validation ──────────────────────────────────────────────────────────────
|
|
127
132
|
function validate(args) {
|
|
128
133
|
if (!args.skill) return ERRORS.MISSING_SKILL();
|
|
134
|
+
if (args.skill === true) return ERRORS.MISSING_FLAG_VALUE("--skill");
|
|
129
135
|
if (!args.summary) return ERRORS.MISSING_SUMMARY();
|
|
136
|
+
if (args.summary === true) return ERRORS.MISSING_FLAG_VALUE("--summary");
|
|
137
|
+
if (args["new-change"] !== undefined && !hasValue(args["new-change"])) return ERRORS.MISSING_FLAG_VALUE("--new-change");
|
|
130
138
|
if (args["new-change"] && !args["change-id"]) return ERRORS.CHANGE_ID_REQUIRED();
|
|
139
|
+
if (args["change-id"] !== undefined && !hasValue(args["change-id"])) return ERRORS.MISSING_FLAG_VALUE("--change-id");
|
|
131
140
|
|
|
132
141
|
// Epic combo validation
|
|
133
142
|
if (args["new-epic"] && !args["epic-id"]) return ERRORS.EPIC_ID_REQUIRED();
|
|
143
|
+
if (args["new-epic"] !== undefined && !hasValue(args["new-epic"])) return ERRORS.MISSING_FLAG_VALUE("--new-epic");
|
|
144
|
+
if (args["epic-id"] !== undefined && !hasValue(args["epic-id"])) return ERRORS.MISSING_FLAG_VALUE("--epic-id");
|
|
134
145
|
if (args["close-epic"] && args["new-epic"]) return ERRORS.CLOSE_NEW_EPIC_CONFLICT();
|
|
135
146
|
if (args["epic-id"] && !args["new-change"] && !args["new-epic"]) return ERRORS.EPIC_ID_ORPHAN();
|
|
136
147
|
|
|
148
|
+
for (const flag of ["set-plan-path", "set-change-status", "truncate-history", "set-epic-path", "set-epic-status"]) {
|
|
149
|
+
if (args[flag] !== undefined && !hasValue(args[flag])) {
|
|
150
|
+
return ERRORS.MISSING_FLAG_VALUE(`--${flag}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
137
154
|
// Remove flags require non-empty values
|
|
138
155
|
if (
|
|
139
156
|
args["remove-change"] !== undefined
|
|
@@ -9,7 +9,7 @@ This skill is read-only and does NOT modify `.ai-agents/workspace/session.yaml`.
|
|
|
9
9
|
After the skill's main task, run the session update script **exactly once**:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
node .ai-agents/scripts/session-update.cjs --skill {{current_skill}} --summary "<concise one-line summary>"{{#update_active_change}} --new-change "<active_change.title>" --change-id <active_change.id>{{#link_subchange_to_epic}} --epic-id <active_epic.id>{{/link_subchange_to_epic}}{{/update_active_change}}{{#set_plan_path}} --set-plan-path ".ai-agents/workspace/artifacts/{active_change.id}/plan.yaml"{{/set_plan_path}}{{#update_change}} --update-change{{/update_change}}{{#close_change}} --close-change{{/close_change}}{{#set_change_status}} --set-change-status <status>{{/set_change_status}}{{#new_epic}} --new-epic "<epic_title>" --epic-id <epic_id>{{/new_epic}}{{#set_epic_path}} --set-epic-path <epic_path>{{/set_epic_path}}{{#set_epic_status}} --set-epic-status <status>{{/set_epic_status}}{{#close_epic}} --close-epic{{/close_epic}}{{#no_change}} --no-change{{/no_change}}{{#set_synced}} --set-synced{{/set_synced}}{{#truncate_history}} --truncate-history <count>{{/truncate_history}}{{#update_initialized_at}} --set-initialized{{/update_initialized_at}}{{#remove_change}} --remove-change <ids>{{/remove_change}}{{#remove_epic}} --remove-epic <ids>{{/remove_epic}}
|
|
12
|
+
node .ai-agents/scripts/session-update.cjs --skill {{current_skill}} --summary "<concise one-line summary>"{{#update_active_change}} --new-change "<active_change.title>" --change-id <active_change.id>{{#link_subchange_to_epic}} [--epic-id <active_epic.id>]{{/link_subchange_to_epic}}{{/update_active_change}}{{#set_plan_path}} --set-plan-path ".ai-agents/workspace/artifacts/{active_change.id}/plan.yaml"{{/set_plan_path}}{{#update_change}} --update-change{{/update_change}}{{#close_change}} --close-change{{/close_change}}{{#set_change_status}} --set-change-status <status>{{/set_change_status}}{{#new_epic}} --new-epic "<epic_title>" --epic-id <epic_id>{{/new_epic}}{{#set_epic_path}} --set-epic-path <epic_path>{{/set_epic_path}}{{#set_epic_status}} --set-epic-status <status>{{/set_epic_status}}{{#close_epic}} --close-epic{{/close_epic}}{{#no_change}} --no-change{{/no_change}}{{#set_synced}} --set-synced{{/set_synced}}{{#truncate_history}} --truncate-history <count>{{/truncate_history}}{{#update_initialized_at}} --set-initialized{{/update_initialized_at}}{{#remove_change}} --remove-change <ids>{{/remove_change}}{{#remove_epic}} --remove-epic <ids>{{/remove_epic}}
|
|
13
13
|
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -64,7 +64,7 @@ Write `--summary` as one concise line in the configured `interaction_language`.
|
|
|
64
64
|
- `--remove-epic <ids>` removes entries with matching `id` from `session.epics[]` (comma-separated for multiple ids); does NOT touch `active_epic`. Unknown ids are silently skipped; if all ids are unknown, a warning is written to stderr (exit code remains 0).
|
|
65
65
|
{{/remove_epic}}
|
|
66
66
|
{{#link_subchange_to_epic}}
|
|
67
|
-
- `--epic-id` with `--new-change` links the new active change to its parent epic;
|
|
67
|
+
- `--epic-id` with `--new-change` links the new active change to its parent epic; include it only when `active_epic.id` is non-empty. Do not pass `--epic-id` with an empty placeholder.
|
|
68
68
|
{{/link_subchange_to_epic}}
|
|
69
69
|
|
|
70
70
|
If the script exits with code 0, the state update was applied successfully; do not read or verify the session file.
|