@givolt/i3-composer-agent 0.6.12 → 1.0.1

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.
Files changed (53) hide show
  1. package/.agents/plugins/marketplace.json +3 -2
  2. package/.claude-plugin/marketplace.json +2 -1
  3. package/.claude-plugin/plugin.json +3 -1
  4. package/.codex-plugin/plugin.json +3 -1
  5. package/CHANGELOG.md +34 -0
  6. package/NOTICE.md +6 -1
  7. package/README.md +116 -138
  8. package/bin/agent-targets.json +726 -0
  9. package/bin/i3-composer-agent.cjs +89 -18
  10. package/bin/install.cjs +164 -0
  11. package/bin/skills-registry.LICENSE +21 -0
  12. package/bin/wizard.cjs +66 -0
  13. package/docs/antigravity-setup.md +1 -1
  14. package/docs/codebuddy-setup.md +55 -8
  15. package/docs/codex-setup.md +13 -2
  16. package/docs/comparison.md +1 -1
  17. package/hooks/sdd-cache-post.sh +0 -0
  18. package/hooks/sdd-cache-pre.sh +0 -0
  19. package/package.json +75 -17
  20. package/plugin.json +4 -2
  21. package/rebrand/INSTALLATION-SPEC.md +46 -0
  22. package/rebrand/MAINTENANCE.md +93 -31
  23. package/rebrand/NOTICE.md +14 -0
  24. package/rebrand/PR-WORKFLOW.md +34 -0
  25. package/rebrand/UNIFICATION-SPEC.md +33 -0
  26. package/rebrand/VALIDATION.md +32 -0
  27. package/rebrand/VERSIONING.md +33 -0
  28. package/rebrand/apply.cjs +80 -7
  29. package/rebrand/brand.json +6 -2
  30. package/rebrand/catalog.cjs +56 -0
  31. package/rebrand/fork-patches.json +12 -0
  32. package/rebrand/overlay/.github/workflows/test-plugin-install.yml +177 -0
  33. package/rebrand/overlay/CHANGELOG.md +34 -0
  34. package/rebrand/overlay/bin/agent-targets.json +726 -0
  35. package/rebrand/overlay/bin/i3-composer-agent.cjs +190 -0
  36. package/rebrand/overlay/bin/install.cjs +164 -0
  37. package/rebrand/overlay/bin/skills-registry.LICENSE +21 -0
  38. package/rebrand/overlay/bin/wizard.cjs +66 -0
  39. package/rebrand/overlay/scripts/validate-brand.cjs +44 -0
  40. package/rebrand/overlay/tests/bundle.test.cjs +85 -0
  41. package/rebrand/overlay/tests/installer.test.cjs +193 -0
  42. package/rebrand/package.template.json +82 -0
  43. package/rebrand/rebrand.sh +4 -0
  44. package/rebrand/record-upstream.cjs +37 -0
  45. package/rebrand/templates/README.md +83 -137
  46. package/rebrand/templates/codebuddy-setup.md +55 -8
  47. package/rebrand/templates/codex-setup.md +14 -3
  48. package/rebrand/tests/pipeline.test.cjs +185 -0
  49. package/rebrand/upstream.json +171 -0
  50. package/scripts/validate-brand.cjs +11 -3
  51. package/skills/idea-refine/scripts/idea-refine.sh +0 -0
  52. package/tests/bundle.test.cjs +11 -5
  53. package/tests/installer.test.cjs +193 -0
@@ -8,25 +8,39 @@ const root = path.resolve(__dirname, '..');
8
8
  const pkg = require('../package.json');
9
9
  const name = 'i3-composer-agent';
10
10
  const args = process.argv.slice(2);
11
- const help = `${pkg.name} v${pkg.version}
11
+ const identity = `i3 Composer Agent v${pkg.version}\nBased on agent-skills v${pkg.i3ComposerAgent.upstreamVersion}`;
12
+ const help = `${identity}
13
+ ${pkg.name}
12
14
 
13
- Prepare the complete i3 Composer Agent plugin, including shared references.
15
+ Install engineering skills with a multi-tool installation wizard.
14
16
 
15
17
  Usage:
16
- npx ${pkg.name} --codebuddy [--dir <directory>]
17
- npx ${pkg.name} --codex [--dir <directory>]
18
- npx ${pkg.name} --claude [--dir <directory>]
18
+ npx ${pkg.name}
19
+ npx ${pkg.name} --codebuddy
20
+ npx ${pkg.name} --agents codebuddy,codex --scope project --yes
21
+ npx ${pkg.name} --prepare --claude --dir <directory>
19
22
  npx ${pkg.name} --extract --dir <directory>
20
23
 
21
24
  Options:
22
- --dir <directory> Output directory (default: ./i3-composer-agent)
23
- --dry-run Show the planned bundle without writing files
24
- --force Replace an existing i3 bundle, preserving a backup
25
- --version Print the package version
26
- --help Show this help
25
+ --agents <ids> Comma-separated tool IDs; multiple tools are supported
26
+ --codebuddy Preselect CodeBuddy desktop IDE / CodeBuddy Code
27
+ --codex Preselect Codex
28
+ --claude Preselect Claude Code
29
+ --scope <scope> project or global (or use --global / --local)
30
+ --project-dir <dir> Project installation root (default: current directory)
31
+ --yes Confirm explicit noninteractive installation
32
+ --list-agents List all supported tool directories
33
+ --prepare Prepare a complete plugin for native plugin registration
34
+ --dir <directory> Plugin output directory; implies --prepare
35
+ --dry-run Show the installation or bundle plan without writing files
36
+ --force Replace existing assets, preserving backups
37
+ --version Print the package version
38
+ --help Show this help
27
39
 
28
- Host flags prepare the bundle and print native registration commands.
29
- Execute those commands in the intended host to enable the plugin.
40
+ The terminal wizard selects tools and scope, then installs native skill files.
41
+ CodeBuddy and Claude Code also receive short lifecycle commands and personas.
42
+ Other tools receive skills and references; command availability is host-specific.
43
+ Use --prepare for complete plugins, including hooks and host registration.
30
44
  `;
31
45
 
32
46
  function quote(value) { return "'" + value.replaceAll("'", "'\\''") + "'"; }
@@ -41,15 +55,14 @@ function realDestination(target) {
41
55
  }
42
56
  return path.join(fs.realpathSync(existing), ...suffix);
43
57
  }
44
- function main() {
45
- if (!args.length || args.includes('--help')) { console.log(help); return; }
46
- if (args.length === 1 && args[0] === '--version') { console.log(pkg.version); return; }
58
+ function prepare() {
47
59
  let host;
48
60
  let directory = path.join(process.cwd(), name);
49
61
  let dryRun = false;
50
62
  let force = false;
51
63
  for (let i = 0; i < args.length; i++) {
52
64
  const arg = args[i];
65
+ if (arg === '--prepare') continue;
53
66
  if (['--codebuddy', '--codex', '--claude', '--extract'].includes(arg)) {
54
67
  if (host) throw new Error('Choose exactly one host flag or --extract.');
55
68
  host = arg.slice(2);
@@ -74,7 +87,7 @@ function main() {
74
87
  if (manifest.name !== name) throw new Error('Refusing to replace a directory belonging to another plugin.');
75
88
  }
76
89
  if (dryRun) {
77
- console.log(JSON.stringify({ name, version: pkg.version, directory, host, files: pkg.i3ComposerAgent.pluginFiles, replacesExisting: exists }, null, 2));
90
+ console.log(JSON.stringify({ name, version: pkg.version, upstreamVersion: pkg.i3ComposerAgent.upstreamVersion, upstreamCommit: pkg.i3ComposerAgent.upstreamCommit, directory, host, files: pkg.i3ComposerAgent.pluginFiles, replacesExisting: exists }, null, 2));
78
91
  return;
79
92
  }
80
93
  fs.mkdirSync(path.dirname(directory), { recursive: true });
@@ -101,7 +114,8 @@ function main() {
101
114
  try { fs.renameSync(staging, directory); }
102
115
  catch (error) { if (backup) fs.renameSync(backup, directory); throw error; }
103
116
  } finally { if (fs.existsSync(staging)) fs.rmSync(staging, { recursive: true, force: true }); }
104
- console.log(`Prepared ${name} v${pkg.version} at ${directory}`);
117
+ console.log(identity);
118
+ console.log(`Prepared ${name} at ${directory}`);
105
119
  if (backup) console.log(`Previous bundle preserved at ${backup}`);
106
120
  if (host === 'extract') return;
107
121
  console.log('\nRegister and enable the plugin with these native commands:');
@@ -116,4 +130,61 @@ function main() {
116
130
  console.log(`Use /${name}:spec, /${name}:plan, /${name}:build, and /${name}:ship in ${host === 'claude' ? 'Claude Code' : 'CodeBuddy'}.`);
117
131
  }
118
132
  }
119
- try { main(); } catch (error) { console.error(`${name}: ${error.message}`); process.exitCode = 1; }
133
+ async function main() {
134
+ if (args.includes('--help') || (!args.length && !process.stdin.isTTY)) { console.log(help); return; }
135
+ if (args.length === 1 && args[0] === '--version') { console.log(pkg.version); return; }
136
+ if (args.some(arg => ['--extract', '--prepare', '--dir'].includes(arg))) { prepare(); return; }
137
+ const installer = require('./install.cjs');
138
+ if (args.length === 1 && args[0] === '--list-agents') {
139
+ for (const agent of installer.registry.agents) console.log(`${agent.id.padEnd(20)} ${agent.label}${agent.globalSkillsDir ? '' : ' (project only)'}`);
140
+ return;
141
+ }
142
+ const options = { agents: [], projectDir: process.cwd() };
143
+ for (let i = 0; i < args.length; i++) {
144
+ const arg = args[i];
145
+ if (['--codebuddy', '--codex', '--claude'].includes(arg)) options.agents.push(arg.slice(2));
146
+ else if (['--agents', '--scope', '--project-dir'].includes(arg)) {
147
+ if (!args[i + 1] || args[i + 1].startsWith('--')) throw new Error(`${arg} requires a value.`);
148
+ const value = args[++i];
149
+ if (arg === '--agents') options.agents.push(value);
150
+ else if (arg === '--scope') { if (options.scope && options.scope !== value) throw new Error('Choose one installation scope.'); options.scope = value; }
151
+ else options.projectDir = path.resolve(value);
152
+ } else if (arg === '--global' || arg === '--local') {
153
+ const scope = arg === '--global' ? 'global' : 'project';
154
+ if (options.scope && options.scope !== scope) throw new Error('Choose one installation scope.');
155
+ options.scope = scope;
156
+ } else if (arg === '--yes') options.yes = true;
157
+ else if (arg === '--force') options.force = true;
158
+ else if (arg === '--dry-run') options.dryRun = true;
159
+ else throw new Error(`Unknown option: ${arg}`);
160
+ }
161
+ options.agents = installer.selectAgents(options.agents);
162
+ if (process.stdin.isTTY && process.stdout.isTTY && !options.yes && !options.dryRun) {
163
+ const selection = await require('./wizard.cjs').choose(options, identity);
164
+ if (!selection) { console.log('Installation cancelled. No files changed.'); return; }
165
+ Object.assign(options, selection);
166
+ } else if (!options.agents.length || !options.scope || (!options.yes && !options.dryRun)) {
167
+ throw new Error('Use an interactive terminal, or specify --agents <ids> --scope project|global --yes.');
168
+ }
169
+ const plan = installer.planInstallation(options);
170
+ if (options.dryRun) {
171
+ console.log(JSON.stringify({ name, version: pkg.version, upstreamVersion: pkg.i3ComposerAgent.upstreamVersion, upstreamCommit: pkg.i3ComposerAgent.upstreamCommit, mode: plan.mode, scope: plan.scope, agents: plan.agents, targets: plan.targets, conflicts: plan.conflicts }, null, 2));
172
+ return;
173
+ }
174
+ if (!options.yes) {
175
+ const confirmed = await require('./wizard.cjs').confirm(plan);
176
+ if (!confirmed) { console.log('Installation cancelled. No files changed.'); return; }
177
+ options.force = plan.conflicts.length > 0;
178
+ }
179
+ const backups = installer.executeInstallation(plan, { force: options.force });
180
+ console.log(identity);
181
+ for (const target of plan.targets) {
182
+ console.log(`Installed ${target.agents.join(', ')} skills at ${target.skillsDirectory}`);
183
+ if (target.commandsDirectory) console.log(`Commands: /spec, /plan, /build, /ship (all ${fs.readdirSync(target.commandsDirectory).filter(file => file.endsWith('.md')).length} workflows) at ${target.commandsDirectory}`);
184
+ }
185
+ for (const backup of backups) console.log(`Previous assets preserved at ${backup}`);
186
+ console.log('Reload the IDE window or start a new host session to discover installed files.');
187
+ if (plan.agents.some(id => !['codebuddy', 'claude-code'].includes(id))) console.log('These tools use the existing skill IDs; lifecycle slash-command support depends on the host.');
188
+ console.log('Native skill installation does not enable plugin hooks. Use --prepare for complete plugin registration.');
189
+ }
190
+ main().catch(error => { console.error(`${name}: ${error.message}`); process.exitCode = 1; });
@@ -0,0 +1,164 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const os = require('node:os');
6
+ const crypto = require('node:crypto');
7
+ const registry = require('./agent-targets.json');
8
+ const root = path.resolve(__dirname, '..');
9
+ const name = 'i3-composer-agent';
10
+ const aliases = { claude: 'claude-code', gemini: 'gemini-cli', copilot: 'github-copilot' };
11
+ function exists(file) {
12
+ try { fs.lstatSync(file); return true; }
13
+ catch (error) { if (error.code === 'ENOENT') return false; throw error; }
14
+ }
15
+ function selectAgents(values) {
16
+ const ids = [...new Set(values.flatMap(value => value.split(',')).map(id => aliases[id.trim()] || id.trim()).filter(Boolean))];
17
+ for (const id of ids) if (!registry.agents.some(agent => agent.id === id)) throw new Error(`Unknown tool: ${id}. Use --list-agents.`);
18
+ return ids;
19
+ }
20
+ function globalSkillsDir(agent, home, env) {
21
+ const config = agent.globalSkillsDir;
22
+ if (!config) throw new Error(`${agent.label} supports project installation only. Choose project scope or remove this tool.`);
23
+ let base = home;
24
+ if (config.base === 'config') base = env.XDG_CONFIG_HOME && path.isAbsolute(env.XDG_CONFIG_HOME) ? env.XDG_CONFIG_HOME : path.join(home, '.config');
25
+ if (config.base === 'env') base = env[config.env]?.trim() || path.join(home, config.fallback);
26
+ if (config.alternatives && !fs.existsSync(path.join(home, path.dirname(config.path)))) {
27
+ const alternative = config.alternatives.find(relative => fs.existsSync(path.join(home, path.dirname(relative))));
28
+ if (alternative) return path.join(home, alternative);
29
+ }
30
+ return path.resolve(base, config.path);
31
+ }
32
+ // Resolve parent aliases while keeping a leaf symlink replaceable as a link.
33
+ function destination(file) {
34
+ let parent = path.dirname(file);
35
+ const suffix = [path.basename(file)];
36
+ while (!exists(parent)) { suffix.unshift(path.basename(parent)); parent = path.dirname(parent); }
37
+ return path.join(fs.realpathSync(parent), ...suffix);
38
+ }
39
+ function checkDestination(file) {
40
+ const source = fs.realpathSync(root);
41
+ if (file === source || file.startsWith(source + path.sep) || source.startsWith(file + path.sep)) {
42
+ throw new Error('Installation destinations must be outside this package directory.');
43
+ }
44
+ }
45
+ function planInstallation({ agents, scope, projectDir = process.cwd(), home = os.homedir(), env = process.env }) {
46
+ agents = selectAgents(agents);
47
+ if (!agents.length) throw new Error('Choose at least one tool.');
48
+ if (!['project', 'global'].includes(scope)) throw new Error('Choose --scope project or --scope global.');
49
+ const targets = new Map();
50
+ for (const id of agents) {
51
+ const agent = registry.agents.find(item => item.id === id);
52
+ const skillsDirectory = destination(scope === 'project' ? path.resolve(projectDir, agent.projectSkillsDir) : globalSkillsDir(agent, home, env));
53
+ const base = path.dirname(skillsDirectory);
54
+ if (!targets.has(skillsDirectory)) targets.set(skillsDirectory, { agents: [], skillsDirectory, referencesDirectory: path.join(base, 'references'), base });
55
+ const target = targets.get(skillsDirectory);
56
+ target.agents.push(id);
57
+ if (['codebuddy', 'claude-code'].includes(id)) {
58
+ target.commandsDirectory = path.join(base, 'commands');
59
+ target.personasDirectory = path.join(base, 'agents');
60
+ }
61
+ }
62
+ const assets = [];
63
+ function addDirectory(sourceDirectory, destinationDirectory, kind) {
64
+ if (exists(destinationDirectory) && fs.lstatSync(destinationDirectory).isSymbolicLink()) {
65
+ throw new Error(`Installation directory is a symlink: ${destinationDirectory}. Choose its canonical location instead.`);
66
+ }
67
+ for (const entry of fs.readdirSync(path.join(root, sourceDirectory)).sort()) {
68
+ if (entry === '.DS_Store') continue;
69
+ assets.push({ source: path.join(root, sourceDirectory, entry), destination: destination(path.join(destinationDirectory, entry)), kind });
70
+ }
71
+ }
72
+ for (const target of targets.values()) {
73
+ const start = assets.length;
74
+ addDirectory('skills', target.skillsDirectory, 'skill');
75
+ addDirectory('references', target.referencesDirectory, 'reference');
76
+ if (target.commandsDirectory) {
77
+ addDirectory('.claude/commands', target.commandsDirectory, 'command');
78
+ addDirectory('agents', target.personasDirectory, 'persona');
79
+ const docs = path.join(target.base, 'docs');
80
+ if (exists(docs) && fs.lstatSync(docs).isSymbolicLink()) throw new Error(`Installation directory is a symlink: ${docs}.`);
81
+ assets.push({ source: path.join(root, 'docs/agents.md'), destination: destination(path.join(target.base, 'docs/agents.md')), kind: 'documentation' });
82
+ }
83
+ assets.push({ source: path.join(root, 'LICENSE'), destination: destination(path.join(target.base, `${name}-LICENSE`)), kind: 'attribution' });
84
+ assets.push({ source: path.join(root, 'NOTICE.md'), destination: destination(path.join(target.base, `${name}-NOTICE.md`)), kind: 'attribution' });
85
+ assets.push({ target, destination: destination(path.join(target.base, `.${name}.json`)), kind: 'manifest' });
86
+ for (const asset of assets.slice(start)) {
87
+ asset.base = target.base;
88
+ asset.codebuddyCommand = asset.kind === 'command' && target.agents.includes('codebuddy');
89
+ }
90
+ }
91
+ for (const asset of assets) { checkDestination(asset.destination); asset.existing = exists(asset.destination); }
92
+ return { mode: 'install', scope, agents, targets: [...targets.values()], assets, conflicts: assets.filter(asset => asset.existing).map(asset => asset.destination) };
93
+ }
94
+ function executeInstallation(plan, { force = false } = {}) {
95
+ if (plan.conflicts.length && !force) throw new Error('Existing installation assets require confirmation or --force; previous files will be backed up.');
96
+ const pkg = require('../package.json');
97
+ const contexts = new Map();
98
+ const createdDirectories = [];
99
+ const committed = [];
100
+ function mkdir(directory) {
101
+ if (exists(directory)) return;
102
+ mkdir(path.dirname(directory));
103
+ fs.mkdirSync(directory);
104
+ createdDirectories.push(directory);
105
+ }
106
+ function portable(file, markdown = false, codebuddyCommand = false) {
107
+ if (fs.statSync(file).isDirectory()) {
108
+ for (const entry of fs.readdirSync(file)) portable(path.join(file, entry), false, codebuddyCommand);
109
+ } else if (markdown || file.endsWith('.md')) {
110
+ let content = fs.readFileSync(file, 'utf8').replaceAll('i3-composer-agent:', '').replaceAll('agent-skills:', '');
111
+ if (codebuddyCommand) content = content.replaceAll('.claude/agents/', '.codebuddy/agents/');
112
+ fs.writeFileSync(file, content);
113
+ }
114
+ }
115
+ try {
116
+ // Stage every target before replacing the first installed asset.
117
+ for (const [index, asset] of plan.assets.entries()) {
118
+ if (!contexts.has(asset.base)) {
119
+ mkdir(asset.base);
120
+ contexts.set(asset.base, { staging: fs.mkdtempSync(path.join(asset.base, `.${name}-stage-`)), backup: path.join(asset.base, `.${name}-backup-${crypto.randomUUID()}`) });
121
+ }
122
+ const context = contexts.get(asset.base);
123
+ asset.staged = path.join(context.staging, String(index));
124
+ if (asset.kind === 'manifest') {
125
+ fs.writeFileSync(asset.staged, JSON.stringify({ name, version: pkg.version, upstreamVersion: pkg.i3ComposerAgent.upstreamVersion, upstreamCommit: pkg.i3ComposerAgent.upstreamCommit, scope: plan.scope, agents: asset.target.agents, assets: plan.assets.filter(item => item.base === asset.base).map(item => path.relative(asset.base, item.destination)) }, null, 2) + '\n');
126
+ } else {
127
+ fs.cpSync(asset.source, asset.staged, { recursive: true, filter: file => {
128
+ if (fs.lstatSync(file).isSymbolicLink()) throw new Error(`Package contains a symlink: ${file}`);
129
+ return path.basename(file) !== '.DS_Store';
130
+ } });
131
+ portable(asset.staged, asset.source.endsWith('.md'), asset.codebuddyCommand);
132
+ }
133
+ }
134
+ for (const asset of plan.assets) {
135
+ const context = contexts.get(asset.base);
136
+ mkdir(path.dirname(asset.destination));
137
+ // A destination may have appeared since planning; never replace it implicitly.
138
+ if (exists(asset.destination) && !force) throw new Error(`Destination appeared during installation: ${asset.destination}. Retry with confirmed replacement.`);
139
+ const change = { destination: asset.destination, written: false };
140
+ if (exists(asset.destination)) {
141
+ change.backup = path.join(context.backup, path.relative(asset.base, asset.destination));
142
+ mkdir(path.dirname(change.backup));
143
+ fs.renameSync(asset.destination, change.backup);
144
+ }
145
+ committed.push(change);
146
+ fs.renameSync(asset.staged, asset.destination);
147
+ change.written = true;
148
+ }
149
+ return [...contexts.values()].map(context => context.backup).filter(exists);
150
+ } catch (error) {
151
+ for (const change of committed.reverse()) {
152
+ if (change.written) fs.rmSync(change.destination, { recursive: true, force: true });
153
+ if (change.backup) fs.renameSync(change.backup, change.destination);
154
+ }
155
+ throw error;
156
+ } finally {
157
+ for (const context of contexts.values()) fs.rmSync(context.staging, { recursive: true, force: true });
158
+ for (const directory of createdDirectories.reverse()) {
159
+ try { fs.rmdirSync(directory); }
160
+ catch (error) { if (!['ENOENT', 'ENOTEMPTY', 'EEXIST'].includes(error.code)) throw error; }
161
+ }
162
+ }
163
+ }
164
+ module.exports = { registry, selectAgents, planInstallation, executeInstallation, exists };
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vercel, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/bin/wizard.cjs ADDED
@@ -0,0 +1,66 @@
1
+ 'use strict';
2
+
3
+ const readline = require('node:readline');
4
+ const { registry, selectAgents } = require('./install.cjs');
5
+
6
+ async function session(action) {
7
+ const terminal = readline.createInterface({ input: process.stdin, output: process.stdout });
8
+ let closed = false;
9
+ terminal.on('close', () => { closed = true; });
10
+ terminal.on('SIGINT', () => terminal.close());
11
+ const ask = prompt => new Promise(resolve => {
12
+ if (closed) { resolve(null); return; }
13
+ const cancelled = () => resolve(null);
14
+ terminal.once('close', cancelled);
15
+ terminal.question(prompt, answer => {
16
+ terminal.off('close', cancelled);
17
+ resolve(answer.trim());
18
+ });
19
+ });
20
+ try { return await action(ask); }
21
+ finally { terminal.close(); }
22
+ }
23
+ async function choose(options, identity) {
24
+ console.log(identity);
25
+ console.log('\nChoose tools (multiple selections are supported):');
26
+ const entries = registry.agents.map((agent, index) => `${index + 1}. ${agent.label} [${agent.id}]${agent.globalSkillsDir ? '' : ' (project only)'}`);
27
+ for (let index = 0; index < entries.length; index += 2) console.log(entries[index].padEnd(49) + (entries[index + 1] || ''));
28
+ return session(async ask => {
29
+ let agents;
30
+ while (!agents) {
31
+ const input = await ask(`Select tools by number or ID, comma-separated${options.agents.length ? ` [${options.agents.join(',')}]` : ''} (q cancels): `);
32
+ if (input === null || ['q', 'cancel'].includes(input.toLowerCase())) return null;
33
+ try {
34
+ const values = input === 'all' ? registry.agents.map(agent => agent.id) : input ? input.split(',').map(value => {
35
+ const trimmed = value.trim();
36
+ return /^\d+$/.test(trimmed) ? registry.agents[Number(trimmed) - 1]?.id || trimmed : trimmed;
37
+ }) : options.agents;
38
+ agents = selectAgents(values);
39
+ if (!agents.length) { agents = undefined; console.log('Choose at least one tool.'); }
40
+ } catch (error) { console.log(error.message); }
41
+ }
42
+ let scope = options.scope;
43
+ while (!scope) {
44
+ const input = await ask('Installation scope: 1. Project (current project), 2. Global (all projects) [1] (q cancels): ');
45
+ if (input === null || ['q', 'cancel'].includes(input.toLowerCase())) return null;
46
+ if (['', '1', 'project'].includes(input)) scope = 'project';
47
+ else if (['2', 'global'].includes(input)) scope = 'global';
48
+ else console.log('Choose 1 (project) or 2 (global).');
49
+ }
50
+ return { agents, scope };
51
+ });
52
+ }
53
+ async function confirm(plan) {
54
+ console.log(`\nInstallation scope: ${plan.scope}`);
55
+ for (const target of plan.targets) {
56
+ console.log(` ${target.agents.join(', ')}: ${target.skillsDirectory}`);
57
+ if (target.commandsDirectory) console.log(` Commands: ${target.commandsDirectory}`);
58
+ }
59
+ if (plan.conflicts.length) console.log(`${plan.conflicts.length} existing assets will be preserved in backups before replacement.`);
60
+ console.log('Includes all skills and shared references. Plugin hooks require native plugin registration.');
61
+ return session(async ask => {
62
+ const answer = await ask('Install these files? [y/N]: ');
63
+ return answer !== null && ['y', 'yes'].includes(answer.toLowerCase());
64
+ });
65
+ }
66
+ module.exports = { choose, confirm };
@@ -18,7 +18,7 @@ agy plugin install /path/to/i3-composer-agent
18
18
 
19
19
  1. Prepare the plugin bundle:
20
20
  ```bash
21
- npx @givolt/i3-composer-agent --extract --dir ./i3-composer-agent
21
+ npx @givolt/i3-composer-agent --extract --dir ./i3-composer-agent
22
22
  ```
23
23
  2. Install the plugin using `agy`:
24
24
  ```bash
@@ -1,12 +1,60 @@
1
1
  # Using i3 Composer Agent with CodeBuddy
2
2
 
3
- Prepare the complete plugin inside the project where you want to use it:
3
+ ## Desktop IDE installation
4
+
5
+ Run inside your business project:
4
6
 
5
7
  ```bash
6
8
  npx @givolt/i3-composer-agent --codebuddy
7
9
  ```
8
10
 
9
- In the CodeBuddy chat, register the prepared directory and install the plugin:
11
+ The terminal wizard preselects CodeBuddy, still allows multiple tools, and asks
12
+ for project or global scope before installing. Project scope writes these paths:
13
+
14
+ - `.codebuddy/skills/` — all 25 skills with their original IDs.
15
+ - `.codebuddy/references/` — shared checklists used by skill-relative links.
16
+ - `.codebuddy/commands/` — all 9 existing lifecycle workflows.
17
+ - `.codebuddy/agents/` — all 4 persona definitions.
18
+
19
+ Global scope uses the same layout under `~/.codebuddy/`. Existing assets require
20
+ confirmed replacement and are preserved in backup directories. Settings are
21
+ not replaced. Reload the CodeBuddy window or start a new chat, then inspect
22
+ the Skills list and type `/` to inspect the command menu.
23
+
24
+ ```text
25
+ /spec
26
+ /plan
27
+ /build
28
+ /build auto
29
+ /review
30
+ /ship
31
+ ```
32
+
33
+ The command wrappers reference installed skill IDs without the plugin namespace.
34
+ Their workflow content and approval gates are retained. File installation does
35
+ not itself prove that the IDE has discovered or executed every workflow.
36
+
37
+ For multiple tools or a noninteractive installation:
38
+
39
+ ```bash
40
+ npx @givolt/i3-composer-agent --agents codebuddy,codex --scope project --yes
41
+ npx @givolt/i3-composer-agent --codebuddy --scope global --yes
42
+ ```
43
+
44
+ CodeBuddy documents [IDE skills](https://www.codebuddy.cn/docs/ide/Features/Skills)
45
+ and [IDE custom commands](https://www.codebuddy.cn/docs/ide/User-guide/Slash-Commands).
46
+ The directory layout follows its [configuration directory reference](https://www.codebuddy.cn/docs/cli/codebuddy-dir).
47
+
48
+ ## CodeBuddy Code native plugin registration
49
+
50
+ For the terminal CodeBuddy Code plugin manager, explicitly prepare the full
51
+ plugin instead of native skill installation:
52
+
53
+ ```bash
54
+ npx @givolt/i3-composer-agent --prepare --codebuddy
55
+ ```
56
+
57
+ In CodeBuddy Code, register the prepared directory and install the plugin:
10
58
 
11
59
  ```text
12
60
  /plugin marketplace add ./i3-composer-agent
@@ -14,10 +62,10 @@ In the CodeBuddy chat, register the prepared directory and install the plugin:
14
62
  ```
15
63
 
16
64
  Use the absolute directory printed by the CLI if CodeBuddy has a different
17
- working directory. Choose the intended installation scope in CodeBuddy's native
65
+ working directory. Choose the intended installation scope in CodeBuddy Code's native
18
66
  plugin manager. Reload plugins or start a new session after installation.
19
67
 
20
- CodeBuddy supports the existing `.claude-plugin/plugin.json` compatibility
68
+ CodeBuddy Code supports the existing `.claude-plugin/plugin.json` compatibility
21
69
  manifest. It declares `.claude/commands/` and `commands/`, preserving the nine
22
70
  original lifecycle commands and their workflow content.
23
71
 
@@ -33,9 +81,8 @@ original lifecycle commands and their workflow content.
33
81
  Skill IDs, persona names, artifact paths, and workflow gates retain their
34
82
  upstream behavior. The plugin namespace changes to `i3-composer-agent`.
35
83
 
36
- The bundle includes all skills and shared `references/`. Installing individual
37
- skills is a different distribution method and does not install the command
38
- wrappers or persona definitions.
84
+ The prepared plugin includes all skills, personas, commands, hooks and shared
85
+ `references/`. Native skill installation above does not enable plugin hooks.
39
86
 
40
- The CLI prepares files and prints registration commands. Actual host discovery
87
+ In this explicit mode the CLI prepares files and prints registration commands. Actual host discovery
41
88
  and model execution should be verified after registering the plugin.
@@ -1,12 +1,23 @@
1
1
  # Using i3 Composer Agent with Codex
2
2
 
3
- Prepare the complete plugin inside the project where you want to use it:
3
+ For the multi-tool native skill wizard, run in your project:
4
4
 
5
5
  ```bash
6
6
  npx @givolt/i3-composer-agent --codex
7
7
  ```
8
8
 
9
- The CLI copies a persistent `./i3-composer-agent/` bundle and prints these
9
+ Choose project or global scope. Native installation uses `.agents/skills/`
10
+ or `~/.agents/skills/`, plus the sibling `references/` directory. It retains
11
+ the 25 original skill IDs. This mode does not add short lifecycle
12
+ slash commands or enable plugin hooks.
13
+
14
+ For complete plugin registration, prepare a persistent bundle explicitly:
15
+
16
+ ```bash
17
+ npx @givolt/i3-composer-agent --prepare --codex
18
+ ```
19
+
20
+ This mode copies a persistent `./i3-composer-agent/` bundle and prints these
10
21
  native registration commands. Run them to enable the plugin:
11
22
 
12
23
  ```bash
@@ -6,7 +6,7 @@
6
6
 
7
7
  # How agent-skills compares
8
8
 
9
- > **Upstream research:** This comparison is retained from Addy Osmani's `agent-skills` at commit `1401c8b`. Its experiments describe the upstream project; they are not independent evaluations of i3 Composer Agent.
9
+ > **Upstream research:** This comparison is retained from Addy Osmani's `agent-skills` at commit `1401c8b8030e023baeebb31781a6653fe8e93026`. Its experiments describe the upstream project; they are not independent evaluations of i3 Composer Agent.
10
10
 
11
11
  People often ask how **agent-skills** relates to the two other "skills for coding agents" collections they hear about most: **Superpowers** (Jesse Vincent / obra) and **Matt Pocock's skills**. All three are good, share a lot of DNA, and are worth learning from. This page is an honest map of how they are *shaped* differently, so you can pick the one that fits how you work, or borrow across all three.
12
12
 
File without changes
File without changes
package/package.json CHANGED
@@ -1,33 +1,91 @@
1
1
  {
2
2
  "name": "@givolt/i3-composer-agent",
3
- "version": "0.6.12",
4
3
  "description": "i3 Composer Agent: production-grade engineering skills and lifecycle commands for AI coding agents.",
5
4
  "license": "MIT",
6
5
  "author": "Givolt",
7
- "contributors": [{ "name": "Addy Osmani" }],
8
- "bin": { "i3-composer-agent": "bin/i3-composer-agent.cjs" },
9
- "engines": { "node": ">=20" },
10
- "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
6
+ "contributors": [
7
+ {
8
+ "name": "Addy Osmani"
9
+ }
10
+ ],
11
+ "bin": {
12
+ "i3-composer-agent": "bin/i3-composer-agent.cjs"
13
+ },
14
+ "engines": {
15
+ "node": ">=20"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public",
19
+ "registry": "https://registry.npmjs.org/"
20
+ },
11
21
  "files": [
12
- "bin/", "skills/", "references/", "agents/", "commands/",
13
- ".claude/commands/", ".gemini/commands/", ".claude-plugin/",
14
- ".codex-plugin/", ".agents/plugins/", "hooks/", "docs/",
15
- "scripts/", "evals/", "rebrand/", "tests/", "plugin.json", "NOTICE.md"
22
+ "bin/",
23
+ "skills/",
24
+ "references/",
25
+ "agents/",
26
+ "commands/",
27
+ ".claude/commands/",
28
+ ".gemini/commands/",
29
+ ".claude-plugin/",
30
+ ".codex-plugin/",
31
+ ".agents/plugins/",
32
+ "hooks/",
33
+ "docs/",
34
+ "scripts/",
35
+ "evals/",
36
+ "rebrand/",
37
+ "tests/",
38
+ "plugin.json",
39
+ "NOTICE.md",
40
+ "CHANGELOG.md"
16
41
  ],
17
42
  "scripts": {
18
43
  "rebrand": "node rebrand/apply.cjs --apply",
19
44
  "rebrand:check": "node rebrand/apply.cjs --check",
20
45
  "validate": "node scripts/validate-brand.cjs && node scripts/validate-skills.js && node scripts/validate-versions.js && node scripts/validate-commands.js && node scripts/validate-reference-links.js && node scripts/validate-artifact-paths.js",
21
- "test": "node --test scripts/*-test.js scripts/lib/*-test.js tests/*.test.cjs",
46
+ "test": "node --test scripts/*-test.js scripts/lib/*-test.js tests/*.test.cjs rebrand/tests/*.test.cjs",
22
47
  "prepack": "npm run validate"
23
48
  },
24
- "keywords": ["i3-composer", "agent-skills", "codebuddy", "codex", "claude-code"],
49
+ "keywords": [
50
+ "i3-composer",
51
+ "agent-skills",
52
+ "codebuddy",
53
+ "codex",
54
+ "claude-code"
55
+ ],
25
56
  "i3ComposerAgent": {
26
57
  "pluginFiles": [
27
- "bin", "skills", "references", "agents", "commands", ".claude/commands",
28
- ".gemini/commands", ".claude-plugin", ".codex-plugin", ".agents/plugins",
29
- "hooks", "docs", "scripts", "evals", "rebrand", "tests", "plugin.json",
30
- "package.json", "README.md", "LICENSE", "NOTICE.md"
31
- ]
32
- }
58
+ "bin",
59
+ "skills",
60
+ "references",
61
+ "agents",
62
+ "commands",
63
+ ".claude/commands",
64
+ ".gemini/commands",
65
+ ".claude-plugin",
66
+ ".codex-plugin",
67
+ ".agents/plugins",
68
+ "hooks",
69
+ "docs",
70
+ "scripts",
71
+ "evals",
72
+ "rebrand",
73
+ "tests",
74
+ "plugin.json",
75
+ "package.json",
76
+ "README.md",
77
+ "LICENSE",
78
+ "NOTICE.md",
79
+ "CHANGELOG.md"
80
+ ],
81
+ "upstreamVersion": "0.6.12",
82
+ "upstreamCommit": "1401c8b8030e023baeebb31781a6653fe8e93026",
83
+ "upstreamRepository": "https://github.com/addyosmani/agent-skills"
84
+ },
85
+ "version": "1.0.1",
86
+ "repository": {
87
+ "type": "git",
88
+ "url": "https://github.com/49780522/i3-composer-agent.git"
89
+ },
90
+ "homepage": "https://github.com/49780522/i3-composer-agent#readme"
33
91
  }