agrep-cli 0.4.2 → 0.4.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/bin.js +33 -13
- package/package.json +1 -1
- package/postinstall.js +87 -32
package/bin.js
CHANGED
|
@@ -13,13 +13,38 @@ const { version } = require("./package.json");
|
|
|
13
13
|
const SPEC = process.env.AGREP_PYPI_SPEC || `agrep==${version}`;
|
|
14
14
|
const args = process.argv.slice(2);
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
// uv/pipx run agrep out of a cache that a prune wipes while this package
|
|
17
|
+
// stays installed; the sentinel tracks this file instead (see teach.install_anchor).
|
|
18
|
+
const ANCHOR_ENV = "AGREP_INSTALL_ANCHOR";
|
|
19
|
+
|
|
20
|
+
function probe(cmd) {
|
|
21
|
+
const r = spawnSync(cmd, ["--version"], { encoding: "utf8", shell: false });
|
|
22
|
+
return !r.error && r.status === 0 ? String(r.stdout || "") : null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function uvVersion(text) {
|
|
26
|
+
const m = /\buv\s+(\d+)\.(\d+)\.(\d+)/.exec(text || "");
|
|
27
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// `--exclude-newer-package agrep=false` opts agrep out of a user's global
|
|
31
|
+
// exclude-newer pin; only uv >= 0.9.25 parses the `=false` form, older uv
|
|
32
|
+
// rejects the whole invocation, so the flag rides along only where it parses.
|
|
33
|
+
function excludeNewerOptOut(v) {
|
|
34
|
+
return v !== null && (v[0] > 0 || v[1] > 9 || (v[1] === 9 && v[2] >= 25));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function uvArgs(uvVersionText, rest) {
|
|
38
|
+
const flag = excludeNewerOptOut(uvVersion(uvVersionText))
|
|
39
|
+
? ["--exclude-newer-package", "agrep=false"] : [];
|
|
40
|
+
return ["tool", "run", ...flag, "--from", SPEC, "agrep", ...rest];
|
|
19
41
|
}
|
|
20
42
|
|
|
21
43
|
function run(cmd, cmdArgs) {
|
|
22
|
-
const r = spawnSync(cmd, cmdArgs, {
|
|
44
|
+
const r = spawnSync(cmd, cmdArgs, {
|
|
45
|
+
stdio: "inherit", shell: false,
|
|
46
|
+
env: { ...process.env, [ANCHOR_ENV]: __filename },
|
|
47
|
+
});
|
|
23
48
|
if (r.error) {
|
|
24
49
|
console.error(`agrep: failed to launch ${cmd}: ${r.error.message}`);
|
|
25
50
|
process.exit(1);
|
|
@@ -27,16 +52,11 @@ function run(cmd, cmdArgs) {
|
|
|
27
52
|
process.exit(r.status === null ? 1 : r.status);
|
|
28
53
|
}
|
|
29
54
|
|
|
30
|
-
|
|
55
|
+
const uv = probe("uv");
|
|
56
|
+
if (uv !== null) {
|
|
31
57
|
// `uvx agrep` == `uv tool run agrep`; --from lets AGREP_PYPI_SPEC point anywhere
|
|
32
|
-
run("uv",
|
|
33
|
-
|
|
34
|
-
"--exclude-newer-package", "agrep=false",
|
|
35
|
-
"--from", SPEC,
|
|
36
|
-
"agrep",
|
|
37
|
-
...args,
|
|
38
|
-
]);
|
|
39
|
-
} else if (has("pipx")) {
|
|
58
|
+
run("uv", uvArgs(uv, args));
|
|
59
|
+
} else if (probe("pipx") !== null) {
|
|
40
60
|
run("pipx", ["run", "--spec", SPEC, "agrep", ...args]);
|
|
41
61
|
} else {
|
|
42
62
|
console.error(
|
package/package.json
CHANGED
package/postinstall.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Global installs finish enrolled, not merely downloaded: after warming
|
|
3
|
-
// ephemeral tool cache, run the pinned `agrep setup --yes` so the box
|
|
4
|
-
// indexed and the detected agents are taught the moment `npm install -g`
|
|
2
|
+
// Global installs finish enrolled, not merely downloaded: after warming the
|
|
3
|
+
// runner's ephemeral tool cache, run the pinned `agrep setup --yes` so the box
|
|
4
|
+
// is indexed and the detected agents are taught the moment `npm install -g`
|
|
5
5
|
// returns. Everything setup writes is disclosed in its output, owned by
|
|
6
6
|
// agrep, and undone by `agrep remove`; --no-semantic defers the ~52MiB model
|
|
7
7
|
// fetch to the first semantic use, and --no-archive leaves source-store
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
"use strict";
|
|
15
15
|
|
|
16
|
+
const path = require("path");
|
|
16
17
|
const { spawnSync } = require("child_process");
|
|
17
18
|
const { version } = require("./package.json");
|
|
18
19
|
|
|
@@ -22,6 +23,12 @@ const forced = Boolean(process.env.AGREP_POSTINSTALL_FORCE);
|
|
|
22
23
|
const strict = Boolean(process.env.AGREP_POSTINSTALL_STRICT);
|
|
23
24
|
const autoSetup = !process.env.AGREP_NO_AUTO_SETUP;
|
|
24
25
|
const SETUP_TIMEOUT_MS = 15 * 60 * 1000;
|
|
26
|
+
// `agrep setup` exit 3 (docs/OUTPUT_CONTRACTS.md): declined without changing
|
|
27
|
+
// anything; the tool is provisioned, so this is not a failed install
|
|
28
|
+
const SETUP_DECLINED = 3;
|
|
29
|
+
// the sentinel must track this package, not the runner's prunable cache copy
|
|
30
|
+
const ANCHOR_ENV = "AGREP_INSTALL_ANCHOR";
|
|
31
|
+
const anchor = path.join(__dirname, "bin.js");
|
|
25
32
|
|
|
26
33
|
if (process.env.AGREP_SKIP_POSTINSTALL) {
|
|
27
34
|
process.exit(0);
|
|
@@ -31,48 +38,96 @@ if (!npmGlobal && !forced) {
|
|
|
31
38
|
process.exit(0);
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
function
|
|
35
|
-
const
|
|
36
|
-
return
|
|
41
|
+
function probe(cmd) {
|
|
42
|
+
const r = spawnSync(cmd, ["--version"], { encoding: "utf8", shell: false });
|
|
43
|
+
return !r.error && r.status === 0 ? String(r.stdout || "") : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function uvVersion(text) {
|
|
47
|
+
const m = /\buv\s+(\d+)\.(\d+)\.(\d+)/.exec(text || "");
|
|
48
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// same rule as bin.js: the `=false` opt-out parses only on uv >= 0.9.25
|
|
52
|
+
function excludeNewerOptOut(v) {
|
|
53
|
+
return v !== null && (v[0] > 0 || v[1] > 9 || (v[1] === 9 && v[2] >= 25));
|
|
37
54
|
}
|
|
38
55
|
|
|
39
56
|
function tryRun(cmd, args, options) {
|
|
40
|
-
const result = spawnSync(cmd, args, {
|
|
41
|
-
|
|
57
|
+
const result = spawnSync(cmd, args, {
|
|
58
|
+
shell: false, ...options, env: { ...process.env, [ANCHOR_ENV]: anchor },
|
|
59
|
+
});
|
|
60
|
+
return result.error ? null : result.status;
|
|
42
61
|
}
|
|
43
62
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
63
|
+
// Resolve and cache the pinned agrep without running it for real; a failure here
|
|
64
|
+
// is the runner's (network, registry, an unsupported python), not setup's.
|
|
65
|
+
function provision(runner) {
|
|
66
|
+
const result = spawnSync(runner.cmd, runner.args("--version"), {
|
|
67
|
+
shell: false, encoding: "utf8", stdio: ["ignore", "ignore", "pipe"],
|
|
68
|
+
env: { ...process.env, [ANCHOR_ENV]: anchor },
|
|
69
|
+
});
|
|
70
|
+
if (result.error) return { ok: false, detail: result.error.message };
|
|
71
|
+
const lines = String(result.stderr || "").trim().split("\n").filter(Boolean);
|
|
72
|
+
return { ok: result.status === 0, detail: lines.length ? lines[lines.length - 1] : `exit ${result.status}` };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// bin.js's runner choice, in the same order: uv, else pipx
|
|
76
|
+
function findRunner() {
|
|
77
|
+
const uv = probe("uv");
|
|
78
|
+
if (uv !== null) {
|
|
79
|
+
const flag = excludeNewerOptOut(uvVersion(uv))
|
|
80
|
+
? ["--exclude-newer-package", "agrep=false"] : [];
|
|
81
|
+
return {
|
|
82
|
+
cmd: "uv",
|
|
83
|
+
args: (...rest) => ["tool", "run", ...flag, "--from", spec, "agrep", ...rest],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (probe("pipx") !== null) {
|
|
87
|
+
return { cmd: "pipx", args: (...rest) => ["run", "--spec", spec, "agrep", ...rest] };
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
51
90
|
}
|
|
52
91
|
|
|
53
92
|
let done = false;
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
93
|
+
const runner = findRunner();
|
|
94
|
+
if (runner && autoSetup) {
|
|
95
|
+
const fetched = provision(runner);
|
|
96
|
+
if (!fetched.ok) {
|
|
97
|
+
console.warn(
|
|
98
|
+
`agrep: ${runner.cmd} could not fetch ${spec} (${fetched.detail}); nothing was set up.`
|
|
99
|
+
+ " once the registry is reachable, `agrep setup` runs the one-time setup.");
|
|
100
|
+
} else {
|
|
101
|
+
// Inherited stdio: npm surfaces this with --foreground-scripts (and on
|
|
102
|
+
// failure), so every file setup touches is disclosed, never silent.
|
|
103
|
+
console.log(
|
|
104
|
+
"agrep: running one-time setup (agrep setup --yes) - writes agent instruction blocks"
|
|
105
|
+
+ " and an uninstall-cleanup sentinel, all undone by `agrep remove`;"
|
|
106
|
+
+ " AGREP_NO_AUTO_SETUP=1 skips this.");
|
|
107
|
+
const status = tryRun(
|
|
108
|
+
runner.cmd, runner.args("setup", "--yes", "--no-semantic", "--no-archive"),
|
|
109
|
+
{ stdio: "inherit", timeout: SETUP_TIMEOUT_MS });
|
|
110
|
+
done = status === 0 || status === SETUP_DECLINED;
|
|
111
|
+
if (status === null) {
|
|
112
|
+
console.warn(`agrep: could not launch ${runner.cmd} for setup; \`agrep setup\` runs it any time.`);
|
|
113
|
+
} else if (status === SETUP_DECLINED) {
|
|
114
|
+
console.warn(
|
|
115
|
+
"agrep: setup declined and changed nothing; its output above names the cause"
|
|
116
|
+
+ " and the fix. `agrep setup` declines the same way until that is resolved.");
|
|
117
|
+
} else if (!done) {
|
|
118
|
+
console.warn(
|
|
119
|
+
`agrep: setup exited ${status}; its output above names each step that did not complete`
|
|
120
|
+
+ " and the fix. the rest of setup still ran; `agrep setup` retries any time.");
|
|
121
|
+
}
|
|
67
122
|
}
|
|
68
|
-
} else if (
|
|
123
|
+
} else if (runner) {
|
|
69
124
|
// Warm-only: keep npm output clean; the shim provisions on first run.
|
|
70
|
-
done = tryRun(
|
|
125
|
+
done = tryRun(runner.cmd, runner.args("--version"), { stdio: "ignore" }) === 0;
|
|
71
126
|
if (!done) {
|
|
72
|
-
console.warn(
|
|
127
|
+
console.warn(`agrep: ${runner.cmd} cache warm failed; the first agrep run will retry`);
|
|
73
128
|
}
|
|
74
129
|
} else {
|
|
75
|
-
console.warn("agrep: install uv (https://docs.astral.sh/uv) - agrep needs
|
|
130
|
+
console.warn("agrep: install uv (https://docs.astral.sh/uv) or pipx - agrep needs one of them on first run.");
|
|
76
131
|
}
|
|
77
132
|
if (!done && strict) {
|
|
78
133
|
console.error("agrep: postinstall failed to provision the pinned PyPI tool");
|