semantu-agents 1.3.1 → 1.4.0
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 +21 -9
- package/cli.mjs +117 -63
- package/lib/link-type.mjs +3 -0
- package/package.json +6 -3
- package/skills/public/explore/SKILL.md +2 -2
package/README.md
CHANGED
|
@@ -23,33 +23,45 @@ For normal users, rerun:
|
|
|
23
23
|
npx semantu-agents global
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
For contributors with
|
|
26
|
+
For contributors with a registered editable checkout, run this from anywhere:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
npx semantu-agents update
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
This updates
|
|
32
|
+
This updates the registered checkout's `main` branch, repairs its global skill links, then restores your previous branch when possible. If the checkout has uncommitted changes, commit or stash them first.
|
|
33
|
+
|
|
34
|
+
`update` requires a named Git branch. It refuses dirty checkouts and detached HEAD states.
|
|
33
35
|
|
|
34
36
|
## Contributing Skill Changes
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
Register an existing editable checkout:
|
|
37
39
|
|
|
38
40
|
```bash
|
|
41
|
+
cd /path/to/semantu-agents
|
|
39
42
|
npx semantu-agents dev
|
|
40
43
|
```
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
You can also provide the checkout explicitly:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx semantu-agents dev /path/to/semantu-agents
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Without a path, `dev` uses the current directory when it is the agents checkout, then a previously registered checkout, then `~/packages/semantu-agents`. It never clones another checkout.
|
|
52
|
+
|
|
53
|
+
The resolved path is stored in `~/.config/semantu-agents/config.json`. Each package-managed skill in `~/.claude/skills` and `~/.agents/skills` is linked to that checkout, so edits are available globally immediately. Unrelated installed skills are preserved.
|
|
54
|
+
|
|
55
|
+
Directory junctions are used on Windows; directory symlinks are used elsewhere.
|
|
43
56
|
|
|
44
|
-
Dev mode requires
|
|
57
|
+
Dev mode requires an existing checkout. Clone the repository separately first if needed.
|
|
45
58
|
|
|
46
59
|
Make changes in the source checkout only:
|
|
47
60
|
|
|
48
61
|
```bash
|
|
49
|
-
cd
|
|
62
|
+
cd /path/to/semantu-agents
|
|
50
63
|
git checkout -b my-change
|
|
51
64
|
# edit skills/public/... or skills/private/...
|
|
52
|
-
npm run sync
|
|
53
65
|
git add .
|
|
54
66
|
git commit -m "improve workflow skill"
|
|
55
67
|
git push -u origin my-change
|
|
@@ -61,9 +73,9 @@ Do not edit these generated folders directly:
|
|
|
61
73
|
- `~/.claude/skills`
|
|
62
74
|
- `~/.agents/skills`
|
|
63
75
|
|
|
64
|
-
|
|
76
|
+
In normal global mode, package-managed skills are copied from npm. In development mode, they are links managed by `dev`, `sync`, and `update`; edit the registered checkout instead.
|
|
65
77
|
|
|
66
|
-
|
|
78
|
+
Running `semantu-agents global` exits development mode and clears the registered checkout. Run `dev [path]` again to restore live links.
|
|
67
79
|
|
|
68
80
|
## Project Setup
|
|
69
81
|
|
package/cli.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import {execSync} from 'node:child_process';
|
|
4
|
-
import {cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync} from 'node:fs';
|
|
4
|
+
import {cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, symlinkSync, writeFileSync} from 'node:fs';
|
|
5
5
|
import {homedir} from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import {fileURLToPath} from 'node:url';
|
|
8
|
+
import {directoryLinkType} from './lib/link-type.mjs';
|
|
8
9
|
|
|
9
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const args = process.argv.slice(2);
|
|
@@ -38,15 +39,6 @@ function cloneOrPull(dir) {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
function cloneIfMissing(dir) {
|
|
42
|
-
if (existsSync(path.join(dir, '.git'))) {
|
|
43
|
-
console.log('Using existing editable agents repo at ~/.agents-src');
|
|
44
|
-
} else {
|
|
45
|
-
console.log('Cloning agents repo...');
|
|
46
|
-
run(`git clone ${skillsRepo} ${dir}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
42
|
function globalTargets() {
|
|
51
43
|
const home = homedir();
|
|
52
44
|
return [
|
|
@@ -55,93 +47,155 @@ function globalTargets() {
|
|
|
55
47
|
];
|
|
56
48
|
}
|
|
57
49
|
|
|
58
|
-
function
|
|
59
|
-
|
|
50
|
+
function skillSources(sourceDir, includePrivate) {
|
|
51
|
+
const sources = new Map();
|
|
52
|
+
const roots = [path.join(sourceDir, 'skills', 'public')];
|
|
53
|
+
if (includePrivate) roots.push(path.join(sourceDir, 'skills', 'private'));
|
|
60
54
|
|
|
61
|
-
for (const
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
for (const root of roots) {
|
|
56
|
+
if (!existsSync(root)) continue;
|
|
57
|
+
for (const name of readdirSync(root)) {
|
|
58
|
+
const source = path.join(root, name);
|
|
59
|
+
if (statSync(source).isDirectory()) sources.set(name, source);
|
|
60
|
+
}
|
|
64
61
|
}
|
|
62
|
+
|
|
63
|
+
return sources;
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
function
|
|
66
|
+
function installSkillsFromSource(sourceDir, label, options = {}) {
|
|
68
67
|
const includePrivate = options.includePrivate ?? true;
|
|
68
|
+
const mode = options.mode ?? 'copy';
|
|
69
69
|
const targets = globalTargets();
|
|
70
|
-
const
|
|
71
|
-
const privateSkills = path.join(sourceDir, 'skills', 'private');
|
|
70
|
+
const sources = skillSources(sourceDir, includePrivate);
|
|
72
71
|
|
|
73
72
|
for (const dest of targets) {
|
|
74
|
-
|
|
73
|
+
mkdirSync(dest, {recursive: true});
|
|
74
|
+
for (const [name, source] of sources) {
|
|
75
|
+
// Replace only skills managed by this package; preserve every other entry in the global directory.
|
|
76
|
+
const target = path.join(dest, name);
|
|
77
|
+
rmSync(target, {recursive: true, force: true});
|
|
78
|
+
if (mode === 'link') {
|
|
79
|
+
symlinkSync(source, target, directoryLinkType(process.platform));
|
|
80
|
+
} else {
|
|
81
|
+
cpSync(source, target, {recursive: true});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
75
84
|
}
|
|
76
85
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
console.log(`${mode === 'link' ? 'Linked' : 'Synced'} skills from ${label} -> ~/.claude/skills/ and ~/.agents/skills/`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function devConfigPath() {
|
|
90
|
+
return path.join(homedir(), '.config', 'semantu-agents', 'config.json');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function readDevConfig() {
|
|
94
|
+
const configPath = devConfigPath();
|
|
95
|
+
if (!existsSync(configPath)) return null;
|
|
96
|
+
try {
|
|
97
|
+
return JSON.parse(readFileSync(configPath, 'utf8'));
|
|
98
|
+
} catch {
|
|
99
|
+
console.error(`Invalid development configuration: ${configPath}`);
|
|
100
|
+
console.error('Rerun `npx semantu-agents dev [path]` after removing or repairing this file.');
|
|
101
|
+
process.exit(1);
|
|
80
102
|
}
|
|
103
|
+
}
|
|
81
104
|
|
|
82
|
-
|
|
105
|
+
function writeDevConfig(source) {
|
|
106
|
+
const configPath = devConfigPath();
|
|
107
|
+
mkdirSync(path.dirname(configPath), {recursive: true});
|
|
108
|
+
writeFileSync(configPath, `${JSON.stringify({source}, null, 2)}\n`);
|
|
83
109
|
}
|
|
84
110
|
|
|
85
|
-
function
|
|
86
|
-
|
|
87
|
-
console.log('\nDone. Public skills synced globally. Run `npx semantu-agents dev` to install an editable source checkout.');
|
|
111
|
+
function clearDevConfig() {
|
|
112
|
+
rmSync(devConfigPath(), {force: true});
|
|
88
113
|
}
|
|
89
114
|
|
|
90
|
-
function
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
writeFileSync(hookPath, hook, {mode: 0o755});
|
|
100
|
-
console.log('Installed post-merge hook to sync global skills after pulls');
|
|
115
|
+
function isAgentsCheckout(candidate) {
|
|
116
|
+
try {
|
|
117
|
+
const packageJson = JSON.parse(readFileSync(path.join(candidate, 'package.json'), 'utf8'));
|
|
118
|
+
return packageJson.name === 'semantu-agents' &&
|
|
119
|
+
existsSync(path.join(candidate, '.git')) &&
|
|
120
|
+
existsSync(path.join(candidate, 'skills', 'public'));
|
|
121
|
+
} catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
101
124
|
}
|
|
102
125
|
|
|
103
|
-
function
|
|
104
|
-
|
|
126
|
+
function resolveDevSource(inputPath) {
|
|
127
|
+
if (inputPath) {
|
|
128
|
+
const resolved = path.resolve(inputPath);
|
|
129
|
+
if (isAgentsCheckout(resolved)) return realpathSync(resolved);
|
|
130
|
+
console.error(`Not an editable semantu-agents checkout: ${resolved}`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const configured = readDevConfig()?.source;
|
|
135
|
+
const candidates = [
|
|
136
|
+
process.cwd(),
|
|
137
|
+
configured,
|
|
138
|
+
path.join(homedir(), 'packages', 'semantu-agents'),
|
|
139
|
+
].filter(Boolean);
|
|
140
|
+
|
|
141
|
+
for (const candidate of candidates) {
|
|
142
|
+
const resolved = path.resolve(candidate);
|
|
143
|
+
if (isAgentsCheckout(resolved)) return realpathSync(resolved);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.error('No editable semantu-agents checkout found.');
|
|
147
|
+
console.error('Run `npx semantu-agents dev /path/to/checkout` from an existing checkout.');
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
105
150
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
151
|
+
function setupGlobal() {
|
|
152
|
+
installSkillsFromSource(__dirname, 'npm package', {includePrivate: false});
|
|
153
|
+
clearDevConfig();
|
|
154
|
+
console.log('\nDone. Public skills synced globally. Run `npx semantu-agents dev [path]` to link an editable checkout.');
|
|
155
|
+
}
|
|
109
156
|
|
|
110
|
-
|
|
157
|
+
function setupDev(inputPath) {
|
|
158
|
+
const source = resolveDevSource(inputPath);
|
|
159
|
+
writeDevConfig(source);
|
|
160
|
+
installSkillsFromSource(source, source, {mode: 'link'});
|
|
161
|
+
console.log(`\nDone. Edit skills in ${source}; changes are available globally immediately.`);
|
|
111
162
|
}
|
|
112
163
|
|
|
113
164
|
function syncGlobalFromCurrentCheckout() {
|
|
114
|
-
|
|
165
|
+
setupDev(__dirname);
|
|
115
166
|
}
|
|
116
167
|
|
|
117
168
|
function updateDevCheckout() {
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
console.log('
|
|
122
|
-
console.log('Run `npx semantu-agents dev` if you have access to github.com:Semantu/agents.');
|
|
169
|
+
const source = readDevConfig()?.source;
|
|
170
|
+
if (!source || !isAgentsCheckout(source)) {
|
|
171
|
+
console.log('No registered editable checkout found.');
|
|
172
|
+
console.log('Run `npx semantu-agents dev [path]` from an existing checkout.');
|
|
123
173
|
process.exit(1);
|
|
124
174
|
}
|
|
125
175
|
|
|
126
|
-
const currentBranch = output('git branch --show-current', {cwd:
|
|
127
|
-
|
|
176
|
+
const currentBranch = output('git branch --show-current', {cwd: source});
|
|
177
|
+
if (!currentBranch) {
|
|
178
|
+
console.log(`Cannot update ${source} from a detached HEAD.`);
|
|
179
|
+
console.log('Switch to a named branch, then rerun `npx semantu-agents update`.');
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
const dirty = output('git status --porcelain', {cwd: source});
|
|
128
183
|
|
|
129
184
|
if (dirty) {
|
|
130
|
-
console.log(
|
|
185
|
+
console.log(`Cannot update ${source} because it has uncommitted changes.`);
|
|
131
186
|
console.log('Commit, stash, or discard them, then rerun `npx semantu-agents update`.');
|
|
132
187
|
process.exit(1);
|
|
133
188
|
}
|
|
134
189
|
|
|
135
|
-
run('git fetch origin main', {cwd:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
cwd:
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
run(`git switch ${currentBranch}`, {cwd: srcDir});
|
|
190
|
+
run('git fetch origin main', {cwd: source});
|
|
191
|
+
try {
|
|
192
|
+
if (currentBranch !== 'main') run('git switch main', {cwd: source});
|
|
193
|
+
run('git pull --ff-only origin main', {cwd: source});
|
|
194
|
+
installSkillsFromSource(source, source, {mode: 'link'});
|
|
195
|
+
} finally {
|
|
196
|
+
if (currentBranch && currentBranch !== 'main') {
|
|
197
|
+
run(`git switch ${currentBranch}`, {cwd: source});
|
|
198
|
+
}
|
|
145
199
|
}
|
|
146
200
|
}
|
|
147
201
|
|
|
@@ -403,7 +457,7 @@ if (command === 'docs') {
|
|
|
403
457
|
} else if (command === 'global' || isGlobal) {
|
|
404
458
|
setupGlobal();
|
|
405
459
|
} else if (command === 'dev') {
|
|
406
|
-
setupDev();
|
|
460
|
+
setupDev(args[1]);
|
|
407
461
|
} else if (command === 'sync') {
|
|
408
462
|
syncGlobalFromCurrentCheckout();
|
|
409
463
|
} else if (command === 'update') {
|
package/package.json
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "semantu-agents",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Reusable Claude Code skills and hooks for Semantu projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": "cli.mjs",
|
|
7
7
|
"files": [
|
|
8
8
|
"cli.mjs",
|
|
9
|
+
"lib",
|
|
9
10
|
"skills/public"
|
|
10
11
|
],
|
|
11
12
|
"scripts": {
|
|
12
|
-
"sync": "node cli.mjs sync"
|
|
13
|
+
"sync": "node cli.mjs sync",
|
|
14
|
+
"test": "node --test test/*.test.mjs"
|
|
13
15
|
},
|
|
14
16
|
"publishConfig": {
|
|
15
17
|
"access": "public"
|
|
16
18
|
},
|
|
17
19
|
"devDependencies": {
|
|
18
20
|
"@changesets/changelog-github": "^0.5.2",
|
|
19
|
-
"@changesets/cli": "^2.29.8"
|
|
21
|
+
"@changesets/cli": "^2.29.8",
|
|
22
|
+
"js-yaml": "^4.3.2"
|
|
20
23
|
},
|
|
21
24
|
"dependencies": {
|
|
22
25
|
"gray-matter": "^4.0.3"
|
|
@@ -19,8 +19,8 @@ Run this skill when the user explicitly says `explore <topic>`, clearly asks to
|
|
|
19
19
|
2. Decompose the topic into decision candidates and unknowns.
|
|
20
20
|
3. Prioritize planning blockers first when called from ideation or review iteration.
|
|
21
21
|
4. Before presenting options, explain the affected area, current behavior, wider context, and why the decision matters. Assume the user may not know the implementation. Include code, before/after, or concrete examples when useful.
|
|
22
|
-
5. Present exactly 3 viable approaches (A–C), each with pros, cons, and risks.
|
|
23
|
-
6.
|
|
22
|
+
5. Present exactly 3 viable approaches (A–C), each with pros, cons, and risks. Mark the suggested option in its heading with **(Recommended)**.
|
|
23
|
+
6. Immediately after that decision's options, add a separate **Recommendation** section naming the suggested option and why it is the best fit. Do this before presenting the next decision; never collect recommendations at the end of a batch.
|
|
24
24
|
7. If there are multiple decisions, present them in batches of 3 by default (use fewer only when fewer remain or when the user asks for a different batch size). Start with `Decision x-y of N`, and end with `Recap: 1A 2C 3B`; ask for agreement or edits in normal chat.
|
|
25
25
|
8. Iterate until consent; no feedback on a decision counts as acceptance after an explicit agreement prompt.
|
|
26
26
|
9. Recurse only for nested decisions that block planning. Default max depth is 2 unless the user asks for deeper drilldown.
|