@bigking67/pi-67 0.10.8 → 0.10.9

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.9]
4
+
5
+ - Runs settings runtime-marker migration before the local distro updater script
6
+ as well as after it, so latest npm manager repair can clean `M settings.json`
7
+ even when the local checkout still contains an older updater script.
8
+
3
9
  ## [0.10.8]
4
10
 
5
11
  - Adds actionable `pi-67 version` recommendations when the npm manager has
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigking67/pi-67",
3
- "version": "0.10.8",
3
+ "version": "0.10.9",
4
4
  "description": "Cross-platform manager CLI for the pi-67 Pi Coding Agent distribution.",
5
5
  "type": "module",
6
6
  "bin": {
package/scripts/check.mjs CHANGED
@@ -42,6 +42,7 @@ runPublishTargetSelfTests();
42
42
  runShellRunnerSelfTests();
43
43
  runExtensionRegistrySelfTests();
44
44
  runSettingsRuntimeStateSelfTests();
45
+ runUpdatePreflightMigrationSelfTests();
45
46
  runUpdatePlanSelfTests();
46
47
  runUpdateSafetySelfTests();
47
48
 
@@ -407,6 +408,94 @@ function runSettingsRuntimeStateSelfTests() {
407
408
  );
408
409
  }
409
410
 
411
+ function runUpdatePreflightMigrationSelfTests() {
412
+ if (spawnSync("git", ["--version"], { encoding: "utf8" }).status !== 0) return;
413
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "pi67-update-preflight-migration-"));
414
+ const home = path.join(tmpRoot, "home");
415
+ const repo = path.join(tmpRoot, "agent");
416
+ const skillsDir = path.join(tmpRoot, "skills");
417
+ const packagesDir = path.join(tmpRoot, "packages");
418
+ fs.mkdirSync(path.join(repo, "scripts"), { recursive: true });
419
+ fs.mkdirSync(home, { recursive: true });
420
+ fs.mkdirSync(skillsDir, { recursive: true });
421
+ fs.mkdirSync(packagesDir, { recursive: true });
422
+ fs.writeFileSync(path.join(repo, "VERSION"), "0.10.0\n");
423
+ fs.writeFileSync(path.join(repo, "settings.json"), "{\n \"theme\": \"gruvbox-dark\"\n}\n");
424
+ fs.writeFileSync(path.join(repo, "scripts", "pi67-update.sh"), `#!/usr/bin/env bash
425
+ set -euo pipefail
426
+ agent_dir=""
427
+ while [ "$#" -gt 0 ]; do
428
+ case "$1" in
429
+ --agent-dir)
430
+ agent_dir="$2"
431
+ shift 2
432
+ ;;
433
+ *)
434
+ shift
435
+ ;;
436
+ esac
437
+ done
438
+ if grep -q "lastChangelogVersion" "$agent_dir/settings.json"; then
439
+ echo "marker still present before distro script" >&2
440
+ exit 44
441
+ fi
442
+ `);
443
+ for (const args of [
444
+ ["-C", repo, "init", "-q"],
445
+ ["-C", repo, "config", "user.email", "pi67-check@example.invalid"],
446
+ ["-C", repo, "config", "user.name", "pi67-check"],
447
+ ["-C", repo, "add", "VERSION", "settings.json", "scripts/pi67-update.sh"],
448
+ ["-C", repo, "commit", "-q", "-m", "init"],
449
+ ]) {
450
+ const result = spawnSync("git", args, { encoding: "utf8" });
451
+ assert(result.status === 0, `git setup failed for update preflight migration self-test: git ${args.join(" ")}\n${result.stderr}`);
452
+ }
453
+ fs.writeFileSync(path.join(repo, "settings.json"), "{\n \"lastChangelogVersion\": \"0.80.4\",\n \"theme\": \"gruvbox-dark\"\n}\n");
454
+ const env = { ...process.env, HOME: home, USERPROFILE: home };
455
+ const result = spawnSync(process.execPath, [
456
+ path.join(root, "bin", "pi-67.mjs"),
457
+ "--agent-dir",
458
+ repo,
459
+ "--repo-root",
460
+ repo,
461
+ "--skills-dir",
462
+ skillsDir,
463
+ "--packages-dir",
464
+ packagesDir,
465
+ "update",
466
+ "--repair",
467
+ "--no-remote",
468
+ "--no-npm",
469
+ ], {
470
+ cwd: root,
471
+ env,
472
+ encoding: "utf8",
473
+ });
474
+ assert(
475
+ result.status === 0,
476
+ `update must preflight-normalize settings runtime marker before calling the distro script\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`,
477
+ );
478
+ assert(
479
+ result.stdout.includes("Preflight: Migrated settings.json lastChangelogVersion") &&
480
+ result.stdout.includes("Preflight: Normalized settings.json"),
481
+ `update output must report preflight runtime marker migration\n${result.stdout}`,
482
+ );
483
+ const settings = JSON.parse(fs.readFileSync(path.join(repo, "settings.json"), "utf8"));
484
+ assert(settings.lastChangelogVersion === undefined, "update preflight migration must remove lastChangelogVersion from settings.json");
485
+ const state = JSON.parse(fs.readFileSync(path.join(home, ".pi", "pi67", "state.json"), "utf8"));
486
+ assert(
487
+ state.runtimeMarkers?.lastChangelogVersion?.value === "0.80.4",
488
+ "update preflight migration must persist lastChangelogVersion into ignored state",
489
+ );
490
+ const status = spawnSync("git", ["-C", repo, "status", "--short"], { encoding: "utf8" });
491
+ assert(status.status === 0, `git status failed for update preflight migration self-test\n${status.stderr}`);
492
+ assert(
493
+ !status.stdout.split(/\r?\n/).some((line) => line.trim().endsWith("settings.json")),
494
+ `settings.json should be clean after preflight marker migration\n${status.stdout}`,
495
+ );
496
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
497
+ }
498
+
410
499
  function runUpdatePlanSelfTests() {
411
500
  assert(
412
501
  classifyGitShort(" M settings.json\n?? tmp.txt").preservedRuntime.includes("settings.json"),
@@ -61,6 +61,8 @@ export async function updateCommand(ctx, argv) {
61
61
  info(`Preserved runtime backup skipped: ${lifecycle.backupReason}`);
62
62
  }
63
63
 
64
+ reportSettingsRuntimeStateMigration(ctx, { dryRun, phase: "Preflight" });
65
+
64
66
  const args = isWindows()
65
67
  ? buildWindowsUpdateArgs(ctx, options, dryRun)
66
68
  : buildBashUpdateArgs(ctx, options, dryRun);
@@ -68,7 +70,7 @@ export async function updateCommand(ctx, argv) {
68
70
  runDistroScript(ctx, { sh: "pi67-update.sh", ps1: "pi67-update.ps1" }, args, { dryRun: false });
69
71
  if (!dryRun) {
70
72
  writeState(ctx, options.repair ? "repair" : "update");
71
- reportSettingsRuntimeStateMigration(ctx);
73
+ reportSettingsRuntimeStateMigration(ctx, { phase: "Post-update" });
72
74
  }
73
75
  } finally {
74
76
  lifecycle.release();
@@ -86,19 +88,22 @@ export async function updateCommand(ctx, argv) {
86
88
  }
87
89
  }
88
90
 
89
- function reportSettingsRuntimeStateMigration(ctx) {
91
+ function reportSettingsRuntimeStateMigration(ctx, options = {}) {
92
+ const dryRun = Boolean(options.dryRun);
93
+ const phase = options.phase ? `${options.phase}: ` : "";
90
94
  const result = migrateSettingsRuntimeState(ctx, {
91
95
  normalizeSettingsJson: true,
92
96
  installGitFilter: true,
97
+ dryRun,
93
98
  });
94
- if (result.markerFound && result.stateWritten) {
95
- info("Migrated settings.json lastChangelogVersion to ignored state: ~/.pi/pi67/state.json");
99
+ if (result.markerFound && (result.stateWritten || dryRun)) {
100
+ info(`${phase}${dryRun ? "would migrate" : "Migrated"} settings.json lastChangelogVersion to ignored state: ~/.pi/pi67/state.json`);
96
101
  }
97
102
  if (result.settingsNormalized) {
98
- info("Normalized settings.json by removing runtime-only lastChangelogVersion.");
103
+ info(`${phase}${dryRun ? "would normalize" : "Normalized"} settings.json by removing runtime-only lastChangelogVersion.`);
99
104
  }
100
105
  if (result.gitFilterInstalled) {
101
- info("Installed local git clean filter for future settings.json runtime markers.");
106
+ info(`${phase}${dryRun ? "would install" : "Installed"} local git clean filter for future settings.json runtime markers.`);
102
107
  }
103
108
  for (const error of result.errors) {
104
109
  warn(`settings runtime marker migration skipped: ${error}`);
@@ -138,6 +138,32 @@ export function installSettingsRuntimeGitFilter(ctx, options = {}) {
138
138
  }
139
139
 
140
140
  const cleanCommand = `node ${SETTINGS_RUNTIME_FILTER_SCRIPT} --clean`;
141
+ const existingClean = captureCommand("git", [
142
+ "-C",
143
+ ctx.repoRoot,
144
+ "config",
145
+ "--local",
146
+ "--get",
147
+ `filter.${SETTINGS_RUNTIME_FILTER_NAME}.clean`,
148
+ ]);
149
+ const existingRequired = captureCommand("git", [
150
+ "-C",
151
+ ctx.repoRoot,
152
+ "config",
153
+ "--local",
154
+ "--get",
155
+ `filter.${SETTINGS_RUNTIME_FILTER_NAME}.required`,
156
+ ]);
157
+ if (
158
+ existingClean.ok &&
159
+ existingClean.stdout.trim() === cleanCommand &&
160
+ existingRequired.ok &&
161
+ existingRequired.stdout.trim() === "false"
162
+ ) {
163
+ result.skipped = "settings runtime filter already installed";
164
+ return result;
165
+ }
166
+
141
167
  const clean = captureCommand("git", [
142
168
  "-C",
143
169
  ctx.repoRoot,