agentcraft 0.0.3 → 0.0.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/.claude-plugin/plugin.json +1 -1
- package/README.md +6 -5
- package/bin/agentcraft.js +135 -64
- package/opencode.js +29 -10
- package/package.json +1 -1
- package/skills/packs/SKILL.md +160 -0
- package/skills/packs/references/pack-format.md +122 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentcraft",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Assign sounds to AI coding agent lifecycle events. Works with Claude Code and OpenCode.",
|
|
5
5
|
"author": { "name": "rohenaz" },
|
|
6
6
|
"keywords": ["sounds", "hooks", "audio", "productivity", "opencode", "claude-code"]
|
package/README.md
CHANGED
|
@@ -49,11 +49,12 @@ bun install -g agentcraft
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
|
-
agentcraft
|
|
53
|
-
agentcraft
|
|
54
|
-
agentcraft
|
|
55
|
-
agentcraft
|
|
56
|
-
agentcraft
|
|
52
|
+
agentcraft init # first-time setup
|
|
53
|
+
agentcraft add rohenaz/agentcraft-sounds # install a pack from GitHub
|
|
54
|
+
agentcraft list # list installed packs
|
|
55
|
+
agentcraft update rohenaz/agentcraft-sounds # update a pack (git pull)
|
|
56
|
+
agentcraft update # update all packs
|
|
57
|
+
agentcraft remove rohenaz/agentcraft-sounds # remove a pack
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
## Sound Packs
|
package/bin/agentcraft.js
CHANGED
|
@@ -2,20 +2,30 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const { execSync, spawnSync } = require('child_process');
|
|
5
|
-
const { existsSync, readdirSync, rmSync, statSync, mkdirSync } = require('fs');
|
|
6
|
-
const { join } = require('path');
|
|
5
|
+
const { existsSync, readdirSync, rmSync, statSync, mkdirSync, readFileSync, copyFileSync, realpathSync } = require('fs');
|
|
6
|
+
const { join, dirname } = require('path');
|
|
7
7
|
const { homedir } = require('os');
|
|
8
8
|
|
|
9
9
|
const PACKS_DIR = join(homedir(), '.agentcraft', 'packs');
|
|
10
|
+
const ASSIGNMENTS_PATH = join(homedir(), '.agentcraft', 'assignments.json');
|
|
10
11
|
const [,, cmd, sub, ...rest] = process.argv;
|
|
11
12
|
|
|
13
|
+
// ANSI colors — no dependencies
|
|
14
|
+
const c = {
|
|
15
|
+
cyan: s => `\x1b[36m${s}\x1b[0m`,
|
|
16
|
+
green: s => `\x1b[32m${s}\x1b[0m`,
|
|
17
|
+
red: s => `\x1b[31m${s}\x1b[0m`,
|
|
18
|
+
dim: s => `\x1b[2m${s}\x1b[0m`,
|
|
19
|
+
bold: s => `\x1b[1m${s}\x1b[0m`,
|
|
20
|
+
};
|
|
21
|
+
|
|
12
22
|
function ensureDir(p) {
|
|
13
23
|
mkdirSync(p, { recursive: true });
|
|
14
24
|
}
|
|
15
25
|
|
|
16
26
|
function parsePackId(arg) {
|
|
17
27
|
if (!arg || !arg.includes('/')) {
|
|
18
|
-
console.error(
|
|
28
|
+
console.error(c.red(`✗ pack must be "publisher/name", got: ${arg ?? '(nothing)'}`));
|
|
19
29
|
process.exit(1);
|
|
20
30
|
}
|
|
21
31
|
const slash = arg.indexOf('/');
|
|
@@ -24,133 +34,194 @@ function parsePackId(arg) {
|
|
|
24
34
|
return { publisher, name, id: arg, url: `https://github.com/${publisher}/${name}` };
|
|
25
35
|
}
|
|
26
36
|
|
|
37
|
+
function readPackMeta(packPath) {
|
|
38
|
+
try { return JSON.parse(readFileSync(join(packPath, 'pack.json'), 'utf-8')); } catch { return {}; }
|
|
39
|
+
}
|
|
40
|
+
|
|
27
41
|
function getInstalledPacks() {
|
|
28
42
|
if (!existsSync(PACKS_DIR)) return [];
|
|
29
43
|
const packs = [];
|
|
30
44
|
for (const publisher of readdirSync(PACKS_DIR)) {
|
|
31
45
|
const ppath = join(PACKS_DIR, publisher);
|
|
32
|
-
try {
|
|
33
|
-
if (!statSync(ppath).isDirectory()) continue;
|
|
34
|
-
} catch { continue; }
|
|
46
|
+
try { if (!statSync(ppath).isDirectory()) continue; } catch { continue; }
|
|
35
47
|
for (const name of readdirSync(ppath)) {
|
|
36
|
-
try {
|
|
37
|
-
if (statSync(join(ppath, name)).isDirectory()) packs.push(`${publisher}/${name}`);
|
|
38
|
-
} catch { continue; }
|
|
48
|
+
try { if (statSync(join(ppath, name)).isDirectory()) packs.push(`${publisher}/${name}`); } catch { continue; }
|
|
39
49
|
}
|
|
40
50
|
}
|
|
41
51
|
return packs;
|
|
42
52
|
}
|
|
43
53
|
|
|
44
|
-
function
|
|
54
|
+
function packAdd(packArg) {
|
|
45
55
|
const { publisher, name, url } = parsePackId(packArg);
|
|
46
56
|
const dest = join(PACKS_DIR, publisher, name);
|
|
47
57
|
if (existsSync(dest)) {
|
|
48
|
-
console.log(`Already installed: ${publisher}/${name}`);
|
|
58
|
+
console.log(c.dim(`Already installed: ${publisher}/${name}`));
|
|
49
59
|
process.exit(0);
|
|
50
60
|
}
|
|
51
61
|
ensureDir(join(PACKS_DIR, publisher));
|
|
52
|
-
console.log(
|
|
62
|
+
console.log(`→ Installing ${c.cyan(`${publisher}/${name}`)} from ${c.dim(url)} ...`);
|
|
53
63
|
const r = spawnSync('git', ['clone', url, dest], { stdio: 'inherit' });
|
|
54
64
|
if (r.status !== 0) {
|
|
55
|
-
console.error(
|
|
65
|
+
console.error(c.red(`✗ Failed to install ${publisher}/${name}`));
|
|
56
66
|
process.exit(1);
|
|
57
67
|
}
|
|
58
|
-
console.log(
|
|
68
|
+
console.log(c.green(`✓ Installed: ${publisher}/${name}`));
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
function packRemove(packArg) {
|
|
62
72
|
const { publisher, name } = parsePackId(packArg);
|
|
63
73
|
const dest = join(PACKS_DIR, publisher, name);
|
|
64
74
|
if (!existsSync(dest)) {
|
|
65
|
-
console.error(
|
|
75
|
+
console.error(c.red(`✗ Not installed: ${publisher}/${name}`));
|
|
66
76
|
process.exit(1);
|
|
67
77
|
}
|
|
68
78
|
rmSync(dest, { recursive: true, force: true });
|
|
69
|
-
console.log(
|
|
79
|
+
console.log(c.green(`✓ Removed: ${publisher}/${name}`));
|
|
70
80
|
}
|
|
71
81
|
|
|
72
82
|
function packUpdate(packArg) {
|
|
73
83
|
const { publisher, name } = parsePackId(packArg);
|
|
74
84
|
const dest = join(PACKS_DIR, publisher, name);
|
|
75
85
|
if (!existsSync(dest)) {
|
|
76
|
-
console.error(
|
|
86
|
+
console.error(c.red(`✗ Not installed: ${publisher}/${name}`));
|
|
87
|
+
console.error(c.dim(` Run: agentcraft add ${publisher}/${name}`));
|
|
77
88
|
process.exit(1);
|
|
78
89
|
}
|
|
79
|
-
console.log(
|
|
90
|
+
console.log(`→ Updating ${c.cyan(`${publisher}/${name}`)} ...`);
|
|
80
91
|
spawnSync('git', ['-C', dest, 'pull'], { stdio: 'inherit' });
|
|
81
92
|
}
|
|
82
93
|
|
|
83
94
|
function packList() {
|
|
84
95
|
const packs = getInstalledPacks();
|
|
85
96
|
if (!packs.length) {
|
|
86
|
-
console.log('No packs installed.');
|
|
87
|
-
console.log('
|
|
88
|
-
console.log('
|
|
97
|
+
console.log(c.dim('No packs installed.'));
|
|
98
|
+
console.log('');
|
|
99
|
+
console.log('Install the official pack:');
|
|
100
|
+
console.log(` ${c.cyan('agentcraft add rohenaz/agentcraft-sounds')}`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
console.log(c.bold('Installed packs:'));
|
|
104
|
+
for (const p of packs) {
|
|
105
|
+
const meta = readPackMeta(join(PACKS_DIR, ...p.split('/')));
|
|
106
|
+
const version = meta.version ? c.dim(` v${meta.version}`) : '';
|
|
107
|
+
const desc = meta.description ? c.dim(` ${meta.description}`) : '';
|
|
108
|
+
console.log(` ${c.cyan(p)}${version}${desc}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function packInit() {
|
|
113
|
+
console.log(c.bold('Initializing AgentCraft...'));
|
|
114
|
+
console.log('');
|
|
115
|
+
|
|
116
|
+
// 1. Install official pack if missing
|
|
117
|
+
const officialPack = join(PACKS_DIR, 'rohenaz', 'agentcraft-sounds');
|
|
118
|
+
if (!existsSync(officialPack)) {
|
|
119
|
+
packAdd('rohenaz/agentcraft-sounds');
|
|
89
120
|
} else {
|
|
90
|
-
console.log(
|
|
91
|
-
|
|
121
|
+
console.log(c.dim(`✓ Official pack already installed`));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 2. Create assignments.json from pack defaults if missing
|
|
125
|
+
if (!existsSync(ASSIGNMENTS_PATH)) {
|
|
126
|
+
const defaultsPath = join(officialPack, 'defaults', 'assignments.json');
|
|
127
|
+
if (existsSync(defaultsPath)) {
|
|
128
|
+
ensureDir(join(homedir(), '.agentcraft'));
|
|
129
|
+
copyFileSync(defaultsPath, ASSIGNMENTS_PATH);
|
|
130
|
+
console.log(c.green('✓ Created ~/.agentcraft/assignments.json'));
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
console.log(c.dim('✓ assignments.json already exists'));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
console.log('');
|
|
137
|
+
console.log(c.green('✓ AgentCraft ready!'));
|
|
138
|
+
console.log(` Dashboard: ${c.cyan('agentcraft start')}`);
|
|
139
|
+
console.log(` Browse packs: ${c.cyan('agentcraft list')}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function getWebDir() {
|
|
143
|
+
if (process.env.CLAUDE_PLUGIN_ROOT) {
|
|
144
|
+
return join(process.env.CLAUDE_PLUGIN_ROOT, 'web');
|
|
145
|
+
}
|
|
146
|
+
// Global bun/npm install: bin/agentcraft.js → package root → web/
|
|
147
|
+
try {
|
|
148
|
+
return join(dirname(dirname(realpathSync(__filename))), 'web');
|
|
149
|
+
} catch {
|
|
150
|
+
console.error(c.red('✗ Cannot locate web directory. Set CLAUDE_PLUGIN_ROOT or reinstall.'));
|
|
151
|
+
process.exit(1);
|
|
92
152
|
}
|
|
93
153
|
}
|
|
94
154
|
|
|
95
155
|
function showHelp() {
|
|
96
156
|
console.log(`
|
|
97
|
-
AgentCraft
|
|
157
|
+
${c.bold('AgentCraft')} — assign sounds to AI agent lifecycle events
|
|
98
158
|
|
|
99
|
-
Usage:
|
|
100
|
-
agentcraft
|
|
101
|
-
agentcraft
|
|
102
|
-
agentcraft
|
|
103
|
-
agentcraft
|
|
104
|
-
agentcraft
|
|
105
|
-
agentcraft start
|
|
159
|
+
${c.cyan('Usage:')}
|
|
160
|
+
agentcraft init Set up AgentCraft (install pack + config)
|
|
161
|
+
agentcraft add <publisher/name> Install a sound pack from GitHub
|
|
162
|
+
agentcraft remove <publisher/name> Remove an installed pack
|
|
163
|
+
agentcraft update [publisher/name] Update a pack, or all packs if no arg given
|
|
164
|
+
agentcraft list List installed packs
|
|
165
|
+
agentcraft start Launch the dashboard (port 4040)
|
|
106
166
|
|
|
107
|
-
Examples:
|
|
108
|
-
agentcraft
|
|
109
|
-
agentcraft
|
|
110
|
-
agentcraft pack
|
|
167
|
+
${c.cyan('Examples:')}
|
|
168
|
+
agentcraft init
|
|
169
|
+
agentcraft add rohenaz/agentcraft-sounds
|
|
170
|
+
agentcraft add publisher/custom-pack
|
|
171
|
+
agentcraft update
|
|
172
|
+
agentcraft list
|
|
111
173
|
|
|
112
|
-
Packs are stored at: ~/.agentcraft/packs/<publisher>/<name>/
|
|
113
|
-
Any git repo cloned there is automatically discovered by the dashboard.
|
|
174
|
+
${c.dim('Packs are stored at: ~/.agentcraft/packs/<publisher>/<name>/')}
|
|
175
|
+
${c.dim('Any git repo cloned there is automatically discovered by the dashboard.')}
|
|
114
176
|
|
|
115
|
-
Install the Claude Code plugin:
|
|
177
|
+
${c.cyan('Install the Claude Code plugin:')}
|
|
116
178
|
claude plugin install agentcraft@rohenaz
|
|
117
179
|
`);
|
|
118
180
|
}
|
|
119
181
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
packUpdate(rest[0]);
|
|
135
|
-
}
|
|
136
|
-
} else if (sub === 'list') {
|
|
137
|
-
packList();
|
|
182
|
+
// Route commands
|
|
183
|
+
if (cmd === 'init') {
|
|
184
|
+
packInit();
|
|
185
|
+
} else if (cmd === 'add') {
|
|
186
|
+
if (!sub) { console.error(c.red('Usage: agentcraft add <publisher/name>')); process.exit(1); }
|
|
187
|
+
packAdd(sub);
|
|
188
|
+
} else if (cmd === 'remove') {
|
|
189
|
+
if (!sub) { console.error(c.red('Usage: agentcraft remove <publisher/name>')); process.exit(1); }
|
|
190
|
+
packRemove(sub);
|
|
191
|
+
} else if (cmd === 'update') {
|
|
192
|
+
if (!sub) {
|
|
193
|
+
const packs = getInstalledPacks();
|
|
194
|
+
if (!packs.length) { console.log(c.dim('No packs installed.')); process.exit(0); }
|
|
195
|
+
for (const p of packs) packUpdate(p);
|
|
138
196
|
} else {
|
|
139
|
-
|
|
140
|
-
console.error('Usage: agentcraft pack <install|remove|update|list>');
|
|
141
|
-
process.exit(1);
|
|
197
|
+
packUpdate(sub);
|
|
142
198
|
}
|
|
199
|
+
} else if (cmd === 'list') {
|
|
200
|
+
packList();
|
|
143
201
|
} else if (cmd === 'start') {
|
|
144
|
-
const
|
|
145
|
-
if (!
|
|
146
|
-
console.error(
|
|
202
|
+
const webDir = getWebDir();
|
|
203
|
+
if (!existsSync(webDir)) {
|
|
204
|
+
console.error(c.red(`✗ Web directory not found: ${webDir}`));
|
|
147
205
|
process.exit(1);
|
|
148
206
|
}
|
|
149
|
-
|
|
207
|
+
console.log(`→ Starting AgentCraft at ${c.cyan('http://localhost:4040')} ...`);
|
|
208
|
+
execSync(`cd "${webDir}" && bun install --silent && bun dev --port 4040`, { stdio: 'inherit' });
|
|
150
209
|
} else if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
151
210
|
showHelp();
|
|
211
|
+
} else if (cmd === 'pack') {
|
|
212
|
+
// Legacy shim — print migration hint and route through
|
|
213
|
+
const newCmd = sub === 'install' ? 'add' : sub;
|
|
214
|
+
console.error(c.dim(`Note: "agentcraft pack ${sub}" → "agentcraft ${newCmd}"`));
|
|
215
|
+
if (sub === 'install') { if (!rest[0]) { console.error(c.red('Usage: agentcraft add <publisher/name>')); process.exit(1); } packAdd(rest[0]); }
|
|
216
|
+
else if (sub === 'remove') { if (!rest[0]) { console.error(c.red('Usage: agentcraft remove <publisher/name>')); process.exit(1); } packRemove(rest[0]); }
|
|
217
|
+
else if (sub === 'update') {
|
|
218
|
+
if (!rest[0] || rest[0] === '--all') { const packs = getInstalledPacks(); for (const p of packs) packUpdate(p); }
|
|
219
|
+
else packUpdate(rest[0]);
|
|
220
|
+
}
|
|
221
|
+
else if (sub === 'list') packList();
|
|
222
|
+
else { console.error(c.red(`Unknown: agentcraft pack ${sub}`)); process.exit(1); }
|
|
152
223
|
} else {
|
|
153
|
-
console.error(
|
|
224
|
+
console.error(c.red(`✗ Unknown command: ${cmd}`));
|
|
154
225
|
showHelp();
|
|
155
226
|
process.exit(1);
|
|
156
227
|
}
|
package/opencode.js
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Plays sounds on OpenCode lifecycle events using the same assignments.json
|
|
5
5
|
* config as the Claude Code plugin. Shared config lives at:
|
|
6
|
-
* ~/.
|
|
6
|
+
* ~/.agentcraft/assignments.json
|
|
7
|
+
*
|
|
8
|
+
* Packs are stored at:
|
|
9
|
+
* ~/.agentcraft/packs/<publisher>/<name>/
|
|
7
10
|
*
|
|
8
11
|
* To install for OpenCode, symlink or copy this file to:
|
|
9
12
|
* ~/.config/opencode/plugins/agentcraft.js (global)
|
|
@@ -15,24 +18,42 @@ import { execSync } from 'child_process';
|
|
|
15
18
|
import { join } from 'path';
|
|
16
19
|
import { homedir } from 'os';
|
|
17
20
|
|
|
18
|
-
const
|
|
19
|
-
const
|
|
21
|
+
const ASSIGNMENTS_PATH = join(homedir(), '.agentcraft', 'assignments.json');
|
|
22
|
+
const PACKS_DIR = join(homedir(), '.agentcraft', 'packs');
|
|
20
23
|
|
|
21
24
|
function getAssignments() {
|
|
22
25
|
try {
|
|
23
|
-
return JSON.parse(readFileSync(
|
|
26
|
+
return JSON.parse(readFileSync(ASSIGNMENTS_PATH, 'utf8'));
|
|
24
27
|
} catch {
|
|
25
28
|
return null;
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
31
|
|
|
32
|
+
function resolvePackPath(soundPath) {
|
|
33
|
+
if (!soundPath) return null;
|
|
34
|
+
if (soundPath.includes(':')) {
|
|
35
|
+
// "publisher/name:internal/path/to/sound.mp3"
|
|
36
|
+
const colon = soundPath.indexOf(':');
|
|
37
|
+
const packId = soundPath.slice(0, colon);
|
|
38
|
+
const internal = soundPath.slice(colon + 1);
|
|
39
|
+
const slash = packId.indexOf('/');
|
|
40
|
+
const publisher = packId.slice(0, slash);
|
|
41
|
+
const name = packId.slice(slash + 1);
|
|
42
|
+
return join(PACKS_DIR, publisher, name, internal);
|
|
43
|
+
}
|
|
44
|
+
// Legacy path — resolve against official pack
|
|
45
|
+
return join(PACKS_DIR, 'rohenaz', 'agentcraft-sounds', soundPath);
|
|
46
|
+
}
|
|
47
|
+
|
|
29
48
|
function play(soundPath) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (!existsSync(full)) return;
|
|
49
|
+
const full = resolvePackPath(soundPath);
|
|
50
|
+
if (!full || !existsSync(full)) return;
|
|
33
51
|
try {
|
|
34
52
|
if (process.platform === 'darwin') execSync(`afplay "${full}" &`, { stdio: 'ignore' });
|
|
35
|
-
else if (process.platform === 'linux')
|
|
53
|
+
else if (process.platform === 'linux') {
|
|
54
|
+
if (existsSync('/usr/bin/paplay')) execSync(`paplay "${full}" &`, { stdio: 'ignore' });
|
|
55
|
+
else execSync(`aplay "${full}" &`, { stdio: 'ignore' });
|
|
56
|
+
}
|
|
36
57
|
} catch { /* non-blocking, ignore errors */ }
|
|
37
58
|
}
|
|
38
59
|
|
|
@@ -62,7 +83,6 @@ export const AgentCraft = async () => {
|
|
|
62
83
|
'tool.execute.before': async (input) => {
|
|
63
84
|
const a = getAssignments();
|
|
64
85
|
if (!a || a.settings?.enabled === false) return;
|
|
65
|
-
|
|
66
86
|
if (input.tool === 'skill') {
|
|
67
87
|
const key = input.args?.skill;
|
|
68
88
|
if (!key) return;
|
|
@@ -75,7 +95,6 @@ export const AgentCraft = async () => {
|
|
|
75
95
|
'tool.execute.after': async (input) => {
|
|
76
96
|
const a = getAssignments();
|
|
77
97
|
if (!a || a.settings?.enabled === false) return;
|
|
78
|
-
|
|
79
98
|
if (input.tool === 'skill') {
|
|
80
99
|
const key = input.args?.skill;
|
|
81
100
|
if (!key) return;
|
package/package.json
CHANGED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentcraft-packs
|
|
3
|
+
description: This skill should be used when the user asks to "install a sound pack", "add a pack", "find sound packs", "publish a pack", "create a pack", "share my sounds", "agentcraft pack install", "browse packs", "remove a pack", "update packs", or wants to know how the AgentCraft pack system works.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AgentCraft Sound Packs
|
|
7
|
+
|
|
8
|
+
Sound packs are git repos containing audio files. Any GitHub repo can be a pack — no approval, no registry required. Install by `publisher/name` slug, same as GitHub.
|
|
9
|
+
|
|
10
|
+
## Installing Packs
|
|
11
|
+
|
|
12
|
+
### From the CLI
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
agentcraft init # first-time setup
|
|
16
|
+
agentcraft add rohenaz/agentcraft-sounds # official pack
|
|
17
|
+
agentcraft add publisher/repo-name # any GitHub repo
|
|
18
|
+
agentcraft list # show installed packs
|
|
19
|
+
agentcraft update rohenaz/agentcraft-sounds # git pull one pack
|
|
20
|
+
agentcraft update # update all packs
|
|
21
|
+
agentcraft remove publisher/repo-name # uninstall
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`agentcraft pack install publisher/repo-name` resolves to `https://github.com/publisher/repo-name` and clones into `~/.agentcraft/packs/publisher/repo-name/`.
|
|
25
|
+
|
|
26
|
+
Install the CLI globally:
|
|
27
|
+
```bash
|
|
28
|
+
bun install -g agentcraft # or: npm install -g agentcraft
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### From the Dashboard
|
|
32
|
+
|
|
33
|
+
Open the **PACKS** tab in the AgentCraft dashboard. Installed packs show UPDATE/REMOVE buttons. The **BROWSE PACKS** section fetches the community registry and shows packs not yet installed with an INSTALL button.
|
|
34
|
+
|
|
35
|
+
### Manual Install (identical result)
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/publisher/repo-name ~/.agentcraft/packs/publisher/repo-name
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Manual clone and CLI install are exactly equivalent — no manifest or registration step.
|
|
42
|
+
|
|
43
|
+
## Pack Storage
|
|
44
|
+
|
|
45
|
+
Packs live at `~/.agentcraft/packs/<publisher>/<name>/`. The dashboard auto-discovers everything at that path depth — any directory placed there works.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
~/.agentcraft/packs/
|
|
49
|
+
rohenaz/
|
|
50
|
+
agentcraft-sounds/ ← official pack
|
|
51
|
+
publisher/
|
|
52
|
+
custom-pack/ ← any git repo cloned here
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Sound Assignment Paths
|
|
56
|
+
|
|
57
|
+
Assigned sounds are stored in `~/.agentcraft/assignments.json` with a pack-prefixed path:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
rohenaz/agentcraft-sounds:sc2/terran/session-start/scv-ready.mp3
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Format: `publisher/name:internal/path/to/sound.mp3`
|
|
64
|
+
|
|
65
|
+
The hook script resolves this to the absolute path at runtime:
|
|
66
|
+
```
|
|
67
|
+
~/.agentcraft/packs/rohenaz/agentcraft-sounds/sc2/terran/session-start/scv-ready.mp3
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Publishing a Pack
|
|
71
|
+
|
|
72
|
+
Any GitHub repo with audio files (`.mp3`, `.wav`, `.ogg`, `.m4a`) is a valid pack. No manifest required — directory structure is the organization.
|
|
73
|
+
|
|
74
|
+
### Step 1: Organize the repo
|
|
75
|
+
|
|
76
|
+
Recommended structure — group sounds into directories by game, theme, or purpose:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
my-sounds/
|
|
80
|
+
sc2/
|
|
81
|
+
terran/
|
|
82
|
+
session-start/
|
|
83
|
+
ready.mp3
|
|
84
|
+
task-complete/
|
|
85
|
+
salute.mp3
|
|
86
|
+
halo/
|
|
87
|
+
unsc/
|
|
88
|
+
session-start/
|
|
89
|
+
wake-up.mp3
|
|
90
|
+
ui/ ← optional: UI theme sounds
|
|
91
|
+
sc2/
|
|
92
|
+
click.mp3
|
|
93
|
+
hover.mp3
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Any directory layout works. The dashboard groups sounds by their directory path.
|
|
97
|
+
|
|
98
|
+
### Step 2: Add `pack.json` (optional but recommended)
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"name": "my-sounds",
|
|
103
|
+
"publisher": "your-github-username",
|
|
104
|
+
"version": "1.0.0",
|
|
105
|
+
"description": "Short description of the pack",
|
|
106
|
+
"types": ["sounds", "ui"]
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`types` is informational. Use `"ui"` if the pack includes a `ui/` directory with dashboard theme sounds.
|
|
111
|
+
|
|
112
|
+
### Step 3: Tag the repo on GitHub
|
|
113
|
+
|
|
114
|
+
Add the `agentcraft-pack` topic to the GitHub repo. This makes it discoverable in:
|
|
115
|
+
- The community registry at `https://rohenaz.github.io/agentcraft-registry/`
|
|
116
|
+
- GitHub search: `https://github.com/topics/agentcraft-pack`
|
|
117
|
+
|
|
118
|
+
To tag: GitHub repo → **Settings** → **Topics** → type `agentcraft-pack` → Save.
|
|
119
|
+
|
|
120
|
+
The registry GitHub Action runs every 6 hours and automatically picks up newly tagged repos.
|
|
121
|
+
|
|
122
|
+
### Step 4: Share the install command
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
agentcraft pack install your-username/your-repo-name
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
That's the entire publish workflow — push to GitHub, tag it, done.
|
|
129
|
+
|
|
130
|
+
## Pack Discovery
|
|
131
|
+
|
|
132
|
+
Find community packs three ways:
|
|
133
|
+
|
|
134
|
+
1. **Dashboard** — PACKS tab → BROWSE PACKS section shows the registry
|
|
135
|
+
2. **Registry** — `https://github.com/topics/agentcraft-pack`
|
|
136
|
+
3. **Registry JSON** — `https://rohenaz.github.io/agentcraft-registry/index.json`
|
|
137
|
+
|
|
138
|
+
## UI Theme Sounds
|
|
139
|
+
|
|
140
|
+
Packs can include a `ui/` directory with sounds that play as you use the AgentCraft dashboard (hover, click, page change, etc.). The dashboard's **UI SFX** dropdown lets users pick which pack's UI theme to use.
|
|
141
|
+
|
|
142
|
+
Structure the `ui/` directory by theme name:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
ui/
|
|
146
|
+
sc2/
|
|
147
|
+
click.mp3
|
|
148
|
+
hover.mp3
|
|
149
|
+
confirm.mp3
|
|
150
|
+
error.mp3
|
|
151
|
+
pageChange.mp3
|
|
152
|
+
wc3/
|
|
153
|
+
click.mp3
|
|
154
|
+
...
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Additional Resources
|
|
158
|
+
|
|
159
|
+
- **`references/pack-format.md`** — Full audio file requirements, directory naming conventions, and pack.json schema
|
|
160
|
+
- **Registry source** — `https://github.com/rohenaz/agentcraft-registry`
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Pack Format Reference
|
|
2
|
+
|
|
3
|
+
## Audio File Requirements
|
|
4
|
+
|
|
5
|
+
Supported formats: `.mp3`, `.wav`, `.ogg`, `.m4a`
|
|
6
|
+
|
|
7
|
+
Recommended: `.mp3` at 128–192kbps for compatibility and small file size.
|
|
8
|
+
|
|
9
|
+
File names become the display name in the sound browser (dashes/underscores become spaces, title-cased). Keep names descriptive:
|
|
10
|
+
- `scv-ready.mp3` → "Scv Ready"
|
|
11
|
+
- `marine-salute-00.mp3` → "Marine Salute 00"
|
|
12
|
+
|
|
13
|
+
## Directory Structure
|
|
14
|
+
|
|
15
|
+
No required structure. The dashboard reads depth dynamically:
|
|
16
|
+
|
|
17
|
+
- Top-level directories → **Group tabs** (e.g. `sc2`, `halo`, `classic-os`)
|
|
18
|
+
- Second-level directories → **Sub-tabs** (e.g. `sc2/terran`, `halo/unsc`)
|
|
19
|
+
- Third-level directories → **Subcategories** within a sub-tab (e.g. `sc2/terran/session-start`)
|
|
20
|
+
- Files at any depth → Sound cards
|
|
21
|
+
|
|
22
|
+
Example layouts that all work:
|
|
23
|
+
|
|
24
|
+
**Game-organized:**
|
|
25
|
+
```
|
|
26
|
+
sc2/terran/session-start/scv-ready.mp3
|
|
27
|
+
sc2/terran/task-complete/marine-salute.mp3
|
|
28
|
+
sc2/protoss/session-start/probe-ready.mp3
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Flat:**
|
|
32
|
+
```
|
|
33
|
+
sounds/click.mp3
|
|
34
|
+
sounds/beep.mp3
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Mood-organized:**
|
|
38
|
+
```
|
|
39
|
+
ambient/forest/birds.mp3
|
|
40
|
+
action/alert/warning.mp3
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## pack.json Schema
|
|
44
|
+
|
|
45
|
+
All fields are optional. Used for display in the PACKS tab and the community registry.
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"name": "string", // Display name (defaults to directory name)
|
|
50
|
+
"publisher": "string", // GitHub username (defaults to directory publisher)
|
|
51
|
+
"version": "string", // Semantic version, e.g. "1.0.0"
|
|
52
|
+
"description": "string", // One-line description shown in dashboard
|
|
53
|
+
"types": ["sounds", "ui"] // Content types: "sounds", "ui", or both
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## UI Theme Sounds
|
|
58
|
+
|
|
59
|
+
Include a `ui/` directory at the pack root for dashboard UI sounds. Subdirectories are theme names. Each theme can define any subset of these event sounds:
|
|
60
|
+
|
|
61
|
+
| Filename | Trigger |
|
|
62
|
+
|----------|---------|
|
|
63
|
+
| `click.mp3` | Button click |
|
|
64
|
+
| `hover.mp3` | Element hover |
|
|
65
|
+
| `confirm.mp3` | Save / success action |
|
|
66
|
+
| `error.mp3` | Error / failure |
|
|
67
|
+
| `pageChange.mp3` | Tab / page switch |
|
|
68
|
+
| `drag.mp3` | Drag start |
|
|
69
|
+
| `drop.mp3` | Drop onto slot |
|
|
70
|
+
| `open.mp3` | Panel/modal open |
|
|
71
|
+
| `close.mp3` | Panel/modal close |
|
|
72
|
+
|
|
73
|
+
Missing files are silently skipped. Themes with no `ui/` directory are sound-only packs.
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
```
|
|
77
|
+
ui/
|
|
78
|
+
sc2/
|
|
79
|
+
click.mp3
|
|
80
|
+
hover.mp3
|
|
81
|
+
confirm.mp3
|
|
82
|
+
error.mp3
|
|
83
|
+
wc3/
|
|
84
|
+
click.mp3
|
|
85
|
+
hover.mp3
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Assignment Path Format
|
|
89
|
+
|
|
90
|
+
When a sound is assigned to a hook, it's stored as:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
publisher/pack-name:path/to/sound.mp3
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The colon (`:`) separates pack identity from the internal path. The hook script resolves this to:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
~/.agentcraft/packs/publisher/pack-name/path/to/sound.mp3
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Sounds from your pack will automatically use this format when assigned through the dashboard.
|
|
103
|
+
|
|
104
|
+
## GitHub Topics
|
|
105
|
+
|
|
106
|
+
Tag your repo with `agentcraft-pack` to appear in the community registry. Additional optional tags:
|
|
107
|
+
|
|
108
|
+
| Topic | Meaning |
|
|
109
|
+
|-------|---------|
|
|
110
|
+
| `agentcraft-pack` | Required for discovery |
|
|
111
|
+
| `agentcraft-type-sounds` | Pack contains hook sounds |
|
|
112
|
+
| `agentcraft-type-ui` | Pack contains UI theme sounds |
|
|
113
|
+
|
|
114
|
+
The `agentcraft-type-*` topics are parsed by the registry Action and populate the `types` field in `index.json` automatically (as a fallback when no `pack.json` is present).
|
|
115
|
+
|
|
116
|
+
## File Size Guidelines
|
|
117
|
+
|
|
118
|
+
- Individual sounds: ideally < 500KB, max 2MB
|
|
119
|
+
- Total pack size: ideally < 50MB
|
|
120
|
+
- Large packs slow down `agentcraft pack install` (git clone)
|
|
121
|
+
|
|
122
|
+
Consider using `.gitattributes` with Git LFS for packs with many large audio files.
|