agent-skillboard 0.2.12 → 0.2.14
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 +16 -0
- package/README.md +3 -2
- package/docs/install.md +3 -1
- package/package.json +1 -1
- package/src/lifecycle-cli.mjs +67 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.2.14 — 2026-07-03
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Sudo ownership restoration now only chowns managed guidance paths inside the
|
|
10
|
+
resolved invoking user's home, avoiding accidental ownership changes for
|
|
11
|
+
explicit agent roots outside that home.
|
|
12
|
+
|
|
13
|
+
## 0.2.13 — 2026-07-03
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Sudo-driven setup now restores ownership of managed agent guidance files and
|
|
18
|
+
directories under the invoking user's home to `SUDO_UID:SUDO_GID`, avoiding
|
|
19
|
+
root-owned `SKILL.md` files after `sudo npm install -g agent-skillboard`.
|
|
20
|
+
|
|
5
21
|
## 0.2.12 — 2026-07-03
|
|
6
22
|
|
|
7
23
|
### Changed
|
package/README.md
CHANGED
|
@@ -96,8 +96,9 @@ npm install -g agent-skillboard
|
|
|
96
96
|
If your system npm requires elevated permissions, `sudo npm install -g
|
|
97
97
|
agent-skillboard` is also supported. In that flow, install-time setup resolves
|
|
98
98
|
`SUDO_USER` and writes the user-level guidance skill under the invoking user's
|
|
99
|
-
agent homes
|
|
100
|
-
|
|
99
|
+
agent homes. Managed guidance files written under the user's home are restored
|
|
100
|
+
to the invoking user's ownership, while the `skillboard` binary still lands in
|
|
101
|
+
the global prefix used by that npm command.
|
|
101
102
|
|
|
102
103
|
The install-time setup writes a user-level `skillboard` guidance skill under
|
|
103
104
|
detected agent homes. For Codex, detection includes `CODEX_HOME/skills`,
|
package/docs/install.md
CHANGED
|
@@ -38,7 +38,9 @@ sudo npm install -g agent-skillboard
|
|
|
38
38
|
Under sudo, the postinstall setup resolves `SUDO_USER` and targets that user's
|
|
39
39
|
agent homes instead of writing guidance under `/root`. The executable prefix is
|
|
40
40
|
still decided by the npm command you run, so use the same npm/Node environment
|
|
41
|
-
you expect to provide `skillboard` on `PATH`.
|
|
41
|
+
you expect to provide `skillboard` on `PATH`. Managed guidance files and
|
|
42
|
+
directories written under the user's home are restored to the invoking user's
|
|
43
|
+
`SUDO_UID:SUDO_GID` ownership.
|
|
42
44
|
|
|
43
45
|
The install-time setup writes a user-level SkillBoard guidance skill under
|
|
44
46
|
detected agent homes such as `CODEX_HOME`, `AGENTS_HOME`, `CLAUDE_HOME`,
|
package/package.json
CHANGED
package/src/lifecycle-cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { access, chown, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
4
|
import { createInterface } from "node:readline";
|
|
5
5
|
import { promisify } from "node:util";
|
|
@@ -49,7 +49,9 @@ export async function runSetupCommand(options, stdout, runtime = defaultRuntime(
|
|
|
49
49
|
if (options.get("dir") !== undefined) {
|
|
50
50
|
throw new Error("skillboard setup is agent-layer setup and does not accept --dir");
|
|
51
51
|
}
|
|
52
|
-
const
|
|
52
|
+
const env = runtime.env ?? process.env;
|
|
53
|
+
const home = await resolveSetupHome(env, runtime);
|
|
54
|
+
const targets = await agentSetupTargets(options, runtime, home);
|
|
53
55
|
if (targets.length === 0) {
|
|
54
56
|
stdout.write("No supported agent user skill roots were detected.\n");
|
|
55
57
|
stdout.write(`Create a supported agent home or pass --agent ${supportedAgentNames().join(",")} to choose targets.\n`);
|
|
@@ -66,7 +68,7 @@ export async function runSetupCommand(options, stdout, runtime = defaultRuntime(
|
|
|
66
68
|
return 1;
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
|
-
const result = await installAgentIntegration(targets);
|
|
71
|
+
const result = await installAgentIntegration(targets, setupOwnership(env, runtime, home));
|
|
70
72
|
stdout.write("SkillBoard agent integration installed.\n");
|
|
71
73
|
writeList(stdout, "Created", result.created);
|
|
72
74
|
writeList(stdout, "Updated", result.updated);
|
|
@@ -219,9 +221,9 @@ function defaultRuntime() {
|
|
|
219
221
|
};
|
|
220
222
|
}
|
|
221
223
|
|
|
222
|
-
async function agentSetupTargets(options, runtime) {
|
|
224
|
+
async function agentSetupTargets(options, runtime, setupHome) {
|
|
223
225
|
const env = runtime.env ?? process.env;
|
|
224
|
-
const home = await resolveSetupHome(env, runtime);
|
|
226
|
+
const home = setupHome ?? await resolveSetupHome(env, runtime);
|
|
225
227
|
const requested = readCsv(options.get("agent"));
|
|
226
228
|
const supported = new Set(supportedAgentNames());
|
|
227
229
|
const names = requested.length === 0 ? supportedAgentNames() : requested;
|
|
@@ -252,6 +254,31 @@ async function resolveSetupHome(env, runtime) {
|
|
|
252
254
|
return env.HOME ?? env.USERPROFILE;
|
|
253
255
|
}
|
|
254
256
|
|
|
257
|
+
function setupOwnership(env, runtime, home) {
|
|
258
|
+
if (process.platform === "win32" || !shouldUseSudoUserHome(env)) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
const uid = parseNonNegativeInteger(env.SUDO_UID);
|
|
262
|
+
const gid = parseNonNegativeInteger(env.SUDO_GID);
|
|
263
|
+
if (uid === null || gid === null) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
uid,
|
|
268
|
+
gid,
|
|
269
|
+
home: resolve(home),
|
|
270
|
+
chown: runtime.chown ?? chown
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function parseNonNegativeInteger(value) {
|
|
275
|
+
if (value === undefined || !/^\d+$/.test(value)) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
const parsed = Number(value);
|
|
279
|
+
return Number.isSafeInteger(parsed) ? parsed : null;
|
|
280
|
+
}
|
|
281
|
+
|
|
255
282
|
function shouldUseSudoUserHome(env) {
|
|
256
283
|
const sudoUser = nonEmpty(env.SUDO_USER);
|
|
257
284
|
return process.platform !== "win32"
|
|
@@ -308,7 +335,7 @@ function nonEmpty(value) {
|
|
|
308
335
|
return value === undefined || value.trim() === "" ? null : value;
|
|
309
336
|
}
|
|
310
337
|
|
|
311
|
-
async function installAgentIntegration(targets) {
|
|
338
|
+
async function installAgentIntegration(targets, ownership = null) {
|
|
312
339
|
const created = [];
|
|
313
340
|
const updated = [];
|
|
314
341
|
const unchanged = [];
|
|
@@ -331,11 +358,45 @@ async function installAgentIntegration(targets) {
|
|
|
331
358
|
}
|
|
332
359
|
await mkdir(dirname(target.skillPath), { recursive: true });
|
|
333
360
|
await writeFile(target.skillPath, content, "utf8");
|
|
361
|
+
await applyOwnership(target.skillPath, ownership);
|
|
334
362
|
(existing === null ? created : updated).push(`${target.agent}:${target.skillPath}`);
|
|
335
363
|
}
|
|
336
364
|
return { created, updated, unchanged, preserved };
|
|
337
365
|
}
|
|
338
366
|
|
|
367
|
+
async function applyOwnership(path, ownership) {
|
|
368
|
+
if (ownership === null) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
for (const ownedPath of ownershipPaths(path, ownership.home)) {
|
|
372
|
+
await ownership.chown(ownedPath, ownership.uid, ownership.gid);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function ownershipPaths(path, home) {
|
|
377
|
+
const resolvedHome = resolve(home);
|
|
378
|
+
const resolvedPath = resolve(path);
|
|
379
|
+
if (!isInside(resolvedPath, resolvedHome)) {
|
|
380
|
+
return [];
|
|
381
|
+
}
|
|
382
|
+
const directories = [];
|
|
383
|
+
let current = dirname(resolvedPath);
|
|
384
|
+
while (current !== resolvedHome && isInside(current, resolvedHome)) {
|
|
385
|
+
directories.push(current);
|
|
386
|
+
const parent = dirname(current);
|
|
387
|
+
if (parent === current) {
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
current = parent;
|
|
391
|
+
}
|
|
392
|
+
return [...directories.reverse(), resolvedPath];
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function isInside(path, parent) {
|
|
396
|
+
const relativePath = relative(parent, path);
|
|
397
|
+
return relativePath !== "" && !relativePath.startsWith("..") && !isAbsolute(relativePath);
|
|
398
|
+
}
|
|
399
|
+
|
|
339
400
|
function agentIntegrationSkill() {
|
|
340
401
|
return `---
|
|
341
402
|
name: skillboard
|