@givolt/i3-composer-agent 1.0.1 → 2.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 +1 -1
- package/.claude/commands/{build.md → i3-build.md} +10 -10
- package/.claude/commands/{code-simplify.md → i3-code-simplify.md} +2 -2
- package/.claude/commands/{constraints.md → i3-constraints.md} +4 -4
- package/.claude/commands/{plan.md → i3-plan.md} +1 -1
- package/.claude/commands/{review.md → i3-review.md} +3 -3
- package/.claude/commands/{ship.md → i3-ship.md} +4 -4
- package/.claude/commands/{spec.md → i3-spec.md} +1 -1
- package/.claude/commands/{test.md → i3-test.md} +2 -2
- package/.claude/commands/{webperf.md → i3-webperf.md} +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.gemini/commands/{build.toml → i3-build.toml} +10 -10
- package/.gemini/commands/{code-simplify.toml → i3-code-simplify.toml} +2 -2
- package/.gemini/commands/{constraints.toml → i3-constraints.toml} +4 -4
- package/.gemini/commands/{planning.toml → i3-planning.toml} +1 -1
- package/{commands/review.toml → .gemini/commands/i3-review.toml} +3 -3
- package/.gemini/commands/{ship.toml → i3-ship.toml} +4 -4
- package/.gemini/commands/{spec.toml → i3-spec.toml} +1 -1
- package/{commands/test.toml → .gemini/commands/i3-test.toml} +2 -2
- package/.gemini/commands/{webperf.toml → i3-webperf.toml} +1 -1
- package/CHANGELOG.md +17 -0
- package/NOTICE.md +2 -1
- package/README.md +47 -41
- package/agents/code-reviewer.md +2 -2
- package/agents/security-auditor.md +1 -1
- package/agents/test-engineer.md +1 -1
- package/agents/web-performance-auditor.md +3 -3
- package/bin/i3-composer-agent.cjs +2 -2
- package/bin/install.cjs +20 -4
- package/commands/{build.toml → i3-build.toml} +10 -10
- package/commands/{code-simplify.toml → i3-code-simplify.toml} +2 -2
- package/commands/{constraints.toml → i3-constraints.toml} +4 -4
- package/commands/{planning.toml → i3-planning.toml} +1 -1
- package/{.gemini/commands/review.toml → commands/i3-review.toml} +3 -3
- package/commands/{ship.toml → i3-ship.toml} +4 -4
- package/commands/{spec.toml → i3-spec.toml} +1 -1
- package/{.gemini/commands/test.toml → commands/i3-test.toml} +2 -2
- package/commands/{webperf.toml → i3-webperf.toml} +1 -1
- package/docs/adoption-guide.md +35 -35
- package/docs/agents.md +12 -12
- package/docs/antigravity-setup.md +14 -14
- package/docs/codebuddy-setup.md +16 -15
- package/docs/codex-setup.md +6 -6
- package/docs/commandcode-setup.md +5 -5
- package/docs/copilot-cli-setup.md +5 -5
- package/docs/copilot-setup.md +22 -22
- package/docs/cursor-setup.md +20 -20
- package/docs/developer-onboarding.md +5 -5
- package/docs/gemini-cli-setup.md +26 -26
- package/docs/getting-started.md +36 -36
- package/docs/opencode-setup.md +27 -27
- package/docs/skill-anatomy.md +3 -3
- package/docs/windsurf-setup.md +7 -7
- package/evals/cases/{api-and-interface-design.json → i3-api-and-interface-design.json} +4 -4
- package/evals/cases/{browser-testing-with-devtools.json → i3-browser-testing-with-devtools.json} +4 -4
- package/evals/cases/{ci-cd-and-automation.json → i3-ci-cd-and-automation.json} +4 -4
- package/evals/cases/{code-review-and-quality.json → i3-code-review-and-quality.json} +4 -4
- package/evals/cases/{code-simplification.json → i3-code-simplification.json} +4 -4
- package/evals/cases/{constraint-driven-development.json → i3-constraint-driven-development.json} +4 -4
- package/evals/cases/{context-engineering.json → i3-context-engineering.json} +4 -4
- package/evals/cases/{debugging-and-error-recovery.json → i3-debugging-and-error-recovery.json} +5 -5
- package/evals/cases/{deprecation-and-migration.json → i3-deprecation-and-migration.json} +4 -4
- package/evals/cases/{documentation-and-adrs.json → i3-documentation-and-adrs.json} +4 -4
- package/evals/cases/{doubt-driven-development.json → i3-doubt-driven-development.json} +3 -3
- package/evals/cases/{frontend-ui-engineering.json → i3-frontend-ui-engineering.json} +4 -4
- package/evals/cases/{git-workflow-and-versioning.json → i3-git-workflow-and-versioning.json} +3 -3
- package/evals/cases/{idea-refine.json → i3-idea-refine.json} +3 -3
- package/evals/cases/{incremental-implementation.json → i3-incremental-implementation.json} +4 -4
- package/evals/cases/{interview-me.json → i3-interview-me.json} +3 -3
- package/evals/cases/{observability-and-instrumentation.json → i3-observability-and-instrumentation.json} +3 -3
- package/evals/cases/{performance-optimization.json → i3-performance-optimization.json} +4 -4
- package/evals/cases/{planning-and-task-breakdown.json → i3-planning-and-task-breakdown.json} +4 -4
- package/evals/cases/{security-and-hardening.json → i3-security-and-hardening.json} +4 -4
- package/evals/cases/{shipping-and-launch.json → i3-shipping-and-launch.json} +4 -4
- package/evals/cases/{source-driven-development.json → i3-source-driven-development.json} +4 -4
- package/evals/cases/{spec-driven-development.json → i3-spec-driven-development.json} +5 -5
- package/evals/cases/{test-driven-development.json → i3-test-driven-development.json} +5 -5
- package/evals/cases/{using-agent-skills.json → i3-using-agent-skills.json} +4 -4
- package/evals/fixtures/{git-workflow-and-versioning → i3-git-workflow-and-versioning}/.eval/working-tree.patch +3 -3
- package/evals/plugin/code-review-fires/graders/skill-fired.md +1 -1
- package/evals/plugin/code-review-stays-quiet/graders/not-fired.md +1 -1
- package/evals/plugin/code-review-stays-quiet/graders/tdd-fired.md +1 -1
- package/evals/plugin/code-review-stays-quiet/prompt.md +2 -2
- package/evals/plugin/code-review-stays-quiet-on-commit-message/graders/not-fired.md +1 -1
- package/evals/plugin/code-review-stays-quiet-on-commit-message/prompt.md +2 -2
- package/hooks/SDD-CACHE.md +3 -3
- package/hooks/SIMPLIFY-IGNORE.md +2 -2
- package/hooks/session-start-test.sh +1 -1
- package/hooks/session-start.sh +3 -3
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/rebrand/INSTALLATION-SPEC.md +2 -1
- package/rebrand/MAINTENANCE.md +3 -2
- package/rebrand/NOTICE.md +2 -1
- package/rebrand/PREFIX-SPEC.md +16 -0
- package/rebrand/UNIFICATION-SPEC.md +2 -2
- package/rebrand/VALIDATION.md +2 -2
- package/rebrand/VERSIONING.md +5 -3
- package/rebrand/apply.cjs +33 -10
- package/rebrand/brand.json +1 -1
- package/rebrand/catalog.cjs +16 -2
- package/rebrand/fork-patches.json +15 -0
- package/rebrand/identifiers.cjs +41 -0
- package/rebrand/overlay/CHANGELOG.md +17 -0
- package/rebrand/overlay/bin/i3-composer-agent.cjs +2 -2
- package/rebrand/overlay/bin/install.cjs +20 -4
- package/rebrand/overlay/scripts/validate-brand.cjs +3 -3
- package/rebrand/overlay/tests/installer.test.cjs +60 -12
- package/rebrand/templates/README.md +11 -5
- package/rebrand/templates/codebuddy-setup.md +16 -15
- package/rebrand/templates/codex-setup.md +6 -6
- package/rebrand/tests/pipeline.test.cjs +71 -3
- package/references/accessibility-checklist.md +1 -1
- package/references/definition-of-done.md +7 -7
- package/references/observability-checklist.md +3 -3
- package/references/orchestration-patterns.md +17 -17
- package/references/performance-checklist.md +2 -2
- package/references/security-checklist.md +1 -1
- package/references/testing-patterns.md +1 -1
- package/scripts/floor-guard-reference-test.js +2 -2
- package/scripts/lib/skill-lint.js +2 -0
- package/scripts/run-evals-test.js +2 -2
- package/scripts/validate-artifact-paths-test.js +3 -3
- package/scripts/validate-artifact-paths.js +4 -2
- package/scripts/validate-brand.cjs +3 -3
- package/scripts/validate-commands.js +1 -0
- package/scripts/validate-reference-links-test.js +11 -11
- package/scripts/validate-reference-links.js +1 -1
- package/skills/{api-and-interface-design → i3-api-and-interface-design}/SKILL.md +2 -2
- package/skills/{browser-testing-with-devtools → i3-browser-testing-with-devtools}/SKILL.md +1 -1
- package/skills/{ci-cd-and-automation → i3-ci-cd-and-automation}/SKILL.md +2 -2
- package/skills/{code-review-and-quality → i3-code-review-and-quality}/SKILL.md +5 -5
- package/skills/{code-simplification → i3-code-simplification}/SKILL.md +1 -1
- package/skills/{constraint-driven-development → i3-constraint-driven-development}/SKILL.md +15 -15
- package/skills/{context-engineering → i3-context-engineering}/SKILL.md +1 -1
- package/skills/{debugging-and-error-recovery → i3-debugging-and-error-recovery}/SKILL.md +2 -2
- package/skills/{deprecation-and-migration → i3-deprecation-and-migration}/SKILL.md +2 -2
- package/skills/{documentation-and-adrs → i3-documentation-and-adrs}/SKILL.md +1 -1
- package/skills/{doubt-driven-development → i3-doubt-driven-development}/SKILL.md +8 -8
- package/skills/{frontend-ui-engineering → i3-frontend-ui-engineering}/SKILL.md +1 -1
- package/skills/{git-workflow-and-versioning → i3-git-workflow-and-versioning}/SKILL.md +4 -4
- package/skills/{idea-refine → i3-idea-refine}/SKILL.md +2 -2
- package/skills/{idea-refine → i3-idea-refine}/scripts/idea-refine.sh +1 -1
- package/skills/{incremental-implementation → i3-incremental-implementation}/SKILL.md +3 -3
- package/skills/{interview-me → i3-interview-me}/SKILL.md +12 -12
- package/skills/{observability-and-instrumentation → i3-observability-and-instrumentation}/SKILL.md +5 -5
- package/skills/{performance-optimization → i3-performance-optimization}/SKILL.md +1 -1
- package/skills/{performance-optimization → i3-performance-optimization}/references/optimization-patterns.md +1 -1
- package/skills/{planning-and-task-breakdown → i3-planning-and-task-breakdown}/SKILL.md +2 -2
- package/skills/{security-and-hardening → i3-security-and-hardening}/SKILL.md +3 -3
- package/skills/{security-and-hardening → i3-security-and-hardening}/references/hardening-patterns.md +1 -1
- package/skills/{shipping-and-launch → i3-shipping-and-launch}/SKILL.md +2 -2
- package/skills/{source-driven-development → i3-source-driven-development}/SKILL.md +2 -2
- package/skills/{spec-driven-development → i3-spec-driven-development}/SKILL.md +7 -7
- package/skills/{test-driven-development → i3-test-driven-development}/SKILL.md +2 -2
- package/skills/{using-agent-skills → i3-using-agent-skills}/SKILL.md +67 -67
- package/tests/installer.test.cjs +60 -12
- /package/evals/fixtures/{api-and-interface-design → i3-api-and-interface-design}/service-brief.md +0 -0
- /package/evals/fixtures/{browser-testing-with-devtools → i3-browser-testing-with-devtools}/README.md +0 -0
- /package/evals/fixtures/{browser-testing-with-devtools → i3-browser-testing-with-devtools}/index.html +0 -0
- /package/evals/fixtures/{browser-testing-with-devtools → i3-browser-testing-with-devtools}/server.js +0 -0
- /package/evals/fixtures/{ci-cd-and-automation → i3-ci-cd-and-automation}/package.json +0 -0
- /package/evals/fixtures/{ci-cd-and-automation → i3-ci-cd-and-automation}/src/slug.js +0 -0
- /package/evals/fixtures/{ci-cd-and-automation → i3-ci-cd-and-automation}/test/slug.test.js +0 -0
- /package/evals/fixtures/{code-review-and-quality → i3-code-review-and-quality}/user-search.diff +0 -0
- /package/evals/fixtures/{code-simplification → i3-code-simplification}/config-parser.js +0 -0
- /package/evals/fixtures/{code-simplification → i3-code-simplification}/config-parser.test.js +0 -0
- /package/evals/fixtures/{context-engineering → i3-context-engineering}/context-audit.md +0 -0
- /package/evals/fixtures/{debugging-and-error-recovery → i3-debugging-and-error-recovery}/pagination.js +0 -0
- /package/evals/fixtures/{debugging-and-error-recovery → i3-debugging-and-error-recovery}/pagination.test.js +0 -0
- /package/evals/fixtures/{debugging-and-error-recovery → i3-debugging-and-error-recovery}/time-pressure.md +0 -0
- /package/evals/fixtures/{deprecation-and-migration → i3-deprecation-and-migration}/api-inventory.md +0 -0
- /package/evals/fixtures/{documentation-and-adrs → i3-documentation-and-adrs}/decision-context.md +0 -0
- /package/evals/fixtures/{doubt-driven-development → i3-doubt-driven-development}/migration-plan.md +0 -0
- /package/evals/fixtures/{frontend-ui-engineering → i3-frontend-ui-engineering}/Button.tsx +0 -0
- /package/evals/fixtures/{frontend-ui-engineering → i3-frontend-ui-engineering}/design-system.md +0 -0
- /package/evals/fixtures/{git-workflow-and-versioning → i3-git-workflow-and-versioning}/app.js +0 -0
- /package/evals/fixtures/{git-workflow-and-versioning → i3-git-workflow-and-versioning}/app.test.js +0 -0
- /package/evals/fixtures/{incremental-implementation → i3-incremental-implementation}/reports.js +0 -0
- /package/evals/fixtures/{incremental-implementation → i3-incremental-implementation}/reports.test.js +0 -0
- /package/evals/fixtures/{incremental-implementation → i3-incremental-implementation}/tasks/plan.md +0 -0
- /package/evals/fixtures/{observability-and-instrumentation → i3-observability-and-instrumentation}/operations.md +0 -0
- /package/evals/fixtures/{observability-and-instrumentation → i3-observability-and-instrumentation}/payment-retry.js +0 -0
- /package/evals/fixtures/{performance-optimization → i3-performance-optimization}/benchmark.js +0 -0
- /package/evals/fixtures/{performance-optimization → i3-performance-optimization}/products.js +0 -0
- /package/evals/fixtures/{planning-and-task-breakdown → i3-planning-and-task-breakdown}/notifications-spec.md +0 -0
- /package/evals/fixtures/{security-and-hardening → i3-security-and-hardening}/webhook.js +0 -0
- /package/evals/fixtures/{security-and-hardening → i3-security-and-hardening}/webhook.test.js +0 -0
- /package/evals/fixtures/{shipping-and-launch → i3-shipping-and-launch}/authority-pressure.md +0 -0
- /package/evals/fixtures/{shipping-and-launch → i3-shipping-and-launch}/launch-status.md +0 -0
- /package/evals/fixtures/{source-driven-development → i3-source-driven-development}/framework-task.md +0 -0
- /package/evals/fixtures/{spec-driven-development → i3-spec-driven-development}/billing-brief.md +0 -0
- /package/evals/fixtures/{test-driven-development → i3-test-driven-development}/BUG.md +0 -0
- /package/evals/fixtures/{test-driven-development → i3-test-driven-development}/README.md +0 -0
- /package/evals/fixtures/{test-driven-development → i3-test-driven-development}/package.json +0 -0
- /package/evals/fixtures/{test-driven-development → i3-test-driven-development}/src/split.js +0 -0
- /package/evals/fixtures/{test-driven-development → i3-test-driven-development}/test/split.test.js +0 -0
- /package/evals/fixtures/{using-agent-skills → i3-using-agent-skills}/incident.md +0 -0
- /package/skills/{constraint-driven-development → i3-constraint-driven-development}/references/floor-guard.md +0 -0
- /package/skills/{idea-refine → i3-idea-refine}/examples.md +0 -0
- /package/skills/{idea-refine → i3-idea-refine}/frameworks.md +0 -0
- /package/skills/{idea-refine → i3-idea-refine}/refinement-criteria.md +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# i3 command and Skill identifiers
|
|
2
|
+
|
|
3
|
+
The user approved applying the existing i3 naming convention on 2026-10-07.
|
|
4
|
+
This supersedes the earlier preservation of unprefixed distribution identifiers.
|
|
5
|
+
|
|
6
|
+
- `agent/source` keeps upstream files and identifiers unchanged. Changes live in `rebrand/`.
|
|
7
|
+
- Branded Skill directories and YAML `name` fields use `i3-<upstream-id>`.
|
|
8
|
+
- All three command directories use `i3-<upstream-stem>` filenames, retaining the existing `plan`/`planning` host mapping.
|
|
9
|
+
- Native commands include `/i3-spec` and `/i3-build`; plugin commands include `/i3-composer-agent:i3-spec`.
|
|
10
|
+
- Rewrite local references, hooks, routing fixtures and documentation together. Preserve external upstream links, attribution, and Skill frontmatter other than `name`.
|
|
11
|
+
- Retain helper script filenames and executable permissions when their containing Skill directory is renamed.
|
|
12
|
+
- Replay and validation accept only a complete reviewed source catalog or its mapped branded catalog. Replaying branded output is idempotent.
|
|
13
|
+
- Installation migrates only legacy Skill/command paths listed in this package's prior installation manifest. Back up retired paths and support rollback. Unowned commands and skills remain in place.
|
|
14
|
+
- Verify source tests, branded validation/tests, routing/hooks, packed CLI installation, migration, rollback and the actual project installation. IDE discovery and model execution require separate host verification.
|
|
15
|
+
|
|
16
|
+
Release 2.0.0 applies this breaking identifier change. The user authorized publication on 2026-10-07. Publish and verify the reviewed new-version tarball; previously published tarballs remain immutable. The upstream baseline stays at 0.6.12.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
- `agent/source` 保留上游原始名称和源码,在 `rebrand/` 保存本项目的分发工具与转换规则。
|
|
9
9
|
- 候选从 `agent/source` 创建,在候选中重放;`main` 保存经过验证的品牌快照。
|
|
10
10
|
- GSD 仓库只读,不使用它的源码、分支或安装产物。
|
|
11
|
-
- Skill ID
|
|
11
|
+
- 源码保留上游 Skill ID;品牌版按 PREFIX-SPEC.md 使用 i3- 前缀。流程内容、Persona、参考文件和 MIT 来源署名保持。
|
|
12
12
|
- 本次完善源码同步和品牌重放;交互安装向导、Codex 命令适配及新的 npm 发布独立处理。
|
|
13
13
|
|
|
14
14
|
## 实现与命令
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
1. 从干净上游源码及本项目规则能生成完整品牌包,重复重放不产生差异。
|
|
25
25
|
2. 在 `agent/source` 上执行写入式重放必须失败且不修改源码。
|
|
26
26
|
3. 记录上游新增 Skill/Command 后能生成相应清单和说明;删失或改写已记录 Skill ID 时校验失败。
|
|
27
|
-
4.
|
|
27
|
+
4. 品牌版的 Skill name、目录和命令文件名统一使用 i3-;frontmatter 的其他字段、Persona、共享参考和流程契约保持。
|
|
28
28
|
5. 本地候选通过完整测试,并从打包结果运行 CLI 与校验。
|
|
29
29
|
6. 独立品牌仓库中的源/发行分支及远程关系正确;首次发布标签保持原来的 0.6.12 提交。
|
|
30
30
|
|
package/rebrand/VALIDATION.md
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
## 品牌候选
|
|
13
13
|
|
|
14
|
-
- Skill
|
|
15
|
-
- Skill frontmatter
|
|
14
|
+
- Skill 和命令路径按 PREFIX-SPEC.md 映射为 i3- 名称;其余已记录运行文件保持目录一致。
|
|
15
|
+
- Skill frontmatter 只改 name;逆向映射 name 后比较上游哈希,换行差异不作为字段变更。
|
|
16
16
|
- 新增上游 Skill/Command 能进入目录、品牌说明和打包;缺失内容会失败。
|
|
17
17
|
- 文字补丁失去匹配时必须失败,不能静默遗漏。
|
|
18
18
|
- 重放后 --check 成功,完整 Node、路由与 hook 检查通过。
|
package/rebrand/VERSIONING.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
| 信息 | 权威来源 | 当前发行值 |
|
|
6
6
|
| --- | --- | --- |
|
|
7
|
-
| i3 发行版本 | brand.json 的 releaseVersion |
|
|
7
|
+
| i3 发行版本 | brand.json 的 releaseVersion | 2.0.0 |
|
|
8
8
|
| 上游版本 | upstream.json 的 upstreamVersion | 0.6.12 |
|
|
9
9
|
| 上游提交 | upstream.json 的 upstreamCommit | 1401c8b8030e023baeebb31781a6653fe8e93026 |
|
|
10
10
|
|
|
@@ -20,14 +20,16 @@ package.json 的 i3ComposerAgent 来源字段。package.template.json 不保存
|
|
|
20
20
|
README、CLI 帮助和准备完成提示显示:
|
|
21
21
|
|
|
22
22
|
```text
|
|
23
|
-
i3 Composer Agent
|
|
23
|
+
i3 Composer Agent v2.0.0
|
|
24
24
|
Based on agent-skills v0.6.12
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
在上述例子中,--version 输出
|
|
27
|
+
在上述例子中,--version 输出 2.0.0,保留脚本使用的单一版本格式。--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
32
|
1.0.0 已发布。1.0.1 用于修复安装向导遗漏,版本与发布已由用户确认。
|
|
33
|
+
2.0.0 为命令和 Skill 的 i3- 前缀变更;使用者需要调整调用名,因此递增 major。
|
|
34
|
+
用户于 2026-10-07 要求修改并授权发布;上游版本和提交保持不变。
|
|
33
35
|
源码与打包验证不代表 npm 发布;发布仍按 MAINTENANCE.md 单独执行。
|
package/rebrand/apply.cjs
CHANGED
|
@@ -5,6 +5,7 @@ const fs = require('node:fs');
|
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
const { spawnSync } = require('node:child_process');
|
|
7
7
|
const { validateCatalog, frontmatter } = require('./catalog.cjs');
|
|
8
|
+
const { skillId, runtimePath, rewriteReferences } = require('./identifiers.cjs');
|
|
8
9
|
const brand = require('./brand.json');
|
|
9
10
|
const upstream = require('./upstream.json');
|
|
10
11
|
const root = path.resolve(__dirname, '..');
|
|
@@ -20,7 +21,24 @@ if (mode === '--apply') {
|
|
|
20
21
|
process.exit(1);
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
|
-
const
|
|
24
|
+
const branded = fs.existsSync(path.join(root, 'skills', skillId(upstream.skills[0])));
|
|
25
|
+
validateCatalog(root, upstream, { branded });
|
|
26
|
+
const inventory = upstream;
|
|
27
|
+
const renames = new Map();
|
|
28
|
+
if (!branded) {
|
|
29
|
+
for (const id of inventory.skills) renames.set(`skills/${id}`, `skills/${skillId(id)}`);
|
|
30
|
+
for (const file of inventory.runtimeFiles.filter(file => /^(?:\.claude\/commands|\.gemini\/commands|commands)\/[^/]+\.(?:md|toml)$/.test(file))) renames.set(file, runtimePath(file));
|
|
31
|
+
for (const target of renames.values()) if (fs.existsSync(path.join(root, target))) throw new Error(`Brand destination already exists: ${target}`);
|
|
32
|
+
}
|
|
33
|
+
for (const id of inventory.skills) {
|
|
34
|
+
for (const file of [`evals/cases/${id}.json`, `evals/fixtures/${id}`]) {
|
|
35
|
+
if (fs.existsSync(path.join(root, file))) {
|
|
36
|
+
const target = runtimePath(file);
|
|
37
|
+
if (fs.existsSync(path.join(root, target))) throw new Error(`Brand destination already exists: ${target}`);
|
|
38
|
+
renames.set(file, target);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
24
42
|
const packageFile = path.join(root, 'package.json');
|
|
25
43
|
if (fs.existsSync(packageFile) && JSON.parse(fs.readFileSync(packageFile, 'utf8')).name !== brand.npmName) {
|
|
26
44
|
console.error('Upstream package.json now exists; explicitly adapt the distribution overlay before replaying.');
|
|
@@ -30,6 +48,7 @@ const changes = new Map();
|
|
|
30
48
|
function update(relative, content) {
|
|
31
49
|
const file = path.join(root, relative);
|
|
32
50
|
if (!fs.existsSync(file) || fs.readFileSync(file, 'utf8') !== content) changes.set(relative, content);
|
|
51
|
+
else changes.delete(relative);
|
|
33
52
|
}
|
|
34
53
|
function transform(content, relative) {
|
|
35
54
|
const head = /^skills\/[^/]+\/SKILL\.md$/.test(relative) ? frontmatter(content) : '';
|
|
@@ -43,7 +62,7 @@ function transform(content, relative) {
|
|
|
43
62
|
.replaceAll('addy-agent-skills', brand.name)
|
|
44
63
|
.replaceAll('agent-skills@addy-agent-skills', `${brand.name}@${brand.name}`)
|
|
45
64
|
.replaceAll('agent-skills@agent-skills', `${brand.name}@${brand.name}`)
|
|
46
|
-
.
|
|
65
|
+
.replace(/(?<![\w-])agent-skills:/g, `${brand.name}:`)
|
|
47
66
|
.replaceAll('/path/to/agent-skills', `/path/to/${brand.name}`)
|
|
48
67
|
.replaceAll('./agent-skills', `./${brand.name}`)
|
|
49
68
|
.replace(/(?<![\w/.-])agent-skills(?![\w-])/g, brand.name)
|
|
@@ -74,7 +93,9 @@ function transform(content, relative) {
|
|
|
74
93
|
.replace(/\[[^\]]+\]\(https:\/\/github\.com\/addyosmani\/agent-skills\/issues\/(\d+)\)/g,
|
|
75
94
|
(_, issue) => `[upstream agent-skills #${issue}](https://github.com/addyosmani/agent-skills/issues/${issue})`);
|
|
76
95
|
}
|
|
77
|
-
|
|
96
|
+
const name = head.match(/^name:\s*([^\s]+)\s*$/m)?.[1];
|
|
97
|
+
const brandedHead = name && inventory.skills.includes(name) ? head.replace(/^name:.*$/m, `name: ${skillId(name)}`) : head;
|
|
98
|
+
return brandedHead + rewriteReferences(result, false, inventory, !relative.startsWith('scripts/'));
|
|
78
99
|
}
|
|
79
100
|
const skipDirectories = new Set(['.git', 'node_modules', 'rebrand', 'dist', 'tests', 'bin']);
|
|
80
101
|
const skipFiles = new Set(['LICENSE', 'NOTICE.md', 'CHANGELOG.md', 'package.json', 'README.md', 'evals/README.md', 'evals/skill-impact.md', 'docs/comparison.md', 'scripts/validate-brand.cjs', 'scripts/lib/skill-lint.js', 'scripts/lib/skill-lint-test.js']);
|
|
@@ -83,7 +104,7 @@ function walk(directory, prefix = '') {
|
|
|
83
104
|
const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
84
105
|
if (entry.isDirectory()) {
|
|
85
106
|
if (!skipDirectories.has(entry.name) && !relative.startsWith('evals/results')) walk(path.join(directory, entry.name), relative);
|
|
86
|
-
} else if (entry.isFile() && !skipFiles.has(relative) && /\.(?:md|js|cjs|sh|toml|yml)$/.test(entry.name)) {
|
|
107
|
+
} else if (entry.isFile() && !skipFiles.has(relative) && /\.(?:md|js|cjs|sh|toml|yml|json|patch|diff)$/.test(entry.name)) {
|
|
87
108
|
const original = fs.readFileSync(path.join(root, relative), 'utf8');
|
|
88
109
|
update(relative, transform(original, relative));
|
|
89
110
|
}
|
|
@@ -116,7 +137,7 @@ for (const relative of ['plugin.json', '.claude-plugin/plugin.json', '.codex-plu
|
|
|
116
137
|
manifest.interface.developerName = brand.maintainer;
|
|
117
138
|
manifest.interface.shortDescription = 'Engineering workflow skills';
|
|
118
139
|
manifest.interface.longDescription = `${brand.displayName} packages ${inventory.skills.length} engineering skills from Addy Osmani's Agent Skills, covering requirements, planning, implementation, testing, review, and shipping.`;
|
|
119
|
-
manifest.interface.defaultPrompt = manifest.interface.defaultPrompt.map(prompt => prompt.replaceAll('Agent Skills', brand.displayName));
|
|
140
|
+
manifest.interface.defaultPrompt = manifest.interface.defaultPrompt.map(prompt => rewriteReferences(prompt.replaceAll('Agent Skills', brand.displayName)));
|
|
120
141
|
}
|
|
121
142
|
update(relative, JSON.stringify(manifest, null, 2) + '\n');
|
|
122
143
|
}
|
|
@@ -164,7 +185,8 @@ pkg.i3ComposerAgent.upstreamRepository = brand.upstream;
|
|
|
164
185
|
update('package.json', JSON.stringify(pkg, null, 2) + '\n');
|
|
165
186
|
update('NOTICE.md', fs.readFileSync(path.join(__dirname, 'NOTICE.md'), 'utf8'));
|
|
166
187
|
function description(relative) {
|
|
167
|
-
const
|
|
188
|
+
const file = path.join(root, branded ? runtimePath(relative) : relative);
|
|
189
|
+
const head = fs.readFileSync(file, 'utf8').match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1] || '';
|
|
168
190
|
return (head.match(/^description:\s*(.*)$/m)?.[1] || '').replace(/^["']|["']$/g, '').replaceAll('|', '\\|');
|
|
169
191
|
}
|
|
170
192
|
const values = {
|
|
@@ -173,8 +195,8 @@ const values = {
|
|
|
173
195
|
SKILL_COUNT: inventory.skills.length,
|
|
174
196
|
COMMAND_COUNT: inventory.commands.length,
|
|
175
197
|
PERSONA_COUNT: inventory.personas.length,
|
|
176
|
-
SKILL_TABLE: inventory.skills.map(id => `| [${id}](skills/${id}/SKILL.md) | ${description(`skills/${id}/SKILL.md`)} |`).join('\n'),
|
|
177
|
-
COMMAND_TABLE: inventory.commands.map(id => `| /${brand.name}:${id} | ${description(`.claude/commands/${id}.md`)} |`).join('\n'),
|
|
198
|
+
SKILL_TABLE: inventory.skills.map(id => `| [${skillId(id)}](skills/${skillId(id)}/SKILL.md) | ${description(`skills/${id}/SKILL.md`)} |`).join('\n'),
|
|
199
|
+
COMMAND_TABLE: inventory.commands.map(id => `| /${brand.name}:${skillId(id)} | ${description(`.claude/commands/${id}.md`)} |`).join('\n'),
|
|
178
200
|
};
|
|
179
201
|
for (const [relative, template] of [
|
|
180
202
|
['README.md', 'README.md'], ['docs/codex-setup.md', 'codex-setup.md'], ['docs/codebuddy-setup.md', 'codebuddy-setup.md'],
|
|
@@ -190,5 +212,6 @@ if (mode === '--apply') for (const [relative, content] of changes) {
|
|
|
190
212
|
fs.writeFileSync(path.join(root, relative), content);
|
|
191
213
|
}
|
|
192
214
|
if (mode === '--apply') fs.chmodSync(path.join(root, 'bin/i3-composer-agent.cjs'), 0o755);
|
|
193
|
-
|
|
194
|
-
|
|
215
|
+
if (mode === '--apply') for (const [source, target] of renames) fs.renameSync(path.join(root, source), path.join(root, target));
|
|
216
|
+
console.log(JSON.stringify({ mode, changedFiles: [...changes.keys()], renamedPaths: Object.fromEntries(renames) }, null, 2));
|
|
217
|
+
if (mode === '--check' && (changes.size || renames.size)) process.exitCode = 1;
|
package/rebrand/brand.json
CHANGED
package/rebrand/catalog.cjs
CHANGED
|
@@ -4,6 +4,7 @@ const fs = require('node:fs');
|
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
const crypto = require('node:crypto');
|
|
6
6
|
const assert = require('node:assert/strict');
|
|
7
|
+
const { skillId, runtimePath } = require('./identifiers.cjs');
|
|
7
8
|
const runtimeRoots = ['skills', 'references', 'agents', 'commands', '.claude/commands', '.gemini/commands', 'hooks', '.claude-plugin', '.codex-plugin', '.agents/plugins'];
|
|
8
9
|
const runtimeFiles = ['plugin.json', 'LICENSE'];
|
|
9
10
|
|
|
@@ -46,10 +47,23 @@ function catalog(root) {
|
|
|
46
47
|
for (const name of runtimeFiles) if (fs.existsSync(path.join(root, name))) files.push(name);
|
|
47
48
|
return describe(files, file => fs.readFileSync(path.join(root, file), 'utf8'));
|
|
48
49
|
}
|
|
49
|
-
function validateCatalog(root, expected) {
|
|
50
|
+
function validateCatalog(root, expected, { branded = false } = {}) {
|
|
50
51
|
const actual = catalog(root);
|
|
52
|
+
const mapped = branded ? {
|
|
53
|
+
...expected,
|
|
54
|
+
skills: expected.skills.map(skillId),
|
|
55
|
+
commands: expected.commands.map(skillId),
|
|
56
|
+
runtimeFiles: expected.runtimeFiles.map(file => runtimePath(file, false, expected)).sort(),
|
|
57
|
+
skillFrontmatter: Object.fromEntries(expected.skills.map(id => {
|
|
58
|
+
const name = skillId(id);
|
|
59
|
+
const head = frontmatter(fs.readFileSync(path.join(root, 'skills', name, 'SKILL.md'), 'utf8'));
|
|
60
|
+
const normalized = head.replace(new RegExp(`^name:.*$`, 'm'), `name: ${id}`).replace(/\r\n/g, '\n');
|
|
61
|
+
actual.skillFrontmatter[name] = crypto.createHash('sha256').update(normalized).digest('hex');
|
|
62
|
+
return [name, expected.skillFrontmatter[id]];
|
|
63
|
+
})),
|
|
64
|
+
} : expected;
|
|
51
65
|
for (const field of ['skills', 'commands', 'personas', 'references', 'runtimeFiles', 'skillFrontmatter']) {
|
|
52
|
-
assert.deepEqual(actual[field],
|
|
66
|
+
assert.deepEqual(actual[field], mapped[field], `upstream preservation failed: ${field}`);
|
|
53
67
|
}
|
|
54
68
|
return actual;
|
|
55
69
|
}
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"file": "scripts/lib/skill-lint.js",
|
|
4
|
+
"from": "const SECTION_EXEMPT_SKILLS = {",
|
|
5
|
+
"to": "const SECTION_EXEMPT_SKILLS = {\n 'i3-using-agent-skills': 'Branded alias of the reviewed upstream meta-skill.',\n 'i3-idea-refine': 'Branded alias of the reviewed upstream legacy structure.',"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"file": "scripts/validate-artifact-paths.js",
|
|
9
|
+
"from": "function findViolations(relPath) {",
|
|
10
|
+
"to": "GUARDED_FILES.push(...GUARDED_FILES.filter(relative => /(?:^|\\/)commands\\//.test(relative)).map(relative => relative.replace(/(commands\\/)([^/]+)(\\.md|\\.toml)$/, '$1i3-$2$3')));\n\nfunction findViolations(relPath) {"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"file": "scripts/validate-commands.js",
|
|
14
|
+
"from": " plan: 'planning',",
|
|
15
|
+
"to": " plan: 'planning',\n 'i3-plan': 'i3-planning',"
|
|
16
|
+
},
|
|
2
17
|
{
|
|
3
18
|
"file": "scripts/validate-versions.js",
|
|
4
19
|
"from": "const manifestPaths = [\n \"plugin.json\",",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const upstream = require('./upstream.json');
|
|
4
|
+
const prefix = 'i3-';
|
|
5
|
+
const skillId = id => prefix + id;
|
|
6
|
+
const escape = value => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
7
|
+
|
|
8
|
+
function runtimePath(relative, reverse = false, inventory = upstream) {
|
|
9
|
+
const skills = new Map(inventory.skills.map(id => reverse ? [skillId(id), id] : [id, skillId(id)]));
|
|
10
|
+
relative = relative.replace(/^(skills|evals\/fixtures)\/([^/]+)(?=\/|$)/, (match, folder, id) => skills.has(id) ? `${folder}/${skills.get(id)}` : match);
|
|
11
|
+
relative = relative.replace(/^evals\/cases\/([^/]+)\.json$/, (match, id) => skills.has(id) ? `evals/cases/${skills.get(id)}.json` : match);
|
|
12
|
+
const commands = inventory.runtimeFiles.filter(file => /^(?:\.claude\/commands|\.gemini\/commands|commands)\/[^/]+\.(?:md|toml)$/.test(file));
|
|
13
|
+
for (const file of commands) {
|
|
14
|
+
const branded = file.replace(/([^/]+)\.(md|toml)$/, `${prefix}$1.$2`);
|
|
15
|
+
if (relative === (reverse ? branded : file)) return reverse ? file : branded;
|
|
16
|
+
}
|
|
17
|
+
return relative;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function rewriteReferences(content, reverse = false, inventory = upstream, commandReferences = true) {
|
|
21
|
+
const skills = new Map(inventory.skills.map(id => reverse ? [skillId(id), id] : [id, skillId(id)]));
|
|
22
|
+
const names = [...new Set([...inventory.commands, ...inventory.runtimeFiles.filter(file => /commands\/[^/]+\.toml$/.test(file)).map(file => file.split('/').at(-1).slice(0, -5))])];
|
|
23
|
+
const commands = new Map(names.map(id => reverse ? [prefix + id, id] : [id, prefix + id]));
|
|
24
|
+
const skillPattern = new RegExp(`(?<![\\w-])(${[...skills.keys()].map(escape).join('|')})(?![\\w-]|\\.(?:sh|py|js|cjs)\\b)`, 'g');
|
|
25
|
+
const commandPattern = [...commands.keys()].map(escape).join('|');
|
|
26
|
+
const slash = new RegExp(`(?<![\\w/.-])/(${commandPattern})(?![\\w-])`, 'g');
|
|
27
|
+
const filenames = new RegExp(`(commands/)(${commandPattern})\\.(md|toml)\\b`, 'g');
|
|
28
|
+
const namespace = new RegExp(`((?:agent-skills|i3-composer-agent|\\$\\{name\\}):)(${commandPattern})(?![\\w-])`, 'g');
|
|
29
|
+
// External links refer to the upstream repository's original paths.
|
|
30
|
+
return content.split(/(https?:\/\/[^\s)'"<>`]+)/g).map(part => {
|
|
31
|
+
if (/^https?:\/\//.test(part)) return part;
|
|
32
|
+
part = part.replace(skillPattern, match => skills.get(match));
|
|
33
|
+
if (!commandReferences) return part;
|
|
34
|
+
return part
|
|
35
|
+
.replace(slash, (_, id) => '/' + commands.get(id))
|
|
36
|
+
.replace(filenames, (_, folder, id, extension) => `${folder}${commands.get(id)}.${extension}`)
|
|
37
|
+
.replace(namespace, (_, scope, id) => scope + commands.get(id));
|
|
38
|
+
}).join('');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = { prefix, skillId, runtimePath, rewriteReferences };
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# i3 Composer Agent changes
|
|
2
2
|
|
|
3
|
+
## 2.0.0 — 2026-10-07
|
|
4
|
+
|
|
5
|
+
Upstream baseline: agent-skills 0.6.12 at commit 1401c8b8030e023baeebb31781a6653fe8e93026.
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Commands and Skill IDs use the `i3-` prefix, such as `/i3-spec` and `$i3-spec-driven-development`.
|
|
10
|
+
- Native upgrades back up and retire this package's manifest-owned unprefixed paths. Unowned entries remain in place; failed migrations roll back.
|
|
11
|
+
- Local command references, hooks, routing fixtures and setup documentation follow the renamed identifiers.
|
|
12
|
+
|
|
13
|
+
### Migration
|
|
14
|
+
|
|
15
|
+
- Replace `/spec`, `/plan`, `/build` and other lifecycle calls with `/i3-spec`, `/i3-plan`, `/i3-build` and their prefixed equivalents. Plugin calls use `/i3-composer-agent:i3-<command>`.
|
|
16
|
+
- Replace Skill calls such as `$spec-driven-development` with `$i3-spec-driven-development`.
|
|
17
|
+
- Upgrade with `npx @givolt/i3-composer-agent@2.0.0 --codebuddy`, confirm replacement, then reload the host. The installer backs up manifest-owned legacy entries before retiring them.
|
|
18
|
+
- Keep using the pinned 1.0.1 package if a consuming workflow needs the old identifiers. Existing published versions remain available.
|
|
19
|
+
|
|
3
20
|
## 1.0.1 — 2026-10-07
|
|
4
21
|
|
|
5
22
|
Upstream baseline: agent-skills 0.6.12 at commit 1401c8b8030e023baeebb31781a6653fe8e93026.
|
|
@@ -127,7 +127,7 @@ function prepare() {
|
|
|
127
127
|
} else {
|
|
128
128
|
console.log(`/plugin marketplace add ${quote(directory)}`);
|
|
129
129
|
console.log(`/plugin install ${selector}`);
|
|
130
|
-
console.log(`Use /${name}:spec, /${name}:plan, /${name}:build, and /${name}:ship in ${host === 'claude' ? 'Claude Code' : 'CodeBuddy'}.`);
|
|
130
|
+
console.log(`Use /${name}:i3-spec, /${name}:i3-plan, /${name}:i3-build, and /${name}:i3-ship in ${host === 'claude' ? 'Claude Code' : 'CodeBuddy'}.`);
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
async function main() {
|
|
@@ -180,7 +180,7 @@ async function main() {
|
|
|
180
180
|
console.log(identity);
|
|
181
181
|
for (const target of plan.targets) {
|
|
182
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}`);
|
|
183
|
+
if (target.commandsDirectory) console.log(`Commands: /i3-spec, /i3-plan, /i3-build, /i3-ship (all ${fs.readdirSync(target.commandsDirectory).filter(file => file.endsWith('.md')).length} workflows) at ${target.commandsDirectory}`);
|
|
184
184
|
}
|
|
185
185
|
for (const backup of backups) console.log(`Previous assets preserved at ${backup}`);
|
|
186
186
|
console.log('Reload the IDE window or start a new host session to discover installed files.');
|
|
@@ -5,6 +5,7 @@ const path = require('node:path');
|
|
|
5
5
|
const os = require('node:os');
|
|
6
6
|
const crypto = require('node:crypto');
|
|
7
7
|
const registry = require('./agent-targets.json');
|
|
8
|
+
const upstream = require('../rebrand/upstream.json');
|
|
8
9
|
const root = path.resolve(__dirname, '..');
|
|
9
10
|
const name = 'i3-composer-agent';
|
|
10
11
|
const aliases = { claude: 'claude-code', gemini: 'gemini-cli', copilot: 'github-copilot' };
|
|
@@ -82,7 +83,19 @@ function planInstallation({ agents, scope, projectDir = process.cwd(), home = os
|
|
|
82
83
|
}
|
|
83
84
|
assets.push({ source: path.join(root, 'LICENSE'), destination: destination(path.join(target.base, `${name}-LICENSE`)), kind: 'attribution' });
|
|
84
85
|
assets.push({ source: path.join(root, 'NOTICE.md'), destination: destination(path.join(target.base, `${name}-NOTICE.md`)), kind: 'attribution' });
|
|
85
|
-
|
|
86
|
+
const manifestFile = path.join(target.base, `.${name}.json`);
|
|
87
|
+
if (exists(manifestFile)) {
|
|
88
|
+
const previous = JSON.parse(fs.readFileSync(manifestFile, 'utf8'));
|
|
89
|
+
if (previous.name === name && Array.isArray(previous.assets)) {
|
|
90
|
+
const legacy = upstream.skills.map(id => `skills/${id}`);
|
|
91
|
+
if (target.commandsDirectory) legacy.push(...upstream.commands.map(id => `commands/${id}.md`));
|
|
92
|
+
for (const relative of legacy) {
|
|
93
|
+
const file = destination(path.join(target.base, relative));
|
|
94
|
+
if (previous.assets.includes(relative) && exists(file)) assets.push({ destination: file, kind: 'legacy' });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
assets.push({ target, destination: destination(manifestFile), kind: 'manifest' });
|
|
86
99
|
for (const asset of assets.slice(start)) {
|
|
87
100
|
asset.base = target.base;
|
|
88
101
|
asset.codebuddyCommand = asset.kind === 'command' && target.agents.includes('codebuddy');
|
|
@@ -120,9 +133,10 @@ function executeInstallation(plan, { force = false } = {}) {
|
|
|
120
133
|
contexts.set(asset.base, { staging: fs.mkdtempSync(path.join(asset.base, `.${name}-stage-`)), backup: path.join(asset.base, `.${name}-backup-${crypto.randomUUID()}`) });
|
|
121
134
|
}
|
|
122
135
|
const context = contexts.get(asset.base);
|
|
136
|
+
if (asset.kind === 'legacy') continue;
|
|
123
137
|
asset.staged = path.join(context.staging, String(index));
|
|
124
138
|
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');
|
|
139
|
+
fs.writeFileSync(asset.staged, JSON.stringify({ name, version: pkg.version, entryPrefix: 'i3-', upstreamVersion: pkg.i3ComposerAgent.upstreamVersion, upstreamCommit: pkg.i3ComposerAgent.upstreamCommit, scope: plan.scope, agents: asset.target.agents, assets: plan.assets.filter(item => item.base === asset.base && item.kind !== 'legacy').map(item => path.relative(asset.base, item.destination)) }, null, 2) + '\n');
|
|
126
140
|
} else {
|
|
127
141
|
fs.cpSync(asset.source, asset.staged, { recursive: true, filter: file => {
|
|
128
142
|
if (fs.lstatSync(file).isSymbolicLink()) throw new Error(`Package contains a symlink: ${file}`);
|
|
@@ -143,8 +157,10 @@ function executeInstallation(plan, { force = false } = {}) {
|
|
|
143
157
|
fs.renameSync(asset.destination, change.backup);
|
|
144
158
|
}
|
|
145
159
|
committed.push(change);
|
|
146
|
-
|
|
147
|
-
|
|
160
|
+
if (asset.kind !== 'legacy') {
|
|
161
|
+
fs.renameSync(asset.staged, asset.destination);
|
|
162
|
+
change.written = true;
|
|
163
|
+
}
|
|
148
164
|
}
|
|
149
165
|
return [...contexts.values()].map(context => context.backup).filter(exists);
|
|
150
166
|
} catch (error) {
|
|
@@ -9,7 +9,7 @@ const brand = require('../rebrand/brand.json');
|
|
|
9
9
|
const pkg = require('../package.json');
|
|
10
10
|
const upstream = require('../rebrand/upstream.json');
|
|
11
11
|
const { validateCatalog } = require('../rebrand/catalog.cjs');
|
|
12
|
-
validateCatalog(root, upstream);
|
|
12
|
+
validateCatalog(root, upstream, { branded: true });
|
|
13
13
|
function json(relative) { return JSON.parse(fs.readFileSync(path.join(root, relative), 'utf8')); }
|
|
14
14
|
assert.equal(pkg.name, brand.npmName);
|
|
15
15
|
assert.equal(pkg.version, brand.releaseVersion);
|
|
@@ -31,13 +31,13 @@ for (const relative of ['.claude-plugin/marketplace.json', '.agents/plugins/mark
|
|
|
31
31
|
assert.ok(json('.codex-plugin/plugin.json').interface.shortDescription.length <= 30);
|
|
32
32
|
const skills = fs.readdirSync(path.join(root, 'skills')).filter(name => fs.existsSync(path.join(root, 'skills', name, 'SKILL.md')));
|
|
33
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
|
|
34
|
+
assert.ok(skills.includes('i3-using-agent-skills'), 'meta-skill ID must use the branded prefix');
|
|
35
35
|
const commands = fs.readdirSync(path.join(root, '.claude/commands')).filter(name => name.endsWith('.md'));
|
|
36
36
|
assert.equal(commands.length, upstream.commands.length, 'brand conversion must preserve the reviewed upstream commands');
|
|
37
37
|
for (const command of commands) {
|
|
38
38
|
const body = fs.readFileSync(path.join(root, '.claude/commands', command), 'utf8');
|
|
39
39
|
assert.ok(!body.includes('agent-skills:'), `${command}: stale plugin namespace`);
|
|
40
|
-
for (const match of body.matchAll(/i3-composer-agent:([a-
|
|
40
|
+
for (const match of body.matchAll(/(?<!\/)i3-composer-agent:([a-z0-9-]+)/g)) assert.ok(skills.includes(match[1]), `${command}: unknown skill ${match[1]}`);
|
|
41
41
|
}
|
|
42
42
|
for (const relative of pkg.i3ComposerAgent.pluginFiles) assert.ok(fs.existsSync(path.join(root, relative)), `bundle is missing ${relative}`);
|
|
43
43
|
assert.match(fs.readFileSync(path.join(root, 'LICENSE'), 'utf8'), /Copyright \(c\) 2025 Addy Osmani/);
|
|
@@ -25,10 +25,10 @@ test('native CodeBuddy installation includes short commands, personas and usable
|
|
|
25
25
|
assert.equal(fs.readdirSync(path.join(base, 'skills')).length, upstream.skills.length);
|
|
26
26
|
assert.equal(fs.readdirSync(path.join(base, 'commands')).length, upstream.commands.length);
|
|
27
27
|
assert.equal(fs.readdirSync(path.join(base, 'agents')).length, upstream.personas.length);
|
|
28
|
-
const command = fs.readFileSync(path.join(base, 'commands/spec.md'), 'utf8');
|
|
29
|
-
assert.match(command, /Invoke the spec-driven-development skill/);
|
|
28
|
+
const command = fs.readFileSync(path.join(base, 'commands/i3-spec.md'), 'utf8');
|
|
29
|
+
assert.match(command, /Invoke the i3-spec-driven-development skill/);
|
|
30
30
|
assert.doesNotMatch(command, /i3-composer-agent:/);
|
|
31
|
-
const ship = fs.readFileSync(path.join(base, 'commands/ship.md'), 'utf8');
|
|
31
|
+
const ship = fs.readFileSync(path.join(base, 'commands/i3-ship.md'), 'utf8');
|
|
32
32
|
assert.match(ship, /\.codebuddy\/agents\//);
|
|
33
33
|
assert.doesNotMatch(ship, /\.claude\/agents\//);
|
|
34
34
|
assert.ok(fs.existsSync(path.join(base, 'references/definition-of-done.md')));
|
|
@@ -36,7 +36,7 @@ test('native CodeBuddy installation includes short commands, personas and usable
|
|
|
36
36
|
assert.match(fs.readFileSync(path.join(base, 'i3-composer-agent-NOTICE.md'), 'utf8'), /Addy Osmani/);
|
|
37
37
|
assert.equal(fs.existsSync(path.join(directory, 'AGENTS.md')), false);
|
|
38
38
|
assert.equal(fs.existsSync(path.join(directory, 'i3-composer-agent')), false);
|
|
39
|
-
assert.match(result.stdout, /\/spec/);
|
|
39
|
+
assert.match(result.stdout, /\/i3-spec/);
|
|
40
40
|
});
|
|
41
41
|
test('multiple tools retain their own destinations and share universal skills without duplicate work', t => {
|
|
42
42
|
const directory = workspace(t);
|
|
@@ -80,16 +80,64 @@ test('all copied Markdown reference links and stable skill frontmatter remain va
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
visit(path.join(base, 'skills'));
|
|
83
|
-
for (const id of upstream.skills) {
|
|
83
|
+
for (const id of upstream.skills.map(id => `i3-${id}`)) {
|
|
84
84
|
const head = content => content.match(/^---\r?\n[\s\S]*?\r?\n---/)[0];
|
|
85
85
|
assert.equal(head(fs.readFileSync(path.join(base, 'skills', id, 'SKILL.md'), 'utf8')), head(fs.readFileSync(path.join(root, 'skills', id, 'SKILL.md'), 'utf8')));
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
|
+
function legacyInstallation(directory) {
|
|
89
|
+
const base = path.join(directory, '.codebuddy');
|
|
90
|
+
fs.mkdirSync(path.join(base, 'commands'), { recursive: true });
|
|
91
|
+
fs.mkdirSync(path.join(base, 'skills/spec-driven-development'), { recursive: true });
|
|
92
|
+
fs.writeFileSync(path.join(base, 'commands/spec.md'), 'original owned command');
|
|
93
|
+
fs.writeFileSync(path.join(base, 'skills/spec-driven-development/SKILL.md'), 'original owned Skill');
|
|
94
|
+
fs.writeFileSync(path.join(base, 'commands/build.md'), 'unowned custom command');
|
|
95
|
+
fs.writeFileSync(path.join(base, '.i3-composer-agent.json'), JSON.stringify({ name: 'i3-composer-agent', version: '1.0.1', assets: ['commands/spec.md', 'skills/spec-driven-development'] }));
|
|
96
|
+
return base;
|
|
97
|
+
}
|
|
98
|
+
test('prefix migration retires only owned legacy paths and preserves their backups', t => {
|
|
99
|
+
const directory = workspace(t);
|
|
100
|
+
const base = legacyInstallation(directory);
|
|
101
|
+
const args = ['--codebuddy', '--scope', 'project', '--yes'];
|
|
102
|
+
assert.equal(run(directory, args).status, 1);
|
|
103
|
+
assert.equal(fs.readFileSync(path.join(base, 'commands/spec.md'), 'utf8'), 'original owned command');
|
|
104
|
+
const result = run(directory, [...args, '--force']);
|
|
105
|
+
assert.equal(result.status, 0, result.stderr);
|
|
106
|
+
assert.equal(fs.existsSync(path.join(base, 'commands/spec.md')), false);
|
|
107
|
+
assert.equal(fs.existsSync(path.join(base, 'skills/spec-driven-development')), false);
|
|
108
|
+
assert.ok(fs.existsSync(path.join(base, 'commands/i3-spec.md')));
|
|
109
|
+
assert.match(fs.readFileSync(path.join(base, 'skills/i3-spec-driven-development/SKILL.md'), 'utf8'), /^name: i3-spec-driven-development$/m);
|
|
110
|
+
assert.equal(fs.readFileSync(path.join(base, 'commands/build.md'), 'utf8'), 'unowned custom command');
|
|
111
|
+
const backup = fs.readdirSync(base).find(file => file.startsWith('.i3-composer-agent-backup-'));
|
|
112
|
+
assert.equal(fs.readFileSync(path.join(base, backup, 'commands/spec.md'), 'utf8'), 'original owned command');
|
|
113
|
+
assert.equal(fs.readFileSync(path.join(base, backup, 'skills/spec-driven-development/SKILL.md'), 'utf8'), 'original owned Skill');
|
|
114
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(base, '.i3-composer-agent.json'), 'utf8'));
|
|
115
|
+
assert.equal(manifest.entryPrefix, 'i3-');
|
|
116
|
+
assert.ok(!manifest.assets.includes('commands/spec.md'));
|
|
117
|
+
});
|
|
118
|
+
test('a failure after retiring legacy paths rolls the migration back', t => {
|
|
119
|
+
const directory = workspace(t);
|
|
120
|
+
const base = legacyInstallation(directory);
|
|
121
|
+
const before = fs.readFileSync(path.join(base, '.i3-composer-agent.json'), 'utf8');
|
|
122
|
+
const { planInstallation, executeInstallation } = require('../bin/install.cjs');
|
|
123
|
+
const plan = planInstallation({ agents: ['codebuddy'], scope: 'project', projectDir: directory });
|
|
124
|
+
const realRename = fs.renameSync;
|
|
125
|
+
fs.renameSync = (source, destination) => {
|
|
126
|
+
if (destination === path.join(base, '.i3-composer-agent.json') && source.includes('-stage-')) throw new Error('simulated manifest failure');
|
|
127
|
+
return realRename(source, destination);
|
|
128
|
+
};
|
|
129
|
+
try { assert.throws(() => executeInstallation(plan, { force: true }), /simulated manifest failure/); }
|
|
130
|
+
finally { fs.renameSync = realRename; }
|
|
131
|
+
assert.equal(fs.readFileSync(path.join(base, '.i3-composer-agent.json'), 'utf8'), before);
|
|
132
|
+
assert.equal(fs.readFileSync(path.join(base, 'commands/spec.md'), 'utf8'), 'original owned command');
|
|
133
|
+
assert.equal(fs.readFileSync(path.join(base, 'skills/spec-driven-development/SKILL.md'), 'utf8'), 'original owned Skill');
|
|
134
|
+
assert.equal(fs.existsSync(path.join(base, 'commands/i3-spec.md')), false);
|
|
135
|
+
});
|
|
88
136
|
test('existing assets are protected; confirmed replacement backs them up and preserves unrelated settings', t => {
|
|
89
137
|
const directory = workspace(t);
|
|
90
138
|
const base = path.join(directory, '.codebuddy');
|
|
91
139
|
fs.mkdirSync(path.join(base, 'commands'), { recursive: true });
|
|
92
|
-
fs.writeFileSync(path.join(base, 'commands/spec.md'), 'my custom command');
|
|
140
|
+
fs.writeFileSync(path.join(base, 'commands/i3-spec.md'), 'my custom command');
|
|
93
141
|
fs.writeFileSync(path.join(base, 'settings.json'), '{"keep":"my settings"}\n');
|
|
94
142
|
const args = ['--codebuddy', '--scope', 'project', '--yes'];
|
|
95
143
|
assert.equal(run(directory, args).status, 1);
|
|
@@ -98,7 +146,7 @@ test('existing assets are protected; confirmed replacement backs them up and pre
|
|
|
98
146
|
assert.equal(result.status, 0, result.stderr);
|
|
99
147
|
const backups = fs.readdirSync(base).filter(file => file.startsWith('.i3-composer-agent-backup-'));
|
|
100
148
|
assert.equal(backups.length, 1);
|
|
101
|
-
assert.equal(fs.readFileSync(path.join(base, backups[0], 'commands/spec.md'), 'utf8'), 'my custom command');
|
|
149
|
+
assert.equal(fs.readFileSync(path.join(base, backups[0], 'commands/i3-spec.md'), 'utf8'), 'my custom command');
|
|
102
150
|
assert.equal(fs.readFileSync(path.join(base, 'settings.json'), 'utf8'), '{"keep":"my settings"}\n');
|
|
103
151
|
assert.equal(fs.existsSync(path.join(base, backups[0], 'settings.json')), false);
|
|
104
152
|
});
|
|
@@ -133,7 +181,7 @@ test('the pinned tool catalog plans all project choices and rejects unsupported
|
|
|
133
181
|
test('failed installation restores prior assets and removes all newly written files', t => {
|
|
134
182
|
const directory = workspace(t);
|
|
135
183
|
const { planInstallation, executeInstallation } = require('../bin/install.cjs');
|
|
136
|
-
const prior = path.join(directory, '.codebuddy/skills/api-and-interface-design');
|
|
184
|
+
const prior = path.join(directory, '.codebuddy/skills/i3-api-and-interface-design');
|
|
137
185
|
fs.mkdirSync(prior, { recursive: true });
|
|
138
186
|
fs.writeFileSync(path.join(prior, 'custom.md'), 'prior skill content');
|
|
139
187
|
const plan = planInstallation({ agents: ['codebuddy', 'codex'], scope: 'project', projectDir: directory });
|
|
@@ -146,7 +194,7 @@ test('failed installation restores prior assets and removes all newly written fi
|
|
|
146
194
|
try { assert.throws(() => executeInstallation(plan, { force: true }), /simulated disk failure/); }
|
|
147
195
|
finally { fs.renameSync = realRename; }
|
|
148
196
|
assert.equal(fs.readFileSync(path.join(prior, 'custom.md'), 'utf8'), 'prior skill content');
|
|
149
|
-
assert.deepEqual(fs.readdirSync(path.dirname(prior)), ['api-and-interface-design']);
|
|
197
|
+
assert.deepEqual(fs.readdirSync(path.dirname(prior)), ['i3-api-and-interface-design']);
|
|
150
198
|
assert.deepEqual(fs.readdirSync(path.join(directory, '.codebuddy')), ['skills']);
|
|
151
199
|
assert.equal(fs.existsSync(path.join(directory, '.agents')), false);
|
|
152
200
|
});
|
|
@@ -157,14 +205,14 @@ test('replacing a skill symlink backs up the link and leaves its external source
|
|
|
157
205
|
fs.mkdirSync(external);
|
|
158
206
|
fs.writeFileSync(path.join(external, 'custom.md'), 'external source');
|
|
159
207
|
fs.mkdirSync(skills, { recursive: true });
|
|
160
|
-
fs.symlinkSync(external, path.join(skills, 'spec-driven-development'), 'dir');
|
|
208
|
+
fs.symlinkSync(external, path.join(skills, 'i3-spec-driven-development'), 'dir');
|
|
161
209
|
const result = run(directory, ['--codebuddy', '--scope', 'project', '--yes', '--force']);
|
|
162
210
|
assert.equal(result.status, 0, result.stderr);
|
|
163
211
|
assert.deepEqual(fs.readdirSync(external), ['custom.md']);
|
|
164
|
-
assert.equal(fs.lstatSync(path.join(skills, 'spec-driven-development')).isSymbolicLink(), false);
|
|
212
|
+
assert.equal(fs.lstatSync(path.join(skills, 'i3-spec-driven-development')).isSymbolicLink(), false);
|
|
165
213
|
const base = path.dirname(skills);
|
|
166
214
|
const backup = fs.readdirSync(base).find(file => file.startsWith('.i3-composer-agent-backup-'));
|
|
167
|
-
assert.equal(fs.lstatSync(path.join(base, backup, 'skills/spec-driven-development')).isSymbolicLink(), true);
|
|
215
|
+
assert.equal(fs.lstatSync(path.join(base, backup, 'skills/i3-spec-driven-development')).isSymbolicLink(), true);
|
|
168
216
|
});
|
|
169
217
|
test('package destinations, shared directory symlinks and ambiguous noninteractive flags fail without writes', t => {
|
|
170
218
|
const directory = workspace(t);
|
|
@@ -9,7 +9,7 @@ Based on [Agent Skills](https://github.com/addyosmani/agent-skills) by Addy Osma
|
|
|
9
9
|
(MIT). See [NOTICE.md](NOTICE.md) for attribution.
|
|
10
10
|
|
|
11
11
|
The npm package is `@givolt/i3-composer-agent`; the plugin and marketplace ID is
|
|
12
|
-
`i3-composer-agent`. Skill IDs
|
|
12
|
+
`i3-composer-agent`. Commands and Skill IDs use the `i3-` prefix.
|
|
13
13
|
|
|
14
14
|
## Install with the wizard
|
|
15
15
|
|
|
@@ -49,6 +49,12 @@ under each tool configuration root. Unrelated settings are left in place.
|
|
|
49
49
|
`npx -y` accepts npm's package download prompt; the package's own `--yes` flag
|
|
50
50
|
accepts the explicit installation plan.
|
|
51
51
|
|
|
52
|
+
Earlier native installations migrate their unprefixed Skill and command paths
|
|
53
|
+
when those paths belong to this package's previous installation manifest.
|
|
54
|
+
The migration preserves legacy paths in the backup directory. Unowned entries
|
|
55
|
+
remain in place. Use `/i3-spec` in CodeBuddy or `$i3-spec-driven-development`
|
|
56
|
+
in Codex after upgrading.
|
|
57
|
+
|
|
52
58
|
## Prepare the complete plugin
|
|
53
59
|
|
|
54
60
|
For native plugin registration, including plugin hooks, explicitly prepare a
|
|
@@ -78,21 +84,21 @@ codex plugin marketplace add ./i3-composer-agent
|
|
|
78
84
|
codex plugin add i3-composer-agent@i3-composer-agent
|
|
79
85
|
```
|
|
80
86
|
|
|
81
|
-
Start a new session after enabling the plugin. Codex uses the
|
|
82
|
-
names, such as `$spec-driven-development`; lifecycle slash-command adaptation
|
|
87
|
+
Start a new session after enabling the plugin. Codex uses the branded Skill
|
|
88
|
+
names, such as `$i3-spec-driven-development`; lifecycle slash-command adaptation
|
|
83
89
|
is maintained separately.
|
|
84
90
|
|
|
85
91
|
## Commands
|
|
86
92
|
|
|
87
93
|
Native CodeBuddy and Claude Code installations use short filenames such as
|
|
88
|
-
`/spec`, `/plan`, `/build` and `/ship`. Hosts supporting Claude-compatible plugin
|
|
94
|
+
`/i3-spec`, `/i3-plan`, `/i3-build` and `/i3-ship`. Hosts supporting Claude-compatible plugin
|
|
89
95
|
commands load the same workflows under the plugin namespace:
|
|
90
96
|
|
|
91
97
|
| Command | Description |
|
|
92
98
|
| --- | --- |
|
|
93
99
|
{{COMMAND_TABLE}}
|
|
94
100
|
|
|
95
|
-
`/i3-composer-agent:build auto` runs the existing approved-plan workflow.
|
|
101
|
+
`/i3-composer-agent:i3-build auto` runs the existing approved-plan workflow.
|
|
96
102
|
|
|
97
103
|
## Skills
|
|
98
104
|
|