projectstore-claude 0.29.2 → 0.29.4
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 +1 -1
- 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 +2 -1
- package/node_modules/projectstore/README.md +44 -7
- package/node_modules/projectstore/commands/bar.md +30 -0
- package/node_modules/projectstore/commands/bind.md +16 -20
- package/node_modules/projectstore/commands/doctor.md +12 -7
- package/node_modules/projectstore/commands/scaffold.md +24 -16
- package/node_modules/projectstore/commands/status.md +1 -1
- package/node_modules/projectstore/commands/statusline.md +2 -2
- package/node_modules/projectstore/commands/story.md +15 -2
- package/node_modules/projectstore/docs/alongside.md +167 -0
- package/node_modules/projectstore/docs/extending.md +17 -3
- package/node_modules/projectstore/docs/getting-started.md +18 -9
- package/node_modules/projectstore/docs/harnesses.md +1 -1
- package/node_modules/projectstore/docs/how-it-works.md +12 -3
- package/node_modules/projectstore/harnesses/claude-code.json +23 -8
- package/node_modules/projectstore/harnesses/codex.json +16 -6
- package/node_modules/projectstore/hooks/band-state.d.ts +44 -0
- package/node_modules/projectstore/hooks/band.tsx +622 -0
- package/node_modules/projectstore/hooks/hooks.json +1 -0
- package/node_modules/projectstore/hooks/pre-compact.mjs +7 -10
- package/node_modules/projectstore/hooks/session-rules.mjs +14 -8
- package/node_modules/projectstore/hooks/session-start.mjs +56 -20
- package/node_modules/projectstore/hooks/session-stop.mjs +8 -6
- package/node_modules/projectstore/package.json +12 -2
- package/node_modules/projectstore/scaffold/headings.json +8 -0
- package/node_modules/projectstore/scripts/band-shared.mjs +446 -0
- package/node_modules/projectstore/scripts/band.mjs +207 -0
- package/node_modules/projectstore/scripts/binding.mjs +733 -62
- package/node_modules/projectstore/scripts/build-adapters.mjs +98 -35
- package/node_modules/projectstore/scripts/cli.mjs +1463 -267
- package/node_modules/projectstore/scripts/codemap.mjs +22 -7
- package/node_modules/projectstore/scripts/convergence.mjs +638 -0
- package/node_modules/projectstore/scripts/diff-refs.mjs +14 -8
- package/node_modules/projectstore/scripts/doctor-report.mjs +253 -0
- package/node_modules/projectstore/scripts/doctor.mjs +1384 -420
- package/node_modules/projectstore/scripts/draft.mjs +5 -2
- package/node_modules/projectstore/scripts/fetch-shell.mjs +369 -0
- package/node_modules/projectstore/scripts/graph.mjs +19 -13
- package/node_modules/projectstore/scripts/harness.mjs +283 -37
- package/node_modules/projectstore/scripts/install-harness.mjs +4701 -575
- package/node_modules/projectstore/scripts/kanban.mjs +6 -21
- package/node_modules/projectstore/scripts/lib.mjs +933 -199
- package/node_modules/projectstore/scripts/mcp.mjs +386 -105
- package/node_modules/projectstore/scripts/portable-registration.mjs +276 -56
- package/node_modules/projectstore/scripts/provenance.mjs +26 -10
- package/node_modules/projectstore/scripts/query.mjs +441 -72
- package/node_modules/projectstore/scripts/reconcile.mjs +55 -18
- package/node_modules/projectstore/scripts/run-host.mjs +139 -0
- package/node_modules/projectstore/scripts/scaffold.mjs +244 -0
- package/node_modules/projectstore/scripts/setup.mjs +987 -0
- package/node_modules/projectstore/scripts/stages.mjs +84 -0
- package/node_modules/projectstore/scripts/statusline-launcher.mjs +36 -11
- package/node_modules/projectstore/scripts/statusline.mjs +55 -14
- package/node_modules/projectstore/scripts/story-section.mjs +39 -17
- package/node_modules/projectstore/scripts/surfaces.mjs +485 -99
- package/node_modules/projectstore/scripts/term.mjs +1027 -52
- package/node_modules/projectstore/scripts/tokens.mjs +106 -28
- package/node_modules/projectstore/scripts/touch-session.mjs +109 -70
- package/node_modules/projectstore/scripts/version-guard.mjs +79 -27
- package/node_modules/projectstore/scripts/worktree.mjs +16 -5
- package/node_modules/projectstore/templates/de/strings.json +63 -1
- package/node_modules/projectstore/templates/de/vault-readme.md.tmpl +13 -0
- package/node_modules/projectstore/templates/en/strings.json +63 -1
- package/node_modules/projectstore/templates/en/vault-readme.md.tmpl +13 -0
- package/node_modules/projectstore/templates/es/strings.json +63 -1
- package/node_modules/projectstore/templates/es/vault-readme.md.tmpl +13 -0
- package/node_modules/projectstore/templates/fr/strings.json +63 -1
- package/node_modules/projectstore/templates/fr/vault-readme.md.tmpl +13 -0
- package/node_modules/projectstore/templates/ru/strings.json +63 -1
- package/node_modules/projectstore/templates/ru/vault-readme.md.tmpl +13 -0
- package/node_modules/projectstore/templates/zh/strings.json +63 -1
- package/node_modules/projectstore/templates/zh/vault-readme.md.tmpl +13 -0
- package/package.json +2 -2
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
|
7
7
|
import { dirname, join, relative, resolve } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { emittingHarnesses, sourceHarness, invocation, uiWordPatterns } from "./harness.mjs";
|
|
9
|
+
import { emittingHarnesses, sourceHarness, invocation, uiWordPatterns, hasBand } from "./harness.mjs";
|
|
10
10
|
import { writeFileAtomic } from "./lib.mjs";
|
|
11
11
|
|
|
12
12
|
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -26,19 +26,27 @@ const SOURCE_UI = uiWordPatterns(SOURCE);
|
|
|
26
26
|
// way only the source harness calls it. Never inside another form
|
|
27
27
|
// (`/projectstore:`, `$projectstore-`), never a longer name.
|
|
28
28
|
const ROLE_SUFFIX = SOURCE.surfaces.agents.file.replace("<name>", "");
|
|
29
|
-
const SOURCE_ROLES = readdirSync(join(ROOT, SOURCE.surfaces.agents.dir))
|
|
30
|
-
|
|
29
|
+
const SOURCE_ROLES = readdirSync(join(ROOT, SOURCE.surfaces.agents.dir))
|
|
30
|
+
.filter((f) => f.endsWith(ROLE_SUFFIX))
|
|
31
|
+
.map((f) => f.slice(0, -ROLE_SUFFIX.length))
|
|
32
|
+
.sort();
|
|
33
|
+
const SOURCE_ROLE = new RegExp(
|
|
34
|
+
`(?<![\\w/$:-])${escapeRe(SOURCE.surfaces.agents.invocation.split("<name>")[0])}(${SOURCE_ROLES.map(escapeRe).join("|")})(?![\\w-])`,
|
|
35
|
+
"g",
|
|
36
|
+
);
|
|
31
37
|
const TARGETS = emittingHarnesses();
|
|
32
|
-
if (TARGETS.length !== 1)
|
|
38
|
+
if (TARGETS.length !== 1)
|
|
39
|
+
throw new Error(`this renderer currently requires exactly one emitting harness; found ${TARGETS.length}`);
|
|
33
40
|
const TARGET = TARGETS[0];
|
|
34
41
|
const OUT = resolve(ROOT, TARGET.output_dir);
|
|
35
42
|
|
|
36
43
|
const read = (p) => readFileSync(p, "utf8");
|
|
37
44
|
const json = (p) => JSON.parse(read(p));
|
|
38
|
-
const files = (dir, suffix = ".md") =>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
const files = (dir, suffix = ".md") =>
|
|
46
|
+
readdirSync(dir)
|
|
47
|
+
.filter((name) => name.endsWith(suffix))
|
|
48
|
+
.sort()
|
|
49
|
+
.map((name) => join(dir, name));
|
|
42
50
|
|
|
43
51
|
function frontmatter(text, file) {
|
|
44
52
|
const m = text.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
@@ -48,7 +56,8 @@ function frontmatter(text, file) {
|
|
|
48
56
|
const kv = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
49
57
|
if (!kv) continue;
|
|
50
58
|
let value = kv[2].trim();
|
|
51
|
-
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")))
|
|
59
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")))
|
|
60
|
+
value = value.slice(1, -1);
|
|
52
61
|
data[kv[1]] = value;
|
|
53
62
|
}
|
|
54
63
|
return { data, body: m[2].replace(/^\n+/, "") };
|
|
@@ -92,7 +101,8 @@ function skill(name, description, body, { runtime = true } = {}) {
|
|
|
92
101
|
|
|
93
102
|
const COMMAND_OVERRIDES = {
|
|
94
103
|
agents: {
|
|
95
|
-
description:
|
|
104
|
+
description:
|
|
105
|
+
"Register or remove ProjectStore's shared agents block, inspect its Codex model overlay, or configure per-role models. Arguments: <register | unregister | status | configure>.",
|
|
96
106
|
body: `You are managing ProjectStore's Codex agent integration. Require a bound project.
|
|
97
107
|
|
|
98
108
|
## register / unregister
|
|
@@ -122,32 +132,42 @@ contract. A configured model is consumed by the role-orchestration skills on
|
|
|
122
132
|
their next spawn; no restart is needed.`,
|
|
123
133
|
},
|
|
124
134
|
bind: {
|
|
125
|
-
description:
|
|
135
|
+
description:
|
|
136
|
+
"Bind this project to an existing ProjectStore vault, or initialize and bind a new vault, using the harness-neutral core. Arguments: [vault-path] [--layout <name>] [--language <code>].",
|
|
126
137
|
body: `Bind the current project through the core; never write the binding by
|
|
127
138
|
hand.
|
|
128
139
|
|
|
129
140
|
1. Resolve the requested vault path, and the layout and language when the
|
|
130
141
|
request passes \`--layout <name>\` or \`--language <code>\`. If the vault
|
|
131
|
-
exists, use \`bind
|
|
132
|
-
\`init\`.
|
|
142
|
+
exists and holds files, use \`bind\`. If it exists and is empty, use
|
|
143
|
+
\`init\`. If it is missing, use \`init\` when the user asks to create it.
|
|
144
|
+
\`init\` creates the whole vault: the directory (unless it is there and
|
|
145
|
+
empty), its git repository with no commit, and the layout's folders and
|
|
146
|
+
READMEs. Ask for layout and language only when the request has not
|
|
133
147
|
supplied them.
|
|
134
148
|
2. Show the resolved project, vault, verb, layout and language. Ask for explicit
|
|
135
149
|
approval. Naming the vault is the core's non-interactive confirmation.
|
|
136
150
|
3. Run \`node "\${PROJECTSTORE_CORE_ROOT}/bin/projectstore.mjs" <bind|init>
|
|
137
151
|
"<vault-path>" --layout <layout> --language <language> --project "$PWD"
|
|
138
152
|
--json\` and report the result. Use \`--rebind\` only when the user explicitly
|
|
139
|
-
approved replacing an existing binding.
|
|
140
|
-
|
|
141
|
-
|
|
153
|
+
approved replacing an existing binding. If the result carries
|
|
154
|
+
\`git.failed\` or \`scaffold.failed\`, the binding is written; relay the
|
|
155
|
+
remedy and continue.
|
|
156
|
+
4. Run \`status --json\` to verify the stored path and policies. After
|
|
157
|
+
\`bind\` (an \`init\` vault is already scaffolded), run \`scaffold --json
|
|
158
|
+
--project "$PWD"\` and offer \`$projectstore-scaffold\` only when a row
|
|
159
|
+
reads \`create\`.
|
|
142
160
|
|
|
143
|
-
Codex plugin updates are
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
161
|
+
Codex plugin updates are the core's \`upgrade\` with \`--harness codex\`, which
|
|
162
|
+
the person runs from a terminal in this project; this skill never sends the
|
|
163
|
+
user to another harness's plugin UI.
|
|
164
|
+
Codex has no ProjectStore status-line surface, so binding does not offer or
|
|
165
|
+
write one. Model choices, when requested, go through
|
|
166
|
+
\`$projectstore-agents configure\` and the Codex overlay.`,
|
|
148
167
|
},
|
|
149
168
|
doctor: {
|
|
150
|
-
description:
|
|
169
|
+
description:
|
|
170
|
+
"Diagnose the ProjectStore install and vault through the harness-neutral doctor; fixes remain previewed and approval-gated. Arguments: [--install | --vault] [--fix].",
|
|
151
171
|
body: `Run the deterministic doctor through the bundled core with the user's
|
|
152
172
|
arguments and \`--json\`. Summarize every finding without re-deriving it.
|
|
153
173
|
|
|
@@ -159,9 +179,19 @@ running it; then run \`upgrade --harness codex --json\`, whose envelope is the
|
|
|
159
179
|
result — \`--json\` never waits on a terminal's question. Unsupported
|
|
160
180
|
surfaces remain unsupported; do not create host configuration by hand. Never
|
|
161
181
|
claim a fix after a non-zero exit.`,
|
|
182
|
+
},
|
|
183
|
+
bar: {
|
|
184
|
+
description:
|
|
185
|
+
"Explain ProjectStore band availability for Codex. This surface is unsupported and the skill never writes configuration. Arguments: on | off | status | epic [<ID>].",
|
|
186
|
+
body: `ProjectStore's band above the prompt is not supported by the Codex
|
|
187
|
+
harness: Codex has no surface that draws above the prompt and no module
|
|
188
|
+
runtime to draw it. Do not run the band verb's writes and do not edit project
|
|
189
|
+
or host configuration. Report the surface as unsupported. For project state
|
|
190
|
+
use \`$projectstore-status\`.`,
|
|
162
191
|
},
|
|
163
192
|
statusline: {
|
|
164
|
-
description:
|
|
193
|
+
description:
|
|
194
|
+
"Explain ProjectStore status-line availability for Codex. This surface is unsupported and the skill never writes configuration. Arguments: on | off | status.",
|
|
165
195
|
body: `ProjectStore's status-line surface is not supported by the Codex
|
|
166
196
|
harness. Do not run install or uninstall for \`--surface statusline\`, and do
|
|
167
197
|
not edit project or host configuration. Report the surface as unsupported.
|
|
@@ -188,7 +218,10 @@ function renderCommand(file) {
|
|
|
188
218
|
const override = COMMAND_OVERRIDES[verb];
|
|
189
219
|
if (override) return [join("skills", name, "SKILL.md"), skill(name, override.description, override.body)];
|
|
190
220
|
const hint = data["argument-hint"] ? ` Arguments: ${data["argument-hint"]}.` : "";
|
|
191
|
-
return [
|
|
221
|
+
return [
|
|
222
|
+
join("skills", name, "SKILL.md"),
|
|
223
|
+
skill(name, `${data.description || `Run the ProjectStore ${verb} workflow.`}${hint}`, body),
|
|
224
|
+
];
|
|
192
225
|
}
|
|
193
226
|
|
|
194
227
|
function renderPassive(file) {
|
|
@@ -204,23 +237,32 @@ function renderRole(file) {
|
|
|
204
237
|
const role = file.split("/").pop().replace(/\.md$/, "");
|
|
205
238
|
const name = renderedName("agents", role);
|
|
206
239
|
const orchestration = `## Codex orchestration\n\nThis is a role-orchestration skill, not a native agent registration. Resolve the\nrole model by running:\n\n\`\`\`bash\nnode "\${PROJECTSTORE_CORE_ROOT}/bin/projectstore.mjs" agents model ${role} --json --project "$PWD"\n\`\`\`\n\nSpawn a collaboration agent for the bounded task. If the result names a model,\npass that model and use an empty or bounded context fork; otherwise inherit the\ncurrent model. Do not pass a reasoning-effort override: per-role effort belongs\nto a separate accepted story. Give the spawned agent the role contract below\nand the exact artifact/diff it must inspect. Wait for its final result.\n\n## Role contract\n\n`;
|
|
207
|
-
const description = (data.description || `Run the ProjectStore ${role} role in a fresh collaboration agent.`)
|
|
208
|
-
|
|
240
|
+
const description = (data.description || `Run the ProjectStore ${role} role in a fresh collaboration agent.`).replace(
|
|
241
|
+
/^(?:Opus|Sonnet) \(max-effort\)\s+/,
|
|
242
|
+
"",
|
|
243
|
+
);
|
|
209
244
|
return [join("skills", name, "SKILL.md"), skill(name, description, orchestration + body)];
|
|
210
245
|
}
|
|
211
246
|
|
|
212
|
-
function renderHooks(manifest, root = ROOT) {
|
|
247
|
+
export function renderHooks(manifest, root = ROOT) {
|
|
213
248
|
const source = json(join(root, "hooks", "hooks.json"));
|
|
249
|
+
// The band is a hooks module only a harness with a band runs (the band
|
|
250
|
+
// spec, contract 1): elsewhere `modules` would name a file that harness
|
|
251
|
+
// cannot load, so its tree carries the classic hooks alone.
|
|
252
|
+
if (!hasBand(manifest)) delete source.modules;
|
|
214
253
|
const post = source.hooks?.PostToolUse || [];
|
|
215
254
|
for (const group of post) if (group.matcher) group.matcher = (manifest.tools?.write_tools || []).join("|");
|
|
216
255
|
const rewrite = (value) => {
|
|
217
256
|
if (Array.isArray(value)) return value.map(rewrite);
|
|
218
|
-
if (value && typeof value === "object")
|
|
257
|
+
if (value && typeof value === "object")
|
|
258
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, rewrite(v)]));
|
|
219
259
|
if (typeof value !== "string") return value;
|
|
220
260
|
const sourceRoot = "${" + SOURCE.runtime.plugin_root_env + "}";
|
|
221
261
|
const targetRoot = "${" + manifest.runtime.plugin_root_env + "}";
|
|
222
262
|
const command = value.startsWith(`node ${sourceRoot}/`) ? value.slice(`node ${sourceRoot}/`.length) : null;
|
|
223
|
-
return command
|
|
263
|
+
return command
|
|
264
|
+
? `node "${targetRoot}/node_modules/projectstore/${command}"`
|
|
265
|
+
: value.replaceAll(sourceRoot, `${targetRoot}/node_modules/projectstore`);
|
|
224
266
|
};
|
|
225
267
|
const text = JSON.stringify(rewrite(source), null, 2) + "\n";
|
|
226
268
|
return [join("hooks", "hooks.json"), text];
|
|
@@ -228,7 +270,8 @@ function renderHooks(manifest, root = ROOT) {
|
|
|
228
270
|
|
|
229
271
|
export function renderCodexAdapter(root = ROOT) {
|
|
230
272
|
const manifest = json(join(root, "harnesses", `${TARGET.id}.json`));
|
|
231
|
-
if (!manifest.emit || !manifest.output_dir)
|
|
273
|
+
if (!manifest.emit || !manifest.output_dir)
|
|
274
|
+
throw new Error(`${TARGET.id} must declare an output_dir when emit is true`);
|
|
232
275
|
const rows = [
|
|
233
276
|
...files(join(root, "commands")).map(renderCommand),
|
|
234
277
|
...files(join(root, "agents")).map(renderRole),
|
|
@@ -240,15 +283,34 @@ export function renderCodexAdapter(root = ROOT) {
|
|
|
240
283
|
];
|
|
241
284
|
const out = new Map();
|
|
242
285
|
for (const [rel, text] of rows) {
|
|
243
|
-
if (rel.startsWith("..") || resolve(OUT, rel).slice(0, OUT.length + 1) !== OUT + "/")
|
|
286
|
+
if (rel.startsWith("..") || resolve(OUT, rel).slice(0, OUT.length + 1) !== OUT + "/")
|
|
287
|
+
throw new Error(`adapter path escapes output root: ${rel}`);
|
|
244
288
|
if (out.has(rel)) throw new Error(`adapter collision: ${rel}`);
|
|
245
289
|
out.set(rel, text);
|
|
246
290
|
}
|
|
247
291
|
for (const [rel, text] of out) {
|
|
248
|
-
const sourceTokens = [
|
|
292
|
+
const sourceTokens = [
|
|
293
|
+
SOURCE.runtime.plugin_root_env,
|
|
294
|
+
SOURCE.runtime.project_dir_env,
|
|
295
|
+
SOURCE.display_name,
|
|
296
|
+
SOURCE.runtime.harness_dir + "/",
|
|
297
|
+
].filter(Boolean);
|
|
249
298
|
const sourceAgentEnvs = (SOURCE.runtime.agent_overrides || []).map((row) => row.env).filter(Boolean);
|
|
250
|
-
if (
|
|
251
|
-
|
|
299
|
+
if (
|
|
300
|
+
sourceTokens.some((token) => text.includes(token)) ||
|
|
301
|
+
sourceAgentEnvs.some((token) => text.includes(token)) ||
|
|
302
|
+
text.includes(SOURCE_COMMAND_PREFIX) ||
|
|
303
|
+
new RegExp(SOURCE_ROLE.source).test(text) ||
|
|
304
|
+
/\$ARGUMENTS/.test(text)
|
|
305
|
+
)
|
|
306
|
+
throw new Error(`${rel}: source-harness vocabulary leaked into ${TARGET.display_name} adapter`);
|
|
307
|
+
if (
|
|
308
|
+
SOURCE_UI.some(({ re }) => new RegExp(re.source).test(text)) ||
|
|
309
|
+
/harness\/codex-code\.json|\b(?:opus|sonnet|fable)\b/i.test(text)
|
|
310
|
+
)
|
|
311
|
+
throw new Error(
|
|
312
|
+
`${rel}: source-harness model, environment or UI semantics leaked into ${TARGET.display_name} adapter`,
|
|
313
|
+
);
|
|
252
314
|
}
|
|
253
315
|
return out;
|
|
254
316
|
}
|
|
@@ -294,4 +356,5 @@ function main(argv) {
|
|
|
294
356
|
return result.ok ? 0 : 1;
|
|
295
357
|
}
|
|
296
358
|
|
|
297
|
-
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url))
|
|
359
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url))
|
|
360
|
+
process.exitCode = main(process.argv.slice(2));
|