projectstore-claude 0.28.0-rc.2 → 0.28.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 (33) hide show
  1. package/bin/projectstore-claude.mjs +4 -1
  2. package/node_modules/projectstore/.claude-plugin/marketplace.json +1 -1
  3. package/node_modules/projectstore/.claude-plugin/plugin.json +1 -1
  4. package/node_modules/projectstore/README.md +86 -16
  5. package/node_modules/projectstore/commands/agents.md +8 -2
  6. package/node_modules/projectstore/commands/doctor.md +26 -7
  7. package/node_modules/projectstore/docs/extending.md +1 -1
  8. package/node_modules/projectstore/docs/getting-started.md +1 -1
  9. package/node_modules/projectstore/docs/harnesses.md +159 -0
  10. package/node_modules/projectstore/harnesses/claude-code.json +17 -3
  11. package/node_modules/projectstore/harnesses/codex.json +331 -0
  12. package/node_modules/projectstore/hooks/pre-compact.mjs +7 -1
  13. package/node_modules/projectstore/hooks/session-rules.mjs +11 -1
  14. package/node_modules/projectstore/hooks/session-start.mjs +15 -5
  15. package/node_modules/projectstore/hooks/session-stop.mjs +7 -1
  16. package/node_modules/projectstore/package.json +4 -2
  17. package/node_modules/projectstore/scaffold/checklists.json +1 -1
  18. package/node_modules/projectstore/scripts/build-adapters.mjs +264 -0
  19. package/node_modules/projectstore/scripts/cli.mjs +36 -9
  20. package/node_modules/projectstore/scripts/diff-refs.mjs +12 -2
  21. package/node_modules/projectstore/scripts/doctor.mjs +254 -40
  22. package/node_modules/projectstore/scripts/harness.mjs +150 -26
  23. package/node_modules/projectstore/scripts/install-harness.mjs +591 -61
  24. package/node_modules/projectstore/scripts/lib.mjs +208 -24
  25. package/node_modules/projectstore/scripts/portable-registration.mjs +198 -0
  26. package/node_modules/projectstore/scripts/surfaces.mjs +62 -13
  27. package/node_modules/projectstore/scripts/touch-session.mjs +29 -18
  28. package/node_modules/projectstore/scripts/version-guard.mjs +18 -4
  29. package/node_modules/projectstore/skills/{decision-detector → projectstore-decision-detector}/SKILL.md +1 -0
  30. package/node_modules/projectstore/skills/{peer-reviewer → projectstore-peer-reviewer}/SKILL.md +1 -0
  31. package/node_modules/projectstore/skills/{story-completion → projectstore-story-completion}/SKILL.md +1 -0
  32. package/node_modules/projectstore/skills/{vault-communication → projectstore-vault-communication}/SKILL.md +1 -0
  33. package/package.json +2 -2
@@ -76,7 +76,10 @@ if (!core) {
76
76
  // without one, so the child must see the real stdin and stdout. No
77
77
  // timeout — the child waits on a human at the preview. exitCode, not
78
78
  // exit(): the core's own bin says why (a pending write on a pipe).
79
- const r = spawnSync(process.execPath, [core, ...fixed.argv], { stdio: "inherit" });
79
+ const r = spawnSync(process.execPath, [core, ...fixed.argv], {
80
+ stdio: "inherit",
81
+ env: { ...process.env, PROJECTSTORE_DISTRIBUTION_ROOT: root },
82
+ });
80
83
  if (r.error) process.stderr.write(`${SHELL}: ${r.error.message}\n`);
81
84
  // A signal is relayed the shell way (128 + its number): Ctrl-C at the
82
85
  // preview is 130 here as it would be on the core itself.
@@ -12,7 +12,7 @@
12
12
  "name": "projectstore",
13
13
  "displayName": "projectstore",
14
14
  "description": "📚 Your agent runs the project through a verified loop: task → artifact (ADR · spec · epic · story) → adversarial critic → backlog → planner → reviewer → done. Plain markdown in an Obsidian-friendly vault, every write approved by you — and any model can pick the project up tomorrow.",
15
- "version": "0.28.0-rc.2",
15
+ "version": "0.28.0",
16
16
  "author": {
17
17
  "name": "Evgenii Konev",
18
18
  "email": "ekonev@smartandpoint.com",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "projectstore",
3
3
  "displayName": "projectstore",
4
- "version": "0.28.0-rc.2",
4
+ "version": "0.28.0",
5
5
  "description": "Your agent runs the project through a verified loop: task → artifact (ADR / spec / epic / story) → adversarial critic → backlog → planner → reviewer → done. Plain markdown in git — any model can pick the project up tomorrow.",
6
6
  "author": {
7
7
  "name": "Evgenii Konev @ SmartAndPoint",
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![release](https://img.shields.io/github/v/release/SmartAndPoint/ProjectStore?label=release)](https://github.com/SmartAndPoint/ProjectStore/releases) [![license](https://img.shields.io/github/license/SmartAndPoint/ProjectStore?label=license)](./LICENSE) [![Star on GitHub](https://img.shields.io/badge/%E2%AD%90-star_us-yellow?logo=github)](https://github.com/SmartAndPoint/ProjectStore/stargazers)
6
6
 
7
- A [Claude Code](https://claude.com/claude-code) plugin.
7
+ A project workflow plugin for [Claude Code](https://claude.com/claude-code) and [OpenAI Codex](https://developers.openai.com/codex/) (experimental).
8
8
 
9
9
  ---
10
10
 
@@ -88,6 +88,28 @@ npx projectstore-claude install --project "$PWD"
88
88
 
89
89
  The same tree is published to npm as [`projectstore`](https://www.npmjs.com/package/projectstore) — one source package carrying every harness's manifest — and `projectstore-claude` is its Claude Code shell: the core pinned at the same version and bundled inside, the harness fixed, so the one command has the same shape on every harness. It registers the plugin with Claude Code: it writes a small local marketplace of its own under your Claude home, then drives `claude plugin marketplace add` / `plugin install` **at local scope**, so the registration lands in this checkout's `.claude/settings.local.json` and nowhere else. Every host command is printed before it runs; naming the harness is the confirmation. Restart Claude Code afterwards. A git-marketplace copy already enabled for the checkout is silenced there (not globally) so the plugin does not load twice; `uninstall` turns it back on. Pin or upgrade with `npx projectstore-claude@<version> upgrade --project "$PWD"` — the version you name is the version you run. The core's low-level form, `npx projectstore <verb> --harness claude-code …`, is exactly what the shell runs. bun works the same on the packed bin.
90
90
 
91
+ **Codex uses its own rendered plugin root and the same one-command shape.**
92
+ Codex support is **experimental** (see [`docs/harnesses.md`](./docs/harnesses.md)
93
+ for what has been measured and what has not), and its shell is not published
94
+ yet. From this checkout, exercise the exact npx path against the built tarball:
95
+
96
+ ```sh
97
+ npm run shells:build -- --only projectstore-codex --dev --out dist
98
+ npx --package "./dist/projectstore-codex-$(node -p 'require("./package.json").version').tgz" projectstore-codex install --project "$PWD"
99
+ ```
100
+
101
+ The Codex shell carries a canonical portable manifest, namespaced workflow and
102
+ role skills, lifecycle hooks, and the exact bundled core. It stages a stable
103
+ marketplace under `CODEX_HOME`, drives `codex plugin marketplace add` and
104
+ `codex plugin add`, then reads the installation back and verifies its version
105
+ and payload digest. Restart Codex, approve the hooks, and run
106
+ `$projectstore-bind <vault-path>`. After the first explicit npm publication,
107
+ the shorter command is
108
+ `npx projectstore-codex@<version> install --project "$PWD"`; use its `upgrade`
109
+ verb for later releases. Because Codex's
110
+ plugin registry is user-global, ordinary uninstall removes only the project's
111
+ agents block; `uninstall --global` is the explicit machine-wide removal.
112
+
91
113
  The package also carries a `bin`. Without a session — in CI, or in a shell — the same core answers token-free, with a `--json` envelope on every verb:
92
114
 
93
115
  ```
@@ -119,20 +141,48 @@ npx projectstore bind ~/vaults/my-project
119
141
  npx projectstore init ~/vaults/new-project --language ru
120
142
  ```
121
143
 
122
- `projectstore-claude`, `projectstore-codex` and `projectstore-opencode` on npm are this package's per-harness shells — the core pinned and bundled, the harness fixed; the Codex and opencode shells publish once their plugin roots are rendered. The other `projectstore-*` names are reserved placeholders pointing back here. One source package, one version, N published tarballs.
144
+ `projectstore-claude`, `projectstore-codex` and `projectstore-opencode` are this package's per-harness shells — the core pinned and bundled, the harness fixed. Codex's shell is experimental and stays private until a live run has exercised every surface it installs, from an installed release ([`docs/harnesses.md`](./docs/harnesses.md)). The opencode shell publishes after its plugin root is rendered. The other `projectstore-*` names are reserved placeholders pointing back here. One source package, one version, N tarballs.
123
145
  </details>
124
146
 
125
147
  ## Upgrading
126
148
 
127
- `/plugin update` (or auto-update) and a restart is the whole procedure. What
128
- an existing project sees afterwards, and why:
129
-
130
- - **The status line keeps rendering.** A launcher written by an earlier
131
- version still works, but it now carries no file stamp and its embedded
132
- fallback root is frozen at the old version; the startup line says so at
133
- every session start until you run the fix — `/projectstore:doctor --fix` — which
134
- re-stamps it. Nothing rewrites that file behind your back any more: first
135
- wiring and refresh are `install`'s, behind a preview.
149
+ `/plugin update` (or auto-update) and a restart, then one command per project
150
+ bound before 0.28. What an existing project sees afterwards, and why:
151
+
152
+ - **The project's files move to `.projectstore/`.** `.claude/projectstore.json`
153
+ and `.claude/.projectstore/` become `.projectstore/projectstore.json`,
154
+ `.projectstore/harness/claude-code.json` and `.projectstore/state/`. Nothing
155
+ breaks before you move them: every reader falls back to the old paths through
156
+ 0.29, and the startup line names the command until the move is done. Close
157
+ every Claude Code session in the project, run that command from a terminal,
158
+ then restart. The command depends on how you installed:
159
+ - **From the git marketplace** (every 0.27.x install): the installed copy's
160
+ own `bin/projectstore.mjs`, with its path spelled out in the startup line —
161
+ `node "<plugin cache>/bin/projectstore.mjs" upgrade --harness claude-code --no-register --project "$PWD"`.
162
+ `--no-register` leaves your plugin registration as it is.
163
+ - **From npm**: `npx projectstore-claude@<version> upgrade --no-register --project "$PWD"`.
164
+
165
+ Both forms move only the project's files, so neither touches your plugin
166
+ registration. The same run re-stamps the status-line launcher at its new path
167
+ and re-registers the agents block, whose template is now v4. A plain
168
+ `npx projectstore-claude upgrade` on a git-marketplace install does more: it
169
+ also registers the plugin from npm for this checkout and turns the
170
+ git-marketplace copy off here, so `/plugin update` stops reaching the
171
+ checkout. The 0.28.0-rc.1 and rc.2 startup lines named that shell form
172
+ (`npx projectstore-claude@<version> upgrade`, without `--no-register`), so on
173
+ those, update first and run what the new startup line names. If it already
174
+ happened,
175
+ `npx projectstore-claude@<version> uninstall --surface plugin --project "$PWD"`
176
+ (0.28.0 or later) turns the git-marketplace copy back on. That copy must be 0.28 or later — an
177
+ older one reads a moved project as unbound — so update it first if it is not.
178
+ Restart, then run
179
+ `/projectstore:doctor --fix` in the new session, which re-stamps the status
180
+ line against that copy.
181
+ - **The status line keeps rendering.** A launcher written by an earlier version
182
+ still works, but it carries no file stamp and its embedded fallback root is
183
+ frozen at the old version; the move above re-stamps it. Nothing rewrites that
184
+ file behind your back any more: first wiring and refresh are `install`'s,
185
+ behind a preview.
136
186
  - **`/projectstore:status` and `/projectstore:search` answer differently:**
137
187
  facts from artifact frontmatter and the derived views' freshness instead
138
188
  of an `mtime` walk; a literal, bounded, grouped search instead of a shell
@@ -144,9 +194,29 @@ an existing project sees afterwards, and why:
144
194
  a crash.
145
195
  - **The plugin registers an MCP server** (eight read-only tools over the
146
196
  vault). Claude Code may ask you to approve it once.
147
- - **Rolling back** to an earlier version works; that version's first session
148
- overwrites the stamped launcher, and coming forward again costs the same
149
- one `--fix`.
197
+ - **The agents block stays where Claude Code reads it.** In a project with an
198
+ `AGENTS.md`, the block goes there and `CLAUDE.md` carries a one-line
199
+ `@AGENTS.md` import; otherwise the block goes into `CLAUDE.md`. A project with
200
+ an `AGENTS.md` and no `CLAUDE.md` now gains that one-line `CLAUDE.md`.
201
+ - **The passive skills are published under the `projectstore-` prefix**
202
+ (`projectstore-decision-detector`, `projectstore-peer-reviewer`,
203
+ `projectstore-story-completion`, `projectstore-vault-communication`). Nothing
204
+ in a project names them; only a skill listing shows the new names.
205
+ - **Rolling back** to 0.27.x before the move: everything keeps working, since
206
+ 0.27.x still reads the old layout. It rewrites the status-line launcher in its
207
+ own form, as it always did; coming forward again, the startup line names the
208
+ move once more, and the move re-stamps the launcher. If a session already
209
+ re-stamped the status line or re-registered the agents block before the move
210
+ (rc.2's `/projectstore:doctor --fix` did both), 0.27.x reports a foreign
211
+ status line and a v4 block and leaves both alone — unless you run its
212
+ `/projectstore:agents register`, which rewrites the block; the status line
213
+ still renders, and the move settles both.
214
+ - **Rolling back** to 0.27.x after the move: 0.27.x looks for its binding under
215
+ `.claude/`, finds none and offers `bind`. Do not accept. A re-bind writes
216
+ `.claude/projectstore.json` again, and 0.28's `install` and `upgrade` refuse
217
+ while two bindings exist. To come forward again, delete
218
+ `.claude/projectstore.json`, then run the command the startup line names
219
+ once more: a 0.27.x session writes its welcome marker back under `.claude/`.
150
220
  - **Installed from npm?** Then `/plugin update` has nothing to fetch: the
151
221
  registration is refreshed by the package itself — from a terminal outside
152
222
  the session, `npx projectstore-claude@<version> upgrade --project "$PWD"`
@@ -193,11 +263,11 @@ The deep dive — real session files, measured payloads, how every mechanism wor
193
263
 
194
264
  ## Uninstalling
195
265
 
196
- `/plugin uninstall projectstore@SmartAndPoint` for a git-marketplace install; `npx projectstore-claude uninstall --project "$PWD"` (from a terminal) for an npm one — it forgets the registration for this checkout, turns a silenced git copy back on, and removes the local marketplace directory only when no other checkout uses it. Your vault is yours — plain markdown, untouched. One leftover of the `/plugin` path: the agents block in `CLAUDE.md`/`AGENTS.md`. Before uninstalling, run `/projectstore:agents unregister` (which runs the core's `uninstall --surface agents_block` for this harness), or delete everything between `<!-- projectstore:agents … -->` and `<!-- /projectstore:agents -->` by hand.
266
+ `/plugin uninstall projectstore@SmartAndPoint` for a Claude git-marketplace install; `npx projectstore-claude uninstall --project "$PWD"` for its npm registration. For Codex, `npx projectstore-codex uninstall --project "$PWD"` removes only project-owned wiring; add `--global` only to remove the user-global Codex plugin and marketplace. Your vault is yours — plain markdown, untouched. One leftover of a host-managed plugin path can be the agents block in `CLAUDE.md`/`AGENTS.md`; remove it with the harness's agents unregister skill, or delete everything between `<!-- projectstore:agents … -->` and `<!-- /projectstore:agents -->` by hand. `uninstall` leaves a block that lives in `AGENTS.md`, because that file is read by other coding agents too, and removes the `CLAUDE.md` import when that file holds nothing else; `uninstall --surface agents_block` removes the block as well.
197
267
 
198
268
  ## Extending
199
269
 
200
- See [`docs/extending.md`](./docs/extending.md) for adding layouts, templates, and skills.
270
+ See [`docs/extending.md`](./docs/extending.md) for adding layouts, templates, and skills, and [`docs/harnesses.md`](./docs/harnesses.md) for which coding agents projectstore runs on — what "experimental" means there, and what adding one takes.
201
271
 
202
272
  ## Contributing
203
273
 
@@ -20,7 +20,7 @@ subcommand (`.projectstore/projectstore.json`; else point to `/projectstore:bind
20
20
  It renders the block from the installed plugin's template ∩ the layout's
21
21
  roster (`scaffold/layouts/<layout>.json` — only routable agents get lines;
22
22
  the entry-rule line, the instruction-conflict line, the
23
- model-resolution line and the vault-communication line always stay), places it (`AGENTS.md` when it
23
+ model-resolution line and the vault-native communication line always stay), places it (`AGENTS.md` when it
24
24
  exists, else `CLAUDE.md`; a block in the other file is migrated, never
25
25
  duplicated; `CLAUDE.md` gets an `@AGENTS.md` import), previews every
26
26
  write, and applies because the harness is named. A current block is
@@ -28,7 +28,13 @@ subcommand (`.projectstore/projectstore.json`; else point to `/projectstore:bind
28
28
  prose byte-identical.
29
29
  3. A non-zero exit is a refusal — a duplicated or unclosed block, a missing
30
30
  template — relay it and stop. Never write the block with the Write or Edit
31
- tool: the verb is the only writer (install spec, contract 6).
31
+ tool: the verb is the only writer (install spec, contract 6). One exception,
32
+ read from the output, not assumed: when it shows the block applied and the
33
+ `layout` item skipped as deferred to a terminal outside the session, the
34
+ exit 1 is that deferral. The block is registered — say so, and for the move
35
+ relay the command the startup line or `/projectstore:doctor`'s
36
+ `layout-legacy` finding names; never compose one. Any other non-zero exit
37
+ is a refusal.
32
38
 
33
39
  ## `unregister` — remove what register added
34
40
 
@@ -21,7 +21,12 @@ You are running projectstore diagnostics (ADR-005: umbrella doctor).
21
21
  2. **No findings** → done. One line: "Doctor is clean — N info note(s) above."
22
22
 
23
23
  3. **`--fix` requested** → walk the *install-side* findings only, one
24
- AskUserQuestion per repair, never batched silently:
24
+ AskUserQuestion per repair, never batched silently. **When `layout-legacy`
25
+ is in the report, it goes first and its command is the one repair** for a
26
+ stale-launcher `surface`, a v3 `agents-block` and `agents-in-binding` as
27
+ well: relay it and run nothing in-session for those (see its bullet below).
28
+ The report already shows the v3 block, and a block Claude Code cannot see,
29
+ as info that points at the move.
25
30
  - `worktree-unbound` → this checkout is a git worktree of a bound one. Offer
26
31
  `/projectstore:bind --inherit`, and say what it does: copies the parent's
27
32
  binding, leaves the vault shared and unchanged, carries no session state.
@@ -32,7 +37,10 @@ You are running projectstore diagnostics (ADR-005: umbrella doctor).
32
37
  - `agents-block` duplicate or stale → show the finding, then (after approval)
33
38
  run `node "${CLAUDE_PLUGIN_ROOT}/bin/projectstore.mjs" install --harness claude-code --surface agents_block --project "${CLAUDE_PROJECT_DIR}"`
34
39
  and print its output: it removes the copy in the non-preferred file and
35
- keeps the preferred one current. Never Edit or Write the block yourself —
40
+ keeps the preferred one current. A block Claude Code cannot see — in
41
+ `AGENTS.md`, with no `@AGENTS.md` line in `CLAUDE.md` — is the same repair:
42
+ the verb adds the import. For another harness the finding names its own
43
+ `--harness`; relay that command. Never Edit or Write the block yourself —
36
44
  the verb is its only writer (install spec, contract 6).
37
45
  - `statusline` issues → offer running `/projectstore:statusline on|off`,
38
46
  which installs or removes the entry and the launcher behind a preview
@@ -49,7 +57,8 @@ You are running projectstore diagnostics (ADR-005: umbrella doctor).
49
57
  - `upgrade` (an info the SessionStart line carries, not a row of this
50
58
  report: a launcher written before file stamps existed) → in this report
51
59
  the same file is the `surface` issue above; the `upgrade` command re-stamps
52
- it in one run.
60
+ it in one run. While `layout-legacy` is pending the startup line does not
61
+ carry it: the move re-stamps the launcher at its new path.
53
62
  - `surface-foreign` → **never repairable.** A file under our prefix with no
54
63
  provenance line is not ours: no `--fix` flow may edit, delete, move or
55
64
  overwrite it. Print the finding verbatim and relay its resolution — rename
@@ -63,10 +72,20 @@ You are running projectstore diagnostics (ADR-005: umbrella doctor).
63
72
  - `layout-legacy` (warn; the startup line carries it as an offer) → the project
64
73
  still holds the pre-0.28 layout (`.claude/projectstore.json`,
65
74
  `.claude/.projectstore/` — legacy, read through 0.29). The migration is one
66
- previewed `layout` item of the `projectstore-claude` shell's `upgrade`, run
67
- **from a terminal outside this session** (it moves files this session reads and writes; the verb defers
68
- inside one). Relay the finding's command verbatim; never move the files
69
- yourself.
75
+ previewed `layout` item of `upgrade`, run **from a terminal outside this
76
+ session** (it moves files this session reads and writes; the verb defers
77
+ inside one). The finding names the form for the channel this plugin was
78
+ installed through: the installed copy's own `bin/projectstore.mjs` for a
79
+ git-marketplace install or a checkout, the `projectstore-claude` shell for
80
+ the npm registration. Relay the finding's command verbatim and never
81
+ substitute the other form — the shell, run for a git-marketplace install,
82
+ would also move this checkout to the npm channel. When the finding carries
83
+ advice instead of a command — the installed copy predates the move, or
84
+ predates `--no-register` — relay the advice and never compose a command
85
+ yourself. Never move the files yourself. While this finding is in the report, its command is also the one
86
+ repair for a stale-launcher `surface`, a v3 `agents-block` and
87
+ `agents-in-binding`: do not run the in-session `install` or `upgrade` for
88
+ those — the deferred move makes that run stop part-way.
70
89
  - `layout-two-configs` (issue) → both `.claude/projectstore.json` (legacy) and
71
90
  `.projectstore/projectstore.json` exist: `install` and `upgrade` refuse until
72
91
  one is deleted. Show both, ask the user which is the binding they mean, and
@@ -47,7 +47,7 @@ vault-side layout or template override):
47
47
  its index and stay there.
48
48
 
49
49
  4. **Checklist entry** — `scaffold/checklists.json`, consumed by
50
- `/projectstore:review` and the peer-reviewer skill. English-only by design.
50
+ `/projectstore:review` and the `projectstore-peer-reviewer` skill. English-only by design.
51
51
 
52
52
  5. **Command prompt** — `commands/<kind>.md`, a prompt (not code) that calls
53
53
  `node "${CLAUDE_PLUGIN_ROOT}/scripts/draft.mjs" <kind> "$ARGUMENTS"`, previews,
@@ -97,7 +97,7 @@ Every command that writes or edits a file goes through `AskUserQuestion`:
97
97
  One consequence the prompt tells you about: the regeneration rewrites the
98
98
  whole table, so a creation can also repair a stale row for another artifact.
99
99
 
100
- Skills (decision-detector, story-completion) are passive — they suggest commands; they never write directly.
100
+ Skills (`projectstore-decision-detector`, `projectstore-story-completion`) are passive — they suggest commands; they never write directly.
101
101
 
102
102
  ## Disabling skills
103
103
 
@@ -0,0 +1,159 @@
1
+ # Harnesses
2
+
3
+ projectstore's engine is one package. What differs between coding agents —
4
+ where a hook is declared, whether a subagent can be shipped, which environment
5
+ variable names the project — lives in `harnesses/<id>.json`, one **capability
6
+ manifest** per harness, and nowhere else. No script branches on a harness name;
7
+ the portability suite greps for it.
8
+
9
+ This page says which harnesses exist, how far each one is trusted, and what
10
+ "experimental" means when you read it below.
11
+
12
+ ## Status
13
+
14
+ | Harness | id | Status | Verified | Surfaces installed |
15
+ |---|---|---|---|---|
16
+ | Claude Code | `claude-code` | **supported** | 2026-08-30 | hooks, commands, agents, skills, MCP, status line, agents block |
17
+ | Codex | `codex` | **experimental** | — | portable plugin, hooks, rendered workflow skills, agents block |
18
+
19
+ **supported** means the manifest carries a `verified` block: a session id and a
20
+ date on which this harness's surfaces were installed and exercised end to end,
21
+ and the measurements in the manifest came from that run.
22
+
23
+ **experimental** means `verified` is `null`. Every field that was not measured
24
+ is absent or `null` rather than guessed. It is enough to run on; it is not enough
25
+ to promise. An experimental harness is never the source layout. A generated
26
+ adapter may exist while it is experimental, but its distribution shell stays
27
+ private until the complete built artifact passes the live gate.
28
+
29
+ The label is not prose. It is derived from the manifest, and
30
+ `tests/portability.test.mjs` fails if this table and `verified` disagree.
31
+
32
+ ## Claude Code
33
+
34
+ The **source layout**: the repository's own `hooks/`, `commands/`, `agents/`,
35
+ `skills/` and `.mcp.json` are Claude Code's, written by hand and read directly.
36
+ Every other harness is generated from them. Exactly one manifest may say
37
+ `source_layout: true`, which is what keeps "the original" a single place.
38
+
39
+ Config lives in `<project>/.projectstore/`, harness-neutral, shared by every
40
+ harness in the project. The per-harness half — the agents block, which carries
41
+ model names and those are harness-specific — is
42
+ `<project>/.projectstore/harness/claude-code.json`.
43
+
44
+ ## Codex
45
+
46
+ Experimental, measured on `codex-cli 0.153.4`. The initial spike captured 759
47
+ hook firings. The 2026-09-30 gate then built the npm shell from a packed core,
48
+ passed Codex's plugin validator, installed and upgraded it through an isolated
49
+ `CODEX_HOME`, verified the materialised cache by version and digest, and loaded
50
+ `$projectstore-status` in a fresh Codex session. The validator (the
51
+ plugin-creator skill's `validate_plugin.py`) reads only
52
+ `.codex-plugin/plugin.json`, so the canonical root `plugin.json` has not been
53
+ validated.
54
+
55
+ That run exercised one skill, not every installed surface, so `verified` stays
56
+ `null`. The hooks are the reason it matters. On 2026-10-03 the first real
57
+ install's cached hooks could not load the vault in any session. They were run
58
+ by hand through `zsh -lc`; the failure did not depend on the shell form. In the
59
+ first session the failure sat in the model's context under the welcome, which
60
+ was all the user saw. The core had taken the shell's root for its own. That is
61
+ fixed, and the suite now runs every rendered hook from the built shell. A live
62
+ Codex session firing them from an installed release is still owed.
63
+
64
+ How 0.153.4 starts a hook was read from its source, not measured: under the
65
+ session's shell as `<shell> -c`, with the environment the Codex process had when
66
+ it built the session's hooks. `$SHELL -lc` (or `/bin/sh -lc`) is the fallback
67
+ when the hooks are built without exactly one ready local environment. So a hook
68
+ finds `node` on the Codex process's own `PATH`, plus whatever `.zshenv` adds:
69
+ zsh reads `.zshenv` for a `-c` command, not `.zprofile` or `.zshrc`, so a Codex
70
+ not started from a terminal can miss a `node` that only `.zprofile` puts on
71
+ `PATH`.
72
+
73
+ The shell is not published. Build it and install the tarball through npx from
74
+ this checkout:
75
+
76
+ ```sh
77
+ npm run shells:build -- --only projectstore-codex --dev --out dist
78
+ npx --package "./dist/projectstore-codex-$(node -p 'require("./package.json").version').tgz" projectstore-codex install --project "$PWD"
79
+ ```
80
+
81
+ The shell package is release-gated until its first npm publication. From this
82
+ repository, the same flow is exercised against the packed tarball. Installation
83
+ is user-global because Codex stores marketplace registrations and plugin caches
84
+ in `CODEX_HOME`; the agents block in `AGENTS.md` remains project-local. After
85
+ the first explicit npm publication, the shorter registry form is
86
+ `npx projectstore-codex@<version> install --project "$PWD"` (or `upgrade`). Ordinary
87
+ uninstall leaves the global plugin in place; add `--global` only when you mean
88
+ to remove it for every project.
89
+
90
+ What is known, and how:
91
+
92
+ - **Hooks are rendered; their live firing from an installed release is not
93
+ yet observed.** The canonical portable `plugin.json` selects
94
+ `./hooks/hooks.json` through `extensions.com.openai`; the compatibility
95
+ `.codex-plugin/plugin.json` stays inside the current ingestion schema. Five
96
+ events: `SessionStart`, `PreToolUse`, `PostToolUse`, `Stop`, `PreCompact`.
97
+ Of the 759 captured firings, 757 came from the earlier inline form, across
98
+ `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse` and `Stop`,
99
+ and 2 from a file-site `hooks/hooks.json` with an absolute `node` path.
100
+ `PreCompact` has never been observed firing on Codex, and neither has the
101
+ `${PLUGIN_ROOT}` form selected through `extensions.com.openai`. The hook process
102
+ receives `PLUGIN_ROOT` in its environment, naming the shell's root with the
103
+ core beneath it in `node_modules/projectstore/`, and **no project-directory
104
+ variable at all**. The project comes from the payload's `cwd`, which every
105
+ projectstore hook adopts before it resolves anything.
106
+ - **Trust is granted per hook, machine-wide**, and it lags: a release that
107
+ changes hooks may not take effect until the session after next.
108
+ - **Source commands are not shipped.** Codex has no registrable root slash command, and
109
+ shipping `commands/` is actively harmful — it rewrites them into skills itself
110
+ and leaves `${CLAUDE_PLUGIN_ROOT}` in the body, producing entry points that
111
+ exit 1. They are rendered as skills instead.
112
+ - **Roles are rendered as orchestration skills.** Codex spawns subagents through
113
+ its collaboration tool, not by loading a plugin's `agents/` directory. The
114
+ six ProjectStore roles therefore ship as namespaced skills that resolve their
115
+ configured model through the core and ask Codex to spawn the role. No effort
116
+ is forced: it inherits unless the user has configured a model policy.
117
+ - **Multi-file edits reach the activity log and entry rule when their paths
118
+ are absolute.** Codex's `apply_patch` carries paths inside
119
+ `tool_input.command`; the shared extractor reads every `Add`, `Update`,
120
+ `Delete` and `Move to` path from that measured envelope field. A relative
121
+ path is not yet resolved against the payload's `cwd`, so it is not recorded.
122
+ The field name remains manifest data, not a Codex branch.
123
+ - **MCP does not ship.** Our `.mcp.json` is in Claude Code's dialect.
124
+
125
+ Codex also sets Claude Code's `CLAUDE_PLUGIN_ROOT` for compatibility. A variable
126
+ two harnesses both set identifies neither, so both manifests demote it: it stops
127
+ being evidence for *either* harness rather than being evidence for the first file
128
+ in alphabetical order.
129
+
130
+ ## Running both over one project
131
+
132
+ Nothing is shared that could collide. The binding
133
+ (`<project>/.projectstore/projectstore.json`) is harness-neutral and one file;
134
+ the agents block is per harness; the session state is keyed by harness id. Two
135
+ harnesses in one project write two overlays and neither disturbs the other.
136
+
137
+ Within one machine, run them in separate **git worktrees** over the same
138
+ checkout, as parallel Claude Code sessions already do. The vault itself is a git
139
+ repository with its own remote — that is how it syncs between people and their
140
+ agents.
141
+
142
+ ## Adding a harness
143
+
144
+ A harness is a manifest plus measurements, not code:
145
+
146
+ 1. Write `harnesses/<id>.json`. Copy the field list from an existing one; leave
147
+ `verified: null` and omit or null every value you have not observed. A field
148
+ that is `null` with a reason is a decision on the record; an absent field is
149
+ one someone forgot.
150
+ 2. Measure. Install it, run a real task, capture the hook payloads. The values
151
+ that matter first: which environment variables the hook process receives,
152
+ where hooks are declared, which tool writes files and where that tool puts the
153
+ path.
154
+ 3. Declare any variable the harness sets that belongs to another harness under
155
+ `runtime.shared_env`, or detection will answer with someone else's id.
156
+ 4. Add a row to the table above. It stays **experimental** until the manifest
157
+ carries a `verified` block — the test enforces the pairing in both directions.
158
+
159
+ See [`docs/extending.md`](./extending.md) for the surfaces themselves.
@@ -222,7 +222,16 @@
222
222
  "format": "skill-md",
223
223
  "scope": "user",
224
224
  "scope_reason": "Claude Code loads skills from the plugin installation itself, not from a per-project directory.",
225
- "registered_by": "plugin"
225
+ "registered_by": "plugin",
226
+ "frontmatter_required": [
227
+ "name",
228
+ "description"
229
+ ],
230
+ "frontmatter_reason": "`description` is what Claude Code matches a skill on; `name` was added 2026-09-08 because Codex's documentation requires it and this is the tree both harnesses load until the generator renders one per harness. Harmless here, required there.",
231
+ "invocation": null,
232
+ "invocation_reason": "UNMEASURED. Claude Code activates a skill from its description rather than from a form the user types; there is no explicit call syntax to record, and null says that rather than leaving the key out and reading as forgotten.",
233
+ "unrendered_source": false,
234
+ "unrendered_source_reason": "This IS the source tree (source_layout: true), so there is nothing to render and no foreign vocabulary to leak into it."
226
235
  },
227
236
  "hooks": {
228
237
  "supported": true,
@@ -244,6 +253,8 @@
244
253
  "AGENTS.md",
245
254
  "CLAUDE.md"
246
255
  ],
256
+ "reads_natively": "CLAUDE.md",
257
+ "reads_natively_reason": "The file this harness reads BY ITSELF. `files` is the block's placement preference and conflates two things: AGENTS.md is first because it is the cross-tool convention, but Claude Code reaches it only through the @AGENTS.md import WE install — remove the bridge and the block is invisible. So installing for this harness must leave CLAUDE.md present: holding the block when it lands here, holding the import when it lands in AGENTS.md. Measured 2026-09-09: a project with an AGENTS.md and no CLAUDE.md took the block into AGENTS.md and created no bridge, so the harness that ran the install could not see it.",
247
258
  "source": "templates/claude-md-block.md.tmpl",
248
259
  "marker": {
249
260
  "open": "<!-- projectstore:agents v",
@@ -461,9 +472,12 @@
461
472
  "",
462
473
  "Without it, you would run `/plugin marketplace update SmartAndPoint` manually. See https://github.com/SmartAndPoint/ProjectStore#upgrading for details.",
463
474
  "",
464
- "**Upgrading from 0.27.x to 0.28**: restart after the update. The status line keeps rendering; the first session names the one step — `/projectstore:doctor --fix` — that re-stamps the launcher written before file stamps existed. `status` and `search` answer from artifact facts; `doctor` gains surface, version-drift and mcp lines and exits 1 on findings; the plugin registers an MCP server the host may ask you to approve once. Rolling back overwrites the stamp; coming forward costs the same one `--fix`."
475
+ "**Upgrading from 0.27.x to 0.28**: restart after the update, then move the project's files to `.projectstore/` with the one command the first session names, run from a terminal outside the session: for a git-marketplace install, the installed copy's own `bin/projectstore.mjs upgrade --harness claude-code --no-register --project <dir>`, which leaves the registration as it is; for the npm registration, `npx projectstore-claude@<version> upgrade --no-register --project <dir>`. The same run re-stamps the status-line launcher, and until then every reader falls back to the old paths. `status` and `search` answer from artifact facts; `doctor` gains surface, version-drift and mcp lines and exits 1 on findings; the plugin registers an MCP server the host may ask you to approve once. On 0.28.0-rc.1 or rc.2, update first: their startup line named the shell form without `--no-register` (`npx projectstore-claude@<version> upgrade`), which also switches a git-marketplace install to npm for the checkout (the README's Upgrading section has the way back). Before the move, a rollback to 0.27.x keeps working and the move is the way forward again; after the move, 0.27.x reads the project as unbound."
465
476
  ],
466
477
  "agent_translation": {
467
- "carries_model": true
478
+ "carries_model": true,
479
+ "cheap_model": "sonnet",
480
+ "cheap_model_evidence": "measured",
481
+ "cheap_model_reason": "The model the clerk is pinned to when a strong default would otherwise lift it (ADR-008). A literal here, in the one file allowed to name a harness's vocabulary; it used to be a literal in scripts/cli.mjs, which made `agents configure --harness codex` write an Anthropic model name into a Codex overlay. MEASURED in the sense that matters: this is the model the clerk has been running on since ADR-008, in this repository, and tests/overlay.test.mjs asserts the pin end to end."
468
482
  }
469
483
  }