@bigking67/pi-67 0.10.11 → 0.10.13

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,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.13]
4
+
5
+ - Normalizes `settings.json` BOM/CRLF line endings during `update --repair`
6
+ even when the runtime `lastChangelogVersion` marker is already absent,
7
+ clearing Windows Git EOL false-dirty states without changing provider,
8
+ model, theme, or other JSON content.
9
+
10
+ ## [0.10.12]
11
+
12
+ - Pins `settings.json` to LF line endings in `.gitattributes` while preserving
13
+ the runtime clean filter, preventing Windows CRLF false-dirty status after
14
+ update cleanup.
15
+
3
16
  ## [0.10.11]
4
17
 
5
18
  - Runs final settings runtime-marker normalization from the Bash and PowerShell
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigking67/pi-67",
3
- "version": "0.10.11",
3
+ "version": "0.10.13",
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
@@ -9,6 +9,7 @@ import { parseCommandOptions, splitGlobalArgs } from "../src/lib/args.mjs";
9
9
  import { readExtensionRegistry, validateExtensionRegistry } from "../src/lib/extension-registry.mjs";
10
10
  import { buildPlanDecisions, classifyGitShort } from "../src/lib/update-plan.mjs";
11
11
  import {
12
+ migrateSettingsRuntimeState,
12
13
  mergeSettingsRuntimeMarkerIntoState,
13
14
  settingsRuntimeMarkerFromObject,
14
15
  stripSettingsRuntimeMarkerText,
@@ -406,6 +407,27 @@ function runSettingsRuntimeStateSelfTests() {
406
407
  state.runtimeMarkers.lastChangelogVersion.storage === "state.json",
407
408
  "settings runtime marker must migrate into state runtimeMarkers",
408
409
  );
410
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "pi67-settings-runtime-state-"));
411
+ const agentDir = path.join(tmpRoot, "agent");
412
+ const stateDir = path.join(tmpRoot, "state");
413
+ fs.mkdirSync(agentDir, { recursive: true });
414
+ fs.writeFileSync(path.join(agentDir, "settings.json"), "{\r\n \"theme\": \"gruvbox-dark\"\r\n}\r\n", "utf8");
415
+ const lineEndingResult = migrateSettingsRuntimeState(
416
+ { agentDir, repoRoot: agentDir, stateDir },
417
+ { normalizeSettingsJson: true },
418
+ );
419
+ const normalizedSettings = fs.readFileSync(path.join(agentDir, "settings.json"), "utf8");
420
+ assert(lineEndingResult.markerFound === false, "line-ending normalization must not require runtime marker");
421
+ assert(lineEndingResult.settingsNormalized === true, "CRLF settings.json must be normalized under --normalize");
422
+ assert(
423
+ lineEndingResult.settingsNormalizeReasons.includes("line-endings"),
424
+ "settings normalization must classify CRLF as line-endings",
425
+ );
426
+ assert(
427
+ normalizedSettings === "{\n \"theme\": \"gruvbox-dark\"\n}\n",
428
+ "settings line-ending normalization must preserve JSON content and indentation",
429
+ );
430
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
409
431
  }
410
432
 
411
433
  function runUpdatePreflightMigrationSelfTests() {
@@ -58,7 +58,7 @@ export async function installCommand(ctx, argv) {
58
58
  info("Migrated settings.json lastChangelogVersion to ignored state: ~/.pi/pi67/state.json");
59
59
  }
60
60
  if (runtimeState.settingsNormalized) {
61
- info("Normalized settings.json by removing runtime-only lastChangelogVersion.");
61
+ info("Normalized settings.json runtime marker/line endings.");
62
62
  }
63
63
  if (runtimeState.gitFilterInstalled) {
64
64
  info("Installed local git clean filter for future settings.json runtime markers.");
@@ -100,7 +100,7 @@ function reportSettingsRuntimeStateMigration(ctx, options = {}) {
100
100
  info(`${phase}${dryRun ? "would migrate" : "Migrated"} settings.json lastChangelogVersion to ignored state: ~/.pi/pi67/state.json`);
101
101
  }
102
102
  if (result.settingsNormalized) {
103
- info(`${phase}${dryRun ? "would normalize" : "Normalized"} settings.json by removing runtime-only lastChangelogVersion.`);
103
+ info(`${phase}${dryRun ? "would normalize" : "Normalized"} settings.json runtime marker/line endings.`);
104
104
  }
105
105
  if (result.gitFilterInstalled) {
106
106
  info(`${phase}${dryRun ? "would install" : "Installed"} local git clean filter for future settings.json runtime markers.`);
@@ -67,6 +67,7 @@ export function migrateSettingsRuntimeState(ctx, options = {}) {
67
67
  markerValue: "",
68
68
  stateWritten: false,
69
69
  settingsNormalized: false,
70
+ settingsNormalizeReasons: [],
70
71
  gitFilterInstalled: false,
71
72
  skipped: [],
72
73
  errors: [],
@@ -75,10 +76,12 @@ export function migrateSettingsRuntimeState(ctx, options = {}) {
75
76
  if (!fs.existsSync(settingsPath)) {
76
77
  result.skipped.push("settings.json missing");
77
78
  } else {
79
+ let rawSettingsText = "";
78
80
  let settingsText = "";
79
81
  let settings = null;
80
82
  try {
81
- settingsText = fs.readFileSync(settingsPath, "utf8").replace(/^\uFEFF/, "");
83
+ rawSettingsText = fs.readFileSync(settingsPath, "utf8");
84
+ settingsText = rawSettingsText.replace(/^\uFEFF/, "");
82
85
  settings = JSON.parse(settingsText);
83
86
  } catch (error) {
84
87
  result.errors.push(`settings.json is not valid JSON: ${error.message}`);
@@ -95,14 +98,28 @@ export function migrateSettingsRuntimeState(ctx, options = {}) {
95
98
  }
96
99
  if (normalizeSettingsJson) {
97
100
  const normalizedText = stableSettingsJson(stripSettingsRuntimeMarker(settings));
98
- if (normalizedText !== settingsText) {
101
+ if (normalizedText !== rawSettingsText) {
99
102
  if (!dryRun) {
100
103
  fs.writeFileSync(settingsPath, normalizedText, "utf8");
101
104
  }
102
105
  result.settingsNormalized = true;
106
+ result.settingsNormalizeReasons.push("runtime-marker");
107
+ if (normalizeSettingsTextLineEndings(rawSettingsText) !== rawSettingsText) {
108
+ result.settingsNormalizeReasons.push("line-endings");
109
+ }
103
110
  }
104
111
  }
105
112
  } else {
113
+ if (normalizeSettingsJson && settings) {
114
+ const normalizedText = normalizeSettingsTextLineEndings(rawSettingsText);
115
+ if (normalizedText !== rawSettingsText) {
116
+ if (!dryRun) {
117
+ fs.writeFileSync(settingsPath, normalizedText, "utf8");
118
+ }
119
+ result.settingsNormalized = true;
120
+ result.settingsNormalizeReasons.push("line-endings");
121
+ }
122
+ }
106
123
  result.skipped.push("settings.json runtime marker absent");
107
124
  }
108
125
  }
@@ -117,6 +134,12 @@ export function migrateSettingsRuntimeState(ctx, options = {}) {
117
134
  return result;
118
135
  }
119
136
 
137
+ export function normalizeSettingsTextLineEndings(text) {
138
+ return String(text || "")
139
+ .replace(/^\uFEFF/, "")
140
+ .replace(/\r\n?/g, "\n");
141
+ }
142
+
120
143
  export function installSettingsRuntimeGitFilter(ctx, options = {}) {
121
144
  const result = {
122
145
  installed: false,
@@ -51,7 +51,7 @@ function printHuman(result) {
51
51
  console.log("PASS settings.json runtime marker is already absent");
52
52
  }
53
53
  if (result.settingsNormalized) {
54
- console.log("PASS normalized settings.json by removing runtime-only lastChangelogVersion");
54
+ console.log("PASS normalized settings.json runtime marker/line endings");
55
55
  }
56
56
  if (result.gitFilterInstalled) {
57
57
  console.log("PASS installed local git clean filter for settings.json runtime marker");