projectstore-claude 0.30.1 → 0.30.3

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 (30) hide show
  1. package/node_modules/projectstore/.claude-plugin/marketplace.json +1 -1
  2. package/node_modules/projectstore/.claude-plugin/plugin.json +1 -1
  3. package/node_modules/projectstore/AGENTS.md +7 -5
  4. package/node_modules/projectstore/README.md +5 -5
  5. package/node_modules/projectstore/agents/clerk.md +2 -2
  6. package/node_modules/projectstore/commands/agents.md +38 -26
  7. package/node_modules/projectstore/commands/bind.md +1 -1
  8. package/node_modules/projectstore/commands/doctor.md +15 -3
  9. package/node_modules/projectstore/commands/reconcile.md +2 -2
  10. package/node_modules/projectstore/commands/review.md +1 -1
  11. package/node_modules/projectstore/commands/story.md +2 -1
  12. package/node_modules/projectstore/docs/getting-started.md +1 -1
  13. package/node_modules/projectstore/docs/harnesses.md +3 -2
  14. package/node_modules/projectstore/harnesses/claude-code.json +92 -2
  15. package/node_modules/projectstore/harnesses/codex.json +81 -14
  16. package/node_modules/projectstore/package.json +1 -1
  17. package/node_modules/projectstore/scripts/agents-change.mjs +64 -10
  18. package/node_modules/projectstore/scripts/build-adapters.mjs +28 -13
  19. package/node_modules/projectstore/scripts/cli.mjs +32 -9
  20. package/node_modules/projectstore/scripts/configure-menu.mjs +716 -0
  21. package/node_modules/projectstore/scripts/configure.mjs +239 -91
  22. package/node_modules/projectstore/scripts/doctor.mjs +95 -31
  23. package/node_modules/projectstore/scripts/harness.mjs +13 -3
  24. package/node_modules/projectstore/scripts/install-harness.mjs +29 -0
  25. package/node_modules/projectstore/scripts/lib.mjs +32 -9
  26. package/node_modules/projectstore/scripts/menu.mjs +746 -0
  27. package/node_modules/projectstore/scripts/models.mjs +649 -0
  28. package/node_modules/projectstore/scripts/term.mjs +167 -0
  29. package/node_modules/projectstore/templates/claude-md-block.md.tmpl +7 -5
  30. package/package.json +2 -2
@@ -137,6 +137,17 @@ export const GLYPHS = Object.freeze({
137
137
  // A move or a state change in a step's text: `a → b` (decided 2026-10-10,
138
138
  // added to the spec's table at the story's close-out).
139
139
  transition: Object.freeze(["→", "->"]),
140
+ // The settings menu's frames and marks (contract 14, added 2026-10-13): a
141
+ // frame's corners (its sides are the rail and the rule), the cursor on the
142
+ // row the keys act on, the rows a frame hides above and below, and the
143
+ // path line's separator.
144
+ frameTopLeft: Object.freeze(["╭", "+"]),
145
+ frameTopRight: Object.freeze(["╮", "+"]),
146
+ frameBottomLeft: Object.freeze(["╰", "+"]),
147
+ frameBottomRight: Object.freeze(["╯", "+"]),
148
+ cursor: Object.freeze(["›", ">"]),
149
+ moreAbove: Object.freeze(["↑", "^"]),
150
+ moreBelow: Object.freeze(["↓", "v"]),
140
151
  });
141
152
 
142
153
  // The other names a glyph answers to: the spec's shared rows (fail / issue,
@@ -160,6 +171,8 @@ export const GLYPH_ALIASES = Object.freeze({
160
171
  // The question rail's checklist cursor: the row it is on is marked by a
161
172
  // glyph, not by colour alone (contract 3).
162
173
  pointer: "transition",
174
+ // The menu's path line: Settings › Agents · Claude Code › critic.
175
+ pathSeparator: "cursor",
163
176
  });
164
177
 
165
178
  // The spinner (presentation spec contract 4): braille frames, one every 80 ms.
@@ -1090,6 +1103,160 @@ export function openRail({
1090
1103
  };
1091
1104
  }
1092
1105
 
1106
+ // A full screen for the settings menu (presentation spec contract 14): the
1107
+ // terminal's alternate screen, raw keys and a hidden cursor for the screen's
1108
+ // whole life, each draw from the top-left, and the terminal restored on
1109
+ // every path — close(), `exit`, SIGTERM / SIGHUP / SIGINT — as the rail
1110
+ // restores it. What the screen showed is gone from the scrollback when it
1111
+ // closes; the caller prints what should stay after that.
1112
+ //
1113
+ // screen.size() → { columns, rows }
1114
+ // screen.draw(lines) → the lines, each line's rest cleared, then the rest
1115
+ // screen.next() → the next event: a key name (up, down, left, right,
1116
+ // space, enter, escape, backspace, cancel), { char },
1117
+ // "resize", or what wake() was given
1118
+ // screen.wake(event) → an event for next(), from outside the keys (a
1119
+ // background read that answered)
1120
+ // screen.drain(turns) → the keys queued so far dropped, and those arriving in
1121
+ // the next `turns` turns of the loop (the review screen)
1122
+ // screen.close() → the terminal is the caller's again
1123
+ //
1124
+ // Ctrl+C and the end of input are "cancel": an answer, never process.exit.
1125
+ export const ALT_ON = "\x1b[?1049h";
1126
+ export const ALT_OFF = "\x1b[?1049l";
1127
+ export function openScreen({
1128
+ stdin = process.stdin,
1129
+ stdout = process.stdout,
1130
+ caps: c = caps(stdout),
1131
+ proc = process,
1132
+ escapeMs = ESCAPE_MS,
1133
+ } = {}) {
1134
+ if (!railAvailable(stdin, c))
1135
+ throw new Error("no screen: stdin must be a terminal with setRawMode and stdout a live one (railAvailable)");
1136
+ const write = (s) => stdout.write(s);
1137
+ const queue = [];
1138
+ let waiter = null;
1139
+ const push = (e) => {
1140
+ queue.push(e);
1141
+ if (waiter) {
1142
+ const w = waiter;
1143
+ waiter = null;
1144
+ w();
1145
+ }
1146
+ };
1147
+ // A lone Esc is reported after the escape timeout as { name: "escape",
1148
+ // meta: true }: it is ←, never a modifier (the rail reads it the same way).
1149
+ const nameOf = (str, k = {}) => {
1150
+ if (k.ctrl && k.name === "c") return "cancel";
1151
+ if (k.name === "escape") return "escape";
1152
+ if (k.ctrl || k.meta) return null;
1153
+ if (["up", "down", "left", "right", "space", "escape", "backspace"].includes(k.name)) return k.name;
1154
+ if (isEnter(k)) return "enter";
1155
+ return printable({ ...k, str }) ? { char: str } : null;
1156
+ };
1157
+ // While a drain lasts, keys are dropped as they arrive (drain() below).
1158
+ let dropping = false;
1159
+ const onKey = (str, key) => {
1160
+ const e = nameOf(str, key || {});
1161
+ if (e && !(dropping && e !== "cancel")) push(e);
1162
+ };
1163
+ const onEnd = () => push("cancel");
1164
+ const onResize = () => push("resize");
1165
+ let open = true;
1166
+ const restore = () => {
1167
+ try {
1168
+ stdin.setRawMode(false);
1169
+ } catch {}
1170
+ try {
1171
+ write(`${SHOW_CURSOR}${ALT_OFF}`);
1172
+ } catch {}
1173
+ };
1174
+ const onExit = () => restore();
1175
+ const onSignal = {};
1176
+ const release = () => {
1177
+ proc.removeListener("exit", onExit);
1178
+ for (const [sig, f] of Object.entries(onSignal)) proc.removeListener(sig, f);
1179
+ };
1180
+ for (const sig of ["SIGTERM", "SIGHUP", "SIGINT"]) {
1181
+ onSignal[sig] = () => {
1182
+ if (open) restore();
1183
+ open = false;
1184
+ release();
1185
+ try {
1186
+ proc.kill(proc.pid, sig);
1187
+ } catch {}
1188
+ };
1189
+ }
1190
+ proc.once("exit", onExit);
1191
+ for (const [sig, f] of Object.entries(onSignal)) proc.on(sig, f);
1192
+ emitKeypressEvents(stdin, { escapeCodeTimeout: escapeMs });
1193
+ stdin.on("keypress", onKey);
1194
+ stdin.on("end", onEnd);
1195
+ stdin.on("error", onEnd);
1196
+ if (typeof stdout.on === "function") stdout.on("resize", onResize);
1197
+ if (stdin.readableEnded) queue.push("cancel");
1198
+ try {
1199
+ stdin.setRawMode(true);
1200
+ write(`${ALT_ON}${HIDE_CURSOR}`);
1201
+ stdin.resume();
1202
+ } catch (e) {
1203
+ restore();
1204
+ release();
1205
+ throw e;
1206
+ }
1207
+ return {
1208
+ size: () => ({
1209
+ columns: Number(stdout.columns) > 0 ? Number(stdout.columns) : c.columns || 100,
1210
+ rows: Number(stdout.rows) > 0 ? Number(stdout.rows) : 24,
1211
+ }),
1212
+ // Each line after an erase of its row, at most `rows` of them, and no
1213
+ // newline after the last, so the alternate screen never scrolls.
1214
+ draw(lines) {
1215
+ if (!open) return;
1216
+ const rows = Number(stdout.rows) > 0 ? Number(stdout.rows) : 24;
1217
+ write(
1218
+ `\x1b[H${lines
1219
+ .slice(0, rows)
1220
+ .map((l) => `\x1b[2K${l}`)
1221
+ .join("\r\n")}\x1b[J`,
1222
+ );
1223
+ },
1224
+ // The keys queued so far dropped, and those that arrive in the next
1225
+ // `turns` turns of the event loop too: what was in the terminal's buffer
1226
+ // when the review was drawn was typed before it was seen, as drainInput
1227
+ // reasons for the line-mode confirm. Ctrl+C is never dropped.
1228
+ drain: (turns = 0) => {
1229
+ for (let k = queue.length - 1; k >= 0; k--) if (queue[k] !== "cancel") queue.splice(k, 1);
1230
+ if (!turns) return;
1231
+ dropping = true;
1232
+ let n = 0;
1233
+ const step = () => (++n >= turns ? (dropping = false) : setImmediate(step));
1234
+ setImmediate(step);
1235
+ },
1236
+ next: () =>
1237
+ queue.length
1238
+ ? Promise.resolve(queue.shift())
1239
+ : new Promise((settle) => {
1240
+ waiter = () => settle(queue.shift());
1241
+ }),
1242
+ wake: (e) => push(e),
1243
+ close() {
1244
+ if (!open) return;
1245
+ open = false;
1246
+ stdin.removeListener("keypress", onKey);
1247
+ stdin.removeListener("end", onEnd);
1248
+ stdin.removeListener("error", onEnd);
1249
+ if (typeof stdout.removeListener === "function") stdout.removeListener("resize", onResize);
1250
+ queue.length = 0;
1251
+ try {
1252
+ stdin.pause();
1253
+ } catch {}
1254
+ restore();
1255
+ release();
1256
+ },
1257
+ };
1258
+ }
1259
+
1093
1260
  // What was typed ahead of a line-mode question, discarded (the setup story,
1094
1261
  // rule 2: the rail's raw mode ends at `└`, and the confirm after it is
1095
1262
  // askLine's). Keys typed while the run fetched, read the registry and drew
@@ -1,4 +1,4 @@
1
- <!-- projectstore:agents v4 (managed by projectstore — edit outside markers) -->
1
+ <!-- projectstore:agents v5 (managed by projectstore — edit outside markers) -->
2
2
  ## projectstore agents
3
3
 
4
4
  - **A feature-sized request opens a vault artifact before it opens an editor.**
@@ -11,10 +11,12 @@
11
11
  harness-level instruction contradicts this block, say so and ask which wins.
12
12
  Resolving it silently is how the contradiction becomes invisible to the person
13
13
  who could have settled it.
14
- - When spawning any agent below, resolve its model from
15
- `.projectstore/harness/<harness>.json` → `agents.per_agent.<name>.model ?? agents.default.model`,
16
- where `<name>` is the **bare** agent name (`critic` for `projectstore:critic`),
17
- and pass it as the spawn's model parameter. No key — pass nothing.
14
+ - When spawning any agent below, resolve its model and its effort from
15
+ `.projectstore/harness/<harness>.json` → `agents.per_agent.<name>.model ?? agents.default.model`
16
+ and `agents.per_agent.<name>.effort ?? agents.default.effort`, where `<name>` is
17
+ the **bare** agent name (`critic` for `projectstore:critic`), and pass them as
18
+ the spawn's model and effort parameters, the effort only when the spawn offers
19
+ one. No key — pass nothing.
18
20
  - After authoring or revising any vault artifact (ADR/research/epic/story) or
19
21
  design proposal: run the `projectstore:critic` agent on it before treating it final.
20
22
  - Before implementing an epic/story: consult `projectstore:planner` — it plans
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projectstore-claude",
3
- "version": "0.30.1",
3
+ "version": "0.30.3",
4
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",
@@ -37,7 +37,7 @@
37
37
  "README.md"
38
38
  ],
39
39
  "dependencies": {
40
- "projectstore": "=0.30.1"
40
+ "projectstore": "=0.30.3"
41
41
  },
42
42
  "bundleDependencies": [
43
43
  "projectstore"