novahiz 0.5.2 → 0.7.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/CHANGELOG.md +87 -0
- package/README.md +4 -4
- package/adapters/opencode/commands/novahiz-upgrade.md +13 -0
- package/adapters/opencode/instructions.md +21 -9
- package/adapters/opencode/novahiz-plugin.ts +173 -31
- package/catalog/categories.json +14 -0
- package/catalog/providers.json +25 -0
- package/catalog/rules.json +19 -0
- package/catalog/vault-structure.json +366 -0
- package/dist/cli.js +19 -39
- package/dist/commands/docs.js +55 -0
- package/dist/commands/gate.js +54 -0
- package/dist/commands/second-memory.js +1116 -67
- package/dist/commands/stitch.js +298 -0
- package/dist/commands/upgrade.js +161 -0
- package/dist/gate.js +85 -1
- package/dist/spec.js +28 -1
- package/install/install.mjs +148 -16
- package/install/ui.d.mts +35 -0
- package/install/ui.mjs +246 -0
- package/mcp/novahiz-docs/index.mjs +63 -0
- package/mcp/novahiz-docs/src/query.ts +1 -1
- package/mcp/stitch-template/recommandation.md +14 -0
- package/package.json +1 -1
- package/skills/novahiz-second-memory/SKILL.md +189 -277
- package/skills/novahiz-second-memory/references/arborescence.md +131 -0
- package/skills/novahiz-second-memory/references/templates.md +153 -0
- package/skills/novahiz-stitch-fidelity/SKILL.md +113 -0
- package/skills/novahiz-stitch-fidelity/defaults/verify.config.defaults.json +29 -0
- package/skills/novahiz-stitch-fidelity/references/capture-recipes.md +74 -0
- package/skills/novahiz-stitch-fidelity/references/judgment-grid.md +67 -0
- package/skills/novahiz-stitch-fidelity/scripts/stitch_capture.mjs +598 -0
- package/skills/novahiz-stitch-fidelity/scripts/stitch_composite.mjs +271 -0
- package/skills/stitch-design-fidelity/SKILL.md +140 -0
- package/skills/stitch-design-fidelity/evals.json +23 -0
- package/skills/stitch-design-fidelity/references/anti-ai-patterns.md +115 -0
- package/skills/stitch-design-fidelity/references/design-system.md +192 -0
- package/skills/stitch-design-fidelity/references/prompt-methodology.md +143 -0
- package/skills/stitch-design-fidelity/references/stitch-fidelity.md +65 -0
- package/skills/stitch-design-fidelity/references/stitch-mcp.md +100 -0
- package/skills/stitch-design-fidelity/scripts/validate-frontmatter.py +11 -0
- package/src/cli.ts +18 -39
- package/src/commands/docs.ts +62 -0
- package/src/commands/gate.ts +54 -0
- package/src/commands/second-memory.ts +1164 -66
- package/src/commands/stitch.ts +295 -0
- package/src/commands/upgrade.ts +174 -0
- package/src/gate.ts +77 -1
- package/src/spec.ts +51 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,93 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.7.0] - 2026-10-08
|
|
9
|
+
|
|
10
|
+
Feature release: the Obsidian vault becomes a first-class citizen of the
|
|
11
|
+
workflow, and the install ships a self-sufficient runtime — the MCP trio is
|
|
12
|
+
wired statically, the docs index is seeded, and the LSP section is declared.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Vault auto-consult**: the plugin reads the `second-memory` vault during
|
|
17
|
+
session setup (`memory.auto.vault`), so decisions and next steps from the
|
|
18
|
+
vault reach the model without a manual read. `instructions.md` now states
|
|
19
|
+
the Obsidian/second-memory consultation rules (T2, T3).
|
|
20
|
+
- **`novahiz docs` command** (`src/commands/docs.ts`): `status` reports the
|
|
21
|
+
local documentation index per library, `ingest <id>|--all [--dry]` fills it
|
|
22
|
+
over the network. Backed by new CLI modes on the docs MCP itself
|
|
23
|
+
(`node mcp/novahiz-docs/index.mjs --ingest … | --status`) — `src/ingest.ts`
|
|
24
|
+
finally has an entry point (D1).
|
|
25
|
+
- **Docs index seed at install**: the installer fills the core bouquet
|
|
26
|
+
(react, nextjs, typescript, nodejs, tailwindcss, dart, flutter, expo) in a
|
|
27
|
+
non-blocking step; offline installs still succeed and the fill command stays
|
|
28
|
+
available. `read_docs` now names the command when a library is unindexed.
|
|
29
|
+
- **`novahiz second-memory search`** (T1) and **`doctor --apply` regenerates a
|
|
30
|
+
drifted `STRUCTURE.md`** (T6, `structureDrift`).
|
|
31
|
+
- **`stitchConfigured()`**: `novahiz init` recommends `novahiz stitch` only
|
|
32
|
+
when the API key is absent (T5); new `src/commands/stitch.ts`.
|
|
33
|
+
- **`novahiz upgrade`** command and the `/novahiz-upgrade` in-session command
|
|
34
|
+
(`src/commands/upgrade.ts`, `adapters/opencode/commands/novahiz-upgrade.md`).
|
|
35
|
+
- **Installer UI**: dedicated `install/ui.mjs` (+ `.d.mts`), plain/ASCII banner,
|
|
36
|
+
step layout — `tests/installer-ui.test.ts`.
|
|
37
|
+
- **Obsidian vault step (10/10)**: the installer creates the `second-memory`
|
|
38
|
+
vault structure and its ten community plugins when missing (T7).
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- **MCP trio wired statically (D2)**: `novahiz-core`, `novahiz-gate` and
|
|
43
|
+
`novahiz-scan` are written into the generated `opencode.jsonc`, and the
|
|
44
|
+
plugin transform registers them *before* the awaited `providers --mcp-json`
|
|
45
|
+
spawn — a transform landing after MCP resolution never binds servers (audit
|
|
46
|
+
2026-10-08: no connection, no warning). The trio now connects on install;
|
|
47
|
+
the plugin skips ids already present, so no duplicates.
|
|
48
|
+
- **Plugin renamed `novahiz` → `novahiz-workflow`** (descriptive id in the
|
|
49
|
+
image of `novahiz-token-economy`), and the live plugin copy is resynced from
|
|
50
|
+
the source on install — the running session no longer lags the repo.
|
|
51
|
+
- **Gate tests are hermetic to `NOVAHIZ_GATE`**: an operator running
|
|
52
|
+
`novahiz gate off` no longer turns two MCP gate tests red — child processes
|
|
53
|
+
get a sanitized environment.
|
|
54
|
+
- **`tsconfig.build.json` restored**: `npm run prepare` referenced a file that
|
|
55
|
+
was missing from the working copy.
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- **LSP declared**: the live opencode config carries an `lsp` object with five
|
|
60
|
+
servers verified present on the machine (dart, typescript, bash, yaml, json);
|
|
61
|
+
the generated config writes `lsp: true` (portable — no dead entries on user
|
|
62
|
+
machines). Verified against OpenCode v2.0.24: the config is accepted and
|
|
63
|
+
preserved (`config.get` echoes it), but V2 has no LSP runtime yet — no server
|
|
64
|
+
is started until upstream ships one.
|
|
65
|
+
|
|
66
|
+
## [0.6.0] - 2026-10-06
|
|
67
|
+
|
|
68
|
+
Feature release: the gate learns project scope, and Stitch design projects
|
|
69
|
+
get a capture → composite → fidelity-verdict loop.
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- **`R16-stitch-fidelity` gate rule**: editing a UI file (`*.tsx`, `*.jsx`,
|
|
74
|
+
`*.vue`, `*.svelte`, `*.dart`, `*.css`, `*.scss`, `*.html`) now requires
|
|
75
|
+
the `novahiz-stitch-fidelity` skill — but only inside a project that
|
|
76
|
+
carries `stitch/**` references. This is the new `projectGlobs` selector
|
|
77
|
+
(`src/spec.ts`, `src/gate.ts`): nearest ancestor of the edited file
|
|
78
|
+
holding `.git`, `package.json` or `pubspec.yaml`, globs written from that
|
|
79
|
+
root. Projects without Stitch references are unaffected.
|
|
80
|
+
- **`novahiz-stitch-fidelity` skill**: `scripts/stitch_capture.mjs` (adb:
|
|
81
|
+
uiautomator dump, screencap, input, adb.autoStart), `scripts/stitch_composite.mjs`
|
|
82
|
+
(screenshot ↔ design overlay), the fidelity judgment grid, capture
|
|
83
|
+
recipes and a `verify.config.defaults.json`.
|
|
84
|
+
- **`stitch-fidelity` provider** (`kind: commands`): adb prerequisite with
|
|
85
|
+
per-OS bootstrap (winget / brew / apt), categories `design-ui`, `expo`,
|
|
86
|
+
`flutter`; `stitch_capture.mjs --check` reports the seven readiness items.
|
|
87
|
+
- Optional `stitch-verify` roadmap step (`kind: verify`) in two roadmaps.
|
|
88
|
+
- Tests: `tests/gate-stitch-fidelity.test.ts` — project-scoped enforcement.
|
|
89
|
+
|
|
90
|
+
### Changed
|
|
91
|
+
|
|
92
|
+
- README headline and counts refreshed: 99 skills, 13 gate rules, 8 MCP
|
|
93
|
+
providers, version line at 0.6.0 (was advertising 96/11/7 and 0.4.1).
|
|
94
|
+
|
|
8
95
|
## [0.5.2] - 2026-10-05
|
|
9
96
|
|
|
10
97
|
Maintenance release closing the three leftovers traced right after `0.5.1`:
|
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
> **Couche d'application sans dépendance pour les agents de code IA** — classe les prompts, attribue des roadmaps d'exécution, bloque les éditions non sûres tant que les bonnes skills ne sont pas chargées, et persiste les décisions d'une session à l'autre — tout cela de façon déterministe, sans appel de modèle.
|
|
4
4
|
|
|
5
|
-
17 catégories,
|
|
5
|
+
17 catégories, 99 skills, 13 gate rules, 8 MCP providers — tout déterministe, tout local, tout JSON.
|
|
6
6
|
|
|
7
|
-
> **Version 0.
|
|
7
|
+
> **Version 0.6.0** — publiée sur npm (`npm install -g --allow-scripts=novahiz novahiz` installe et configure tout : core, plugin, agent, skills packs et Playwright MCP). `CHANGELOG.md` et `scripts/` sont dans le tarball.
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
┌──────────────────────────────────────────────────────────────────────────┐
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
└─ tier, skills ──────│ └─ block : charger les skills nommées, retry une fois
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
**17 catégories**, **
|
|
38
|
+
**17 catégories**, **99 skills**, **13 gate rules**, **8 MCP providers** — tout déterministe, tout local, tout JSON.
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
@@ -310,7 +310,7 @@ Deux pièces complémentaires : la session consomme moins sans perdre en puissan
|
|
|
310
310
|
|
|
311
311
|
## Skills installées
|
|
312
312
|
|
|
313
|
-
Novahiz livre
|
|
313
|
+
Novahiz livre 99 skills couvrant toutes les catégories :
|
|
314
314
|
|
|
315
315
|
| Catégorie | Skills | Objectif |
|
|
316
316
|
|-----------|--------|----------|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Check for a new Novahiz version and update it via npm. Arguments: $ARGUMENTS
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
1. Run the version check. Prefer the global CLI when available: `novahiz upgrade`. If `novahiz` is not on PATH, locate the Novahiz home (`NOVAHIZ_HOME` if set, otherwise `~/.config/novahiz`) and run `node src/cli.ts upgrade` from there.
|
|
6
|
+
|
|
7
|
+
2. If the output reports "up to date", confirm it in one sentence and stop.
|
|
8
|
+
|
|
9
|
+
3. If an update is available, report the installed -> latest versions, then ask the user for explicit approval. Never run the update without that approval (skip this confirmation only when `$ARGUMENTS` contains `--yes`).
|
|
10
|
+
|
|
11
|
+
4. After approval, run `novahiz upgrade --apply` (same CLI used in step 1). This runs `npm install -g novahiz@latest` for npm installs, or `git pull --ff-only` plus a catalog rebuild for source checkouts. Report the exact result, including any npm/git error verbatim — never claim success you did not observe.
|
|
12
|
+
|
|
13
|
+
5. Finish by telling the user to restart OpenCode completely so the new version loads. Offer `novahiz doctor` as the follow-up check.
|
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
# System Instructions
|
|
2
2
|
|
|
3
|
-
## Obsidian Memory
|
|
3
|
+
## Obsidian Memory — the `second-memory` vault
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The system's official vault is **`~/Documents/second-memory`** (`NOVAHIZ_SM_VAULT` overrides it for tests). It is created by the installer together with the plugins and skills: it is part of Novahiz itself, not an optional add-on. The old `~/Documents/Novahiz` path does not exist — never reference it.
|
|
6
|
+
|
|
7
|
+
### Automatic consultation (already done for you)
|
|
8
|
+
The plugin auto-consults the vault (`memory.auto.vault`, enabled by default): every `every` prompts (default 3) it runs `novahiz second-memory search <prompt head> --k=3 --min-score=0.25 --json` and injects the top hits under `[Novahiz memory] vault second-memory auto-consulted`. Treat those hits as leads, not conclusions: open the referenced note with the read tool before relying on a snippet.
|
|
9
|
+
|
|
10
|
+
### When to consult the vault proactively
|
|
11
|
+
Search it yourself (`novahiz second-memory search "<terms>"`, then read the note) when:
|
|
12
|
+
- the user asks what was decided or learned earlier (project, strategy, preference, decision);
|
|
13
|
+
- you are about to redo work that has likely already happened (design, migration, analysis, troubleshooting);
|
|
14
|
+
- the topic matches a vault domain (Trading, AI, Design, DevOps, Security, Business, Learning, Wiki, Projects, Journal);
|
|
15
|
+
- the user says « dans ma mémoire », « dans le vault », « comme on avait décidé », "second memory";
|
|
16
|
+
- an answer must survive this session.
|
|
17
|
+
Do **not** consult it for trivial edits, unrelated questions, or when the prompt already carries the needed context.
|
|
6
18
|
|
|
7
19
|
### Rules
|
|
8
|
-
1. When the user asks to **save / memorize / update
|
|
9
|
-
2.
|
|
10
|
-
3.
|
|
11
|
-
4. Never write to `index.md`, `log.md`, `hot.md`, `.manifest.json`, `_meta/`, or `.obsidian/`.
|
|
12
|
-
5. Never create a root
|
|
13
|
-
6. Include mandatory frontmatter: `title, category, tags, sources, created, updated, summary`.
|
|
14
|
-
7.
|
|
20
|
+
1. When the user asks to **save / memorize / update** memory, load the `memory` skill (alias for `novahiz-memory`) and follow its procedure without exception. For vault-only writes and structure questions, load `novahiz-second-memory` instead: it owns the vault's fixed, predefined arborescence.
|
|
21
|
+
2. Vault routing comes from the predefined structure (`catalog/vault-structure.json`, surfaced by the `novahiz-second-memory` skill) — never guess a location. When ambiguous, ask the user, and display the chosen path before writing.
|
|
22
|
+
3. Search before you write: `novahiz second-memory search "<terms>"` — merge into an existing note rather than creating a duplicate. Link pages with `[[wikilinks]]`.
|
|
23
|
+
4. Never write to `index.md`, `log.md`, `hot.md`, `.manifest.json`, `_meta/`, or `.obsidian/`. If the user asks for it, do it step by step in front of them, with a backup first for anything under `.obsidian/`.
|
|
24
|
+
5. Never create a root domain on your own: every new domain requires user agreement and a structure update.
|
|
25
|
+
6. Include mandatory frontmatter: `title, category, tags, sources, created, updated, summary`.
|
|
26
|
+
7. Structure maintenance is CLI work: `novahiz second-memory [init|doctor|fix|sync|status]` (dry-run by default, `--apply` to write).
|
|
15
27
|
|
|
16
28
|
## Playwright Browser — Persistent Profile
|
|
17
29
|
|
|
@@ -397,10 +397,23 @@ type MemoryAutoReadConfig = {
|
|
|
397
397
|
// sessions precedentes + resumes de pertinence compris. Defaut 1500.
|
|
398
398
|
budgetTokens: number;
|
|
399
399
|
};
|
|
400
|
+
// V-AUTO: consultation automatique du vault second-memory (vault Obsidian
|
|
401
|
+
// officiel du systeme, cree a l'installation). every = cadence en occurrences
|
|
402
|
+
// de prompt (1 = chaque prompt, defaut 3 = "de temps en temps"), budgetTokens
|
|
403
|
+
// = plafond d'injection du bloc vault (~4 chars/token). Miroir de
|
|
404
|
+
// MemoryAutoVaultConfig dans src/spec.ts.
|
|
405
|
+
type MemoryAutoVaultConfig = {
|
|
406
|
+
enabled: boolean;
|
|
407
|
+
k: number;
|
|
408
|
+
minScore: number;
|
|
409
|
+
every: number;
|
|
410
|
+
budgetTokens: number;
|
|
411
|
+
};
|
|
400
412
|
type MemoryAutoConfig = {
|
|
401
413
|
enabled: boolean;
|
|
402
414
|
write: MemoryAutoWriteConfig;
|
|
403
415
|
read: MemoryAutoReadConfig;
|
|
416
|
+
vault: MemoryAutoVaultConfig;
|
|
404
417
|
};
|
|
405
418
|
type NovahizConfig = { gate?: GateConfig; memory?: unknown };
|
|
406
419
|
|
|
@@ -442,7 +455,18 @@ export function resolveMemoryAuto(raw: unknown, envValue: string | undefined): M
|
|
|
442
455
|
postCompaction: bool(readRaw.postCompaction, true),
|
|
443
456
|
budgetTokens: inRange(readRaw.budgetTokens, 100, 4000) ? Math.trunc(readRaw.budgetTokens) : 1500
|
|
444
457
|
};
|
|
445
|
-
|
|
458
|
+
// V-AUTO: vault second-memory — defauts full-on aussi (actif partout par
|
|
459
|
+
// defaut, comme write/read), kill-switch partage NOVAHIZ_MEM_AUTO + reset
|
|
460
|
+
// local memory.auto.vault.enabled=false. Miroir de src/spec.ts.
|
|
461
|
+
const vaultRaw = auto.vault && typeof auto.vault === "object" ? (auto.vault as Record<string, unknown>) : {};
|
|
462
|
+
const vault: MemoryAutoVaultConfig = {
|
|
463
|
+
enabled: bool(vaultRaw.enabled, true),
|
|
464
|
+
k: inRange(vaultRaw.k, 1, 10) ? Math.trunc(vaultRaw.k) : 3,
|
|
465
|
+
minScore: inRange(vaultRaw.minScore, 0, 1) ? vaultRaw.minScore : 0.25,
|
|
466
|
+
every: inRange(vaultRaw.every, 1, 100) ? Math.trunc(vaultRaw.every) : 3,
|
|
467
|
+
budgetTokens: inRange(vaultRaw.budgetTokens, 100, 2000) ? Math.trunc(vaultRaw.budgetTokens) : 600
|
|
468
|
+
};
|
|
469
|
+
return { enabled: true, write, read, vault };
|
|
446
470
|
}
|
|
447
471
|
|
|
448
472
|
function readConfig(): NovahizConfig {
|
|
@@ -826,6 +850,39 @@ export function mergeSummaryWindow(
|
|
|
826
850
|
return { ids, details };
|
|
827
851
|
}
|
|
828
852
|
|
|
853
|
+
// V-AUTO: vault officiel du systeme — cree par l'installateur a
|
|
854
|
+
// `~/Documents/second-memory` (NOVAHIZ_SM_VAULT pour tests/overrides).
|
|
855
|
+
export const VAULT_ROOT: string =
|
|
856
|
+
process.env.NOVAHIZ_SM_VAULT && process.env.NOVAHIZ_SM_VAULT.trim().length > 0
|
|
857
|
+
? process.env.NOVAHIZ_SM_VAULT
|
|
858
|
+
: join(homedir(), "Documents", "second-memory");
|
|
859
|
+
|
|
860
|
+
type VaultHitLike = { rel?: unknown; title?: unknown; score?: unknown; snippet?: unknown };
|
|
861
|
+
|
|
862
|
+
// V-AUTO: formatage des hits `second-memory search --json` en lignes
|
|
863
|
+
// d'injection (une ligne chemin+titre+score, une ligne extrait tronque),
|
|
864
|
+
// filtres k / minScore. Exporte tel quel pour les tests (pure function).
|
|
865
|
+
export function buildVaultLines(
|
|
866
|
+
hits: readonly VaultHitLike[],
|
|
867
|
+
cfg: MemoryAutoVaultConfig
|
|
868
|
+
): { injectedRels: string[]; details: string[] } {
|
|
869
|
+
const injectedRels: string[] = [];
|
|
870
|
+
const details: string[] = [];
|
|
871
|
+
for (const hit of hits) {
|
|
872
|
+
if (injectedRels.length >= cfg.k) break;
|
|
873
|
+
if (!hit || typeof hit.rel !== "string" || hit.rel.length === 0) continue;
|
|
874
|
+
if (typeof hit.score === "number" && hit.score < cfg.minScore) continue;
|
|
875
|
+
const title = typeof hit.title === "string" && hit.title.length > 0 ? hit.title : hit.rel;
|
|
876
|
+
const score = typeof hit.score === "number" ? hit.score.toFixed(2) : "-";
|
|
877
|
+
injectedRels.push(hit.rel);
|
|
878
|
+
details.push(`- ${hit.rel} [${score}] — ${title}`);
|
|
879
|
+
if (typeof hit.snippet === "string" && hit.snippet.trim().length > 0) {
|
|
880
|
+
details.push(` ${hit.snippet.replace(/\s+/g, " ").trim().slice(0, 220)}`);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return { injectedRels, details };
|
|
884
|
+
}
|
|
885
|
+
|
|
829
886
|
// --- P3: heritage inter-sessions (injection au demarrage) -----------------
|
|
830
887
|
// L'index porte id/title/updated/status/tags (SlotMeta, src/memory.ts); le
|
|
831
888
|
// plugin installe ne resout pas ../../src/*, donc le type est re-declare ici.
|
|
@@ -1047,6 +1104,10 @@ async function setup(ctx: Context): Promise<Cleanup> {
|
|
|
1047
1104
|
// chaque prompt, chunk de resumes courant (remplacement post-compaction)
|
|
1048
1105
|
// et cles de directive T3 deja posees.
|
|
1049
1106
|
const autoReadBySession = new Map<string, { ids: string[]; details: string[] }>();
|
|
1107
|
+
// V-AUTO: fenetre de notes vault re-injectee entre deux consultations, et
|
|
1108
|
+
// compteur de prompts pour la cadence memory.auto.vault.every.
|
|
1109
|
+
const vaultWindowBySession = new Map<string, { rels: string[]; details: string[] }>();
|
|
1110
|
+
const vaultCountBySession = new Map<string, number>();
|
|
1050
1111
|
const lastPromptBySession = new Map<string, string>();
|
|
1051
1112
|
const directivesBySession = new Map<string, string[]>();
|
|
1052
1113
|
const summaryChunkBySession = new Map<string, string[]>();
|
|
@@ -1261,21 +1322,81 @@ async function setup(ctx: Context): Promise<Cleanup> {
|
|
|
1261
1322
|
}
|
|
1262
1323
|
};
|
|
1263
1324
|
|
|
1264
|
-
//
|
|
1265
|
-
//
|
|
1266
|
-
|
|
1325
|
+
// V-AUTO: consultation automatique du vault second-memory. Aller simple via
|
|
1326
|
+
// la CLI (`second-memory search --json`) : le plugin installe ne peut pas
|
|
1327
|
+
// importer src/*, donc le ranking reel est celui de la CLI — une seule
|
|
1328
|
+
// source de verite pour l'agent et pour l'injection. Cadence every (defaut
|
|
1329
|
+
// 3) : le 1er prompt consulte toujours, la fenetre est re-emise entre deux
|
|
1330
|
+
// consultations pour que les notes restent dans le contexte.
|
|
1331
|
+
const buildVaultRead = async (
|
|
1332
|
+
sessionID: string,
|
|
1333
|
+
query: string
|
|
1334
|
+
): Promise<{ lines: string[]; newCount: number }> => {
|
|
1335
|
+
if (!MEMORY_AUTO || !MEMORY_AUTO.vault.enabled) return { lines: [], newCount: 0 };
|
|
1267
1336
|
try {
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1337
|
+
let state = vaultWindowBySession.get(sessionID) ?? { rels: [], details: [] };
|
|
1338
|
+
let newCount = 0;
|
|
1339
|
+
const n = (vaultCountBySession.get(sessionID) ?? 0) + 1;
|
|
1340
|
+
vaultCountBySession.set(sessionID, n);
|
|
1341
|
+
if ((n - 1) % MEMORY_AUTO.vault.every === 0) {
|
|
1342
|
+
if (!existsSync(VAULT_ROOT)) {
|
|
1343
|
+
if (n === 1) void log("info", `vault auto-consult skipped: ${VAULT_ROOT} absent`);
|
|
1344
|
+
} else {
|
|
1345
|
+
const call = await run([
|
|
1346
|
+
"second-memory",
|
|
1347
|
+
"search",
|
|
1348
|
+
autoReadQuery(query, 24),
|
|
1349
|
+
`--k=${MEMORY_AUTO.vault.k}`,
|
|
1350
|
+
`--min-score=${MEMORY_AUTO.vault.minScore}`,
|
|
1351
|
+
"--json"
|
|
1352
|
+
]);
|
|
1353
|
+
if (call.status !== 0 || call.stdout.trim().length === 0) {
|
|
1354
|
+
void log("info", `vault auto-consult failed: ${call.stderr.slice(0, 200) || `exit ${call.status}`}`);
|
|
1355
|
+
} else {
|
|
1356
|
+
try {
|
|
1357
|
+
const parsed = JSON.parse(call.stdout) as { hits?: unknown };
|
|
1358
|
+
const hits = Array.isArray(parsed.hits) ? (parsed.hits as VaultHitLike[]) : [];
|
|
1359
|
+
const fresh = buildVaultLines(hits, MEMORY_AUTO.vault);
|
|
1360
|
+
if (fresh.injectedRels.length > 0) {
|
|
1361
|
+
newCount = fresh.injectedRels.length;
|
|
1362
|
+
state = { rels: fresh.injectedRels, details: fresh.details };
|
|
1363
|
+
} else {
|
|
1364
|
+
void log(
|
|
1365
|
+
"info",
|
|
1366
|
+
`vault auto-consult: ${hits.length} hit(s), 0 injected (minScore ${MEMORY_AUTO.vault.minScore})`
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
} catch {
|
|
1370
|
+
void log("info", "vault auto-consult: invalid JSON from second-memory search");
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1275
1373
|
}
|
|
1276
|
-
} else if (providers.status !== 0) {
|
|
1277
|
-
await log("warn", `Providers command failed (exit ${providers.status}), MCP auto-register skipped`);
|
|
1278
1374
|
}
|
|
1375
|
+
if (state.rels.length === 0) return { lines: [], newCount };
|
|
1376
|
+
vaultWindowBySession.set(sessionID, state);
|
|
1377
|
+
const kept = applyInjectionBudget(state.details, MEMORY_AUTO.vault.budgetTokens * 4);
|
|
1378
|
+
const lines = [
|
|
1379
|
+
`[Novahiz memory] vault second-memory auto-consulted (${state.rels.length}/${MEMORY_AUTO.vault.k}):`,
|
|
1380
|
+
...kept,
|
|
1381
|
+
`Vault: ${VAULT_ROOT} — read a full note with the read tool on <vault>/<rel>; re-search: novahiz second-memory search "<terms>".`
|
|
1382
|
+
];
|
|
1383
|
+
return { lines, newCount };
|
|
1384
|
+
} catch (error) {
|
|
1385
|
+
void log("info", `vault auto-consult failed: ${String(error).slice(0, 200)}`);
|
|
1386
|
+
return { lines: [], newCount: 0 };
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
|
|
1390
|
+
// V1 `config` hook → MCP transform. Audit 2026-10-08 : le transform partait
|
|
1391
|
+
// APRES l'attente du spawn `providers --mcp-json` (~0,5-1s) et arrivait trop
|
|
1392
|
+
// tard — aucun serveur lié, aucun avertissement au log. Le trio runtime part
|
|
1393
|
+
// donc en premier, avant toute await ; le catalogue compose en second
|
|
1394
|
+
// transform (les transforms se cumulent dans leur domaine). Les callbacks
|
|
1395
|
+
// restent synchrones et sans effet de bord un seul. En pratique la config
|
|
1396
|
+
// statique de l'installateur porte deja les entrees : editor.get(id) les
|
|
1397
|
+
// saute, sans doublon.
|
|
1398
|
+
if (!DISABLED) {
|
|
1399
|
+
try {
|
|
1279
1400
|
await ctx.mcp.transform((editor) => {
|
|
1280
1401
|
// A server configured by the user wins over the catalog registration.
|
|
1281
1402
|
// Audit 2026-09-25 (P2): novahiz-scan joined the hard-coded trio — when
|
|
@@ -1290,23 +1411,38 @@ async function setup(ctx: Context): Promise<Cleanup> {
|
|
|
1290
1411
|
if (!editor.get("novahiz-scan")) {
|
|
1291
1412
|
editor.set("novahiz-scan", { type: "local", command: [NODE, join(HOME, "mcp", "argus", "src", "cli.mjs")], timeout: MCP_TIMEOUT });
|
|
1292
1413
|
}
|
|
1293
|
-
for (const [id, raw] of Object.entries(entries)) {
|
|
1294
|
-
if (editor.get(id)) continue;
|
|
1295
|
-
const entry = raw as { type?: unknown; command?: unknown; url?: unknown; enabled?: unknown };
|
|
1296
|
-
// V2 replaced `enabled` with `disabled`: an explicit `enabled: false`
|
|
1297
|
-
// is carried across instead of silently flipping the server on.
|
|
1298
|
-
const disabled = entry.enabled === false ? { disabled: true as const } : {};
|
|
1299
|
-
if (entry.type === "remote" && typeof entry.url === "string") {
|
|
1300
|
-
editor.set(id, { type: "remote", url: entry.url, timeout: MCP_TIMEOUT, ...disabled });
|
|
1301
|
-
} else if (
|
|
1302
|
-
entry.type === "local" &&
|
|
1303
|
-
Array.isArray(entry.command) &&
|
|
1304
|
-
entry.command.every((part) => typeof part === "string")
|
|
1305
|
-
) {
|
|
1306
|
-
editor.set(id, { type: "local", command: entry.command as string[], timeout: MCP_TIMEOUT, ...disabled });
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
1414
|
});
|
|
1415
|
+
const providers = await run(["providers", "--mcp-json"]);
|
|
1416
|
+
const entries: Record<string, unknown> = {};
|
|
1417
|
+
if (providers.status === 0 && providers.stdout.trim().length > 0) {
|
|
1418
|
+
try {
|
|
1419
|
+
Object.assign(entries, JSON.parse(providers.stdout) as Record<string, unknown>);
|
|
1420
|
+
} catch {
|
|
1421
|
+
await log("warn", "Providers returned invalid JSON, MCP auto-register skipped");
|
|
1422
|
+
}
|
|
1423
|
+
} else if (providers.status !== 0) {
|
|
1424
|
+
await log("warn", `Providers command failed (exit ${providers.status}), MCP auto-register skipped`);
|
|
1425
|
+
}
|
|
1426
|
+
if (Object.keys(entries).length > 0) {
|
|
1427
|
+
await ctx.mcp.transform((editor) => {
|
|
1428
|
+
for (const [id, raw] of Object.entries(entries)) {
|
|
1429
|
+
if (editor.get(id)) continue;
|
|
1430
|
+
const entry = raw as { type?: unknown; command?: unknown; url?: unknown; enabled?: unknown };
|
|
1431
|
+
// V2 replaced `enabled` with `disabled`: an explicit `enabled: false`
|
|
1432
|
+
// is carried across instead of silently flipping the server on.
|
|
1433
|
+
const disabled = entry.enabled === false ? { disabled: true as const } : {};
|
|
1434
|
+
if (entry.type === "remote" && typeof entry.url === "string") {
|
|
1435
|
+
editor.set(id, { type: "remote", url: entry.url, timeout: MCP_TIMEOUT, ...disabled });
|
|
1436
|
+
} else if (
|
|
1437
|
+
entry.type === "local" &&
|
|
1438
|
+
Array.isArray(entry.command) &&
|
|
1439
|
+
entry.command.every((part) => typeof part === "string")
|
|
1440
|
+
) {
|
|
1441
|
+
editor.set(id, { type: "local", command: entry.command as string[], timeout: MCP_TIMEOUT, ...disabled });
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1310
1446
|
} catch (error) {
|
|
1311
1447
|
await log("warn", `MCP registration failed: ${String(error).slice(0, 200)}`);
|
|
1312
1448
|
}
|
|
@@ -1454,7 +1590,13 @@ async function setup(ctx: Context): Promise<Cleanup> {
|
|
|
1454
1590
|
// chemin lecture a tourne, y compris quand il ressort vide.
|
|
1455
1591
|
await log("info", `memory auto-read prompt: ${auto.lines.length} line(s), ${auto.newCount} new`);
|
|
1456
1592
|
lines.push(...auto.lines);
|
|
1457
|
-
|
|
1593
|
+
// V-AUTO: bloc vault second-memory — meme contrat que les resumes :
|
|
1594
|
+
// reconstruit a chaque prompt, fenetre persistante entre deux
|
|
1595
|
+
// consultations, log inconditionnel pour prouver la consultation.
|
|
1596
|
+
const vault = await buildVaultRead(sessionID, text);
|
|
1597
|
+
await log("info", `vault auto-consult prompt: ${vault.lines.length} line(s), ${vault.newCount} new`);
|
|
1598
|
+
lines.push(...vault.lines);
|
|
1599
|
+
summaryChunkBySession.set(sessionID, [...auto.lines, ...vault.lines]);
|
|
1458
1600
|
const directives = directivesBySession.get(sessionID) ?? [];
|
|
1459
1601
|
lines.push(...directives);
|
|
1460
1602
|
}
|
|
@@ -1712,5 +1854,5 @@ async function setup(ctx: Context): Promise<Cleanup> {
|
|
|
1712
1854
|
return () => events.abort();
|
|
1713
1855
|
}
|
|
1714
1856
|
|
|
1715
|
-
const novahizPlugin: Plugin = { id: "novahiz", setup };
|
|
1857
|
+
const novahizPlugin: Plugin = { id: "novahiz-workflow", setup };
|
|
1716
1858
|
export default novahizPlugin;
|
package/catalog/categories.json
CHANGED
|
@@ -198,6 +198,13 @@
|
|
|
198
198
|
"kind": "skill",
|
|
199
199
|
"requireSkills": ["dart-add-unit-test"]
|
|
200
200
|
},
|
|
201
|
+
{
|
|
202
|
+
"id": "stitch-verify",
|
|
203
|
+
"label": "Verify screens against Stitch references (projects carrying stitch/**)",
|
|
204
|
+
"kind": "verify",
|
|
205
|
+
"requireSkills": ["novahiz-stitch-fidelity"],
|
|
206
|
+
"optional": true
|
|
207
|
+
},
|
|
201
208
|
{
|
|
202
209
|
"id": "impeccable-critique",
|
|
203
210
|
"label": "Impeccable critique (UI work)",
|
|
@@ -307,6 +314,13 @@
|
|
|
307
314
|
"kind": "edit",
|
|
308
315
|
"requireSkills": ["novahiz-implement"]
|
|
309
316
|
},
|
|
317
|
+
{
|
|
318
|
+
"id": "stitch-verify",
|
|
319
|
+
"label": "Verify screens against Stitch references (projects carrying stitch/**)",
|
|
320
|
+
"kind": "verify",
|
|
321
|
+
"requireSkills": ["novahiz-stitch-fidelity"],
|
|
322
|
+
"optional": true
|
|
323
|
+
},
|
|
310
324
|
{
|
|
311
325
|
"id": "converge",
|
|
312
326
|
"label": "Converge",
|
package/catalog/providers.json
CHANGED
|
@@ -108,6 +108,31 @@
|
|
|
108
108
|
"categories": ["audit"],
|
|
109
109
|
"purpose": "Local stdio static-analysis server (zero npm dependency): scans files and directories with local security rules (SQL injection, XSS, command injection, hardcoded secrets, insecure deserialization), creates custom rules behind a ReDoS gate, exports json/sarif/csv/html. Original clean-room implementation for the audit category."
|
|
110
110
|
},
|
|
111
|
+
{
|
|
112
|
+
"id": "stitch-fidelity",
|
|
113
|
+
"label": "Stitch fidelity capture (adb + Android emulator)",
|
|
114
|
+
"kind": "commands",
|
|
115
|
+
"requires": ["adb"],
|
|
116
|
+
"bootstrap": {
|
|
117
|
+
"win32": [
|
|
118
|
+
"winget install --id Google.PlatformTools",
|
|
119
|
+
"winget install --id Google.AndroidStudio"
|
|
120
|
+
],
|
|
121
|
+
"darwin": [
|
|
122
|
+
"brew install --cask android-platform-tools",
|
|
123
|
+
"brew install --cask android-commandlinetools"
|
|
124
|
+
],
|
|
125
|
+
"linux": [
|
|
126
|
+
"sudo apt-get install -y adb",
|
|
127
|
+
"sudo apt-get install -y android-sdk"
|
|
128
|
+
],
|
|
129
|
+
"default": ["install Android platform-tools (adb) and the Android emulator on PATH"]
|
|
130
|
+
},
|
|
131
|
+
"source": "https://github.com/novahiz/novahiz",
|
|
132
|
+
"license": "Apache-2.0",
|
|
133
|
+
"categories": ["design-ui", "expo", "flutter"],
|
|
134
|
+
"purpose": "Prerequisite for skill novahiz-stitch-fidelity: `adb` drives the capture (uiautomator dump, screencap, input). `emulator` is only needed to auto-start a device when nothing is online (adb.autoStart); `stitch_capture.mjs --check` reports all seven readiness items."
|
|
135
|
+
},
|
|
111
136
|
{
|
|
112
137
|
"id": "novahiz-gate",
|
|
113
138
|
"label": "novahiz-gate",
|
package/catalog/rules.json
CHANGED
|
@@ -156,5 +156,24 @@
|
|
|
156
156
|
]
|
|
157
157
|
},
|
|
158
158
|
"require": ["token-economy"]
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"id": "R16-stitch-fidelity",
|
|
162
|
+
"description": "Load novahiz-stitch-fidelity before editing the UI of a project that carries Stitch references (stitch/**): capture, composite and fidelity verdict must be part of that loop.",
|
|
163
|
+
"when": {
|
|
164
|
+
"match": "all",
|
|
165
|
+
"pathGlobs": [
|
|
166
|
+
"**/*.tsx",
|
|
167
|
+
"**/*.jsx",
|
|
168
|
+
"**/*.vue",
|
|
169
|
+
"**/*.svelte",
|
|
170
|
+
"**/*.dart",
|
|
171
|
+
"**/*.css",
|
|
172
|
+
"**/*.scss",
|
|
173
|
+
"**/*.html"
|
|
174
|
+
],
|
|
175
|
+
"projectGlobs": ["stitch/**"]
|
|
176
|
+
},
|
|
177
|
+
"require": ["novahiz-stitch-fidelity"]
|
|
159
178
|
}
|
|
160
179
|
]
|