@miadi/stateloom-mcp 0.2.1 → 0.2.2
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/dist/projectSwitch.d.ts +13 -0
- package/dist/projectSwitch.js +66 -3
- package/dist/server.js +1 -1
- package/package.json +1 -1
package/dist/projectSwitch.d.ts
CHANGED
|
@@ -8,4 +8,17 @@ export interface SwitchResult {
|
|
|
8
8
|
/** True when the new path equals the previous one. */
|
|
9
9
|
unchanged: boolean;
|
|
10
10
|
}
|
|
11
|
+
export declare function chronicleRoot(env?: Record<string, string | undefined>): string;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve `miadi-chronicle://<episode>/[diagrams/]<name>[.smdf.json]` to the
|
|
14
|
+
* absolute path `<root>/<episode-dir>/diagrams/<name>.smdf.json`.
|
|
15
|
+
*
|
|
16
|
+
* Episode matching: exact folder name first; else a bare number matches
|
|
17
|
+
* `-episode-NNN-` folders. Episode numbers are NOT unique in the chronicle
|
|
18
|
+
* (011, 016, 043, 078, 083, 098, 120 are doubled) — ambiguity is a HARD
|
|
19
|
+
* ERROR naming every candidate, never a silent first match: a resolver that
|
|
20
|
+
* guesses writes the wrong episode into the record. A missing diagram file
|
|
21
|
+
* stays legitimate — naming it is how an episode's FIRST diagram is created.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveChronicleUri(uri: string, env?: Record<string, string | undefined>): string;
|
|
11
24
|
export declare function resolveProjectSwitch(next: string, previous: string): SwitchResult;
|
package/dist/projectSwitch.js
CHANGED
|
@@ -1,11 +1,74 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pure logic behind the `set_project_file` MCP tool (path power — the agent
|
|
3
3
|
* chooses which `.smdf.json` document the loom weaves).
|
|
4
|
+
*
|
|
5
|
+
* Two modalities, by William's orientation (2026-08-07): STANDALONE — a plain
|
|
6
|
+
* path, no episode, just designing — and RELATIONAL — the document lives
|
|
7
|
+
* inside a chronicle episode and is addressed as
|
|
8
|
+
* `miadi-chronicle://<episode>/<diagram>`, resolving over
|
|
9
|
+
* $MIADI_CHRONICLE_ROOT to `<episode-dir>/diagrams/<diagram>.smdf.json`.
|
|
10
|
+
* The relationship is carried by WHERE the file lives (the scalar truth of
|
|
11
|
+
* jgwill/Miadi#593); wheel edges stay derived/ceremonial, never minted here.
|
|
4
12
|
*/
|
|
5
|
-
import { existsSync } from "fs";
|
|
6
|
-
import { resolve } from "path";
|
|
13
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
|
14
|
+
import { join, resolve } from "path";
|
|
15
|
+
const CHRONICLE_SCHEME = /^miadi-chronicle:\/\//i;
|
|
16
|
+
export function chronicleRoot(env = process.env) {
|
|
17
|
+
return env.MIADI_CHRONICLE_ROOT ?? "/srv/miadi/episodes/miadi-chronicle";
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve `miadi-chronicle://<episode>/[diagrams/]<name>[.smdf.json]` to the
|
|
21
|
+
* absolute path `<root>/<episode-dir>/diagrams/<name>.smdf.json`.
|
|
22
|
+
*
|
|
23
|
+
* Episode matching: exact folder name first; else a bare number matches
|
|
24
|
+
* `-episode-NNN-` folders. Episode numbers are NOT unique in the chronicle
|
|
25
|
+
* (011, 016, 043, 078, 083, 098, 120 are doubled) — ambiguity is a HARD
|
|
26
|
+
* ERROR naming every candidate, never a silent first match: a resolver that
|
|
27
|
+
* guesses writes the wrong episode into the record. A missing diagram file
|
|
28
|
+
* stays legitimate — naming it is how an episode's FIRST diagram is created.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveChronicleUri(uri, env = process.env) {
|
|
31
|
+
const rest = uri.replace(CHRONICLE_SCHEME, "").replace(/[?#].*$/, "");
|
|
32
|
+
const segments = rest.split("/").filter(Boolean);
|
|
33
|
+
if (segments.length < 2) {
|
|
34
|
+
throw new Error(`chronicle address needs <episode>/<diagram> (got '${uri}') — e.g. miadi-chronicle://103/film-preprod`);
|
|
35
|
+
}
|
|
36
|
+
const episodeRef = segments[0];
|
|
37
|
+
const name = segments[segments.length - 1];
|
|
38
|
+
const root = chronicleRoot(env);
|
|
39
|
+
let dirs;
|
|
40
|
+
try {
|
|
41
|
+
dirs = readdirSync(root).filter((d) => {
|
|
42
|
+
try {
|
|
43
|
+
return statSync(join(root, d)).isDirectory();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new Error(`chronicle root not readable: ${root} (MIADI_CHRONICLE_ROOT)`);
|
|
52
|
+
}
|
|
53
|
+
let matches = dirs.filter((d) => d === episodeRef);
|
|
54
|
+
if (matches.length === 0 && /^\d+$/.test(episodeRef)) {
|
|
55
|
+
const num = String(parseInt(episodeRef, 10));
|
|
56
|
+
matches = dirs.filter((d) => {
|
|
57
|
+
const m = d.match(/-episode-0*(\d+)-/);
|
|
58
|
+
return m !== null && m[1] === num;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (matches.length === 0) {
|
|
62
|
+
throw new Error(`no episode matches '${episodeRef}' under ${root}`);
|
|
63
|
+
}
|
|
64
|
+
if (matches.length > 1) {
|
|
65
|
+
throw new Error(`episode '${episodeRef}' is ambiguous — candidates:\n ${matches.sort().join("\n ")}\nName the full folder.`);
|
|
66
|
+
}
|
|
67
|
+
const file = name.endsWith(".json") ? name : `${name}.smdf.json`;
|
|
68
|
+
return join(root, matches[0], "diagrams", file);
|
|
69
|
+
}
|
|
7
70
|
export function resolveProjectSwitch(next, previous) {
|
|
8
|
-
const path = resolve(next);
|
|
71
|
+
const path = CHRONICLE_SCHEME.test(next) ? resolveChronicleUri(next) : resolve(next);
|
|
9
72
|
if (!path.endsWith(".json")) {
|
|
10
73
|
throw new Error(`project file must be a .json document (got '${path}') — the loom weaves .smdf.json files`);
|
|
11
74
|
}
|
package/dist/server.js
CHANGED
|
@@ -787,7 +787,7 @@ function buildServer() {
|
|
|
787
787
|
return { content: [{ type: "text", text: "No events defined yet." }] };
|
|
788
788
|
return { content: [{ type: "text", text: events.join("\n") }] };
|
|
789
789
|
});
|
|
790
|
-
server.tool("set_project_file", "Choose
|
|
790
|
+
server.tool("set_project_file", "Choose the active document the loom weaves — two modalities. STANDALONE: any .smdf.json path. RELATIONAL: a chronicle address `miadi-chronicle://<episode>/<diagram>` resolving over $MIADI_CHRONICLE_ROOT to <episode-dir>/diagrams/<diagram>.smdf.json — an ambiguous episode number is a hard error naming the candidates. Disk persistence AND the live bridge room both re-point. A missing file is legitimate — naming it is how an episode's first diagram is created.", { path: z.string() }, async ({ path }) => {
|
|
791
791
|
try {
|
|
792
792
|
const r = resolveProjectSwitch(path, PROJECT_FILE);
|
|
793
793
|
const denial = denyProjectSwitch(r.path);
|
package/package.json
CHANGED