@jaybeeuu/agent-cortex 1.37.1 → 1.39.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 (135) hide show
  1. package/.agents/skills/style-versioning/SKILL.md +9 -8
  2. package/.github/workflows/ci.yml +190 -8
  3. package/AGENTS.md +27 -6
  4. package/CHANGELOG.md +72 -0
  5. package/README.md +156 -39
  6. package/agents/README.md +49 -13
  7. package/agents/plan/agent.md +51 -0
  8. package/agents/plan/claude/frontmatter.json +6 -0
  9. package/agents/plan/copilot/frontmatter.json +6 -0
  10. package/agents/plan/pi/frontmatter.json +6 -0
  11. package/agents/plan.agent.md +3 -0
  12. package/agents/ralph/agent.md +262 -0
  13. package/agents/ralph/claude/frontmatter.json +6 -0
  14. package/agents/ralph/claude/polling.md +31 -0
  15. package/agents/ralph/copilot/frontmatter.json +6 -0
  16. package/agents/ralph/copilot/polling.md +62 -0
  17. package/agents/ralph/pi/frontmatter.json +6 -0
  18. package/agents/ralph/pi/polling.md +74 -0
  19. package/agents/ralph-plan/agent.md +114 -0
  20. package/agents/ralph-plan/claude/frontmatter.json +6 -0
  21. package/agents/ralph-plan/copilot/frontmatter.json +6 -0
  22. package/agents/ralph-plan/pi/frontmatter.json +6 -0
  23. package/agents/ralph-plan.agent.md +6 -3
  24. package/agents/ralph.agent.md +83 -57
  25. package/agents/strategy/agent.md +104 -0
  26. package/agents/strategy/claude/frontmatter.json +6 -0
  27. package/agents/strategy/copilot/frontmatter.json +6 -0
  28. package/agents/strategy/pi/frontmatter.json +6 -0
  29. package/agents/strategy.agent.md +3 -0
  30. package/bin/agent-cortex.mjs +76 -1
  31. package/bin/installers/claude.mjs +376 -0
  32. package/bin/installers/copilot.mjs +129 -0
  33. package/bin/installers/pi.mjs +197 -0
  34. package/claude/.claude-plugin/plugin.json +1 -1
  35. package/claude/agents/plan.md +3 -1
  36. package/claude/agents/ralph-plan.md +6 -4
  37. package/claude/agents/strategy.md +3 -1
  38. package/claude/hooks/scripts/notify.mjs +54 -0
  39. package/claude/hooks.json +15 -0
  40. package/docs/claude-hooks.md +115 -0
  41. package/docs/ideas/auto-model-selection.md +75 -0
  42. package/docs/ideas/extension-manifests.md +75 -0
  43. package/docs/ideas/improve-pi-system-prompt.md +34 -0
  44. package/docs/ideas/model-selector.md +40 -0
  45. package/docs/ideas/pi-task-memory.md +34 -0
  46. package/docs/ideas/qa-extension.md +43 -0
  47. package/docs/ideas/record-ideas-as-beads.md +115 -0
  48. package/docs/ideas/subagent-visibility.md +72 -0
  49. package/docs/ideas/task-session-context.md +89 -0
  50. package/docs/ideas/unified-ci-pipeline.md +28 -0
  51. package/docs/research/skill-design-research.md +4 -4
  52. package/docs/research/skill-improvements-analysis.md +1 -1
  53. package/docs/skills/skill-anatomy.md +47 -2
  54. package/docs/technical-direction/migration-execution-labels.md +132 -0
  55. package/extensions/agent-modes/README.md +54 -0
  56. package/extensions/agent-modes/discover.test.ts +241 -0
  57. package/extensions/agent-modes/discover.ts +458 -0
  58. package/extensions/agent-modes/index.ts +13 -224
  59. package/extensions/agent-modes/node_modules/.bin/pi +21 -0
  60. package/extensions/agent-modes/node_modules/.bin/tsc +21 -0
  61. package/extensions/agent-modes/node_modules/.bin/tsserver +21 -0
  62. package/extensions/agent-modes/node_modules/.bin/tsx +21 -0
  63. package/extensions/agent-modes/package.json +20 -0
  64. package/extensions/agent-modes/tsconfig.json +12 -0
  65. package/extensions/subagent/README.md +19 -0
  66. package/extensions/subagent/index.ts +131 -45
  67. package/extensions/subagent/node_modules/.bin/pi +21 -0
  68. package/extensions/subagent/node_modules/.bin/pi-ai +21 -0
  69. package/extensions/subagent/node_modules/.bin/tsc +21 -0
  70. package/extensions/subagent/node_modules/.bin/tsserver +21 -0
  71. package/extensions/subagent/node_modules/.bin/tsx +21 -0
  72. package/extensions/subagent/package.json +23 -0
  73. package/extensions/subagent/tsconfig.json +12 -0
  74. package/extensions/subagent/wait.test.ts +202 -0
  75. package/extensions/subagent/wait.ts +118 -0
  76. package/hooks/claude/hooks.json +38 -0
  77. package/hooks/claude/scripts/notify.mjs +54 -0
  78. package/lib/cli.mjs +68 -3
  79. package/package.json +6 -4
  80. package/plugin.json +1 -1
  81. package/pnpm-workspace.yaml +1 -1
  82. package/scripts/build-copilot-agents.mjs +30 -0
  83. package/scripts/lib/compose-agent.mjs +241 -0
  84. package/skills/engineering/design-an-interface/SKILL.md +78 -58
  85. package/skills/engineering/grill-with-docs/REFERENCE.md +78 -0
  86. package/skills/engineering/grill-with-docs/SKILL.md +66 -63
  87. package/skills/engineering/improve-codebase-architecture/SKILL.md +66 -48
  88. package/skills/engineering/technical-direction/FORMAT.md +38 -0
  89. package/skills/engineering/technical-direction/SKILL.md +70 -77
  90. package/skills/planning/classify-bead/SKILL.md +59 -35
  91. package/skills/planning/create-task/REFERENCE.md +40 -0
  92. package/skills/planning/create-task/SKILL.md +70 -66
  93. package/skills/planning/create-task/scripts/create-chores.ts +6 -1
  94. package/skills/planning/create-task/scripts/node_modules/.bin/tsc +21 -0
  95. package/skills/planning/create-task/scripts/node_modules/.bin/tsserver +21 -0
  96. package/skills/planning/create-task/scripts/node_modules/.bin/tsx +21 -0
  97. package/skills/planning/prd-to-tasks/REFERENCE.md +66 -0
  98. package/skills/planning/prd-to-tasks/SKILL.md +86 -170
  99. package/skills/planning/record-idea/FORMAT.md +44 -0
  100. package/skills/planning/record-idea/SKILL.md +70 -53
  101. package/skills/planning/request-refactor-plan/FORMAT.md +45 -0
  102. package/skills/planning/request-refactor-plan/SKILL.md +57 -46
  103. package/skills/planning/write-a-prd/FORMAT.md +54 -0
  104. package/skills/planning/write-a-prd/SKILL.md +49 -48
  105. package/skills/planning/write-a-ticket/SKILL.md +68 -53
  106. package/skills/productivity/handoff/SKILL.md +12 -0
  107. package/skills/productivity/init-beads/SKILL.md +48 -40
  108. package/skills/productivity/validate-agent-dir/scripts/node_modules/.bin/tsc +21 -0
  109. package/skills/productivity/validate-agent-dir/scripts/node_modules/.bin/tsserver +21 -0
  110. package/skills/productivity/validate-agent-dir/scripts/node_modules/.bin/tsx +21 -0
  111. package/skills/productivity/write-a-skill/SKILL.md +2 -1
  112. package/skills/review/maintain-agent-docs/SKILL.md +71 -51
  113. package/skills/workflow/hitl-collab/SKILL.md +86 -17
  114. package/skills/workflow/ralph/REFERENCE.md +24 -19
  115. package/skills/workflow/ralph/SKILL.md +88 -74
  116. package/skills/workflow/run-pipeline-stage/REFERENCE.md +22 -0
  117. package/skills/workflow/run-pipeline-stage/SKILL.md +91 -93
  118. package/skills/workflow/run-pipeline-stage/scripts/generate-progress.test.ts +6 -107
  119. package/skills/workflow/run-pipeline-stage/scripts/generate-progress.ts +4 -2
  120. package/skills/workflow/run-pipeline-stage/scripts/node_modules/.bin/tsc +21 -0
  121. package/skills/workflow/run-pipeline-stage/scripts/node_modules/.bin/tsserver +21 -0
  122. package/skills/workflow/run-pipeline-stage/scripts/node_modules/.bin/tsx +21 -0
  123. package/skills/workflow/using-agent-skills/SKILL.md +122 -0
  124. package/test/claude-installer.test.mjs +248 -0
  125. package/test/cli.test.mjs +115 -2
  126. package/test/copilot-installer.test.mjs +298 -0
  127. package/test/installer.test.mjs +179 -0
  128. package/test/notify-hook.test.mjs +63 -0
  129. package/test/pi-installer.test.mjs +371 -0
  130. package/token-map.README.md +130 -0
  131. package/token-map.json +106 -0
  132. package/.github/workflows/ci.test.ts +0 -217
  133. package/.github/workflows/release.yml +0 -58
  134. package/scripts/build-claude-agents.mjs +0 -179
  135. package/scripts/publish-release.sh +0 -32
@@ -1,217 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { readFileSync, existsSync } from "node:fs";
4
- import { join } from "node:path";
5
- import { parse as parseYaml } from "yaml";
6
-
7
- const ROOT = join(import.meta.dirname, "..", "..");
8
-
9
- function loadYaml(relPath: string): Record<string, unknown> {
10
- const fullPath = join(ROOT, relPath);
11
- assert.ok(existsSync(fullPath), `${relPath} does not exist`);
12
- const content = readFileSync(fullPath, "utf-8");
13
- return parseYaml(content) as Record<string, unknown>;
14
- }
15
-
16
- function loadJson(relPath: string): Record<string, unknown> {
17
- const fullPath = join(ROOT, relPath);
18
- assert.ok(existsSync(fullPath), `${relPath} does not exist`);
19
- const content = readFileSync(fullPath, "utf-8");
20
- return JSON.parse(content) as Record<string, unknown>;
21
- }
22
-
23
- // ─── CI Workflow ─────────────────────────────────────────────────────────────
24
-
25
- describe("CI workflow", () => {
26
- const ci = loadYaml(".github/workflows/ci.yml") as Record<string, unknown>;
27
-
28
- it("triggers on push to main and pull requests", () => {
29
- const on = ci.on as Record<string, unknown>;
30
- assert.ok(on.push, "has push trigger");
31
- assert.ok("pull_request" in on, "has pull_request trigger");
32
-
33
- const push = on.push as Record<string, unknown>;
34
- assert.deepStrictEqual(push.branches, ["main"]);
35
- });
36
-
37
- it("has a job that runs on ubuntu-latest", () => {
38
- const jobs = ci.jobs as Record<string, unknown>;
39
- const jobNames = Object.keys(jobs);
40
- assert.ok(jobNames.length > 0, "has at least one job");
41
-
42
- const firstJob = jobs[jobNames[0]] as Record<string, unknown>;
43
- assert.equal(firstJob["runs-on"], "ubuntu-latest");
44
- });
45
-
46
- it("checks out code", () => {
47
- const steps = getSteps(ci);
48
- const checkout = steps.find(
49
- (s: Record<string, unknown>) => s.name === "Checkout"
50
- );
51
- assert.ok(checkout, "has Checkout step");
52
- const uses = (checkout as Record<string, unknown>).uses as string;
53
- assert.ok(uses.startsWith("actions/checkout@"), "uses actions/checkout");
54
- });
55
-
56
- it("sets up Node.js", () => {
57
- const steps = getSteps(ci);
58
- const setupNode = steps.find(
59
- (s: Record<string, unknown>) => s.name === "Setup Node"
60
- );
61
- assert.ok(setupNode, "has Setup Node step");
62
- });
63
-
64
- it("sets up pnpm", () => {
65
- const steps = getSteps(ci);
66
- const setupPnpm = steps.find(
67
- (s: Record<string, unknown>) => s.name === "Setup pnpm"
68
- );
69
- assert.ok(setupPnpm, "has Setup pnpm step");
70
- const uses = (setupPnpm as Record<string, unknown>).uses as string;
71
- assert.ok(uses.startsWith("pnpm/action-setup@"), "uses pnpm/action-setup");
72
- });
73
-
74
- it("installs dependencies", () => {
75
- const steps = getSteps(ci);
76
- const install = steps.find(
77
- (s: Record<string, unknown>) => s.name === "Install dependencies"
78
- );
79
- assert.ok(install, "has Install dependencies step");
80
- });
81
-
82
- it("runs lint", () => {
83
- const steps = getSteps(ci);
84
- const lint = steps.find(
85
- (s: Record<string, unknown>) => s.name === "Lint"
86
- );
87
- assert.ok(lint, "has Lint step");
88
- });
89
-
90
- it("runs tests", () => {
91
- const steps = getSteps(ci);
92
- const test = steps.find(
93
- (s: Record<string, unknown>) => s.name === "Test"
94
- );
95
- assert.ok(test, "has Test step");
96
- });
97
-
98
- it("runs build", () => {
99
- const steps = getSteps(ci);
100
- const build = steps.find(
101
- (s: Record<string, unknown>) => s.name === "Build"
102
- );
103
- assert.ok(build, "has Build step");
104
- });
105
- });
106
-
107
- // ─── Publish Workflow ────────────────────────────────────────────────────────
108
-
109
- describe("Publish workflow", () => {
110
- const publish = loadYaml(
111
- ".github/workflows/publish.yml"
112
- ) as Record<string, unknown>;
113
-
114
- it("triggers on release published", () => {
115
- const on = publish.on as Record<string, unknown>;
116
- assert.ok(on.release, "has release trigger");
117
- const release = on.release as Record<string, unknown>;
118
- assert.deepStrictEqual(release.types, ["published"]);
119
- });
120
-
121
- it("has a job that runs on ubuntu-latest", () => {
122
- const jobs = publish.jobs as Record<string, unknown>;
123
- const jobNames = Object.keys(jobs);
124
- assert.ok(jobNames.length > 0, "has at least one job");
125
-
126
- const firstJob = jobs[jobNames[0]] as Record<string, unknown>;
127
- assert.equal(firstJob["runs-on"], "ubuntu-latest");
128
- });
129
-
130
- it("sets up Node 24 with setup-node v6 for OIDC", () => {
131
- const steps = getSteps(publish);
132
- const setupNode = steps.find(
133
- (s: Record<string, unknown>) => s.name === "Setup Node"
134
- );
135
- assert.ok(setupNode, "has Setup Node step");
136
- const uses = (setupNode as Record<string, unknown>).uses as string;
137
- assert.ok(uses.startsWith("actions/setup-node@v6"), "uses setup-node v6");
138
- const with_ = (setupNode as Record<string, unknown>).with as Record<
139
- string,
140
- unknown
141
- >;
142
- assert.equal(with_["node-version"], 24);
143
- assert.equal(
144
- with_["registry-url"],
145
- undefined,
146
- "no registry-url — OIDC auth needs no registry config"
147
- );
148
- });
149
-
150
- it("packs the tarball then publishes with npm", () => {
151
- const steps = getSteps(publish);
152
- const publishStep = steps.find(
153
- (s: Record<string, unknown>) => s.name === "Publish to npm"
154
- );
155
- assert.ok(publishStep, "has Publish to npm step");
156
- const run = (publishStep as Record<string, unknown>).run as string;
157
- assert.ok(run.includes("pnpm pack"), "packs with pnpm");
158
- assert.ok(run.includes("npm publish"), "publishes with npm");
159
- assert.ok(!run.includes("pnpm publish"), "does not publish via pnpm");
160
- });
161
-
162
- it("grants contents: read and id-token: write permissions for OIDC", () => {
163
- const jobs = publish.jobs as Record<string, unknown>;
164
- const jobNames = Object.keys(jobs);
165
- const job = jobs[jobNames[0]] as Record<string, unknown>;
166
- const permissions = job.permissions as Record<string, unknown>;
167
- assert.equal(permissions["contents"], "read");
168
- assert.equal(permissions["id-token"], "write");
169
- });
170
-
171
- it("has no NPM_TOKEN secret or NODE_AUTH_TOKEN auth", () => {
172
- const content = readFileSync(
173
- join(ROOT, ".github/workflows/publish.yml"),
174
- "utf-8"
175
- );
176
- assert.ok(!content.includes("NPM_TOKEN"), "no NPM_TOKEN secret reference");
177
- assert.ok(
178
- !content.includes("NODE_AUTH_TOKEN"),
179
- "no NODE_AUTH_TOKEN auth env"
180
- );
181
- });
182
- });
183
-
184
- // ─── Package.json ────────────────────────────────────────────────────────────
185
-
186
- describe("package.json", () => {
187
- const pkg = loadJson("package.json") as Record<string, unknown>;
188
-
189
- it("is not private (publishable to npm)", () => {
190
- assert.equal(pkg.private, undefined, "package is not private");
191
- });
192
-
193
- it("has a build script", () => {
194
- const scripts = pkg.scripts as Record<string, unknown>;
195
- assert.ok(scripts.build, "has build script");
196
- });
197
-
198
- it("has a lint script", () => {
199
- const scripts = pkg.scripts as Record<string, unknown>;
200
- assert.ok(scripts.lint, "has lint script");
201
- });
202
-
203
- it("has publishConfig with public access", () => {
204
- const publishConfig = pkg.publishConfig as Record<string, unknown>;
205
- assert.ok(publishConfig, "has publishConfig");
206
- assert.equal(publishConfig.access, "public");
207
- });
208
- });
209
-
210
- // ─── Helpers ─────────────────────────────────────────────────────────────────
211
-
212
- function getSteps(workflow: Record<string, unknown>): Record<string, unknown>[] {
213
- const jobs = workflow.jobs as Record<string, unknown>;
214
- const jobNames = Object.keys(jobs);
215
- const firstJob = jobs[jobNames[0]] as Record<string, unknown>;
216
- return firstJob.steps as Record<string, unknown>[];
217
- }
@@ -1,58 +0,0 @@
1
- name: Release
2
-
3
- on:
4
- push:
5
- branches:
6
- - main
7
-
8
- concurrency: ${{ github.workflow }}-${{ github.ref }}
9
-
10
- jobs:
11
- release:
12
- name: Release
13
- runs-on: ubuntu-latest
14
- permissions:
15
- contents: write
16
- pull-requests: write
17
- id-token: write
18
- steps:
19
- - name: Checkout
20
- uses: actions/checkout@v4
21
- with:
22
- fetch-depth: 0
23
-
24
- - name: Setup Node
25
- uses: actions/setup-node@v6
26
- with:
27
- node-version: 24
28
-
29
- - name: Setup pnpm
30
- uses: pnpm/action-setup@v4
31
- with:
32
- version: 10
33
-
34
- - name: Install dependencies
35
- run: pnpm install --frozen-lockfile
36
-
37
- # changesets/action is used ONLY to version packages and open/update
38
- # the "Version Packages" PR. We deliberately do NOT pass a publish
39
- # script here - changesets/action does not propagate the OIDC env vars
40
- # (ACTIONS_ID_TOKEN_REQUEST_URL / _TOKEN) to the spawned npm process,
41
- # so npm publish would fail with ENEEDAUTH. Publishing happens in a
42
- # separate top-level step below where env vars flow through cleanly.
43
- - name: Create Version Packages PR
44
- id: changesets
45
- uses: changesets/action@v1
46
- with:
47
- version: pnpm version-packages
48
- commit: "chore: version packages"
49
- title: "chore: version packages"
50
- env:
51
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52
-
53
- # When there are no changesets (i.e. the "Version Packages" PR was
54
- # merged), changesets/action does nothing - so we publish directly.
55
- # NOTE: @v1 (v1.9.0) exposes the output as 'hasChangesets' (camelCase).
56
- - name: Publish to npm
57
- if: steps.changesets.outputs.hasChangesets == 'false'
58
- run: bash scripts/publish-release.sh
@@ -1,179 +0,0 @@
1
- // Builds the self-contained Claude Code plugin subtree at claude/ from the canonical
2
- // Copilot sources. The Copilot files (agents/*.agent.md) and the grouped skills/ tree
3
- // stay the single source of truth; the claude/ subtree is generated and committed.
4
- // CI runs this and checks `git diff --exit-code claude/` to guarantee it is never stale.
5
- //
6
- // claude/agents/*.md generated from agents/*.agent.md (frontmatter + tools converted)
7
- // claude/skills/<name> symlinks to the grouped skills/<group>/<name> dirs (single source)
8
- //
9
- // Claude only loads agents from the default ./agents/ dir of the plugin root (custom
10
- // `agents` manifest paths are ignored), so the plugin root is claude/ — isolating it
11
- // from the Copilot .agent.md files, which would otherwise load as broken agents.
12
- //
13
- // Run: pnpm build:claude (node scripts/build-claude-agents.mjs)
14
- // Zero dependencies so it runs on the CI Node (20) and local Node alike.
15
-
16
- import { readdirSync, readFileSync, writeFileSync, mkdirSync, rmSync, symlinkSync, statSync, copyFileSync, existsSync } from "node:fs";
17
- import { join, dirname, relative } from "node:path";
18
- import { fileURLToPath } from "node:url";
19
-
20
- const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
21
- const AGENT_SRC = join(ROOT, "agents");
22
- const NATIVE_SRC = join(ROOT, "agents-native");
23
- const SKILL_SRC = join(ROOT, "skills");
24
- const AGENT_OUT = join(ROOT, "claude", "agents");
25
- const SKILL_OUT = join(ROOT, "claude", "skills");
26
-
27
- // Copilot tool name -> Claude tool name. `null` = drop (no Claude equivalent).
28
- const TOOL_MAP = {
29
- bash: "Bash",
30
- view: "Read",
31
- edit: "Edit",
32
- create: "Write",
33
- grep: "Grep",
34
- rg: "Grep",
35
- glob: "Glob",
36
- ask_user: "AskUserQuestion",
37
- web_fetch: "WebFetch",
38
- skill: "Skill",
39
- task: "Task",
40
- read_agent: null, // Claude's Task returns results inline; no separate read step.
41
- };
42
-
43
- // Agents NOT transformed from the Copilot source. `ralph` is authored natively for Claude
44
- // instead (agents-native/ralph.md) — its event-driven orchestration can't be produced by
45
- // token substitution from the poll-loop Copilot source.
46
- const DEFER = new Set(["ralph"]);
47
-
48
- // Ralph-coupled Copilot skills that don't apply to the lean Claude Ralph (and carry dead
49
- // ~/.copilot orchestration paths). Not symlinked into the Claude plugin.
50
- const SKILL_EXCLUDE = new Set(["ralph", "run-pipeline-stage"]);
51
-
52
- const COPILOT_PLUGIN_ROOT = "~/.copilot/installed-plugins/_direct/agent-cortex";
53
- const CLAUDE_PLUGIN_ROOT = "${CLAUDE_PLUGIN_ROOT}";
54
- const NAME_PREFIX = "agent-cortex:";
55
-
56
- function parseFrontmatter(text, file) {
57
- const m = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
58
- if (!m) throw new Error(`${file}: no YAML frontmatter found`);
59
- return { fm: m[1], body: text.slice(m[0].length) };
60
- }
61
-
62
- function scalarLine(fm, key) {
63
- const m = fm.match(new RegExp(`^${key}:[ \\t]*(.+?)[ \\t]*$`, "m"));
64
- return m ? m[1] : undefined;
65
- }
66
-
67
- function stripQuotes(s) {
68
- return s === undefined ? undefined : s.replace(/^["']([\s\S]*)["']$/, "$1");
69
- }
70
-
71
- function mapTools(fm, file) {
72
- const raw = scalarLine(fm, "tools");
73
- if (!raw) throw new Error(`${file}: no tools field`);
74
- let list;
75
- try {
76
- list = JSON.parse(raw);
77
- } catch {
78
- throw new Error(`${file}: tools is not a JSON array: ${raw}`);
79
- }
80
- const out = [];
81
- for (const t of list) {
82
- if (!(t in TOOL_MAP)) throw new Error(`${file}: unknown tool "${t}" — extend TOOL_MAP`);
83
- const mapped = TOOL_MAP[t];
84
- if (mapped && !out.includes(mapped)) out.push(mapped);
85
- }
86
- return out;
87
- }
88
-
89
- // Rewrite harness-specific tokens in the prose body: exact backticked tool names and
90
- // the hardcoded Copilot plugin path. Only touches `\`token\`` so shell snippets like
91
- // `bd create` inside code fences are untouched.
92
- function transformBody(body) {
93
- let out = body;
94
- for (const [copilot, claude] of Object.entries(TOOL_MAP)) {
95
- if (claude) out = out.split("`" + copilot + "`").join("`" + claude + "`");
96
- }
97
- return out.split(COPILOT_PLUGIN_ROOT).join(CLAUDE_PLUGIN_ROOT);
98
- }
99
-
100
- function buildAgents() {
101
- rmSync(AGENT_OUT, { recursive: true, force: true });
102
- mkdirSync(AGENT_OUT, { recursive: true });
103
-
104
- const written = [];
105
- const skipped = [];
106
- for (const file of readdirSync(AGENT_SRC).filter((f) => f.endsWith(".agent.md")).sort()) {
107
- const { fm, body } = parseFrontmatter(readFileSync(join(AGENT_SRC, file), "utf8"), file);
108
-
109
- const rawName = stripQuotes(scalarLine(fm, "name"));
110
- if (!rawName) throw new Error(`${file}: no name field`);
111
- const slug = rawName.startsWith(NAME_PREFIX) ? rawName.slice(NAME_PREFIX.length) : rawName;
112
- if (DEFER.has(slug)) {
113
- skipped.push(slug);
114
- continue;
115
- }
116
-
117
- const description = scalarLine(fm, "description");
118
- if (!description) throw new Error(`${file}: no description field`);
119
-
120
- const header =
121
- `---\n` +
122
- `# GENERATED from agents/${file} by scripts/build-claude-agents.mjs — DO NOT EDIT.\n` +
123
- `name: ${slug}\n` +
124
- `description: ${description}\n` +
125
- `tools: ${mapTools(fm, file).join(", ")}\n` +
126
- `---\n`;
127
- const out = `${header}\n${transformBody(body).replace(/^\r?\n+/, "").replace(/\s*$/, "")}\n`;
128
- writeFileSync(join(AGENT_OUT, `${slug}.md`), out);
129
- written.push(slug);
130
- }
131
- console.log(`Generated ${written.length} Claude agent(s): ${written.join(", ")}`);
132
- if (skipped.length) console.log(`Skipped Copilot source(s): ${skipped.join(", ")}`);
133
- }
134
-
135
- // Copy hand-authored Claude-native agents into claude/agents/ verbatim, alongside the
136
- // generated ones. These have no Copilot source (their bodies differ structurally per harness).
137
- function copyNativeAgents() {
138
- if (!existsSync(NATIVE_SRC)) return;
139
- const copied = [];
140
- for (const file of readdirSync(NATIVE_SRC).filter((f) => f.endsWith(".md")).sort()) {
141
- copyFileSync(join(NATIVE_SRC, file), join(AGENT_OUT, file));
142
- copied.push(file.replace(/\.md$/, ""));
143
- }
144
- if (copied.length) console.log(`Copied ${copied.length} native Claude agent(s): ${copied.join(", ")}`);
145
- }
146
-
147
- // Symlink each grouped skill (skills/<group>/<name>/) into the flat claude/skills/ dir
148
- // so Claude's one-level-deep skill scan discovers them without duplicating content.
149
- function buildSkills() {
150
- rmSync(SKILL_OUT, { recursive: true, force: true });
151
- mkdirSync(SKILL_OUT, { recursive: true });
152
-
153
- const byName = new Map();
154
- for (const group of readdirSync(SKILL_SRC).sort()) {
155
- const groupDir = join(SKILL_SRC, group);
156
- if (!statSync(groupDir).isDirectory()) continue;
157
- for (const name of readdirSync(groupDir).sort()) {
158
- const skillDir = join(groupDir, name);
159
- let hasSkill;
160
- try {
161
- hasSkill = statSync(join(skillDir, "SKILL.md")).isFile();
162
- } catch {
163
- hasSkill = false;
164
- }
165
- if (!hasSkill) continue;
166
- if (SKILL_EXCLUDE.has(name)) continue;
167
- if (byName.has(name)) {
168
- throw new Error(`duplicate skill name "${name}" (${byName.get(name)} and ${group}) — names must be unique when flattened`);
169
- }
170
- byName.set(name, group);
171
- symlinkSync(relative(SKILL_OUT, skillDir), join(SKILL_OUT, name));
172
- }
173
- }
174
- console.log(`Linked ${byName.size} skill(s) into claude/skills/`);
175
- }
176
-
177
- buildAgents();
178
- copyNativeAgents();
179
- buildSkills();
@@ -1,32 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Custom publish script for changesets that uses pnpm pack + npm publish
3
- # This is needed because changeset publish doesn't properly handle OIDC
4
-
5
- set -euo pipefail
6
-
7
- # Check if there are any packages to publish
8
- VERSION=$(node -p "require('./package.json').version")
9
- PACKAGE_NAME=$(node -p "require('./package.json').name")
10
-
11
- echo "Publishing $PACKAGE_NAME@$VERSION"
12
-
13
- # Check if this version is already published
14
- if npm view "$PACKAGE_NAME@$VERSION" version 2>/dev/null; then
15
- echo "Version $VERSION is already published, skipping"
16
- exit 0
17
- fi
18
-
19
- # Pack the tarball
20
- TARBALL=$(pnpm pack | tail -n 1)
21
- echo "Created tarball: $TARBALL"
22
-
23
- # Publish using npm with OIDC trusted publishing.
24
- # NOTE: provenance is disabled because this repo is private - npm's
25
- # trusted publishing auto-enables provenance, but sigstore only supports
26
- # public repos. The masked error would be ENEEDAUTH.
27
- npm publish "$TARBALL" --access public --no-provenance
28
-
29
- echo "✓ Published $PACKAGE_NAME@$VERSION"
30
-
31
- # Create git tag
32
- git tag -a "v$VERSION" -m "Release $PACKAGE_NAME@$VERSION" || echo "Tag already exists"