@givolt/i3-composer-agent 1.0.0 → 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.
- package/.agents/plugins/marketplace.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +12 -1
- package/NOTICE.md +6 -1
- package/README.md +53 -15
- package/bin/agent-targets.json +726 -0
- package/bin/i3-composer-agent.cjs +83 -15
- package/bin/install.cjs +164 -0
- package/bin/skills-registry.LICENSE +21 -0
- package/bin/wizard.cjs +66 -0
- package/docs/codebuddy-setup.md +55 -8
- package/docs/codex-setup.md +13 -2
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/rebrand/INSTALLATION-SPEC.md +46 -0
- package/rebrand/MAINTENANCE.md +2 -1
- package/rebrand/NOTICE.md +6 -1
- package/rebrand/VALIDATION.md +4 -0
- package/rebrand/VERSIONING.md +6 -5
- package/rebrand/brand.json +1 -1
- package/rebrand/overlay/CHANGELOG.md +12 -1
- package/rebrand/overlay/bin/agent-targets.json +726 -0
- package/rebrand/overlay/bin/i3-composer-agent.cjs +83 -15
- package/rebrand/overlay/bin/install.cjs +164 -0
- package/rebrand/overlay/bin/skills-registry.LICENSE +21 -0
- package/rebrand/overlay/bin/wizard.cjs +66 -0
- package/rebrand/overlay/tests/installer.test.cjs +193 -0
- package/rebrand/templates/README.md +52 -14
- package/rebrand/templates/codebuddy-setup.md +55 -8
- package/rebrand/templates/codex-setup.md +14 -3
- package/tests/installer.test.cjs +193 -0
|
@@ -12,23 +12,35 @@ const identity = `i3 Composer Agent v${pkg.version}\nBased on agent-skills v${pk
|
|
|
12
12
|
const help = `${identity}
|
|
13
13
|
${pkg.name}
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Install engineering skills with a multi-tool installation wizard.
|
|
16
16
|
|
|
17
17
|
Usage:
|
|
18
|
-
npx ${pkg.name}
|
|
19
|
-
npx ${pkg.name} --
|
|
20
|
-
npx ${pkg.name} --
|
|
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>
|
|
21
22
|
npx ${pkg.name} --extract --dir <directory>
|
|
22
23
|
|
|
23
24
|
Options:
|
|
24
|
-
--
|
|
25
|
-
--
|
|
26
|
-
--
|
|
27
|
-
--
|
|
28
|
-
--
|
|
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
|
|
29
39
|
|
|
30
|
-
|
|
31
|
-
|
|
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.
|
|
32
44
|
`;
|
|
33
45
|
|
|
34
46
|
function quote(value) { return "'" + value.replaceAll("'", "'\\''") + "'"; }
|
|
@@ -43,15 +55,14 @@ function realDestination(target) {
|
|
|
43
55
|
}
|
|
44
56
|
return path.join(fs.realpathSync(existing), ...suffix);
|
|
45
57
|
}
|
|
46
|
-
function
|
|
47
|
-
if (!args.length || args.includes('--help')) { console.log(help); return; }
|
|
48
|
-
if (args.length === 1 && args[0] === '--version') { console.log(pkg.version); return; }
|
|
58
|
+
function prepare() {
|
|
49
59
|
let host;
|
|
50
60
|
let directory = path.join(process.cwd(), name);
|
|
51
61
|
let dryRun = false;
|
|
52
62
|
let force = false;
|
|
53
63
|
for (let i = 0; i < args.length; i++) {
|
|
54
64
|
const arg = args[i];
|
|
65
|
+
if (arg === '--prepare') continue;
|
|
55
66
|
if (['--codebuddy', '--codex', '--claude', '--extract'].includes(arg)) {
|
|
56
67
|
if (host) throw new Error('Choose exactly one host flag or --extract.');
|
|
57
68
|
host = arg.slice(2);
|
|
@@ -119,4 +130,61 @@ function main() {
|
|
|
119
130
|
console.log(`Use /${name}:spec, /${name}:plan, /${name}:build, and /${name}:ship in ${host === 'claude' ? 'Claude Code' : 'CodeBuddy'}.`);
|
|
120
131
|
}
|
|
121
132
|
}
|
|
122
|
-
|
|
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; });
|
package/bin/install.cjs
ADDED
|
@@ -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 };
|
package/docs/codebuddy-setup.md
CHANGED
|
@@ -1,12 +1,60 @@
|
|
|
1
1
|
# Using i3 Composer Agent with CodeBuddy
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
|
37
|
-
|
|
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
|
-
|
|
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.
|
package/docs/codex-setup.md
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
# Using i3 Composer Agent with Codex
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
package/package.json
CHANGED
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"upstreamCommit": "1401c8b8030e023baeebb31781a6653fe8e93026",
|
|
83
83
|
"upstreamRepository": "https://github.com/addyosmani/agent-skills"
|
|
84
84
|
},
|
|
85
|
-
"version": "1.0.
|
|
85
|
+
"version": "1.0.1",
|
|
86
86
|
"repository": {
|
|
87
87
|
"type": "git",
|
|
88
88
|
"url": "https://github.com/49780522/i3-composer-agent.git"
|
package/plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i3-composer-agent",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Production-grade engineering skills for AI coding agents covering the development lifecycle from spec to ship.",
|
|
5
5
|
"homepage": "https://github.com/49780522/i3-composer-agent",
|
|
6
6
|
"repository": "https://github.com/49780522/i3-composer-agent"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Installation wizard correction
|
|
2
|
+
|
|
3
|
+
The published 1.0.0 CLI prepares plugin files but does not implement the
|
|
4
|
+
installation wizard previously described to the user. Restore the promised
|
|
5
|
+
installation flow, including the user's confirmed multiple-tool selection and
|
|
6
|
+
CodeBuddy desktop IDE target.
|
|
7
|
+
|
|
8
|
+
## Acceptance contract
|
|
9
|
+
|
|
10
|
+
- Running the package in an interactive terminal opens a tool multi-select,
|
|
11
|
+
project/global scope selection, destination summary, and confirmation.
|
|
12
|
+
- Host flags preselect tools rather than silently preparing a plugin. The tool
|
|
13
|
+
catalog preserves the installation paths from the pinned Vercel Skills CLI
|
|
14
|
+
registry; CodeBuddy, Codex, Claude Code, Cursor and other tools remain choices.
|
|
15
|
+
- Native installation copies all skills and their shared references. CodeBuddy
|
|
16
|
+
and Claude Code also receive the nine existing command workflows and personas.
|
|
17
|
+
Portable command references use the installed stable skill IDs.
|
|
18
|
+
- Shared destinations are installed once when several tools use the same path.
|
|
19
|
+
- Existing assets require confirmed replacement and are retained in backups.
|
|
20
|
+
Unrelated settings and project files are untouched. Failed installation rolls
|
|
21
|
+
back completed writes; cancellation and dry runs write nothing.
|
|
22
|
+
- `--prepare`, `--extract`, and existing explicit `--dir` calls retain complete
|
|
23
|
+
plugin preparation. Plain `--version` remains machine-readable.
|
|
24
|
+
- Noninteractive installation requires explicit tools, scope and `--yes`;
|
|
25
|
+
replacement additionally requires `--force`.
|
|
26
|
+
- Packaging tests exercise the shipped CLI; a real terminal session verifies
|
|
27
|
+
the wizard. File discovery and actual model execution are separate evidence.
|
|
28
|
+
|
|
29
|
+
## Implementation and verification
|
|
30
|
+
|
|
31
|
+
Keep upstream content unchanged on `agent/source`; all fork changes live under
|
|
32
|
+
`rebrand/`. Distribution modules and tests are replayed from `rebrand/overlay/`.
|
|
33
|
+
Use Node.js built-ins and the existing `node:test` conventions. No new runtime
|
|
34
|
+
dependency is required. Keep the upstream baseline at 0.6.12 and the release
|
|
35
|
+
version at 1.0.0 during development. Recommend and obtain the user's approval
|
|
36
|
+
for a new release version before changing it or publishing.
|
|
37
|
+
|
|
38
|
+
On 2026-10-07 the user accepted the recommended 1.0.1 correction release and
|
|
39
|
+
authorized publication. The upstream baseline remains 0.6.12.
|
|
40
|
+
|
|
41
|
+
Slices: first prove native multi-tool installation fails on the published CLI;
|
|
42
|
+
then implement directory planning and copying; then add the interactive flow;
|
|
43
|
+
finally verify conflicts, rollback, package extraction and terminal execution.
|
|
44
|
+
|
|
45
|
+
Commands: `node --test tests/installer.test.cjs`, `npm test`, `npm run validate`,
|
|
46
|
+
`npm run rebrand:check`, and `npm pack` in a generated branded candidate.
|
package/rebrand/MAINTENANCE.md
CHANGED
|
@@ -97,7 +97,8 @@ npm pack --pack-destination ../releases/next
|
|
|
97
97
|
|
|
98
98
|
## 发布及验收
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
维护流程与安装能力分开验收。默认 npm CLI 使用多工具安装向导;`--prepare`
|
|
101
|
+
保留完整插件准备和宿主注册提示。两种模式分别验收,不把准备文件等同于安装。
|
|
101
102
|
实际宿主安装范围、启用、命令执行以及交互向导仍需各自验证。
|
|
102
103
|
Codex 的生命周期斜杠命令适配是独立工作,已有 Skill ID 不因维护流程改名。
|
|
103
104
|
|
package/rebrand/NOTICE.md
CHANGED
|
@@ -5,5 +5,10 @@ i3 Composer Agent is a branded distribution of
|
|
|
5
5
|
The initial distribution is based on upstream commit `1401c8b` (version 0.6.12).
|
|
6
6
|
|
|
7
7
|
The original MIT license and copyright notice are preserved in LICENSE.
|
|
8
|
-
Givolt maintains the branding, npm distribution, and
|
|
8
|
+
Givolt maintains the branding, npm distribution, and installation CLI.
|
|
9
9
|
The original skill IDs, command workflows, and persona definitions are retained.
|
|
10
|
+
|
|
11
|
+
The tool installation directory presets in `bin/agent-targets.json` are adapted
|
|
12
|
+
from Vercel's [Skills CLI registry](https://github.com/vercel-labs/skills/blob/48dc9e8eb8aa19040e91cb90ba0b642c9cbad57e/src/agents.ts)
|
|
13
|
+
at commit `48dc9e8eb8aa19040e91cb90ba0b642c9cbad57e`. Copyright (c) 2026
|
|
14
|
+
Vercel, Inc. Its MIT license is retained in `bin/skills-registry.LICENSE`.
|
package/rebrand/VALIDATION.md
CHANGED
package/rebrand/VERSIONING.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
本规则已经用户确认,仅适用于 i3 Composer Agent 仓库。
|
|
4
4
|
|
|
5
|
-
| 信息 | 权威来源 |
|
|
5
|
+
| 信息 | 权威来源 | 当前发行值 |
|
|
6
6
|
| --- | --- | --- |
|
|
7
|
-
| i3 发行版本 | brand.json 的 releaseVersion | 1.0.
|
|
7
|
+
| i3 发行版本 | brand.json 的 releaseVersion | 1.0.1 |
|
|
8
8
|
| 上游版本 | upstream.json 的 upstreamVersion | 0.6.12 |
|
|
9
9
|
| 上游提交 | upstream.json 的 upstreamCommit | 1401c8b8030e023baeebb31781a6653fe8e93026 |
|
|
10
10
|
|
|
@@ -20,13 +20,14 @@ package.json 的 i3ComposerAgent 来源字段。package.template.json 不保存
|
|
|
20
20
|
README、CLI 帮助和准备完成提示显示:
|
|
21
21
|
|
|
22
22
|
```text
|
|
23
|
-
i3 Composer Agent v1.0.
|
|
23
|
+
i3 Composer Agent v1.0.1
|
|
24
24
|
Based on agent-skills v0.6.12
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
在上述例子中,--version 输出 1.0.
|
|
27
|
+
在上述例子中,--version 输出 1.0.1,保留脚本使用的单一版本格式。--dry-run 的 JSON
|
|
28
28
|
分别提供 version、upstreamVersion、upstreamCommit。
|
|
29
29
|
|
|
30
30
|
已经发布的 @givolt/i3-composer-agent@0.6.12 及其 Git 标签作为历史记录保留。
|
|
31
31
|
之前的 0.6.13 是未发布候选,新的独立版本候选替代它。
|
|
32
|
-
1.0.0
|
|
32
|
+
1.0.0 已发布。1.0.1 用于修复安装向导遗漏,版本与发布已由用户确认。
|
|
33
|
+
源码与打包验证不代表 npm 发布;发布仍按 MAINTENANCE.md 单独执行。
|
package/rebrand/brand.json
CHANGED