scrumrun 2.6.4 → 2.6.6
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/README.md +1 -1
- package/bin/scrumrun.js +32 -9
- package/lib/commands/repair.js +23 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
ScrumRun gives an agent a small command surface and a precise project memory: what should be done, how each attempt happened, which decisions constrain the code, and why the architecture exists in its current form.
|
|
6
6
|
|
|
7
|
-
**Package:** `2.6.
|
|
7
|
+
**Package:** `2.6.6` · **Method target:** `2.0.0` · **Runtime:** Node.js `>=22.13.0` · **License:** MIT
|
|
8
8
|
|
|
9
9
|
**New here?** Read the [Quickstart](docs/QUICKSTART.md) — first Run in under 10 minutes, no `SPEC.md` reading required. Full docs map in [`docs/INDEX.md`](docs/INDEX.md).
|
|
10
10
|
|
package/bin/scrumrun.js
CHANGED
|
@@ -44,6 +44,12 @@ const COMMANDS = ["sc"];
|
|
|
44
44
|
const COMPATIBILITY_COMMANDS = Object.keys(COMMAND_ALIASES);
|
|
45
45
|
const MANAGED_COMMANDS = [...new Set(["sc", "sc-plan", "sc-knowledge", "sc-rules", ...COMPATIBILITY_COMMANDS])];
|
|
46
46
|
|
|
47
|
+
let installVerbose = false;
|
|
48
|
+
const installSummary = { cleaned: 0, written: 0, skipped: 0, targets: [] };
|
|
49
|
+
function installLog(msg) {
|
|
50
|
+
if (installVerbose) console.log(msg);
|
|
51
|
+
}
|
|
52
|
+
|
|
47
53
|
function usage() {
|
|
48
54
|
console.log(`ScrumRun
|
|
49
55
|
|
|
@@ -52,7 +58,7 @@ Usage:
|
|
|
52
58
|
scrumrun sc
|
|
53
59
|
scrumrun sc <noun> <subject> <action> [args]
|
|
54
60
|
scrumrun install [all|codex|opencode|claude] [--force]
|
|
55
|
-
scrumrun update [all|codex|opencode|claude] [--no-migrate]
|
|
61
|
+
scrumrun update [all|codex|opencode|claude] [--no-migrate] [--verbose]
|
|
56
62
|
scrumrun init [--local|--shared] [--lean] [--no-agent-hint] [--force]
|
|
57
63
|
scrumrun status
|
|
58
64
|
scrumrun core [--path|--prompt]
|
|
@@ -184,8 +190,13 @@ function copyDir(srcDir, destDir, options = {}) {
|
|
|
184
190
|
return results;
|
|
185
191
|
}
|
|
186
192
|
|
|
187
|
-
function printResults(title, results) {
|
|
193
|
+
function printResults(title, results, { quietable = false } = {}) {
|
|
194
|
+
for (const result of results) {
|
|
195
|
+
if (result.status === "written") installSummary.written += 1;
|
|
196
|
+
else if (result.status === "skipped") installSummary.skipped += 1;
|
|
197
|
+
}
|
|
188
198
|
console.log(`\n${title}`);
|
|
199
|
+
if (quietable && !installVerbose) return;
|
|
189
200
|
for (const result of results) {
|
|
190
201
|
const prefix = result.status === "skipped" ? "skip" : result.status;
|
|
191
202
|
console.log(` ${prefix} ${result.dest}`);
|
|
@@ -202,14 +213,16 @@ function cleanupLegacy(commandsDir, skillsDir) {
|
|
|
202
213
|
const managed = MANAGED_COMMANDS.some((command) => name === `${command}.md`);
|
|
203
214
|
if (legacyPrefix || oldConsolidated || managed) {
|
|
204
215
|
fs.rmSync(path.join(commandsDir, name), { force: true });
|
|
205
|
-
|
|
216
|
+
installSummary.cleaned += 1;
|
|
217
|
+
installLog(` rm legacy ${path.join(commandsDir, name)}`);
|
|
206
218
|
}
|
|
207
219
|
}
|
|
208
220
|
}
|
|
209
221
|
const oldSkill = path.join(skillsDir, "ai-scrum");
|
|
210
222
|
if (fs.existsSync(oldSkill)) {
|
|
211
223
|
fs.rmSync(oldSkill, { recursive: true, force: true });
|
|
212
|
-
|
|
224
|
+
installSummary.cleaned += 1;
|
|
225
|
+
installLog(` rm legacy ${oldSkill}`);
|
|
213
226
|
}
|
|
214
227
|
}
|
|
215
228
|
|
|
@@ -231,8 +244,9 @@ function installClient(label, commandsDir, skillsDir, skillTemplateDir, force, {
|
|
|
231
244
|
results.push(writeCommandAsset(path.join(commandsDir, `${alias}.md`), renderCompatibilityPrompt(alias), force));
|
|
232
245
|
}
|
|
233
246
|
}
|
|
234
|
-
|
|
235
|
-
|
|
247
|
+
installSummary.targets.push(label);
|
|
248
|
+
printResults(`Installed ${label}`, results, { quietable: true });
|
|
249
|
+
installLog(compatibility
|
|
236
250
|
? " compatibility adapters installed for this v1 upgrade cycle"
|
|
237
251
|
: " canonical surface: /sc only");
|
|
238
252
|
}
|
|
@@ -345,7 +359,12 @@ function migrationPreflightOnUpdate({ apply = false } = {}) {
|
|
|
345
359
|
}
|
|
346
360
|
}
|
|
347
361
|
|
|
348
|
-
function updateInstallation(target, { migrate = false } = {}) {
|
|
362
|
+
function updateInstallation(target, { migrate = false, verbose = false } = {}) {
|
|
363
|
+
installVerbose = verbose;
|
|
364
|
+
installSummary.cleaned = 0;
|
|
365
|
+
installSummary.written = 0;
|
|
366
|
+
installSummary.skipped = 0;
|
|
367
|
+
installSummary.targets.length = 0;
|
|
349
368
|
const migration = migrationPreflightOnUpdate({ apply: migrate });
|
|
350
369
|
install(target, true, { compatibility: true });
|
|
351
370
|
if (migrate && v2Project()) {
|
|
@@ -356,6 +375,10 @@ function updateInstallation(target, { migrate = false } = {}) {
|
|
|
356
375
|
// Conformance reports stale or unsafe state separately.
|
|
357
376
|
}
|
|
358
377
|
}
|
|
378
|
+
if (!verbose) {
|
|
379
|
+
const targetSummary = installSummary.targets.join(", ") || "no clients";
|
|
380
|
+
console.log(`Updated ${targetSummary} — ${installSummary.written} files written, ${installSummary.cleaned} legacy removed. Run with --verbose to see file paths.`);
|
|
381
|
+
}
|
|
359
382
|
return migration;
|
|
360
383
|
}
|
|
361
384
|
|
|
@@ -1575,7 +1598,7 @@ function executeRootRoute(route) {
|
|
|
1575
1598
|
}
|
|
1576
1599
|
if (noun === "config" && subject === "update") {
|
|
1577
1600
|
const target = ["all", "codex", "opencode", "claude"].includes(routeArgs[0]) ? routeArgs[0] : "all";
|
|
1578
|
-
return updateInstallation(target, { migrate: !routeArgs.includes("--no-migrate") });
|
|
1601
|
+
return updateInstallation(target, { migrate: !routeArgs.includes("--no-migrate"), verbose: routeArgs.includes("--verbose") });
|
|
1579
1602
|
}
|
|
1580
1603
|
if (noun === "config" && subject === "init") {
|
|
1581
1604
|
const localMode = routeArgs.includes("--local");
|
|
@@ -2500,7 +2523,7 @@ if (!command || command === "--help" || command === "-h") {
|
|
|
2500
2523
|
console.log(`ScrumRun ${version}`);
|
|
2501
2524
|
} else if (command === "install" || command === "update") {
|
|
2502
2525
|
const target = ["all", "codex", "opencode", "claude"].includes(args[1]) ? args[1] : "all";
|
|
2503
|
-
if (command === "update") updateInstallation(target, { migrate: !args.includes("--no-migrate") });
|
|
2526
|
+
if (command === "update") updateInstallation(target, { migrate: !args.includes("--no-migrate"), verbose: args.includes("--verbose") });
|
|
2504
2527
|
else install(target, true, { compatibility: false });
|
|
2505
2528
|
} else if (command === "sc") {
|
|
2506
2529
|
runRoot(args.slice(1));
|
package/lib/commands/repair.js
CHANGED
|
@@ -31,6 +31,8 @@ const RUN_FILE = /^RUN-\d{3,}\.md$/;
|
|
|
31
31
|
const FEAT_FILE = /^FEAT-\d{3,}\.md$/;
|
|
32
32
|
const SPRINT_FILE = /^SPRINT-\d{3,}\.md$/;
|
|
33
33
|
const MEMORY_FILE = /^(K|DEC|INS|DOS)-\d{3,}\.md$/;
|
|
34
|
+
const TASK_REF = /^TASK-\d{3,}$/;
|
|
35
|
+
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
34
36
|
const FEAT_REF = /^FEAT-\d{3,}$/;
|
|
35
37
|
const SLUG_REF = /^[a-z0-9][a-z0-9-]*$/;
|
|
36
38
|
|
|
@@ -105,6 +107,19 @@ function planFile(file, kind, ctx) {
|
|
|
105
107
|
let header = split.header;
|
|
106
108
|
const changes = [];
|
|
107
109
|
|
|
110
|
+
const updated = extractField(header, "updated");
|
|
111
|
+
if (updated !== undefined && updated !== "null" && !ISO_DATE.test(updated)) {
|
|
112
|
+
const parsed = new Date(updated);
|
|
113
|
+
if (!isNaN(parsed.getTime())) {
|
|
114
|
+
const iso = parsed.toISOString().slice(0, 10);
|
|
115
|
+
const next = replaceField(header, "updated", iso);
|
|
116
|
+
if (next !== header) {
|
|
117
|
+
changes.push({ field: "updated", from: updated, to: iso });
|
|
118
|
+
header = next;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
108
123
|
const method = extractField(header, "method");
|
|
109
124
|
if (method === undefined) {
|
|
110
125
|
if (hasField(header, "id")) {
|
|
@@ -145,6 +160,14 @@ function planFile(file, kind, ctx) {
|
|
|
145
160
|
}
|
|
146
161
|
|
|
147
162
|
if (kind === "run") {
|
|
163
|
+
const taskRef = extractField(header, "task");
|
|
164
|
+
if (taskRef !== undefined && taskRef !== "null" && taskRef !== null && taskRef !== "" && !TASK_REF.test(taskRef)) {
|
|
165
|
+
const next = replaceField(header, "task", "null");
|
|
166
|
+
if (next !== header) {
|
|
167
|
+
changes.push({ field: "task", from: taskRef, to: "null" });
|
|
168
|
+
header = next;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
148
171
|
const status = extractField(header, "status");
|
|
149
172
|
if (status && RUN_STATUS_ALIAS[status]) {
|
|
150
173
|
const next = replaceField(header, "status", RUN_STATUS_ALIAS[status]);
|