projectstore-claude 0.29.4 → 0.30.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.
- package/README.md +13 -12
- package/bin/projectstore-claude.mjs +1 -1
- package/node_modules/projectstore/.claude-plugin/marketplace.json +1 -1
- package/node_modules/projectstore/.claude-plugin/plugin.json +1 -1
- package/node_modules/projectstore/README.md +137 -78
- package/node_modules/projectstore/docs/extending.md +11 -6
- package/node_modules/projectstore/docs/getting-started.md +21 -1
- package/node_modules/projectstore/docs/harnesses.md +13 -10
- package/node_modules/projectstore/docs/how-it-works.md +1 -1
- package/node_modules/projectstore/harnesses/claude-code.json +11 -10
- package/node_modules/projectstore/harnesses/codex.json +8 -8
- package/node_modules/projectstore/package.json +1 -1
- package/node_modules/projectstore/scripts/fetch-shell.mjs +97 -34
- package/node_modules/projectstore/scripts/install-harness.mjs +27 -8
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
# projectstore-claude
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **To install ProjectStore, run `npx projectstore setup` from a terminal in your project.** This package is a release artifact; you do not need it.
|
|
4
|
+
|
|
5
|
+
The Claude Code distribution shell of [projectstore](https://www.npmjs.com/package/projectstore): the core, pinned at exactly this version and bundled inside this tarball, with the harness fixed to Claude Code.
|
|
6
|
+
|
|
7
|
+
From a terminal in your project:
|
|
4
8
|
|
|
5
9
|
```sh
|
|
6
|
-
npx projectstore
|
|
10
|
+
npx projectstore setup
|
|
7
11
|
```
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- See before you write: `npx projectstore-claude plan --project "$PWD"` prints the same plan and writes nothing; `--verbose` adds every row's reasoning; `npx projectstore-claude <verb> --help` lists a verb's options with examples.
|
|
13
|
+
Setup finds the coding agents the project uses, Claude Code among them, and installs ProjectStore for each, with a vault for the project's notes. It asks a few questions, prints one plan — every write and every host command, verbatim — and asks once before anything runs. For Claude Code it registers the plugin for that checkout at the host's local scope, through a small local marketplace under your Claude home. Restart Claude Code afterwards.
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
- Behind a registry mirror: if `npx` answers `could not determine executable to run`, the mirror's index knows only the reserved `0.0.1` placeholder — name the version, and if needed the public registry: `npx --registry https://registry.npmjs.org projectstore-claude@<version> install --project "$PWD"`.
|
|
15
|
-
- Uninstall: `npx projectstore-claude uninstall --project "$PWD"` — forgets the registration for that checkout and nothing else; your vault is plain markdown and stays yours.
|
|
16
|
-
- `doctor`, `status`, `search` and the other read verbs pass through unchanged: `npx projectstore-claude doctor --json`.
|
|
15
|
+
Upgrade, or pin, from the same place; the version you name is the version you run:
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
```sh
|
|
18
|
+
npx projectstore@<version> upgrade
|
|
19
|
+
```
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
This shell's bin is `projectstore <verb> --harness claude-code` and nothing more. `bin/projectstore-claude.mjs` locates the bundled core under `node_modules/projectstore/` and execs it; a different `--harness` is refused (exit 2). The shell carries no plugin of its own: the plugin Claude Code loads is the bundled core.
|
|
21
22
|
|
|
22
|
-
- Docs: https://github.com/SmartAndPoint/ProjectStore#install
|
|
23
|
+
- Docs: https://github.com/SmartAndPoint/ProjectStore#install
|
|
23
24
|
- Source: https://github.com/SmartAndPoint/ProjectStore
|
|
24
25
|
- Issues: https://github.com/SmartAndPoint/ProjectStore/issues
|
|
25
26
|
|
|
@@ -57,7 +57,7 @@ function fixHarness(argv) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
const other = given.find((g) => g !== HARNESS);
|
|
60
|
-
if (other !== undefined) return { error: `installs for ${HARNESS} only — \`--harness ${other}\` names another harness. Run
|
|
60
|
+
if (other !== undefined) return { error: `installs for ${HARNESS} only — \`--harness ${other}\` names another harness. Run the core: npx projectstore <verb> --harness ${other} …` };
|
|
61
61
|
if (given.length) return { argv }; // named already: pass through, never twice (the core's option repeats)
|
|
62
62
|
const at = scan.findIndex((a) => !a.startsWith("-"));
|
|
63
63
|
if (at === -1 || !HARNESS_VERBS.has(scan[at])) return { argv };
|
|
@@ -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.
|
|
15
|
+
"version": "0.30.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.
|
|
4
|
+
"version": "0.30.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
|
[](https://github.com/SmartAndPoint/ProjectStore/releases) [](./LICENSE) [](https://github.com/SmartAndPoint/ProjectStore/stargazers)
|
|
6
6
|
|
|
7
|
-
A project workflow plugin for [Claude Code](https://claude.com/claude-code) and [OpenAI Codex](https://developers.openai.com/codex/) (experimental). Both are released together at one version, and
|
|
7
|
+
A project workflow plugin for [Claude Code](https://claude.com/claude-code) and [OpenAI Codex](https://developers.openai.com/codex/) (experimental). Both are released together at one version, and one command sets a project up for either or both: see [Install](#install).
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -75,24 +75,40 @@ In Claude Code a band above the prompt goes further (experimental): the session'
|
|
|
75
75
|
|
|
76
76
|
Open the vault in [Obsidian](https://obsidian.md) and you get the graph view and the board for free. Don't use Obsidian? Everything renders on GitHub and in any editor.
|
|
77
77
|
|
|
78
|
-
## Install
|
|
78
|
+
## Install
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
From a terminal in your project:
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
```
|
|
83
|
+
npx projectstore setup
|
|
84
|
+
```
|
|
83
85
|
|
|
84
|
-
|
|
86
|
+
Setup finds the coding agents the project uses — Claude Code, Codex or both — and installs ProjectStore for each, with a vault for the project's notes. At a terminal it asks a few questions: which folder is the project, which agents to install for, where the vault lives (an existing folder is bound; a missing or empty one becomes a new vault, with its own git repository and the layout's folders) and whether to show ProjectStore in the status line. Then it prints one plan — every file it writes and every host command, verbatim — and asks once before it writes anything. Restart the agent afterwards; Codex asks you to approve the ProjectStore hooks once.
|
|
85
87
|
|
|
86
|
-
|
|
88
|
+
`npx projectstore setup --dry-run` shows the questions and the plan and writes nothing. Without a terminal (a script, CI, an agent's tool) nothing is asked: `--harness <id>` names an agent and is the confirmation, and `--vault <path>` names the vault. `npx projectstore setup --help` lists the rest.
|
|
89
|
+
|
|
90
|
+
If you prefer a global command:
|
|
87
91
|
|
|
88
92
|
```
|
|
89
|
-
|
|
93
|
+
npm install -g projectstore
|
|
94
|
+
projectstore setup
|
|
90
95
|
```
|
|
91
96
|
|
|
92
|
-
|
|
97
|
+
Behind a registry mirror (often a company's), `npx` runs the newest `projectstore` the mirror knows. That can be a release older than setup, which answers that `setup` is an unknown verb, or no release at all. Name the version, and if that is not enough, the public registry:
|
|
93
98
|
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
```
|
|
100
|
+
npm_config_registry=https://registry.npmjs.org npx projectstore@<version> setup
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
npm reads that variable wherever it runs, so the Codex plugin setup fetches comes from the same registry.
|
|
104
|
+
|
|
105
|
+
### In a Claude Code session
|
|
106
|
+
|
|
107
|
+
Open Claude Code in your project and say:
|
|
108
|
+
|
|
109
|
+
> Install the projectstore plugin from https://github.com/SmartAndPoint/ProjectStore and set it up for this project.
|
|
110
|
+
|
|
111
|
+
Claude adds the git marketplace, installs the plugin, and walks you through binding a vault, scaffolding it and wiring the status line — every step previewed, nothing written without your Yes. Or type it yourself:
|
|
96
112
|
|
|
97
113
|
```
|
|
98
114
|
/plugin marketplace add SmartAndPoint/ProjectStore
|
|
@@ -103,49 +119,31 @@ Then restart Codex, approve the ProjectStore hooks when it asks, and run `$proje
|
|
|
103
119
|
|
|
104
120
|
One switch worth flipping: Claude Code does **not** auto-update third-party plugins by default — `/plugin` → **Marketplaces** → **SmartAndPoint** → toggle **auto-update** on. If you skip it, `/projectstore:doctor` will remind you later with the exact setting.
|
|
105
121
|
|
|
106
|
-
Contributors: `git clone` this repo, then
|
|
107
|
-
|
|
108
|
-
**Or from npm, in one command** — from a terminal, not inside a Claude Code session:
|
|
122
|
+
Contributors: `git clone` this repo, then run Claude Code from the clone's parent folder:
|
|
109
123
|
|
|
110
124
|
```
|
|
111
|
-
|
|
125
|
+
claude --plugin-dir ./ProjectStore
|
|
112
126
|
```
|
|
113
127
|
|
|
114
|
-
|
|
128
|
+
<details>
|
|
129
|
+
<summary>What setup does for each agent, the low-level verbs, the CLI and MCP</summary>
|
|
115
130
|
|
|
116
|
-
**
|
|
117
|
-
`npx projectstore-codex install --project "$PWD"`. It carries a Codex
|
|
118
|
-
plugin manifest, namespaced workflow and role skills, lifecycle hooks, and
|
|
119
|
-
the exact bundled core. It stages a stable marketplace under `CODEX_HOME`,
|
|
120
|
-
drives `codex plugin marketplace add` and `codex plugin add`, then reads the
|
|
121
|
-
installation back and verifies its version and payload digest. Before it
|
|
122
|
-
changes anything it asks Codex for that read-back (`codex plugin list --json`),
|
|
123
|
-
so a Codex too old to answer stops the registration before it changes anything
|
|
124
|
-
and tells you to upgrade Codex. Restart Codex,
|
|
125
|
-
approve the hooks, and run `$projectstore-bind <vault-path>`. For later
|
|
126
|
-
releases, `npx projectstore-codex@<version> upgrade --project "$PWD"`. Because
|
|
127
|
-
Codex's plugin registry is user-global, ordinary uninstall removes only the
|
|
128
|
-
project's agents block; `uninstall --global` is the explicit machine-wide
|
|
129
|
-
removal. If `npx` answers `could not determine executable to run`, your
|
|
130
|
-
registry (often a company mirror) serves a stale index that knows only the
|
|
131
|
-
reserved `0.0.1` placeholder: name the version, and if that is not enough, the
|
|
132
|
-
public registry: `npx --registry https://registry.npmjs.org
|
|
133
|
-
projectstore-codex@<version> install --project "$PWD"` (the same holds for
|
|
134
|
-
`projectstore-claude`). Codex is **experimental** until a live session has exercised every
|
|
135
|
-
surface it installs; [`docs/harnesses.md`](./docs/harnesses.md) says what has
|
|
136
|
-
been measured and what has not. From a checkout, the same npx path runs against
|
|
137
|
-
a built tarball:
|
|
131
|
+
**Claude Code.** Setup registers the plugin from the npm package [`projectstore`](https://www.npmjs.com/package/projectstore) — one source package carrying every harness's manifest. 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. The host CLI and a live session both rewrite the same settings files, so inside a Claude Code session the registration is deferred, and the plan says so: run setup from a terminal outside the session. When the checkout already loads ProjectStore from the git marketplace, setup asks whether to switch this project to the npm install or keep the git marketplace. A switch silences the git-marketplace copy for this checkout only (not globally), so the plugin does not load twice; `uninstall` turns it back on.
|
|
138
132
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
133
|
+
**Codex.** The Codex plugin ships in a package of its own; setup fetches it at setup's own version and plans Codex in the same plan as everything else. It stages a stable marketplace under `CODEX_HOME`, drives `codex plugin marketplace add` and `codex plugin add`, then reads the installation back and verifies its version and payload digest. Before it changes anything it asks Codex for that read-back (`codex plugin list --json`), so a Codex too old to answer stops the registration before it changes anything and tells you to upgrade Codex. Codex's plugin registry is user-global; the agents block in `AGENTS.md` stays project-local. Codex is **experimental** until a live session has exercised every surface it installs: [`docs/harnesses.md`](./docs/harnesses.md#codex) says what has been verified and what has not. A project can be worked from both agents: they share the binding in `.projectstore/`, and each keeps its own overlay and session state beside it ([running both over one project](./docs/harnesses.md#running-both-over-one-project)).
|
|
134
|
+
|
|
135
|
+
**The low-level verbs.** Setup composes verbs that also run on their own. One agent's install, with no questions about the project or the vault:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
npx projectstore install --harness claude-code # shows the plan, asks at a terminal, then writes; without a terminal naming the harness is the confirmation — there is no --yes
|
|
142
139
|
```
|
|
143
140
|
|
|
144
|
-
|
|
141
|
+
`plan` prints the same plan and writes nothing; `--verbose` adds every row's reasoning; `npx projectstore <verb> --help` lists a verb's options with examples. bun works the same on the packed bin.
|
|
142
|
+
|
|
143
|
+
Without a session — in CI, or in a shell — the same core answers token-free, with a `--json` envelope on every verb:
|
|
145
144
|
|
|
146
145
|
```
|
|
147
146
|
npx projectstore doctor --json
|
|
148
|
-
npx projectstore install --harness claude-code # the low-level form the shell runs: shows the plan, asks at a terminal, then writes; without a terminal naming the harness is the confirmation — there is no --yes
|
|
149
147
|
npx projectstore reconcile --write --only kanban
|
|
150
148
|
```
|
|
151
149
|
|
|
@@ -175,43 +173,76 @@ npx projectstore init ~/vaults/new-project --language ru
|
|
|
175
173
|
npx projectstore scaffold --write
|
|
176
174
|
```
|
|
177
175
|
|
|
178
|
-
`projectstore-claude`, `projectstore-codex` and `projectstore-opencode` are
|
|
176
|
+
`projectstore-claude`, `projectstore-codex` and `projectstore-opencode` are release artifacts: per-harness packages that carry the core, pinned at the same version and bundled, with the harness fixed. Nobody needs to type their names. Setup fetches the Codex one, which carries the Codex plugin; the Claude Code one is published at the core's version too; the opencode one publishes after its plugin root is rendered. The other `projectstore-*` names are reserved placeholders pointing back here. One source package, one version, N tarballs. How a release builds and publishes them, and how to run a checkout's own build: [`packaging/README.md`](./packaging/README.md).
|
|
179
177
|
</details>
|
|
180
178
|
|
|
181
179
|
## Upgrading
|
|
182
180
|
|
|
183
|
-
|
|
184
|
-
|
|
181
|
+
Installed with setup or from npm: from a terminal in your project, name the version you want to run (`latest` is the newest):
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
npx projectstore@<version> upgrade
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
It upgrades every agent the project uses — each one whose own directory (`.claude/`, `.codex/`) or `.projectstore/state/<id>/` is there — in one plan, and fetches the Codex plugin at the same version. `npx projectstore setup` converges a project the same way and asks its questions again. Installed from the git marketplace in a Claude Code session: `/plugin update` (or auto-update) and a restart.
|
|
188
|
+
|
|
189
|
+
A bare upgrade selects agents by those directories, so on Codex name it, which upgrades Codex even in a project where no Codex session has run yet:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
npx projectstore@<version> upgrade --harness codex
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
With the global command, update it first, then run it in the project:
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
npm install -g projectstore@latest
|
|
199
|
+
projectstore upgrade
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
A project bound before 0.28 needs one more command. What an existing project sees afterwards, and why:
|
|
185
203
|
|
|
186
204
|
- **The project's files move to `.projectstore/`.** `.claude/projectstore.json`
|
|
187
205
|
and `.claude/.projectstore/` become `.projectstore/projectstore.json`,
|
|
188
206
|
`.projectstore/harness/claude-code.json` and `.projectstore/state/`. Nothing
|
|
189
207
|
breaks before you move them: every reader falls back to the old paths through
|
|
190
|
-
0.
|
|
191
|
-
every Claude Code session in the project, run that command from a terminal
|
|
192
|
-
then restart. The command depends on how you installed
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
208
|
+
0.30, and the startup line names the command until the move is done. Close
|
|
209
|
+
every Claude Code session in the project, run that command from a terminal in
|
|
210
|
+
the project, then restart. The command depends on how you installed. From the
|
|
211
|
+
git marketplace (every 0.27.x install), it is the installed copy's own
|
|
212
|
+
`bin/projectstore.mjs`, with its path spelled out in the startup line:
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
node "<plugin cache>/bin/projectstore.mjs" upgrade --harness claude-code --no-register
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
From npm:
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
npx projectstore@<version> upgrade --harness claude-code --no-register
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`--no-register` leaves your plugin registration as it is: both forms move
|
|
225
|
+
only the project's files. The same run re-stamps the status-line launcher at
|
|
226
|
+
its new path and re-registers the agents block, whose template is now v4. An
|
|
227
|
+
upgrade without `--no-register` on a git-marketplace install does more: it
|
|
203
228
|
also registers the plugin from npm for this checkout and turns the
|
|
204
229
|
git-marketplace copy off here, so `/plugin update` stops reaching the
|
|
205
|
-
checkout. The 0.28.0-rc.1 and rc.2 startup lines named that
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
230
|
+
checkout. The 0.28.0-rc.1 and rc.2 startup lines named that form, without
|
|
231
|
+
`--no-register`, so on those, update first and run what the new startup line
|
|
232
|
+
names. If it already happened, this turns the git-marketplace copy back on
|
|
233
|
+
(0.28.0 or later), from a terminal in the project:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
npx projectstore@<version> uninstall --harness claude-code --surface plugin
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
That copy must be 0.28 or later — an older one reads a moved project as
|
|
240
|
+
unbound — so update it first if it is not. Restart, then in the new session,
|
|
241
|
+
to re-stamp the status line against that copy:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
/projectstore:doctor --fix
|
|
245
|
+
```
|
|
215
246
|
- **The status line keeps rendering.** A launcher written by an earlier version
|
|
216
247
|
still works, but it carries no file stamp and its embedded fallback root is
|
|
217
248
|
frozen at the old version; the move above re-stamps it. Nothing rewrites that
|
|
@@ -242,9 +273,9 @@ bound before 0.28. What an existing project sees afterwards, and why:
|
|
|
242
273
|
move once more, and the move re-stamps the launcher. If a session already
|
|
243
274
|
re-stamped the status line or re-registered the agents block before the move
|
|
244
275
|
(rc.2's `/projectstore:doctor --fix` did both), 0.27.x reports a foreign
|
|
245
|
-
status line and a v4 block and leaves both alone — unless you run
|
|
246
|
-
`/projectstore:agents register
|
|
247
|
-
still renders, and the move settles both.
|
|
276
|
+
status line and a v4 block and leaves both alone — unless you run
|
|
277
|
+
`/projectstore:agents register` in it, which rewrites the block; the status
|
|
278
|
+
line still renders, and the move settles both.
|
|
248
279
|
- **Rolling back** to 0.27.x after the move: 0.27.x looks for its binding under
|
|
249
280
|
`.claude/`, finds none and offers `bind`. Do not accept. A re-bind writes
|
|
250
281
|
`.claude/projectstore.json` again, and 0.28's `install` and `upgrade` refuse
|
|
@@ -252,11 +283,11 @@ bound before 0.28. What an existing project sees afterwards, and why:
|
|
|
252
283
|
`.claude/projectstore.json`, then run the command the startup line names
|
|
253
284
|
once more: a 0.27.x session writes its welcome marker back under `.claude/`.
|
|
254
285
|
- **Installed from npm?** Then `/plugin update` has nothing to fetch: the
|
|
255
|
-
|
|
256
|
-
the session,
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
286
|
+
package itself refreshes the registration. The upgrade at the top of this
|
|
287
|
+
section, run from a terminal outside the session, rewrites the local
|
|
288
|
+
marketplace and runs the host's `plugin update` for this checkout.
|
|
289
|
+
`/projectstore:doctor` says when the registration is behind the package, and
|
|
290
|
+
names the command.
|
|
260
291
|
|
|
261
292
|
## When an agent starts a task, it can find its way
|
|
262
293
|
|
|
@@ -297,7 +328,25 @@ The deep dive — real session files, measured payloads, how every mechanism wor
|
|
|
297
328
|
|
|
298
329
|
## Uninstalling
|
|
299
330
|
|
|
300
|
-
|
|
331
|
+
From a terminal in your project:
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
npx projectstore uninstall
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
With no `--harness` it covers every agent the project uses — each one whose own directory (`.claude/`, `.codex/`) or `.projectstore/state/<id>/` is there — and removes what install wrote, and only that. For Codex that is the project-owned wiring: its plugin and marketplace are user-global, and removing them for every project is explicit:
|
|
338
|
+
|
|
339
|
+
```
|
|
340
|
+
npx projectstore uninstall --harness codex --global
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
A Claude Code install from the git marketplace is removed in the session:
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
/plugin uninstall projectstore@SmartAndPoint
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
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. A one-harness `uninstall`, with `--harness <id>`, leaves a block that lives in `AGENTS.md`, because that file is read by other coding agents too. Claude Code's then removes the `CLAUDE.md` import when that file holds nothing else; Codex's keeps it, so Claude Code still sees the block. `uninstall --surface agents_block` removes the block as well. Once every harness that can read the block's file is among those uninstalled (for `AGENTS.md`, Claude Code and Codex), the block goes too, and the `CLAUDE.md` import with it unless your own text keeps both files.
|
|
301
350
|
|
|
302
351
|
## Extending
|
|
303
352
|
|
|
@@ -322,7 +371,17 @@ One formatting-only commit is listed in `.git-blame-ignore-revs`. GitHub skips i
|
|
|
322
371
|
git config blame.ignoreRevsFile .git-blame-ignore-revs
|
|
323
372
|
```
|
|
324
373
|
|
|
325
|
-
A branch opened before that commit rebases in two steps, so that the lint fixes before it are kept
|
|
374
|
+
A branch opened before that commit rebases in two steps, so that the lint fixes before it are kept. First onto the commit before it, resolving real conflicts as usual:
|
|
375
|
+
|
|
376
|
+
```sh
|
|
377
|
+
git rebase <the commit before it>
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Then onto the formatting commit itself, where every conflict is formatting only: take your side, format the file with `npx prettier --write <file>` and `git add` it, then continue.
|
|
381
|
+
|
|
382
|
+
```sh
|
|
383
|
+
git rebase --onto <it> <the commit before it>
|
|
384
|
+
```
|
|
326
385
|
|
|
327
386
|
## License
|
|
328
387
|
|
|
@@ -49,12 +49,17 @@ vault-side layout or template override):
|
|
|
49
49
|
4. **Checklist entry** — `scaffold/checklists.json`, consumed by
|
|
50
50
|
`/projectstore:review` and the `projectstore-peer-reviewer` skill. English-only by design.
|
|
51
51
|
|
|
52
|
-
5. **Command prompt** — `commands/<kind>.md`, a prompt (not code)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
5. **Command prompt** — `commands/<kind>.md`, a prompt (not code). It renders
|
|
53
|
+
the draft with the line below, previews it, and gates every write behind
|
|
54
|
+
AskUserQuestion:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/draft.mjs" <kind> "$ARGUMENTS"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Copy `commands/research.md` for a plain kind, `commands/adr.md` for one
|
|
61
|
+
that renders the draft's `collision`/`warnings` fields and updates an
|
|
62
|
+
index, `commands/spec.md` for one with status transitions.
|
|
58
63
|
|
|
59
64
|
6. **Folder strings** — `templates/<lang>/strings.json` → `folders.<kind>`,
|
|
60
65
|
a `name` and a one-line `description`, in **every** bundled language's
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Getting started with projectstore
|
|
2
2
|
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
From a terminal in your project:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
npx projectstore setup
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Setup finds the coding agents the project uses (Claude Code, Codex or both), asks a few questions — among them where the vault lives — then shows one plan and asks once before it writes. It binds the vault, or creates one when the folder is missing or empty. The README's [Install](../README.md#install) section has the details, the global form and the registry-mirror form.
|
|
12
|
+
|
|
13
|
+
The next two sections are the other ways in: a checkout, and the git marketplace inside a Claude Code session. After either of them, the first-time setup below binds the vault.
|
|
14
|
+
|
|
3
15
|
## Install (local dev)
|
|
4
16
|
|
|
5
17
|
Until projectstore is published to a public marketplace, install it from a local clone.
|
|
@@ -132,6 +144,14 @@ What the language does and does not change: section headings, table labels and p
|
|
|
132
144
|
|
|
133
145
|
## Updating to a new version
|
|
134
146
|
|
|
147
|
+
Installed with setup or from npm: from a terminal in your project, name the version you want to run:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
npx projectstore@<version> upgrade
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Installed from the git marketplace, in a Claude Code session:
|
|
154
|
+
|
|
135
155
|
```
|
|
136
156
|
/plugin marketplace update SmartAndPoint
|
|
137
157
|
/reload-plugins
|
|
@@ -139,4 +159,4 @@ What the language does and does not change: section headings, table labels and p
|
|
|
139
159
|
|
|
140
160
|
Or enable auto-update once (`/plugin` → **Marketplaces** → **SmartAndPoint** → toggle **auto-update**) and Claude Code will detect new releases at startup.
|
|
141
161
|
|
|
142
|
-
**After any update, run `/projectstore:doctor
|
|
162
|
+
**After any update, run `/projectstore:doctor` in a session.** It compares your project's wiring against what the new version expects and names each fix with the command to run: a stale agents block in `CLAUDE.md` gets `/projectstore:agents register` as its fix and leftover agent copies that override nothing get `/projectstore:agents configure` as theirs. It also names auto-update still off (the exact setting and file) and a newer release than the one running. `doctor --fix` applies the install-side repairs interactively; `/projectstore:reconcile` rebuilds the board/indexes/code-map if content drifted. Silence at session start means healthy — the cheap checks run automatically and only speak up when something is wrong.
|
|
@@ -75,24 +75,27 @@ not started from a terminal can miss a `node` that only `.zprofile` puts on
|
|
|
75
75
|
`PATH`.
|
|
76
76
|
|
|
77
77
|
The shell is published as `projectstore-codex` from 0.28.1, ahead of that live
|
|
78
|
-
run, by the maintainer's decision:
|
|
78
|
+
run, by the maintainer's decision. Nobody types its name: setup, install and
|
|
79
|
+
upgrade fetch it at their own version and plan Codex from it. From a terminal
|
|
80
|
+
in your project:
|
|
79
81
|
|
|
80
82
|
```sh
|
|
81
|
-
npx projectstore
|
|
83
|
+
npx projectstore setup
|
|
82
84
|
```
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
Installation is user-global because Codex stores marketplace registrations and
|
|
86
|
-
plugin caches in `CODEX_HOME`; the agents block in `AGENTS.md` remains
|
|
87
|
-
project-local. Ordinary uninstall leaves the global plugin in place; add
|
|
88
|
-
`--global` only when you mean to remove it for every project. From this
|
|
89
|
-
checkout, the same flow runs against a built tarball:
|
|
86
|
+
To upgrade Codex alone, naming the version you want to run:
|
|
90
87
|
|
|
91
88
|
```sh
|
|
92
|
-
|
|
93
|
-
npx --package "./dist/projectstore-codex-$(node -p 'require("./package.json").version').tgz" projectstore-codex install --project "$PWD"
|
|
89
|
+
npx projectstore@<version> upgrade --harness codex
|
|
94
90
|
```
|
|
95
91
|
|
|
92
|
+
Installation is user-global because Codex stores marketplace registrations and
|
|
93
|
+
plugin caches in `CODEX_HOME`; the agents block in `AGENTS.md` remains
|
|
94
|
+
project-local. Ordinary uninstall leaves the global plugin in place; add
|
|
95
|
+
`--global` only when you mean to remove it for every project. A checkout's own
|
|
96
|
+
build runs through the same flow: [`packaging/README.md`](../packaging/README.md#running-a-checkouts-build)
|
|
97
|
+
has the tarball loop.
|
|
98
|
+
|
|
96
99
|
What is known, and how:
|
|
97
100
|
|
|
98
101
|
- **Hooks load from Codex's default file, so the shell carries no root
|
|
@@ -191,7 +191,7 @@ resistance. v0.13 makes that safe instead of forbidden:
|
|
|
191
191
|
sees Bash-mediated writes, which bypass hook path extraction entirely.
|
|
192
192
|
|
|
193
193
|
The division of labor: deterministic checks catch the mechanical 90% for free;
|
|
194
|
-
the LLM critic
|
|
194
|
+
the LLM critic that `/projectstore:review` runs is saved for the judgment calls.
|
|
195
195
|
|
|
196
196
|
## Naming a session after the work it settled on
|
|
197
197
|
|
|
@@ -11,26 +11,27 @@
|
|
|
11
11
|
"date": "2026-09-04"
|
|
12
12
|
},
|
|
13
13
|
"install": {
|
|
14
|
-
"mechanism": "the package's own registration
|
|
14
|
+
"mechanism": "the package's own registration (setup or install, from npm), or the built-in plugin marketplace",
|
|
15
15
|
"shell": "projectstore-claude",
|
|
16
|
-
"shell_reason": "the distribution shell
|
|
17
|
-
"why_not_scripted": "Two channels.
|
|
16
|
+
"shell_reason": "the distribution shell of this harness on npm (the layout ADR decision 6; the shells ADR): `projectstore-claude install` is the core's `install --harness claude-code`, with the core pinned at the same version and bundled inside the shell's tarball. Data, not `projectstore-<id>`: the id is claude-code and the reserved, published name is projectstore-claude. Since the front-door ADR the shell is a release artifact: no command a person is told to type names it (packageCommand builds the core's form, `npx projectstore <verb> --harness claude-code`), and this key tells the core which harness a running shell fixes (harness.mjs bundlingShellHarnessId, speakingHarnessId).",
|
|
17
|
+
"why_not_scripted": "Two channels. The npm package registers the plugin for a project itself — run `npx projectstore setup` or the core's low-level `install --harness claude-code` from a terminal in the project — through a local marketplace under the Claude home, driven by Claude Code's own `claude plugin` CLI. `/plugin install projectstore@SmartAndPoint` installs it from the git marketplace. `/plugin` lists and toggles either. The npm registration is upgraded, and pinned, by `npx projectstore@<version> upgrade` from a terminal in the project.",
|
|
18
18
|
"steps": [
|
|
19
|
-
"
|
|
20
|
-
"/plugin marketplace add SmartAndPoint/ProjectStore && /plugin install projectstore@SmartAndPoint
|
|
19
|
+
"npm, for this checkout (the core registers the plugin), from a terminal in the project: npx projectstore install --harness claude-code",
|
|
20
|
+
"git marketplace, user-wide, in a Claude Code session: /plugin marketplace add SmartAndPoint/ProjectStore && /plugin install projectstore@SmartAndPoint",
|
|
21
21
|
"restart Claude Code"
|
|
22
22
|
],
|
|
23
23
|
"next": [
|
|
24
24
|
"restart Claude Code in this project"
|
|
25
25
|
],
|
|
26
26
|
"notes": [
|
|
27
|
-
"Git marketplace only: auto-update is OFF by default for third-party plugins: /plugin -> Marketplaces -> SmartAndPoint -> toggle auto-update. The npm registration updates when you run `npx projectstore
|
|
28
|
-
"Pin a git-marketplace release
|
|
27
|
+
"Git marketplace only: auto-update is OFF by default for third-party plugins: /plugin -> Marketplaces -> SmartAndPoint -> toggle auto-update. The npm registration updates when you run `npx projectstore@<version> upgrade` from a terminal in the project.",
|
|
28
|
+
"Pin a git-marketplace release by adding the marketplace at a tag: /plugin marketplace add SmartAndPoint/ProjectStore#<tag>",
|
|
29
|
+
"The npm registration is pinned by the version its upgrade names: npx projectstore@<version> upgrade",
|
|
29
30
|
"Two enabled copies of the plugin (git marketplace and npm) in one checkout would load twice; the npm registration silences the other one for that checkout only (settings.local.json), and says so in its preview.",
|
|
30
31
|
"From a checkout instead: claude --plugin-dir ./ProjectStore",
|
|
31
32
|
"The npm registration is per checkout (local scope) and per machine; a fresh clone or a new worktree runs the install command again. It must run from a terminal outside a Claude Code session: the host CLI and a live session both rewrite settings files."
|
|
32
33
|
],
|
|
33
|
-
"docs": "https://github.com/SmartAndPoint/ProjectStore#install
|
|
34
|
+
"docs": "https://github.com/SmartAndPoint/ProjectStore#install"
|
|
34
35
|
},
|
|
35
36
|
"runtime": {
|
|
36
37
|
"detect_env": [
|
|
@@ -498,14 +499,14 @@
|
|
|
498
499
|
"reason": "What the first-run welcome and its one-line notice say about updates (generation spec, contract 18): `line` ends the notice, `welcome` is the welcome's update block. update_instructions is the long-form guidance; this is the part a session prints, kept here so no hook names a harness's update path."
|
|
499
500
|
},
|
|
500
501
|
"update_instructions": [
|
|
501
|
-
"**About future updates**: installed from the git marketplace, Claude Code does NOT auto-update third-party marketplaces by default
|
|
502
|
+
"**About future updates**: installed from the git marketplace, Claude Code does NOT auto-update third-party marketplaces by default; an npm registration updates with `npx projectstore@<version> upgrade` from a terminal in the project. To get notified of new git-marketplace releases (v0.7+):",
|
|
502
503
|
"1. Open `/plugin` → **Marketplaces** tab.",
|
|
503
504
|
"2. Find **SmartAndPoint**.",
|
|
504
505
|
"3. Toggle **auto-update** on.",
|
|
505
506
|
"",
|
|
506
507
|
"Without it, you would run `/plugin marketplace update SmartAndPoint` manually. See https://github.com/SmartAndPoint/ProjectStore#upgrading for details.",
|
|
507
508
|
"",
|
|
508
|
-
"**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
|
|
509
|
+
"**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 in the project, outside the session. For a git-marketplace install it is the installed copy's own `bin/projectstore.mjs` with `upgrade --harness claude-code --no-register`, which leaves the registration as it is. For the npm registration it is `npx projectstore@<version> upgrade --harness claude-code --no-register` and nothing else. 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 form without `--no-register`, 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."
|
|
509
510
|
],
|
|
510
511
|
"agent_translation": {
|
|
511
512
|
"carries_model": true,
|
|
@@ -311,32 +311,32 @@
|
|
|
311
311
|
},
|
|
312
312
|
"rewrites": [],
|
|
313
313
|
"install": {
|
|
314
|
-
"mechanism": "the package's portable plugin root,
|
|
314
|
+
"mechanism": "the package's portable plugin root, fetched in its distribution shell and registered through Codex's own marketplace CLI",
|
|
315
315
|
"shell": "projectstore-codex",
|
|
316
316
|
"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.",
|
|
317
|
-
"why_not_scripted": "The
|
|
317
|
+
"why_not_scripted": "The installer does not edit Codex's registry or cache. It atomically stages the plugin source from the shell it fetched, 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.",
|
|
318
318
|
"steps": [
|
|
319
|
-
"npx projectstore
|
|
319
|
+
"from a terminal in the project (the core fetches the Codex shell at its own version): npx projectstore install --harness codex",
|
|
320
320
|
"restart Codex and approve the ProjectStore hooks when prompted",
|
|
321
|
-
"
|
|
321
|
+
"in Codex, bind a vault: $projectstore-bind <vault-path>"
|
|
322
322
|
],
|
|
323
323
|
"next": [
|
|
324
324
|
"restart Codex from a terminal and approve the ProjectStore hooks when it asks"
|
|
325
325
|
],
|
|
326
326
|
"notes": [
|
|
327
327
|
"The installation is user-global because Codex's marketplace, plugin row and cache live in CODEX_HOME; the AGENTS.md block is project-local.",
|
|
328
|
-
"Upgrade with `npx projectstore
|
|
328
|
+
"Upgrade with `npx projectstore@<version> upgrade --harness codex` from a terminal in the project; the version it names is also the plugin version.",
|
|
329
329
|
"A second enabled ProjectStore plugin from another marketplace is a refusal, not silently disabled globally. Remove or disable that older registration explicitly, then retry.",
|
|
330
|
-
"Ordinary uninstall removes only project-owned surfaces. `npx projectstore
|
|
330
|
+
"Ordinary uninstall removes only project-owned surfaces. `npx projectstore uninstall --harness codex --global` also removes the user-global plugin registration after a preview."
|
|
331
331
|
],
|
|
332
|
-
"docs": "https://github.com/SmartAndPoint/ProjectStore#install
|
|
332
|
+
"docs": "https://github.com/SmartAndPoint/ProjectStore#install"
|
|
333
333
|
},
|
|
334
334
|
"update_hint": {
|
|
335
335
|
"line": "Update from a terminal in this project:\n{upgrade}",
|
|
336
336
|
"welcome": ["**About future updates**: from a terminal in this project, run:", "", "```", "{upgrade}", "```", "", "Then restart Codex and approve any hook it asks about. See https://github.com/SmartAndPoint/ProjectStore#upgrading for details."],
|
|
337
337
|
"reason": "What the first-run welcome and its one-line notice say about updates (generation spec, contract 18): `line` ends the notice, `welcome` is the welcome's update block, and `{upgrade}` is filled with the core's upgrade for this harness at latest (`--harness codex`, no `--project`), so what is printed runs as printed. Each says where to run it and puts the command alone on its line (the story \"User-facing text says where to run and names no project path and no shell\", rules 4 and 5). update_instructions is the long-form guidance; this is the part a session prints, kept here so no hook names a harness's update path."
|
|
338
338
|
},
|
|
339
|
-
"update_instructions": "
|
|
339
|
+
"update_instructions": "From a terminal in the project, run `npx projectstore@<version> upgrade --harness codex` and restart Codex afterwards, so the new skills and hooks are loaded. The run fetches the shell at that version, 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.",
|
|
340
340
|
"agent_translation": {
|
|
341
341
|
"carries_model": false,
|
|
342
342
|
"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.",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projectstore",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"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.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"project-management",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
// npmUserRegistry({ home, env }) → the user's own registry, read from home, or null
|
|
19
19
|
// fetchShell(job, { dir, registry }) → { ok, root, ms } or { ok: false, code, refusal }
|
|
20
20
|
// verifyFetched(root, { … }) → null, or the refusal naming file and values
|
|
21
|
-
// fetchRefusal(code, ctx) → rule 4's texts
|
|
21
|
+
// fetchRefusal(code, ctx) → rule 4's texts, the envelope's string
|
|
22
|
+
// fetchRefusalAdvice(code, ctx) → the same, as PLAN draws it: { text, blocks }
|
|
22
23
|
// sweepFetchRuns(fetchDir) → the leftovers of dead runs, removed
|
|
23
24
|
// fetchRunId() → <pid>-<epoch ms>
|
|
24
25
|
//
|
|
@@ -29,8 +30,8 @@
|
|
|
29
30
|
|
|
30
31
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
31
32
|
import { join } from "node:path";
|
|
32
|
-
import { registrationDeferred } from "./harness.mjs";
|
|
33
|
-
import { removeTreeUnder, pidAlive, whichOnPath } from "./lib.mjs";
|
|
33
|
+
import { registrationDeferred, packageCommand } from "./harness.mjs";
|
|
34
|
+
import { removeTreeUnder, pidAlive, whichOnPath, fromTerminalIn } from "./lib.mjs";
|
|
34
35
|
import {
|
|
35
36
|
isPortablePluginRoot,
|
|
36
37
|
payloadManifest,
|
|
@@ -213,12 +214,45 @@ export function npmError(stdout, stderr) {
|
|
|
213
214
|
|
|
214
215
|
const seconds = (ms) => `${Number((ms / 1000).toFixed(ms % 1000 ? 1 : 0))} s`;
|
|
215
216
|
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
|
|
217
|
+
// npm's own registry: the one README's registry-mirror advice names.
|
|
218
|
+
export const PUBLIC_REGISTRY = "https://registry.npmjs.org";
|
|
219
|
+
|
|
220
|
+
// A registry that has no such shell may be a mirror that has not synced it:
|
|
221
|
+
// the run once more, from npm's public registry, in README's form — the
|
|
222
|
+
// variable, not `npx --registry`, because `npm config get registry`, which
|
|
223
|
+
// the fetch's resolver reads, honours it (the story "User-facing text says
|
|
224
|
+
// where to run and names no project path and no shell", rule 7). It is the
|
|
225
|
+
// run the person just saw: its verb and version, and its selection as given —
|
|
226
|
+
// each named --harness, each --surface, and --no-register — never --project
|
|
227
|
+
// (the front-door ADR, decision 5); the directory goes in the lead, and the
|
|
228
|
+
// command stands alone after it (rule 4). `args` is the shape the converge
|
|
229
|
+
// story's stop repeats a run with. Null when the run is unknown, or when the
|
|
230
|
+
// registry is already the public one; a null registry is npm's own
|
|
231
|
+
// resolution, which can be a mirror, so it gets the line.
|
|
232
|
+
function mirrorBlock({ registry = null, verb = null, version = null, args = {}, project = null } = {}) {
|
|
233
|
+
if (!verb || !version || String(registry || "").replace(/\/+$/, "") === PUBLIC_REGISTRY) return null;
|
|
234
|
+
const { harnesses = [], surfaces = null, register = true } = args || {};
|
|
235
|
+
const narrowing = [
|
|
236
|
+
...(surfaces || []).map((s) => `--surface ${s}`),
|
|
237
|
+
...(register === false ? ["--no-register"] : []),
|
|
238
|
+
];
|
|
239
|
+
const command = packageCommand(
|
|
240
|
+
harnesses.map((id) => ({ id })),
|
|
241
|
+
verb,
|
|
242
|
+
{ version, args: narrowing.join(" ") },
|
|
243
|
+
);
|
|
244
|
+
const where = project ? fromTerminalIn(project) : "from a terminal in this project";
|
|
245
|
+
return {
|
|
246
|
+
lead: `A mirror that has not synced it? Run this again from npm's public registry, ${where}:`,
|
|
247
|
+
command: `npm_config_registry=${PUBLIC_REGISTRY} ${command}`,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Rule 4's refusals, as PLAN draws them: `{ text, blocks }`, the prose, then
|
|
252
|
+
// each lead and its command (presentation spec contract 6), so PLAN prints a
|
|
253
|
+
// command whole, unwrapped, alone on its line. Only the missing shell has a
|
|
254
|
+
// block today; every other refusal is its text alone.
|
|
255
|
+
export function fetchRefusalAdvice(code, ctx = {}) {
|
|
222
256
|
const {
|
|
223
257
|
pkg = "the shell",
|
|
224
258
|
shell = "the shell",
|
|
@@ -233,29 +267,61 @@ export function fetchRefusal(code, ctx = {}) {
|
|
|
233
267
|
} = ctx;
|
|
234
268
|
const where = registry || "the registry npm resolves";
|
|
235
269
|
const without = `name --harness without ${display}`;
|
|
270
|
+
const said = (text, blocks = []) => ({ text, blocks });
|
|
236
271
|
switch (code) {
|
|
237
272
|
case "ETARGET":
|
|
238
|
-
case "E404":
|
|
239
|
-
|
|
273
|
+
case "E404": {
|
|
274
|
+
const mirror = mirrorBlock(ctx);
|
|
275
|
+
return said(
|
|
276
|
+
`${pkg} is not on ${where} (npm ${code}); ${display} is registered from that shell at this exact version, and no other version is tried. Just released? The shell is published after the core, so retry in a few minutes. From a checkout at an unpublished version, point PROJECTSTORE_DISTRIBUTION_ROOT at the output of \`npm run shells:build -- --dev\` (dist/build/${shell}). Or ${without}.`,
|
|
277
|
+
mirror ? [mirror] : [],
|
|
278
|
+
);
|
|
279
|
+
}
|
|
240
280
|
// Our budget, not npm's: npm's own ETIMEDOUT (a socket) is any other code.
|
|
241
281
|
case "EBUDGET":
|
|
242
|
-
return
|
|
282
|
+
return said(
|
|
283
|
+
`fetching ${pkg} from ${where} took longer than ${seconds(budget)} and was stopped. Retry, or ${without}.`,
|
|
284
|
+
);
|
|
243
285
|
case "FETCH_TIMEOUT":
|
|
244
|
-
return `npm gave up on ${ctx.url || where} (network timeout) while fetching ${pkg}. Retry, or ${without}
|
|
286
|
+
return said(`npm gave up on ${ctx.url || where} (network timeout) while fetching ${pkg}. Retry, or ${without}.`);
|
|
245
287
|
case "E401":
|
|
246
288
|
case "E403":
|
|
247
|
-
return
|
|
289
|
+
return said(
|
|
290
|
+
`${where} refused the credentials for ${pkg} (npm ${code}). A project .npmrc's token does not reach this fetch — only its registry does — so the registry's token must be in user config: ~/.npmrc, or an NPM_CONFIG_… variable. Or point PROJECTSTORE_DISTRIBUTION_ROOT at a ${shell} root obtained another way.`,
|
|
291
|
+
);
|
|
248
292
|
case "ESTART":
|
|
249
|
-
return
|
|
293
|
+
return said(
|
|
294
|
+
`${cause}; ${display} is registered from its shell, which npm fetches. Put npm on PATH, or ${without}.`,
|
|
295
|
+
);
|
|
250
296
|
case "ENOCACHE":
|
|
251
|
-
return
|
|
297
|
+
return said(
|
|
298
|
+
`the shell fetch has no cache directory: ${variable} is not an absolute path and the home directory is unknown. Set ${variable} to a writable directory, then run this again.`,
|
|
299
|
+
);
|
|
252
300
|
case "ECACHE":
|
|
253
|
-
return
|
|
301
|
+
return said(
|
|
302
|
+
`the cache directory ${path} cannot be written (${ctx.errno || "error"}); it comes from ${from}. Set ${variable} to a writable directory, then run this again.`,
|
|
303
|
+
);
|
|
254
304
|
default:
|
|
255
|
-
return
|
|
305
|
+
return said(
|
|
306
|
+
`npm could not fetch ${pkg} from ${where}: ${code || "an unknown error"}${summary ? ` — ${summary.split(/\r?\n/)[0]}` : ""}. ${display} is registered from its shell, which needs the network: retry once the registry is reachable, or ${without}.`,
|
|
307
|
+
);
|
|
256
308
|
}
|
|
257
309
|
}
|
|
258
310
|
|
|
311
|
+
// Rule 4's refusals, by npm's code or ours: the envelope's string, every part
|
|
312
|
+
// on its own line — the prose, then each lead and its command — which an agent
|
|
313
|
+
// relays as it is. `ctx`: pkg ("<shell>@<v>"), shell, registry (null: npm's
|
|
314
|
+
// own), display (the harness's display name), budget, summary (npm's), cause
|
|
315
|
+
// (a start failure's), path/from/variable (the cache), and the run — its verb,
|
|
316
|
+
// version, project and `args` (the selection as given) — which the missing
|
|
317
|
+
// shell's refusal repeats from the public registry. Every way out is a
|
|
318
|
+
// terminal command or a variable; none names a version but the one this run
|
|
319
|
+
// is at.
|
|
320
|
+
export function fetchRefusal(code, ctx = {}) {
|
|
321
|
+
const { text, blocks } = fetchRefusalAdvice(code, ctx);
|
|
322
|
+
return [text, ...blocks.flatMap((b) => [b.lead, b.command])].join("\n");
|
|
323
|
+
}
|
|
324
|
+
|
|
259
325
|
// Rule 3: the fetched tree is what this run plans, before it is planned —
|
|
260
326
|
// the shell's name and version, a plugin root by the analyser's own predicate,
|
|
261
327
|
// its plugin manifest's name and version, and the bundled core at the same
|
|
@@ -294,26 +360,29 @@ export function verifyFetched(root, { shell, version, pluginName, coreName }) {
|
|
|
294
360
|
// as *** (measured). A failure is a refusal before the plan, never a fallback.
|
|
295
361
|
export async function fetchShell(
|
|
296
362
|
{ harness, s, version, coreName },
|
|
297
|
-
{ dir, registry = null, env = process.env, spawn, budget = HOST_BUDGET_MS } = {},
|
|
363
|
+
{ dir, registry = null, env = process.env, spawn, budget = HOST_BUDGET_MS, run = {} } = {},
|
|
298
364
|
) {
|
|
299
365
|
const shell = harness.install.shell;
|
|
300
366
|
const pkg = `${shell}@${version}`;
|
|
301
|
-
|
|
367
|
+
// `run`: the verb, selection (`args`) and project of the run that fetches,
|
|
368
|
+
// for the missing shell's public-registry form; the version is this one.
|
|
369
|
+
const ctx = { pkg, shell, registry, display: harness.display_name, budget, ...run, version };
|
|
370
|
+
// A refusal in both forms: the envelope's string, and what PLAN draws.
|
|
371
|
+
const refused = (code, extra = {}) => ({
|
|
372
|
+
refusal: fetchRefusal(code, { ...ctx, ...extra }),
|
|
373
|
+
advice: fetchRefusalAdvice(code, { ...ctx, ...extra }),
|
|
374
|
+
});
|
|
302
375
|
const call = npmCall(fetchArgv({ prefix: dir, registry, shell, version }), env);
|
|
303
376
|
const t0 = Date.now();
|
|
304
377
|
const r = await runHost(call.bin, call.argv, { env, cwd: dir, budget, spawn, shell: call.shell });
|
|
305
378
|
const ms = Date.now() - t0;
|
|
306
379
|
if (r.error) {
|
|
307
|
-
if (r.error.code === "ETIMEDOUT")
|
|
308
|
-
return { ok: false, ms, code: "ETIMEDOUT", refusal: fetchRefusal("EBUDGET", ctx) };
|
|
380
|
+
if (r.error.code === "ETIMEDOUT") return { ok: false, ms, code: "ETIMEDOUT", ...refused("EBUDGET") };
|
|
309
381
|
return {
|
|
310
382
|
ok: false,
|
|
311
383
|
ms,
|
|
312
384
|
code: r.error.code || "ESTART",
|
|
313
|
-
|
|
314
|
-
...ctx,
|
|
315
|
-
cause: startFailure(r.error, { bin: "npm", resolved: call.resolved, cwd: dir }),
|
|
316
|
-
}),
|
|
385
|
+
...refused("ESTART", { cause: startFailure(r.error, { bin: "npm", resolved: call.resolved, cwd: dir }) }),
|
|
317
386
|
};
|
|
318
387
|
}
|
|
319
388
|
if (r.status !== 0) {
|
|
@@ -321,14 +390,8 @@ export async function fetchShell(
|
|
|
321
390
|
// npm reports a request it gave up on as FETCH_ERROR, "network timeout
|
|
322
391
|
// at: <url>" (measured in review, 2026-10-08), not as ETIMEDOUT.
|
|
323
392
|
const timeout = e.code === "FETCH_ERROR" && /network timeout at:\s*(\S+)/.exec(e.summary);
|
|
324
|
-
if (timeout)
|
|
325
|
-
|
|
326
|
-
return {
|
|
327
|
-
ok: false,
|
|
328
|
-
ms,
|
|
329
|
-
code: e.code || `exit ${r.status}`,
|
|
330
|
-
refusal: fetchRefusal(e.code, { ...ctx, summary: e.summary }),
|
|
331
|
-
};
|
|
393
|
+
if (timeout) return { ok: false, ms, code: e.code, ...refused("FETCH_TIMEOUT", { url: timeout[1] }) };
|
|
394
|
+
return { ok: false, ms, code: e.code || `exit ${r.status}`, ...refused(e.code, { summary: e.summary }) };
|
|
332
395
|
}
|
|
333
396
|
const root = join(dir, "node_modules", shell);
|
|
334
397
|
const wrong = verifyFetched(root, { shell, version, pluginName: s.plugin_name, coreName });
|
|
@@ -5050,10 +5050,11 @@ async function fetchShells(
|
|
|
5050
5050
|
}
|
|
5051
5051
|
}
|
|
5052
5052
|
if (!jobs.length) return none;
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5053
|
+
// `advice`: the refusal as PLAN draws it, its commands in their own blocks
|
|
5054
|
+
// (fetchRefusalAdvice) — kept off the envelope, as the channel "git"
|
|
5055
|
+
// refusal's is, so `--json` keeps the one string.
|
|
5056
|
+
const refuse = (message, cleanup = () => {}, advice = null) => {
|
|
5057
|
+
const refused = {
|
|
5057
5058
|
projectDir,
|
|
5058
5059
|
mode: "install",
|
|
5059
5060
|
named,
|
|
@@ -5066,8 +5067,10 @@ async function fetchShells(
|
|
|
5066
5067
|
incomplete: false,
|
|
5067
5068
|
root,
|
|
5068
5069
|
plannedAgainst: {},
|
|
5069
|
-
}
|
|
5070
|
-
|
|
5070
|
+
};
|
|
5071
|
+
if (advice?.blocks?.length) Object.defineProperty(refused, "refusalAdvice", { value: advice, enumerable: false });
|
|
5072
|
+
return { ...none, cleanup, refused };
|
|
5073
|
+
};
|
|
5071
5074
|
const cache = cachePaths({ env, home });
|
|
5072
5075
|
if (!cache.root) return refuse(fetchRefusal("ENOCACHE", { variable: cache.variable }));
|
|
5073
5076
|
sweepFetchRuns(cache.fetch);
|
|
@@ -5104,6 +5107,19 @@ async function fetchShells(
|
|
|
5104
5107
|
const differs = Boolean(registry) && bare(registry) !== bare(mine);
|
|
5105
5108
|
const payloadRoots = {},
|
|
5106
5109
|
fetched = [];
|
|
5110
|
+
// The run, for the missing shell's public-registry form: its verb, its
|
|
5111
|
+
// project, and its selection as given, in the shape the stop repeats a
|
|
5112
|
+
// run with — each named --harness (setup's answered set is named too),
|
|
5113
|
+
// each --surface, --no-register.
|
|
5114
|
+
const run = {
|
|
5115
|
+
verb,
|
|
5116
|
+
project: projectDir,
|
|
5117
|
+
args: {
|
|
5118
|
+
harnesses: named ? (opts.harnesses || []).filter(Boolean) : [],
|
|
5119
|
+
surfaces: opts.surfaces || null,
|
|
5120
|
+
register: opts.register !== false,
|
|
5121
|
+
},
|
|
5122
|
+
};
|
|
5107
5123
|
for (const job of jobs) {
|
|
5108
5124
|
const { harness } = job;
|
|
5109
5125
|
const pkg = `${harness.install.shell}@${version}`;
|
|
@@ -5112,10 +5128,13 @@ async function fetchShells(
|
|
|
5112
5128
|
const dir = jobs.length === 1 ? runDir : join(runDir, harness.id);
|
|
5113
5129
|
if (dir !== runDir) mkdirSync(dir, { recursive: true });
|
|
5114
5130
|
line = opts.onFetch ? opts.onFetch(`fetching ${pkg} for ${harness.display_name}`, ids) : null;
|
|
5115
|
-
const r = await fetchShell(
|
|
5131
|
+
const r = await fetchShell(
|
|
5132
|
+
{ ...job, version, coreName },
|
|
5133
|
+
{ dir, registry, env, spawn: opts.fetchSpawn, budget, run },
|
|
5134
|
+
);
|
|
5116
5135
|
if (!r.ok) {
|
|
5117
5136
|
if (line) line.end(false, `fetch failed ${pkg}`, r.code);
|
|
5118
|
-
return refuse(r.refusal, cleanup);
|
|
5137
|
+
return refuse(r.refusal, cleanup, r.advice);
|
|
5119
5138
|
}
|
|
5120
5139
|
if (line) line.end(true, `fetched ${pkg} for ${harness.display_name}`);
|
|
5121
5140
|
line = null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projectstore-claude",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.30.0",
|
|
4
|
+
"description": "projectstore for Claude Code: the core pinned and bundled, the harness fixed. A release artifact; the way in is npx projectstore setup",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"projectstore",
|
|
7
7
|
"installer",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"README.md"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"projectstore": "=0.
|
|
40
|
+
"projectstore": "=0.30.0"
|
|
41
41
|
},
|
|
42
42
|
"bundleDependencies": [
|
|
43
43
|
"projectstore"
|