@panaversity/ksor 0.0.35 → 0.0.37
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/CHANGELOG.md +32 -0
- package/README.md +6 -0
- package/dist/cli.mjs +180 -9
- package/docs/deploying.md +5 -0
- package/docs/index.md +4 -1
- package/package.json +1 -1
- package/templates/scaffold/AGENTS.md +2 -1
- package/templates/scaffold/README.md +47 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @panaversity/ksor
|
|
2
2
|
|
|
3
|
+
## 0.0.37
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 3eafb25: The package README states the KSoR architecture: one governed record — Markdown in the KSoR Profile of the Open Knowledge Format (OKF) — behind one governance boundary, projected through open standards.
|
|
8
|
+
|
|
9
|
+
## 0.0.36
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 617dc46: The scaffold meets your package manager
|
|
14
|
+
|
|
15
|
+
`ksor init` now emits the scaffold for the manager that ran it: `npx
|
|
16
|
+
@panaversity/ksor init` produces an npm project, `bunx` a bun one, `pnpm dlx`
|
|
17
|
+
(or anything unrecognized) the pnpm shape every scaffold got before. Node stays
|
|
18
|
+
the one prerequisite — nobody installs a second package manager to open their
|
|
19
|
+
own knowledge base (issue #28).
|
|
20
|
+
|
|
21
|
+
The whole scaffold speaks the detected manager: scripts, README, AGENTS.md, the
|
|
22
|
+
agent kit, the CLI's own handoff text. npm and bun scaffolds declare
|
|
23
|
+
`workspaces` in the manifest and ship no lockfile — the pinned CLI version
|
|
24
|
+
cannot be pre-resolved into one, so the first install writes it and the README
|
|
25
|
+
says to commit it. The install-script denial carries over (`.npmrc` with
|
|
26
|
+
`ignore-scripts=true` for npm; bun refuses dependency lifecycle scripts by
|
|
27
|
+
default). What npm and bun cannot offer is pnpm's 48-hour quarantine on newly
|
|
28
|
+
published dependency versions — the emitted scaffold discloses that instead of
|
|
29
|
+
staying silent about it.
|
|
30
|
+
|
|
31
|
+
Each manager's shape was proven end to end before shipping — install, `ksor`
|
|
32
|
+
bin resolution, format checker, full static site build — and CI now walks npm
|
|
33
|
+
and bun scaffolds on every change.
|
|
34
|
+
|
|
3
35
|
## 0.0.35
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -56,6 +56,12 @@ A quiz whose answers are guessable is refused by the build, and because ingest
|
|
|
56
56
|
creates no node for an attachment, a quiz's answer key can never reach the
|
|
57
57
|
agent surface at all.
|
|
58
58
|
|
|
59
|
+
The architecture: **one governed record** — Markdown in the KSoR Profile of
|
|
60
|
+
the Open Knowledge Format (OKF) — behind **one governance boundary**,
|
|
61
|
+
projected through open standards: MCP for agents, `llms.txt` for AI
|
|
62
|
+
discovery, OAuth/OIDC for identity, SLSA/Sigstore for publication integrity,
|
|
63
|
+
OpenTelemetry for observability.
|
|
64
|
+
|
|
59
65
|
Full concept, design goals, and project status:
|
|
60
66
|
**<https://github.com/panaversity/ksor>**
|
|
61
67
|
|
package/dist/cli.mjs
CHANGED
|
@@ -6723,6 +6723,152 @@ function isEnvironmentError(value) {
|
|
|
6723
6723
|
return code !== null && ENVIRONMENT_CODES.has(code);
|
|
6724
6724
|
}
|
|
6725
6725
|
//#endregion
|
|
6726
|
+
//#region src/init/manager.ts
|
|
6727
|
+
/**
|
|
6728
|
+
* Which manager spawned this process, from `npm_config_user_agent`
|
|
6729
|
+
* (e.g. "pnpm/11.22.0 npm/? node/v24.5.0 darwin arm64"). The first token
|
|
6730
|
+
* names the manager; only managers we emit a scaffold for are recognized.
|
|
6731
|
+
*/
|
|
6732
|
+
function detectManager(userAgent) {
|
|
6733
|
+
const head = (userAgent ?? "").split("/")[0]?.trim();
|
|
6734
|
+
if (head === "npm") return "npm";
|
|
6735
|
+
if (head === "bun") return "bun";
|
|
6736
|
+
return "pnpm";
|
|
6737
|
+
}
|
|
6738
|
+
/** Template files that belong to exactly one manager's scaffold. */
|
|
6739
|
+
function isSkippedFor(templateName, manager) {
|
|
6740
|
+
if (manager === "pnpm") return false;
|
|
6741
|
+
return templateName === "pnpm-workspace.yaml" || templateName === "pnpm-lock.yaml";
|
|
6742
|
+
}
|
|
6743
|
+
/**
|
|
6744
|
+
* The workspace globs, shared by every manager. pnpm reads them from
|
|
6745
|
+
* pnpm-workspace.yaml; npm and bun read a `workspaces` field. One constant so
|
|
6746
|
+
* the two spellings cannot drift.
|
|
6747
|
+
*/
|
|
6748
|
+
const WORKSPACE_GLOBS = [
|
|
6749
|
+
"system/site",
|
|
6750
|
+
"system/gateways/*",
|
|
6751
|
+
"system/packages/*"
|
|
6752
|
+
];
|
|
6753
|
+
/**
|
|
6754
|
+
* The root scripts, per manager. pnpm's are the template's own bytes; npm and
|
|
6755
|
+
* bun REPLACE the manager-specific bodies and inherit everything else.
|
|
6756
|
+
* npm: `--prefix` is npm's spelling of "run it over there".
|
|
6757
|
+
* bun: cd-chains — see the module comment for why not `--cwd`.
|
|
6758
|
+
*/
|
|
6759
|
+
const SCRIPT_BODIES = {
|
|
6760
|
+
npm: {
|
|
6761
|
+
dev: "npm --prefix system/site run dev",
|
|
6762
|
+
build: "npm run export-denylist && npm --prefix system/site run build",
|
|
6763
|
+
provision: "npm run schema && npm run grant",
|
|
6764
|
+
refresh: "npm run ingest && npm run gc"
|
|
6765
|
+
},
|
|
6766
|
+
bun: {
|
|
6767
|
+
dev: "cd system/site && bun run dev",
|
|
6768
|
+
build: "bun run export-denylist && cd system/site && bun run build",
|
|
6769
|
+
provision: "bun run schema && bun run grant",
|
|
6770
|
+
refresh: "bun run ingest && bun run gc"
|
|
6771
|
+
}
|
|
6772
|
+
};
|
|
6773
|
+
/**
|
|
6774
|
+
* Rewrite the scaffold's root package.json for the manager. Structured — a
|
|
6775
|
+
* JSON transform, never string surgery — because the manifest is the one
|
|
6776
|
+
* file where a half-applied spelling map would still parse and then lie.
|
|
6777
|
+
*/
|
|
6778
|
+
function transformManifest(source, manager) {
|
|
6779
|
+
if (manager === "pnpm") return source;
|
|
6780
|
+
const parsed = JSON.parse(source);
|
|
6781
|
+
const { packageManager: _dropped, ...rest } = parsed;
|
|
6782
|
+
const out = {
|
|
6783
|
+
...rest,
|
|
6784
|
+
scripts: {
|
|
6785
|
+
...parsed.scripts,
|
|
6786
|
+
...SCRIPT_BODIES[manager]
|
|
6787
|
+
},
|
|
6788
|
+
workspaces: [...WORKSPACE_GLOBS]
|
|
6789
|
+
};
|
|
6790
|
+
return `${JSON.stringify(out, null, 2)}\n`;
|
|
6791
|
+
}
|
|
6792
|
+
/**
|
|
6793
|
+
* Ordered prose translation, longest spelling first so `pnpm install` is
|
|
6794
|
+
* never half-eaten by a shorter rule. Applied to every emitted text file
|
|
6795
|
+
* except package.json (structured above). The conformance test asserts ZERO
|
|
6796
|
+
* surviving "pnpm" tokens outside the quarantine disclosure, so a template
|
|
6797
|
+
* edit that adds a spelling this map misses goes red instead of shipping an
|
|
6798
|
+
* instruction the adopter cannot run.
|
|
6799
|
+
*/
|
|
6800
|
+
const SCRIPT_NAMES = [
|
|
6801
|
+
"dev",
|
|
6802
|
+
"build",
|
|
6803
|
+
"check",
|
|
6804
|
+
"serve",
|
|
6805
|
+
"provision",
|
|
6806
|
+
"refresh",
|
|
6807
|
+
"schema",
|
|
6808
|
+
"grant",
|
|
6809
|
+
"ingest",
|
|
6810
|
+
"gc",
|
|
6811
|
+
"export-denylist"
|
|
6812
|
+
];
|
|
6813
|
+
function spellings(manager) {
|
|
6814
|
+
const run = (script) => manager === "npm" ? `npm run ${script}` : `bun run ${script}`;
|
|
6815
|
+
const pairs = [
|
|
6816
|
+
["pnpm install --no-frozen-lockfile", manager === "npm" ? "npm install" : "bun install"],
|
|
6817
|
+
["pnpm install", manager === "npm" ? "npm install" : "bun install"],
|
|
6818
|
+
["pnpm exec ksor", manager === "npm" ? "npx ksor" : "bunx ksor"],
|
|
6819
|
+
["pnpm dlx", manager === "npm" ? "npx" : "bunx"],
|
|
6820
|
+
["pnpm add -D", manager === "npm" ? "npm i -D" : "bun add -d"],
|
|
6821
|
+
["pnpm -C system/site", manager === "npm" ? "npm --prefix system/site run" : "cd system/site && bun run"]
|
|
6822
|
+
];
|
|
6823
|
+
for (const script of SCRIPT_NAMES) pairs.push([`pnpm ${script}`, run(script)]);
|
|
6824
|
+
return pairs;
|
|
6825
|
+
}
|
|
6826
|
+
/**
|
|
6827
|
+
* Manager-conditional blocks in markdown templates:
|
|
6828
|
+
*
|
|
6829
|
+
* <!-- ksor:pm pnpm npm -->
|
|
6830
|
+
* ...lines kept only for those managers...
|
|
6831
|
+
* <!-- /ksor:pm -->
|
|
6832
|
+
*
|
|
6833
|
+
* The marker lines themselves never survive into any scaffold, so the pnpm
|
|
6834
|
+
* output stays exactly what an adopter always got.
|
|
6835
|
+
*/
|
|
6836
|
+
const BLOCK_OPEN = /^[ \t]*<!-- ksor:pm ([a-z ]+?) -->[ \t]*$/;
|
|
6837
|
+
const BLOCK_CLOSE = /^[ \t]*<!-- \/ksor:pm -->[ \t]*$/;
|
|
6838
|
+
function applyProse(text, manager) {
|
|
6839
|
+
const lines = text.split("\n");
|
|
6840
|
+
const kept = [];
|
|
6841
|
+
let keeping = true;
|
|
6842
|
+
let inBlock = false;
|
|
6843
|
+
for (const line of lines) {
|
|
6844
|
+
const open = BLOCK_OPEN.exec(line);
|
|
6845
|
+
if (open !== null) {
|
|
6846
|
+
inBlock = true;
|
|
6847
|
+
keeping = open[1].split(/\s+/).includes(manager);
|
|
6848
|
+
continue;
|
|
6849
|
+
}
|
|
6850
|
+
if (BLOCK_CLOSE.test(line)) {
|
|
6851
|
+
inBlock = false;
|
|
6852
|
+
keeping = true;
|
|
6853
|
+
continue;
|
|
6854
|
+
}
|
|
6855
|
+
if (!inBlock || keeping) kept.push(line);
|
|
6856
|
+
}
|
|
6857
|
+
let out = kept.join("\n");
|
|
6858
|
+
if (manager !== "pnpm") for (const [from, to] of spellings(manager)) out = out.replaceAll(from, to);
|
|
6859
|
+
return out;
|
|
6860
|
+
}
|
|
6861
|
+
/**
|
|
6862
|
+
* Files a manager's scaffold gains beyond the template tree. npm's `.npmrc`
|
|
6863
|
+
* carries the denial half of the posture and DISCLOSES the missing half; bun
|
|
6864
|
+
* needs no file — denial is bun's own default — so its disclosure lives in
|
|
6865
|
+
* the README's lockfile note.
|
|
6866
|
+
*/
|
|
6867
|
+
function extraFiles(manager) {
|
|
6868
|
+
if (manager !== "npm") return [];
|
|
6869
|
+
return [[".npmrc", "# Dependency install scripts are denied — the same posture the pnpm\n# scaffold enforces per-package. Flip to false only with a comment naming\n# what breaks without it.\n#\n# What npm cannot give you is pnpm's 48-hour quarantine on newly\n# published dependency versions (minimumReleaseAge): under npm a routine\n# install can pick up a day-zero compromised release the day it ships.\n# That protection exists only under pnpm.\nignore-scripts=true\n"]];
|
|
6870
|
+
}
|
|
6871
|
+
//#endregion
|
|
6726
6872
|
//#region src/init/materialize.ts
|
|
6727
6873
|
const EMITTED_NAMES = /* @__PURE__ */ new Map([
|
|
6728
6874
|
["gitignore", ".gitignore"],
|
|
@@ -6755,9 +6901,14 @@ function isTextFile(file) {
|
|
|
6755
6901
|
* children, so a caller that cannot rename-over (the `init .` form) can undo
|
|
6756
6902
|
* a half-written tree in reverse order.
|
|
6757
6903
|
*/
|
|
6758
|
-
function materialize(templateDir, targetDir, stamps, created = []) {
|
|
6904
|
+
function materialize(templateDir, targetDir, stamps, manager = "pnpm", created = []) {
|
|
6905
|
+
materializeTree(templateDir, targetDir, stamps, manager, created, true);
|
|
6906
|
+
return created;
|
|
6907
|
+
}
|
|
6908
|
+
function materializeTree(templateDir, targetDir, stamps, manager, created, isRoot) {
|
|
6759
6909
|
for (const entry of readdirSync(templateDir, { withFileTypes: true })) {
|
|
6760
6910
|
if (entry.name === "node_modules") continue;
|
|
6911
|
+
if (isRoot && isSkippedFor(entry.name, manager)) continue;
|
|
6761
6912
|
const from = path.join(templateDir, entry.name);
|
|
6762
6913
|
const to = path.join(targetDir, EMITTED_NAMES.get(entry.name) ?? entry.name);
|
|
6763
6914
|
if (entry.isDirectory()) {
|
|
@@ -6765,9 +6916,10 @@ function materialize(templateDir, targetDir, stamps, created = []) {
|
|
|
6765
6916
|
mkdirSync(to, { recursive: true });
|
|
6766
6917
|
created.push(to);
|
|
6767
6918
|
}
|
|
6768
|
-
|
|
6919
|
+
materializeTree(from, to, stamps, manager, created, false);
|
|
6769
6920
|
} else if (isTextFile(from)) {
|
|
6770
|
-
const
|
|
6921
|
+
const stamped = readFileSync(from, "utf8").replaceAll("KSOR-STAMP-NAME", stamps.name).replaceAll("KSOR-STAMP-VERSION", stamps.version);
|
|
6922
|
+
const text = isRoot && entry.name === "package.json" ? transformManifest(stamped, manager) : applyProse(stamped, manager);
|
|
6771
6923
|
created.push(to);
|
|
6772
6924
|
writeFileSync(to, text);
|
|
6773
6925
|
} else {
|
|
@@ -6775,6 +6927,14 @@ function materialize(templateDir, targetDir, stamps, created = []) {
|
|
|
6775
6927
|
copyFileSync(from, to);
|
|
6776
6928
|
}
|
|
6777
6929
|
}
|
|
6930
|
+
}
|
|
6931
|
+
/** Emit the files a manager's scaffold gains beyond the template tree. */
|
|
6932
|
+
function materializeExtras(targetDir, manager, created = []) {
|
|
6933
|
+
for (const [name, content] of extraFiles(manager)) {
|
|
6934
|
+
const to = path.join(targetDir, name);
|
|
6935
|
+
created.push(to);
|
|
6936
|
+
writeFileSync(to, content);
|
|
6937
|
+
}
|
|
6778
6938
|
return created;
|
|
6779
6939
|
}
|
|
6780
6940
|
//#endregion
|
|
@@ -6906,14 +7066,22 @@ function gitInit(dir, io) {
|
|
|
6906
7066
|
io.err(`note: git init failed: ${detail}\n`);
|
|
6907
7067
|
}
|
|
6908
7068
|
}
|
|
6909
|
-
function handoff(io, name, targetWasDot) {
|
|
7069
|
+
function handoff(io, name, targetWasDot, manager) {
|
|
6910
7070
|
const enter = targetWasDot ? "" : ` cd ${name}\n`;
|
|
7071
|
+
const run = (script) => manager === "pnpm" ? `pnpm ${script}` : manager === "npm" ? `npm run ${script}` : `bun run ${script}`;
|
|
7072
|
+
const install = manager === "pnpm" ? "pnpm install" : `${manager} install`;
|
|
7073
|
+
const pnpmHint = manager === "pnpm" ? "no pnpm? run: npm install -g pnpm — or `corepack enable pnpm` on Nodes that bundle corepack\n\n" : "";
|
|
6911
7074
|
io.out(`${name} is ready — your knowledge, your repo, yours outright.\n
|
|
6912
7075
|
Next (or just tell your coding agent to take it from here):
|
|
6913
|
-
` + enter +
|
|
7076
|
+
` + enter + ` ${install}\n ${run("dev").padEnd(15)} # the site, live at http://localhost:3000\n
|
|
7077
|
+
Then, for the agent surface (needs Postgres and a provider key):
|
|
7078
|
+
${run("provision").padEnd(15)} # once: uncomment \`database:\` in instance.md, copy\n # .env.example to .env, then apply the schema
|
|
7079
|
+
${run("refresh").padEnd(15)} # PUBLISH the record — ingest knowledge/ into a generation\n ${run("serve").padEnd(15)} # the MCP server, over what you just published\n
|
|
7080
|
+
` + pnpmHint + "Start in knowledge/ — AGENTS.md carries the working rules.\n");
|
|
6914
7081
|
}
|
|
6915
7082
|
function init(args, cwd, io, env) {
|
|
6916
7083
|
const { version, templatesDir } = env;
|
|
7084
|
+
const manager = detectManager(env.userAgent);
|
|
6917
7085
|
if (!existsSync(templatesDir)) return fail(io, "broken-install", [`the ksor package is missing its templates: ${templatesDir}`, "reinstall it — `pnpm add -D @panaversity/ksor`, or `npm i -g @panaversity/ksor`."], exitCodes.environment);
|
|
6918
7086
|
const word = args[0] ?? null;
|
|
6919
7087
|
if (word === null) return usage$1(io);
|
|
@@ -6946,7 +7114,8 @@ function init(args, cwd, io, env) {
|
|
|
6946
7114
|
materialize(templatesDir, targetDir, {
|
|
6947
7115
|
name,
|
|
6948
7116
|
version
|
|
6949
|
-
}, created);
|
|
7117
|
+
}, manager, created);
|
|
7118
|
+
materializeExtras(targetDir, manager, created);
|
|
6950
7119
|
} catch (error) {
|
|
6951
7120
|
rollback(created);
|
|
6952
7121
|
throw error;
|
|
@@ -6957,7 +7126,8 @@ function init(args, cwd, io, env) {
|
|
|
6957
7126
|
materialize(templatesDir, stage, {
|
|
6958
7127
|
name,
|
|
6959
7128
|
version
|
|
6960
|
-
});
|
|
7129
|
+
}, manager);
|
|
7130
|
+
materializeExtras(stage, manager);
|
|
6961
7131
|
} catch (error) {
|
|
6962
7132
|
rmSync(stage, {
|
|
6963
7133
|
recursive: true,
|
|
@@ -6990,7 +7160,7 @@ function init(args, cwd, io, env) {
|
|
|
6990
7160
|
const detail = error instanceof Error ? error.message : String(error);
|
|
6991
7161
|
io.err(`note: the project was created, but a follow-up step failed: ${detail}\n`);
|
|
6992
7162
|
}
|
|
6993
|
-
handoff(io, name, isDot);
|
|
7163
|
+
handoff(io, name, isDot, manager);
|
|
6994
7164
|
return 0;
|
|
6995
7165
|
}
|
|
6996
7166
|
function runInit(args, cwd, io, env) {
|
|
@@ -7080,7 +7250,8 @@ async function main(args) {
|
|
|
7080
7250
|
err: (text) => process.stderr.write(text)
|
|
7081
7251
|
}, {
|
|
7082
7252
|
version: pkg.version,
|
|
7083
|
-
templatesDir: fileURLToPath(new URL("../templates/scaffold", import.meta.url))
|
|
7253
|
+
templatesDir: fileURLToPath(new URL("../templates/scaffold", import.meta.url)),
|
|
7254
|
+
userAgent: process.env.npm_config_user_agent
|
|
7084
7255
|
});
|
|
7085
7256
|
}
|
|
7086
7257
|
if (verb === "serve") {
|
package/docs/deploying.md
CHANGED
|
@@ -7,6 +7,11 @@ status: draft
|
|
|
7
7
|
|
|
8
8
|
## Before you start
|
|
9
9
|
|
|
10
|
+
Commands on this page use the pnpm spelling (`pnpm build`, `pnpm serve`).
|
|
11
|
+
Since 0.0.36, `ksor init` emits the scaffold for the manager that ran it —
|
|
12
|
+
npm and bun included — and your scaffold's own README speaks that manager;
|
|
13
|
+
translate accordingly (`npm run build`, `bun run build`).
|
|
14
|
+
|
|
10
15
|
Four things must exist, and the order matters. Nothing below works without them,
|
|
11
16
|
and three of the four are outside this page.
|
|
12
17
|
|
package/docs/index.md
CHANGED
|
@@ -19,7 +19,10 @@ instead of their training memory. The corpus grows with each implemented verb.
|
|
|
19
19
|
`.claude/skills/` copies), adopter CI, and a dependency-free format
|
|
20
20
|
checker (`pnpm check`). `ksor init .` scaffolds into an empty directory
|
|
21
21
|
whose name passes the project-name grammar. Everything emitted belongs to
|
|
22
|
-
the adopter (templates are MIT-0).
|
|
22
|
+
the adopter (templates are MIT-0). The scaffold is emitted for the package
|
|
23
|
+
manager that ran init — `npx …` yields an npm project, `bunx …` a bun one,
|
|
24
|
+
`pnpm dlx …` (or a bare `ksor`) the pnpm shape — so the commands below use
|
|
25
|
+
the pnpm spelling and your own scaffold's README speaks your manager.
|
|
23
26
|
- Inside a scaffolded project, `pnpm install && pnpm dev` serves the record
|
|
24
27
|
at `http://localhost:3000`; `pnpm build` writes a fully static export to
|
|
25
28
|
`system/site/out/`. `KSOR_BASE_PATH=/repo pnpm build` targets sub-path
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@panaversity/ksor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.37",
|
|
4
4
|
"description": "Knowledge System of Record — compile governed markdown into a static site for people and an MCP server for AI agents, with citations and measured abstention.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"abstention",
|
|
@@ -112,7 +112,8 @@ Stand it up in this order (each step's errors explain how to fix themselves):
|
|
|
112
112
|
|
|
113
113
|
`provision` is separate on purpose: applying DDL and granting ingest are acts
|
|
114
114
|
an operator performs, not side effects of starting a server. (It is not
|
|
115
|
-
called `setup` because
|
|
115
|
+
called `setup` because package managers claim that word for commands of
|
|
116
|
+
their own, which would shadow
|
|
116
117
|
it — the step would print "No changes to the environment were made" and do
|
|
117
118
|
nothing.)
|
|
118
119
|
|
|
@@ -18,8 +18,11 @@ pnpm install
|
|
|
18
18
|
pnpm dev # browse the knowledge at http://localhost:3000
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
<!-- ksor:pm pnpm -->
|
|
21
22
|
No pnpm? Run `npm install -g pnpm` — or `corepack enable pnpm` on Node
|
|
22
|
-
versions that bundle corepack.
|
|
23
|
+
versions that bundle corepack.
|
|
24
|
+
<!-- /ksor:pm -->
|
|
25
|
+
The first `pnpm install` also fetches the
|
|
23
26
|
`ksor` tool (pinned in `package.json`) and writes it into your lockfile —
|
|
24
27
|
commit the updated lockfile.
|
|
25
28
|
|
|
@@ -128,6 +131,7 @@ the record well-formed (`format-checker`, also `pnpm check`).
|
|
|
128
131
|
|
|
129
132
|
### A note on the lockfile
|
|
130
133
|
|
|
134
|
+
<!-- ksor:pm pnpm -->
|
|
131
135
|
The committed `pnpm-lock.yaml` covers the site. It cannot cover
|
|
132
136
|
`@panaversity/ksor` itself, because the version pinned in `package.json` is
|
|
133
137
|
stamped by the CLI that scaffolded this project and could not be resolved before
|
|
@@ -138,6 +142,34 @@ The deploy config already accounts for this (`vercel.json` installs with
|
|
|
138
142
|
`--no-frozen-lockfile`), and the shipped `validate.yml` runs no install. If you
|
|
139
143
|
add CI of your own, note that pnpm turns on `--frozen-lockfile` automatically
|
|
140
144
|
whenever `CI` is set.
|
|
145
|
+
<!-- /ksor:pm -->
|
|
146
|
+
<!-- ksor:pm npm -->
|
|
147
|
+
No lockfile ships with this scaffold: npm keeps ONE lock for the whole
|
|
148
|
+
workspace, and the `@panaversity/ksor` version pinned in `package.json` was
|
|
149
|
+
stamped by the CLI that scaffolded this project — it could not be resolved
|
|
150
|
+
into a lock before it existed. Your FIRST `npm install` writes
|
|
151
|
+
`package-lock.json`; run it before you push, and COMMIT the result — that
|
|
152
|
+
lock is why two machines build the same site.
|
|
153
|
+
|
|
154
|
+
One honest difference from the pnpm scaffold: pnpm quarantines newly
|
|
155
|
+
published dependency versions for 48 hours (`minimumReleaseAge`), so a
|
|
156
|
+
routine install never picks up a day-zero compromised release. npm has no
|
|
157
|
+
equivalent — `.npmrc` here carries the install-script denial half of that
|
|
158
|
+
posture, and this sentence is the disclosure of the half it cannot.
|
|
159
|
+
<!-- /ksor:pm -->
|
|
160
|
+
<!-- ksor:pm bun -->
|
|
161
|
+
No lockfile ships with this scaffold: the `@panaversity/ksor` version pinned
|
|
162
|
+
in `package.json` was stamped by the CLI that scaffolded this project — it
|
|
163
|
+
could not be resolved into a lock before it existed. Your FIRST
|
|
164
|
+
`bun install` writes `bun.lock`; run it before you push, and COMMIT the
|
|
165
|
+
result — that lock is why two machines build the same site.
|
|
166
|
+
|
|
167
|
+
One honest difference from the pnpm scaffold: pnpm quarantines newly
|
|
168
|
+
published dependency versions for 48 hours (`minimumReleaseAge`), so a
|
|
169
|
+
routine install never picks up a day-zero compromised release. bun has no
|
|
170
|
+
equivalent (its default refusal of dependency install scripts covers the
|
|
171
|
+
OTHER half of that posture), and this sentence is the disclosure.
|
|
172
|
+
<!-- /ksor:pm -->
|
|
141
173
|
|
|
142
174
|
## The files, explained
|
|
143
175
|
|
|
@@ -158,9 +190,18 @@ different coding agent's way of finding the same working contract.
|
|
|
158
190
|
| `.gitattributes` | markdown is checked out byte-stable on every platform, so the same commit hashes the same everywhere. |
|
|
159
191
|
| `.env.example` | the variables the served rung needs; copy to `.env` (gitignored) and fill in. |
|
|
160
192
|
| `.gitignore` | keeps build output, `node_modules/`, and `.env` out of the record's history. |
|
|
161
|
-
| `package.json` | the surface commands — `pnpm dev` (the site) and `pnpm provision` / `pnpm refresh` / `pnpm serve` (the agent surface: set up once, publish, then serve) — plus `pnpm build` / `pnpm check`, the pinned `@panaversity/ksor` tool
|
|
193
|
+
| `package.json` | the surface commands — `pnpm dev` (the site) and `pnpm provision` / `pnpm refresh` / `pnpm serve` (the agent surface: set up once, publish, then serve) — plus `pnpm build` / `pnpm check`, the pinned `@panaversity/ksor` tool and the workspace layout the manifest declares. |
|
|
194
|
+
<!-- ksor:pm pnpm -->
|
|
162
195
|
| `pnpm-workspace.yaml` | where the workspace looks for code (`system/site`, plus reserved `system/gateways/*` and `system/packages/*`), and the supply-chain policy for installs. |
|
|
163
196
|
| `pnpm-lock.yaml` | the exact dependency versions — the reason two machines build the same site. |
|
|
197
|
+
<!-- /ksor:pm -->
|
|
198
|
+
<!-- ksor:pm npm -->
|
|
199
|
+
| `.npmrc` | dependency install scripts are denied; the comment inside discloses the one protection this scaffold lacks (a 48-hour quarantine on new releases). |
|
|
200
|
+
| `package-lock.json` | the exact dependency versions — written by your FIRST install; commit it, it is the reason two machines build the same site. |
|
|
201
|
+
<!-- /ksor:pm -->
|
|
202
|
+
<!-- ksor:pm bun -->
|
|
203
|
+
| `bun.lock` | the exact dependency versions — written by your FIRST install; commit it, it is the reason two machines build the same site. |
|
|
204
|
+
<!-- /ksor:pm -->
|
|
164
205
|
|
|
165
206
|
`format-checker` deliberately contains a program, `check.mjs`, and not only
|
|
166
207
|
prose: rules that are only written down cannot refuse anything. `pnpm check`
|
|
@@ -181,9 +222,11 @@ and anything that can serve files can serve it.
|
|
|
181
222
|
(never pin `system/site` as the root directory — the record lives
|
|
182
223
|
outside it), build with `pnpm build`, serve `system/site/out/`. It also
|
|
183
224
|
declares the MCP **door** as a second service built from the shipped
|
|
184
|
-
`Dockerfile`, so `/mcp` and the site share one domain.
|
|
185
|
-
|
|
225
|
+
`Dockerfile`, so `/mcp` and the site share one domain.
|
|
226
|
+
<!-- ksor:pm pnpm -->
|
|
227
|
+
If the build image's pnpm predates the `packageManager` pin, set the
|
|
186
228
|
`ENABLE_EXPERIMENTAL_COREPACK=1` build environment variable.
|
|
229
|
+
<!-- /ksor:pm -->
|
|
187
230
|
**Once `instance.md` declares a `database:`, the BUILD needs the DSN too.**
|
|
188
231
|
`pnpm build` first runs `pnpm export-denylist`, which asks the record's
|
|
189
232
|
database what has been withdrawn (`ksor takedown --export`) and writes
|