@bigking67/pi-67 0.10.7 → 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 +12 -0
- package/package.json +1 -1
- package/scripts/check.mjs +152 -0
- package/src/commands/update.mjs +11 -6
- package/src/commands/version.mjs +79 -2
- package/src/lib/settings-runtime-state.mjs +26 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
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
|
+
|
|
9
|
+
## [0.10.8]
|
|
10
|
+
|
|
11
|
+
- Adds actionable `pi-67 version` recommendations when the npm manager has
|
|
12
|
+
been updated but the local distro checkout is still old or `settings.json`
|
|
13
|
+
still has runtime-marker dirty state.
|
|
14
|
+
|
|
3
15
|
## [0.10.7]
|
|
4
16
|
|
|
5
17
|
- Migrates `settings.json.lastChangelogVersion` into ignored manager state at
|
package/package.json
CHANGED
package/scripts/check.mjs
CHANGED
|
@@ -37,10 +37,12 @@ for (const file of files.filter((item) => item.endsWith(".json"))) {
|
|
|
37
37
|
await runNpmRegistrySelfTests();
|
|
38
38
|
runArgsSelfTests();
|
|
39
39
|
runCliHelpContractSelfTests();
|
|
40
|
+
runVersionRecommendationSelfTests();
|
|
40
41
|
runPublishTargetSelfTests();
|
|
41
42
|
runShellRunnerSelfTests();
|
|
42
43
|
runExtensionRegistrySelfTests();
|
|
43
44
|
runSettingsRuntimeStateSelfTests();
|
|
45
|
+
runUpdatePreflightMigrationSelfTests();
|
|
44
46
|
runUpdatePlanSelfTests();
|
|
45
47
|
runUpdateSafetySelfTests();
|
|
46
48
|
|
|
@@ -122,6 +124,68 @@ function runCliHelpContractSelfTests() {
|
|
|
122
124
|
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
123
125
|
}
|
|
124
126
|
|
|
127
|
+
function runVersionRecommendationSelfTests() {
|
|
128
|
+
if (spawnSync("git", ["--version"], { encoding: "utf8" }).status !== 0) return;
|
|
129
|
+
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "pi67-version-recommendation-"));
|
|
130
|
+
const home = path.join(tmpRoot, "home");
|
|
131
|
+
const repo = path.join(tmpRoot, "agent");
|
|
132
|
+
fs.mkdirSync(home, { recursive: true });
|
|
133
|
+
fs.mkdirSync(repo, { recursive: true });
|
|
134
|
+
fs.writeFileSync(path.join(repo, "VERSION"), "0.10.0\n");
|
|
135
|
+
fs.writeFileSync(path.join(repo, "settings.json"), "{\n \"lastChangelogVersion\": \"0.80.3\",\n \"theme\": \"gruvbox-dark\"\n}\n");
|
|
136
|
+
for (const args of [
|
|
137
|
+
["-C", repo, "init", "-q"],
|
|
138
|
+
["-C", repo, "config", "user.email", "pi67-check@example.invalid"],
|
|
139
|
+
["-C", repo, "config", "user.name", "pi67-check"],
|
|
140
|
+
["-C", repo, "add", "VERSION", "settings.json"],
|
|
141
|
+
["-C", repo, "commit", "-q", "-m", "init"],
|
|
142
|
+
]) {
|
|
143
|
+
const result = spawnSync("git", args, { encoding: "utf8" });
|
|
144
|
+
assert(result.status === 0, `git setup failed for version recommendation self-test: git ${args.join(" ")}\n${result.stderr}`);
|
|
145
|
+
}
|
|
146
|
+
fs.writeFileSync(path.join(repo, "settings.json"), "{\n \"lastChangelogVersion\": \"0.80.4\",\n \"theme\": \"gruvbox-dark\"\n}\n");
|
|
147
|
+
const env = { ...process.env, HOME: home, USERPROFILE: home };
|
|
148
|
+
const result = spawnSync(process.execPath, [
|
|
149
|
+
path.join(root, "bin", "pi-67.mjs"),
|
|
150
|
+
"--agent-dir",
|
|
151
|
+
repo,
|
|
152
|
+
"--repo-root",
|
|
153
|
+
repo,
|
|
154
|
+
"version",
|
|
155
|
+
], {
|
|
156
|
+
cwd: root,
|
|
157
|
+
env,
|
|
158
|
+
encoding: "utf8",
|
|
159
|
+
});
|
|
160
|
+
assert(result.status === 0, `version recommendation command failed\n${result.stderr || result.stdout}`);
|
|
161
|
+
assert(
|
|
162
|
+
result.stdout.includes("npm install updated only the manager package") &&
|
|
163
|
+
result.stdout.includes("update --repair") &&
|
|
164
|
+
result.stdout.includes("settings.json has Pi runtime changelog marker state"),
|
|
165
|
+
`version output must explain manager/distro mismatch and runtime marker repair\n${result.stdout}`,
|
|
166
|
+
);
|
|
167
|
+
const json = spawnSync(process.execPath, [
|
|
168
|
+
path.join(root, "bin", "pi-67.mjs"),
|
|
169
|
+
"--agent-dir",
|
|
170
|
+
repo,
|
|
171
|
+
"--repo-root",
|
|
172
|
+
repo,
|
|
173
|
+
"version",
|
|
174
|
+
"--json",
|
|
175
|
+
], {
|
|
176
|
+
cwd: root,
|
|
177
|
+
env,
|
|
178
|
+
encoding: "utf8",
|
|
179
|
+
});
|
|
180
|
+
assert(json.status === 0, `version --json recommendation command failed\n${json.stderr || json.stdout}`);
|
|
181
|
+
const parsed = JSON.parse(json.stdout);
|
|
182
|
+
assert(
|
|
183
|
+
parsed.recommendations?.some((item) => item.message.includes("update --repair")),
|
|
184
|
+
"version --json must include actionable recommendations",
|
|
185
|
+
);
|
|
186
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
187
|
+
}
|
|
188
|
+
|
|
125
189
|
function runPublishTargetSelfTests() {
|
|
126
190
|
const unpublished = {
|
|
127
191
|
skipped: false,
|
|
@@ -344,6 +408,94 @@ function runSettingsRuntimeStateSelfTests() {
|
|
|
344
408
|
);
|
|
345
409
|
}
|
|
346
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
|
+
|
|
347
499
|
function runUpdatePlanSelfTests() {
|
|
348
500
|
assert(
|
|
349
501
|
classifyGitShort(" M settings.json\n?? tmp.txt").preservedRuntime.includes("settings.json"),
|
package/src/commands/update.mjs
CHANGED
|
@@ -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}`);
|
package/src/commands/version.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { captureCommand } from "../lib/shell-runner.mjs";
|
|
2
2
|
import { gitStatus } from "../lib/git.mjs";
|
|
3
3
|
import { currentTheme } from "../lib/theme-policy.mjs";
|
|
4
|
-
import { keyValue, printJson } from "../lib/output.mjs";
|
|
4
|
+
import { info, keyValue, printJson, section, warn } from "../lib/output.mjs";
|
|
5
5
|
import { platformName } from "../lib/platform.mjs";
|
|
6
6
|
import { readCliPackageJson, readTextIfExists } from "../lib/paths.mjs";
|
|
7
|
+
import { readJsonFileIfExists } from "../lib/config-json.mjs";
|
|
7
8
|
import { parseCommandOptions } from "../lib/args.mjs";
|
|
8
9
|
import path from "node:path";
|
|
9
10
|
|
|
@@ -17,6 +18,8 @@ export async function versionCommand(ctx, argv) {
|
|
|
17
18
|
const pkg = readCliPackageJson();
|
|
18
19
|
const git = gitStatus(ctx.repoRoot);
|
|
19
20
|
const pi = captureCommand("pi", ["--version"]);
|
|
21
|
+
const distroVersion = readTextIfExists(path.join(ctx.repoRoot, "VERSION")).trim();
|
|
22
|
+
const settings = readJsonFileIfExists(path.join(ctx.agentDir, "settings.json")) || {};
|
|
20
23
|
const data = {
|
|
21
24
|
schema: "pi67.version.v1",
|
|
22
25
|
manager: {
|
|
@@ -24,7 +27,7 @@ export async function versionCommand(ctx, argv) {
|
|
|
24
27
|
version: pkg.version,
|
|
25
28
|
},
|
|
26
29
|
distro: {
|
|
27
|
-
version:
|
|
30
|
+
version: distroVersion,
|
|
28
31
|
commit: git.commit || "",
|
|
29
32
|
branch: git.branch || "",
|
|
30
33
|
dirty: Boolean(git.dirty),
|
|
@@ -42,6 +45,7 @@ export async function versionCommand(ctx, argv) {
|
|
|
42
45
|
},
|
|
43
46
|
theme: currentTheme(ctx),
|
|
44
47
|
};
|
|
48
|
+
data.recommendations = buildVersionRecommendations(ctx, data, git, settings);
|
|
45
49
|
if (json) {
|
|
46
50
|
printJson(data);
|
|
47
51
|
return;
|
|
@@ -54,6 +58,79 @@ export async function versionCommand(ctx, argv) {
|
|
|
54
58
|
keyValue("platform", data.runtime.platform);
|
|
55
59
|
keyValue("agentDir", data.paths.agentDir);
|
|
56
60
|
keyValue("theme", data.theme || "unset");
|
|
61
|
+
printRecommendations(data.recommendations);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function buildVersionRecommendations(ctx, data, git, settings) {
|
|
65
|
+
const recommendations = [];
|
|
66
|
+
const managerVsDistro = compareSemver(data.manager.version, data.distro.version);
|
|
67
|
+
const settingsDirty = git.short
|
|
68
|
+
.split(/\r?\n/)
|
|
69
|
+
.some((line) => line.trim().endsWith("settings.json"));
|
|
70
|
+
const hasRuntimeMarker = settings && Object.prototype.hasOwnProperty.call(settings, "lastChangelogVersion");
|
|
71
|
+
const updateCommand = `pi-67 --agent-dir "${ctx.agentDir}" --repo-root "${ctx.repoRoot}" update --repair`;
|
|
72
|
+
|
|
73
|
+
if (managerVsDistro > 0) {
|
|
74
|
+
recommendations.push({
|
|
75
|
+
level: "WARN",
|
|
76
|
+
message: `manager is ${data.manager.version} but local distro is ${data.distro.version || "unknown"}; npm install updated only the manager package.`,
|
|
77
|
+
});
|
|
78
|
+
recommendations.push({
|
|
79
|
+
level: "INFO",
|
|
80
|
+
message: `Run: ${updateCommand}`,
|
|
81
|
+
});
|
|
82
|
+
} else if (managerVsDistro < 0) {
|
|
83
|
+
recommendations.push({
|
|
84
|
+
level: "WARN",
|
|
85
|
+
message: `local distro ${data.distro.version} is newer than manager ${data.manager.version}; update the manager with npm install -g ${data.manager.package}@latest.`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (settingsDirty && hasRuntimeMarker) {
|
|
90
|
+
recommendations.push({
|
|
91
|
+
level: "INFO",
|
|
92
|
+
message: "settings.json has Pi runtime changelog marker state; update --repair migrates it into ~/.pi/pi67/state.json and normalizes settings.json.",
|
|
93
|
+
});
|
|
94
|
+
} else if (settingsDirty) {
|
|
95
|
+
recommendations.push({
|
|
96
|
+
level: "INFO",
|
|
97
|
+
message: "settings.json is dirty; inspect with git diff -- settings.json before updating if it contains personal provider/model/theme edits.",
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!data.runtime.pi) {
|
|
102
|
+
recommendations.push({
|
|
103
|
+
level: "INFO",
|
|
104
|
+
message: "`pi` is not on PATH in this shell; this does not block pi-67 manager updates.",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return recommendations;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function printRecommendations(recommendations) {
|
|
111
|
+
if (!recommendations?.length) return;
|
|
112
|
+
section("Next steps");
|
|
113
|
+
for (const item of recommendations) {
|
|
114
|
+
if (item.level === "WARN") warn(item.message);
|
|
115
|
+
else info(item.message);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function compareSemver(left, right) {
|
|
120
|
+
const a = parseSemver(left);
|
|
121
|
+
const b = parseSemver(right);
|
|
122
|
+
if (!a || !b) return 0;
|
|
123
|
+
for (let index = 0; index < 3; index += 1) {
|
|
124
|
+
if (a[index] > b[index]) return 1;
|
|
125
|
+
if (a[index] < b[index]) return -1;
|
|
126
|
+
}
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function parseSemver(value) {
|
|
131
|
+
const match = String(value || "").trim().match(/^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);
|
|
132
|
+
if (!match) return null;
|
|
133
|
+
return match.slice(1, 4).map((item) => Number(item));
|
|
57
134
|
}
|
|
58
135
|
|
|
59
136
|
function printVersionHelp() {
|
|
@@ -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,
|