projectstore-codex 0.0.1 → 0.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/.codex-plugin/plugin.json +48 -0
  2. package/README.md +15 -7
  3. package/bin/projectstore-codex.mjs +88 -0
  4. package/hooks/hooks.json +59 -0
  5. package/node_modules/projectstore/.claude-plugin/marketplace.json +40 -0
  6. package/node_modules/projectstore/.claude-plugin/plugin.json +23 -0
  7. package/node_modules/projectstore/.mcp.json +14 -0
  8. package/node_modules/projectstore/AGENTS.md +26 -0
  9. package/node_modules/projectstore/LICENSE +21 -0
  10. package/node_modules/projectstore/README.md +284 -0
  11. package/node_modules/projectstore/agents/archaeologist.md +76 -0
  12. package/node_modules/projectstore/agents/clerk.md +93 -0
  13. package/node_modules/projectstore/agents/critic.md +94 -0
  14. package/node_modules/projectstore/agents/librarian.md +81 -0
  15. package/node_modules/projectstore/agents/planner.md +80 -0
  16. package/node_modules/projectstore/agents/reviewer.md +98 -0
  17. package/node_modules/projectstore/bin/projectstore.mjs +7 -0
  18. package/node_modules/projectstore/commands/adr.md +57 -0
  19. package/node_modules/projectstore/commands/agents.md +180 -0
  20. package/node_modules/projectstore/commands/bind.md +128 -0
  21. package/node_modules/projectstore/commands/codemap.md +50 -0
  22. package/node_modules/projectstore/commands/concept.md +17 -0
  23. package/node_modules/projectstore/commands/doctor.md +166 -0
  24. package/node_modules/projectstore/commands/epic.md +40 -0
  25. package/node_modules/projectstore/commands/graph.md +56 -0
  26. package/node_modules/projectstore/commands/kanban.md +40 -0
  27. package/node_modules/projectstore/commands/meeting.md +17 -0
  28. package/node_modules/projectstore/commands/reconcile.md +73 -0
  29. package/node_modules/projectstore/commands/research.md +17 -0
  30. package/node_modules/projectstore/commands/review.md +89 -0
  31. package/node_modules/projectstore/commands/runbook.md +17 -0
  32. package/node_modules/projectstore/commands/scaffold.md +23 -0
  33. package/node_modules/projectstore/commands/search.md +22 -0
  34. package/node_modules/projectstore/commands/spec.md +91 -0
  35. package/node_modules/projectstore/commands/status.md +27 -0
  36. package/node_modules/projectstore/commands/statusline.md +46 -0
  37. package/node_modules/projectstore/commands/story.md +113 -0
  38. package/node_modules/projectstore/docs/extending.md +172 -0
  39. package/node_modules/projectstore/docs/getting-started.md +133 -0
  40. package/node_modules/projectstore/docs/harnesses.md +163 -0
  41. package/node_modules/projectstore/docs/how-it-works.md +263 -0
  42. package/node_modules/projectstore/docs/images/loop-light.svg +94 -0
  43. package/node_modules/projectstore/docs/images/loop.svg +93 -0
  44. package/node_modules/projectstore/docs/images/statusline-hud.png +0 -0
  45. package/node_modules/projectstore/docs/images/team-light.svg +79 -0
  46. package/node_modules/projectstore/docs/images/team.svg +79 -0
  47. package/node_modules/projectstore/harnesses/claude-code.json +483 -0
  48. package/node_modules/projectstore/harnesses/codex.json +332 -0
  49. package/node_modules/projectstore/hooks/hooks.json +59 -0
  50. package/node_modules/projectstore/hooks/pre-compact.mjs +121 -0
  51. package/node_modules/projectstore/hooks/session-rules.mjs +63 -0
  52. package/node_modules/projectstore/hooks/session-start.mjs +301 -0
  53. package/node_modules/projectstore/hooks/session-stop.mjs +84 -0
  54. package/node_modules/projectstore/package.json +70 -0
  55. package/node_modules/projectstore/scaffold/checklists.json +88 -0
  56. package/node_modules/projectstore/scaffold/headings.json +171 -0
  57. package/node_modules/projectstore/scaffold/layouts/engineering.json +85 -0
  58. package/node_modules/projectstore/scripts/binding.mjs +165 -0
  59. package/node_modules/projectstore/scripts/build-adapters.mjs +264 -0
  60. package/node_modules/projectstore/scripts/cli.mjs +595 -0
  61. package/node_modules/projectstore/scripts/codemap.mjs +99 -0
  62. package/node_modules/projectstore/scripts/diff-refs.mjs +127 -0
  63. package/node_modules/projectstore/scripts/doctor.mjs +2127 -0
  64. package/node_modules/projectstore/scripts/draft.mjs +261 -0
  65. package/node_modules/projectstore/scripts/graph.mjs +219 -0
  66. package/node_modules/projectstore/scripts/harness.mjs +608 -0
  67. package/node_modules/projectstore/scripts/install-harness.mjs +1387 -0
  68. package/node_modules/projectstore/scripts/kanban.mjs +174 -0
  69. package/node_modules/projectstore/scripts/lib.mjs +3085 -0
  70. package/node_modules/projectstore/scripts/mcp.mjs +391 -0
  71. package/node_modules/projectstore/scripts/portable-registration.mjs +198 -0
  72. package/node_modules/projectstore/scripts/provenance.mjs +375 -0
  73. package/node_modules/projectstore/scripts/query.mjs +490 -0
  74. package/node_modules/projectstore/scripts/reconcile.mjs +422 -0
  75. package/node_modules/projectstore/scripts/statusline-launcher.mjs +141 -0
  76. package/node_modules/projectstore/scripts/statusline.mjs +253 -0
  77. package/node_modules/projectstore/scripts/story-section.mjs +209 -0
  78. package/node_modules/projectstore/scripts/surfaces.mjs +421 -0
  79. package/node_modules/projectstore/scripts/tokens.mjs +449 -0
  80. package/node_modules/projectstore/scripts/touch-session.mjs +336 -0
  81. package/node_modules/projectstore/scripts/version-guard.mjs +255 -0
  82. package/node_modules/projectstore/scripts/worktree.mjs +109 -0
  83. package/node_modules/projectstore/skills/projectstore-decision-detector/SKILL.md +40 -0
  84. package/node_modules/projectstore/skills/projectstore-peer-reviewer/SKILL.md +38 -0
  85. package/node_modules/projectstore/skills/projectstore-story-completion/SKILL.md +50 -0
  86. package/node_modules/projectstore/skills/projectstore-vault-communication/SKILL.md +96 -0
  87. package/node_modules/projectstore/templates/claude-md-block.md.tmpl +26 -0
  88. package/node_modules/projectstore/templates/de/adr.md.tmpl +67 -0
  89. package/node_modules/projectstore/templates/de/concept.md.tmpl +43 -0
  90. package/node_modules/projectstore/templates/de/epic.md.tmpl +59 -0
  91. package/node_modules/projectstore/templates/de/folder-readme.md.tmpl +14 -0
  92. package/node_modules/projectstore/templates/de/kanban.md.tmpl +36 -0
  93. package/node_modules/projectstore/templates/de/meeting.md.tmpl +38 -0
  94. package/node_modules/projectstore/templates/de/research.md.tmpl +47 -0
  95. package/node_modules/projectstore/templates/de/runbook.md.tmpl +53 -0
  96. package/node_modules/projectstore/templates/de/spec.md.tmpl +64 -0
  97. package/node_modules/projectstore/templates/de/story.md.tmpl +76 -0
  98. package/node_modules/projectstore/templates/de/strings.json +6 -0
  99. package/node_modules/projectstore/templates/en/adr.md.tmpl +67 -0
  100. package/node_modules/projectstore/templates/en/concept.md.tmpl +43 -0
  101. package/node_modules/projectstore/templates/en/epic.md.tmpl +59 -0
  102. package/node_modules/projectstore/templates/en/folder-readme.md.tmpl +14 -0
  103. package/node_modules/projectstore/templates/en/kanban.md.tmpl +36 -0
  104. package/node_modules/projectstore/templates/en/meeting.md.tmpl +38 -0
  105. package/node_modules/projectstore/templates/en/research.md.tmpl +47 -0
  106. package/node_modules/projectstore/templates/en/runbook.md.tmpl +53 -0
  107. package/node_modules/projectstore/templates/en/spec.md.tmpl +64 -0
  108. package/node_modules/projectstore/templates/en/story.md.tmpl +76 -0
  109. package/node_modules/projectstore/templates/en/strings.json +6 -0
  110. package/node_modules/projectstore/templates/es/adr.md.tmpl +67 -0
  111. package/node_modules/projectstore/templates/es/concept.md.tmpl +43 -0
  112. package/node_modules/projectstore/templates/es/epic.md.tmpl +59 -0
  113. package/node_modules/projectstore/templates/es/folder-readme.md.tmpl +14 -0
  114. package/node_modules/projectstore/templates/es/kanban.md.tmpl +36 -0
  115. package/node_modules/projectstore/templates/es/meeting.md.tmpl +38 -0
  116. package/node_modules/projectstore/templates/es/research.md.tmpl +47 -0
  117. package/node_modules/projectstore/templates/es/runbook.md.tmpl +53 -0
  118. package/node_modules/projectstore/templates/es/spec.md.tmpl +64 -0
  119. package/node_modules/projectstore/templates/es/story.md.tmpl +76 -0
  120. package/node_modules/projectstore/templates/es/strings.json +6 -0
  121. package/node_modules/projectstore/templates/fr/adr.md.tmpl +67 -0
  122. package/node_modules/projectstore/templates/fr/concept.md.tmpl +43 -0
  123. package/node_modules/projectstore/templates/fr/epic.md.tmpl +59 -0
  124. package/node_modules/projectstore/templates/fr/folder-readme.md.tmpl +14 -0
  125. package/node_modules/projectstore/templates/fr/kanban.md.tmpl +36 -0
  126. package/node_modules/projectstore/templates/fr/meeting.md.tmpl +38 -0
  127. package/node_modules/projectstore/templates/fr/research.md.tmpl +47 -0
  128. package/node_modules/projectstore/templates/fr/runbook.md.tmpl +53 -0
  129. package/node_modules/projectstore/templates/fr/spec.md.tmpl +64 -0
  130. package/node_modules/projectstore/templates/fr/story.md.tmpl +76 -0
  131. package/node_modules/projectstore/templates/fr/strings.json +6 -0
  132. package/node_modules/projectstore/templates/ru/adr.md.tmpl +67 -0
  133. package/node_modules/projectstore/templates/ru/concept.md.tmpl +43 -0
  134. package/node_modules/projectstore/templates/ru/epic.md.tmpl +59 -0
  135. package/node_modules/projectstore/templates/ru/folder-readme.md.tmpl +14 -0
  136. package/node_modules/projectstore/templates/ru/kanban.md.tmpl +36 -0
  137. package/node_modules/projectstore/templates/ru/meeting.md.tmpl +38 -0
  138. package/node_modules/projectstore/templates/ru/research.md.tmpl +47 -0
  139. package/node_modules/projectstore/templates/ru/runbook.md.tmpl +53 -0
  140. package/node_modules/projectstore/templates/ru/spec.md.tmpl +64 -0
  141. package/node_modules/projectstore/templates/ru/story.md.tmpl +76 -0
  142. package/node_modules/projectstore/templates/ru/strings.json +6 -0
  143. package/node_modules/projectstore/templates/zh/adr.md.tmpl +67 -0
  144. package/node_modules/projectstore/templates/zh/concept.md.tmpl +43 -0
  145. package/node_modules/projectstore/templates/zh/epic.md.tmpl +59 -0
  146. package/node_modules/projectstore/templates/zh/folder-readme.md.tmpl +14 -0
  147. package/node_modules/projectstore/templates/zh/kanban.md.tmpl +36 -0
  148. package/node_modules/projectstore/templates/zh/meeting.md.tmpl +38 -0
  149. package/node_modules/projectstore/templates/zh/research.md.tmpl +47 -0
  150. package/node_modules/projectstore/templates/zh/runbook.md.tmpl +53 -0
  151. package/node_modules/projectstore/templates/zh/spec.md.tmpl +64 -0
  152. package/node_modules/projectstore/templates/zh/story.md.tmpl +76 -0
  153. package/node_modules/projectstore/templates/zh/strings.json +6 -0
  154. package/package.json +36 -14
  155. package/plugin.json +53 -0
  156. package/skills/projectstore-adr/SKILL.md +76 -0
  157. package/skills/projectstore-agents/SKILL.md +50 -0
  158. package/skills/projectstore-archaeologist/SKILL.md +109 -0
  159. package/skills/projectstore-bind/SKILL.md +44 -0
  160. package/skills/projectstore-clerk/SKILL.md +126 -0
  161. package/skills/projectstore-codemap/SKILL.md +69 -0
  162. package/skills/projectstore-concept/SKILL.md +36 -0
  163. package/skills/projectstore-critic/SKILL.md +127 -0
  164. package/skills/projectstore-decision-detector/SKILL.md +59 -0
  165. package/skills/projectstore-doctor/SKILL.md +33 -0
  166. package/skills/projectstore-epic/SKILL.md +59 -0
  167. package/skills/projectstore-graph/SKILL.md +75 -0
  168. package/skills/projectstore-kanban/SKILL.md +60 -0
  169. package/skills/projectstore-librarian/SKILL.md +114 -0
  170. package/skills/projectstore-meeting/SKILL.md +36 -0
  171. package/skills/projectstore-peer-reviewer/SKILL.md +57 -0
  172. package/skills/projectstore-planner/SKILL.md +113 -0
  173. package/skills/projectstore-reconcile/SKILL.md +92 -0
  174. package/skills/projectstore-research/SKILL.md +36 -0
  175. package/skills/projectstore-review/SKILL.md +108 -0
  176. package/skills/projectstore-reviewer/SKILL.md +131 -0
  177. package/skills/projectstore-runbook/SKILL.md +36 -0
  178. package/skills/projectstore-scaffold/SKILL.md +42 -0
  179. package/skills/projectstore-search/SKILL.md +41 -0
  180. package/skills/projectstore-spec/SKILL.md +110 -0
  181. package/skills/projectstore-status/SKILL.md +47 -0
  182. package/skills/projectstore-statusline/SKILL.md +29 -0
  183. package/skills/projectstore-story/SKILL.md +132 -0
  184. package/skills/projectstore-story-completion/SKILL.md +69 -0
  185. package/skills/projectstore-vault-communication/SKILL.md +115 -0
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env node
2
+ // Deterministic harness adapter renderer. The source commands, agents and
3
+ // passive skills stay Claude-shaped; emitting harnesses receive a committed
4
+ // tree in their own vocabulary.
5
+
6
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from "node:fs";
7
+ import { dirname, join, relative, resolve } from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+ import { emittingHarnesses, sourceHarness } from "./harness.mjs";
10
+ import { writeFileAtomic } from "./lib.mjs";
11
+
12
+ const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
13
+ const SOURCE = sourceHarness();
14
+ const TARGETS = emittingHarnesses();
15
+ if (TARGETS.length !== 1) throw new Error(`this renderer currently requires exactly one emitting harness; found ${TARGETS.length}`);
16
+ const TARGET = TARGETS[0];
17
+ const OUT = resolve(ROOT, TARGET.output_dir);
18
+
19
+ const read = (p) => readFileSync(p, "utf8");
20
+ const json = (p) => JSON.parse(read(p));
21
+ const files = (dir, suffix = ".md") => readdirSync(dir)
22
+ .filter((name) => name.endsWith(suffix))
23
+ .sort()
24
+ .map((name) => join(dir, name));
25
+
26
+ function frontmatter(text, file) {
27
+ const m = text.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
28
+ if (!m) throw new Error(`${relative(ROOT, file)}: missing YAML frontmatter`);
29
+ const data = {};
30
+ for (const line of m[1].split("\n")) {
31
+ const kv = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
32
+ if (!kv) continue;
33
+ let value = kv[2].trim();
34
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) value = value.slice(1, -1);
35
+ data[kv[1]] = value;
36
+ }
37
+ return { data, body: m[2].replace(/^\n+/, "") };
38
+ }
39
+
40
+ function yamlString(value) {
41
+ return JSON.stringify(String(value).replace(/\s+/g, " ").trim());
42
+ }
43
+
44
+ function runtimePreamble() {
45
+ return `## Runtime path\n\nResolve paths from this skill's own directory, never from the checkout or a\nremembered cache path. The plugin root is two directories above this SKILL.md;\nthe bundled core is \`<plugin-root>/node_modules/projectstore\`. Before running\nany ProjectStore command, export \`PROJECTSTORE_CORE_ROOT\` to that bundled-core\npath in its own shell statement, then use \`node "\${PROJECTSTORE_CORE_ROOT}/…"\`.\nDo not prefix the command with the assignment: a shell expands the quoted path\nbefore that inline assignment takes effect. If the bundled core is missing, stop\nand report a broken plugin install; do not fetch a different version from npm.\n\n## User arguments\n\nThe source command's host-substituted argument token is rendered here as\n\`<user-arguments>\` (or \`<user-arguments-without-fix>\`). Before executing a\nshown command, replace that token with the actual arguments from the user's\nrequest and shell-quote values safely. Never pass the angle-bracket token\nliterally and never treat it as a shell variable.\n`;
46
+ }
47
+
48
+ function rewriteBody(body) {
49
+ const variable = (name) => "${" + name + "}";
50
+ const sourceRoot = variable(SOURCE.runtime.plugin_root_env);
51
+ const sourceProject = SOURCE.runtime.project_dir_env ? variable(SOURCE.runtime.project_dir_env) : null;
52
+ const sourceWord = SOURCE.id.split("-")[0];
53
+ return body
54
+ .replaceAll("$ARGUMENTS_WITHOUT_FIX", "<user-arguments-without-fix>")
55
+ .replaceAll("$ARGUMENTS", "<user-arguments>")
56
+ .replaceAll(sourceRoot, "${PROJECTSTORE_CORE_ROOT}")
57
+ .replaceAll(sourceProject || "<no-project-variable>", "$PWD")
58
+ .replaceAll(`--harness ${SOURCE.id}`, `--harness ${TARGET.id}`)
59
+ .replaceAll(SOURCE.display_name, TARGET.display_name)
60
+ .replaceAll(SOURCE.display_name.split(" ")[0], TARGET.display_name)
61
+ .replaceAll(sourceWord, TARGET.id)
62
+ .replaceAll(SOURCE.surfaces.agents_block.reads_natively, TARGET.surfaces.agents_block.reads_natively)
63
+ .replaceAll(SOURCE.runtime.harness_dir + "/", TARGET.runtime.harness_dir + "/")
64
+ .replaceAll("/projectstore:*", "$projectstore-*")
65
+ .replace(/\/projectstore:([a-z0-9-]+)/g, "$projectstore-$1")
66
+ .replaceAll("AskUserQuestion", "the harness's user-input mechanism")
67
+ .replaceAll("Read tool", "file-reading tool")
68
+ .replaceAll("Write tool", "file-writing tool")
69
+ .replaceAll("Edit tools", "file-editing tools");
70
+ }
71
+
72
+ function skill(name, description, body, { runtime = true } = {}) {
73
+ return `---\nname: ${name}\ndescription: ${yamlString(rewriteBody(description))}\n---\n\n${runtime ? runtimePreamble() + "\n" : ""}${rewriteBody(body).trim()}\n`;
74
+ }
75
+
76
+ const COMMAND_OVERRIDES = {
77
+ agents: {
78
+ description: "Register or remove ProjectStore's shared agents block, inspect its Codex model overlay, or configure per-role models. Arguments: <register | unregister | status | configure>.",
79
+ body: `You are managing ProjectStore's Codex agent integration. Require a bound project.
80
+
81
+ ## register / unregister
82
+
83
+ Preview the requested change and ask for explicit approval. On approval, run the
84
+ core's \`install\` or \`uninstall\` verb with \`--harness codex --surface
85
+ agents_block --project "$PWD"\`. Print its output verbatim. Never edit the
86
+ managed block by hand.
87
+
88
+ ## status
89
+
90
+ Run \`plan --json --harness codex --surface agents_block --project "$PWD"\`,
91
+ then \`agents show --json --project "$PWD"\`. Report the block state and each
92
+ role's resolved model and source. The active overlay path returned by the core
93
+ is authoritative.
94
+
95
+ ## configure
96
+
97
+ Ask for a default model and optional per-role model ids. Use actual Codex model
98
+ ids supplied by the user or visible in the current host; do not translate model
99
+ names from another harness. Preview the exact argv, ask for approval, then run
100
+ \`agents configure --harness codex [--default <model>] [--agent
101
+ <role>=<model> ...] --project "$PWD"\`. The core is the only writer. Do not pass
102
+ an effort override: per-role effort is outside this integration's current
103
+ contract. A configured model is consumed by the role-orchestration skills on
104
+ their next spawn; no restart is needed.`,
105
+ },
106
+ bind: {
107
+ description: "Bind this project to an existing ProjectStore vault, or initialize and bind a new vault, using the harness-neutral core. Arguments: [vault-path].",
108
+ body: `Bind the current project through the core; never write the binding by
109
+ hand.
110
+
111
+ 1. Resolve the requested vault path. If it exists, use \`bind\`; if the user
112
+ explicitly asks to create it, use \`init\`. Ask for layout and language only
113
+ when the user has not supplied them.
114
+ 2. Show the resolved project, vault, verb, layout and language. Ask for explicit
115
+ approval. Naming the vault is the core's non-interactive confirmation.
116
+ 3. Run \`node "\${PROJECTSTORE_CORE_ROOT}/bin/projectstore.mjs" <bind|init>
117
+ "<vault-path>" --layout <layout> --language <language> --project "$PWD"
118
+ --json\` and report the result. Use \`--rebind\` only when the user explicitly
119
+ approved replacing an existing binding.
120
+ 4. Run \`status --json\` to verify the stored path and policies. Offer
121
+ \`$projectstore-scaffold\` only for a newly initialized empty vault.
122
+
123
+ Codex plugin updates are performed with the \`projectstore-codex upgrade\`
124
+ installer command; this skill never sends the user to another harness's plugin
125
+ UI. Codex has no ProjectStore status-line surface, so binding does not offer or
126
+ write one. Model choices, when requested, go through \`$projectstore-agents
127
+ configure\` and the Codex overlay.`,
128
+ },
129
+ doctor: {
130
+ description: "Diagnose the ProjectStore install and vault through the harness-neutral doctor; fixes remain previewed and approval-gated. Arguments: [--install | --vault] [--fix].",
131
+ body: `Run the deterministic doctor through the bundled core with the user's
132
+ arguments and \`--json\`. Summarize every finding without re-deriving it.
133
+
134
+ When \`--fix\` is absent, remain read-only. When it is present, separate fixes
135
+ by owner: derived vault views use \`$projectstore-reconcile\`; Codex plugin or
136
+ agents-block drift uses the core's \`upgrade --harness codex\` path. Preview
137
+ each mutation and ask for explicit approval before running it. Unsupported
138
+ surfaces remain unsupported; do not create host configuration by hand. Never
139
+ claim a fix after a non-zero exit.`,
140
+ },
141
+ statusline: {
142
+ description: "Explain ProjectStore status-line availability for Codex. This surface is unsupported and the skill never writes configuration. Arguments: on | off | status.",
143
+ body: `ProjectStore's status-line surface is not supported by the Codex
144
+ harness. Do not run install or uninstall for \`--surface statusline\`, and do
145
+ not edit project or host configuration. Report the surface as unsupported.
146
+ For project state use \`$projectstore-status\`; for a compact live view use the
147
+ Codex application's own task and terminal UI.`,
148
+ },
149
+ };
150
+
151
+ function renderCommand(file) {
152
+ const { data, body } = frontmatter(read(file), file);
153
+ const verb = file.split("/").pop().replace(/\.md$/, "");
154
+ const name = `projectstore-${verb}`;
155
+ const override = COMMAND_OVERRIDES[verb];
156
+ if (override) return [join("skills", name, "SKILL.md"), skill(name, override.description, override.body)];
157
+ const hint = data["argument-hint"] ? ` Arguments: ${data["argument-hint"]}.` : "";
158
+ return [join("skills", name, "SKILL.md"), skill(name, `${data.description || `Run the ProjectStore ${verb} workflow.`}${hint}`, body)];
159
+ }
160
+
161
+ function renderPassive(file) {
162
+ const { data, body } = frontmatter(read(file), file);
163
+ const dir = file.split("/").at(-2);
164
+ const name = data.name || dir;
165
+ if (!name.startsWith("projectstore-")) throw new Error(`${relative(ROOT, file)}: skill name must be projectstore-*`);
166
+ return [join("skills", name, "SKILL.md"), skill(name, data.description || name, body)];
167
+ }
168
+
169
+ function renderRole(file) {
170
+ const { data, body } = frontmatter(read(file), file);
171
+ const role = file.split("/").pop().replace(/\.md$/, "");
172
+ const name = `projectstore-${role}`;
173
+ const orchestration = `## Codex orchestration\n\nThis is a role-orchestration skill, not a native agent registration. Resolve the\nrole model by running:\n\n\`\`\`bash\nnode "\${PROJECTSTORE_CORE_ROOT}/bin/projectstore.mjs" agents model ${role} --json --project "$PWD"\n\`\`\`\n\nSpawn a collaboration agent for the bounded task. If the result names a model,\npass that model and use an empty or bounded context fork; otherwise inherit the\ncurrent model. Do not pass a reasoning-effort override: per-role effort belongs\nto a separate accepted story. Give the spawned agent the role contract below\nand the exact artifact/diff it must inspect. Wait for its final result.\n\n## Role contract\n\n`;
174
+ const description = (data.description || `Run the ProjectStore ${role} role in a fresh collaboration agent.`)
175
+ .replace(/^(?:Opus|Sonnet) \(max-effort\)\s+/, "");
176
+ return [join("skills", name, "SKILL.md"), skill(name, description, orchestration + body)];
177
+ }
178
+
179
+ function renderHooks(manifest, root = ROOT) {
180
+ const source = json(join(root, "hooks", "hooks.json"));
181
+ const post = source.hooks?.PostToolUse || [];
182
+ for (const group of post) if (group.matcher) group.matcher = (manifest.tools?.write_tools || []).join("|");
183
+ const rewrite = (value) => {
184
+ if (Array.isArray(value)) return value.map(rewrite);
185
+ if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, rewrite(v)]));
186
+ if (typeof value !== "string") return value;
187
+ const sourceRoot = "${" + SOURCE.runtime.plugin_root_env + "}";
188
+ const targetRoot = "${" + manifest.runtime.plugin_root_env + "}";
189
+ const command = value.startsWith(`node ${sourceRoot}/`) ? value.slice(`node ${sourceRoot}/`.length) : null;
190
+ return command ? `node "${targetRoot}/node_modules/projectstore/${command}"` : value.replaceAll(sourceRoot, `${targetRoot}/node_modules/projectstore`);
191
+ };
192
+ const text = JSON.stringify(rewrite(source), null, 2) + "\n";
193
+ return [join("hooks", "hooks.json"), text];
194
+ }
195
+
196
+ export function renderCodexAdapter(root = ROOT) {
197
+ const manifest = json(join(root, "harnesses", `${TARGET.id}.json`));
198
+ if (!manifest.emit || !manifest.output_dir) throw new Error(`${TARGET.id} must declare an output_dir when emit is true`);
199
+ const rows = [
200
+ ...files(join(root, "commands")).map(renderCommand),
201
+ ...files(join(root, "agents")).map(renderRole),
202
+ ...readdirSync(join(root, "skills"), { withFileTypes: true })
203
+ .filter((e) => e.isDirectory() && e.name.startsWith("projectstore-"))
204
+ .sort((a, b) => a.name.localeCompare(b.name))
205
+ .map((e) => renderPassive(join(root, "skills", e.name, "SKILL.md"))),
206
+ renderHooks(manifest, root),
207
+ ];
208
+ const out = new Map();
209
+ for (const [rel, text] of rows) {
210
+ if (rel.startsWith("..") || resolve(OUT, rel).slice(0, OUT.length + 1) !== OUT + "/") throw new Error(`adapter path escapes output root: ${rel}`);
211
+ if (out.has(rel)) throw new Error(`adapter collision: ${rel}`);
212
+ out.set(rel, text);
213
+ }
214
+ for (const [rel, text] of out) {
215
+ const sourceTokens = [SOURCE.runtime.plugin_root_env, SOURCE.runtime.project_dir_env, SOURCE.display_name, SOURCE.runtime.harness_dir + "/"].filter(Boolean);
216
+ const sourceAgentEnvs = (SOURCE.runtime.agent_overrides || []).map((row) => row.env).filter(Boolean);
217
+ if (sourceTokens.some((token) => text.includes(token)) || sourceAgentEnvs.some((token) => text.includes(token)) || /\$ARGUMENTS|\/projectstore:|\bAskUserQuestion\b/.test(text)) throw new Error(`${rel}: source-harness vocabulary leaked into ${TARGET.display_name} adapter`);
218
+ if (/\/reload-plugins\b|\/plugin(?:\s|\b)|harness\/codex-code\.json|\b(?:opus|sonnet|fable)\b/i.test(text)) throw new Error(`${rel}: source-harness model, environment or UI semantics leaked into ${TARGET.display_name} adapter`);
219
+ }
220
+ return out;
221
+ }
222
+
223
+ function currentTree(dir) {
224
+ const out = new Map();
225
+ const walk = (at) => {
226
+ if (!existsSync(at)) return;
227
+ for (const e of readdirSync(at, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
228
+ const p = join(at, e.name);
229
+ if (e.isDirectory()) walk(p);
230
+ else out.set(relative(dir, p), read(p));
231
+ }
232
+ };
233
+ walk(dir);
234
+ return out;
235
+ }
236
+
237
+ export function checkCodexAdapter(root = ROOT) {
238
+ const wanted = renderCodexAdapter(root);
239
+ const actual = currentTree(resolve(root, TARGET.output_dir));
240
+ const missing = [...wanted.keys()].filter((k) => !actual.has(k));
241
+ const unexpected = [...actual.keys()].filter((k) => !wanted.has(k));
242
+ const drift = [...wanted.keys()].filter((k) => actual.has(k) && actual.get(k) !== wanted.get(k));
243
+ return { ok: !missing.length && !unexpected.length && !drift.length, count: wanted.size, missing, unexpected, drift };
244
+ }
245
+
246
+ export function writeCodexAdapter(root = ROOT) {
247
+ const wanted = renderCodexAdapter(root);
248
+ const out = resolve(root, TARGET.output_dir);
249
+ rmSync(out, { recursive: true, force: true });
250
+ for (const [rel, text] of wanted) {
251
+ const p = join(out, rel);
252
+ mkdirSync(dirname(p), { recursive: true });
253
+ writeFileAtomic(p, text, { sweep: false });
254
+ }
255
+ return { ok: true, count: wanted.size, output: relative(root, out) };
256
+ }
257
+
258
+ function main(argv) {
259
+ const result = argv.includes("--write") ? writeCodexAdapter() : checkCodexAdapter();
260
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
261
+ return result.ok ? 0 : 1;
262
+ }
263
+
264
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) process.exitCode = main(process.argv.slice(2));