@bigking67/pi-67 0.10.7 → 0.10.8
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 +6 -0
- package/package.json +1 -1
- package/scripts/check.mjs +63 -0
- package/src/commands/version.mjs +79 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.10.8]
|
|
4
|
+
|
|
5
|
+
- Adds actionable `pi-67 version` recommendations when the npm manager has
|
|
6
|
+
been updated but the local distro checkout is still old or `settings.json`
|
|
7
|
+
still has runtime-marker dirty state.
|
|
8
|
+
|
|
3
9
|
## [0.10.7]
|
|
4
10
|
|
|
5
11
|
- Migrates `settings.json.lastChangelogVersion` into ignored manager state at
|
package/package.json
CHANGED
package/scripts/check.mjs
CHANGED
|
@@ -37,6 +37,7 @@ 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();
|
|
@@ -122,6 +123,68 @@ function runCliHelpContractSelfTests() {
|
|
|
122
123
|
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
123
124
|
}
|
|
124
125
|
|
|
126
|
+
function runVersionRecommendationSelfTests() {
|
|
127
|
+
if (spawnSync("git", ["--version"], { encoding: "utf8" }).status !== 0) return;
|
|
128
|
+
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "pi67-version-recommendation-"));
|
|
129
|
+
const home = path.join(tmpRoot, "home");
|
|
130
|
+
const repo = path.join(tmpRoot, "agent");
|
|
131
|
+
fs.mkdirSync(home, { recursive: true });
|
|
132
|
+
fs.mkdirSync(repo, { recursive: true });
|
|
133
|
+
fs.writeFileSync(path.join(repo, "VERSION"), "0.10.0\n");
|
|
134
|
+
fs.writeFileSync(path.join(repo, "settings.json"), "{\n \"lastChangelogVersion\": \"0.80.3\",\n \"theme\": \"gruvbox-dark\"\n}\n");
|
|
135
|
+
for (const args of [
|
|
136
|
+
["-C", repo, "init", "-q"],
|
|
137
|
+
["-C", repo, "config", "user.email", "pi67-check@example.invalid"],
|
|
138
|
+
["-C", repo, "config", "user.name", "pi67-check"],
|
|
139
|
+
["-C", repo, "add", "VERSION", "settings.json"],
|
|
140
|
+
["-C", repo, "commit", "-q", "-m", "init"],
|
|
141
|
+
]) {
|
|
142
|
+
const result = spawnSync("git", args, { encoding: "utf8" });
|
|
143
|
+
assert(result.status === 0, `git setup failed for version recommendation self-test: git ${args.join(" ")}\n${result.stderr}`);
|
|
144
|
+
}
|
|
145
|
+
fs.writeFileSync(path.join(repo, "settings.json"), "{\n \"lastChangelogVersion\": \"0.80.4\",\n \"theme\": \"gruvbox-dark\"\n}\n");
|
|
146
|
+
const env = { ...process.env, HOME: home, USERPROFILE: home };
|
|
147
|
+
const result = spawnSync(process.execPath, [
|
|
148
|
+
path.join(root, "bin", "pi-67.mjs"),
|
|
149
|
+
"--agent-dir",
|
|
150
|
+
repo,
|
|
151
|
+
"--repo-root",
|
|
152
|
+
repo,
|
|
153
|
+
"version",
|
|
154
|
+
], {
|
|
155
|
+
cwd: root,
|
|
156
|
+
env,
|
|
157
|
+
encoding: "utf8",
|
|
158
|
+
});
|
|
159
|
+
assert(result.status === 0, `version recommendation command failed\n${result.stderr || result.stdout}`);
|
|
160
|
+
assert(
|
|
161
|
+
result.stdout.includes("npm install updated only the manager package") &&
|
|
162
|
+
result.stdout.includes("update --repair") &&
|
|
163
|
+
result.stdout.includes("settings.json has Pi runtime changelog marker state"),
|
|
164
|
+
`version output must explain manager/distro mismatch and runtime marker repair\n${result.stdout}`,
|
|
165
|
+
);
|
|
166
|
+
const json = spawnSync(process.execPath, [
|
|
167
|
+
path.join(root, "bin", "pi-67.mjs"),
|
|
168
|
+
"--agent-dir",
|
|
169
|
+
repo,
|
|
170
|
+
"--repo-root",
|
|
171
|
+
repo,
|
|
172
|
+
"version",
|
|
173
|
+
"--json",
|
|
174
|
+
], {
|
|
175
|
+
cwd: root,
|
|
176
|
+
env,
|
|
177
|
+
encoding: "utf8",
|
|
178
|
+
});
|
|
179
|
+
assert(json.status === 0, `version --json recommendation command failed\n${json.stderr || json.stdout}`);
|
|
180
|
+
const parsed = JSON.parse(json.stdout);
|
|
181
|
+
assert(
|
|
182
|
+
parsed.recommendations?.some((item) => item.message.includes("update --repair")),
|
|
183
|
+
"version --json must include actionable recommendations",
|
|
184
|
+
);
|
|
185
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
186
|
+
}
|
|
187
|
+
|
|
125
188
|
function runPublishTargetSelfTests() {
|
|
126
189
|
const unpublished = {
|
|
127
190
|
skipped: false,
|
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() {
|