@bigking67/pi-67 0.10.3 → 0.10.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/CHANGELOG.md +44 -0
- package/README.md +17 -8
- package/package.json +1 -1
- package/scripts/check.mjs +96 -0
- package/src/cli.mjs +6 -3
- package/src/commands/backups.mjs +187 -1
- package/src/commands/doctor.mjs +30 -1
- package/src/commands/extensions.mjs +6 -1
- package/src/commands/external.mjs +28 -0
- package/src/commands/install.mjs +22 -0
- package/src/commands/manifest.mjs +21 -0
- package/src/commands/publish-check.mjs +24 -0
- package/src/commands/report.mjs +22 -0
- package/src/commands/self-update.mjs +19 -0
- package/src/commands/skills.mjs +83 -4
- package/src/commands/smoke.mjs +21 -0
- package/src/commands/status.mjs +20 -0
- package/src/commands/themes.mjs +29 -0
- package/src/commands/update.mjs +51 -2
- package/src/commands/version.mjs +22 -1
- package/src/commands/xtalpi.mjs +129 -2
- package/src/lib/args.mjs +7 -1
- package/src/lib/skill-policy.mjs +130 -3
- package/src/lib/update-plan.mjs +90 -6
- package/src/lib/update-safety.mjs +21 -1
package/src/commands/install.mjs
CHANGED
|
@@ -13,6 +13,10 @@ export async function installCommand(ctx, argv) {
|
|
|
13
13
|
strings: ["repo", "branch"],
|
|
14
14
|
bools: ["dry-run", "yes", "repair"],
|
|
15
15
|
});
|
|
16
|
+
if (options.help) {
|
|
17
|
+
printInstallHelp();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
16
20
|
const dryRun = ctx.dryRun || options.dryRun;
|
|
17
21
|
const repo = options.repo || DEFAULT_REPO_URL;
|
|
18
22
|
|
|
@@ -46,3 +50,21 @@ export async function installCommand(ctx, argv) {
|
|
|
46
50
|
if (!dryRun) writeState(ctx, "install");
|
|
47
51
|
info("Install finished. Run `pi-67 doctor` next.");
|
|
48
52
|
}
|
|
53
|
+
|
|
54
|
+
function printInstallHelp() {
|
|
55
|
+
process.stdout.write(`pi-67 install - clone/install pi-67 safely
|
|
56
|
+
|
|
57
|
+
Usage:
|
|
58
|
+
pi-67 install [--repo URL] [--branch NAME] [--dry-run] [--repair]
|
|
59
|
+
|
|
60
|
+
Options:
|
|
61
|
+
--repo URL Git repository URL. Defaults to the pi-67 upstream repo.
|
|
62
|
+
--branch NAME Clone a specific branch.
|
|
63
|
+
--dry-run Print planned writes without changing files.
|
|
64
|
+
--repair Force owned asset repair during the installer update phase.
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
pi-67 install
|
|
68
|
+
pi-67 install --dry-run
|
|
69
|
+
`);
|
|
70
|
+
}
|
|
@@ -7,6 +7,10 @@ export async function manifestCommand(ctx, argv) {
|
|
|
7
7
|
const { options } = parseCommandOptions(argv, {
|
|
8
8
|
bools: ["json", "validate"],
|
|
9
9
|
});
|
|
10
|
+
if (options.help) {
|
|
11
|
+
printManifestHelp();
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
10
14
|
const manifest = buildDistroManifest(ctx);
|
|
11
15
|
const validation = validateExtensionRegistry(manifest.extensionRegistry, { manifest });
|
|
12
16
|
if (ctx.json || options.json) {
|
|
@@ -72,3 +76,20 @@ function printValidation(validation) {
|
|
|
72
76
|
}
|
|
73
77
|
for (const warning of validation.warnings) warn(warning);
|
|
74
78
|
}
|
|
79
|
+
|
|
80
|
+
function printManifestHelp() {
|
|
81
|
+
process.stdout.write(`pi-67 manifest - show managed ownership policy
|
|
82
|
+
|
|
83
|
+
Usage:
|
|
84
|
+
pi-67 manifest [--json]
|
|
85
|
+
pi-67 manifest --validate [--json]
|
|
86
|
+
|
|
87
|
+
Options:
|
|
88
|
+
--validate Validate the extension registry against the distro manifest.
|
|
89
|
+
--json Emit machine-readable manifest or validation JSON.
|
|
90
|
+
|
|
91
|
+
Examples:
|
|
92
|
+
pi-67 manifest
|
|
93
|
+
pi-67 manifest --validate
|
|
94
|
+
`);
|
|
95
|
+
}
|
|
@@ -13,6 +13,10 @@ export async function publishCheckCommand(ctx, argv) {
|
|
|
13
13
|
const { options } = parseCommandOptions(argv, {
|
|
14
14
|
bools: ["json", "no-remote", "no-pack", "quiet", "strict", "allow-first-publish"],
|
|
15
15
|
});
|
|
16
|
+
if (options.help) {
|
|
17
|
+
printPublishCheckHelp();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
16
20
|
const json = ctx.json || options.json;
|
|
17
21
|
const quiet = options.quiet;
|
|
18
22
|
const noRemote = ctx.noRemote || options.noRemote;
|
|
@@ -246,6 +250,26 @@ function workflowCheck(file) {
|
|
|
246
250
|
};
|
|
247
251
|
}
|
|
248
252
|
|
|
253
|
+
function printPublishCheckHelp() {
|
|
254
|
+
process.stdout.write(`pi-67 publish-check - verify npm publish readiness
|
|
255
|
+
|
|
256
|
+
Usage:
|
|
257
|
+
pi-67 publish-check [--strict] [--json] [--no-remote] [--no-pack]
|
|
258
|
+
|
|
259
|
+
Options:
|
|
260
|
+
--strict Exit non-zero when blockers are present.
|
|
261
|
+
--json Emit machine-readable publish readiness JSON.
|
|
262
|
+
--no-remote Skip npm registry/scope/auth remote probes.
|
|
263
|
+
--no-pack Skip npm pack dry-run.
|
|
264
|
+
--quiet Suppress human output; status is in the exit code.
|
|
265
|
+
--allow-first-publish Explicitly allow first-publish local gate probing.
|
|
266
|
+
|
|
267
|
+
Examples:
|
|
268
|
+
pi-67 publish-check
|
|
269
|
+
pi-67 publish-check --strict --no-pack
|
|
270
|
+
`);
|
|
271
|
+
}
|
|
272
|
+
|
|
249
273
|
function npmAuthCheck(options) {
|
|
250
274
|
if (options.noRemote) return skipped("remote auth check skipped");
|
|
251
275
|
const result = captureCommand("npm", ["whoami"], { timeoutMs: 10000 });
|
package/src/commands/report.mjs
CHANGED
|
@@ -7,6 +7,10 @@ export async function reportCommand(ctx, argv) {
|
|
|
7
7
|
bools: ["dry-run", "no-doctor"],
|
|
8
8
|
strings: ["operation", "output"],
|
|
9
9
|
});
|
|
10
|
+
if (options.help) {
|
|
11
|
+
printReportHelp();
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
10
14
|
const args = isWindows()
|
|
11
15
|
? ["-AgentDir", ctx.agentDir, "-RepoRoot", ctx.repoRoot, "-SkillsDir", ctx.skillsDir]
|
|
12
16
|
: ["--agent-dir", ctx.agentDir, "--repo-root", ctx.repoRoot, "--skills-dir", ctx.skillsDir];
|
|
@@ -18,3 +22,21 @@ export async function reportCommand(ctx, argv) {
|
|
|
18
22
|
dryRun: false,
|
|
19
23
|
});
|
|
20
24
|
}
|
|
25
|
+
|
|
26
|
+
function printReportHelp() {
|
|
27
|
+
process.stdout.write(`pi-67 report - generate pi67-report.json
|
|
28
|
+
|
|
29
|
+
Usage:
|
|
30
|
+
pi-67 report [--operation NAME] [--output FILE] [--no-doctor] [--dry-run]
|
|
31
|
+
|
|
32
|
+
Options:
|
|
33
|
+
--operation NAME Operation label to embed in the report.
|
|
34
|
+
--output FILE Output path. Defaults to the distro report location.
|
|
35
|
+
--no-doctor Skip doctor data collection where supported.
|
|
36
|
+
--dry-run Print the script invocation without running it.
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
pi-67 report
|
|
40
|
+
pi-67 report --operation update
|
|
41
|
+
`);
|
|
42
|
+
}
|
|
@@ -5,6 +5,10 @@ import { info, pass } from "../lib/output.mjs";
|
|
|
5
5
|
|
|
6
6
|
export async function selfUpdateCommand(ctx, argv) {
|
|
7
7
|
const { options } = parseCommandOptions(argv, { bools: ["dry-run"] });
|
|
8
|
+
if (options.help) {
|
|
9
|
+
printSelfUpdateHelp();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
8
12
|
const pkg = readCliPackageJson();
|
|
9
13
|
const dryRun = ctx.dryRun || options.dryRun;
|
|
10
14
|
runCommand("npm", ["install", "-g", `${pkg.name}@latest`], { dryRun });
|
|
@@ -14,3 +18,18 @@ export async function selfUpdateCommand(ctx, argv) {
|
|
|
14
18
|
}
|
|
15
19
|
pass(`${pkg.name} manager updated. Run \`pi-67 version\` to verify the active PATH entry.`);
|
|
16
20
|
}
|
|
21
|
+
|
|
22
|
+
function printSelfUpdateHelp() {
|
|
23
|
+
process.stdout.write(`pi-67 self-update - update the npm manager package
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
pi-67 self-update [--dry-run]
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
--dry-run Print the npm install command without running it.
|
|
30
|
+
|
|
31
|
+
Examples:
|
|
32
|
+
pi-67 self-update --dry-run
|
|
33
|
+
pi-67 self-update
|
|
34
|
+
`);
|
|
35
|
+
}
|
package/src/commands/skills.mjs
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { parseCommandOptions } from "../lib/args.mjs";
|
|
2
|
-
import { inventorySkills, syncSkills } from "../lib/skill-policy.mjs";
|
|
3
|
-
import { CliError, info, keyValue, printJson, section, warn } from "../lib/output.mjs";
|
|
2
|
+
import { diffSkill, inventorySkills, planSkills, syncSkills } from "../lib/skill-policy.mjs";
|
|
3
|
+
import { CliError, info, keyValue, pass, printJson, section, warn } from "../lib/output.mjs";
|
|
4
4
|
|
|
5
5
|
export async function skillsCommand(ctx, argv) {
|
|
6
6
|
const [sub = "inventory", ...rest] = argv;
|
|
7
|
+
if (sub === "-h" || sub === "--help" || sub === "help") {
|
|
8
|
+
printSkillsHelp();
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
7
11
|
if (sub === "inventory") return inventory(ctx, rest);
|
|
12
|
+
if (sub === "plan") return plan(ctx, rest);
|
|
13
|
+
if (sub === "diff") return diff(ctx, rest);
|
|
8
14
|
if (sub === "sync") return sync(ctx, rest);
|
|
9
15
|
if (sub === "migrate") return migrate(ctx, rest);
|
|
10
16
|
throw new CliError(`unknown skills command: ${sub}`, 2);
|
|
@@ -12,6 +18,7 @@ export async function skillsCommand(ctx, argv) {
|
|
|
12
18
|
|
|
13
19
|
function inventory(ctx, argv) {
|
|
14
20
|
const { options } = parseCommandOptions(argv, { bools: ["json"] });
|
|
21
|
+
if (options.help) return printSkillsHelp();
|
|
15
22
|
const data = inventorySkills(ctx);
|
|
16
23
|
if (ctx.json || options.json) return printJson(data);
|
|
17
24
|
section("pi-67 shared skills inventory");
|
|
@@ -26,9 +33,56 @@ function inventory(ctx, argv) {
|
|
|
26
33
|
}
|
|
27
34
|
}
|
|
28
35
|
|
|
36
|
+
function plan(ctx, argv) {
|
|
37
|
+
const { options, positionals } = parseCommandOptions(argv, { bools: ["json"] });
|
|
38
|
+
if (options.help) return printSkillsHelp();
|
|
39
|
+
const data = planSkills(ctx, { names: positionals });
|
|
40
|
+
if (ctx.json || options.json) return printJson(data);
|
|
41
|
+
section("pi-67 shared skills plan");
|
|
42
|
+
keyValue("Source", data.sourceRoot);
|
|
43
|
+
keyValue("Target", data.skillsDir);
|
|
44
|
+
keyValue("Selected", data.selected.length === 0 ? "all changed skills" : data.selected.join(", "));
|
|
45
|
+
keyValue("Missing", data.actions.filter((item) => item.action === "copy-missing").length);
|
|
46
|
+
keyValue("Conflicts", data.actions.filter((item) => item.action === "preserve-conflict").length);
|
|
47
|
+
for (const action of data.actions) {
|
|
48
|
+
const command = action.conflict
|
|
49
|
+
? `inspect: pi-67 skills diff ${action.name}; explicit sync: pi-67 skills sync ${action.name} --dry-run`
|
|
50
|
+
: `sync: pi-67 skills sync ${action.name}`;
|
|
51
|
+
info(`${action.name}: ${action.action}; ${command}`);
|
|
52
|
+
}
|
|
53
|
+
if (data.actions.length === 0) pass("no shared skill action is required");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function diff(ctx, argv) {
|
|
57
|
+
const { options, positionals } = parseCommandOptions(argv, { bools: ["json"] });
|
|
58
|
+
if (options.help) return printSkillsHelp();
|
|
59
|
+
const name = positionals[0];
|
|
60
|
+
if (!name) throw new CliError("skills diff requires a skill name", 2);
|
|
61
|
+
const data = diffSkill(ctx, name);
|
|
62
|
+
if (ctx.json || options.json) return printJson(data);
|
|
63
|
+
section(`pi-67 shared skill diff: ${name}`);
|
|
64
|
+
keyValue("Source", data.source);
|
|
65
|
+
keyValue("Target", data.target);
|
|
66
|
+
keyValue("Target exists", data.targetExists ? "yes" : "no");
|
|
67
|
+
keyValue("Identical", data.identical ? "yes" : "no");
|
|
68
|
+
keyValue("Added files", data.diff.added.length);
|
|
69
|
+
keyValue("Removed files", data.diff.removed.length);
|
|
70
|
+
keyValue("Modified files", data.diff.modified.length);
|
|
71
|
+
for (const file of data.diff.added.slice(0, 20)) info(`added in source: ${file}`);
|
|
72
|
+
for (const file of data.diff.removed.slice(0, 20)) warn(`missing from source: ${file}`);
|
|
73
|
+
for (const file of data.diff.modified.slice(0, 20)) info(`modified: ${file}`);
|
|
74
|
+
const omitted = data.diff.added.length + data.diff.removed.length + data.diff.modified.length - 60;
|
|
75
|
+
if (omitted > 0) warn(`${omitted} additional file diffs omitted; rerun with --json for full metadata`);
|
|
76
|
+
}
|
|
77
|
+
|
|
29
78
|
function sync(ctx, argv) {
|
|
30
|
-
const { options } = parseCommandOptions(argv, { bools: ["json", "dry-run"] });
|
|
31
|
-
|
|
79
|
+
const { options, positionals } = parseCommandOptions(argv, { bools: ["json", "dry-run", "yes"] });
|
|
80
|
+
if (options.help) return printSkillsHelp();
|
|
81
|
+
const data = syncSkills(ctx, {
|
|
82
|
+
dryRun: ctx.dryRun || options.dryRun,
|
|
83
|
+
names: positionals,
|
|
84
|
+
yes: ctx.yes || options.yes,
|
|
85
|
+
});
|
|
32
86
|
if (ctx.json || options.json) return printJson(data);
|
|
33
87
|
section("pi-67 shared skills sync");
|
|
34
88
|
for (const action of data.actions) {
|
|
@@ -39,6 +93,7 @@ function sync(ctx, argv) {
|
|
|
39
93
|
|
|
40
94
|
function migrate(ctx, argv) {
|
|
41
95
|
const { options } = parseCommandOptions(argv, { bools: ["dry-run", "json"] });
|
|
96
|
+
if (options.help) return printSkillsHelp();
|
|
42
97
|
const data = syncSkills(ctx, { dryRun: true });
|
|
43
98
|
data.schema = "pi67.skills-migrate-preview.v1";
|
|
44
99
|
if (ctx.json || options.json) return printJson(data);
|
|
@@ -47,3 +102,27 @@ function migrate(ctx, argv) {
|
|
|
47
102
|
keyValue("Missing", data.summary.missing);
|
|
48
103
|
keyValue("Conflicts", data.summary.conflicts);
|
|
49
104
|
}
|
|
105
|
+
|
|
106
|
+
function printSkillsHelp() {
|
|
107
|
+
process.stdout.write(`pi-67 skills - inspect and safely sync shared skills
|
|
108
|
+
|
|
109
|
+
Usage:
|
|
110
|
+
pi-67 skills inventory [--json]
|
|
111
|
+
pi-67 skills plan [skill...] [--json]
|
|
112
|
+
pi-67 skills diff <skill> [--json]
|
|
113
|
+
pi-67 skills sync [skill...] [--dry-run] [--yes] [--json]
|
|
114
|
+
pi-67 skills migrate [--dry-run] [--json]
|
|
115
|
+
|
|
116
|
+
Safety:
|
|
117
|
+
Missing skills are copied by default. Existing different global skills are
|
|
118
|
+
preserved unless you name the skill explicitly and pass --yes. Bulk conflict
|
|
119
|
+
overwrite is intentionally blocked.
|
|
120
|
+
|
|
121
|
+
Examples:
|
|
122
|
+
pi-67 skills inventory
|
|
123
|
+
pi-67 skills plan
|
|
124
|
+
pi-67 skills diff lark-doc
|
|
125
|
+
pi-67 skills sync lark-doc --dry-run
|
|
126
|
+
pi-67 skills sync lark-doc --yes
|
|
127
|
+
`);
|
|
128
|
+
}
|
package/src/commands/smoke.mjs
CHANGED
|
@@ -6,6 +6,10 @@ export async function smokeCommand(ctx, argv) {
|
|
|
6
6
|
const { options } = parseCommandOptions(argv, {
|
|
7
7
|
bools: ["ci", "quick", "dry-run"],
|
|
8
8
|
});
|
|
9
|
+
if (options.help) {
|
|
10
|
+
printSmokeHelp();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
9
13
|
const args = [];
|
|
10
14
|
if (isWindows()) {
|
|
11
15
|
if (options.ci || options.quick) args.push("-Ci");
|
|
@@ -16,3 +20,20 @@ export async function smokeCommand(ctx, argv) {
|
|
|
16
20
|
dryRun: ctx.dryRun || options.dryRun,
|
|
17
21
|
});
|
|
18
22
|
}
|
|
23
|
+
|
|
24
|
+
function printSmokeHelp() {
|
|
25
|
+
process.stdout.write(`pi-67 smoke - run repository smoke gates
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
pi-67 smoke [--ci|--quick] [--dry-run]
|
|
29
|
+
|
|
30
|
+
Options:
|
|
31
|
+
--ci Run the CI smoke profile.
|
|
32
|
+
--quick Alias for the CI smoke profile.
|
|
33
|
+
--dry-run Print the script invocation without running it.
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
pi-67 smoke
|
|
37
|
+
pi-67 smoke --ci
|
|
38
|
+
`);
|
|
39
|
+
}
|
package/src/commands/status.mjs
CHANGED
|
@@ -6,6 +6,10 @@ export async function statusCommand(ctx, argv) {
|
|
|
6
6
|
const { options } = parseCommandOptions(argv, {
|
|
7
7
|
bools: ["json", "no-remote"],
|
|
8
8
|
});
|
|
9
|
+
if (options.help) {
|
|
10
|
+
printStatusHelp();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
9
13
|
const plan = await buildUpdatePlan(ctx, { noRemote: ctx.noRemote || options.noRemote });
|
|
10
14
|
if (ctx.json || options.json) {
|
|
11
15
|
printJson(plan);
|
|
@@ -19,3 +23,19 @@ export async function statusCommand(ctx, argv) {
|
|
|
19
23
|
keyValue("Theme", plan.settings.theme || "unset");
|
|
20
24
|
keyValue("Shared skills", `${plan.skills.identical} ok, ${plan.skills.missing} missing, ${plan.skills.conflicts} conflicts`);
|
|
21
25
|
}
|
|
26
|
+
|
|
27
|
+
function printStatusHelp() {
|
|
28
|
+
process.stdout.write(`pi-67 status - read-only status summary
|
|
29
|
+
|
|
30
|
+
Usage:
|
|
31
|
+
pi-67 status [--json] [--no-remote]
|
|
32
|
+
|
|
33
|
+
Options:
|
|
34
|
+
--json Emit the full update-plan status JSON.
|
|
35
|
+
--no-remote Skip remote git/npm registry checks.
|
|
36
|
+
|
|
37
|
+
Examples:
|
|
38
|
+
pi-67 status
|
|
39
|
+
pi-67 status --json --no-remote
|
|
40
|
+
`);
|
|
41
|
+
}
|
package/src/commands/themes.mjs
CHANGED
|
@@ -7,6 +7,10 @@ import { CliError, keyValue, printJson, section, pass, fail, info } from "../lib
|
|
|
7
7
|
|
|
8
8
|
export async function themesCommand(ctx, argv) {
|
|
9
9
|
const [sub = "current", ...rest] = argv;
|
|
10
|
+
if (sub === "-h" || sub === "--help" || sub === "help") {
|
|
11
|
+
printThemesHelp();
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
10
14
|
if (sub === "current") return current(ctx, rest);
|
|
11
15
|
if (sub === "list") return list(ctx, rest);
|
|
12
16
|
if (sub === "doctor") return doctor(ctx, rest);
|
|
@@ -16,6 +20,7 @@ export async function themesCommand(ctx, argv) {
|
|
|
16
20
|
|
|
17
21
|
function current(ctx, argv) {
|
|
18
22
|
const { options } = parseCommandOptions(argv, { bools: ["json"] });
|
|
23
|
+
if (options.help) return printThemesHelp();
|
|
19
24
|
const value = currentTheme(ctx);
|
|
20
25
|
const data = {
|
|
21
26
|
schema: "pi67.theme-current.v1",
|
|
@@ -29,6 +34,7 @@ function current(ctx, argv) {
|
|
|
29
34
|
|
|
30
35
|
function list(ctx, argv) {
|
|
31
36
|
const { options } = parseCommandOptions(argv, { bools: ["json"] });
|
|
37
|
+
if (options.help) return printThemesHelp();
|
|
32
38
|
const themes = listThemes(ctx);
|
|
33
39
|
if (ctx.json || options.json) return printJson({ schema: "pi67.theme-list.v1", themes });
|
|
34
40
|
section("Available themes");
|
|
@@ -37,6 +43,7 @@ function list(ctx, argv) {
|
|
|
37
43
|
|
|
38
44
|
function doctor(ctx, argv) {
|
|
39
45
|
const { options } = parseCommandOptions(argv, { bools: ["json"] });
|
|
46
|
+
if (options.help) return printThemesHelp();
|
|
40
47
|
const theme = currentTheme(ctx);
|
|
41
48
|
const installed = theme ? hasTheme(ctx, theme) : false;
|
|
42
49
|
const data = { schema: "pi67.theme-doctor.v1", theme, installed };
|
|
@@ -48,6 +55,7 @@ function doctor(ctx, argv) {
|
|
|
48
55
|
|
|
49
56
|
function setTheme(ctx, argv) {
|
|
50
57
|
const { options, positionals } = parseCommandOptions(argv, { bools: ["force", "dry-run"] });
|
|
58
|
+
if (options.help) return printThemesHelp();
|
|
51
59
|
const name = positionals[0];
|
|
52
60
|
if (!name) throw new CliError("themes set requires a theme name", 2);
|
|
53
61
|
if (!options.force && !hasTheme(ctx, name)) {
|
|
@@ -71,3 +79,24 @@ function setTheme(ctx, argv) {
|
|
|
71
79
|
pass(`theme set: ${previous || "unset"} -> ${name}`);
|
|
72
80
|
info(`Preserved runtime backup: ${backupDir}`);
|
|
73
81
|
}
|
|
82
|
+
|
|
83
|
+
function printThemesHelp() {
|
|
84
|
+
process.stdout.write(`pi-67 themes - inspect and explicitly set themes
|
|
85
|
+
|
|
86
|
+
Usage:
|
|
87
|
+
pi-67 themes current [--json]
|
|
88
|
+
pi-67 themes list [--json]
|
|
89
|
+
pi-67 themes doctor [--json]
|
|
90
|
+
pi-67 themes set <name> [--force] [--dry-run]
|
|
91
|
+
|
|
92
|
+
Safety:
|
|
93
|
+
Updates install theme assets but never change the selected theme. Only
|
|
94
|
+
\`pi-67 themes set <name>\` changes settings.json, and it writes a runtime
|
|
95
|
+
backup first unless --dry-run is used.
|
|
96
|
+
|
|
97
|
+
Examples:
|
|
98
|
+
pi-67 themes current
|
|
99
|
+
pi-67 themes list
|
|
100
|
+
pi-67 themes set gruvbox-dark --dry-run
|
|
101
|
+
`);
|
|
102
|
+
}
|
package/src/commands/update.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parseCommandOptions } from "../lib/args.mjs";
|
|
2
2
|
import { buildUpdatePlan } from "../lib/update-plan.mjs";
|
|
3
|
-
import { printJson, section, keyValue, pass, warn, info } from "../lib/output.mjs";
|
|
3
|
+
import { CliError, printJson, section, keyValue, pass, warn, info } from "../lib/output.mjs";
|
|
4
4
|
import { runDistroScript } from "../lib/distro-scripts.mjs";
|
|
5
5
|
import { isWindows } from "../lib/platform.mjs";
|
|
6
6
|
import { runCommand } from "../lib/shell-runner.mjs";
|
|
@@ -24,6 +24,10 @@ export async function updateCommand(ctx, argv) {
|
|
|
24
24
|
"strict-shared-skills",
|
|
25
25
|
],
|
|
26
26
|
});
|
|
27
|
+
if (options.help) {
|
|
28
|
+
printUpdateHelp();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
27
31
|
const dryRun = ctx.dryRun || options.dryRun;
|
|
28
32
|
const json = ctx.json || options.json;
|
|
29
33
|
if (options.check) {
|
|
@@ -43,10 +47,12 @@ export async function updateCommand(ctx, argv) {
|
|
|
43
47
|
noRemote: ctx.noRemote || options.noRemote,
|
|
44
48
|
strictSharedSkills: options.strictSharedSkills,
|
|
45
49
|
});
|
|
50
|
+
assertPlanCanProceed(plan, { allowDirty: options.allowDirty });
|
|
46
51
|
const lifecycle = beginUpdateLifecycle(ctx, {
|
|
47
52
|
operation: options.repair ? "repair" : "update",
|
|
48
53
|
dryRun,
|
|
49
54
|
plan,
|
|
55
|
+
backupRuntime: false,
|
|
50
56
|
});
|
|
51
57
|
if (!dryRun && lifecycle.backedUp.length > 0) {
|
|
52
58
|
info(`Preserved runtime backup: ${lifecycle.backupDir}`);
|
|
@@ -86,6 +92,12 @@ function buildBashUpdateArgs(ctx, options, dryRun) {
|
|
|
86
92
|
return args;
|
|
87
93
|
}
|
|
88
94
|
|
|
95
|
+
function assertPlanCanProceed(plan, options = {}) {
|
|
96
|
+
if (options.allowDirty || !plan.blocked?.length) return;
|
|
97
|
+
for (const item of plan.blocked) warn(`${item.id}: ${item.reason}`);
|
|
98
|
+
throw new CliError("pi-67 update is blocked; run `pi-67 update --check` for the full plan or rerun with --allow-dirty if you accept the risk", 2);
|
|
99
|
+
}
|
|
100
|
+
|
|
89
101
|
function buildWindowsUpdateArgs(ctx, options, dryRun) {
|
|
90
102
|
const args = ["-AgentDir", ctx.agentDir, "-RepoRoot", ctx.repoRoot, "-SkillsDir", ctx.skillsDir];
|
|
91
103
|
if (dryRun) args.push("-DryRun");
|
|
@@ -123,7 +135,9 @@ function printPlan(plan) {
|
|
|
123
135
|
if (plan.actions?.length > 0) {
|
|
124
136
|
section("Planned safe actions");
|
|
125
137
|
for (const action of plan.actions) {
|
|
126
|
-
|
|
138
|
+
const writes = action.writes?.length ? action.writes.join(", ") : "none";
|
|
139
|
+
const suffix = action.backupCondition ? `; backup=${action.backupCondition}` : "";
|
|
140
|
+
info(`${action.id}: ${action.operation}; writes=${writes}; preserves=${action.preserves.join(", ")}${suffix}`);
|
|
127
141
|
}
|
|
128
142
|
}
|
|
129
143
|
if (plan.blocked?.length > 0) {
|
|
@@ -138,3 +152,38 @@ function printPlan(plan) {
|
|
|
138
152
|
for (const item of plan.recommendations) info(item);
|
|
139
153
|
if (!plan.git?.dirty) pass("update check completed without local dirty blocker");
|
|
140
154
|
}
|
|
155
|
+
|
|
156
|
+
function printUpdateHelp() {
|
|
157
|
+
process.stdout.write(`pi-67 update - update pi-67 safely
|
|
158
|
+
|
|
159
|
+
Usage:
|
|
160
|
+
pi-67 update [--repair] [--dry-run] [--no-remote] [--no-npm]
|
|
161
|
+
pi-67 update --check [--json] [--no-remote]
|
|
162
|
+
|
|
163
|
+
Options:
|
|
164
|
+
--check Print the read-only update plan and exit.
|
|
165
|
+
--repair Re-run owned asset/config repair during update.
|
|
166
|
+
--dry-run Print planned script actions without changing files.
|
|
167
|
+
--no-remote Skip remote git/npm registry checks where supported.
|
|
168
|
+
--no-npm Skip npm package sync in the distro updater.
|
|
169
|
+
--allow-dirty Let the script-level updater handle non-runtime dirty files.
|
|
170
|
+
--strict-shared-skills Treat differing shared skills as blocking.
|
|
171
|
+
--include-pi Also run upstream \`pi update --all\` when paired with --yes.
|
|
172
|
+
--include-external Report explicit external repo update commands.
|
|
173
|
+
--all Alias for --include-pi --include-external.
|
|
174
|
+
--yes Confirm explicit opt-in actions.
|
|
175
|
+
--json Emit JSON for --check.
|
|
176
|
+
|
|
177
|
+
Safety:
|
|
178
|
+
Runtime config backup/restore is owned by the platform updater script when
|
|
179
|
+
dirty preserved runtime files overlap incoming changed paths. The npm manager
|
|
180
|
+
owns the update lock and never creates a runtime backup for --help, a blocked
|
|
181
|
+
update plan, or an already-up-to-date update.
|
|
182
|
+
|
|
183
|
+
Examples:
|
|
184
|
+
pi-67 update --check
|
|
185
|
+
pi-67 update --check --json --no-remote
|
|
186
|
+
pi-67 update
|
|
187
|
+
pi-67 update --repair
|
|
188
|
+
`);
|
|
189
|
+
}
|
package/src/commands/version.mjs
CHANGED
|
@@ -4,10 +4,16 @@ import { currentTheme } from "../lib/theme-policy.mjs";
|
|
|
4
4
|
import { keyValue, printJson } from "../lib/output.mjs";
|
|
5
5
|
import { platformName } from "../lib/platform.mjs";
|
|
6
6
|
import { readCliPackageJson, readTextIfExists } from "../lib/paths.mjs";
|
|
7
|
+
import { parseCommandOptions } from "../lib/args.mjs";
|
|
7
8
|
import path from "node:path";
|
|
8
9
|
|
|
9
10
|
export async function versionCommand(ctx, argv) {
|
|
10
|
-
const
|
|
11
|
+
const { options } = parseCommandOptions(argv, { bools: ["json"] });
|
|
12
|
+
if (options.help) {
|
|
13
|
+
printVersionHelp();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const json = ctx.json || options.json;
|
|
11
17
|
const pkg = readCliPackageJson();
|
|
12
18
|
const git = gitStatus(ctx.repoRoot);
|
|
13
19
|
const pi = captureCommand("pi", ["--version"]);
|
|
@@ -49,3 +55,18 @@ export async function versionCommand(ctx, argv) {
|
|
|
49
55
|
keyValue("agentDir", data.paths.agentDir);
|
|
50
56
|
keyValue("theme", data.theme || "unset");
|
|
51
57
|
}
|
|
58
|
+
|
|
59
|
+
function printVersionHelp() {
|
|
60
|
+
process.stdout.write(`pi-67 version - print manager and distro versions
|
|
61
|
+
|
|
62
|
+
Usage:
|
|
63
|
+
pi-67 version [--json]
|
|
64
|
+
|
|
65
|
+
Options:
|
|
66
|
+
--json Emit machine-readable version metadata.
|
|
67
|
+
|
|
68
|
+
Examples:
|
|
69
|
+
pi-67 version
|
|
70
|
+
pi-67 version --json
|
|
71
|
+
`);
|
|
72
|
+
}
|