projectstore-codex 0.0.1 → 0.28.2

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 (184) 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 +176 -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 +261 -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 +35 -14
  155. package/skills/projectstore-adr/SKILL.md +76 -0
  156. package/skills/projectstore-agents/SKILL.md +50 -0
  157. package/skills/projectstore-archaeologist/SKILL.md +109 -0
  158. package/skills/projectstore-bind/SKILL.md +44 -0
  159. package/skills/projectstore-clerk/SKILL.md +126 -0
  160. package/skills/projectstore-codemap/SKILL.md +69 -0
  161. package/skills/projectstore-concept/SKILL.md +36 -0
  162. package/skills/projectstore-critic/SKILL.md +127 -0
  163. package/skills/projectstore-decision-detector/SKILL.md +59 -0
  164. package/skills/projectstore-doctor/SKILL.md +33 -0
  165. package/skills/projectstore-epic/SKILL.md +59 -0
  166. package/skills/projectstore-graph/SKILL.md +75 -0
  167. package/skills/projectstore-kanban/SKILL.md +60 -0
  168. package/skills/projectstore-librarian/SKILL.md +114 -0
  169. package/skills/projectstore-meeting/SKILL.md +36 -0
  170. package/skills/projectstore-peer-reviewer/SKILL.md +57 -0
  171. package/skills/projectstore-planner/SKILL.md +113 -0
  172. package/skills/projectstore-reconcile/SKILL.md +92 -0
  173. package/skills/projectstore-research/SKILL.md +36 -0
  174. package/skills/projectstore-review/SKILL.md +108 -0
  175. package/skills/projectstore-reviewer/SKILL.md +131 -0
  176. package/skills/projectstore-runbook/SKILL.md +36 -0
  177. package/skills/projectstore-scaffold/SKILL.md +42 -0
  178. package/skills/projectstore-search/SKILL.md +41 -0
  179. package/skills/projectstore-spec/SKILL.md +110 -0
  180. package/skills/projectstore-status/SKILL.md +47 -0
  181. package/skills/projectstore-statusline/SKILL.md +29 -0
  182. package/skills/projectstore-story/SKILL.md +132 -0
  183. package/skills/projectstore-story-completion/SKILL.md +69 -0
  184. package/skills/projectstore-vault-communication/SKILL.md +115 -0
@@ -0,0 +1,332 @@
1
+ {
2
+ "_comment": "Harness capability manifest — Codex. The committed adapters/codex tree renders the source commands, roles, passive skills and hooks into Codex vocabulary. Measurements began with 759 captured hook firings on 2026-09-07/08; the complete built shell, isolated marketplace install, upgrade, validator and fresh-session skill load passed on codex-cli 0.153.4 on 2026-09-30. The validator reads only .codex-plugin/plugin.json, which from 0.28.2 is the shell's one manifest: Codex picks a root Agent Plugins plugin.json first and has loaded no hooks from that format since openai/codex#37027 (merged 2026-08-05).",
3
+ "id": "codex",
4
+ "display_name": "Codex",
5
+ "emit": true,
6
+ "source_layout": false,
7
+ "output_dir": "adapters/codex",
8
+ "docs": "https://developers.openai.com/codex/",
9
+ "verified": null,
10
+ "verified_reason": "Null, restored 2026-10-03. docs/harnesses.md calls a harness supported only when every installed surface has been exercised end to end, and contract 16 of the generation spec (Generated harness surfaces: manifests, the generator and the three invariants) derives that label from this field. The 2026-09-30 run (session 01a0f079-7ecd-7331-95a9-2471def3cb22) exercised one surface: a fresh `codex exec` session loaded projectstore-status from the materialised cache. The hooks were not exercised, and the first real install showed why that matters. Its cached hooks, run by hand through `zsh -lc` (a form the failure did not depend on: read from source, 0.153.4 runs a hook as `<session shell> -c` with the Codex process's environment, and `$SHELL -lc` only as a fallback), could not load the vault in any session; in the first, the failure sat beneath the welcome. The core had taken the shell's root for its own. That is fixed and pinned by a test that runs every rendered hook from the built shell. This block is set by a live run that exercises every installed surface: the hooks once registration has settled, including PreCompact, and the skill flows. The shell is published from 0.28.1, ahead of that run, by the maintainer's decision of 2026-10-04; publication does not set this block.",
11
+ "runtime": {
12
+ "detect_env": [
13
+ "PLUGIN_ROOT",
14
+ "PLUGIN_DATA",
15
+ "CODEX_HOME"
16
+ ],
17
+ "project_dir_env": null,
18
+ "project_dir_env_reason": "Codex exports no project-directory variable. Measured: a plugin hook process receives PLUGIN_ROOT, PLUGIN_DATA, CLAUDE_PLUGIN_ROOT and CLAUDE_PLUGIN_DATA and nothing else of ours. The project comes from the hook payload's `cwd`, which the hooks adopt before they resolve anything (story: Every hook resolves the project before reading the payload that names it). Null, not absent: the key states the decision.",
19
+ "plugin_root_env": "PLUGIN_ROOT",
20
+ "home_env": "CODEX_HOME",
21
+ "home_default": ".codex",
22
+ "harness_dir": ".codex",
23
+ "harness_dir_reason": "Codex's own directory in a project. Never where projectstore's config lives — that is <project>/.projectstore/, one for every harness (the layout ADR).",
24
+ "overlay": "codex",
25
+ "overlay_reason": "The file under <project>/.projectstore/harness/ carrying this harness's agents block. Separate from claude-code's because a model name is harness-specific (ADR-008).",
26
+ "shared_env": [
27
+ "CLAUDE_PLUGIN_ROOT",
28
+ "CLAUDE_PLUGIN_DATA"
29
+ ],
30
+ "shared_env_reason": "Codex sets Claude Code's plugin-root names too, for compatibility — measured in every one of the 759 captured hook payloads. Without declaring them, a Codex session scores a strong detection hit for claude-code, ties with codex, and loses the tie to manifest filename order. A variable two harnesses both set identifies neither, so these are demoted to weak for every manifest (generation spec, contract 1, amended 2026-09-08).",
31
+ "session_env": [],
32
+ "session_env_reason": "Unmeasured. CODEX_HOME never appeared in a hook environment — it is a shell-profile signal, not a session marker — and no equivalent of CLAUDECODE was observed."
33
+ },
34
+ "tools": {
35
+ "write_tools": [
36
+ "apply_patch"
37
+ ],
38
+ "write_tools_reason": "The only file-writing tool observed: 26 apply_patch firings against 571 Bash. Bash writes too, and is excluded here for the same reason claude-code excludes it — a shell call is not a declared write and counting it would make every command an edit.",
39
+ "path_fields": [],
40
+ "path_fields_reason": "MEASURED EMPTY. apply_patch carries paths inside tool_input.command (26 of 26 firings), not in file_path/notebook_path/path. The shared manifest-driven extractor parses every `*** Add/Update/Delete File:` line (and `*** Move to:`) from patch_envelope_field, so multi-file patches feed the activity log and entry rule without a Codex branch.",
41
+ "patch_envelope_field": "command",
42
+ "approval_tool": null,
43
+ "approval_tool_reason": "UNMEASURED, and null rather than absent. Claude Code has AskUserQuestion; no equivalent structured-choice tool appeared in the 759 captured firings, and Codex's own tool list has not been read for one. Null means contract 10's approval-gate lint treats this harness as having no such tool — the safe reading — instead of inheriting the source harness's.",
44
+ "known_non_write_tools": [
45
+ "Bash",
46
+ "webrun",
47
+ "collaborationspawn_agent",
48
+ "collaborationlist_agents",
49
+ "collaborationwait_agent",
50
+ "collaborationsend_message",
51
+ "collaborationfollowup_task"
52
+ ],
53
+ "known_non_write_tools_reason": "Every other tool name observed. The collaboration* family is Codex's subagent mechanism — spawn_agent takes task_name, fork_turns and message — which is how it ran our roster's roles without loading agents/*.md (story: Subagent registration per host)."
54
+ },
55
+ "hooks": {
56
+ "config_file": "hooks/hooks.json",
57
+ "config_file_reason": "Codex's default hooks file. A legacy .codex-plugin/plugin.json with no `hooks` key falls back to hooks/hooks.json (DEFAULT_HOOKS_CONFIG_FILE in codex-rs/core-plugins/src/loader.rs at rust-v0.160.0), so the manifest names no hooks at all. The shell ships no root Agent Plugins plugin.json. Codex picks that manifest first and then loads no hooks from it, a runtime boundary since openai/codex#37027 (loader.rs:955 at rust-v0.153.4, 952-962 at rust-v0.160.0). With one, the maintainer's codex-cli 0.160.0 listed none of ours in /hooks on 2026-10-04; with the file renamed away in the installed cache, it listed all six. The app-server's hooks/list agrees: 0 for 0.28.1's root, 6 for 0.28.2's. The spike's inline form fired 757 events across five event types.",
58
+ "events": {
59
+ "SessionStart": "SessionStart",
60
+ "PreToolUse": "PreToolUse",
61
+ "PostToolUse": "PostToolUse",
62
+ "Stop": "Stop",
63
+ "PreCompact": "PreCompact"
64
+ },
65
+ "events_reason": "The five projectstore registers, and the list is checked against hooks/hooks.json rather than remembered: the map is what a generator would register, so an event here with no handler would render a hook that does nothing. Codex's full vocabulary, read from the binary, also has UserPromptSubmit (measured firing, and its payload is recorded under output_channels), PermissionRequest, PostCompact, SessionEnd, SubagentStart, SubagentStop and Interrupt, and no Notification. Handler kinds are Command, Async and MCP Server.",
66
+ "root_placeholder": "${PLUGIN_ROOT}",
67
+ "root_placeholder_literal": true,
68
+ "_root_placeholder_literal_comment": "true: Codex substitutes nothing in a hook command string. It does not need to — the hook process receives PLUGIN_ROOT in its environment (measured). The generator therefore renders an absolute interpreter and an absolute script path; a bare `node` is a PATH gamble."
69
+ },
70
+ "output_channels": {
71
+ "_comment": "Per-event payload fields, captured from live firings. `fields` here are the STDIN keys the event delivers — Codex's documented output channels (additionalContext and a decision object) are not yet measured, so `decision` stays null and `system_message` null rather than false.",
72
+ "SessionStart": {
73
+ "fields": [
74
+ "session_id",
75
+ "transcript_path",
76
+ "cwd",
77
+ "hook_event_name",
78
+ "model",
79
+ "permission_mode",
80
+ "source"
81
+ ],
82
+ "system_message": null,
83
+ "decision": null,
84
+ "evidence": "measured",
85
+ "note": "additionalContext reaches the model: asked to name the vault and count its ADRs without reading files, the model answered from the skeleton and was right."
86
+ },
87
+ "UserPromptSubmit": {
88
+ "fields": [
89
+ "session_id",
90
+ "turn_id",
91
+ "transcript_path",
92
+ "cwd",
93
+ "hook_event_name",
94
+ "model",
95
+ "permission_mode",
96
+ "prompt"
97
+ ],
98
+ "system_message": null,
99
+ "decision": null,
100
+ "evidence": "measured"
101
+ },
102
+ "PreToolUse": {
103
+ "fields": [
104
+ "session_id",
105
+ "turn_id",
106
+ "transcript_path",
107
+ "cwd",
108
+ "hook_event_name",
109
+ "model",
110
+ "permission_mode",
111
+ "tool_name",
112
+ "tool_input",
113
+ "tool_use_id"
114
+ ],
115
+ "system_message": null,
116
+ "decision": null,
117
+ "evidence": "measured",
118
+ "note": "372 firings. Codex has a dedicated PermissionRequest event, so whether a decision returned from PreToolUse is honoured is unmeasured and must not be assumed."
119
+ },
120
+ "PostToolUse": {
121
+ "fields": [
122
+ "session_id",
123
+ "turn_id",
124
+ "transcript_path",
125
+ "cwd",
126
+ "hook_event_name",
127
+ "model",
128
+ "permission_mode",
129
+ "tool_name",
130
+ "tool_input",
131
+ "tool_response",
132
+ "tool_use_id"
133
+ ],
134
+ "system_message": null,
135
+ "decision": null,
136
+ "evidence": "measured",
137
+ "note": "371 firings. tool_response is NOT one shape: 368 strings and 3 content-block arrays (all webrun). Claude Code sends an object. Nothing may depend on it."
138
+ },
139
+ "Stop": {
140
+ "fields": [
141
+ "session_id",
142
+ "turn_id",
143
+ "transcript_path",
144
+ "cwd",
145
+ "hook_event_name",
146
+ "model",
147
+ "permission_mode",
148
+ "stop_hook_active",
149
+ "last_assistant_message"
150
+ ],
151
+ "system_message": null,
152
+ "decision": null,
153
+ "evidence": "measured",
154
+ "note": "stop_hook_active is present, so hooks/session-stop.mjs's loop guard ports unchanged. last_assistant_message has no Claude Code counterpart."
155
+ },
156
+ "PreCompact": null
157
+ },
158
+ "surfaces": {
159
+ "agents": {
160
+ "supported": false,
161
+ "kind": "host",
162
+ "scope": "user",
163
+ "scope_reason": "Codex spawns subagents through a tool (collaborationspawn_agent), not by loading a plugin's agents/ directory.",
164
+ "why_unsupported": "Measured: told to run projectstore:critic, Codex spawned six of its own generic subagents named after our roster and briefed them itself; none of our agents/*.md prose reached them. No route from a plugin's agents/ entry to that mechanism has been found (roadmap B4)."
165
+ },
166
+ "agents_block": {
167
+ "supported": true,
168
+ "kind": "shared",
169
+ "format": "markdown-block",
170
+ "scope": "project",
171
+ "scope_reason": "Codex reads AGENTS.md from the project root; it does not read CLAUDE.md.",
172
+ "files": [
173
+ "AGENTS.md"
174
+ ],
175
+ "reads_natively": "AGENTS.md",
176
+ "reads_natively_reason": "Codex reads AGENTS.md from the project root by itself, and reads no other file of this kind — its `files` list is the same single entry, so placement and native reading coincide here and the distinction costs nothing.",
177
+ "files_reason": "One entry, deliberately. analyseBlock takes files[0] when it exists and the last entry otherwise; with claude-code's two-entry list and neither file present, a fresh install wrote CLAUDE.md — which Codex never reads. Measured on the smoke stand, where AGENTS.md had to be written by hand before Codex saw the rules at all.",
178
+ "source": "templates/claude-md-block.md.tmpl",
179
+ "marker": {
180
+ "open": "<!-- projectstore:agents v",
181
+ "close": "<!-- /projectstore:agents -->"
182
+ }
183
+ },
184
+ "commands": {
185
+ "supported": false,
186
+ "kind": "host",
187
+ "scope": "user",
188
+ "scope_reason": "Codex has no registrable root slash command; the /prompts: mechanism is deprecated.",
189
+ "why_unsupported": "Shipping commands/ is actively harmful here: Codex rewrites them into skills itself and keeps ${CLAUDE_PLUGIN_ROOT} in the body, producing entry points that exit 1 with Cannot find module '/bin/projectstore.mjs' (reproduced). The generator renders them as skills instead (roadmap B5)."
190
+ },
191
+ "hooks": {
192
+ "supported": true,
193
+ "kind": "host",
194
+ "scope": "user",
195
+ "scope_reason": "Loaded with the plugin from its default hooks/hooks.json, which .codex-plugin/plugin.json leaves to Codex by naming no hooks; trust is granted per hook, machine-wide, and cannot be silenced per checkout."
196
+ },
197
+ "mcp": {
198
+ "supported": false,
199
+ "kind": "host",
200
+ "scope": "project",
201
+ "scope_reason": "Codex auto-discovers a plugin-root .mcp.json even when the manifest does not declare mcpServers — measured.",
202
+ "why_unsupported": "Our .mcp.json is in Claude Code's dialect: its argv carries ${CLAUDE_PLUGIN_ROOT} and ${CLAUDE_PROJECT_DIR}, which nothing substitutes there, so the server failed its handshake and printed a red error in every session. Ships only once the Codex dialect is rendered and the spike's criterion (h) says what cwd and environment the server receives."
203
+ },
204
+ "skills": {
205
+ "supported": true,
206
+ "kind": "host",
207
+ "scope": "user",
208
+ "scope_reason": "Codex loads skills from the plugin root's skills/ directory, declared in the manifest as skills: \"./skills/\" — 47 of the 64 curated plugin manifests on the measuring machine declare theirs that way, and ours loaded.",
209
+ "dir": "skills",
210
+ "file": "<name>/SKILL.md",
211
+ "registered_by": "plugin",
212
+ "frontmatter_required": [
213
+ "name",
214
+ "description"
215
+ ],
216
+ "frontmatter_reason": "\"The SKILL.md file must include name and description\" — learn.chatgpt.com/docs/build-skills, read 2026-09-08. DOCUMENTED, and it disagrees with what we measured: our four skills carried only `description` and loaded anyway, and the model listed them. Either the name falls back to the directory or the loader is laxer than the page. We now ship both fields, so the discrepancy stops mattering for us and stays recorded for whoever meets it.",
217
+ "invocation": "$<name>",
218
+ "invocation_evidence": "documented",
219
+ "invocation_reason": "Explicitly: type `$<skill-name>`, or `/skills` to list what is loaded (examples given: `$skill-installer linear`). Implicitly: the model chooses a skill when the task matches its `description` — `allow_implicit_invocation` defaults true and is disabled per skill in agents/openai.yaml. So `description` is the TRIGGER here, not a hint for a human picking from a menu, which is what it is on the source harness. learn.chatgpt.com/docs/build-skills, read 2026-09-08. NOT measured: nobody has typed a $ at this stand's prompt.",
220
+ "discovery": [
221
+ "$CWD/.agents/skills",
222
+ "$REPO_ROOT/.agents/skills",
223
+ "$HOME/.agents/skills",
224
+ "/etc/codex/skills",
225
+ "bundled with Codex"
226
+ ],
227
+ "discovery_reason": "The four scopes the documentation lists, in its order. Note what is ABSENT from that list: a plugin's own skills/ directory. Ours loads all the same, declared as skills: \"./skills/\" in the plugin manifest and measured on the spike — 47 of 64 curated plugin manifests on the measuring machine declare theirs the same way. The plugin route is real and undocumented on this page; do not conclude from the page that it does not exist.",
228
+ "description_limit": "shortened above 2% of the context window, or 8000 characters",
229
+ "unrendered_source": false,
230
+ "unrendered_source_reason": "Codex loads the committed adapters/codex/skills tree rendered from commands, roles and passive source skills. Generation fails if a Claude command namespace or root variable survives."
231
+ },
232
+ "statusline": {
233
+ "supported": false,
234
+ "kind": "host",
235
+ "scope": "user",
236
+ "scope_reason": "There is no per-project status-line slot to occupy: Codex has no status-line surface at all, so nothing of ours is written anywhere and the scope is the host's, not a file's.",
237
+ "why_unsupported": "No status-line mechanism was found in the harness — no settings slot, no plugin field, no handler contract. Recorded as a row rather than an absence so the generator's coverage invariant sees a stated omission (contract 9) instead of a surface that quietly does not exist here."
238
+ },
239
+ "plugin": {
240
+ "supported": true,
241
+ "kind": "registration",
242
+ "format": "portable-plugin-registration",
243
+ "scope": "user",
244
+ "scope_reason": "Measured on codex-cli 0.153.4: marketplace registration, plugin installation and the materialised cache live in CODEX_HOME. Trusted project config can override enablement but cannot create an installation, so ordinary project uninstall never removes this global surface.",
245
+ "ownership": "global",
246
+ "condition": "distribution_root",
247
+ "marketplace_name": "projectstore-npx",
248
+ "plugin_name": "projectstore",
249
+ "dir": [
250
+ "projectstore",
251
+ "marketplace"
252
+ ],
253
+ "plugin_subdir": "plugins/projectstore",
254
+ "manifest": ".agents/plugins/marketplace.json",
255
+ "ownership_manifest": ".projectstore-registration.json",
256
+ "provenance_key": "projectstore",
257
+ "registry": {
258
+ "format": "toml",
259
+ "global_config": ["config.toml"],
260
+ "project_config": ["config.toml"],
261
+ "cache_dir": ["plugins", "cache"]
262
+ },
263
+ "cli": {
264
+ "bin": "codex",
265
+ "verified": {
266
+ "version": "0.153.4",
267
+ "date": "2026-09-29",
268
+ "evidence": "measured with an isolated CODEX_HOME and a trusted project override"
269
+ },
270
+ "required": ["marketplace_add", "install", "uninstall", "list"],
271
+ "optional": ["validate", "update", "marketplace_remove", "enable", "disable"],
272
+ "fallbacks": {
273
+ "validate": "unsupported",
274
+ "update": "install",
275
+ "marketplace_remove": "explicit-global-only",
276
+ "enable": "project-config-only",
277
+ "disable": "project-config-only"
278
+ },
279
+ "commands": {
280
+ "marketplace_add": ["plugin", "marketplace", "add", "{dir}"],
281
+ "install": ["plugin", "add", "{id}"],
282
+ "uninstall": ["plugin", "remove", "{id}"],
283
+ "list": ["plugin", "list", "--json"],
284
+ "marketplace_remove": ["plugin", "marketplace", "remove", "{marketplace}"]
285
+ }
286
+ }
287
+ }
288
+ },
289
+ "capabilities": {},
290
+ "rewrites": [],
291
+ "install": {
292
+ "mechanism": "the package's portable plugin root, registered through its distribution shell and Codex's own marketplace CLI",
293
+ "shell": "projectstore-codex",
294
+ "shell_reason": "The built `projectstore-codex` shell carries the rendered Codex skills and hooks plus an exact bundled core, fixes --harness codex, stages a stable marketplace under CODEX_HOME, and asks Codex itself to install it. It is published from 0.28.1, by the maintainer's decision of 2026-10-04. Until then this key was absent, so generated guidance could not send users to the reserved 0.0.1 placeholder; the core alone is not Codex's plugin root and cannot register it.",
295
+ "why_not_scripted": "The shell does not edit Codex's registry or cache. It atomically stages the plugin source, previews the host commands, then runs `codex plugin marketplace add` and `codex plugin add`; a read-back verifies the installed version and payload digest. The global plugin survives ordinary project uninstall; `uninstall --global` is the explicit destructive form.",
296
+ "steps": [
297
+ "npx projectstore-codex install --project \"$PWD\"",
298
+ "restart Codex and approve the ProjectStore hooks when prompted",
299
+ "run `$projectstore-bind <vault-path>` in Codex"
300
+ ],
301
+ "notes": [
302
+ "The installation is user-global because Codex's marketplace, plugin row and cache live in CODEX_HOME; the AGENTS.md block is project-local.",
303
+ "Upgrade with `npx projectstore-codex@<version> upgrade --project \"$PWD\"`; the named package version is also the plugin version.",
304
+ "A second enabled ProjectStore plugin from another marketplace is a refusal, not silently disabled globally. Remove or disable that older registration explicitly, then retry.",
305
+ "Ordinary uninstall removes only project-owned surfaces. `npx projectstore-codex uninstall --global --project \"$PWD\"` also removes the user-global plugin registration after a preview."
306
+ ],
307
+ "docs": "https://github.com/SmartAndPoint/ProjectStore#install--one-message"
308
+ },
309
+ "update_instructions": "Run `npx projectstore-codex@<version> upgrade --project <dir>`. The installer atomically replaces its stable source and `codex plugin add projectstore@projectstore-npx` materialises the new version; this was measured across two dev builds on codex-cli 0.153.4. Restart Codex afterwards so the new skills and hooks are loaded.",
310
+ "agent_translation": {
311
+ "carries_model": false,
312
+ "carries_model_reason": "The surface projectstore ships — a plugin's agents/ entry — carries no model on Codex: surfaces.agents is supported:false because no route from a plugin's agents/ directory to collaborationspawn_agent has been found (measured). Codex DOES have a definition file that carries model and effort, recorded below, but it is project- or user-scoped and not plugin-shippable, so contract 12 holds as written: what we ship cannot carry a model. Was true here until 2026-09-08, which contradicted both contract 12 and this file's own agents row.",
313
+ "definition_file": ".codex/agents/<name>.toml",
314
+ "definition_file_evidence": "documented",
315
+ "definition_file_reason": "Codex agent definitions are TOML under .codex/agents/ (project) or ~/.codex/agents/ (personal) and accept `model`, `model_reasoning_effort`, `sandbox_mode` and `mcp_servers`. Documented at learn.chatgpt.com/docs/agent-configuration/subagents, read 2026-09-08; NOT measured, and not reachable from a plugin today. This is the route the rendering story (Per-agent effort returns, PS-AGENTS) has to decide on, and B4 has to measure.",
316
+ "cheap_model": "gpt-5.6-luna",
317
+ "cheap_model_evidence": "chosen",
318
+ "cheap_model_reason": "CHOSEN, not measured: the verified plugin session exercised the rendered skill surface, not this optional model pin. It reaches a user's overlay — `agents configure --harness codex` writes it as the clerk's pin — so if the id is wrong the failure is at spawn time, on the harness, not here. The transcriber's model. gpt-5.6-luna at $0.20/$1.20 per Mtok is the cost tier gpt-5.6-sol ($4/$20) is twenty times above, and the clerk composes nothing: it copies an approved file and runs a pinned ceremony (ADR-008). Chosen 2026-09-08 from the published model list, by the same reasoning that pins the clerk to the cheap tier on Claude Code.",
319
+ "effort_levels": [
320
+ "low",
321
+ "medium",
322
+ "high",
323
+ "xhigh",
324
+ "max",
325
+ "ultra"
326
+ ],
327
+ "effort_key": "model_reasoning_effort",
328
+ "effort_evidence": "documented",
329
+ "effort_precedence": "explicit spawn value, then the [agents] default from config, then the parent agent's value. A model chosen without an effort uses that model's default effort.",
330
+ "effort_reason": "Recorded, not yet consumed: the overlay's allowlist rejects an effort key and rewrites it out, deliberately, until the story Per-agent effort returns decides whether a role's effort travels in a rendered definition or at spawn. Documented 2026-09-08; the `ultra` level is Codex's and is not a level Claude Code accepts."
331
+ }
332
+ }
@@ -0,0 +1,59 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/session-start.mjs"
9
+ },
10
+ {
11
+ "type": "command",
12
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/session-rules.mjs"
13
+ }
14
+ ]
15
+ }
16
+ ],
17
+ "PreToolUse": [
18
+ {
19
+ "hooks": [
20
+ {
21
+ "type": "command",
22
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/touch-session.mjs"
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "PostToolUse": [
28
+ {
29
+ "matcher": "Edit|Write|MultiEdit|NotebookEdit",
30
+ "hooks": [
31
+ {
32
+ "type": "command",
33
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/touch-session.mjs"
34
+ }
35
+ ]
36
+ }
37
+ ],
38
+ "Stop": [
39
+ {
40
+ "hooks": [
41
+ {
42
+ "type": "command",
43
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/session-stop.mjs"
44
+ }
45
+ ]
46
+ }
47
+ ],
48
+ "PreCompact": [
49
+ {
50
+ "hooks": [
51
+ {
52
+ "type": "command",
53
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/pre-compact.mjs"
54
+ }
55
+ ]
56
+ }
57
+ ]
58
+ }
59
+ }
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ // projectstore — PreCompact hook.
3
+ //
4
+ // Fires before context compaction (both manual /compact and automatic).
5
+ //
6
+ // ONE channel: `systemMessage`, the line the user sees in their /compact
7
+ // output. PreCompact has no `additionalContext` — the runtime rejects the whole
8
+ // object when one is present, taking `systemMessage` down with it, so for its
9
+ // entire history this hook assembled a packet, printed a red validation error,
10
+ // and reached nobody. Agent-facing continuity after a compaction is
11
+ // SessionStart's job, on `source: "compact"` (spec contract 19).
12
+ //
13
+ // What this hook may CLAIM is narrower than what it knows, and deliberately so:
14
+ // - naming the in-flight artifact is always safe — it is a fact about the log
15
+ // it just read, not a promise about the future;
16
+ // - promising that context comes back requires `trigger === "manual"` AND
17
+ // `auto_inject !== false`, the two falsifiers knowable at claim time;
18
+ // - promising that RECENT FILES come back additionally requires the log to
19
+ // have resolved nonempty, because an empty log renders nothing there.
20
+ //
21
+ // Session identity comes from Claude's own session_id in stdin input (so two
22
+ // Claude instances on the same project don't share activity).
23
+ // Silent no-op when no projectstore config is present.
24
+
25
+ import { basename } from "node:path";
26
+ import {
27
+ readConfig,
28
+ readStdinJson,
29
+ adoptHookInput,
30
+ loadLayout,
31
+ readActivityAsync,
32
+ resolveInFlightArtifact,
33
+ pathCell,
34
+ } from "../scripts/lib.mjs";
35
+
36
+ // Exits after the flush, never before: process.exit does not drain a pending
37
+ // pipe write, and the budgeted read below can still be outstanding.
38
+ function emit(systemMessage) {
39
+ process.stdout.write(JSON.stringify({ systemMessage }) + "\n", () => process.exit(0));
40
+ }
41
+
42
+ // Contract 13's budget applies here too. The session file lives inside the
43
+ // vault, so an evicted copy blocks uninterruptibly — and hanging a compaction
44
+ // is a worse outcome than a line with no artifact name. The READ itself comes
45
+ // from lib.mjs: SessionStart reads the same file for the same reason, and
46
+ // contract 24's argument against two implementations of one question does not
47
+ // stop at the resolver.
48
+ // No {readFile} seam here on purpose: this hook is only ever driven as a
49
+ // process, so a seam nothing can reach would read as testability it does not
50
+ // have. The race is unit-tested through the gather's copy of it.
51
+ // null → the read did not resolve in time
52
+ // [] → resolved: missing, unreadable and genuinely empty, indistinguishable
53
+ async function readActivityBudgeted(vault, sid, budgetMs) {
54
+ if (!sid) return [];
55
+ let timer = null;
56
+ const deadline = new Promise((resolve) => {
57
+ timer = setTimeout(() => resolve(null), budgetMs);
58
+ });
59
+ const out = await Promise.race([
60
+ readActivityAsync(vault, sid).catch(() => []),
61
+ deadline,
62
+ ]);
63
+ clearTimeout(timer);
64
+ return out;
65
+ }
66
+
67
+ async function main() {
68
+ // The payload before the project: readConfig() resolves through
69
+ // projectRoot(), and on a harness that exports no project-dir variable the
70
+ // payload's cwd is the only answer better than this process's cwd. Read once
71
+ // — fd 0 is empty on a second read.
72
+ const input = adoptHookInput(readStdinJson());
73
+
74
+ const cfg = readConfig();
75
+ if (!cfg) process.exit(0);
76
+
77
+ const sid = input?.session_id || null;
78
+ const activity = await readActivityBudgeted(cfg.vault_path, sid, 200);
79
+
80
+ // Defensive: loadLayout throws on an unknown layout name, and in this hook the
81
+ // outer catch produces a failure notice rather than a compaction line — so a
82
+ // layout throw would take the whole message down for the sake of one clause.
83
+ let layout = null;
84
+ try {
85
+ layout = loadLayout(cfg.layout);
86
+ } catch {}
87
+ const artifact = activity && layout
88
+ ? resolveInFlightArtifact(activity, layout, cfg.vault_path)
89
+ : null;
90
+
91
+ const parts = [`vault ${basename(cfg.vault_path)}`];
92
+ // Dropped rather than rendered empty when nothing resolves.
93
+ // Contract 19's "one path form, three renderings": the same 200-character
94
+ // front-truncated cell the skeleton uses, so a >200 path cannot render
95
+ // truncated in one place and whole one line away.
96
+ if (artifact) parts.push(`in flight: ${pathCell(artifact)}`);
97
+
98
+ // The delivery claim, and only where it is true. On `auto` this plugin makes
99
+ // no claim that SessionStart fires at all; under `auto_inject: false` the
100
+ // skeleton is deterministically not emitted, and gating on config presence
101
+ // alone would print the promise happily on exactly that configuration.
102
+ const mayPromise = input?.trigger === "manual" && cfg.auto_inject !== false;
103
+ if (mayPromise) {
104
+ // Recent files are a separate referent with a separate condition: an empty
105
+ // log renders no continuity section, so the stronger wording would be false
106
+ // on a path the activity log's last-tool-wins dedupe makes ordinary.
107
+ parts.push(activity && activity.length > 0
108
+ ? "orientation and this session's recent files return at session start"
109
+ : "orientation returns at session start");
110
+ } else {
111
+ // True on every path: plugin commands are installation-scoped and not
112
+ // gated by auto_inject.
113
+ parts.push("run /projectstore:status to reorient");
114
+ }
115
+
116
+ emit(`projectstore: compacting — ${parts.join(", ")}`);
117
+ }
118
+
119
+ main().catch((e) => {
120
+ emit(`projectstore: PreCompact hook failed — ${e.message}`);
121
+ });
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ // projectstore — session-rules.mjs
3
+ //
4
+ // The SECOND SessionStart hook. It carries the standing rules an agent needs
5
+ // before it starts working, and nothing else.
6
+ //
7
+ // Why a separate hook rather than more text in session-start.mjs: hook output
8
+ // strings are capped at 10,000 characters PER VALUE, and over the cap the
9
+ // harness replaces the text with a file path and a preview. session-start's
10
+ // vault map is already over that line on a real vault (11,596 characters when
11
+ // this was written), so anything appended to it would arrive as a reference
12
+ // rather than as context. Several hooks returning additionalContext for one
13
+ // event are all delivered, so a small second value is delivered inline no
14
+ // matter how large the map grows. Keep this payload small — permanently.
15
+ //
16
+ // Spec: "Entry-rule detection: the score, the open-story predicate, and the
17
+ // delivery seams", contract 17.
18
+
19
+ import { readConfig, readStdinJson, adoptHookInput } from "../scripts/lib.mjs";
20
+
21
+ // Kept well under the cap; asserted by a test rather than by intention.
22
+ const RULES = `# projectstore — standing rules for this session
23
+
24
+ **Artifact-first order.** A feature-sized request opens a vault artifact before
25
+ it opens an editor: analysis → placement (which epic, which story) → an ADR
26
+ and/or spec when the "how" is non-trivial → \`projectstore:critic\` → only then
27
+ implementation → \`projectstore:reviewer\`. "Feature-sized" is not a judgement
28
+ call about how the request was phrased — it is about what the work touches. If
29
+ you are about to write across several source files, open the story first.
30
+
31
+ **Report instruction conflicts; do not arbitrate them.** If a session-level or
32
+ harness-level instruction contradicts these rules or the \`AGENTS.md\`
33
+ registration block, say so and ask which wins. Resolving it silently is how the
34
+ contradiction becomes invisible to the person who could have settled it.`;
35
+
36
+ function emit(additionalContext) {
37
+ process.stdout.write(JSON.stringify({
38
+ hookSpecificOutput: { hookEventName: "SessionStart", additionalContext },
39
+ }) + "\n");
40
+ }
41
+
42
+ function main() {
43
+ // The RULES text below is the same for every project, so it is tempting to
44
+ // skip the payload here. The GATE is not: both `!cfg` and `auto_inject`
45
+ // are facts about one specific project, and this hook shares its event with
46
+ // session-start.mjs. If only one of the two adopted the payload, the pair
47
+ // could answer for two different projects in the same session start — one
48
+ // emitting a vault's skeleton while the other decided, from the process's
49
+ // cwd, whether to speak at all. One readFileSync(0) is the whole cost of
50
+ // keeping them symmetric.
51
+ adoptHookInput(readStdinJson());
52
+
53
+ const cfg = readConfig();
54
+ if (!cfg) return;
55
+ // Honour auto_inject: a session that opted out of context injection is
56
+ // exactly the case for which the AGENTS.md block remains the durable copy.
57
+ if (cfg.auto_inject === false) return;
58
+ emit(RULES);
59
+ }
60
+
61
+ try { main(); } catch {
62
+ // A hook must never break session startup.
63
+ }