@alavida/agentpack 0.1.3 → 0.1.4
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/README.md +24 -0
- package/package.json +6 -2
- package/skills/agentpack-cli/SKILL.md +6 -1
- package/skills/authoring-skillgraphs-from-knowledge/SKILL.md +1 -1
- package/skills/developing-and-testing-skills/SKILL.md +20 -2
- package/skills/getting-started-skillgraphs/SKILL.md +1 -1
- package/skills/identifying-skill-opportunities/SKILL.md +1 -1
- package/skills/maintaining-skillgraph-freshness/SKILL.md +1 -1
- package/skills/repairing-broken-skill-or-plugin-state/SKILL.md +5 -1
- package/skills/shipping-production-plugins-and-packages/SKILL.md +1 -1
- package/skills/sync-state.json +83 -0
- package/src/commands/skills.js +28 -10
- package/src/infrastructure/fs/dev-session-repository.js +25 -0
- package/src/infrastructure/runtime/materialize-skills.js +18 -1
- package/src/lib/skills.js +185 -4
package/README.md
CHANGED
|
@@ -253,6 +253,28 @@ Validate the shipped agent skill:
|
|
|
253
253
|
npm run intent:validate
|
|
254
254
|
```
|
|
255
255
|
|
|
256
|
+
## Releases
|
|
257
|
+
|
|
258
|
+
This repo now uses Changesets for versioning and publishing.
|
|
259
|
+
|
|
260
|
+
Normal maintainer flow:
|
|
261
|
+
|
|
262
|
+
1. Add a changeset in any PR that changes user-facing behavior.
|
|
263
|
+
2. Merge the feature PR to `main`.
|
|
264
|
+
3. Let GitHub open or update the `Version Packages` release PR.
|
|
265
|
+
4. Review and merge that release PR.
|
|
266
|
+
5. The merge publishes to npm automatically.
|
|
267
|
+
|
|
268
|
+
Useful local commands:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
npx changeset
|
|
272
|
+
npm run version-packages
|
|
273
|
+
npm run release
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Manual git tags are no longer the normal release path.
|
|
277
|
+
|
|
256
278
|
## Optional Agent Integration
|
|
257
279
|
|
|
258
280
|
This package also ships an agent-facing skill under:
|
|
@@ -273,10 +295,12 @@ In authoring repos:
|
|
|
273
295
|
|
|
274
296
|
- commit `.agentpack/build-state.json`
|
|
275
297
|
- commit `.agentpack/catalog.json`
|
|
298
|
+
- commit `skills/sync-state.json` when maintaining shipped Intent skills
|
|
276
299
|
|
|
277
300
|
In consumer/runtime repos:
|
|
278
301
|
|
|
279
302
|
- do not commit `.agentpack/install.json`
|
|
303
|
+
- do not commit `.agentpack/dev-session.json`
|
|
280
304
|
|
|
281
305
|
## License
|
|
282
306
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alavida/agentpack",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Package-backed skills lifecycle CLI for agent skills and plugins",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"agentpack": "bin/agentpack.js",
|
|
8
|
-
"intent": "
|
|
8
|
+
"intent": "bin/intent.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"bin",
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
"validate:live": "node scripts/live-validation.mjs",
|
|
20
20
|
"smoke:monorepo": "node scripts/smoke-monorepo.mjs",
|
|
21
21
|
"docs:dev": "cd docs && mint dev",
|
|
22
|
+
"changeset": "changeset",
|
|
23
|
+
"version-packages": "changeset version",
|
|
24
|
+
"release": "changeset publish",
|
|
22
25
|
"intent:validate": "npx @tanstack/intent validate",
|
|
23
26
|
"build:dashboard": "node scripts/build-dashboard.mjs"
|
|
24
27
|
},
|
|
@@ -51,6 +54,7 @@
|
|
|
51
54
|
"react-dom": "^19.1.1"
|
|
52
55
|
},
|
|
53
56
|
"devDependencies": {
|
|
57
|
+
"@changesets/cli": "^2.30.0",
|
|
54
58
|
"@tanstack/intent": "^0.0.14",
|
|
55
59
|
"esbuild": "^0.25.10"
|
|
56
60
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agentpack-cli
|
|
3
3
|
description: Use the agentpack CLI correctly when treating knowledge as a package. Apply the authored skill lifecycle, plugin lifecycle, source-backed validation, install flow, and bundled plugin artifact flow without mixing those stages together.
|
|
4
|
-
library_version: 0.1.
|
|
4
|
+
library_version: 0.1.3
|
|
5
5
|
sources:
|
|
6
6
|
- README.md
|
|
7
7
|
- docs/introduction.mdx
|
|
@@ -71,11 +71,16 @@ Persistence rule:
|
|
|
71
71
|
- commit `.agentpack/build-state.json` so stale detection works across GitHub, CI, and teammate machines
|
|
72
72
|
- commit `.agentpack/catalog.json` in authoring repos
|
|
73
73
|
- do not commit `.agentpack/install.json`
|
|
74
|
+
- do not commit `.agentpack/dev-session.json`
|
|
75
|
+
- commit `skills/sync-state.json` when maintaining the shipped Intent skills for this package
|
|
74
76
|
|
|
75
77
|
Runtime notes:
|
|
76
78
|
|
|
77
79
|
- after `skills dev` writes to `.claude/skills/` or `.agents/skills/`, start a fresh agent session if the current one was already running
|
|
78
80
|
- `skills dev` starts a localhost workbench by default for one selected skill, with provenance edges, direct required skills, and actions like validate or stale checks
|
|
81
|
+
- `skills dev` records the active session in `.agentpack/dev-session.json` so the next run can clean up stale runtime links after abnormal termination
|
|
82
|
+
- if a stale local dev session blocks startup, use `agentpack skills dev cleanup` and escalate to `agentpack skills dev cleanup --force` only when the recorded pid is a false positive
|
|
83
|
+
- use `agentpack skills unlink <root> --recursive` when you need to remove one active dev root plus its transitive local runtime links
|
|
79
84
|
- do not reload `metadata.sources` manually once the dev-linked skill exists; trust the compiled `SKILL.md` artifact unless you are explicitly updating the skill
|
|
80
85
|
- invoke the resulting skill through the runtime's skill mechanism, not by opening the file and reading it as plain text
|
|
81
86
|
|
|
@@ -3,7 +3,7 @@ name: authoring-skillgraphs-from-knowledge
|
|
|
3
3
|
description: Use when authoring packaged skills from source knowledge with valid SKILL.md metadata, package.json release fields, provenance sources, and requires edges in agentpack.
|
|
4
4
|
type: core
|
|
5
5
|
library: agentpack
|
|
6
|
-
library_version: "0.1.
|
|
6
|
+
library_version: "0.1.3"
|
|
7
7
|
sources:
|
|
8
8
|
- "alavida-ai/agentpack:docs/commands.mdx"
|
|
9
9
|
- "alavida-ai/agentpack:docs/architecture.mdx"
|
|
@@ -3,7 +3,7 @@ name: developing-and-testing-skills
|
|
|
3
3
|
description: Use when iterating locally on a packaged skill with agentpack skills dev, the localhost workbench, repo-local materialization, and runtime testing feedback loops.
|
|
4
4
|
type: core
|
|
5
5
|
library: agentpack
|
|
6
|
-
library_version: "0.1.
|
|
6
|
+
library_version: "0.1.3"
|
|
7
7
|
sources:
|
|
8
8
|
- "alavida-ai/agentpack:docs/commands.mdx"
|
|
9
9
|
- "alavida-ai/agentpack:docs/introduction.mdx"
|
|
@@ -30,7 +30,7 @@ agentpack skills dev domains/value/skills/copywriting
|
|
|
30
30
|
agentpack skills dev domains/value/skills/copywriting
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
This links the selected skill into `.claude/skills/` and `.agents/skills
|
|
33
|
+
This links the selected skill into `.claude/skills/` and `.agents/skills/`, records the active dev session in `.agentpack/dev-session.json`, and starts a localhost workbench by default.
|
|
34
34
|
|
|
35
35
|
### Use CLI-only mode when you explicitly do not want the dashboard
|
|
36
36
|
|
|
@@ -44,6 +44,24 @@ agentpack skills dev --no-dashboard domains/value/skills/copywriting
|
|
|
44
44
|
agentpack skills unlink value-copywriting
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
If the previous dev process was killed badly and left stale runtime links behind:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
agentpack skills dev cleanup
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
If a wrapper-managed process left a false-positive live pid in `.agentpack/dev-session.json`:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
agentpack skills dev cleanup --force
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If you need to remove the active root plus its recorded transitive links in one shot:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
agentpack skills unlink value-copywriting --recursive
|
|
63
|
+
```
|
|
64
|
+
|
|
47
65
|
## Common Mistakes
|
|
48
66
|
|
|
49
67
|
### HIGH Expecting the current agent session to pick up new links
|
|
@@ -3,7 +3,7 @@ name: getting-started-skillgraphs
|
|
|
3
3
|
description: Use when starting from an empty repo or empty skillgraph and needing the first correct authoring loop, lifecycle framing, repo-root routing, and inspect/validate/dev command flow in agentpack.
|
|
4
4
|
type: lifecycle
|
|
5
5
|
library: agentpack
|
|
6
|
-
library_version: "0.1.
|
|
6
|
+
library_version: "0.1.3"
|
|
7
7
|
sources:
|
|
8
8
|
- "alavida-ai/agentpack:docs/introduction.mdx"
|
|
9
9
|
- "alavida-ai/agentpack:docs/commands.mdx"
|
|
@@ -3,7 +3,7 @@ name: identifying-skill-opportunities
|
|
|
3
3
|
description: Use when deciding what raw knowledge should become a packaged skill, a reusable capability boundary, a requires edge, or a plugin-local wrapper in an agentpack skillgraph.
|
|
4
4
|
type: core
|
|
5
5
|
library: agentpack
|
|
6
|
-
library_version: "0.1.
|
|
6
|
+
library_version: "0.1.3"
|
|
7
7
|
sources:
|
|
8
8
|
- "alavida-ai/agentpack:docs/architecture.mdx"
|
|
9
9
|
- "alavida-ai/agentpack:docs/overview.mdx"
|
|
@@ -3,7 +3,7 @@ name: maintaining-skillgraph-freshness
|
|
|
3
3
|
description: Use when validating authored skills, checking stale state, and keeping build-state and catalog metadata aligned with changing knowledge in an agentpack skillgraph.
|
|
4
4
|
type: lifecycle
|
|
5
5
|
library: agentpack
|
|
6
|
-
library_version: "0.1.
|
|
6
|
+
library_version: "0.1.3"
|
|
7
7
|
sources:
|
|
8
8
|
- "alavida-ai/agentpack:docs/commands.mdx"
|
|
9
9
|
- "alavida-ai/agentpack:docs/current-state.mdx"
|
|
@@ -3,7 +3,7 @@ name: repairing-broken-skill-or-plugin-state
|
|
|
3
3
|
description: Use when auditing or repairing stale skills, unresolved requires, missing runtime dependencies, affected dependents, or malformed plugin definition files in agentpack.
|
|
4
4
|
type: lifecycle
|
|
5
5
|
library: agentpack
|
|
6
|
-
library_version: "0.1.
|
|
6
|
+
library_version: "0.1.3"
|
|
7
7
|
sources:
|
|
8
8
|
- "alavida-ai/agentpack:docs/current-state.mdx"
|
|
9
9
|
- "alavida-ai/agentpack:docs/commands.mdx"
|
|
@@ -100,11 +100,15 @@ Correct:
|
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
102
|
agentpack skills unlink value-copywriting
|
|
103
|
+
agentpack skills unlink value-copywriting --recursive
|
|
104
|
+
agentpack skills dev cleanup
|
|
103
105
|
agentpack skills install @alavida-ai/value-copywriting
|
|
104
106
|
```
|
|
105
107
|
|
|
106
108
|
Runtime state should be repaired through agentpack lifecycle commands, not direct edits under `.claude/skills` or `.agents/skills`.
|
|
107
109
|
|
|
110
|
+
Use `skills dev cleanup --force` only when a wrapper-managed process or pid reuse leaves a false-positive active session in `.agentpack/dev-session.json`.
|
|
111
|
+
|
|
108
112
|
Source: docs/architecture.mdx
|
|
109
113
|
|
|
110
114
|
## References
|
|
@@ -3,7 +3,7 @@ name: shipping-production-plugins-and-packages
|
|
|
3
3
|
description: Use when turning maintained skills into deployable bundled plugins or publishable standalone packages with explicit dependency closure, hooks, MCP tools, and production checks in agentpack.
|
|
4
4
|
type: core
|
|
5
5
|
library: agentpack
|
|
6
|
-
library_version: "0.1.
|
|
6
|
+
library_version: "0.1.3"
|
|
7
7
|
sources:
|
|
8
8
|
- "alavida-ai/agentpack:docs/commands.mdx"
|
|
9
9
|
- "alavida-ai/agentpack:docs/architecture.mdx"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"skills": {
|
|
4
|
+
"agentpack-cli": {
|
|
5
|
+
"library_version": "0.1.3",
|
|
6
|
+
"synced_at": "2026-03-12T00:00:00.000Z",
|
|
7
|
+
"sources_sha": {
|
|
8
|
+
"README.md": "2680d4fa603b070e6bd92c162b3d172131b71c39c9dc39351ed452d95567b0bd",
|
|
9
|
+
"docs/introduction.mdx": "c1a8989382dc81a3a33dae1e8f243d159b92835a1999a8793741a25b317b455d",
|
|
10
|
+
"docs/overview.mdx": "48151b11dfef153223ba2aed7100eeeafc100c475694c98e053f9c4ffb025357",
|
|
11
|
+
"docs/architecture.mdx": "400617fb8ba4a5f09a78b998e3b1a01262caed05e0344206ca1f8fd71519ea22",
|
|
12
|
+
"docs/build-lifecycle.mdx": "0e697180c639a72514272e6421f900ac3d5248ecc09220409efc716bf0d4fa71",
|
|
13
|
+
"docs/commands.mdx": "5f2ec2839caa8a6758f36daaa150f6af8d3700e26b135333fad6a0719dacb05b",
|
|
14
|
+
"docs/current-state.mdx": "b4ad89e40706aff29bbe3149daeeb3feac6d07bb3ddfd4f164e630cd5b7997a8",
|
|
15
|
+
"docs/distribution.mdx": "060e47b28c3da13ff61e6b1b04e018beb8dc5d6ebd30b5a3d1574fceb289e3f4",
|
|
16
|
+
"docs/end-to-end-test-plan.md": "c1e37589660a438cb1a9742f68e769ed68f6f8849f1510d8445e99c2bff1ec21"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"authoring-skillgraphs-from-knowledge": {
|
|
20
|
+
"library_version": "0.1.3",
|
|
21
|
+
"synced_at": "2026-03-12T00:00:00.000Z",
|
|
22
|
+
"sources_sha": {
|
|
23
|
+
"alavida-ai/agentpack:docs/commands.mdx": "5f2ec2839caa8a6758f36daaa150f6af8d3700e26b135333fad6a0719dacb05b",
|
|
24
|
+
"alavida-ai/agentpack:docs/architecture.mdx": "400617fb8ba4a5f09a78b998e3b1a01262caed05e0344206ca1f8fd71519ea22",
|
|
25
|
+
"alavida-ai/agentpack:skills/agentpack-cli/SKILL.md": "54d9a43172ff6b7e7218bb9a9454b56d38233e9d1a6df9362e7167ceb9dc2249"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"developing-and-testing-skills": {
|
|
29
|
+
"library_version": "0.1.3",
|
|
30
|
+
"synced_at": "2026-03-12T00:00:00.000Z",
|
|
31
|
+
"sources_sha": {
|
|
32
|
+
"alavida-ai/agentpack:docs/commands.mdx": "5f2ec2839caa8a6758f36daaa150f6af8d3700e26b135333fad6a0719dacb05b",
|
|
33
|
+
"alavida-ai/agentpack:docs/introduction.mdx": "c1a8989382dc81a3a33dae1e8f243d159b92835a1999a8793741a25b317b455d",
|
|
34
|
+
"alavida-ai/agentpack:README.md": "2680d4fa603b070e6bd92c162b3d172131b71c39c9dc39351ed452d95567b0bd"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"getting-started-skillgraphs": {
|
|
38
|
+
"library_version": "0.1.3",
|
|
39
|
+
"synced_at": "2026-03-12T00:00:00.000Z",
|
|
40
|
+
"sources_sha": {
|
|
41
|
+
"alavida-ai/agentpack:docs/introduction.mdx": "c1a8989382dc81a3a33dae1e8f243d159b92835a1999a8793741a25b317b455d",
|
|
42
|
+
"alavida-ai/agentpack:docs/commands.mdx": "5f2ec2839caa8a6758f36daaa150f6af8d3700e26b135333fad6a0719dacb05b",
|
|
43
|
+
"alavida-ai/agentpack:README.md": "2680d4fa603b070e6bd92c162b3d172131b71c39c9dc39351ed452d95567b0bd"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"identifying-skill-opportunities": {
|
|
47
|
+
"library_version": "0.1.3",
|
|
48
|
+
"synced_at": "2026-03-12T00:00:00.000Z",
|
|
49
|
+
"sources_sha": {
|
|
50
|
+
"alavida-ai/agentpack:docs/architecture.mdx": "400617fb8ba4a5f09a78b998e3b1a01262caed05e0344206ca1f8fd71519ea22",
|
|
51
|
+
"alavida-ai/agentpack:docs/overview.mdx": "48151b11dfef153223ba2aed7100eeeafc100c475694c98e053f9c4ffb025357",
|
|
52
|
+
"alavida-ai/agentpack:skills/agentpack-cli/SKILL.md": "54d9a43172ff6b7e7218bb9a9454b56d38233e9d1a6df9362e7167ceb9dc2249"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"maintaining-skillgraph-freshness": {
|
|
56
|
+
"library_version": "0.1.3",
|
|
57
|
+
"synced_at": "2026-03-12T00:00:00.000Z",
|
|
58
|
+
"sources_sha": {
|
|
59
|
+
"alavida-ai/agentpack:docs/commands.mdx": "5f2ec2839caa8a6758f36daaa150f6af8d3700e26b135333fad6a0719dacb05b",
|
|
60
|
+
"alavida-ai/agentpack:docs/current-state.mdx": "b4ad89e40706aff29bbe3149daeeb3feac6d07bb3ddfd4f164e630cd5b7997a8",
|
|
61
|
+
"alavida-ai/agentpack:README.md": "2680d4fa603b070e6bd92c162b3d172131b71c39c9dc39351ed452d95567b0bd"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"repairing-broken-skill-or-plugin-state": {
|
|
65
|
+
"library_version": "0.1.3",
|
|
66
|
+
"synced_at": "2026-03-12T00:00:00.000Z",
|
|
67
|
+
"sources_sha": {
|
|
68
|
+
"alavida-ai/agentpack:docs/current-state.mdx": "b4ad89e40706aff29bbe3149daeeb3feac6d07bb3ddfd4f164e630cd5b7997a8",
|
|
69
|
+
"alavida-ai/agentpack:docs/commands.mdx": "5f2ec2839caa8a6758f36daaa150f6af8d3700e26b135333fad6a0719dacb05b",
|
|
70
|
+
"alavida-ai/agentpack:src/domain/plugins/load-plugin-definition.js": "3aac67288351229e878d0877097b41cd369cc594fc8683fccf2506b94a5f17e7"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"shipping-production-plugins-and-packages": {
|
|
74
|
+
"library_version": "0.1.3",
|
|
75
|
+
"synced_at": "2026-03-12T00:00:00.000Z",
|
|
76
|
+
"sources_sha": {
|
|
77
|
+
"alavida-ai/agentpack:docs/commands.mdx": "5f2ec2839caa8a6758f36daaa150f6af8d3700e26b135333fad6a0719dacb05b",
|
|
78
|
+
"alavida-ai/agentpack:docs/architecture.mdx": "400617fb8ba4a5f09a78b998e3b1a01262caed05e0344206ca1f8fd71519ea22",
|
|
79
|
+
"alavida-ai/agentpack:README.md": "2680d4fa603b070e6bd92c162b3d172131b71c39c9dc39351ed452d95567b0bd"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/commands/skills.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
installSkills,
|
|
12
12
|
listOutdatedSkills,
|
|
13
13
|
resolveInstallTargets,
|
|
14
|
+
cleanupSkillDevSession,
|
|
14
15
|
startSkillDev,
|
|
15
16
|
unlinkSkill,
|
|
16
17
|
uninstallSkills,
|
|
@@ -22,13 +23,16 @@ export function skillsCommand() {
|
|
|
22
23
|
const cmd = new Command('skills')
|
|
23
24
|
.description('Inspect and manage package-backed skills');
|
|
24
25
|
|
|
25
|
-
cmd
|
|
26
|
+
const devCmd = cmd
|
|
26
27
|
.command('dev')
|
|
27
28
|
.description('Link one local packaged skill for local Claude and agent discovery')
|
|
28
29
|
.option('--no-sync', 'Skip syncing managed package dependencies from requires')
|
|
29
30
|
.option('--no-dashboard', 'Skip starting the local skill development workbench')
|
|
30
|
-
.argument('
|
|
31
|
+
.argument('[target]', 'Packaged skill directory or SKILL.md path')
|
|
31
32
|
.action(async (target, opts, command) => {
|
|
33
|
+
if (!target) {
|
|
34
|
+
command.help({ error: true });
|
|
35
|
+
}
|
|
32
36
|
const globalOpts = command.optsWithGlobals();
|
|
33
37
|
const session = startSkillDev(target, {
|
|
34
38
|
sync: opts.sync,
|
|
@@ -70,23 +74,37 @@ export function skillsCommand() {
|
|
|
70
74
|
},
|
|
71
75
|
});
|
|
72
76
|
|
|
73
|
-
const stop = () => {
|
|
74
|
-
session.close();
|
|
75
|
-
process.exit(0);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
process.once('SIGTERM', stop);
|
|
79
|
-
process.once('SIGINT', stop);
|
|
80
77
|
await session.ready;
|
|
81
78
|
});
|
|
82
79
|
|
|
80
|
+
devCmd
|
|
81
|
+
.command('cleanup')
|
|
82
|
+
.description('Remove recorded skills dev links for a stale session')
|
|
83
|
+
.option('--force', 'Remove recorded links even if the session pid still appears alive')
|
|
84
|
+
.action((opts, command) => {
|
|
85
|
+
const globalOpts = command.optsWithGlobals();
|
|
86
|
+
const result = cleanupSkillDevSession({ force: opts.force });
|
|
87
|
+
|
|
88
|
+
if (globalOpts.json) {
|
|
89
|
+
output.json(result);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
output.write(`Cleaned: ${result.cleaned}`);
|
|
94
|
+
if (result.name) output.write(`Root Skill: ${result.name}`);
|
|
95
|
+
for (const removed of result.removed) {
|
|
96
|
+
output.write(`Removed: ${removed}`);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
83
100
|
cmd
|
|
84
101
|
.command('unlink')
|
|
85
102
|
.description('Remove one locally linked skill from Claude and agent discovery paths')
|
|
103
|
+
.option('--recursive', 'Remove the active dev root and its recorded transitive links')
|
|
86
104
|
.argument('<name>', 'Skill frontmatter name')
|
|
87
105
|
.action((name, opts, command) => {
|
|
88
106
|
const globalOpts = command.optsWithGlobals();
|
|
89
|
-
const result = unlinkSkill(name);
|
|
107
|
+
const result = unlinkSkill(name, { recursive: opts.recursive });
|
|
90
108
|
|
|
91
109
|
if (globalOpts.json) {
|
|
92
110
|
output.json(result);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
function getDevSessionPath(repoRoot) {
|
|
5
|
+
return join(repoRoot, '.agentpack', 'dev-session.json');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function readDevSession(repoRoot) {
|
|
9
|
+
const sessionPath = getDevSessionPath(repoRoot);
|
|
10
|
+
if (!existsSync(sessionPath)) return null;
|
|
11
|
+
return JSON.parse(readFileSync(sessionPath, 'utf-8'));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function writeDevSession(repoRoot, session) {
|
|
15
|
+
mkdirSync(join(repoRoot, '.agentpack'), { recursive: true });
|
|
16
|
+
writeFileSync(getDevSessionPath(repoRoot), JSON.stringify(session, null, 2) + '\n');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function removeDevSession(repoRoot) {
|
|
20
|
+
rmSync(getDevSessionPath(repoRoot), { force: true });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function devSessionExists(repoRoot) {
|
|
24
|
+
return existsSync(getDevSessionPath(repoRoot));
|
|
25
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, rmSync, symlinkSync } from 'node:fs';
|
|
2
|
-
import { dirname, join, relative } from 'node:path';
|
|
2
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
3
3
|
import { writeInstallState } from '../fs/install-state-repository.js';
|
|
4
4
|
|
|
5
5
|
function ensureDir(pathValue) {
|
|
@@ -40,6 +40,23 @@ export function removeSkillLinksByNames(repoRoot, names, normalizeDisplayPath) {
|
|
|
40
40
|
return [...new Set(removed)];
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export function removeSkillLinksByPaths(repoRoot, paths, normalizeDisplayPath) {
|
|
44
|
+
const removed = [];
|
|
45
|
+
const allowedRoots = [
|
|
46
|
+
resolve(repoRoot, '.claude', 'skills'),
|
|
47
|
+
resolve(repoRoot, '.agents', 'skills'),
|
|
48
|
+
];
|
|
49
|
+
for (const relativePath of paths || []) {
|
|
50
|
+
const pathValue = resolve(repoRoot, relativePath);
|
|
51
|
+
const inAllowedRoot = allowedRoots.some((root) => pathValue === root || pathValue.startsWith(`${root}/`));
|
|
52
|
+
if (!inAllowedRoot) continue;
|
|
53
|
+
if (!existsSync(pathValue)) continue;
|
|
54
|
+
removePathIfExists(pathValue);
|
|
55
|
+
removed.push(normalizeDisplayPath(repoRoot, pathValue));
|
|
56
|
+
}
|
|
57
|
+
return [...new Set(removed)];
|
|
58
|
+
}
|
|
59
|
+
|
|
43
60
|
function ensureSymlink(targetPath, linkPath) {
|
|
44
61
|
removePathIfExists(linkPath);
|
|
45
62
|
mkdirSync(dirname(linkPath), { recursive: true });
|
package/src/lib/skills.js
CHANGED
|
@@ -9,11 +9,13 @@ import {
|
|
|
9
9
|
readNodeStatus,
|
|
10
10
|
} from '../domain/skills/skill-graph.js';
|
|
11
11
|
import { readInstallState } from '../infrastructure/fs/install-state-repository.js';
|
|
12
|
+
import { readDevSession, writeDevSession, removeDevSession } from '../infrastructure/fs/dev-session-repository.js';
|
|
12
13
|
import {
|
|
13
14
|
ensureSkillLink,
|
|
14
15
|
rebuildInstallState,
|
|
15
16
|
removePathIfExists,
|
|
16
17
|
removeSkillLinks,
|
|
18
|
+
removeSkillLinksByPaths,
|
|
17
19
|
removeSkillLinksByNames,
|
|
18
20
|
} from '../infrastructure/runtime/materialize-skills.js';
|
|
19
21
|
import {
|
|
@@ -171,6 +173,100 @@ function readPackageJson(packageDir) {
|
|
|
171
173
|
};
|
|
172
174
|
}
|
|
173
175
|
|
|
176
|
+
function isProcessAlive(pid) {
|
|
177
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
178
|
+
try {
|
|
179
|
+
process.kill(pid, 0);
|
|
180
|
+
return true;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
if (error.code === 'EPERM') return true;
|
|
183
|
+
if (error.code === 'ESRCH') return false;
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function buildDevSessionNextSteps(command) {
|
|
189
|
+
return [{
|
|
190
|
+
action: 'run_command',
|
|
191
|
+
command,
|
|
192
|
+
reason: 'Use the dev session cleanup flow to remove recorded linked skills for this repo',
|
|
193
|
+
}];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function toDevSessionRecord(repoRoot, target, result, existing = null) {
|
|
197
|
+
const now = new Date().toISOString();
|
|
198
|
+
const rootSkill = result.linkedSkills.find((entry) => entry.name === result.name) || result.linkedSkills[0] || null;
|
|
199
|
+
return {
|
|
200
|
+
version: 1,
|
|
201
|
+
session_id: existing?.session_id || `dev-${now.replaceAll(':', '-').replaceAll('.', '-')}`,
|
|
202
|
+
status: 'active',
|
|
203
|
+
pid: process.pid,
|
|
204
|
+
repo_root: repoRoot,
|
|
205
|
+
target,
|
|
206
|
+
root_skill: rootSkill
|
|
207
|
+
? {
|
|
208
|
+
name: rootSkill.name,
|
|
209
|
+
package_name: rootSkill.packageName,
|
|
210
|
+
path: rootSkill.path,
|
|
211
|
+
}
|
|
212
|
+
: null,
|
|
213
|
+
linked_skills: result.linkedSkills.map((entry) => ({
|
|
214
|
+
name: entry.name,
|
|
215
|
+
package_name: entry.packageName,
|
|
216
|
+
path: entry.path,
|
|
217
|
+
})),
|
|
218
|
+
links: result.links,
|
|
219
|
+
started_at: existing?.started_at || now,
|
|
220
|
+
updated_at: now,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function cleanupRecordedDevSession(repoRoot, session, status = 'stale') {
|
|
225
|
+
if (!session) {
|
|
226
|
+
return {
|
|
227
|
+
cleaned: false,
|
|
228
|
+
removed: [],
|
|
229
|
+
session: null,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
writeDevSession(repoRoot, {
|
|
234
|
+
...session,
|
|
235
|
+
status,
|
|
236
|
+
updated_at: new Date().toISOString(),
|
|
237
|
+
});
|
|
238
|
+
const removed = removeSkillLinksByPaths(repoRoot, session.links || [], normalizeDisplayPath);
|
|
239
|
+
removeDevSession(repoRoot);
|
|
240
|
+
return {
|
|
241
|
+
cleaned: removed.length > 0 || Boolean(session),
|
|
242
|
+
removed,
|
|
243
|
+
session,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function reconcileDevSession(repoRoot) {
|
|
248
|
+
const session = readDevSession(repoRoot);
|
|
249
|
+
if (!session) return null;
|
|
250
|
+
|
|
251
|
+
if (session.status === 'active' && isProcessAlive(session.pid)) {
|
|
252
|
+
throw new AgentpackError('A skills dev session is already active in this repo', {
|
|
253
|
+
code: 'skills_dev_session_active',
|
|
254
|
+
exitCode: EXIT_CODES.GENERAL,
|
|
255
|
+
nextSteps: [
|
|
256
|
+
...buildDevSessionNextSteps('agentpack skills dev cleanup'),
|
|
257
|
+
...buildDevSessionNextSteps('agentpack skills dev cleanup --force'),
|
|
258
|
+
],
|
|
259
|
+
details: {
|
|
260
|
+
rootSkill: session.root_skill?.name || null,
|
|
261
|
+
pid: session.pid,
|
|
262
|
+
startedAt: session.started_at || null,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return cleanupRecordedDevSession(repoRoot, session, 'stale');
|
|
268
|
+
}
|
|
269
|
+
|
|
174
270
|
export function syncSkillDependencies(skillDir) {
|
|
175
271
|
const skillFile = join(skillDir, 'SKILL.md');
|
|
176
272
|
const metadata = parseSkillFrontmatterFile(skillFile);
|
|
@@ -271,12 +367,14 @@ export function startSkillDev(target, {
|
|
|
271
367
|
const outerRepoRoot = findRepoRoot(cwd);
|
|
272
368
|
const { skillDir } = resolveLocalPackagedSkillDir(outerRepoRoot, target);
|
|
273
369
|
const repoRoot = findRepoRoot(skillDir);
|
|
370
|
+
reconcileDevSession(repoRoot);
|
|
274
371
|
let closed = false;
|
|
275
372
|
let timer = null;
|
|
276
373
|
let currentNames = [];
|
|
277
374
|
let watcher = null;
|
|
278
375
|
let workbench = null;
|
|
279
376
|
let initialResult = null;
|
|
377
|
+
let sessionRecord = null;
|
|
280
378
|
|
|
281
379
|
const cleanup = () => {
|
|
282
380
|
if (closed) return { name: currentNames[0] || null, unlinked: false, removed: [] };
|
|
@@ -284,10 +382,20 @@ export function startSkillDev(target, {
|
|
|
284
382
|
clearTimeout(timer);
|
|
285
383
|
if (watcher) watcher.close();
|
|
286
384
|
if (workbench) workbench.close();
|
|
287
|
-
|
|
385
|
+
if (sessionRecord) {
|
|
386
|
+
writeDevSession(repoRoot, {
|
|
387
|
+
...sessionRecord,
|
|
388
|
+
status: 'cleaning',
|
|
389
|
+
updated_at: new Date().toISOString(),
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
const removed = sessionRecord
|
|
393
|
+
? removeSkillLinksByPaths(repoRoot, sessionRecord.links || [], normalizeDisplayPath)
|
|
394
|
+
: removeSkillLinksByNames(repoRoot, currentNames, normalizeDisplayPath);
|
|
395
|
+
removeDevSession(repoRoot);
|
|
288
396
|
detachProcessCleanup();
|
|
289
397
|
return {
|
|
290
|
-
name: currentNames[0] || null,
|
|
398
|
+
name: sessionRecord?.root_skill?.name || currentNames[0] || null,
|
|
291
399
|
unlinked: removed.length > 0,
|
|
292
400
|
removed,
|
|
293
401
|
};
|
|
@@ -295,9 +403,14 @@ export function startSkillDev(target, {
|
|
|
295
403
|
|
|
296
404
|
const processCleanupHandlers = new Map();
|
|
297
405
|
const attachProcessCleanup = () => {
|
|
298
|
-
|
|
406
|
+
const exitHandler = () => cleanup();
|
|
407
|
+
processCleanupHandlers.set('exit', exitHandler);
|
|
408
|
+
process.once('exit', exitHandler);
|
|
409
|
+
|
|
410
|
+
for (const eventName of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
|
|
299
411
|
const handler = () => {
|
|
300
412
|
cleanup();
|
|
413
|
+
process.exit(0);
|
|
301
414
|
};
|
|
302
415
|
processCleanupHandlers.set(eventName, handler);
|
|
303
416
|
process.once(eventName, handler);
|
|
@@ -333,6 +446,8 @@ export function startSkillDev(target, {
|
|
|
333
446
|
removeSkillLinksByNames(repoRoot, staleNames, normalizeDisplayPath);
|
|
334
447
|
}
|
|
335
448
|
currentNames = nextNames;
|
|
449
|
+
sessionRecord = toDevSessionRecord(repoRoot, target, result, sessionRecord);
|
|
450
|
+
writeDevSession(repoRoot, sessionRecord);
|
|
336
451
|
return result;
|
|
337
452
|
};
|
|
338
453
|
|
|
@@ -390,8 +505,37 @@ export function startSkillDev(target, {
|
|
|
390
505
|
};
|
|
391
506
|
}
|
|
392
507
|
|
|
393
|
-
export function unlinkSkill(name, { cwd = process.cwd() } = {}) {
|
|
508
|
+
export function unlinkSkill(name, { cwd = process.cwd(), recursive = false } = {}) {
|
|
394
509
|
const repoRoot = findRepoRoot(cwd);
|
|
510
|
+
const session = readDevSession(repoRoot);
|
|
511
|
+
|
|
512
|
+
if (recursive) {
|
|
513
|
+
if (!session || session.root_skill?.name !== name) {
|
|
514
|
+
throw new AgentpackError('Recursive unlink requires the active dev-session root skill', {
|
|
515
|
+
code: 'linked_skill_recursive_unlink_requires_root',
|
|
516
|
+
exitCode: EXIT_CODES.GENERAL,
|
|
517
|
+
nextSteps: session?.root_skill?.name
|
|
518
|
+
? [{
|
|
519
|
+
action: 'run_command',
|
|
520
|
+
command: `agentpack skills unlink ${session.root_skill.name} --recursive`,
|
|
521
|
+
reason: 'Recursive unlink in v1 only works for the recorded dev-session root skill',
|
|
522
|
+
}]
|
|
523
|
+
: buildDevSessionNextSteps('agentpack skills dev cleanup --force'),
|
|
524
|
+
details: {
|
|
525
|
+
rootSkill: session?.root_skill?.name || null,
|
|
526
|
+
},
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const removed = removeSkillLinksByPaths(repoRoot, session.links || [], normalizeDisplayPath);
|
|
531
|
+
removeDevSession(repoRoot);
|
|
532
|
+
return {
|
|
533
|
+
name,
|
|
534
|
+
unlinked: removed.length > 0,
|
|
535
|
+
removed,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
|
|
395
539
|
const existing = [
|
|
396
540
|
join(repoRoot, '.claude', 'skills', name),
|
|
397
541
|
join(repoRoot, '.agents', 'skills', name),
|
|
@@ -413,6 +557,43 @@ export function unlinkSkill(name, { cwd = process.cwd() } = {}) {
|
|
|
413
557
|
};
|
|
414
558
|
}
|
|
415
559
|
|
|
560
|
+
export function cleanupSkillDevSession({ cwd = process.cwd(), force = false } = {}) {
|
|
561
|
+
const repoRoot = findRepoRoot(cwd);
|
|
562
|
+
const session = readDevSession(repoRoot);
|
|
563
|
+
if (!session) {
|
|
564
|
+
return {
|
|
565
|
+
cleaned: false,
|
|
566
|
+
active: false,
|
|
567
|
+
removed: [],
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (!force && session.status === 'active' && isProcessAlive(session.pid)) {
|
|
572
|
+
throw new AgentpackError('A skills dev session is still active in this repo', {
|
|
573
|
+
code: 'skills_dev_session_active',
|
|
574
|
+
exitCode: EXIT_CODES.GENERAL,
|
|
575
|
+
nextSteps: [
|
|
576
|
+
...buildDevSessionNextSteps('agentpack skills dev cleanup'),
|
|
577
|
+
...buildDevSessionNextSteps('agentpack skills dev cleanup --force'),
|
|
578
|
+
],
|
|
579
|
+
details: {
|
|
580
|
+
rootSkill: session.root_skill?.name || null,
|
|
581
|
+
pid: session.pid,
|
|
582
|
+
startedAt: session.started_at || null,
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const result = cleanupRecordedDevSession(repoRoot, session, 'stale');
|
|
588
|
+
return {
|
|
589
|
+
cleaned: true,
|
|
590
|
+
active: false,
|
|
591
|
+
forced: force,
|
|
592
|
+
name: session.root_skill?.name || null,
|
|
593
|
+
removed: result.removed,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
|
|
416
597
|
function buildValidateNextSteps(packageMetadata, valid) {
|
|
417
598
|
if (!valid || !packageMetadata.packageName) return [];
|
|
418
599
|
|