td-ai-tools 1.0.6 → 1.0.8
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
CHANGED
|
@@ -33,6 +33,12 @@ npx td-ai-tools install
|
|
|
33
33
|
npx td-ai-tools install --all
|
|
34
34
|
npx td-ai-tools install pr-solver
|
|
35
35
|
npx td-ai-tools install pr-solver horizon-component-library
|
|
36
|
+
npx td-ai-tools update
|
|
37
|
+
npx td-ai-tools update --all
|
|
38
|
+
npx td-ai-tools update pr-solver
|
|
39
|
+
npx td-ai-tools delete
|
|
40
|
+
npx td-ai-tools delete --all
|
|
41
|
+
npx td-ai-tools delete pr-solver
|
|
36
42
|
```
|
|
37
43
|
|
|
38
44
|
The installer copies requested items into both agent layouts:
|
|
@@ -41,6 +47,10 @@ The installer copies requested items into both agent layouts:
|
|
|
41
47
|
|
|
42
48
|
This keeps the installed assets available to both Claude-style and `.agents`-style project conventions.
|
|
43
49
|
|
|
50
|
+
`install` now errors when the target item already exists. Use `update` to replace an existing installed skill or agent pack.
|
|
51
|
+
|
|
52
|
+
`delete` removes installed items from both `.claude/` and `.agents/` target directories, and works on any installed skill or agent pack regardless of whether it is in the catalogue.
|
|
53
|
+
|
|
44
54
|
## Local Verification
|
|
45
55
|
Run the local smoke test to package the repo and verify installation into a throwaway project:
|
|
46
56
|
|
package/bin/cli.js
CHANGED
|
@@ -44,7 +44,7 @@ function readFrontmatterField(mdPath, field) {
|
|
|
44
44
|
return match ? match[1].trim() : '';
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function installSkill(name) {
|
|
47
|
+
function installSkill(name, { replaceExisting = false } = {}) {
|
|
48
48
|
const src = path.join(SKILLS_DIR, name);
|
|
49
49
|
if (!fs.existsSync(src)) {
|
|
50
50
|
console.error(` [error] Skill "${name}" not found.`);
|
|
@@ -52,13 +52,38 @@ function installSkill(name) {
|
|
|
52
52
|
}
|
|
53
53
|
for (const target of INSTALL_TARGETS) {
|
|
54
54
|
const dest = path.join(TARGET_ROOT, target.root, 'skills', name);
|
|
55
|
+
if (fs.existsSync(dest) && !replaceExisting) {
|
|
56
|
+
console.error(` [error] Skill "${name}" already exists at ${target.root}/skills/${name}/ (use "update" to replace).`);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (fs.existsSync(dest) && replaceExisting) {
|
|
60
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
61
|
+
}
|
|
55
62
|
copyDir(src, dest);
|
|
56
|
-
|
|
63
|
+
const action = replaceExisting ? 'updated' : 'installed';
|
|
64
|
+
console.log(` [skill] ${name} ${action} -> ${target.root}/skills/${name}/`);
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function deleteSkill(name) {
|
|
70
|
+
let deletedAny = false;
|
|
71
|
+
for (const target of INSTALL_TARGETS) {
|
|
72
|
+
const dest = path.join(TARGET_ROOT, target.root, 'skills', name);
|
|
73
|
+
if (fs.existsSync(dest)) {
|
|
74
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
75
|
+
console.log(` [skill] ${name} deleted from ${target.root}/skills/${name}/`);
|
|
76
|
+
deletedAny = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!deletedAny) {
|
|
80
|
+
console.error(` [error] Skill "${name}" is not installed.`);
|
|
81
|
+
return false;
|
|
57
82
|
}
|
|
58
83
|
return true;
|
|
59
84
|
}
|
|
60
85
|
|
|
61
|
-
function installAgent(name) {
|
|
86
|
+
function installAgent(name, { replaceExisting = false } = {}) {
|
|
62
87
|
const src = path.join(AGENTS_DIR, name);
|
|
63
88
|
if (!fs.existsSync(src)) {
|
|
64
89
|
console.error(` [error] Agent pack "${name}" not found.`);
|
|
@@ -66,12 +91,52 @@ function installAgent(name) {
|
|
|
66
91
|
}
|
|
67
92
|
for (const target of INSTALL_TARGETS) {
|
|
68
93
|
const dest = path.join(TARGET_ROOT, target.root, 'agents', name);
|
|
94
|
+
if (fs.existsSync(dest) && !replaceExisting) {
|
|
95
|
+
console.error(` [error] Agent pack "${name}" already exists at ${target.root}/agents/${name}/ (use "update" to replace).`);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if (fs.existsSync(dest) && replaceExisting) {
|
|
99
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
100
|
+
}
|
|
69
101
|
copyDir(src, dest);
|
|
70
|
-
|
|
102
|
+
const action = replaceExisting ? 'updated' : 'installed';
|
|
103
|
+
console.log(` [agent] ${name} ${action} -> ${target.root}/agents/${name}/`);
|
|
71
104
|
}
|
|
72
105
|
return true;
|
|
73
106
|
}
|
|
74
107
|
|
|
108
|
+
function deleteAgent(name) {
|
|
109
|
+
let deletedAny = false;
|
|
110
|
+
for (const target of INSTALL_TARGETS) {
|
|
111
|
+
const dest = path.join(TARGET_ROOT, target.root, 'agents', name);
|
|
112
|
+
if (fs.existsSync(dest)) {
|
|
113
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
114
|
+
console.log(` [agent] ${name} deleted from ${target.root}/agents/${name}/`);
|
|
115
|
+
deletedAny = true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!deletedAny) {
|
|
119
|
+
console.error(` [error] Agent pack "${name}" is not installed.`);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function getInstalled(type) {
|
|
126
|
+
const installed = new Set();
|
|
127
|
+
for (const target of INSTALL_TARGETS) {
|
|
128
|
+
const dir = path.join(TARGET_ROOT, target.root, type);
|
|
129
|
+
if (fs.existsSync(dir)) {
|
|
130
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
131
|
+
if (fs.statSync(path.join(dir, entry)).isDirectory()) {
|
|
132
|
+
installed.add(entry);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return [...installed].sort();
|
|
138
|
+
}
|
|
139
|
+
|
|
75
140
|
function printList() {
|
|
76
141
|
const skills = getAvailable(SKILLS_DIR);
|
|
77
142
|
const agents = getAvailable(AGENTS_DIR);
|
|
@@ -97,14 +162,27 @@ function buildMenu() {
|
|
|
97
162
|
return [...skills, ...agents];
|
|
98
163
|
}
|
|
99
164
|
|
|
100
|
-
function
|
|
165
|
+
function buildDeleteMenu() {
|
|
166
|
+
const skills = getInstalled('skills').map(name => ({ type: 'skill', name }));
|
|
167
|
+
const agents = getInstalled('agents').map(name => ({ type: 'agent', name }));
|
|
168
|
+
return [...skills, ...agents];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function installItems(items, options = {}) {
|
|
172
|
+
for (const item of items) {
|
|
173
|
+
if (item.type === 'skill') installSkill(item.name, options);
|
|
174
|
+
else installAgent(item.name, options);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function deleteItems(items) {
|
|
101
179
|
for (const item of items) {
|
|
102
|
-
if (item.type === 'skill')
|
|
103
|
-
else
|
|
180
|
+
if (item.type === 'skill') deleteSkill(item.name);
|
|
181
|
+
else deleteAgent(item.name);
|
|
104
182
|
}
|
|
105
183
|
}
|
|
106
184
|
|
|
107
|
-
async function interactiveInstall() {
|
|
185
|
+
async function interactiveInstall(mode = 'install') {
|
|
108
186
|
const menu = buildMenu();
|
|
109
187
|
if (menu.length === 0) {
|
|
110
188
|
console.log('No skills or agent packs available.');
|
|
@@ -120,7 +198,40 @@ async function interactiveInstall() {
|
|
|
120
198
|
|
|
121
199
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
122
200
|
return new Promise(resolve => {
|
|
123
|
-
rl.question(
|
|
201
|
+
rl.question(`Select items to ${mode}: `, answer => {
|
|
202
|
+
rl.close();
|
|
203
|
+
const trimmed = answer.trim().toLowerCase();
|
|
204
|
+
let selected;
|
|
205
|
+
if (trimmed === 'all') {
|
|
206
|
+
selected = menu;
|
|
207
|
+
} else {
|
|
208
|
+
const indices = trimmed.split(/\s+/).map(n => parseInt(n, 10) - 1);
|
|
209
|
+
selected = indices.filter(i => i >= 0 && i < menu.length).map(i => menu[i]);
|
|
210
|
+
}
|
|
211
|
+
console.log('');
|
|
212
|
+
installItems(selected, { replaceExisting: mode === 'update' });
|
|
213
|
+
resolve();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function interactiveDelete() {
|
|
219
|
+
const menu = buildDeleteMenu();
|
|
220
|
+
if (menu.length === 0) {
|
|
221
|
+
console.log('No installed skills or agent packs to delete.');
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
console.log('\nInstalled (enter numbers separated by spaces, or "all"):\n');
|
|
226
|
+
menu.forEach((item, i) => {
|
|
227
|
+
const label = item.type === 'skill' ? 'skill' : 'agent';
|
|
228
|
+
console.log(` [${i + 1}] ${label}: ${item.name}`);
|
|
229
|
+
});
|
|
230
|
+
console.log('');
|
|
231
|
+
|
|
232
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
233
|
+
return new Promise(resolve => {
|
|
234
|
+
rl.question('Select items to delete: ', answer => {
|
|
124
235
|
rl.close();
|
|
125
236
|
const trimmed = answer.trim().toLowerCase();
|
|
126
237
|
let selected;
|
|
@@ -131,7 +242,7 @@ async function interactiveInstall() {
|
|
|
131
242
|
selected = indices.filter(i => i >= 0 && i < menu.length).map(i => menu[i]);
|
|
132
243
|
}
|
|
133
244
|
console.log('');
|
|
134
|
-
|
|
245
|
+
deleteItems(selected);
|
|
135
246
|
resolve();
|
|
136
247
|
});
|
|
137
248
|
});
|
|
@@ -153,6 +264,22 @@ function resolveNames(names) {
|
|
|
153
264
|
return items;
|
|
154
265
|
}
|
|
155
266
|
|
|
267
|
+
function resolveDeleteNames(names) {
|
|
268
|
+
const installedSkills = getInstalled('skills');
|
|
269
|
+
const installedAgents = getInstalled('agents');
|
|
270
|
+
const items = [];
|
|
271
|
+
for (const name of names) {
|
|
272
|
+
if (installedSkills.includes(name)) {
|
|
273
|
+
items.push({ type: 'skill', name });
|
|
274
|
+
} else if (installedAgents.includes(name)) {
|
|
275
|
+
items.push({ type: 'agent', name });
|
|
276
|
+
} else {
|
|
277
|
+
console.error(` [error] "${name}" is not installed.`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return items;
|
|
281
|
+
}
|
|
282
|
+
|
|
156
283
|
async function main() {
|
|
157
284
|
const args = process.argv.slice(2);
|
|
158
285
|
|
|
@@ -164,6 +291,12 @@ Usage:
|
|
|
164
291
|
npx td-ai-tools install Interactive install
|
|
165
292
|
npx td-ai-tools install --all Install everything
|
|
166
293
|
npx td-ai-tools install <name...> Install specific skills or agent packs
|
|
294
|
+
npx td-ai-tools update Interactive update (replaces existing items)
|
|
295
|
+
npx td-ai-tools update --all Update everything
|
|
296
|
+
npx td-ai-tools update <name...> Update specific skills or agent packs
|
|
297
|
+
npx td-ai-tools delete Interactive delete
|
|
298
|
+
npx td-ai-tools delete --all Delete everything
|
|
299
|
+
npx td-ai-tools delete <name...> Delete specific skills or agent packs
|
|
167
300
|
|
|
168
301
|
Skills are installed to: .claude/skills/<name>/
|
|
169
302
|
.agents/skills/<name>/
|
|
@@ -178,7 +311,7 @@ Agent packs installed to: .claude/agents/<name>/
|
|
|
178
311
|
console.log('------------');
|
|
179
312
|
|
|
180
313
|
if (!cmd || cmd === 'install' && args.length === 1) {
|
|
181
|
-
await interactiveInstall();
|
|
314
|
+
await interactiveInstall('install');
|
|
182
315
|
return;
|
|
183
316
|
}
|
|
184
317
|
|
|
@@ -199,6 +332,38 @@ Agent packs installed to: .claude/agents/<name>/
|
|
|
199
332
|
return;
|
|
200
333
|
}
|
|
201
334
|
|
|
335
|
+
if (cmd === 'update') {
|
|
336
|
+
const rest = args.slice(1);
|
|
337
|
+
if (rest.length === 0) {
|
|
338
|
+
await interactiveInstall('update');
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (rest[0] === '--all') {
|
|
342
|
+
console.log('');
|
|
343
|
+
installItems(buildMenu(), { replaceExisting: true });
|
|
344
|
+
} else {
|
|
345
|
+
console.log('');
|
|
346
|
+
installItems(resolveNames(rest), { replaceExisting: true });
|
|
347
|
+
}
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (cmd === 'delete') {
|
|
352
|
+
const rest = args.slice(1);
|
|
353
|
+
if (rest.length === 0) {
|
|
354
|
+
await interactiveDelete();
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (rest[0] === '--all') {
|
|
358
|
+
console.log('');
|
|
359
|
+
deleteItems(buildDeleteMenu());
|
|
360
|
+
} else {
|
|
361
|
+
console.log('');
|
|
362
|
+
deleteItems(resolveDeleteNames(rest));
|
|
363
|
+
}
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
202
367
|
console.error(`Unknown command: "${cmd}". Run with --help for usage.`);
|
|
203
368
|
process.exit(1);
|
|
204
369
|
}
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@ machine or one project clone location.
|
|
|
8
8
|
|
|
9
9
|
- `config.toml.template` — the source of truth. Uses two placeholders:
|
|
10
10
|
- `__PROJECT_ROOT__` — replaced with the absolute path to the repo root
|
|
11
|
-
- `
|
|
11
|
+
- `__BROWSER_PATH__` — replaced with the absolute path to the Chromium/Chrome binary
|
|
12
12
|
- `setup-codex.sh` — reads the template, substitutes the placeholders, writes
|
|
13
13
|
`<repo>/.codex/config.toml`.
|
|
14
14
|
|
|
@@ -27,7 +27,7 @@ The script prints the values it resolved, e.g.:
|
|
|
27
27
|
```
|
|
28
28
|
wrote /home/you/code/lio/.codex/config.toml
|
|
29
29
|
PROJECT_ROOT=/home/you/code/lio
|
|
30
|
-
|
|
30
|
+
BROWSER_PATH=/usr/bin/chromium
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
Re-run it any time the template changes or you move the repo.
|
|
@@ -41,17 +41,19 @@ Re-run it any time the template changes or you move the repo.
|
|
|
41
41
|
2. Falls back to walking three directories up from the script
|
|
42
42
|
(`.agents/skills/playwright-cli` → repo root) if the repo isn't a git checkout
|
|
43
43
|
|
|
44
|
-
**`
|
|
44
|
+
**`BROWSER_PATH`** — derived in this order:
|
|
45
45
|
|
|
46
|
-
1. The `
|
|
47
|
-
2.
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
1. The `BROWSER_PATH` environment variable, if set
|
|
47
|
+
2. The legacy `CHROME_PATH` environment variable, if set (kept for backwards compat)
|
|
48
|
+
3. First match from `PATH` of: `chromium`, `chromium-browser`, `google-chrome`,
|
|
49
|
+
`google-chrome-stable` — Chromium is preferred because Chrome is often missing
|
|
50
|
+
on WSL shells
|
|
51
|
+
4. Falls back to `/usr/bin/chromium` with a warning if nothing is found
|
|
50
52
|
|
|
51
|
-
Override the
|
|
53
|
+
Override the browser binary explicitly when needed:
|
|
52
54
|
|
|
53
55
|
```bash
|
|
54
|
-
|
|
56
|
+
BROWSER_PATH=/usr/bin/chromium .agents/skills/playwright-cli/setup-codex.sh
|
|
55
57
|
```
|
|
56
58
|
|
|
57
59
|
## Why a generator instead of variable expansion
|
|
@@ -158,6 +158,7 @@ playwright-cli video-stop video.webm
|
|
|
158
158
|
## Open parameters
|
|
159
159
|
```bash
|
|
160
160
|
# Use specific browser when creating session
|
|
161
|
+
playwright-cli open --browser=chromium
|
|
161
162
|
playwright-cli open --browser=chrome
|
|
162
163
|
playwright-cli open --browser=firefox
|
|
163
164
|
playwright-cli open --browser=webkit
|
|
@@ -19,10 +19,10 @@ inherit = "all"
|
|
|
19
19
|
|
|
20
20
|
[shell_environment_policy.set]
|
|
21
21
|
PLAYWRIGHT_BROWSERS_PATH = "__PROJECT_ROOT__/.artifacts/playwright/ms-playwright"
|
|
22
|
-
PLAYWRIGHT_MCP_BROWSER = "
|
|
23
|
-
PLAYWRIGHT_MCP_EXECUTABLE_PATH = "
|
|
24
|
-
PLAYWRIGHT_DAEMON_SOCKETS_DIR = "__PROJECT_ROOT__/.
|
|
25
|
-
BROWSER = "
|
|
22
|
+
PLAYWRIGHT_MCP_BROWSER = "chromium"
|
|
23
|
+
PLAYWRIGHT_MCP_EXECUTABLE_PATH = "__BROWSER_PATH__"
|
|
24
|
+
PLAYWRIGHT_DAEMON_SOCKETS_DIR = "__PROJECT_ROOT__/.tmp/codex-playwright/sockets"
|
|
25
|
+
BROWSER = "chromium"
|
|
26
26
|
HOME = "__PROJECT_ROOT__/.tmp/codex-playwright/home"
|
|
27
27
|
XDG_CONFIG_HOME = "__PROJECT_ROOT__/.tmp/codex-playwright/config"
|
|
28
28
|
XDG_STATE_HOME = "__PROJECT_ROOT__/.tmp/codex-playwright/state"
|
|
@@ -11,19 +11,19 @@ else
|
|
|
11
11
|
fi
|
|
12
12
|
OUTPUT="$PROJECT_ROOT/.codex/config.toml"
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
if [ -z "$
|
|
16
|
-
echo "warning: no Chrome
|
|
17
|
-
|
|
14
|
+
BROWSER_PATH="${BROWSER_PATH:-${CHROME_PATH:-$(command -v chromium || command -v chromium-browser || command -v google-chrome || command -v google-chrome-stable || true)}}"
|
|
15
|
+
if [ -z "$BROWSER_PATH" ]; then
|
|
16
|
+
echo "warning: no Chromium/Chrome binary found on PATH; set BROWSER_PATH=/path/to/browser and rerun" >&2
|
|
17
|
+
BROWSER_PATH="/usr/bin/chromium"
|
|
18
18
|
fi
|
|
19
19
|
|
|
20
20
|
mkdir -p "$(dirname "$OUTPUT")"
|
|
21
21
|
|
|
22
22
|
sed \
|
|
23
23
|
-e "s|__PROJECT_ROOT__|$PROJECT_ROOT|g" \
|
|
24
|
-
-e "s|
|
|
24
|
+
-e "s|__BROWSER_PATH__|$BROWSER_PATH|g" \
|
|
25
25
|
"$TEMPLATE" > "$OUTPUT"
|
|
26
26
|
|
|
27
27
|
echo "wrote $OUTPUT"
|
|
28
28
|
echo " PROJECT_ROOT=$PROJECT_ROOT"
|
|
29
|
-
echo "
|
|
29
|
+
echo " BROWSER_PATH=$BROWSER_PATH"
|