zelari-code 2.55.0 → 2.57.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.
Files changed (106) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/app.js +2 -2
  3. package/dist/cli/app.js.map +1 -1
  4. package/dist/cli/budget/modelContextBuilder.js +4 -1
  5. package/dist/cli/budget/modelContextBuilder.js.map +1 -1
  6. package/dist/cli/commands/evolve.js +247 -0
  7. package/dist/cli/commands/evolve.js.map +1 -0
  8. package/dist/cli/commands/replay.js +202 -0
  9. package/dist/cli/commands/replay.js.map +1 -0
  10. package/dist/cli/commands/session.js +264 -0
  11. package/dist/cli/commands/session.js.map +1 -0
  12. package/dist/cli/commands/spineSession.js +77 -0
  13. package/dist/cli/commands/spineSession.js.map +1 -0
  14. package/dist/cli/components/StatusBar.js +6 -2
  15. package/dist/cli/components/StatusBar.js.map +1 -1
  16. package/dist/cli/components/statusChips.js +43 -0
  17. package/dist/cli/components/statusChips.js.map +1 -1
  18. package/dist/cli/councilDispatcher.js +12 -0
  19. package/dist/cli/councilDispatcher.js.map +1 -1
  20. package/dist/cli/evolution/controller.js +107 -0
  21. package/dist/cli/evolution/controller.js.map +1 -0
  22. package/dist/cli/evolution/ledger.js +127 -0
  23. package/dist/cli/evolution/ledger.js.map +1 -1
  24. package/dist/cli/evolution/patternLedger.js +165 -0
  25. package/dist/cli/evolution/patternLedger.js.map +1 -0
  26. package/dist/cli/headless/runOneTurn.js +31 -1
  27. package/dist/cli/headless/runOneTurn.js.map +1 -1
  28. package/dist/cli/hooks/useChatTurn.js +60 -2
  29. package/dist/cli/hooks/useChatTurn.js.map +1 -1
  30. package/dist/cli/hooks/useSlashDispatch.js +8 -0
  31. package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
  32. package/dist/cli/inbox.js +63 -10
  33. package/dist/cli/inbox.js.map +1 -1
  34. package/dist/cli/inboxSources.js +173 -0
  35. package/dist/cli/inboxSources.js.map +1 -0
  36. package/dist/cli/main.bundled.js +11889 -8078
  37. package/dist/cli/main.bundled.js.map +4 -4
  38. package/dist/cli/main.js +80 -0
  39. package/dist/cli/main.js.map +1 -1
  40. package/dist/cli/memory/mcpAdapter.js +3 -1
  41. package/dist/cli/memory/mcpAdapter.js.map +1 -1
  42. package/dist/cli/memory/onePager.js +93 -0
  43. package/dist/cli/memory/onePager.js.map +1 -0
  44. package/dist/cli/memory/serviceFactory.js +5 -0
  45. package/dist/cli/memory/serviceFactory.js.map +1 -1
  46. package/dist/cli/memory/sqliteBackend.js +40 -3
  47. package/dist/cli/memory/sqliteBackend.js.map +1 -1
  48. package/dist/cli/memory/sqliteCodec.js +2 -0
  49. package/dist/cli/memory/sqliteCodec.js.map +1 -1
  50. package/dist/cli/memory/sqliteSchema.js +11 -1
  51. package/dist/cli/memory/sqliteSchema.js.map +1 -1
  52. package/dist/cli/plugins/bundleCommand.js +238 -0
  53. package/dist/cli/plugins/bundleCommand.js.map +1 -0
  54. package/dist/cli/plugins/bundleDiscover.js +74 -0
  55. package/dist/cli/plugins/bundleDiscover.js.map +1 -0
  56. package/dist/cli/plugins/bundleFs.js +57 -0
  57. package/dist/cli/plugins/bundleFs.js.map +1 -0
  58. package/dist/cli/plugins/bundleHookDef.js +70 -0
  59. package/dist/cli/plugins/bundleHookDef.js.map +1 -0
  60. package/dist/cli/plugins/bundleLoad.js +180 -0
  61. package/dist/cli/plugins/bundleLoad.js.map +1 -0
  62. package/dist/cli/plugins/bundleManifest.js +233 -0
  63. package/dist/cli/plugins/bundleManifest.js.map +1 -0
  64. package/dist/cli/plugins/bundleMcp.js +41 -0
  65. package/dist/cli/plugins/bundleMcp.js.map +1 -0
  66. package/dist/cli/plugins/bundleState.js +142 -0
  67. package/dist/cli/plugins/bundleState.js.map +1 -0
  68. package/dist/cli/runHeadless.js +102 -12
  69. package/dist/cli/runHeadless.js.map +1 -1
  70. package/dist/cli/safety/decisionEmit.js +43 -0
  71. package/dist/cli/safety/decisionEmit.js.map +1 -0
  72. package/dist/cli/safety/permissionGate.js +239 -0
  73. package/dist/cli/safety/permissionGate.js.map +1 -0
  74. package/dist/cli/safety/permissionPolicy.js +279 -0
  75. package/dist/cli/safety/permissionPolicy.js.map +1 -0
  76. package/dist/cli/safety/permissionRules.js +145 -0
  77. package/dist/cli/safety/permissionRules.js.map +1 -0
  78. package/dist/cli/safety/sessionSink.js +169 -0
  79. package/dist/cli/safety/sessionSink.js.map +1 -0
  80. package/dist/cli/slashCommands.js +29 -3
  81. package/dist/cli/slashCommands.js.map +1 -1
  82. package/dist/cli/slashHandlers/evolve.js +41 -0
  83. package/dist/cli/slashHandlers/evolve.js.map +1 -0
  84. package/dist/cli/slashHandlers/memory.js +21 -0
  85. package/dist/cli/slashHandlers/memory.js.map +1 -1
  86. package/dist/cli/slashHandlers/permissions.js +170 -0
  87. package/dist/cli/slashHandlers/permissions.js.map +1 -0
  88. package/dist/cli/statusline/inboxFeed.js +76 -0
  89. package/dist/cli/statusline/inboxFeed.js.map +1 -0
  90. package/dist/cli/statusline/statuslineItems.js +15 -0
  91. package/dist/cli/statusline/statuslineItems.js.map +1 -1
  92. package/dist/cli/toolRegistry.js +161 -7
  93. package/dist/cli/toolRegistry.js.map +1 -1
  94. package/dist/cli/toolResultCache.js +4 -22
  95. package/dist/cli/toolResultCache.js.map +1 -1
  96. package/dist/cli/tools/askUser.js +17 -2
  97. package/dist/cli/tools/askUser.js.map +1 -1
  98. package/dist/cli/tools/execProcess.js +15 -1
  99. package/dist/cli/tools/execProcess.js.map +1 -1
  100. package/dist/cli/tools/krakenWorktree.js +334 -43
  101. package/dist/cli/tools/krakenWorktree.js.map +1 -1
  102. package/dist/cli/tools/taskTool.js +179 -43
  103. package/dist/cli/tools/taskTool.js.map +1 -1
  104. package/dist/cli/tools/verifyDebtSpine.js +41 -0
  105. package/dist/cli/tools/verifyDebtSpine.js.map +1 -1
  106. package/package.json +6 -2
@@ -0,0 +1,57 @@
1
+ /**
2
+ * plugins/bundleFs — the filesystem primitives a bundle check needs.
3
+ *
4
+ * Extracted so `bundleLoad.ts`, `bundleDiscover.ts` and `bundleCommand.ts`
5
+ * agree on ONE definition of "the declared file exists and stays inside the
6
+ * bundle" instead of each growing its own copy.
7
+ *
8
+ * @since v2.58.0 (WS6 / plugin bundle v1)
9
+ */
10
+ import { stat } from 'node:fs/promises';
11
+ import path from 'node:path';
12
+ /** `true` only for an existing regular file; never throws. */
13
+ export async function isFile(p) {
14
+ try {
15
+ return (await stat(p)).isFile();
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ }
21
+ /** `true` only for an existing directory; never throws. */
22
+ export async function isDir(p) {
23
+ try {
24
+ return (await stat(p)).isDirectory();
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ /** Narrow a parsed JSON value to an object literal (not an array, not null). */
31
+ export function isRecord(value) {
32
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
33
+ }
34
+ /**
35
+ * Resolve one declared bundle-relative path and prove it is a real file INSIDE
36
+ * the bundle directory.
37
+ *
38
+ * `bundleManifest.ts` already rejects an absolute path or a `..` segment, so
39
+ * this is defence in depth: a bundle may only ever read files it actually
40
+ * contains, even if a future path rule loosens. On failure the message names
41
+ * the manifest, the FIELD and what was wrong; `null` is returned and the
42
+ * caller skips the entry.
43
+ */
44
+ export async function resolveInsideBundle(root, relPath, at, manifestPath, errors) {
45
+ const abs = path.resolve(root, relPath);
46
+ const rel = path.relative(root, abs);
47
+ if (rel === '' || rel.startsWith('..') || path.isAbsolute(rel)) {
48
+ errors.push(`${manifestPath}: invalid bundle manifest at '${at}': path escapes the bundle directory ('${relPath}')`);
49
+ return null;
50
+ }
51
+ if (!(await isFile(abs))) {
52
+ errors.push(`${manifestPath}: invalid bundle manifest at '${at}': file not found ('${relPath}')`);
53
+ return null;
54
+ }
55
+ return abs;
56
+ }
57
+ //# sourceMappingURL=bundleFs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundleFs.js","sourceRoot":"","sources":["../../../src/cli/plugins/bundleFs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,CAAS;IACpC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,2DAA2D;AAC3D,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,CAAS;IACnC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAAY,EACZ,OAAe,EACf,EAAU,EACV,YAAoB,EACpB,MAAgB;IAEhB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACrC,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/D,MAAM,CAAC,IAAI,CACT,GAAG,YAAY,iCAAiC,EAAE,0CAA0C,OAAO,IAAI,CACxG,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,iCAAiC,EAAE,uBAAuB,OAAO,IAAI,CAAC,CAAC;QAClG,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { BUNDLE_HOOK_EVENTS } from './bundleManifest.js';
2
+ import { isRecord } from './bundleFs.js';
3
+ /** Runtime guard for the bundle hook-event vocabulary. */
4
+ export function isBundleHookEvent(value) {
5
+ return typeof value === 'string' && BUNDLE_HOOK_EVENTS.includes(value);
6
+ }
7
+ /**
8
+ * Validate `raw` (the hook file's text) as a `HookDefinition` that subscribes
9
+ * to `event`. Returns either a definition ready for `runner.addHook` or an
10
+ * error naming the FILE and what is wrong — never both, never a partial one.
11
+ *
12
+ * `warnings` collects non-fatal observations (e.g. an event the bundle
13
+ * vocabulary does not know).
14
+ */
15
+ export function readHookDefinition(raw, file, event, warnings) {
16
+ let json;
17
+ try {
18
+ json = JSON.parse(raw);
19
+ }
20
+ catch (err) {
21
+ return { error: `${file}: invalid JSON (${err instanceof Error ? err.message : String(err)})` };
22
+ }
23
+ if (!isRecord(json))
24
+ return { error: `${file}: hook definition must be a JSON object` };
25
+ const name = json['name'];
26
+ if (typeof name !== 'string' || name.trim() === '') {
27
+ return { error: `${file}: hook definition is missing a non-empty "name"` };
28
+ }
29
+ const match = json['match'];
30
+ if (!isRecord(match)) {
31
+ return { error: `${file}: hook definition is missing a "match" object` };
32
+ }
33
+ const events = match['events'];
34
+ if (!Array.isArray(events) || events.length === 0) {
35
+ return { error: `${file}: hook "match.events" must be a non-empty array` };
36
+ }
37
+ if (!events.includes(event)) {
38
+ return {
39
+ error: `${file}: hook does not subscribe to '${event}' (match.events: ${events.join(', ')})`,
40
+ };
41
+ }
42
+ const hasCommand = typeof json['command'] === 'string' && json['command'].trim() !== '';
43
+ const hasUrl = typeof json['url'] === 'string' && json['url'].trim() !== '';
44
+ if (hasCommand === hasUrl) {
45
+ return { error: `${file}: exactly one of "command" or "url" is required` };
46
+ }
47
+ for (const extra of events) {
48
+ if (isBundleHookEvent(extra))
49
+ continue;
50
+ warnings.push(`${file}: hook also lists non-bundle event '${String(extra)}' (ignored by the bundle)`);
51
+ }
52
+ const tools = match['tools'];
53
+ const definition = {
54
+ name,
55
+ match: {
56
+ tools: Array.isArray(tools) ? tools.map(String) : ['*'],
57
+ events: events,
58
+ },
59
+ };
60
+ if (hasCommand)
61
+ definition.command = json['command'];
62
+ if (hasUrl)
63
+ definition.url = json['url'];
64
+ if (typeof json['timeoutMs'] === 'number')
65
+ definition.timeoutMs = json['timeoutMs'];
66
+ if (typeof json['cwd'] === 'string')
67
+ definition.cwd = json['cwd'];
68
+ return { definition };
69
+ }
70
+ //# sourceMappingURL=bundleHookDef.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundleHookDef.js","sourceRoot":"","sources":["../../../src/cli/plugins/bundleHookDef.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,180 @@
1
+ /**
2
+ * plugins/bundleLoad — read, validate and LOAD a WS6 plugin bundle.
3
+ *
4
+ * Three layers, deliberately separated:
5
+ *
6
+ * 1. {@link readBundle} — I/O + validation. Reads the manifest, then checks
7
+ * every DECLARED path: does it exist, does it stay inside the bundle
8
+ * directory, and does it parse as the format it claims? Skill files go
9
+ * through the REAL loader parser (`skillsMd.parseSkillMd`, the same one
10
+ * `/skill` uses), hooks through `bundleHookDef.ts`, and MCP presets through
11
+ * the REAL preset registry (`bundleMcp.ts`) — nothing is re-implemented.
12
+ * 2. {@link projectBundleContributions} — PURE. Given a bundle and the enable
13
+ * state it returns the contribution list, and executes NOTHING.
14
+ * 3. {@link loadBundle} — 1 + 2, the function callers actually want.
15
+ *
16
+ * Fail-closed, matching the rest of the CLI: a bundle that is present but not
17
+ * listed as enabled in `.zelari/plugins.json` contributes NOTHING (not just
18
+ * "nothing dangerous" — nothing at all). A bundle that fails to read produces
19
+ * no contributions, never a partial set.
20
+ *
21
+ * Module map: manifest schema → `bundleManifest.ts`, FS guards → `bundleFs.ts`,
22
+ * hook-file validator → `bundleHookDef.ts`, MCP resolution → `bundleMcp.ts`,
23
+ * discovery → `bundleDiscover.ts`, enable state → `bundleState.ts`, CLI →
24
+ * `bundleCommand.ts`.
25
+ *
26
+ * @since v2.58.0 (WS6 / plugin bundle v1)
27
+ */
28
+ import { readFile } from 'node:fs/promises';
29
+ import path from 'node:path';
30
+ import { isObserverEvent } from '@zelari/core/harness';
31
+ import { parseSkillMd } from '../skillsMd.js';
32
+ import { BUNDLE_MANIFEST_FILE, parseBundleManifest, } from './bundleManifest.js';
33
+ import { isDir, resolveInsideBundle } from './bundleFs.js';
34
+ import { readHookDefinition } from './bundleHookDef.js';
35
+ import { readMcp } from './bundleMcp.js';
36
+ /** Read the declared skills, each through the real SKILL.md parser. */
37
+ async function readSkills(manifestSkills, root, manifestPath, errors) {
38
+ const skills = [];
39
+ for (let i = 0; i < manifestSkills.length; i += 1) {
40
+ const ref = manifestSkills[i];
41
+ const abs = await resolveInsideBundle(root, ref.path, `skills.${i}.path`, manifestPath, errors);
42
+ if (!abs)
43
+ continue;
44
+ const skill = parseSkillMd(await readFile(abs, 'utf8'), abs);
45
+ if (!skill) {
46
+ errors.push(`${manifestPath}: invalid bundle manifest at 'skills.${i}.path': '${ref.path}' is not a valid SKILL.md ` +
47
+ '(needs YAML frontmatter with name + description and a non-empty body)');
48
+ continue;
49
+ }
50
+ if (skill.name !== ref.id) {
51
+ errors.push(`${manifestPath}: invalid bundle manifest at 'skills.${i}.id': id '${ref.id}' ` +
52
+ `does not match the SKILL.md name '${skill.name}' in '${ref.path}'`);
53
+ continue;
54
+ }
55
+ skills.push({ id: ref.id, path: abs, skill });
56
+ }
57
+ return skills;
58
+ }
59
+ /** Read the declared hooks, each validated against the event it claims. */
60
+ async function readHooks(manifestHooks, root, manifestPath, errors, warnings) {
61
+ const hooks = [];
62
+ for (let i = 0; i < manifestHooks.length; i += 1) {
63
+ const ref = manifestHooks[i];
64
+ const abs = await resolveInsideBundle(root, ref.path, `hooks.${i}.path`, manifestPath, errors);
65
+ if (!abs)
66
+ continue;
67
+ const result = readHookDefinition(await readFile(abs, 'utf8'), abs, ref.event, warnings);
68
+ if (!result.definition) {
69
+ errors.push(`${manifestPath}: invalid bundle manifest at 'hooks.${i}.path': ${result.error ?? 'invalid hook'}`);
70
+ continue;
71
+ }
72
+ // `hooks[].config` may only override timeoutMs/cwd — the hook FILE owns
73
+ // command, url, match and name (see the schema).
74
+ hooks.push({
75
+ event: ref.event,
76
+ path: abs,
77
+ observer: isObserverEvent(ref.event),
78
+ definition: { ...result.definition, ...(ref.config ?? {}) },
79
+ });
80
+ }
81
+ return hooks;
82
+ }
83
+ /**
84
+ * Read + validate one bundle directory. Never throws: every problem is an
85
+ * entry in `errors` naming the file and the field.
86
+ */
87
+ export async function readBundle(dir, manifestFileName = BUNDLE_MANIFEST_FILE) {
88
+ const errors = [];
89
+ const warnings = [];
90
+ const root = path.resolve(dir);
91
+ const manifestPath = path.join(root, manifestFileName);
92
+ if (!(await isDir(root))) {
93
+ return { errors: [`${root}: not a bundle directory`], warnings };
94
+ }
95
+ let manifestRaw;
96
+ try {
97
+ manifestRaw = await readFile(manifestPath, 'utf8');
98
+ }
99
+ catch {
100
+ return { errors: [`${manifestPath}: bundle manifest not found`], warnings };
101
+ }
102
+ const parsed = parseBundleManifest(manifestRaw, manifestPath);
103
+ if (!parsed.manifest)
104
+ return { errors: parsed.errors, warnings };
105
+ const manifest = parsed.manifest;
106
+ const skills = await readSkills(manifest.skills, root, manifestPath, errors);
107
+ const hooks = await readHooks(manifest.hooks, root, manifestPath, errors, warnings);
108
+ const mcp = readMcp(manifest.mcp, manifestPath, errors, warnings);
109
+ const agents = [];
110
+ for (let i = 0; i < manifest.agents.length; i += 1) {
111
+ const ref = manifest.agents[i];
112
+ const at = `agents.${i}.path`;
113
+ const abs = await resolveInsideBundle(root, ref.path, at, manifestPath, errors);
114
+ if (!abs)
115
+ continue;
116
+ if ((await readFile(abs, 'utf8')).trim() === '') {
117
+ errors.push(`${manifestPath}: invalid bundle manifest at '${at}': '${ref.path}' is empty`);
118
+ continue;
119
+ }
120
+ agents.push({ id: ref.id, path: abs, ...(ref.description ? { description: ref.description } : {}) });
121
+ }
122
+ if (agents.length > 0) {
123
+ warnings.push('agents are declared and validated but NOT executed (no agent loader in this release)');
124
+ }
125
+ if (errors.length > 0)
126
+ return { errors, warnings };
127
+ return {
128
+ bundle: {
129
+ name: manifest.name,
130
+ version: manifest.version,
131
+ ...(manifest.description ? { description: manifest.description } : {}),
132
+ dir: root,
133
+ manifestPath,
134
+ skills,
135
+ hooks,
136
+ mcp,
137
+ agents,
138
+ warnings,
139
+ },
140
+ errors,
141
+ warnings,
142
+ };
143
+ }
144
+ /**
145
+ * PURE projection: bundle + enable state → contribution list. Executes nothing
146
+ * and reads nothing. A disabled bundle contributes NOTHING (fail-closed), which
147
+ * is the seam a future apply/eval step consumes.
148
+ */
149
+ export function projectBundleContributions(bundle, opts) {
150
+ const base = {
151
+ bundle: bundle.name,
152
+ version: bundle.version,
153
+ dir: bundle.dir,
154
+ warnings: bundle.warnings,
155
+ };
156
+ if (!opts.enabled) {
157
+ return { ...base, enabled: false, skills: [], hooks: [], mcp: [], agents: [] };
158
+ }
159
+ return {
160
+ ...base,
161
+ enabled: true,
162
+ skills: bundle.skills,
163
+ hooks: bundle.hooks,
164
+ mcp: bundle.mcp,
165
+ agents: bundle.agents,
166
+ };
167
+ }
168
+ /** Read + validate + project. `ok:false` ⇒ no contributions, never partial. */
169
+ export async function loadBundle(dir, opts) {
170
+ const read = await readBundle(dir);
171
+ if (!read.bundle)
172
+ return { ok: false, errors: read.errors, warnings: read.warnings };
173
+ return {
174
+ ok: true,
175
+ errors: [],
176
+ warnings: read.warnings,
177
+ contributions: projectBundleContributions(read.bundle, { enabled: opts.enabled }),
178
+ };
179
+ }
180
+ //# sourceMappingURL=bundleLoad.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundleLoad.js","sourceRoot":"","sources":["../../../src/cli/plugins/bundleLoad.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,233 @@
1
+ /**
2
+ * plugins/bundleManifest — the WS6 plugin BUNDLE manifest, format v1.
3
+ *
4
+ * A *bundle* is a DIRECTORY holding one manifest (`zelari-plugin.json`) plus
5
+ * the files it declares:
6
+ *
7
+ * ```json
8
+ * {
9
+ * "$comment": "optional documentation slot — accepted and stripped",
10
+ * "formatVersion": 1,
11
+ * "name": "my-bundle",
12
+ * "version": "1.0.0",
13
+ * "description": "what it gives you",
14
+ * "skills": [{ "id": "repo-hygiene", "path": "skills/repo-hygiene/SKILL.md" }],
15
+ * "hooks": [{ "event": "PermissionRequest", "path": "hooks/observer.json" }],
16
+ * "mcp": [{ "name": "my-server", "preset": "unreal-mcp" }],
17
+ * "agents": [{ "id": "auditor", "path": "agents/auditor.md" }]
18
+ * }
19
+ * ```
20
+ *
21
+ * This module is PURE: it parses and validates TEXT. It never touches the
22
+ * filesystem (does a declared `path` EXIST? — see bundleLoad.ts) and never
23
+ * executes anything. Every schema is `.strict()`: an unknown key is a
24
+ * MALFORMED manifest, not a silently ignored option — the `$comment`
25
+ * documentation slot is the one exception, ACCEPTED AND STRIPPED exactly as in
26
+ * `safety/permissionPolicy.ts`, so a comment can never change a decision.
27
+ *
28
+ * Errors always NAME the file and the offending field
29
+ * (`<file>: invalid bundle manifest at 'skills.0.path': …`), and ALL problems
30
+ * are reported — a validator that stops at the first typo turns fixing a
31
+ * manifest into a guessing game.
32
+ *
33
+ * @since v2.58.0 (WS6 / plugin bundle v1)
34
+ */
35
+ import path from 'node:path';
36
+ import { z } from 'zod';
37
+ /** Manifest file name expected inside a bundle directory. */
38
+ export const BUNDLE_MANIFEST_FILE = 'zelari-plugin.json';
39
+ /** The only format this CLI understands. A future v2 must not load as v1. */
40
+ export const BUNDLE_FORMAT_VERSION = 1;
41
+ /** Slug rule for bundle name / skill id / agent id (mirrors skillsMd.ts). */
42
+ export const BUNDLE_SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
43
+ /** MCP server-name rule — mirrors the one `upsertMcpServer` enforces. */
44
+ export const BUNDLE_MCP_NAME_RE = /^[a-zA-Z0-9_-]+$/;
45
+ /**
46
+ * Every event a bundle hook may declare: the four v1.32 GATE events plus the
47
+ * four v2.57 OBSERVER events (WS5). `satisfies readonly AnyHookEvent[]` makes
48
+ * the compiler reject a typo or a renamed event here; `OBSERVER_HOOK_EVENTS`
49
+ * has no runtime counterpart for the gate half, so
50
+ * `tests/unit/plugin-bundle.test.ts` additionally asserts this list still
51
+ * contains every event core exports as an observer.
52
+ */
53
+ export const BUNDLE_HOOK_EVENTS = [
54
+ 'PreToolUse',
55
+ 'PostToolUse',
56
+ 'SessionStart',
57
+ 'SessionEnd',
58
+ 'PermissionRequest',
59
+ 'SubagentStart',
60
+ 'SubagentEnd',
61
+ 'Notification',
62
+ ];
63
+ /**
64
+ * `$comment` — the ONE non-config key these schemas tolerate. ACCEPTED AND
65
+ * STRIPPED: never validated, never reaching any consumer, so documentation can
66
+ * never change behaviour. Every other unknown key stays fatal (same contract
67
+ * as the WS1 permission policy).
68
+ */
69
+ const commentSchema = z.string().optional();
70
+ /** Shallow copy of a validated object without its documentation slot. */
71
+ function stripComment(value) {
72
+ const { $comment: _comment, ...rest } = value;
73
+ return rest;
74
+ }
75
+ /**
76
+ * A declared path must stay INSIDE the bundle directory: relative, no drive
77
+ * letter / root, no `..` segment. `path.win32.isAbsolute` is checked too, so a
78
+ * `C:\…` path is rejected even when the validator itself runs on POSIX.
79
+ */
80
+ function escapesBundleRoot(p) {
81
+ if (path.isAbsolute(p) || path.win32.isAbsolute(p))
82
+ return true;
83
+ return p.split(/[\\/]+/).includes('..');
84
+ }
85
+ const bundleRelativePath = z
86
+ .string()
87
+ .min(1)
88
+ .refine((p) => !escapesBundleRoot(p), {
89
+ message: 'must be a path relative to the bundle directory (no absolute path, no "..")',
90
+ });
91
+ const bundleSlug = (what) => z.string().regex(BUNDLE_SLUG_RE, `${what} must be a slug: lowercase letters, digits, hyphens (max 64)`);
92
+ /** `skills: [{ id, path }]` — one SKILL.md the bundle ships. */
93
+ export const BundleSkillRefSchema = z
94
+ .object({
95
+ $comment: commentSchema,
96
+ id: bundleSlug('skill id'),
97
+ path: bundleRelativePath,
98
+ })
99
+ .strict()
100
+ .transform(stripComment);
101
+ /**
102
+ * `hooks: [{ event, path, config? }]` — one hook definition FILE plus an
103
+ * optional overlay.
104
+ *
105
+ * `config` may set ONLY `timeoutMs` / `cwd`: the hook file stays authoritative
106
+ * for `command` / `url` / `match` / `name`, so a manifest can never smuggle an
107
+ * executable that the hook file does not already declare.
108
+ */
109
+ export const BundleHookRefSchema = z
110
+ .object({
111
+ $comment: commentSchema,
112
+ event: z.enum(BUNDLE_HOOK_EVENTS, {
113
+ message: `unknown hook event (expected one of: ${BUNDLE_HOOK_EVENTS.join(', ')})`,
114
+ }),
115
+ path: bundleRelativePath,
116
+ config: z
117
+ .object({
118
+ timeoutMs: z.number().int().positive().optional(),
119
+ cwd: z.string().min(1).optional(),
120
+ })
121
+ .strict()
122
+ .optional(),
123
+ })
124
+ .strict()
125
+ .transform(stripComment);
126
+ /**
127
+ * `mcp: [{ name, preset | command, args? }]` — exactly ONE of `preset`
128
+ * (a repo preset id, resolved by `mcp/mcpPresets.ts`) or `command` (a stdio
129
+ * server) must be given. No `env`: secrets never travel inside a bundle — a
130
+ * preset factory reads its key from the environment at APPLY time.
131
+ */
132
+ export const BundleMcpRefSchema = z
133
+ .object({
134
+ $comment: commentSchema,
135
+ name: z
136
+ .string()
137
+ .regex(BUNDLE_MCP_NAME_RE, 'mcp name must use letters, digits, underscore or hyphen'),
138
+ preset: z.string().min(1).optional(),
139
+ command: z.string().min(1).optional(),
140
+ args: z.array(z.string()).optional(),
141
+ })
142
+ .strict()
143
+ .transform(stripComment)
144
+ .refine((m) => (m.preset === undefined) !== (m.command === undefined), {
145
+ message: 'exactly one of "preset" (repo preset id) or "command" (stdio server) is required',
146
+ path: ['preset'],
147
+ });
148
+ /**
149
+ * `agents: [{ id, path, description? }]` — declared, validated, surfaced as a
150
+ * contribution, NOT executed: WS6 ships no agent loader (see the debt in
151
+ * docs/TOOLS.md).
152
+ */
153
+ export const BundleAgentRefSchema = z
154
+ .object({
155
+ $comment: commentSchema,
156
+ id: bundleSlug('agent id'),
157
+ path: bundleRelativePath,
158
+ description: z.string().min(1).optional(),
159
+ })
160
+ .strict()
161
+ .transform(stripComment);
162
+ /** The bundle manifest envelope (`.strict()`: unknown key ⇒ malformed). */
163
+ export const BundleManifestSchema = z
164
+ .object({
165
+ $comment: commentSchema,
166
+ formatVersion: z
167
+ .literal(BUNDLE_FORMAT_VERSION, {
168
+ message: `unsupported bundle format — this CLI reads formatVersion ${BUNDLE_FORMAT_VERSION}`,
169
+ })
170
+ .optional()
171
+ .default(BUNDLE_FORMAT_VERSION),
172
+ name: bundleSlug('bundle name'),
173
+ version: z.string().min(1),
174
+ description: z.string().min(1).optional(),
175
+ skills: z.array(BundleSkillRefSchema).default([]),
176
+ hooks: z.array(BundleHookRefSchema).default([]),
177
+ mcp: z.array(BundleMcpRefSchema).default([]),
178
+ agents: z.array(BundleAgentRefSchema).default([]),
179
+ })
180
+ .strict()
181
+ .transform(stripComment);
182
+ /** Format every zod issue as `<file>: invalid bundle manifest at '<field>': <why>`. */
183
+ function formatIssues(error, filePath) {
184
+ return error.issues.map((issue) => {
185
+ const at = issue.path.length > 0 ? issue.path.join('.') : 'file';
186
+ return `${filePath}: invalid bundle manifest at '${at}': ${issue.message}`;
187
+ });
188
+ }
189
+ /**
190
+ * Cross-field rules zod cannot express per-field: an ambiguous manifest
191
+ * (two entries fighting for the same id / server name) is REJECTED rather than
192
+ * silently resolved, so "which one won" can never depend on array order.
193
+ */
194
+ function duplicateErrors(manifest, filePath) {
195
+ const errors = [];
196
+ const check = (values, kind, field) => {
197
+ const seen = new Set();
198
+ values.forEach((value, i) => {
199
+ if (seen.has(value)) {
200
+ errors.push(`${filePath}: invalid bundle manifest at '${field}.${i}': duplicate ${kind} '${value}'`);
201
+ }
202
+ seen.add(value);
203
+ });
204
+ };
205
+ check(manifest.skills.map((s) => s.id), 'skill id', 'skills');
206
+ check(manifest.mcp.map((m) => m.name), 'mcp server name', 'mcp');
207
+ check(manifest.agents.map((a) => a.id), 'agent id', 'agents');
208
+ return errors;
209
+ }
210
+ /**
211
+ * Parse manifest TEXT (already read from disk). Invalid JSON, a schema
212
+ * violation, an unknown key (`$comment` aside), a declared path escaping the
213
+ * bundle root, or a duplicate id all come back as `errors` naming the file and
214
+ * the field; `manifest` is then absent — never a half-usable manifest.
215
+ */
216
+ export function parseBundleManifest(raw, filePath) {
217
+ let json;
218
+ try {
219
+ json = JSON.parse(raw);
220
+ }
221
+ catch (err) {
222
+ const detail = err instanceof Error ? err.message : String(err);
223
+ return { errors: [`${filePath}: invalid JSON (${detail})`] };
224
+ }
225
+ const parsed = BundleManifestSchema.safeParse(json);
226
+ if (!parsed.success)
227
+ return { errors: formatIssues(parsed.error, filePath) };
228
+ const errors = duplicateErrors(parsed.data, filePath);
229
+ if (errors.length > 0)
230
+ return { errors };
231
+ return { manifest: parsed.data, errors: [] };
232
+ }
233
+ //# sourceMappingURL=bundleManifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundleManifest.js","sourceRoot":"","sources":["../../../src/cli/plugins/bundleManifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { getMcpPreset, listMcpPresetIds } from '../mcp/mcpPresets.js';
2
+ export function readMcp(manifestMcp, manifestPath, errors, warnings) {
3
+ const mcp = [];
4
+ for (let i = 0; i < manifestMcp.length; i += 1) {
5
+ const ref = manifestMcp[i];
6
+ const at = `mcp.${i}`;
7
+ if (ref.preset !== undefined) {
8
+ const preset = getMcpPreset(ref.preset);
9
+ if (!preset) {
10
+ errors.push(`${manifestPath}: invalid bundle manifest at '${at}.preset': unknown MCP preset ` +
11
+ `'${ref.preset}' (known: ${listMcpPresetIds().join(', ')})`);
12
+ continue;
13
+ }
14
+ const names = Object.keys(preset.servers);
15
+ const template = names.length > 0 ? preset.servers[names[0]] : undefined;
16
+ if (!template) {
17
+ errors.push(`${manifestPath}: invalid bundle manifest at '${at}.preset': preset '${ref.preset}' has no server`);
18
+ continue;
19
+ }
20
+ if (names.length > 1) {
21
+ warnings.push(`preset '${ref.preset}' defines ${names.length} servers; using '${names[0]}'`);
22
+ }
23
+ mcp.push({
24
+ name: ref.name,
25
+ config: template,
26
+ source: 'preset',
27
+ presetId: ref.preset,
28
+ notes: preset.notes,
29
+ });
30
+ continue;
31
+ }
32
+ // `command` — the schema already proved exactly one of preset|command is set.
33
+ mcp.push({
34
+ name: ref.name,
35
+ config: { command: ref.command, args: ref.args, type: 'stdio', enabled: true },
36
+ source: 'command',
37
+ });
38
+ }
39
+ return mcp;
40
+ }
41
+ //# sourceMappingURL=bundleMcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundleMcp.js","sourceRoot":"","sources":["../../../src/cli/plugins/bundleMcp.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAqBtE,MAAM,UAAU,OAAO,CACrB,WAAyC,EACzC,YAAoB,EACpB,MAAgB,EAChB,QAAkB;IAElB,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;QACtB,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACxC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,CAAC,IAAI,CACT,GAAG,YAAY,iCAAiC,EAAE,+BAA+B;oBAC/E,IAAI,GAAG,CAAC,MAAM,aAAa,gBAAgB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC9D,CAAC;gBACF,SAAS;YACX,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC1E,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,CACT,GAAG,YAAY,iCAAiC,EAAE,qBAAqB,GAAG,CAAC,MAAM,iBAAiB,CACnG,CAAC;gBACF,SAAS;YACX,CAAC;YACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrB,QAAQ,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,MAAM,aAAa,KAAK,CAAC,MAAM,oBAAoB,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAC/F,CAAC;YACD,GAAG,CAAC,IAAI,CAAC;gBACP,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,MAAM,EAAE,QAAQ;gBAChB,MAAM,EAAE,QAAQ;gBAChB,QAAQ,EAAE,GAAG,CAAC,MAAM;gBACpB,KAAK,EAAE,MAAM,CAAC,KAAK;aACpB,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,8EAA8E;QAC9E,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,MAAM,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;YAC9E,MAAM,EAAE,SAAS;SAClB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}