@nusoft/nuos-build-catalogue 0.33.3 → 0.36.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 +3 -3
- package/dist/cli.js +48 -0
- package/dist/commands/end-of-session.js +67 -14
- package/dist/commands/state-compile.d.ts +108 -0
- package/dist/commands/state-compile.js +793 -0
- package/dist/embedder/ollama.d.ts +7 -0
- package/dist/embedder/ollama.js +27 -1
- package/package.json +5 -4
- package/scripts/hooks/pre-commit +43 -0
- package/templates/hooks/pre-commit +43 -0
- package/templates/starter-kit/methodfile.json +7 -1
|
@@ -32,6 +32,13 @@
|
|
|
32
32
|
* idle-timeout (the keep_alive: "1m" we sent) cleans up within a
|
|
33
33
|
* minute.
|
|
34
34
|
*
|
|
35
|
+
* **Bounded footprint while loaded.** Beyond unloading promptly, each call
|
|
36
|
+
* also pins `options.num_ctx` (see EMBED_NUM_CTX) so the model loads with an
|
|
37
|
+
* embedding-sized context window instead of inheriting the daemon's
|
|
38
|
+
* chat-sized OLLAMA_CONTEXT_LENGTH. Without this the 639MB model loads at
|
|
39
|
+
* ~5.7GB resident; with it, ~1.1GB. This is what keeps a reindex from pushing
|
|
40
|
+
* a developer's machine into swap.
|
|
41
|
+
*
|
|
35
42
|
* Sizing note — the new 0.6b default is ~600MB on disk and runs
|
|
36
43
|
* comfortably on any modern laptop, including CPU-only. The 4b variant
|
|
37
44
|
* (~2.5GB) and 8b variant (~4.7GB, benefits from ~16GB RAM + Metal)
|
package/dist/embedder/ollama.js
CHANGED
|
@@ -32,6 +32,13 @@
|
|
|
32
32
|
* idle-timeout (the keep_alive: "1m" we sent) cleans up within a
|
|
33
33
|
* minute.
|
|
34
34
|
*
|
|
35
|
+
* **Bounded footprint while loaded.** Beyond unloading promptly, each call
|
|
36
|
+
* also pins `options.num_ctx` (see EMBED_NUM_CTX) so the model loads with an
|
|
37
|
+
* embedding-sized context window instead of inheriting the daemon's
|
|
38
|
+
* chat-sized OLLAMA_CONTEXT_LENGTH. Without this the 639MB model loads at
|
|
39
|
+
* ~5.7GB resident; with it, ~1.1GB. This is what keeps a reindex from pushing
|
|
40
|
+
* a developer's machine into swap.
|
|
41
|
+
*
|
|
35
42
|
* Sizing note — the new 0.6b default is ~600MB on disk and runs
|
|
36
43
|
* comfortably on any modern laptop, including CPU-only. The 4b variant
|
|
37
44
|
* (~2.5GB) and 8b variant (~4.7GB, benefits from ~16GB RAM + Metal)
|
|
@@ -47,6 +54,16 @@ const KNOWN_DIMENSIONS = {
|
|
|
47
54
|
'qwen3-embedding:4b': 2560,
|
|
48
55
|
'qwen3-embedding:0.6b': 1024,
|
|
49
56
|
};
|
|
57
|
+
// Context window for embedding loads. The Ollama daemon's global
|
|
58
|
+
// OLLAMA_CONTEXT_LENGTH — set high for chat models (commonly 32K–64K) — is
|
|
59
|
+
// inherited by every model that doesn't override it. Inherited unchanged, it
|
|
60
|
+
// inflates the 639MB qwen3-embedding:0.6b model to ~5.7GB resident, which is
|
|
61
|
+
// enough to push a 16–18GB developer machine into swap during a reindex.
|
|
62
|
+
// Embedding inputs are capped at ~600 tokens (MAX_CHUNK_CHARS in
|
|
63
|
+
// indexer/chunk.ts), so a 2048-token window leaves ~3x headroom and never
|
|
64
|
+
// truncates a chunk. Measured 2026-06-01 (qwen3-embedding:0.6b, Apple Silicon):
|
|
65
|
+
// inherited 32K ctx → 5.7GB resident; num_ctx 2048 → 1.1GB resident.
|
|
66
|
+
const EMBED_NUM_CTX = 2048;
|
|
50
67
|
export class OllamaEmbedder {
|
|
51
68
|
dimensions;
|
|
52
69
|
modelId;
|
|
@@ -68,7 +85,13 @@ export class OllamaEmbedder {
|
|
|
68
85
|
const probe = await fetch(`${host}/api/embed`, {
|
|
69
86
|
method: 'POST',
|
|
70
87
|
headers: { 'content-type': 'application/json' },
|
|
71
|
-
body: JSON.stringify({
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
model: modelId,
|
|
90
|
+
input: 'probe',
|
|
91
|
+
// Pin the context window here too — the probe is what first loads the
|
|
92
|
+
// model, so without it the probe alone would pull in the full ~5.7GB.
|
|
93
|
+
options: { num_ctx: EMBED_NUM_CTX },
|
|
94
|
+
}),
|
|
72
95
|
});
|
|
73
96
|
if (!probe.ok) {
|
|
74
97
|
const body = await probe.text().catch(() => '<unreadable>');
|
|
@@ -121,6 +144,9 @@ export class OllamaEmbedder {
|
|
|
121
144
|
// Keep the model warm only for the duration of one operation.
|
|
122
145
|
// dispose() at the end of the run sends keep_alive: 0 to unload.
|
|
123
146
|
keep_alive: '1m',
|
|
147
|
+
// Cap the context window so the model loads at ~1.1GB rather than
|
|
148
|
+
// inheriting the daemon's chat-sized window and ballooning to ~5.7GB.
|
|
149
|
+
options: { num_ctx: EMBED_NUM_CTX },
|
|
124
150
|
}),
|
|
125
151
|
});
|
|
126
152
|
if (!res.ok) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nusoft/nuos-build-catalogue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"description": "NuOS build-catalogue tooling: semantic search (WU 110) + migration runner that lifts markdown artefacts into JSON-backed workflow records (WU 111, Phase G).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,15 +19,16 @@
|
|
|
19
19
|
"build": "rm -rf dist && tsc && chmod +x dist/cli.js",
|
|
20
20
|
"prepublishOnly": "npm run build",
|
|
21
21
|
"verify-storage": "tsx scripts/verify-persistence.ts",
|
|
22
|
-
"test": "tsx --test tests/chunk.test.ts tests/metadata.test.ts tests/crawl.test.ts tests/migrate.test.ts tests/commands-read.test.ts tests/regenerate.test.ts tests/commands-write.test.ts tests/ac-parse.test.ts tests/create.test.ts tests/init.test.ts tests/wu-111-soak-findings.test.ts tests/plan.test.ts tests/mode.test.ts tests/render.test.ts tests/swarm.test.ts tests/setup-progress-bar.test.ts tests/setup-ollama-pull.test.ts tests/setup-run-llm-setup.test.ts tests/wu-active.test.ts tests/install-claude-hooks.test.ts tests/protocols-in-sync.test.ts tests/end-of-session.test.ts tests/hooks-in-sync.test.ts tests/memory-store-separation.test.ts",
|
|
22
|
+
"test": "tsx --test tests/chunk.test.ts tests/metadata.test.ts tests/crawl.test.ts tests/migrate.test.ts tests/commands-read.test.ts tests/regenerate.test.ts tests/commands-write.test.ts tests/ac-parse.test.ts tests/create.test.ts tests/init.test.ts tests/wu-111-soak-findings.test.ts tests/plan.test.ts tests/mode.test.ts tests/render.test.ts tests/swarm.test.ts tests/setup-progress-bar.test.ts tests/setup-ollama-pull.test.ts tests/setup-run-llm-setup.test.ts tests/wu-active.test.ts tests/install-claude-hooks.test.ts tests/protocols-in-sync.test.ts tests/end-of-session.test.ts tests/hooks-in-sync.test.ts tests/memory-store-separation.test.ts tests/state-compile.test.ts tests/state-drift-check.test.ts tests/hook-isolation.test.ts",
|
|
23
23
|
"typecheck": "tsc --noEmit",
|
|
24
24
|
"index": "tsx src/cli.ts index",
|
|
25
25
|
"search": "tsx src/cli.ts search"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@nusoft/nuvector": "^0.1.5",
|
|
29
28
|
"@nusoft/nuflow": "^0.4.1",
|
|
30
|
-
"@nusoft/nuflow-pack-nuos-build-catalogue": "^0.
|
|
29
|
+
"@nusoft/nuflow-pack-nuos-build-catalogue": "^0.3.0",
|
|
30
|
+
"@nusoft/nuvector": "^0.1.5",
|
|
31
|
+
"@nusoft/nuwiki": "^0.3.0"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@nusoft/nuflow": "file:../nuflow",
|
package/scripts/hooks/pre-commit
CHANGED
|
@@ -177,6 +177,49 @@ if [[ -n "$locked_decisions" ]]; then
|
|
|
177
177
|
EXIT_CODE=1
|
|
178
178
|
fi
|
|
179
179
|
|
|
180
|
+
# ---------- Rule 3: STATE.md generated-region drift block (WU 113b Stage B) ---
|
|
181
|
+
|
|
182
|
+
# Only run when docs/build/STATE.md is in the staged changes.
|
|
183
|
+
# Guard on nuos-catalogue being present and supporting `state drift-check`.
|
|
184
|
+
# Fail-open: if the binary is absent, old (doesn't know drift-check), or
|
|
185
|
+
# errors for any infra reason, skip this check silently — a missing binary
|
|
186
|
+
# must never block all commits.
|
|
187
|
+
#
|
|
188
|
+
# Old-binary detection: an old binary (< 0.35.0) exits non-zero with
|
|
189
|
+
# "unknown state subcommand: drift-check" on stderr. We distinguish this
|
|
190
|
+
# from a genuine drift finding by checking whether the output contains the
|
|
191
|
+
# drift-specific marker phrase. If the output does NOT contain "generated regions"
|
|
192
|
+
# (the phrase only the new drift-check command emits), we skip.
|
|
193
|
+
staged_state_md=$(git diff --cached --name-only | grep -F 'docs/build/STATE.md' || true)
|
|
194
|
+
|
|
195
|
+
if [[ -n "$staged_state_md" ]]; then
|
|
196
|
+
dim "[nuos:pre-commit] STATE.md generated-region drift check (WU 113b)"
|
|
197
|
+
|
|
198
|
+
if ! command -v nuos-catalogue > /dev/null 2>&1; then
|
|
199
|
+
dim "[nuos:pre-commit] nuos-catalogue not found — skipping STATE.md drift check"
|
|
200
|
+
else
|
|
201
|
+
# Run drift-check; capture output + exit code.
|
|
202
|
+
drift_output=$(nuos-catalogue state drift-check 2>&1) || drift_exit=$?
|
|
203
|
+
drift_exit=${drift_exit:-0}
|
|
204
|
+
|
|
205
|
+
if [[ $drift_exit -ne 0 ]]; then
|
|
206
|
+
# Non-zero exit — check whether this is a genuine drift finding or an
|
|
207
|
+
# infra/version problem (old binary, missing store, etc.).
|
|
208
|
+
if echo "$drift_output" | grep -qF 'generated regions'; then
|
|
209
|
+
# Confirmed generated-region drift — block the commit.
|
|
210
|
+
red "✖ STATE.md generated-region drift — BLOCKED (WU 113b enforcement):"
|
|
211
|
+
echo "$drift_output" | while IFS= read -r line; do echo " $line"; done
|
|
212
|
+
log_event "state-drift-block" "generated-region drift detected"
|
|
213
|
+
EXIT_CODE=1
|
|
214
|
+
else
|
|
215
|
+
# Not a drift finding (unknown subcommand from old binary, infra error, etc.)
|
|
216
|
+
# — skip silently (fail open).
|
|
217
|
+
dim "[nuos:pre-commit] STATE.md drift check returned non-zero (not a drift finding) — skipping"
|
|
218
|
+
fi
|
|
219
|
+
fi
|
|
220
|
+
fi
|
|
221
|
+
fi
|
|
222
|
+
|
|
180
223
|
# ---------- Result ------------------------------------------------------
|
|
181
224
|
|
|
182
225
|
if [[ $EXIT_CODE -eq 0 ]]; then
|
|
@@ -177,6 +177,49 @@ if [[ -n "$locked_decisions" ]]; then
|
|
|
177
177
|
EXIT_CODE=1
|
|
178
178
|
fi
|
|
179
179
|
|
|
180
|
+
# ---------- Rule 3: STATE.md generated-region drift block (WU 113b Stage B) ---
|
|
181
|
+
|
|
182
|
+
# Only run when docs/build/STATE.md is in the staged changes.
|
|
183
|
+
# Guard on nuos-catalogue being present and supporting `state drift-check`.
|
|
184
|
+
# Fail-open: if the binary is absent, old (doesn't know drift-check), or
|
|
185
|
+
# errors for any infra reason, skip this check silently — a missing binary
|
|
186
|
+
# must never block all commits.
|
|
187
|
+
#
|
|
188
|
+
# Old-binary detection: an old binary (< 0.35.0) exits non-zero with
|
|
189
|
+
# "unknown state subcommand: drift-check" on stderr. We distinguish this
|
|
190
|
+
# from a genuine drift finding by checking whether the output contains the
|
|
191
|
+
# drift-specific marker phrase. If the output does NOT contain "generated regions"
|
|
192
|
+
# (the phrase only the new drift-check command emits), we skip.
|
|
193
|
+
staged_state_md=$(git diff --cached --name-only | grep -F 'docs/build/STATE.md' || true)
|
|
194
|
+
|
|
195
|
+
if [[ -n "$staged_state_md" ]]; then
|
|
196
|
+
dim "[nuos:pre-commit] STATE.md generated-region drift check (WU 113b)"
|
|
197
|
+
|
|
198
|
+
if ! command -v nuos-catalogue > /dev/null 2>&1; then
|
|
199
|
+
dim "[nuos:pre-commit] nuos-catalogue not found — skipping STATE.md drift check"
|
|
200
|
+
else
|
|
201
|
+
# Run drift-check; capture output + exit code.
|
|
202
|
+
drift_output=$(nuos-catalogue state drift-check 2>&1) || drift_exit=$?
|
|
203
|
+
drift_exit=${drift_exit:-0}
|
|
204
|
+
|
|
205
|
+
if [[ $drift_exit -ne 0 ]]; then
|
|
206
|
+
# Non-zero exit — check whether this is a genuine drift finding or an
|
|
207
|
+
# infra/version problem (old binary, missing store, etc.).
|
|
208
|
+
if echo "$drift_output" | grep -qF 'generated regions'; then
|
|
209
|
+
# Confirmed generated-region drift — block the commit.
|
|
210
|
+
red "✖ STATE.md generated-region drift — BLOCKED (WU 113b enforcement):"
|
|
211
|
+
echo "$drift_output" | while IFS= read -r line; do echo " $line"; done
|
|
212
|
+
log_event "state-drift-block" "generated-region drift detected"
|
|
213
|
+
EXIT_CODE=1
|
|
214
|
+
else
|
|
215
|
+
# Not a drift finding (unknown subcommand from old binary, infra error, etc.)
|
|
216
|
+
# — skip silently (fail open).
|
|
217
|
+
dim "[nuos:pre-commit] STATE.md drift check returned non-zero (not a drift finding) — skipping"
|
|
218
|
+
fi
|
|
219
|
+
fi
|
|
220
|
+
fi
|
|
221
|
+
fi
|
|
222
|
+
|
|
180
223
|
# ---------- Result ------------------------------------------------------
|
|
181
224
|
|
|
182
225
|
if [[ $EXIT_CODE -eq 0 ]]; then
|
|
@@ -17,7 +17,13 @@
|
|
|
17
17
|
"database": null,
|
|
18
18
|
"deployment": null,
|
|
19
19
|
"externalServices": [],
|
|
20
|
-
"notes": null
|
|
20
|
+
"notes": null,
|
|
21
|
+
"services": {
|
|
22
|
+
"database": { "provider": "none", "migrations": "none", "deployOnMerge": false, "secrets": [] },
|
|
23
|
+
"jobs": { "provider": "none", "deployOnMerge": false, "secrets": [] },
|
|
24
|
+
"web": { "provider": "none", "autoDeploy": false, "secrets": [] }
|
|
25
|
+
},
|
|
26
|
+
"servicesComment": "Backend service wiring (D150 / WU 231). Per service: 'provider', whether it 'deployOnMerge' to main, and the 'secrets' it needs; 'database' also names its 'migrations' tool; 'web' declares 'autoDeploy' (e.g. Vercel's Git integration). Set provider to 'none' to opt a service out. When a provider is set, `nuos init` scaffolds the deploy automation (build packages → migrate deploy → jobs deploy via the installed CLI) and the doctor verifies the secrets exist and flags deploy drift. Providers — database: supabase|neon|railway|none; jobs: trigger.dev|none; web: vercel|none."
|
|
21
27
|
},
|
|
22
28
|
"testing": {
|
|
23
29
|
"framework": "vitest",
|