scrumrun 2.6.3 → 2.6.5

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 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.3` · **Method target:** `2.0.0` · **Runtime:** Node.js `>=22.13.0` · **License:** MIT
7
+ **Package:** `2.6.5` · **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
- console.log(` rm legacy ${path.join(commandsDir, name)}`);
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
- console.log(` rm legacy ${oldSkill}`);
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
- printResults(`Installed ${label}`, results);
235
- console.log(compatibility
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));
@@ -29,16 +29,24 @@ const path = require("node:path");
29
29
  const TASK_FILE = /^TASK-\d{3,}\.md$/;
30
30
  const RUN_FILE = /^RUN-\d{3,}\.md$/;
31
31
  const FEAT_FILE = /^FEAT-\d{3,}\.md$/;
32
+ const SPRINT_FILE = /^SPRINT-\d{3,}\.md$/;
33
+ const MEMORY_FILE = /^(K|DEC|INS|DOS)-\d{3,}\.md$/;
32
34
  const FEAT_REF = /^FEAT-\d{3,}$/;
33
35
  const SLUG_REF = /^[a-z0-9][a-z0-9-]*$/;
34
36
 
35
- const STATUS_ALIAS = {
37
+ const TASK_STATUS_ALIAS = {
36
38
  done: "completed",
37
39
  todo: "backlog",
38
40
  complete: "completed",
39
41
  in_progress: "executing"
40
42
  };
41
43
 
44
+ const RUN_STATUS_ALIAS = {
45
+ complete: "completed",
46
+ in_progress: "executing",
47
+ done: "completed"
48
+ };
49
+
42
50
  function readIf(file) {
43
51
  try {
44
52
  return fs.readFileSync(file, "utf8");
@@ -98,7 +106,15 @@ function planFile(file, kind, ctx) {
98
106
  const changes = [];
99
107
 
100
108
  const method = extractField(header, "method");
101
- if (method !== undefined && method !== "2.0.0" && method !== "null") {
109
+ if (method === undefined) {
110
+ if (hasField(header, "id")) {
111
+ const next = insertFieldAfter(header, "id", "method", "2.0.0");
112
+ if (next !== header) {
113
+ changes.push({ field: "method", from: "(missing)", to: "2.0.0" });
114
+ header = next;
115
+ }
116
+ }
117
+ } else if (method !== "2.0.0" && method !== "null") {
102
118
  const next = replaceField(header, "method", "2.0.0");
103
119
  if (next !== header) {
104
120
  changes.push({ field: "method", from: method, to: "2.0.0" });
@@ -119,16 +135,24 @@ function planFile(file, kind, ctx) {
119
135
  }
120
136
 
121
137
  const status = extractField(header, "status");
122
- if (status && STATUS_ALIAS[status]) {
123
- const next = replaceField(header, "status", STATUS_ALIAS[status]);
138
+ if (status && TASK_STATUS_ALIAS[status]) {
139
+ const next = replaceField(header, "status", TASK_STATUS_ALIAS[status]);
124
140
  if (next !== header) {
125
- changes.push({ field: "status", from: status, to: STATUS_ALIAS[status] });
141
+ changes.push({ field: "status", from: status, to: TASK_STATUS_ALIAS[status] });
126
142
  header = next;
127
143
  }
128
144
  }
129
145
  }
130
146
 
131
147
  if (kind === "run") {
148
+ const status = extractField(header, "status");
149
+ if (status && RUN_STATUS_ALIAS[status]) {
150
+ const next = replaceField(header, "status", RUN_STATUS_ALIAS[status]);
151
+ if (next !== header) {
152
+ changes.push({ field: "status", from: status, to: RUN_STATUS_ALIAS[status] });
153
+ header = next;
154
+ }
155
+ }
132
156
  if (!hasField(header, "attempt") && hasField(header, "task")) {
133
157
  const next = insertFieldAfter(header, "task", "attempt", "1");
134
158
  if (next !== header) {
@@ -161,6 +185,16 @@ function analyze(scrumDir) {
161
185
  const plan = planFile(file, "feature", ctx);
162
186
  if (plan && plan.changes.length) entries.push(plan);
163
187
  }
188
+ for (const file of listDir(path.join(scrumDir, "sprints"), SPRINT_FILE)) {
189
+ const plan = planFile(file, "sprint", ctx);
190
+ if (plan && plan.changes.length) entries.push(plan);
191
+ }
192
+ for (const subdir of ["memory", "memory/knowledge", "memory/decisions", "memory/insights", "memory/dossiers"]) {
193
+ for (const file of listDir(path.join(scrumDir, subdir), MEMORY_FILE)) {
194
+ const plan = planFile(file, "memory", ctx);
195
+ if (plan && plan.changes.length) entries.push(plan);
196
+ }
197
+ }
164
198
 
165
199
  const totals = { files: entries.length, byField: {} };
166
200
  for (const entry of entries) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scrumrun",
3
- "version": "2.6.3",
3
+ "version": "2.6.5",
4
4
  "description": "Evidence-driven Agile runtime and semantic project memory for AI coding agents.",
5
5
  "bin": {
6
6
  "scrumrun": "bin/scrumrun.js",