zelari-code 0.6.2 → 0.7.8

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 (63) hide show
  1. package/dist/cli/app.js +64 -29
  2. package/dist/cli/app.js.map +1 -1
  3. package/dist/cli/components/ChatStream.js +40 -126
  4. package/dist/cli/components/ChatStream.js.map +1 -1
  5. package/dist/cli/components/LiveRegion.js +54 -0
  6. package/dist/cli/components/LiveRegion.js.map +1 -0
  7. package/dist/cli/components/SplashScreen.js +158 -0
  8. package/dist/cli/components/SplashScreen.js.map +1 -0
  9. package/dist/cli/components/StatusBar.js +42 -0
  10. package/dist/cli/components/StatusBar.js.map +1 -0
  11. package/dist/cli/components/ToolOutput.js +68 -0
  12. package/dist/cli/components/ToolOutput.js.map +1 -0
  13. package/dist/cli/components/toolFormat.js +195 -0
  14. package/dist/cli/components/toolFormat.js.map +1 -0
  15. package/dist/cli/councilDispatcher.js +20 -0
  16. package/dist/cli/councilDispatcher.js.map +1 -1
  17. package/dist/cli/grokOAuth.js +93 -75
  18. package/dist/cli/grokOAuth.js.map +1 -1
  19. package/dist/cli/hooks/chatState.js +139 -0
  20. package/dist/cli/hooks/chatState.js.map +1 -0
  21. package/dist/cli/hooks/eventsToMessages.js +1 -1
  22. package/dist/cli/hooks/eventsToMessages.js.map +1 -1
  23. package/dist/cli/hooks/messageHelpers.js +11 -4
  24. package/dist/cli/hooks/messageHelpers.js.map +1 -1
  25. package/dist/cli/hooks/useBatchedMessages.js +14 -10
  26. package/dist/cli/hooks/useBatchedMessages.js.map +1 -1
  27. package/dist/cli/hooks/useChatTurn.js +400 -87
  28. package/dist/cli/hooks/useChatTurn.js.map +1 -1
  29. package/dist/cli/hooks/useSession.js +23 -11
  30. package/dist/cli/hooks/useSession.js.map +1 -1
  31. package/dist/cli/hooks/useSlashDispatch.js +9 -1
  32. package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
  33. package/dist/cli/main.bundled.js +15468 -12961
  34. package/dist/cli/main.bundled.js.map +4 -4
  35. package/dist/cli/main.js +43 -2
  36. package/dist/cli/main.js.map +1 -1
  37. package/dist/cli/mcp/mcpClient.js +152 -0
  38. package/dist/cli/mcp/mcpClient.js.map +1 -0
  39. package/dist/cli/mcp/mcpManager.js +139 -0
  40. package/dist/cli/mcp/mcpManager.js.map +1 -0
  41. package/dist/cli/refreshRegistry.js +9 -6
  42. package/dist/cli/refreshRegistry.js.map +1 -1
  43. package/dist/cli/skillsMd.js +155 -0
  44. package/dist/cli/skillsMd.js.map +1 -0
  45. package/dist/cli/slashHandlers/skills.js +16 -0
  46. package/dist/cli/slashHandlers/skills.js.map +1 -1
  47. package/dist/cli/toolRegistry.js +31 -0
  48. package/dist/cli/toolRegistry.js.map +1 -1
  49. package/dist/cli/wizard/index.js +1 -1
  50. package/dist/cli/wizard/index.js.map +1 -1
  51. package/dist/cli/workspace/completeDesign.js +128 -0
  52. package/dist/cli/workspace/completeDesign.js.map +1 -0
  53. package/dist/cli/workspace/paths.js +20 -13
  54. package/dist/cli/workspace/paths.js.map +1 -1
  55. package/dist/cli/workspace/postCouncilHook.js +146 -20
  56. package/dist/cli/workspace/postCouncilHook.js.map +1 -1
  57. package/dist/cli/workspace/storage.js +7 -3
  58. package/dist/cli/workspace/storage.js.map +1 -1
  59. package/dist/cli/workspace/stubs.js +350 -136
  60. package/dist/cli/workspace/stubs.js.map +1 -1
  61. package/dist/cli/workspace/workspaceSummary.js +255 -0
  62. package/dist/cli/workspace/workspaceSummary.js.map +1 -0
  63. package/package.json +2 -2
package/dist/cli/main.js CHANGED
@@ -7,13 +7,16 @@ import React from 'react';
7
7
  import { render } from 'ink';
8
8
  // @ts-ignore
9
9
  import { App } from './app.js';
10
+ import { SplashGate } from './components/SplashScreen.js';
10
11
  import { getMetricsLogger } from './metrics.js';
11
12
  import { getProviderConfigPath } from './providerConfig.js';
12
13
  import { parseWizardFlags, shouldRunWizard, } from './wizard/firstRun.js';
13
14
  import { RunWizard } from './wizard/runWizard.js';
14
15
  import { parseHeadlessFlags } from './headless.js';
15
16
  import { runHeadless } from './runHeadless.js';
16
- export const VERSION = '0.6.2';
17
+ import { loadSkillMdSkills } from './skillsMd.js';
18
+ import { listCodingSkills } from '@zelari/core/skills';
19
+ export const VERSION = '0.7.8';
17
20
  /**
18
21
  * Silent background update check (Task N.6, v3-N).
19
22
  *
@@ -55,6 +58,14 @@ async function shutdown() {
55
58
  catch {
56
59
  // Best-effort — never block shutdown on a metrics write error.
57
60
  }
61
+ try {
62
+ // v0.7.5: kill spawned MCP server processes so they don't outlive the CLI.
63
+ const { closeMcpClients } = await import('./mcp/mcpManager.js');
64
+ closeMcpClients();
65
+ }
66
+ catch {
67
+ // Best-effort.
68
+ }
58
69
  process.exit(0);
59
70
  }
60
71
  /**
@@ -124,12 +135,42 @@ function pickRootComponent() {
124
135
  console.error(`[zelari-code] starting wizard: ${decision.reason}`);
125
136
  return { kind: 'wizard', element: React.createElement(RunWizard) };
126
137
  }
127
- return { kind: 'app', element: React.createElement(App) };
138
+ // v0.7.8: one-shot startup splash (ASCII emblem, ~2s or any-key skip),
139
+ // then the App mounts. Skipped automatically for non-TTY stdout, small
140
+ // terminals, or ZELARI_NO_SPLASH=1 — see components/SplashScreen.tsx.
141
+ return {
142
+ kind: 'app',
143
+ element: React.createElement(SplashGate, { version: VERSION }, React.createElement(App)),
144
+ };
145
+ }
146
+ /**
147
+ * v0.7.5: load user SKILL.md skills (opencode/Hermes/Claude-compatible
148
+ * format) into the coding-skill catalog BEFORE the App mounts, so
149
+ * `/skill` autocomplete and dispatch see them. Best-effort: a broken
150
+ * SKILL.md is skipped with a one-line stderr note, never a crash.
151
+ */
152
+ function loadUserSkills() {
153
+ try {
154
+ const existing = new Set(listCodingSkills().map((s) => s.id));
155
+ const summary = loadSkillMdSkills(process.cwd(), { existingIds: existing });
156
+ if (summary.loaded.length > 0) {
157
+ // eslint-disable-next-line no-console
158
+ console.error(`[zelari-code] loaded ${summary.loaded.length} SKILL.md skill(s): ${summary.loaded.join(', ')}`);
159
+ }
160
+ for (const s of summary.skipped) {
161
+ // eslint-disable-next-line no-console
162
+ console.error(`[zelari-code] skipped SKILL.md at ${s.path}: ${s.reason}`);
163
+ }
164
+ }
165
+ catch {
166
+ // Skill loading is an enhancement — the CLI must start without it.
167
+ }
128
168
  }
129
169
  function main() {
130
170
  const picked = pickRootComponent();
131
171
  if (picked.kind === 'done')
132
172
  return; // --version or --help printed + exited
173
+ loadUserSkills();
133
174
  if (picked.kind === 'headless') {
134
175
  void runHeadless(picked.headlessOpts).then((code) => {
135
176
  void getMetricsLogger().flush().catch(() => { });
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * mcpClient — minimal Model Context Protocol client over stdio (v0.7.5).
3
+ *
4
+ * Implements exactly the slice of MCP the CLI needs to consume external
5
+ * tool servers: `initialize` handshake, `tools/list` discovery, and
6
+ * `tools/call` execution, speaking newline-delimited JSON-RPC 2.0 over a
7
+ * child process's stdin/stdout (the MCP stdio transport).
8
+ *
9
+ * No SDK dependency on purpose: the protocol slice is ~150 lines and the
10
+ * official SDK would be the CLI's heaviest dependency by far.
11
+ *
12
+ * @see https://modelcontextprotocol.io/specification (stdio transport)
13
+ */
14
+ import { spawn } from 'node:child_process';
15
+ const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
16
+ const INIT_TIMEOUT_MS = 15_000;
17
+ export const MCP_PROTOCOL_VERSION = '2025-03-26';
18
+ export class McpClient {
19
+ serverName;
20
+ config;
21
+ child = null;
22
+ nextId = 1;
23
+ pending = new Map();
24
+ stdoutBuffer = '';
25
+ closed = false;
26
+ constructor(serverName, config) {
27
+ this.serverName = serverName;
28
+ this.config = config;
29
+ }
30
+ /** Spawn the server process and run the MCP initialize handshake. */
31
+ async start() {
32
+ if (this.child)
33
+ return;
34
+ const child = spawn(this.config.command, this.config.args ?? [], {
35
+ stdio: ['pipe', 'pipe', 'pipe'],
36
+ env: { ...process.env, ...(this.config.env ?? {}) },
37
+ // On Windows `npx`/`uvx` resolve to .cmd shims which plain spawn
38
+ // cannot execute; shell:true lets cmd.exe resolve them.
39
+ shell: process.platform === 'win32',
40
+ windowsHide: true,
41
+ });
42
+ this.child = child;
43
+ child.stdout.setEncoding('utf8');
44
+ child.stdout.on('data', (chunk) => this.onStdout(chunk));
45
+ child.on('error', (err) => this.failAll(new Error(`[mcp:${this.serverName}] spawn failed: ${err.message}`)));
46
+ child.on('exit', (code) => {
47
+ if (!this.closed) {
48
+ this.failAll(new Error(`[mcp:${this.serverName}] server exited (code ${code ?? 'null'})`));
49
+ }
50
+ });
51
+ await this.request('initialize', {
52
+ protocolVersion: MCP_PROTOCOL_VERSION,
53
+ capabilities: {},
54
+ clientInfo: { name: 'zelari-code', version: '0.7.8' },
55
+ }, INIT_TIMEOUT_MS);
56
+ this.notify('notifications/initialized', {});
57
+ }
58
+ /** Discover the server's tools. */
59
+ async listTools() {
60
+ const res = (await this.request('tools/list', {}));
61
+ return (res.tools ?? [])
62
+ .filter((t) => !!t.name)
63
+ .map((t) => ({
64
+ name: t.name,
65
+ description: t.description ?? '',
66
+ inputSchema: t.inputSchema ?? { type: 'object', properties: {} },
67
+ }));
68
+ }
69
+ /**
70
+ * Call a tool. Returns the concatenated text content; non-text content
71
+ * items are summarized by type. Throws when the server flags isError.
72
+ */
73
+ async callTool(name, args, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) {
74
+ const res = (await this.request('tools/call', { name, arguments: args }, timeoutMs));
75
+ const text = (res.content ?? [])
76
+ .map((c) => (c.type === 'text' && typeof c.text === 'string' ? c.text : `[${c.type ?? 'unknown'} content]`))
77
+ .join('\n');
78
+ if (res.isError)
79
+ throw new Error(text || `tool "${name}" reported an error`);
80
+ return text;
81
+ }
82
+ /** Terminate the server process and reject all in-flight requests. */
83
+ close() {
84
+ this.closed = true;
85
+ this.failAll(new Error(`[mcp:${this.serverName}] client closed`));
86
+ this.child?.kill();
87
+ this.child = null;
88
+ }
89
+ // ── JSON-RPC plumbing ────────────────────────────────────────────────
90
+ request(method, params, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) {
91
+ const child = this.child;
92
+ if (!child)
93
+ return Promise.reject(new Error(`[mcp:${this.serverName}] not started`));
94
+ const id = this.nextId++;
95
+ const payload = JSON.stringify({ jsonrpc: '2.0', id, method, params });
96
+ return new Promise((resolve, reject) => {
97
+ const timer = setTimeout(() => {
98
+ this.pending.delete(id);
99
+ reject(new Error(`[mcp:${this.serverName}] ${method} timed out after ${timeoutMs}ms`));
100
+ }, timeoutMs);
101
+ this.pending.set(id, { resolve, reject, timer });
102
+ child.stdin.write(payload + '\n', (err) => {
103
+ if (err) {
104
+ clearTimeout(timer);
105
+ this.pending.delete(id);
106
+ reject(err);
107
+ }
108
+ });
109
+ });
110
+ }
111
+ notify(method, params) {
112
+ this.child?.stdin.write(JSON.stringify({ jsonrpc: '2.0', method, params }) + '\n');
113
+ }
114
+ onStdout(chunk) {
115
+ this.stdoutBuffer += chunk;
116
+ let nl;
117
+ while ((nl = this.stdoutBuffer.indexOf('\n')) >= 0) {
118
+ const line = this.stdoutBuffer.slice(0, nl).trim();
119
+ this.stdoutBuffer = this.stdoutBuffer.slice(nl + 1);
120
+ if (!line)
121
+ continue;
122
+ let msg;
123
+ try {
124
+ msg = JSON.parse(line);
125
+ }
126
+ catch {
127
+ continue; // servers sometimes log garbage to stdout — ignore non-JSON
128
+ }
129
+ if (typeof msg.id !== 'number')
130
+ continue; // notification from server — none handled yet
131
+ const pending = this.pending.get(msg.id);
132
+ if (!pending)
133
+ continue;
134
+ this.pending.delete(msg.id);
135
+ clearTimeout(pending.timer);
136
+ if (msg.error) {
137
+ pending.reject(new Error(`[mcp:${this.serverName}] ${msg.error.message ?? 'JSON-RPC error'} (code ${msg.error.code ?? '?'})`));
138
+ }
139
+ else {
140
+ pending.resolve(msg.result);
141
+ }
142
+ }
143
+ }
144
+ failAll(err) {
145
+ for (const [, p] of this.pending) {
146
+ clearTimeout(p.timer);
147
+ p.reject(err);
148
+ }
149
+ this.pending.clear();
150
+ }
151
+ }
152
+ //# sourceMappingURL=mcpClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpClient.js","sourceRoot":"","sources":["../../../src/cli/mcp/mcpClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * mcpManager — MCP server lifecycle + tool registration (v0.7.5).
3
+ *
4
+ * Reads MCP server definitions from (project wins on name conflicts):
5
+ * 1. <project>/.zelari/mcp.json
6
+ * 2. ~/.zelari-code/mcp.json
7
+ *
8
+ * Config format is Claude-Desktop-compatible so existing configs can be
9
+ * copied verbatim:
10
+ * { "mcpServers": { "<name>": { "command": "npx", "args": [...], "env": {...} } } }
11
+ *
12
+ * Servers are started lazily ONCE per CLI process (module-level cache) and
13
+ * their tools registered into each turn's ToolRegistry as
14
+ * `mcp_<server>_<tool>`. A server that fails to start is skipped and not
15
+ * retried for the rest of the session (a broken server must not add a
16
+ * spawn-timeout to every prompt).
17
+ *
18
+ * Kill switch: ZELARI_MCP=0 disables everything.
19
+ */
20
+ import { existsSync, readFileSync } from 'node:fs';
21
+ import { join } from 'node:path';
22
+ import { homedir } from 'node:os';
23
+ import { z } from 'zod';
24
+ import { typedOk, typedErr } from '@zelari/core/harness/tools/toolTypes';
25
+ import { McpClient } from './mcpClient.js';
26
+ /** Module-level session cache. */
27
+ const state = { loaded: false, tools: [], warnings: [], clients: [] };
28
+ /** Read + merge MCP config files (project entries win on name conflict). */
29
+ export function readMcpConfig(projectRoot = process.cwd()) {
30
+ const merged = {};
31
+ const paths = [
32
+ join(homedir(), '.zelari-code', 'mcp.json'),
33
+ join(projectRoot, '.zelari', 'mcp.json'), // later = higher precedence
34
+ ];
35
+ for (const p of paths) {
36
+ if (!existsSync(p))
37
+ continue;
38
+ try {
39
+ const parsed = JSON.parse(readFileSync(p, 'utf8'));
40
+ for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
41
+ if (!cfg || typeof cfg.command !== 'string' || cfg.command.length === 0)
42
+ continue;
43
+ merged[name] = cfg;
44
+ }
45
+ }
46
+ catch {
47
+ // Malformed config file — skip it; the other source may still work.
48
+ }
49
+ }
50
+ return merged;
51
+ }
52
+ /** Start configured servers (once per process) and discover their tools. */
53
+ async function ensureLoaded(projectRoot) {
54
+ if (state.loaded)
55
+ return;
56
+ state.loaded = true; // set FIRST — a throwing server must not cause retry storms
57
+ const servers = readMcpConfig(projectRoot);
58
+ for (const [name, cfg] of Object.entries(servers)) {
59
+ if (cfg.enabled === false)
60
+ continue;
61
+ const client = new McpClient(name, cfg);
62
+ try {
63
+ await client.start();
64
+ const tools = await client.listTools();
65
+ state.clients.push(client);
66
+ for (const info of tools) {
67
+ state.tools.push({
68
+ registryName: sanitizeToolName(`mcp_${name}_${info.name}`),
69
+ serverName: name,
70
+ info,
71
+ client,
72
+ });
73
+ }
74
+ }
75
+ catch (err) {
76
+ client.close();
77
+ state.warnings.push(`[mcp:${name}] disabled: ${err instanceof Error ? err.message : String(err)}`);
78
+ }
79
+ }
80
+ }
81
+ /** OpenAI tool names must match ^[a-zA-Z0-9_-]{1,64}$. */
82
+ function sanitizeToolName(raw) {
83
+ return raw.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 64);
84
+ }
85
+ /**
86
+ * Register all discovered MCP tools into a ToolRegistry. Returns warnings
87
+ * collected during (first-time) discovery so the caller can surface them.
88
+ *
89
+ * Safe to call on every turn: discovery runs once, registration is an
90
+ * upsert into the fresh per-turn registry.
91
+ */
92
+ export async function registerMcpTools(registry, projectRoot = process.cwd()) {
93
+ if (process.env['ZELARI_MCP'] === '0')
94
+ return { registered: [], warnings: [] };
95
+ await ensureLoaded(projectRoot);
96
+ const registered = [];
97
+ for (const t of state.tools) {
98
+ registry.register({
99
+ name: t.registryName,
100
+ description: `[MCP:${t.serverName}] ${t.info.description}`.slice(0, 1024),
101
+ // The MCP server owns validation; its JSON Schema is forwarded to the
102
+ // provider via toOpenAITools, so the model sees the real parameter
103
+ // shape even though the local zod gate is permissive.
104
+ permissions: ['network'],
105
+ inputSchema: z.any(),
106
+ // Some registries introspect this for provider tool schemas.
107
+ jsonSchema: t.info.inputSchema,
108
+ execute: async (input) => {
109
+ try {
110
+ const args = (input ?? {});
111
+ const text = await t.client.callTool(t.info.name, args);
112
+ return typedOk(text);
113
+ }
114
+ catch (err) {
115
+ return typedErr(`[${t.registryName}] ${err instanceof Error ? err.message : String(err)}`);
116
+ }
117
+ },
118
+ });
119
+ registered.push(t.registryName);
120
+ }
121
+ // Drain warnings: they are surfaced to the chat ONCE (first turn after
122
+ // discovery), not repeated on every subsequent prompt.
123
+ const warnings = state.warnings.splice(0, state.warnings.length);
124
+ return { registered, warnings };
125
+ }
126
+ /** Shut down all MCP servers (process exit path). */
127
+ export function closeMcpClients() {
128
+ for (const c of state.clients)
129
+ c.close();
130
+ state.clients = [];
131
+ state.tools = [];
132
+ state.loaded = false;
133
+ state.warnings = [];
134
+ }
135
+ /** Test hook: reset the module cache. */
136
+ export function _resetMcpForTests() {
137
+ closeMcpClients();
138
+ }
139
+ //# sourceMappingURL=mcpManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpManager.js","sourceRoot":"","sources":["../../../src/cli/mcp/mcpManager.ts"],"names":[],"mappings":"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"}
@@ -23,7 +23,7 @@
23
23
  * @see electron/cli/keyStore.ts (RefreshImpl type, resolveApiKeyWithMeta)
24
24
  * @see docs/plans/2026-06-29-anathema-coder-v3-F.md
25
25
  */
26
- import { refreshGrokToken } from './grokOAuth.js';
26
+ import { refreshGrokToken, DEFAULT_GROK_OAUTH_CLIENT_ID } from './grokOAuth.js';
27
27
  const registry = new Map();
28
28
  /**
29
29
  * Register a refresh impl for a provider. Overwrites any previous impl.
@@ -56,13 +56,16 @@ export function clearRefreshRegistry() {
56
56
  /**
57
57
  * Adapter from `refreshGrokToken({clientId, refreshToken})` to the generic
58
58
  * `RefreshImpl(providerId, refreshToken)` shape. Reads GROK_OAUTH_CLIENT_ID
59
- * from env at call time so tests can override.
59
+ * from env at call time so tests can override, falling back to the same
60
+ * built-in public client id the `/login grok` OAuth flow uses — the refresh
61
+ * MUST use the client the token was issued to, and users who logged in with
62
+ * the default client have no env var set.
60
63
  */
61
64
  export const grokRefreshAdapter = async (_providerId, refreshToken) => {
62
- const clientId = process.env.GROK_OAUTH_CLIENT_ID;
63
- if (!clientId || clientId.trim().length === 0) {
64
- throw new Error('Missing GROK_OAUTH_CLIENT_ID env var');
65
- }
65
+ const envClientId = process.env.GROK_OAUTH_CLIENT_ID;
66
+ const clientId = envClientId && envClientId.trim().length > 0
67
+ ? envClientId
68
+ : DEFAULT_GROK_OAUTH_CLIENT_ID;
66
69
  return refreshGrokToken({ clientId, refreshToken });
67
70
  };
68
71
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"refreshRegistry.js","sourceRoot":"","sources":["../../src/cli/refreshRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAmBlD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAC;AAEtD;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAgB,EAAE,IAAwB;IAC5E,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACzB,CAAC;AACH,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,qBAAqB,CAAC,EAAgB;IACpD,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACtB,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,cAAc,CAAC,EAAgB;IAC7C,OAAO,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;AAClC,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,gBAAgB;IAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,oBAAoB;IAClC,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAgB,KAAK,EAClD,WAAW,EACX,YAAY,EACZ,EAAE;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IAClD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,gBAAgB,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC;AACtD,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,2BAA2B;IACzC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"refreshRegistry.js","sourceRoot":"","sources":["../../src/cli/refreshRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAmBhF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAC;AAEtD;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAgB,EAAE,IAAwB;IAC5E,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACzB,CAAC;AACH,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,qBAAqB,CAAC,EAAgB;IACpD,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACtB,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,cAAc,CAAC,EAAgB;IAC7C,OAAO,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;AAClC,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,gBAAgB;IAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,oBAAoB;IAClC,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAgB,KAAK,EAClD,WAAW,EACX,YAAY,EACZ,EAAE;IACF,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IACrD,MAAM,QAAQ,GAAG,WAAW,IAAI,WAAW,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QAC3D,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,4BAA4B,CAAC;IACjC,OAAO,gBAAgB,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC;AACtD,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,2BAA2B;IACzC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * skillsMd — load user-defined skills from SKILL.md files (v0.7.5).
3
+ *
4
+ * opencode, Hermes Agent, and Claude Code converge on the same on-disk
5
+ * format: one directory per skill containing a `SKILL.md` with YAML
6
+ * frontmatter (`name`, `description`) and a markdown body of instructions.
7
+ * Supporting that format means ANY skill from those ecosystems can be
8
+ * dropped into this project and used via `/skill <name>` unchanged.
9
+ *
10
+ * Discovery order (first occurrence of a name wins — project beats global):
11
+ * 1. <project>/.zelari/skills/<name>/SKILL.md (native)
12
+ * 2. <project>/.claude/skills/<name>/SKILL.md (Claude Code compat)
13
+ * 3. <project>/.opencode/skills/<name>/SKILL.md (opencode compat)
14
+ * 4. ~/.zelari-code/skills/<name>/SKILL.md (global)
15
+ *
16
+ * The body becomes the skill's systemPromptFragment; recognized frontmatter
17
+ * beyond name/description: `category` (one of the coding categories),
18
+ * `tools` (list or comma string → requiredTools), `cost` (low|medium|high).
19
+ * Unknown fields are ignored, never fatal — a malformed SKILL.md is skipped
20
+ * with a warning entry in the returned summary.
21
+ */
22
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
23
+ import { join } from 'node:path';
24
+ import { homedir } from 'node:os';
25
+ import { registerCodingSkill } from '@zelari/core/skills';
26
+ /** Valid coding categories (mirror of CODING_CATEGORY values in core). */
27
+ const CODING_CATEGORIES = new Set([
28
+ 'plan', 'refactor', 'debug', 'review', 'test', 'docs', 'ops', 'git', 'db', 'maint',
29
+ ]);
30
+ /** Skill discovery roots for a project (project-local first, then global). */
31
+ export function skillMdSearchDirs(projectRoot = process.cwd()) {
32
+ return [
33
+ join(projectRoot, '.zelari', 'skills'),
34
+ join(projectRoot, '.claude', 'skills'),
35
+ join(projectRoot, '.opencode', 'skills'),
36
+ join(homedir(), '.zelari-code', 'skills'),
37
+ ];
38
+ }
39
+ /**
40
+ * Parse a SKILL.md file: YAML frontmatter subset (flat `key: value` pairs
41
+ * and `[a, b]` inline lists — nested structures are ignored) + body.
42
+ * Returns null with no throw on files without usable name/description.
43
+ */
44
+ export function parseSkillMd(content, sourcePath) {
45
+ const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(content);
46
+ if (!fmMatch)
47
+ return null;
48
+ const [, fmRaw = '', body = ''] = fmMatch;
49
+ const fields = {};
50
+ for (const line of fmRaw.split(/\r?\n/)) {
51
+ const kv = /^([A-Za-z_][\w-]*)\s*:\s*(.*)$/.exec(line);
52
+ if (!kv)
53
+ continue; // nested/indented lines from unsupported YAML — skip
54
+ const key = (kv[1] ?? '').toLowerCase();
55
+ let value = (kv[2] ?? '').trim();
56
+ if ((value.startsWith('"') && value.endsWith('"')) ||
57
+ (value.startsWith("'") && value.endsWith("'"))) {
58
+ value = value.slice(1, -1);
59
+ }
60
+ fields[key] = value;
61
+ }
62
+ const name = (fields['name'] ?? '').trim().toLowerCase();
63
+ const description = (fields['description'] ?? '').trim();
64
+ if (!name || !description)
65
+ return null;
66
+ // Same constraint opencode enforces: lowercase alphanumerics + hyphens.
67
+ if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(name))
68
+ return null;
69
+ const rawTools = fields['tools'] ?? fields['requiredtools'] ?? fields['required_tools'] ?? '';
70
+ const requiredTools = rawTools
71
+ .replace(/^\[|\]$/g, '')
72
+ .split(',')
73
+ .map((t) => t.trim().replace(/^["']|["']$/g, ''))
74
+ .filter((t) => t.length > 0);
75
+ const rawCategory = (fields['category'] ?? '').trim().toLowerCase();
76
+ const category = CODING_CATEGORIES.has(rawCategory) ? rawCategory : 'maint';
77
+ const rawCost = (fields['cost'] ?? fields['estimatedcost'] ?? '').trim().toLowerCase();
78
+ const estimatedCost = rawCost === 'low' || rawCost === 'high' ? rawCost : 'medium';
79
+ const trimmedBody = body.trim();
80
+ if (!trimmedBody)
81
+ return null;
82
+ return { name, description, category, requiredTools, estimatedCost, body: trimmedBody, sourcePath };
83
+ }
84
+ /** Adapt a parsed SKILL.md into the strict CodingSkillDefinition shape. */
85
+ export function toCodingSkillDefinition(parsed) {
86
+ return {
87
+ id: parsed.name,
88
+ name: parsed.name,
89
+ description: parsed.description,
90
+ version: '1.0.0',
91
+ category: parsed.category,
92
+ systemPromptFragment: parsed.body,
93
+ requiredTools: parsed.requiredTools,
94
+ enabledByDefault: true,
95
+ builtin: false,
96
+ requires: [],
97
+ examples: [],
98
+ triggers: [],
99
+ antiPatterns: [],
100
+ requiredRoles: [],
101
+ estimatedCost: parsed.estimatedCost,
102
+ outputSchema: 'string',
103
+ relatedSkills: [],
104
+ tags: ['user', 'skill-md'],
105
+ };
106
+ }
107
+ /**
108
+ * Scan the search dirs, parse every `<dir>/<skill>/SKILL.md`, and register
109
+ * the results in the coding-skill catalog (upsert — but a name already
110
+ * loaded from an earlier dir in the search order is NOT overridden, and
111
+ * builtin skills are never shadowed).
112
+ *
113
+ * Best-effort: any I/O or parse error skips that one skill.
114
+ */
115
+ export function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
116
+ const summary = { loaded: [], skipped: [] };
117
+ const seen = new Set(options.existingIds ?? []);
118
+ for (const dir of skillMdSearchDirs(projectRoot)) {
119
+ if (!existsSync(dir))
120
+ continue;
121
+ let entries;
122
+ try {
123
+ entries = readdirSync(dir, { withFileTypes: true })
124
+ .filter((e) => e.isDirectory())
125
+ .map((e) => e.name);
126
+ }
127
+ catch {
128
+ continue;
129
+ }
130
+ for (const entry of entries) {
131
+ const skillPath = join(dir, entry, 'SKILL.md');
132
+ if (!existsSync(skillPath))
133
+ continue;
134
+ try {
135
+ const parsed = parseSkillMd(readFileSync(skillPath, 'utf8'), skillPath);
136
+ if (!parsed) {
137
+ summary.skipped.push({ path: skillPath, reason: 'missing/invalid frontmatter (name, description) or empty body' });
138
+ continue;
139
+ }
140
+ if (seen.has(parsed.name)) {
141
+ summary.skipped.push({ path: skillPath, reason: `name "${parsed.name}" already registered (earlier dir or builtin wins)` });
142
+ continue;
143
+ }
144
+ registerCodingSkill(toCodingSkillDefinition(parsed));
145
+ seen.add(parsed.name);
146
+ summary.loaded.push(parsed.name);
147
+ }
148
+ catch (err) {
149
+ summary.skipped.push({ path: skillPath, reason: err instanceof Error ? err.message : String(err) });
150
+ }
151
+ }
152
+ }
153
+ return summary;
154
+ }
155
+ //# sourceMappingURL=skillsMd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillsMd.js","sourceRoot":"","sources":["../../src/cli/skillsMd.ts"],"names":[],"mappings":"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"}
@@ -76,6 +76,22 @@ export async function handleSteer(ctx, text, usageHint) {
76
76
  });
77
77
  }
78
78
  export function handleClearChat(setMessages, setSessionActive) {
79
+ // v0.7.0 static-scrollback: <Static> can't retro-erase the lines it already
80
+ // printed to real stdout (they are in the terminal's native scrollback).
81
+ // Emit ANSI clear-screen + scrollback-wipe + cursor-home so the visible
82
+ // pane and the scrollback are cleared together. Windows Terminal honors all
83
+ // three; legacy conhost ignores `3J` (scrollback wipe) — acceptable
84
+ // degradation, the visible pane still clears.
85
+ //
86
+ // \x1b[2J — clear visible screen
87
+ // \x1b[3J — clear scrollback history
88
+ // \x1b[H — cursor home
89
+ try {
90
+ process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
91
+ }
92
+ catch {
93
+ // Best-effort — never block a /clear on a stdout write error.
94
+ }
79
95
  setMessages([]);
80
96
  setSessionActive(false);
81
97
  }