@givolt/i3-composer-agent 0.6.12 → 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.
Files changed (37) hide show
  1. package/.agents/plugins/marketplace.json +3 -2
  2. package/.claude-plugin/marketplace.json +2 -1
  3. package/.claude-plugin/plugin.json +3 -1
  4. package/.codex-plugin/plugin.json +3 -1
  5. package/CHANGELOG.md +23 -0
  6. package/README.md +77 -137
  7. package/bin/i3-composer-agent.cjs +6 -3
  8. package/docs/antigravity-setup.md +1 -1
  9. package/docs/comparison.md +1 -1
  10. package/hooks/sdd-cache-post.sh +0 -0
  11. package/hooks/sdd-cache-pre.sh +0 -0
  12. package/package.json +75 -17
  13. package/plugin.json +4 -2
  14. package/rebrand/MAINTENANCE.md +92 -31
  15. package/rebrand/NOTICE.md +9 -0
  16. package/rebrand/PR-WORKFLOW.md +34 -0
  17. package/rebrand/UNIFICATION-SPEC.md +33 -0
  18. package/rebrand/VALIDATION.md +28 -0
  19. package/rebrand/VERSIONING.md +32 -0
  20. package/rebrand/apply.cjs +80 -7
  21. package/rebrand/brand.json +6 -2
  22. package/rebrand/catalog.cjs +56 -0
  23. package/rebrand/fork-patches.json +12 -0
  24. package/rebrand/overlay/.github/workflows/test-plugin-install.yml +177 -0
  25. package/rebrand/overlay/CHANGELOG.md +23 -0
  26. package/rebrand/overlay/bin/i3-composer-agent.cjs +122 -0
  27. package/rebrand/overlay/scripts/validate-brand.cjs +44 -0
  28. package/rebrand/overlay/tests/bundle.test.cjs +85 -0
  29. package/rebrand/package.template.json +82 -0
  30. package/rebrand/rebrand.sh +4 -0
  31. package/rebrand/record-upstream.cjs +37 -0
  32. package/rebrand/templates/README.md +44 -136
  33. package/rebrand/tests/pipeline.test.cjs +185 -0
  34. package/rebrand/upstream.json +171 -0
  35. package/scripts/validate-brand.cjs +11 -3
  36. package/skills/idea-refine/scripts/idea-refine.sh +0 -0
  37. package/tests/bundle.test.cjs +11 -5
@@ -1,51 +1,112 @@
1
- # Maintaining i3 Composer Agent
1
+ # i3 Composer Agent 仓库维护
2
2
 
3
- The product and plugin ID is `i3-composer-agent`; the npm package is
4
- `@givolt/i3-composer-agent`. The separate GSD-based project is maintained in its
5
- own repository and is outside this conversion.
3
+ 本指南只适用于 Agent 仓库;GSD / i3 Composer Work 是另一个独立仓库。
4
+ 这里借鉴其维护方式,不复用其源码、安装产物或 Git 分支。
6
5
 
7
- `brand.json` records the identity and upstream baseline. `apply.cjs` replays the
8
- brand substitutions and marketplace normalization; `templates/` holds the
9
- branded installation pages. Preserve LICENSE, NOTICE, upstream URLs, historical
10
- eval reports, the upstream comparison, and the skill ID `using-agent-skills`.
11
- References to the independent Agent Skills specification retain that name.
6
+ ## 仓库和分支
12
7
 
13
- ## Source and release workflow
8
+ - 品牌仓库:`https://github.com/49780522/i3-composer-agent`(Private)。
9
+ - `origin` 指向品牌仓库;`upstream` 指向 `https://github.com/addyosmani/agent-skills.git`。
10
+ - `agent/source`:原始上游源码与名称,加本项目 `rebrand/` 工具和规则。
11
+ - `main`:经过重放与验证的品牌快照;npm 身份为 `@givolt/i3-composer-agent`。
12
+ - `i3-composer-agent-v0.6.12`:已经发布的首次包所对应的原始品牌提交。
14
13
 
15
- Keep the upstream source on `i3/source`, with its original names. Perform source
16
- updates there. Start release candidates from the current branded branch, bringing
17
- in the upstream changes while retaining the npm CLI and `rebrand/` files, then
18
- replay the brand transformation. A fresh checkout of `i3/source` alone does not
19
- contain the distribution tools.
20
- Keep the original upstream remote until a dedicated fork repository is created;
21
- do not push branded changes to `addyosmani/agent-skills`.
14
+ 源码工作树为 `/Users/howard/Workspace/composer-skills-source/agent-source`;
15
+ 品牌工作树为 `/Users/howard/Workspace/composer-skills-source/agent-skills`。
16
+ 旧的 Agent 本地 `i3/source` 分支已改为 `agent/source`,避免与 GSD 分支名称混淆。
17
+
18
+ ## 获取源码
19
+
20
+ ```bash
21
+ git clone https://github.com/49780522/i3-composer-agent.git
22
+ cd i3-composer-agent
23
+ git remote add upstream https://github.com/addyosmani/agent-skills.git
24
+ git fetch upstream --tags
25
+ git worktree add --track -b agent/source ../agent-source origin/agent/source
26
+ ```
27
+
28
+ 源分支没有发行用的根 package.json。包入口、发行测试和配置由
29
+ `rebrand/overlay/`、`package.template.json` 在品牌候选中生成。
30
+
31
+ ## 同步上游
32
+
33
+ 1. 在 Agent 源工作树获取上游,选择并审查一个明确的版本或提交。
34
+ 2. 从 `agent/source` 创建维护分支,合并该 ref 并解决源码冲突。
35
+ 3. 从同一 ref 记录上游目录和 Skill frontmatter;审查记录中的新增、删除和描述变化。
36
+ 4. 调整文字补丁、分发 overlay 和品牌模板;源码分支保持原始名称。
37
+ 5. 运行源验证,提交维护分支,通过 PR 合入 `agent/source`。
38
+ 6. 从更新后的源分支创建独立品牌候选工作树,重放、验证和打包。
39
+ 7. 将验证过的品牌快照通过发行 PR 更新 `main`;npm 发布单独执行。
40
+
41
+ 源维护阶段示例(把示例分支和 ref 替换为实际选定值):
42
+
43
+ ```bash
44
+ cd /Users/howard/Workspace/composer-skills-source/agent-source
45
+ git fetch upstream --tags
46
+ git switch -c sync/upstream-next agent/source
47
+ git merge --no-commit --no-ff upstream/main
48
+ # 审查并处理源码冲突后:
49
+ node rebrand/record-upstream.cjs upstream/main
50
+ node --test scripts/*-test.js scripts/lib/*-test.js rebrand/tests/*.test.cjs
51
+ ```
52
+
53
+ 记录工具读取 Git 对象中的上游文件,写入 `upstream.json` 和 `brand.json` 的基线。
54
+ 它不会把任意本地新增文件认定为上游。发布版本由 `brand.json.releaseVersion`
55
+ 控制,与 `upstream.json.upstreamVersion` 分开;按实际更改递增,保持六份清单一致。
56
+
57
+ 品牌版本从 `1.0.0` 开始使用独立编号;本次仅修改 i3,上游基线保留为 `0.6.12`。
58
+ 之后的当前基线以 `upstream.json` 记录为准。
59
+ 仅修改 i3 的修复或分发规则时,只递增品牌版本;上游版本和提交保持原值。
60
+ README、CLI 帮助和准备完成提示同时显示这两个版本;`--version` 保留单一品牌版本输出。
61
+ 具体规则与历史版本处理见 [VERSIONING.md](VERSIONING.md)。
62
+
63
+ ## 创建品牌候选
64
+
65
+ 从源分支创建候选,每次换一个分支名和独立目录:
22
66
 
23
67
  ```bash
24
- node rebrand/apply.cjs --apply
25
- node rebrand/apply.cjs --check
68
+ cd /Users/howard/Workspace/composer-skills-source/agent-skills
69
+ git fetch origin
70
+ git worktree add -b release/agent-next ../agent-candidate origin/agent/source
71
+ cd ../agent-candidate
72
+ bash rebrand/rebrand.sh --apply
73
+ bash rebrand/rebrand.sh --check
26
74
  npm run validate
27
75
  npm test
28
76
  node scripts/run-evals.js --min-rank1 95
29
77
  bash hooks/session-start-test.sh
30
78
  bash hooks/sdd-cache-test.sh
31
79
  bash hooks/simplify-ignore-test.sh
32
- npm pack --pack-destination /path/to/release-directory
80
+ mkdir -p ../releases/next
81
+ npm pack --pack-destination ../releases/next
33
82
  ```
34
83
 
35
- The transform is idempotent. The check exits with a failure when another replay
36
- would change files. Keep package.json and all plugin manifest versions in sync.
37
- Inspect the tarball and test its CLI in an isolated directory before publishing.
84
+ 在打包前先创建输出目录。对 tarball 重新解包,验证 CLI、完整文件目录及宿主清单。
85
+ 源码分支上执行 `--apply` 会在写入前失败;不得把未经重放的源分支直接更新到 `main`。
86
+ 源 PR 与发行 PR 的边界见 [PR-WORKFLOW.md](PR-WORKFLOW.md)。
87
+
88
+ ## 审查重放规则
89
+
90
+ - `fork-patches.json`:明确的文字补丁,源文本不再匹配时失败,要求人工适配。
91
+ - `overlay/`:本项目的 npm CLI、发行测试、品牌校验和发行 CI;上游相关文件改变时需合并审查。
92
+ - `templates/`:品牌 README 和安装指南;上游对应说明改变时同步模板。
93
+ - 目录数量、Skill 表、命令表和插件描述由已记录的目录生成,不固定为 25/9。
94
+ - Skill frontmatter 保留原文;校验比较换行规范化后的哈希,兼容 LF/CRLF 检出。
95
+ - LICENSE、NOTICE、真实上游链接、历史评估说明和标准名称保留。
96
+ - 如果上游新增根 package.json,重放会失败,先适配分发配置和依赖再继续。
97
+
98
+ ## 发布及验收
38
99
 
39
- The CLI prepares a self-contained plugin directory and prints native host
40
- registration commands. It does not write business-project AGENTS.md, change
41
- existing plugin settings, or register lifecycle slash commands in Codex.
100
+ 维护流程与安装能力分开验收。当前 npm CLI 准备文件并打印宿主注册命令;
101
+ 实际宿主安装范围、启用、命令执行以及交互向导仍需各自验证。
102
+ Codex 的生命周期斜杠命令适配是独立工作,已有 Skill ID 不因维护流程改名。
42
103
 
43
- Publishing is a separate step after the package is reviewed:
104
+ 发布审核通过后,发布已经测过的 tarball,先使用试用标签,完成 registry 安装验证后再更新 latest:
44
105
 
45
106
  ```bash
46
- npm publish /path/to/givolt-i3-composer-agent-0.6.12.tgz --access public
107
+ npm publish /absolute/path/to/reviewed-package.tgz --access public --tag next
108
+ npm dist-tag add @givolt/i3-composer-agent@<reviewed-version> latest
47
109
  ```
48
110
 
49
- Verify the registry version, integrity, and a fresh registry install after
50
- publication. A local tarball test does not prove registry publication or actual
51
- model execution of a command.
111
+ 每个已发布包名和版本只用一次。GitHub Actions 与 GSD 品牌仓库一样先保持关闭,
112
+ 适配并验证 CI 后再启用。当前验证范围见 [VALIDATION.md](VALIDATION.md)。
@@ -0,0 +1,9 @@
1
+ # Attribution
2
+
3
+ i3 Composer Agent is a branded distribution of
4
+ [Agent Skills](https://github.com/addyosmani/agent-skills) by Addy Osmani.
5
+ The initial distribution is based on upstream commit `1401c8b` (version 0.6.12).
6
+
7
+ The original MIT license and copyright notice are preserved in LICENSE.
8
+ Givolt maintains the branding, npm distribution, and bundle preparation CLI.
9
+ The original skill IDs, command workflows, and persona definitions are retained.
@@ -0,0 +1,34 @@
1
+ # Agent 源码与发行 PR
2
+
3
+ 此流程只操作 49780522/i3-composer-agent。GSD 仓库只是维护方法的参考。
4
+
5
+ 源码、修复和重放规则的 PR 以 `agent/source` 为 base,从该分支创建维护分支。
6
+ 保持上游文件的原始名称;本项目分发工具放在 `rebrand/`。
7
+
8
+ ```bash
9
+ git fetch origin
10
+ git worktree add -b fix/my-change ../agent-fix origin/agent/source
11
+ cd ../agent-fix
12
+ node --test scripts/*-test.js scripts/lib/*-test.js rebrand/tests/*.test.cjs
13
+ ```
14
+
15
+ 修改重放规则后,从维护提交另建品牌候选并执行 MAINTENANCE.md 中的完整验证。
16
+ 审查和暂存本次文件后提交,再推送并创建源码 PR:
17
+
18
+ ```bash
19
+ git push -u origin fix/my-change
20
+ gh pr create --base agent/source --head fix/my-change
21
+ ```
22
+
23
+ 源码 PR 合入后,从最新 `agent/source` 建立候选,重放、验证、打包并提交品牌内容。
24
+ 发行 PR 以 `main` 为 base;head 必须包含验证后的品牌快照。
25
+
26
+ 当 main 和源候选历史产生合并冲突时,从 main 创建发行 PR 分支,合入已验证的
27
+ 品牌候选并审查冲突。最终跟踪文件树必须与验证过的候选一致,然后提交:
28
+
29
+ ```bash
30
+ git diff --cached <validated-candidate-commit>
31
+ ```
32
+
33
+ 上述差异应为空。若合并中需要实际代码调整,重新验证调整后的发行内容和 tarball。
34
+ 不要把原始 agent/source 直接作为发行 PR 的 head;不向 upstream 推送品牌提交。
@@ -0,0 +1,33 @@
1
+ # Agent 项目维护流程统一
2
+
3
+ 用户已确认借鉴 GSD 项目维护方式,并要求项目互相独立。本规范只约束 Agent 仓库。
4
+
5
+ ## 目标与边界
6
+
7
+ - `upstream` 指向 addyosmani/agent-skills;`origin` 指向私有品牌仓库 49780522/i3-composer-agent。
8
+ - `agent/source` 保留上游原始名称和源码,在 `rebrand/` 保存本项目的分发工具与转换规则。
9
+ - 候选从 `agent/source` 创建,在候选中重放;`main` 保存经过验证的品牌快照。
10
+ - GSD 仓库只读,不使用它的源码、分支或安装产物。
11
+ - Skill ID、流程命令内容、Persona、参考文件和 MIT 来源署名保持既有契约。
12
+ - 本次完善源码同步和品牌重放;交互安装向导、Codex 命令适配及新的 npm 发布独立处理。
13
+
14
+ ## 实现与命令
15
+
16
+ 转换入口保持 Node.js 20+ 和零依赖:`node rebrand/apply.cjs --apply|--check`;另提供与 GSD 相同形式的 `bash rebrand/rebrand.sh` 入口。
17
+
18
+ 上游基线由 `node rebrand/record-upstream.cjs <明确的 ref>` 从 Git 对象记录;源码分支上运行 `node --test rebrand/tests/*.test.cjs`。候选执行 `npm run validate`、`npm test` 和既有路由/hook 检查。
19
+
20
+ 代码沿用 CommonJS、Node 内置 fs/path/assert。分发专属文件放在 `rebrand/overlay/`,发行包配置由模板生成,不把 branded README 或清单混入源码分支。
21
+
22
+ ## 验收条件
23
+
24
+ 1. 从干净上游源码及本项目规则能生成完整品牌包,重复重放不产生差异。
25
+ 2. 在 `agent/source` 上执行写入式重放必须失败且不修改源码。
26
+ 3. 记录上游新增 Skill/Command 后能生成相应清单和说明;删失或改写已记录 Skill ID 时校验失败。
27
+ 4. 上游的 25 个 Skill frontmatter、24 个非元 Skill、4 个 Persona、7 个共享参考保持原始内容;9 个流程只变品牌命名空间。
28
+ 5. 本地候选通过完整测试,并从打包结果运行 CLI 与校验。
29
+ 6. 独立品牌仓库中的源/发行分支及远程关系正确;首次发布标签保持原来的 0.6.12 提交。
30
+
31
+ ## 执行顺序
32
+
33
+ 先为源到品牌的重放、源码保护、目录增长与完整性写失败测试;实现工具及模板生成;验证真实上游归档;提交源分支;生成并审查品牌候选;最后建立远程并同步分支。
@@ -0,0 +1,28 @@
1
+ # Agent 维护流程验收合同
2
+
3
+ 本合同对应 UNIFICATION-SPEC.md;完整执行记录写入每次的审计或发布目录。
4
+
5
+ ## 源维护
6
+
7
+ - 上游 ref 是明确的 Git 提交;目录与 frontmatter 从该 ref 的对象记录。
8
+ - `agent/source` 的上游文件保留原始名称与内容;本项目新增文件限定在 rebrand/。
9
+ - 重放在源码分支上写入前失败。
10
+ - 重放规则改变时,运行 rebrand/tests/pipeline.test.cjs 并验证源候选到品牌的完整路径。
11
+
12
+ ## 品牌候选
13
+
14
+ - Skill、命令、Persona、references 和全部已记录普通运行文件与上游目录一致。
15
+ - Skill frontmatter 的名称和字段内容保持;换行差异不作为字段变更。
16
+ - 新增上游 Skill/Command 能进入目录、品牌说明和打包;缺失内容会失败。
17
+ - 文字补丁失去匹配时必须失败,不能静默遗漏。
18
+ - 重放后 --check 成功,完整 Node、路由与 hook 检查通过。
19
+ - npm package、插件和市场身份一致;六份版本一致。
20
+ - 品牌修复能独立递增发行版本,保留上游目录记录原文;包来源元数据与该记录一致。
21
+ - README、CLI 帮助和准备提示显示品牌版本与上游基线;--version 保持单一品牌版本输出。
22
+ - 打包后从压缩包重新验证完整布局、CLI 和宿主清单。
23
+
24
+ ## 证据边界
25
+
26
+ 源或包校验不等于 registry 发布,也不等于实际宿主启用和模型执行。
27
+ 英文确定性 TF-IDF 路由分数不能代替中文或真实模型行为验收。
28
+ 安装向导、宿主范围、升级和移除沿用安装遗漏审计的独立验收项。
@@ -0,0 +1,32 @@
1
+ # i3 Composer Agent 版本规则
2
+
3
+ 本规则已经用户确认,仅适用于 i3 Composer Agent 仓库。
4
+
5
+ | 信息 | 权威来源 | 本次确认值 |
6
+ | --- | --- | --- |
7
+ | i3 发行版本 | brand.json 的 releaseVersion | 1.0.0 |
8
+ | 上游版本 | upstream.json 的 upstreamVersion | 0.6.12 |
9
+ | 上游提交 | upstream.json 的 upstreamCommit | 1401c8b8030e023baeebb31781a6653fe8e93026 |
10
+
11
+ 品牌版本采用独立的 major.minor.patch 编号。1.0.0 是这套编号的起点;
12
+ 兼容的 i3 修复递增 patch,兼容的新功能递增 minor,需要使用者调整用法的变更递增 major。
13
+ 实际同步上游并审查选定提交后,才用 record-upstream.cjs 更新上游版本与提交。
14
+ 上游版本变动不自动决定品牌版本;品牌版本根据本次发行的实际变化确定。
15
+
16
+ 重放将品牌版本写入 npm 包和插件清单,把上游版本、提交及仓库写入
17
+ package.json 的 i3ComposerAgent 来源字段。package.template.json 不保存发行版本,
18
+ 发行版本统一由 brand.json 生成。
19
+
20
+ README、CLI 帮助和准备完成提示显示:
21
+
22
+ ```text
23
+ i3 Composer Agent v1.0.0
24
+ Based on agent-skills v0.6.12
25
+ ```
26
+
27
+ 在上述例子中,--version 输出 1.0.0,保留脚本使用的单一版本格式。--dry-run 的 JSON
28
+ 分别提供 version、upstreamVersion、upstreamCommit。
29
+
30
+ 已经发布的 @givolt/i3-composer-agent@0.6.12 及其 Git 标签作为历史记录保留。
31
+ 之前的 0.6.13 是未发布候选,新的独立版本候选替代它。
32
+ 1.0.0 的源码与打包验证不代表 npm 发布;发布仍按 MAINTENANCE.md 单独执行。
package/rebrand/apply.cjs CHANGED
@@ -3,19 +3,37 @@
3
3
 
4
4
  const fs = require('node:fs');
5
5
  const path = require('node:path');
6
+ const { spawnSync } = require('node:child_process');
7
+ const { validateCatalog, frontmatter } = require('./catalog.cjs');
6
8
  const brand = require('./brand.json');
9
+ const upstream = require('./upstream.json');
7
10
  const root = path.resolve(__dirname, '..');
8
11
  const mode = process.argv[2];
9
12
  if (!['--apply', '--check'].includes(mode)) {
10
13
  console.error('Usage: node rebrand/apply.cjs --apply | --check');
11
14
  process.exit(1);
12
15
  }
16
+ if (mode === '--apply') {
17
+ const branch = spawnSync('git', ['-C', root, 'branch', '--show-current'], { encoding: 'utf8' });
18
+ if (branch.status === 0 && branch.stdout.trim() === brand.sourceBranch) {
19
+ console.error(`Refusing to transform the source branch ${brand.sourceBranch}; create a release candidate first.`);
20
+ process.exit(1);
21
+ }
22
+ }
23
+ const inventory = validateCatalog(root, upstream);
24
+ const packageFile = path.join(root, 'package.json');
25
+ if (fs.existsSync(packageFile) && JSON.parse(fs.readFileSync(packageFile, 'utf8')).name !== brand.npmName) {
26
+ console.error('Upstream package.json now exists; explicitly adapt the distribution overlay before replaying.');
27
+ process.exit(1);
28
+ }
13
29
  const changes = new Map();
14
30
  function update(relative, content) {
15
31
  const file = path.join(root, relative);
16
32
  if (!fs.existsSync(file) || fs.readFileSync(file, 'utf8') !== content) changes.set(relative, content);
17
33
  }
18
34
  function transform(content, relative) {
35
+ const head = /^skills\/[^/]+\/SKILL\.md$/.test(relative) ? frontmatter(content) : '';
36
+ content = content.slice(head.length);
19
37
  // Agent Skills is also the name of an independent specification.
20
38
  const standard = '__I3_AGENT_SKILLS_STANDARD__';
21
39
  let result = content
@@ -56,10 +74,10 @@ function transform(content, relative) {
56
74
  .replace(/\[[^\]]+\]\(https:\/\/github\.com\/addyosmani\/agent-skills\/issues\/(\d+)\)/g,
57
75
  (_, issue) => `[upstream agent-skills #${issue}](https://github.com/addyosmani/agent-skills/issues/${issue})`);
58
76
  }
59
- return result;
77
+ return head + result;
60
78
  }
61
79
  const skipDirectories = new Set(['.git', 'node_modules', 'rebrand', 'dist', 'tests', 'bin']);
62
- const skipFiles = new Set(['LICENSE', 'NOTICE.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']);
80
+ 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']);
63
81
  function walk(directory, prefix = '') {
64
82
  for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
65
83
  const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
@@ -72,6 +90,13 @@ function walk(directory, prefix = '') {
72
90
  }
73
91
  }
74
92
  walk(root);
93
+ for (const patch of require('./fork-patches.json')) {
94
+ if (path.isAbsolute(patch.file) || patch.file.split(/[\\/]/).includes('..')) throw new Error('Patch must stay inside this Agent repository.');
95
+ const original = (changes.get(patch.file) ?? fs.readFileSync(path.join(root, patch.file), 'utf8')).replace(/\r\n/g, '\n');
96
+ if (original.includes(patch.to)) continue;
97
+ if (!original.includes(patch.from)) throw new Error(`Upstream changed ${patch.file}; review the fork patch before proceeding.`);
98
+ update(patch.file, original.replace(patch.from, patch.to));
99
+ }
75
100
  const comparisonPath = 'docs/comparison.md';
76
101
  const comparison = fs.readFileSync(path.join(root, comparisonPath), 'utf8');
77
102
  if (!comparison.includes('> **Upstream research:**')) {
@@ -81,15 +106,16 @@ if (!comparison.includes('> **Upstream research:**')) {
81
106
  for (const relative of ['plugin.json', '.claude-plugin/plugin.json', '.codex-plugin/plugin.json']) {
82
107
  const manifest = JSON.parse(fs.readFileSync(path.join(root, relative), 'utf8'));
83
108
  manifest.name = brand.name;
109
+ manifest.version = brand.releaseVersion;
84
110
  manifest.description = brand.description;
85
111
  if (manifest.author) manifest.author = { name: brand.maintainer };
86
- delete manifest.homepage;
87
- delete manifest.repository;
112
+ manifest.homepage = brand.repository;
113
+ manifest.repository = brand.repository;
88
114
  if (manifest.interface) {
89
115
  manifest.interface.displayName = brand.displayName;
90
116
  manifest.interface.developerName = brand.maintainer;
91
117
  manifest.interface.shortDescription = 'Engineering workflow skills';
92
- manifest.interface.longDescription = `${brand.displayName} packages 25 engineering skills from Addy Osmani's Agent Skills, covering requirements, planning, implementation, testing, review, and shipping.`;
118
+ 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.`;
93
119
  manifest.interface.defaultPrompt = manifest.interface.defaultPrompt.map(prompt => prompt.replaceAll('Agent Skills', brand.displayName));
94
120
  }
95
121
  update(relative, JSON.stringify(manifest, null, 2) + '\n');
@@ -97,25 +123,72 @@ for (const relative of ['plugin.json', '.claude-plugin/plugin.json', '.codex-plu
97
123
  for (const relative of ['.claude-plugin/marketplace.json', '.agents/plugins/marketplace.json']) {
98
124
  const manifest = JSON.parse(fs.readFileSync(path.join(root, relative), 'utf8'));
99
125
  manifest.name = brand.name;
126
+ if (manifest.version) manifest.version = brand.releaseVersion;
100
127
  if (manifest.description) manifest.description = brand.description;
101
128
  if (manifest.owner) manifest.owner = { name: brand.maintainer };
102
129
  if (manifest.interface) manifest.interface.displayName = brand.displayName;
103
130
  for (const plugin of manifest.plugins) {
104
131
  plugin.name = brand.name;
132
+ plugin.version = brand.releaseVersion;
105
133
  plugin.description = brand.description;
106
134
  plugin.source = relative.startsWith('.agents/') ? { source: 'local', path: './' } : './';
107
- delete plugin.homepage;
135
+ plugin.homepage = brand.repository;
108
136
  }
109
137
  update(relative, JSON.stringify(manifest, null, 2) + '\n');
110
138
  }
139
+ function overlay(directory, prefix = '') {
140
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
141
+ const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
142
+ if (relative === '.github' && !fs.existsSync(path.join(root, '.github'))) continue;
143
+ if (entry.isDirectory()) overlay(path.join(directory, entry.name), relative);
144
+ else if (entry.isFile()) update(relative, fs.readFileSync(path.join(directory, entry.name), 'utf8'));
145
+ else throw new Error(`Unsupported distribution overlay entry: ${relative}`);
146
+ }
147
+ }
148
+ overlay(path.join(__dirname, 'overlay'));
149
+ const ignoreFile = path.join(root, '.gitignore');
150
+ if (fs.existsSync(ignoreFile)) {
151
+ const original = fs.readFileSync(ignoreFile, 'utf8');
152
+ const entries = new Set(original.split(/\r?\n/));
153
+ const missing = ['dist/', '*.tgz', `${brand.name}/`].filter(entry => !entries.has(entry));
154
+ if (missing.length) update('.gitignore', original.replace(/\n?$/, '\n') + missing.join('\n') + '\n');
155
+ }
156
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.template.json'), 'utf8'));
157
+ pkg.name = brand.npmName;
158
+ pkg.version = brand.releaseVersion;
159
+ pkg.repository = { type: 'git', url: brand.repository + '.git' };
160
+ pkg.homepage = brand.repository + '#readme';
161
+ pkg.i3ComposerAgent.upstreamVersion = upstream.upstreamVersion;
162
+ pkg.i3ComposerAgent.upstreamCommit = upstream.upstreamCommit;
163
+ pkg.i3ComposerAgent.upstreamRepository = brand.upstream;
164
+ update('package.json', JSON.stringify(pkg, null, 2) + '\n');
165
+ update('NOTICE.md', fs.readFileSync(path.join(__dirname, 'NOTICE.md'), 'utf8'));
166
+ function description(relative) {
167
+ const head = fs.readFileSync(path.join(root, relative), 'utf8').match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1] || '';
168
+ return (head.match(/^description:\s*(.*)$/m)?.[1] || '').replace(/^["']|["']$/g, '').replaceAll('|', '\\|');
169
+ }
170
+ const values = {
171
+ RELEASE_VERSION: brand.releaseVersion,
172
+ UPSTREAM_VERSION: upstream.upstreamVersion,
173
+ SKILL_COUNT: inventory.skills.length,
174
+ COMMAND_COUNT: inventory.commands.length,
175
+ 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'),
178
+ };
111
179
  for (const [relative, template] of [
112
180
  ['README.md', 'README.md'], ['docs/codex-setup.md', 'codex-setup.md'], ['docs/codebuddy-setup.md', 'codebuddy-setup.md'],
113
181
  ]) {
114
- update(relative, fs.readFileSync(path.join(__dirname, 'templates', template), 'utf8'));
182
+ const content = fs.readFileSync(path.join(__dirname, 'templates', template), 'utf8').replace(/\{\{([A-Z_]+)\}\}/g, (_, key) => {
183
+ if (!(key in values)) throw new Error(`Unknown template variable: ${key}`);
184
+ return values[key];
185
+ });
186
+ update(relative, content);
115
187
  }
116
188
  if (mode === '--apply') for (const [relative, content] of changes) {
117
189
  fs.mkdirSync(path.dirname(path.join(root, relative)), { recursive: true });
118
190
  fs.writeFileSync(path.join(root, relative), content);
119
191
  }
192
+ if (mode === '--apply') fs.chmodSync(path.join(root, 'bin/i3-composer-agent.cjs'), 0o755);
120
193
  console.log(JSON.stringify({ mode, changedFiles: [...changes.keys()] }, null, 2));
121
194
  if (mode === '--check' && changes.size) process.exitCode = 1;
@@ -4,6 +4,10 @@
4
4
  "npmName": "@givolt/i3-composer-agent",
5
5
  "maintainer": "Givolt",
6
6
  "upstream": "https://github.com/addyosmani/agent-skills",
7
- "upstreamCommit": "1401c8b",
8
- "description": "Production-grade engineering skills for AI coding agents covering the development lifecycle from spec to ship."
7
+ "upstreamCommit": "1401c8b8030e023baeebb31781a6653fe8e93026",
8
+ "description": "Production-grade engineering skills for AI coding agents covering the development lifecycle from spec to ship.",
9
+ "repository": "https://github.com/49780522/i3-composer-agent",
10
+ "sourceBranch": "agent/source",
11
+ "releaseBranch": "main",
12
+ "releaseVersion": "1.0.0"
9
13
  }
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const crypto = require('node:crypto');
6
+ const assert = require('node:assert/strict');
7
+ const runtimeRoots = ['skills', 'references', 'agents', 'commands', '.claude/commands', '.gemini/commands', 'hooks', '.claude-plugin', '.codex-plugin', '.agents/plugins'];
8
+ const runtimeFiles = ['plugin.json', 'LICENSE'];
9
+
10
+ function frontmatter(content) {
11
+ const match = content.match(/^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/);
12
+ assert.ok(match, 'Skill must have YAML frontmatter');
13
+ return match[0];
14
+ }
15
+ function describe(files, read) {
16
+ files = files.filter(file => path.basename(file) !== '.DS_Store');
17
+ const select = pattern => files.filter(name => pattern.test(name)).sort();
18
+ const skillFiles = select(/^skills\/[^/]+\/SKILL\.md$/);
19
+ const skillFrontmatter = {};
20
+ for (const file of skillFiles) {
21
+ const id = file.split('/')[1];
22
+ const head = frontmatter(read(file));
23
+ const name = head.match(/^name:\s*["']?([^\s"']+)["']?\s*$/m)?.[1];
24
+ assert.equal(name, id, `${file}: unstable skill identifier`);
25
+ skillFrontmatter[id] = crypto.createHash('sha256').update(head.replace(/\r\n/g, '\n')).digest('hex');
26
+ }
27
+ return {
28
+ skills: skillFiles.map(file => file.split('/')[1]),
29
+ commands: select(/^\.claude\/commands\/[^/]+\.md$/).map(file => path.basename(file, '.md')),
30
+ personas: select(/^agents\/[^/]+\.md$/).map(file => path.basename(file, '.md')),
31
+ references: select(/^references\/[^/]+\.md$/),
32
+ runtimeFiles: files.filter(file => runtimeFiles.includes(file) || runtimeRoots.some(root => file.startsWith(root + '/'))).sort(),
33
+ skillFrontmatter,
34
+ };
35
+ }
36
+ function catalog(root) {
37
+ const files = [];
38
+ function walk(directory, prefix) {
39
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
40
+ const relative = `${prefix}/${entry.name}`;
41
+ if (entry.isDirectory()) walk(path.join(directory, entry.name), relative);
42
+ else if (entry.isFile()) files.push(relative);
43
+ }
44
+ }
45
+ for (const name of runtimeRoots) if (fs.existsSync(path.join(root, name))) walk(path.join(root, name), name);
46
+ for (const name of runtimeFiles) if (fs.existsSync(path.join(root, name))) files.push(name);
47
+ return describe(files, file => fs.readFileSync(path.join(root, file), 'utf8'));
48
+ }
49
+ function validateCatalog(root, expected) {
50
+ const actual = catalog(root);
51
+ for (const field of ['skills', 'commands', 'personas', 'references', 'runtimeFiles', 'skillFrontmatter']) {
52
+ assert.deepEqual(actual[field], expected[field], `upstream preservation failed: ${field}`);
53
+ }
54
+ return actual;
55
+ }
56
+ module.exports = { runtimeRoots, runtimeFiles, frontmatter, describe, catalog, validateCatalog };
@@ -0,0 +1,12 @@
1
+ [
2
+ {
3
+ "file": "scripts/validate-versions.js",
4
+ "from": "const manifestPaths = [\n \"plugin.json\",",
5
+ "to": "const manifestPaths = [\n \"package.json\",\n \"plugin.json\","
6
+ },
7
+ {
8
+ "file": "scripts/validate-versions-test.js",
9
+ "from": "const manifestPaths = [\n \"plugin.json\",",
10
+ "to": "const manifestPaths = [\n \"package.json\",\n \"plugin.json\","
11
+ }
12
+ ]