@givolt/i3-composer-agent 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +23 -0
- package/.claude/commands/build.md +44 -0
- package/.claude/commands/code-simplify.md +22 -0
- package/.claude/commands/constraints.md +32 -0
- package/.claude/commands/plan.md +18 -0
- package/.claude/commands/review.md +16 -0
- package/.claude/commands/ship.md +72 -0
- package/.claude/commands/spec.md +17 -0
- package/.claude/commands/test.md +19 -0
- package/.claude/commands/webperf.md +32 -0
- package/.claude-plugin/marketplace.json +27 -0
- package/.claude-plugin/plugin.json +19 -0
- package/.codex-plugin/plugin.json +28 -0
- package/.gemini/commands/build.toml +43 -0
- package/.gemini/commands/code-simplify.toml +22 -0
- package/.gemini/commands/constraints.toml +32 -0
- package/.gemini/commands/planning.toml +18 -0
- package/.gemini/commands/review.toml +16 -0
- package/.gemini/commands/ship.toml +72 -0
- package/.gemini/commands/spec.toml +17 -0
- package/.gemini/commands/test.toml +19 -0
- package/.gemini/commands/webperf.toml +32 -0
- package/CHANGELOG.md +23 -0
- package/LICENSE +21 -0
- package/NOTICE.md +9 -0
- package/README.md +117 -2
- package/agents/code-reviewer.md +102 -0
- package/agents/security-auditor.md +112 -0
- package/agents/test-engineer.md +95 -0
- package/agents/web-performance-auditor.md +184 -0
- package/bin/i3-composer-agent.cjs +122 -0
- package/commands/build.toml +43 -0
- package/commands/code-simplify.toml +22 -0
- package/commands/constraints.toml +32 -0
- package/commands/planning.toml +18 -0
- package/commands/review.toml +16 -0
- package/commands/ship.toml +72 -0
- package/commands/spec.toml +17 -0
- package/commands/test.toml +19 -0
- package/commands/webperf.toml +32 -0
- package/docs/adoption-guide.md +129 -0
- package/docs/advanced-per-agent-configuration.md +70 -0
- package/docs/agents.md +123 -0
- package/docs/antigravity-setup.md +128 -0
- package/docs/codebuddy-setup.md +41 -0
- package/docs/codex-setup.md +34 -0
- package/docs/commandcode-setup.md +69 -0
- package/docs/comparison.md +131 -0
- package/docs/copilot-cli-setup.md +64 -0
- package/docs/copilot-setup.md +162 -0
- package/docs/cursor-setup.md +225 -0
- package/docs/developer-onboarding.md +116 -0
- package/docs/gemini-cli-setup.md +133 -0
- package/docs/getting-started.md +208 -0
- package/docs/opencode-setup.md +259 -0
- package/docs/other-hosts.md +7 -0
- package/docs/skill-anatomy.md +203 -0
- package/docs/windsurf-setup.md +48 -0
- package/evals/README.md +101 -0
- package/evals/cases/api-and-interface-design.json +45 -0
- package/evals/cases/browser-testing-with-devtools.json +44 -0
- package/evals/cases/ci-cd-and-automation.json +45 -0
- package/evals/cases/code-review-and-quality.json +49 -0
- package/evals/cases/code-simplification.json +45 -0
- package/evals/cases/constraint-driven-development.json +79 -0
- package/evals/cases/context-engineering.json +44 -0
- package/evals/cases/debugging-and-error-recovery.json +58 -0
- package/evals/cases/deprecation-and-migration.json +52 -0
- package/evals/cases/documentation-and-adrs.json +44 -0
- package/evals/cases/doubt-driven-development.json +43 -0
- package/evals/cases/frontend-ui-engineering.json +52 -0
- package/evals/cases/git-workflow-and-versioning.json +47 -0
- package/evals/cases/idea-refine.json +43 -0
- package/evals/cases/incremental-implementation.json +57 -0
- package/evals/cases/interview-me.json +42 -0
- package/evals/cases/observability-and-instrumentation.json +44 -0
- package/evals/cases/performance-optimization.json +53 -0
- package/evals/cases/planning-and-task-breakdown.json +45 -0
- package/evals/cases/security-and-hardening.json +53 -0
- package/evals/cases/shipping-and-launch.json +56 -0
- package/evals/cases/source-driven-development.json +44 -0
- package/evals/cases/spec-driven-development.json +72 -0
- package/evals/cases/test-driven-development.json +73 -0
- package/evals/cases/using-agent-skills.json +44 -0
- package/evals/fixtures/api-and-interface-design/service-brief.md +19 -0
- package/evals/fixtures/browser-testing-with-devtools/README.md +5 -0
- package/evals/fixtures/browser-testing-with-devtools/index.html +24 -0
- package/evals/fixtures/browser-testing-with-devtools/server.js +15 -0
- package/evals/fixtures/ci-cd-and-automation/package.json +8 -0
- package/evals/fixtures/ci-cd-and-automation/src/slug.js +3 -0
- package/evals/fixtures/ci-cd-and-automation/test/slug.test.js +9 -0
- package/evals/fixtures/code-review-and-quality/user-search.diff +16 -0
- package/evals/fixtures/code-simplification/config-parser.js +46 -0
- package/evals/fixtures/code-simplification/config-parser.test.js +15 -0
- package/evals/fixtures/context-engineering/context-audit.md +15 -0
- package/evals/fixtures/debugging-and-error-recovery/pagination.js +8 -0
- package/evals/fixtures/debugging-and-error-recovery/pagination.test.js +9 -0
- package/evals/fixtures/debugging-and-error-recovery/time-pressure.md +6 -0
- package/evals/fixtures/deprecation-and-migration/api-inventory.md +9 -0
- package/evals/fixtures/documentation-and-adrs/decision-context.md +16 -0
- package/evals/fixtures/doubt-driven-development/migration-plan.md +19 -0
- package/evals/fixtures/frontend-ui-engineering/Button.tsx +7 -0
- package/evals/fixtures/frontend-ui-engineering/design-system.md +11 -0
- package/evals/fixtures/git-workflow-and-versioning/.eval/working-tree.patch +21 -0
- package/evals/fixtures/git-workflow-and-versioning/app.js +7 -0
- package/evals/fixtures/git-workflow-and-versioning/app.test.js +9 -0
- package/evals/fixtures/incremental-implementation/reports.js +7 -0
- package/evals/fixtures/incremental-implementation/reports.test.js +12 -0
- package/evals/fixtures/incremental-implementation/tasks/plan.md +8 -0
- package/evals/fixtures/incremental-implementation-pressure/draft-export.js +17 -0
- package/evals/fixtures/incremental-implementation-pressure/scenario.md +9 -0
- package/evals/fixtures/observability-and-instrumentation/operations.md +11 -0
- package/evals/fixtures/observability-and-instrumentation/payment-retry.js +14 -0
- package/evals/fixtures/performance-optimization/benchmark.js +15 -0
- package/evals/fixtures/performance-optimization/products.js +14 -0
- package/evals/fixtures/planning-and-task-breakdown/notifications-spec.md +18 -0
- package/evals/fixtures/security-and-hardening/webhook.js +11 -0
- package/evals/fixtures/security-and-hardening/webhook.test.js +13 -0
- package/evals/fixtures/shipping-and-launch/authority-pressure.md +6 -0
- package/evals/fixtures/shipping-and-launch/launch-status.md +11 -0
- package/evals/fixtures/source-driven-development/framework-task.md +10 -0
- package/evals/fixtures/spec-driven-development/billing-brief.md +16 -0
- package/evals/fixtures/spec-driven-development-decomposition/portal-brief.md +22 -0
- package/evals/fixtures/test-driven-development/BUG.md +10 -0
- package/evals/fixtures/test-driven-development/README.md +28 -0
- package/evals/fixtures/test-driven-development/package.json +9 -0
- package/evals/fixtures/test-driven-development/src/split.js +8 -0
- package/evals/fixtures/test-driven-development/test/split.test.js +13 -0
- package/evals/fixtures/test-driven-development-ecosystem/README.md +9 -0
- package/evals/fixtures/test-driven-development-ecosystem/ledger.py +15 -0
- package/evals/fixtures/test-driven-development-ecosystem/test_ledger.py +19 -0
- package/evals/fixtures/using-agent-skills/incident.md +6 -0
- package/evals/plugin/code-review-fires/graders/off-by-one.md +7 -0
- package/evals/plugin/code-review-fires/graders/severity-labels.md +4 -0
- package/evals/plugin/code-review-fires/graders/skill-fired.md +5 -0
- package/evals/plugin/code-review-fires/prompt.md +31 -0
- package/evals/plugin/code-review-stays-quiet/graders/not-fired.md +8 -0
- package/evals/plugin/code-review-stays-quiet/graders/tdd-fired.md +6 -0
- package/evals/plugin/code-review-stays-quiet/prompt.md +8 -0
- package/evals/plugin/code-review-stays-quiet-on-commit-message/graders/not-fired.md +8 -0
- package/evals/plugin/code-review-stays-quiet-on-commit-message/prompt.md +25 -0
- package/evals/skill-impact.md +6 -0
- package/hooks/SDD-CACHE.md +167 -0
- package/hooks/SIMPLIFY-IGNORE.md +91 -0
- package/hooks/sdd-cache-post.sh +135 -0
- package/hooks/sdd-cache-pre.sh +106 -0
- package/hooks/sdd-cache-test.sh +246 -0
- package/hooks/session-start-test.sh +47 -0
- package/hooks/session-start.sh +33 -0
- package/hooks/simplify-ignore-test.sh +476 -0
- package/hooks/simplify-ignore.sh +352 -0
- package/package.json +89 -4
- package/plugin.json +7 -0
- package/rebrand/MAINTENANCE.md +112 -0
- package/rebrand/NOTICE.md +9 -0
- package/rebrand/PR-WORKFLOW.md +34 -0
- package/rebrand/UNIFICATION-SPEC.md +33 -0
- package/rebrand/VALIDATION.md +28 -0
- package/rebrand/VERSIONING.md +32 -0
- package/rebrand/apply.cjs +194 -0
- package/rebrand/brand.json +13 -0
- package/rebrand/catalog.cjs +56 -0
- package/rebrand/fork-patches.json +12 -0
- package/rebrand/overlay/.github/workflows/test-plugin-install.yml +177 -0
- package/rebrand/overlay/CHANGELOG.md +23 -0
- package/rebrand/overlay/bin/i3-composer-agent.cjs +122 -0
- package/rebrand/overlay/scripts/validate-brand.cjs +44 -0
- package/rebrand/overlay/tests/bundle.test.cjs +85 -0
- package/rebrand/package.template.json +82 -0
- package/rebrand/rebrand.sh +4 -0
- package/rebrand/record-upstream.cjs +37 -0
- package/rebrand/templates/README.md +86 -0
- package/rebrand/templates/codebuddy-setup.md +41 -0
- package/rebrand/templates/codex-setup.md +34 -0
- package/rebrand/tests/pipeline.test.cjs +185 -0
- package/rebrand/upstream.json +171 -0
- package/references/accessibility-checklist.md +160 -0
- package/references/definition-of-done.md +67 -0
- package/references/observability-checklist.md +92 -0
- package/references/orchestration-patterns.md +370 -0
- package/references/performance-checklist.md +236 -0
- package/references/security-checklist.md +248 -0
- package/references/testing-patterns.md +235 -0
- package/scripts/floor-guard-reference-test.js +291 -0
- package/scripts/lib/skill-lint-test.js +650 -0
- package/scripts/lib/skill-lint.js +584 -0
- package/scripts/run-evals-test.js +623 -0
- package/scripts/run-evals.js +638 -0
- package/scripts/validate-artifact-paths-test.js +179 -0
- package/scripts/validate-artifact-paths.js +128 -0
- package/scripts/validate-brand.cjs +44 -0
- package/scripts/validate-commands-test.js +216 -0
- package/scripts/validate-commands.js +222 -0
- package/scripts/validate-reference-links-test.js +495 -0
- package/scripts/validate-reference-links.js +187 -0
- package/scripts/validate-skills.js +69 -0
- package/scripts/validate-versions-test.js +32 -0
- package/scripts/validate-versions.js +35 -0
- package/skills/api-and-interface-design/SKILL.md +367 -0
- package/skills/browser-testing-with-devtools/SKILL.md +317 -0
- package/skills/ci-cd-and-automation/SKILL.md +390 -0
- package/skills/code-review-and-quality/SKILL.md +398 -0
- package/skills/code-simplification/SKILL.md +331 -0
- package/skills/constraint-driven-development/SKILL.md +311 -0
- package/skills/constraint-driven-development/references/floor-guard.md +182 -0
- package/skills/context-engineering/SKILL.md +353 -0
- package/skills/debugging-and-error-recovery/SKILL.md +300 -0
- package/skills/deprecation-and-migration/SKILL.md +247 -0
- package/skills/documentation-and-adrs/SKILL.md +288 -0
- package/skills/doubt-driven-development/SKILL.md +243 -0
- package/skills/frontend-ui-engineering/SKILL.md +340 -0
- package/skills/git-workflow-and-versioning/SKILL.md +355 -0
- package/skills/idea-refine/SKILL.md +178 -0
- package/skills/idea-refine/examples.md +238 -0
- package/skills/idea-refine/frameworks.md +99 -0
- package/skills/idea-refine/refinement-criteria.md +113 -0
- package/skills/idea-refine/scripts/idea-refine.sh +15 -0
- package/skills/incremental-implementation/SKILL.md +249 -0
- package/skills/interview-me/SKILL.md +235 -0
- package/skills/observability-and-instrumentation/SKILL.md +238 -0
- package/skills/performance-optimization/SKILL.md +267 -0
- package/skills/performance-optimization/references/optimization-patterns.md +250 -0
- package/skills/planning-and-task-breakdown/SKILL.md +257 -0
- package/skills/security-and-hardening/SKILL.md +216 -0
- package/skills/security-and-hardening/references/hardening-patterns.md +324 -0
- package/skills/shipping-and-launch/SKILL.md +330 -0
- package/skills/source-driven-development/SKILL.md +216 -0
- package/skills/spec-driven-development/SKILL.md +259 -0
- package/skills/test-driven-development/SKILL.md +398 -0
- package/skills/using-agent-skills/SKILL.md +192 -0
- package/tests/bundle.test.cjs +85 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const root = path.resolve(__dirname, '..');
|
|
8
|
+
const brand = require('../rebrand/brand.json');
|
|
9
|
+
const pkg = require('../package.json');
|
|
10
|
+
const upstream = require('../rebrand/upstream.json');
|
|
11
|
+
const { validateCatalog } = require('../rebrand/catalog.cjs');
|
|
12
|
+
validateCatalog(root, upstream);
|
|
13
|
+
function json(relative) { return JSON.parse(fs.readFileSync(path.join(root, relative), 'utf8')); }
|
|
14
|
+
assert.equal(pkg.name, brand.npmName);
|
|
15
|
+
assert.equal(pkg.version, brand.releaseVersion);
|
|
16
|
+
assert.equal(brand.upstreamCommit, upstream.upstreamCommit);
|
|
17
|
+
assert.equal(pkg.i3ComposerAgent.upstreamVersion, upstream.upstreamVersion);
|
|
18
|
+
assert.equal(pkg.i3ComposerAgent.upstreamCommit, upstream.upstreamCommit);
|
|
19
|
+
assert.equal(pkg.i3ComposerAgent.upstreamRepository, brand.upstream);
|
|
20
|
+
assert.equal(Object.keys(pkg.bin).length, 1, 'npx must resolve one executable');
|
|
21
|
+
assert.ok(fs.existsSync(path.join(root, pkg.bin[brand.name])));
|
|
22
|
+
for (const relative of ['plugin.json', '.claude-plugin/plugin.json', '.codex-plugin/plugin.json']) {
|
|
23
|
+
assert.equal(json(relative).name, brand.name, `${relative}: inconsistent plugin identity`);
|
|
24
|
+
}
|
|
25
|
+
for (const relative of ['.claude-plugin/marketplace.json', '.agents/plugins/marketplace.json']) {
|
|
26
|
+
const marketplace = json(relative);
|
|
27
|
+
assert.equal(marketplace.name, brand.name);
|
|
28
|
+
assert.equal(marketplace.plugins[0].name, brand.name);
|
|
29
|
+
assert.ok(marketplace.plugins[0].source === './' || marketplace.plugins[0].source.path === './', 'marketplace must load this distribution');
|
|
30
|
+
}
|
|
31
|
+
assert.ok(json('.codex-plugin/plugin.json').interface.shortDescription.length <= 30);
|
|
32
|
+
const skills = fs.readdirSync(path.join(root, 'skills')).filter(name => fs.existsSync(path.join(root, 'skills', name, 'SKILL.md')));
|
|
33
|
+
assert.equal(skills.length, upstream.skills.length, 'brand conversion must preserve the reviewed upstream skills');
|
|
34
|
+
assert.ok(skills.includes('using-agent-skills'), 'meta-skill ID must remain stable');
|
|
35
|
+
const commands = fs.readdirSync(path.join(root, '.claude/commands')).filter(name => name.endsWith('.md'));
|
|
36
|
+
assert.equal(commands.length, upstream.commands.length, 'brand conversion must preserve the reviewed upstream commands');
|
|
37
|
+
for (const command of commands) {
|
|
38
|
+
const body = fs.readFileSync(path.join(root, '.claude/commands', command), 'utf8');
|
|
39
|
+
assert.ok(!body.includes('agent-skills:'), `${command}: stale plugin namespace`);
|
|
40
|
+
for (const match of body.matchAll(/i3-composer-agent:([a-z-]+)/g)) assert.ok(skills.includes(match[1]), `${command}: unknown skill ${match[1]}`);
|
|
41
|
+
}
|
|
42
|
+
for (const relative of pkg.i3ComposerAgent.pluginFiles) assert.ok(fs.existsSync(path.join(root, relative)), `bundle is missing ${relative}`);
|
|
43
|
+
assert.match(fs.readFileSync(path.join(root, 'LICENSE'), 'utf8'), /Copyright \(c\) 2025 Addy Osmani/);
|
|
44
|
+
console.log('Brand identity, package entrypoint, reviewed upstream catalog, and attribution are consistent.');
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const os = require('node:os');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
const { spawnSync } = require('node:child_process');
|
|
9
|
+
const root = path.resolve(__dirname, '..');
|
|
10
|
+
const upstream = require('../rebrand/upstream.json');
|
|
11
|
+
const cli = path.join(root, 'bin/i3-composer-agent.cjs');
|
|
12
|
+
function run(args) { return spawnSync(process.execPath, [cli, ...args], { encoding: 'utf8' }); }
|
|
13
|
+
function workspace(t) {
|
|
14
|
+
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'i3-agent-bundle-'));
|
|
15
|
+
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
|
|
16
|
+
return directory;
|
|
17
|
+
}
|
|
18
|
+
test('help and version identify the package without preparing files', () => {
|
|
19
|
+
const help = run(['--help']).stdout;
|
|
20
|
+
assert.match(help, /@givolt\/i3-composer-agent/);
|
|
21
|
+
assert.ok(help.includes(`i3 Composer Agent v${require('../package.json').version}`));
|
|
22
|
+
assert.ok(help.includes(`Based on agent-skills v${upstream.upstreamVersion}`));
|
|
23
|
+
assert.equal(run(['--version']).stdout.trim(), require('../package.json').version);
|
|
24
|
+
});
|
|
25
|
+
test('dry run and invalid arguments leave the target untouched', t => {
|
|
26
|
+
const target = path.join(workspace(t), 'plugin');
|
|
27
|
+
assert.equal(run(['--codex', '--dir', target, '--dry-run']).status, 0);
|
|
28
|
+
assert.equal(fs.existsSync(target), false);
|
|
29
|
+
assert.equal(run(['--codebuddy', '--codex', '--dir', target]).status, 1);
|
|
30
|
+
assert.equal(run(['--extract', '--dir']).status, 1);
|
|
31
|
+
assert.equal(fs.existsSync(target), false);
|
|
32
|
+
});
|
|
33
|
+
test('complete bundle preserves reference links and all host assets at a path with spaces', t => {
|
|
34
|
+
const target = path.join(workspace(t), 'plugin with spaces');
|
|
35
|
+
const result = run(['--codebuddy', '--dir', target]);
|
|
36
|
+
assert.equal(result.status, 0, result.stderr);
|
|
37
|
+
assert.ok(result.stdout.includes(`i3 Composer Agent v${require('../package.json').version}`));
|
|
38
|
+
assert.ok(result.stdout.includes(`Based on agent-skills v${upstream.upstreamVersion}`));
|
|
39
|
+
assert.match(result.stdout, /\/plugin install i3-composer-agent@i3-composer-agent/);
|
|
40
|
+
assert.equal(fs.readdirSync(path.join(target, 'skills')).length, upstream.skills.length);
|
|
41
|
+
assert.equal(fs.readdirSync(path.join(target, '.claude/commands')).length, upstream.commands.length);
|
|
42
|
+
assert.equal(fs.readdirSync(path.join(target, 'agents')).filter(name => name.endsWith('.md')).length, upstream.personas.length);
|
|
43
|
+
assert.equal(JSON.parse(fs.readFileSync(path.join(target, '.codex-plugin/plugin.json'), 'utf8')).name, 'i3-composer-agent');
|
|
44
|
+
const check = spawnSync(process.execPath, [path.join(target, 'scripts/validate-reference-links.js')], { cwd: target, encoding: 'utf8' });
|
|
45
|
+
assert.equal(check.status, 0, check.stdout + check.stderr);
|
|
46
|
+
assert.equal(fs.existsSync(path.join(target, 'AGENTS.md')), false);
|
|
47
|
+
assert.equal(fs.existsSync(path.join(target, 'CLAUDE.md')), false);
|
|
48
|
+
});
|
|
49
|
+
test('existing unrelated files cannot be replaced even with force', t => {
|
|
50
|
+
const target = path.join(workspace(t), 'existing');
|
|
51
|
+
fs.mkdirSync(target);
|
|
52
|
+
fs.writeFileSync(path.join(target, 'plugin.json'), '{"name":"another-plugin"}\n');
|
|
53
|
+
fs.writeFileSync(path.join(target, 'keep.txt'), 'business data');
|
|
54
|
+
assert.equal(run(['--extract', '--dir', target]).status, 1);
|
|
55
|
+
assert.equal(run(['--extract', '--dir', target, '--force']).status, 1);
|
|
56
|
+
assert.equal(fs.readFileSync(path.join(target, 'keep.txt'), 'utf8'), 'business data');
|
|
57
|
+
});
|
|
58
|
+
test('installed packages beneath node_modules remain fully extractable', t => {
|
|
59
|
+
const parent = workspace(t);
|
|
60
|
+
const installed = path.join(parent, 'node_modules', '@givolt', 'i3-composer-agent');
|
|
61
|
+
assert.equal(run(['--extract', '--dir', installed]).status, 0);
|
|
62
|
+
const target = path.join(parent, 'plugin');
|
|
63
|
+
const result = spawnSync(process.execPath, [path.join(installed, 'bin/i3-composer-agent.cjs'), '--extract', '--dir', target], { encoding: 'utf8' });
|
|
64
|
+
assert.equal(result.status, 0, result.stderr);
|
|
65
|
+
assert.equal(JSON.parse(fs.readFileSync(path.join(target, 'package.json'), 'utf8')).name, '@givolt/i3-composer-agent');
|
|
66
|
+
assert.equal(fs.readdirSync(path.join(target, 'skills')).length, upstream.skills.length);
|
|
67
|
+
});
|
|
68
|
+
test('replacement preserves the complete previous bundle in a backup', t => {
|
|
69
|
+
const parent = workspace(t);
|
|
70
|
+
const target = path.join(parent, 'plugin');
|
|
71
|
+
assert.equal(run(['--extract', '--dir', target]).status, 0);
|
|
72
|
+
fs.writeFileSync(path.join(target, 'custom-note.txt'), 'preserve me');
|
|
73
|
+
const result = run(['--extract', '--dir', target, '--force']);
|
|
74
|
+
assert.equal(result.status, 0, result.stderr);
|
|
75
|
+
const backups = fs.readdirSync(parent).filter(name => name.startsWith('plugin.backup-'));
|
|
76
|
+
assert.equal(backups.length, 1);
|
|
77
|
+
assert.equal(fs.readFileSync(path.join(parent, backups[0], 'custom-note.txt'), 'utf8'), 'preserve me');
|
|
78
|
+
assert.equal(fs.existsSync(path.join(target, 'custom-note.txt')), false);
|
|
79
|
+
});
|
|
80
|
+
test('source directories and aliases cannot be overwritten', t => {
|
|
81
|
+
assert.equal(run(['--extract', '--dir', root, '--force']).status, 1);
|
|
82
|
+
const alias = path.join(workspace(t), 'source-alias');
|
|
83
|
+
fs.symlinkSync(root, alias, 'dir');
|
|
84
|
+
assert.equal(run(['--extract', '--dir', alias, '--force']).status, 1);
|
|
85
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@givolt/i3-composer-agent",
|
|
3
|
+
"description": "i3 Composer Agent: production-grade engineering skills and lifecycle commands for AI coding agents.",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": "Givolt",
|
|
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
|
+
},
|
|
21
|
+
"files": [
|
|
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"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"rebrand": "node rebrand/apply.cjs --apply",
|
|
44
|
+
"rebrand:check": "node rebrand/apply.cjs --check",
|
|
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",
|
|
46
|
+
"test": "node --test scripts/*-test.js scripts/lib/*-test.js tests/*.test.cjs rebrand/tests/*.test.cjs",
|
|
47
|
+
"prepack": "npm run validate"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"i3-composer",
|
|
51
|
+
"agent-skills",
|
|
52
|
+
"codebuddy",
|
|
53
|
+
"codex",
|
|
54
|
+
"claude-code"
|
|
55
|
+
],
|
|
56
|
+
"i3ComposerAgent": {
|
|
57
|
+
"pluginFiles": [
|
|
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
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { execFileSync } = require('node:child_process');
|
|
7
|
+
const { describe, runtimeRoots, runtimeFiles } = require('./catalog.cjs');
|
|
8
|
+
const root = path.resolve(__dirname, '..');
|
|
9
|
+
const ref = process.argv[2];
|
|
10
|
+
if (!ref || ref.startsWith('-') || process.argv.length !== 3) {
|
|
11
|
+
console.error('Usage: node rebrand/record-upstream.cjs <reviewed upstream ref>');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const git = args => execFileSync('git', args, { cwd: root, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
|
|
16
|
+
const commit = git(['rev-parse', '--verify', '--end-of-options', `${ref}^{commit}`]).trim();
|
|
17
|
+
const entries = git(['ls-tree', '-rz', commit, '--', ...runtimeRoots, ...runtimeFiles]).split('\0').filter(Boolean);
|
|
18
|
+
const blobs = new Map(entries.filter(line => /^100\d+ blob /.test(line)).map(line => {
|
|
19
|
+
const [metadata, file] = line.split('\t');
|
|
20
|
+
return [file, metadata.split(' ')[2]];
|
|
21
|
+
}));
|
|
22
|
+
const read = file => git(['cat-file', 'blob', blobs.get(file)]);
|
|
23
|
+
const snapshot = {
|
|
24
|
+
upstreamCommit: commit,
|
|
25
|
+
upstreamVersion: JSON.parse(read('plugin.json')).version,
|
|
26
|
+
...describe([...blobs.keys()], read),
|
|
27
|
+
};
|
|
28
|
+
fs.writeFileSync(path.join(__dirname, 'upstream.json'), JSON.stringify(snapshot, null, 2) + '\n');
|
|
29
|
+
const brandFile = path.join(__dirname, 'brand.json');
|
|
30
|
+
const brand = JSON.parse(fs.readFileSync(brandFile, 'utf8'));
|
|
31
|
+
brand.upstreamCommit = commit;
|
|
32
|
+
fs.writeFileSync(brandFile, JSON.stringify(brand, null, 2) + '\n');
|
|
33
|
+
console.log(JSON.stringify({ commit, version: snapshot.upstreamVersion, skills: snapshot.skills.length, commands: snapshot.commands.length }));
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error(error.message);
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# i3 Composer Agent v{{RELEASE_VERSION}}
|
|
2
|
+
|
|
3
|
+
**Based on agent-skills v{{UPSTREAM_VERSION}}**
|
|
4
|
+
|
|
5
|
+
**Intelligence. Integration. Innovation.**
|
|
6
|
+
|
|
7
|
+
Engineering skills, lifecycle commands, and specialist personas for AI coding agents.
|
|
8
|
+
Based on [Agent Skills](https://github.com/addyosmani/agent-skills) by Addy Osmani
|
|
9
|
+
(MIT). See [NOTICE.md](NOTICE.md) for attribution.
|
|
10
|
+
|
|
11
|
+
The npm package is `@givolt/i3-composer-agent`; the plugin and marketplace ID is
|
|
12
|
+
`i3-composer-agent`. Skill IDs retain their upstream names.
|
|
13
|
+
|
|
14
|
+
## Prepare the complete plugin
|
|
15
|
+
|
|
16
|
+
Run in the project where you want to prepare the plugin:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx @givolt/i3-composer-agent --codebuddy
|
|
20
|
+
# Other hosts:
|
|
21
|
+
npx @givolt/i3-composer-agent --codex
|
|
22
|
+
npx @givolt/i3-composer-agent --claude
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The command prepares a persistent `./i3-composer-agent/` directory containing
|
|
26
|
+
{{SKILL_COUNT}} skills, {{COMMAND_COUNT}} commands, {{PERSONA_COUNT}} personas,
|
|
27
|
+
and shared references. It prints native registration commands; run those in
|
|
28
|
+
the selected host to enable the plugin. Preparing files does not select a host
|
|
29
|
+
installation scope. See [CodeBuddy setup](docs/codebuddy-setup.md) and
|
|
30
|
+
[Codex setup](docs/codex-setup.md).
|
|
31
|
+
|
|
32
|
+
CodeBuddy / Claude Code:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
/plugin marketplace add ./i3-composer-agent
|
|
36
|
+
/plugin install i3-composer-agent@i3-composer-agent
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Codex:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
codex plugin marketplace add ./i3-composer-agent
|
|
43
|
+
codex plugin add i3-composer-agent@i3-composer-agent
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Start a new session after enabling the plugin. Codex uses the existing Skill
|
|
47
|
+
names, such as `$spec-driven-development`; lifecycle slash-command adaptation
|
|
48
|
+
is maintained separately.
|
|
49
|
+
|
|
50
|
+
## Commands
|
|
51
|
+
|
|
52
|
+
Hosts supporting Claude-compatible plugin commands load these workflows:
|
|
53
|
+
|
|
54
|
+
| Command | Description |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
{{COMMAND_TABLE}}
|
|
57
|
+
|
|
58
|
+
`/i3-composer-agent:build auto` runs the existing approved-plan workflow.
|
|
59
|
+
|
|
60
|
+
## Skills
|
|
61
|
+
|
|
62
|
+
| Skill | Description |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
{{SKILL_TABLE}}
|
|
65
|
+
|
|
66
|
+
## Bundle options
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx @givolt/i3-composer-agent --extract --dir /path/to/plugin
|
|
70
|
+
npx @givolt/i3-composer-agent --codebuddy --dir /path/to/plugin --dry-run
|
|
71
|
+
npx @givolt/i3-composer-agent --codebuddy --dir /path/to/plugin --force
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Replacement preserves the previous i3 bundle as a backup. Repository-scoped
|
|
75
|
+
AGENTS.md and CLAUDE.md are not copied into business projects.
|
|
76
|
+
|
|
77
|
+
## Maintaining this fork
|
|
78
|
+
|
|
79
|
+
This repository is independent of the GSD-based i3 Composer Work repository.
|
|
80
|
+
`agent/source` retains upstream source and the `rebrand/` rules; `main` contains
|
|
81
|
+
the validated branded distribution. Create release candidates from the source
|
|
82
|
+
branch, replay branding, validate, then update the release branch.
|
|
83
|
+
|
|
84
|
+
See [maintenance](rebrand/MAINTENANCE.md), [source PR workflow](rebrand/PR-WORKFLOW.md),
|
|
85
|
+
[versioning](rebrand/VERSIONING.md), [changelog](CHANGELOG.md),
|
|
86
|
+
and [validation contract](rebrand/VALIDATION.md).
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Using i3 Composer Agent with CodeBuddy
|
|
2
|
+
|
|
3
|
+
Prepare the complete plugin inside the project where you want to use it:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx @givolt/i3-composer-agent --codebuddy
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
In the CodeBuddy chat, register the prepared directory and install the plugin:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
/plugin marketplace add ./i3-composer-agent
|
|
13
|
+
/plugin install i3-composer-agent@i3-composer-agent
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
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
|
|
18
|
+
plugin manager. Reload plugins or start a new session after installation.
|
|
19
|
+
|
|
20
|
+
CodeBuddy supports the existing `.claude-plugin/plugin.json` compatibility
|
|
21
|
+
manifest. It declares `.claude/commands/` and `commands/`, preserving the nine
|
|
22
|
+
original lifecycle commands and their workflow content.
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
/i3-composer-agent:spec
|
|
26
|
+
/i3-composer-agent:plan
|
|
27
|
+
/i3-composer-agent:build
|
|
28
|
+
/i3-composer-agent:build auto
|
|
29
|
+
/i3-composer-agent:review
|
|
30
|
+
/i3-composer-agent:ship
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Skill IDs, persona names, artifact paths, and workflow gates retain their
|
|
34
|
+
upstream behavior. The plugin namespace changes to `i3-composer-agent`.
|
|
35
|
+
|
|
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.
|
|
39
|
+
|
|
40
|
+
The CLI prepares files and prints registration commands. Actual host discovery
|
|
41
|
+
and model execution should be verified after registering the plugin.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Using i3 Composer Agent with Codex
|
|
2
|
+
|
|
3
|
+
Prepare the complete plugin inside the project where you want to use it:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx @givolt/i3-composer-agent --codex
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The CLI copies a persistent `./i3-composer-agent/` bundle and prints these
|
|
10
|
+
native registration commands. Run them to enable the plugin:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
codex plugin marketplace add ./i3-composer-agent
|
|
14
|
+
codex plugin add i3-composer-agent@i3-composer-agent
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Start a new Codex session after registration. All 25 existing skills keep their
|
|
18
|
+
original IDs. In CLI/IDE, use `/skills` or `$spec-driven-development`; in chat,
|
|
19
|
+
explicitly request the relevant skill from i3 Composer Agent.
|
|
20
|
+
|
|
21
|
+
The adapter in `.codex-plugin/plugin.json` points at the unchanged `skills/`
|
|
22
|
+
layout. Shared checklists remain under `references/`, so skill-relative links
|
|
23
|
+
resolve inside the complete plugin bundle.
|
|
24
|
+
|
|
25
|
+
The short lifecycle commands (`/spec`, `/plan`, `/build`, `/ship`) remain
|
|
26
|
+
available in hosts that load the Claude-compatible command manifest. Codex
|
|
27
|
+
lifecycle-command adaptation is separate from this brand conversion. Native
|
|
28
|
+
Codex `/plan` switches planning mode; it is not the plugin's planning wrapper.
|
|
29
|
+
|
|
30
|
+
Do not preload `using-agent-skills/SKILL.md` into always-on context: Codex routes
|
|
31
|
+
skills natively. The meta-skill can remain installed and explicitly invoked.
|
|
32
|
+
|
|
33
|
+
For local source testing, replace `./i3-composer-agent` above with the absolute
|
|
34
|
+
path to the branded checkout. The CLI does not modify existing plugin settings.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const os = require('node:os');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
const { spawnSync } = require('node:child_process');
|
|
9
|
+
const root = path.resolve(__dirname, '../..');
|
|
10
|
+
const baselineVersion = require('../upstream.json').upstreamVersion;
|
|
11
|
+
|
|
12
|
+
function run(directory, executable, args) {
|
|
13
|
+
return spawnSync(executable, args, { cwd: directory, encoding: 'utf8' });
|
|
14
|
+
}
|
|
15
|
+
function git(directory, args) {
|
|
16
|
+
const result = run(directory, 'git', args);
|
|
17
|
+
assert.equal(result.status, 0, result.stderr);
|
|
18
|
+
return result.stdout.trim();
|
|
19
|
+
}
|
|
20
|
+
function node(directory, file, args = []) { return run(directory, process.execPath, [file, ...args]); }
|
|
21
|
+
function fixture(t, branch = 'release/test-candidate') {
|
|
22
|
+
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'i3-agent-replay-'));
|
|
23
|
+
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
|
|
24
|
+
for (const name of ['skills', 'references', 'agents', 'commands', '.claude', '.gemini', '.claude-plugin', '.codex-plugin', '.agents', 'hooks', 'docs', 'scripts', 'evals', 'rebrand', 'LICENSE', 'plugin.json', 'README.md']) {
|
|
25
|
+
fs.cpSync(path.join(root, name), path.join(directory, name), { recursive: true, filter: file => !file.includes(`${path.sep}results${path.sep}`) });
|
|
26
|
+
}
|
|
27
|
+
// Model an upstream source checkout, including when this suite runs in a packed branded bundle.
|
|
28
|
+
for (const name of ['plugin.json', '.claude-plugin/plugin.json', '.codex-plugin/plugin.json']) {
|
|
29
|
+
const file = path.join(directory, name);
|
|
30
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
31
|
+
data.name = 'agent-skills';
|
|
32
|
+
data.version = baselineVersion;
|
|
33
|
+
fs.writeFileSync(file, JSON.stringify(data, null, 2) + '\n');
|
|
34
|
+
}
|
|
35
|
+
git(directory, ['init', '--initial-branch', branch]);
|
|
36
|
+
git(directory, ['config', 'user.name', 'Pipeline Test']);
|
|
37
|
+
git(directory, ['config', 'user.email', 'pipeline@example.test']);
|
|
38
|
+
git(directory, ['add', '.']);
|
|
39
|
+
git(directory, ['commit', '-m', 'upstream fixture']);
|
|
40
|
+
return directory;
|
|
41
|
+
}
|
|
42
|
+
function record(directory) {
|
|
43
|
+
const result = node(directory, 'rebrand/record-upstream.cjs', ['HEAD']);
|
|
44
|
+
assert.equal(result.status, 0, result.stderr);
|
|
45
|
+
}
|
|
46
|
+
function apply(directory) {
|
|
47
|
+
const result = node(directory, 'rebrand/apply.cjs', ['--apply']);
|
|
48
|
+
assert.equal(result.status, 0, result.stderr);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
test('a source checkout generates a complete, repeatable branded distribution', t => {
|
|
52
|
+
const directory = fixture(t);
|
|
53
|
+
record(directory);
|
|
54
|
+
apply(directory);
|
|
55
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(directory, 'package.json'), 'utf8'));
|
|
56
|
+
assert.equal(pkg.name, '@givolt/i3-composer-agent');
|
|
57
|
+
const brand = JSON.parse(fs.readFileSync(path.join(directory, 'rebrand/brand.json'), 'utf8'));
|
|
58
|
+
assert.equal(pkg.version, brand.releaseVersion);
|
|
59
|
+
assert.equal(fs.existsSync(path.join(directory, 'bin/i3-composer-agent.cjs')), true);
|
|
60
|
+
assert.equal(node(directory, 'scripts/validate-brand.cjs').status, 0);
|
|
61
|
+
const check = node(directory, 'rebrand/apply.cjs', ['--check']);
|
|
62
|
+
assert.equal(check.status, 0, check.stdout + check.stderr);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('brand fixes advance independently while preserving upstream provenance', t => {
|
|
66
|
+
const directory = fixture(t);
|
|
67
|
+
record(directory);
|
|
68
|
+
const upstreamFile = path.join(directory, 'rebrand/upstream.json');
|
|
69
|
+
const originalUpstream = fs.readFileSync(upstreamFile, 'utf8');
|
|
70
|
+
const upstream = JSON.parse(originalUpstream);
|
|
71
|
+
const brandFile = path.join(directory, 'rebrand/brand.json');
|
|
72
|
+
const brand = JSON.parse(fs.readFileSync(brandFile, 'utf8'));
|
|
73
|
+
const releaseVersion = `${Number(upstream.upstreamVersion.split('.')[0]) + 1}.0.1`;
|
|
74
|
+
brand.releaseVersion = releaseVersion;
|
|
75
|
+
fs.writeFileSync(brandFile, JSON.stringify(brand, null, 2) + '\n');
|
|
76
|
+
apply(directory);
|
|
77
|
+
const packageFile = path.join(directory, 'package.json');
|
|
78
|
+
const originalPackage = fs.readFileSync(packageFile, 'utf8');
|
|
79
|
+
const pkg = JSON.parse(originalPackage);
|
|
80
|
+
assert.equal(pkg.version, releaseVersion);
|
|
81
|
+
assert.equal(pkg.i3ComposerAgent.upstreamVersion, upstream.upstreamVersion);
|
|
82
|
+
assert.equal(pkg.i3ComposerAgent.upstreamCommit, upstream.upstreamCommit);
|
|
83
|
+
assert.equal(fs.readFileSync(upstreamFile, 'utf8'), originalUpstream);
|
|
84
|
+
const releaseLabel = `i3 Composer Agent v${releaseVersion}`;
|
|
85
|
+
const upstreamLabel = `Based on agent-skills v${upstream.upstreamVersion}`;
|
|
86
|
+
for (const output of [fs.readFileSync(path.join(directory, 'README.md'), 'utf8'), node(directory, 'bin/i3-composer-agent.cjs', ['--help']).stdout]) {
|
|
87
|
+
assert.ok(output.includes(releaseLabel), output);
|
|
88
|
+
assert.ok(output.includes(upstreamLabel), output);
|
|
89
|
+
}
|
|
90
|
+
assert.equal(node(directory, 'bin/i3-composer-agent.cjs', ['--version']).stdout.trim(), releaseVersion);
|
|
91
|
+
const dryRun = node(directory, 'bin/i3-composer-agent.cjs', ['--extract', '--dry-run', '--dir', directory + '-prepared']);
|
|
92
|
+
assert.equal(dryRun.status, 0, dryRun.stderr);
|
|
93
|
+
assert.equal(JSON.parse(dryRun.stdout).upstreamVersion, upstream.upstreamVersion);
|
|
94
|
+
assert.equal(node(directory, 'scripts/validate-brand.cjs').status, 0);
|
|
95
|
+
pkg.i3ComposerAgent.upstreamVersion = releaseVersion;
|
|
96
|
+
fs.writeFileSync(packageFile, JSON.stringify(pkg));
|
|
97
|
+
assert.notEqual(node(directory, 'scripts/validate-brand.cjs').status, 0);
|
|
98
|
+
fs.writeFileSync(packageFile, originalPackage);
|
|
99
|
+
assert.equal(node(directory, 'rebrand/apply.cjs', ['--check']).status, 0);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('writing a brand transform on the protected source branch changes nothing', t => {
|
|
103
|
+
const directory = fixture(t, 'agent/source');
|
|
104
|
+
const before = fs.readFileSync(path.join(directory, 'plugin.json'));
|
|
105
|
+
const result = node(directory, 'rebrand/apply.cjs', ['--apply']);
|
|
106
|
+
assert.notEqual(result.status, 0);
|
|
107
|
+
assert.match(result.stderr, /source branch/i);
|
|
108
|
+
assert.deepEqual(fs.readFileSync(path.join(directory, 'plugin.json')), before);
|
|
109
|
+
assert.equal(fs.existsSync(path.join(directory, 'package.json')), false);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('a reviewed upstream catalog can grow without losing its new skill or command', t => {
|
|
113
|
+
const directory = fixture(t);
|
|
114
|
+
const skill = path.join(directory, 'skills/upstream-new-skill');
|
|
115
|
+
fs.mkdirSync(skill);
|
|
116
|
+
fs.writeFileSync(path.join(skill, 'SKILL.md'), '---\nname: upstream-new-skill\ndescription: New upstream capability.\n---\n# Upstream capability\n');
|
|
117
|
+
fs.writeFileSync(path.join(directory, '.claude/commands/upstream-new-command.md'), '---\ndescription: Invoke the new upstream capability.\n---\nUse agent-skills:upstream-new-skill.\n');
|
|
118
|
+
git(directory, ['add', 'skills', '.claude/commands']);
|
|
119
|
+
git(directory, ['commit', '-m', 'new upstream capability']);
|
|
120
|
+
record(directory);
|
|
121
|
+
apply(directory);
|
|
122
|
+
const result = node(directory, 'scripts/validate-brand.cjs');
|
|
123
|
+
assert.equal(result.status, 0, result.stderr);
|
|
124
|
+
const description = JSON.parse(fs.readFileSync(path.join(directory, '.codex-plugin/plugin.json'), 'utf8')).interface.longDescription;
|
|
125
|
+
assert.match(description, /26 engineering skills/);
|
|
126
|
+
const readme = fs.readFileSync(path.join(directory, 'README.md'), 'utf8');
|
|
127
|
+
assert.match(readme, /upstream-new-skill/);
|
|
128
|
+
assert.match(readme, /upstream-new-command/);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('missing files and altered skill identifiers fail preservation validation', t => {
|
|
132
|
+
const directory = fixture(t);
|
|
133
|
+
record(directory);
|
|
134
|
+
apply(directory);
|
|
135
|
+
const file = path.join(directory, 'skills/using-agent-skills/SKILL.md');
|
|
136
|
+
const original = fs.readFileSync(file, 'utf8');
|
|
137
|
+
fs.writeFileSync(file, original.replace('name: using-agent-skills', 'name: renamed-meta-skill'));
|
|
138
|
+
assert.notEqual(node(directory, 'scripts/validate-brand.cjs').status, 0);
|
|
139
|
+
fs.writeFileSync(file, original);
|
|
140
|
+
fs.rmSync(file);
|
|
141
|
+
assert.notEqual(node(directory, 'scripts/validate-brand.cjs').status, 0);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('checkout line endings do not masquerade as an upstream skill change', t => {
|
|
145
|
+
const directory = fixture(t);
|
|
146
|
+
record(directory);
|
|
147
|
+
const file = path.join(directory, 'skills/using-agent-skills/SKILL.md');
|
|
148
|
+
fs.writeFileSync(file, fs.readFileSync(file, 'utf8').replace(/\r?\n/g, '\r\n'));
|
|
149
|
+
const script = path.join(directory, 'scripts/validate-versions.js');
|
|
150
|
+
fs.writeFileSync(script, fs.readFileSync(script, 'utf8').replace(/\r?\n/g, '\r\n'));
|
|
151
|
+
apply(directory);
|
|
152
|
+
assert.equal(node(directory, 'scripts/validate-brand.cjs').status, 0);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('a new upstream package manifest requires an explicit distribution adaptation', t => {
|
|
156
|
+
const directory = fixture(t);
|
|
157
|
+
record(directory);
|
|
158
|
+
fs.writeFileSync(path.join(directory, 'package.json'), '{"name":"agent-skills","dependencies":{"upstream-runtime":"1.0.0"}}\n');
|
|
159
|
+
const before = fs.readFileSync(path.join(directory, 'plugin.json'));
|
|
160
|
+
const result = node(directory, 'rebrand/apply.cjs', ['--apply']);
|
|
161
|
+
assert.notEqual(result.status, 0);
|
|
162
|
+
assert.match(result.stderr, /package\.json/);
|
|
163
|
+
assert.deepEqual(fs.readFileSync(path.join(directory, 'plugin.json')), before);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('a distribution without repository CI files remains unchanged by a replay check', t => {
|
|
167
|
+
const directory = fixture(t);
|
|
168
|
+
record(directory);
|
|
169
|
+
apply(directory);
|
|
170
|
+
fs.rmSync(path.join(directory, '.github'), { recursive: true, force: true });
|
|
171
|
+
const result = node(directory, 'rebrand/apply.cjs', ['--check']);
|
|
172
|
+
assert.equal(result.status, 0, result.stdout + result.stderr);
|
|
173
|
+
assert.equal(fs.existsSync(path.join(directory, '.github')), false);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test('Finder metadata does not masquerade as an upstream runtime file', t => {
|
|
177
|
+
const directory = fixture(t);
|
|
178
|
+
record(directory);
|
|
179
|
+
fs.writeFileSync(path.join(directory, 'skills/.DS_Store'), 'Finder metadata');
|
|
180
|
+
fs.writeFileSync(path.join(directory, 'skills/spec-driven-development/.DS_Store'), 'Finder metadata');
|
|
181
|
+
apply(directory);
|
|
182
|
+
assert.equal(node(directory, 'scripts/validate-brand.cjs').status, 0);
|
|
183
|
+
const result = node(directory, 'rebrand/apply.cjs', ['--check']);
|
|
184
|
+
assert.equal(result.status, 0, result.stdout + result.stderr);
|
|
185
|
+
});
|