zelari-code 1.2.0 → 1.4.1

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 (53) hide show
  1. package/README.md +67 -3
  2. package/dist/cli/app.js +3 -1
  3. package/dist/cli/app.js.map +1 -1
  4. package/dist/cli/ast/engine.js +126 -0
  5. package/dist/cli/ast/engine.js.map +1 -0
  6. package/dist/cli/ast/tools.js +71 -0
  7. package/dist/cli/ast/tools.js.map +1 -0
  8. package/dist/cli/browser/driver.js +125 -0
  9. package/dist/cli/browser/driver.js.map +1 -0
  10. package/dist/cli/browser/tools.js +71 -0
  11. package/dist/cli/browser/tools.js.map +1 -0
  12. package/dist/cli/hooks/useSlashDispatch.js +33 -2
  13. package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
  14. package/dist/cli/lsp/client.js +89 -0
  15. package/dist/cli/lsp/client.js.map +1 -0
  16. package/dist/cli/lsp/manager.js +287 -0
  17. package/dist/cli/lsp/manager.js.map +1 -0
  18. package/dist/cli/lsp/protocol.js +83 -0
  19. package/dist/cli/lsp/protocol.js.map +1 -0
  20. package/dist/cli/lsp/servers.js +80 -0
  21. package/dist/cli/lsp/servers.js.map +1 -0
  22. package/dist/cli/lsp/tools.js +125 -0
  23. package/dist/cli/lsp/tools.js.map +1 -0
  24. package/dist/cli/main.bundled.js +1884 -281
  25. package/dist/cli/main.bundled.js.map +4 -4
  26. package/dist/cli/main.js +75 -3
  27. package/dist/cli/main.js.map +1 -1
  28. package/dist/cli/mode.js +32 -0
  29. package/dist/cli/mode.js.map +1 -0
  30. package/dist/cli/semantic/embeddings.js +71 -0
  31. package/dist/cli/semantic/embeddings.js.map +1 -0
  32. package/dist/cli/semantic/index.js +147 -0
  33. package/dist/cli/semantic/index.js.map +1 -0
  34. package/dist/cli/semantic/provider.js +29 -0
  35. package/dist/cli/semantic/provider.js.map +1 -0
  36. package/dist/cli/semantic/store.js +71 -0
  37. package/dist/cli/semantic/store.js.map +1 -0
  38. package/dist/cli/semantic/tools.js +54 -0
  39. package/dist/cli/semantic/tools.js.map +1 -0
  40. package/dist/cli/slashCommands.js +22 -1
  41. package/dist/cli/slashCommands.js.map +1 -1
  42. package/dist/cli/slashHandlers/semantic.js +37 -0
  43. package/dist/cli/slashHandlers/semantic.js.map +1 -0
  44. package/dist/cli/slashHandlers/updater.js +27 -3
  45. package/dist/cli/slashHandlers/updater.js.map +1 -1
  46. package/dist/cli/toolRegistry.js +50 -0
  47. package/dist/cli/toolRegistry.js.map +1 -1
  48. package/dist/cli/utils/doctor.js +23 -0
  49. package/dist/cli/utils/doctor.js.map +1 -1
  50. package/dist/cli/utils/prereqChecks.js +362 -0
  51. package/dist/cli/utils/prereqChecks.js.map +1 -0
  52. package/package.json +3 -3
  53. package/scripts/postinstall.mjs +46 -1
package/dist/cli/main.js CHANGED
@@ -36,6 +36,69 @@ export const VERSION = getCurrentVersion();
36
36
  * Disabled in dev mode (`ANATHEMA_DEV=1`) to avoid noise during local
37
37
  * development where the bundled version is the source repo.
38
38
  */
39
+ /**
40
+ * Boot-time prerequisite gate (v1.4.0).
41
+ *
42
+ * Probes node/git/bash THROUGH the agent's resolved shell — not the main
43
+ * process — because the agent runs `npm`/`tsc`/build scripts inside the
44
+ * resolved bash (Git Bash on Windows), which inherits a different PATH
45
+ * than this Node process. A user can have node visible to the main process
46
+ * yet invisible to the agent's bash; without this check zelari-code boots
47
+ * happily and the failure surfaces only mid-task (`node: not found`), which
48
+ * is exactly what blocked the Anathema-Studio council run on 2026-07-07.
49
+ *
50
+ * Severity:
51
+ * - node unreachable from agent shell → hard-fail (exit 1). Without node
52
+ * the agent cannot run npm/build/tsc — there is nothing useful it can do.
53
+ * - git / bash missing → warn to stderr, continue. Features degrade but
54
+ * the agent still works for non-git, non-POSIX tasks.
55
+ *
56
+ * Bypass: `ZELARI_SKIP_PREFLIGHT=1` or `--skip-checks`. Intended for CI,
57
+ * sandboxes, and emergency recovery — not normal use.
58
+ *
59
+ * Runs AFTER `pickRootComponent()` (so `--version`/`--help`/`--doctor` keep
60
+ * working on a broken install) but BEFORE skill loading and the TUI render.
61
+ * Never throws — `runPrereqChecks` already swallows per-check errors.
62
+ *
63
+ * @see src/cli/utils/prereqChecks.ts — the probe implementation.
64
+ */
65
+ function runPreflight() {
66
+ if (process.env.ZELARI_SKIP_PREFLIGHT === "1")
67
+ return;
68
+ if (process.argv.includes("--skip-checks"))
69
+ return;
70
+ if (process.env.ANATHEMA_DEV === "1")
71
+ return; // dev: avoid noise on a source checkout.
72
+ const { runPrereqChecks } =
73
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
74
+ require("./utils/prereqChecks.js");
75
+ const { results, hasCriticalFail, warnings } = runPrereqChecks({
76
+ mode: "preflight",
77
+ });
78
+ for (const w of warnings) {
79
+ // eslint-disable-next-line no-console
80
+ console.error(`\x1b[33m[zelari-code] ⚠ ${w.tool}: ${w.message}\x1b[0m`);
81
+ }
82
+ if (hasCriticalFail) {
83
+ const critical = results.find((r) => !r.ok && r.severity === "critical");
84
+ // eslint-disable-next-line no-console
85
+ console.error("");
86
+ // eslint-disable-next-line no-console
87
+ console.error("\x1b[31m" +
88
+ "==============================================================\n" +
89
+ " zelari-code cannot start: a critical prerequisite is missing.\n" +
90
+ "==============================================================\x1b[0m");
91
+ if (critical) {
92
+ // eslint-disable-next-line no-console
93
+ console.error(`\n ${critical.tool}: ${critical.message}`);
94
+ }
95
+ // eslint-disable-next-line no-console
96
+ console.error("\n Run `zelari-code --doctor` for the full diagnostic report.\n" +
97
+ " Bypass this check with ZELARI_SKIP_PREFLIGHT=1 (NOT recommended —\n" +
98
+ " the agent will still fail when it tries to run npm/build/tsc).");
99
+ process.exit(1);
100
+ }
101
+ }
39
102
  async function backgroundUpdateCheck() {
40
103
  if (process.env.ANATHEMA_DEV === "1")
41
104
  return;
@@ -115,7 +178,10 @@ function pickRootComponent() {
115
178
  "Options:\n" +
116
179
  " --version, -v Print version and exit\n" +
117
180
  " --help, -h Print this help and exit\n" +
118
- " --doctor Diagnose install health (shim, bundle, PATH, deps)\n" +
181
+ " --doctor Diagnose install health (shim, bundle, PATH, deps,\n" +
182
+ " node/git/bash in the agent shell)\n" +
183
+ " --skip-checks Skip the boot-time prerequisite check\n" +
184
+ " (alias for ZELARI_SKIP_PREFLIGHT=1)\n" +
119
185
  " --no-wizard Skip the first-run wizard\n" +
120
186
  " --reset-config Re-run the wizard (clears provider.json on commit)\n" +
121
187
  " --headless Run a single task without mounting the TUI\n" +
@@ -126,8 +192,9 @@ function pickRootComponent() {
126
192
  " --model <id> Model override (default: provider default)\n" +
127
193
  "\n" +
128
194
  "Environment:\n" +
129
- " ZELARI_NO_WIZARD=1 Skip the first-run wizard\n" +
130
- " ANATHEMA_DEV=1 Disable background update check\n");
195
+ " ZELARI_NO_WIZARD=1 Skip the first-run wizard\n" +
196
+ " ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n" +
197
+ " ANATHEMA_DEV=1 Disable background update check + preflight\n");
131
198
  process.exit(0);
132
199
  }
133
200
  // Headless mode: short-circuit TUI entirely. Must be checked BEFORE
@@ -189,6 +256,11 @@ function main() {
189
256
  const picked = pickRootComponent();
190
257
  if (picked.kind === "done")
191
258
  return; // --version or --help printed + exited
259
+ // v1.4.0: verify node/git/bash BEFORE mounting the TUI or running a headless
260
+ // task. Hard-fails on missing node (the agent cannot run npm/build without it),
261
+ // warns on missing git/bash. Skipped for --version/--help/--doctor (handled
262
+ // above) so a broken install can still be diagnosed. See `runPreflight`.
263
+ runPreflight();
192
264
  loadUserSkills();
193
265
  if (picked.kind === "headless") {
194
266
  void runHeadless(picked.headlessOpts).then((code) => {
@@ -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,32 @@
1
+ /**
2
+ * mode — the free-form dispatch mode (agent / council / zelari) shared by the
3
+ * shift+tab toggle and the `/mode` command.
4
+ *
5
+ * Kept as a tiny pure module so the cycle order is unit-testable and both the
6
+ * keyboard handler (app.tsx) and the slash command (useSlashDispatch) agree on
7
+ * exactly one source of truth.
8
+ */
9
+ /** Cycle order for shift+tab. */
10
+ export const MODES = ['agent', 'council', 'zelari'];
11
+ /** Next mode in the cycle (wraps agent → council → zelari → agent). */
12
+ export function nextMode(current) {
13
+ const i = MODES.indexOf(current);
14
+ return MODES[(i + 1) % MODES.length] ?? 'agent';
15
+ }
16
+ /** Parse a mode name (case-insensitive), or null if unrecognized. */
17
+ export function parseMode(input) {
18
+ const v = input.trim().toLowerCase();
19
+ return MODES.includes(v) ? v : null;
20
+ }
21
+ /** Short human description of what each mode does (for `/mode` feedback). */
22
+ export function describeMode(mode) {
23
+ switch (mode) {
24
+ case 'council':
25
+ return 'council — 6-member pipeline (Caronte…Lucifero)';
26
+ case 'zelari':
27
+ return 'zelari — autonomous multi-run mission';
28
+ default:
29
+ return 'agent — single LLM turn';
30
+ }
31
+ }
32
+ //# sourceMappingURL=mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.js","sourceRoot":"","sources":["../../src/cli/mode.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,iCAAiC;AACjC,MAAM,CAAC,MAAM,KAAK,GAAwB,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAU,CAAC;AAElF,uEAAuE;AACvE,MAAM,UAAU,QAAQ,CAAC,OAAiB;IACxC,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC;AAClD,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,OAAQ,KAA2B,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAc,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3E,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,YAAY,CAAC,IAAc;IACzC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS;YACZ,OAAO,gDAAgD,CAAC;QAC1D,KAAK,QAAQ;YACX,OAAO,uCAAuC,CAAC;QACjD;YACE,OAAO,yBAAyB,CAAC;IACrC,CAAC;AACH,CAAC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * semantic/embeddings — call an OpenAI-compatible `/embeddings` endpoint.
3
+ *
4
+ * Reuses the active provider's base URL + API key (the same ones chat uses).
5
+ * Not every provider exposes embeddings, so this is best-effort: any failure
6
+ * (missing endpoint, HTTP error, bad shape) resolves to `{ error }` and the
7
+ * caller degrades gracefully. The fetch is injectable for tests.
8
+ */
9
+ /** Parse an OpenAI-compatible embeddings response into ordered vectors. */
10
+ export function parseEmbeddingsResponse(json, expected) {
11
+ if (!json || typeof json !== 'object')
12
+ return null;
13
+ const data = json.data;
14
+ if (!Array.isArray(data))
15
+ return null;
16
+ const rows = [];
17
+ for (const item of data) {
18
+ if (!item || typeof item !== 'object')
19
+ continue;
20
+ const it = item;
21
+ if (!Array.isArray(it.embedding))
22
+ continue;
23
+ const embedding = it.embedding.filter((n) => typeof n === 'number');
24
+ if (embedding.length === 0)
25
+ continue;
26
+ rows.push({ index: typeof it.index === 'number' ? it.index : rows.length, embedding });
27
+ }
28
+ if (rows.length === 0)
29
+ return null;
30
+ // Respect the provider-reported order.
31
+ rows.sort((a, b) => a.index - b.index);
32
+ const out = rows.map((r) => r.embedding);
33
+ // Only accept a full result — a partial batch would misalign chunks.
34
+ return out.length === expected ? out : null;
35
+ }
36
+ /** Embed a batch of texts. Never throws. */
37
+ export async function embedTexts(texts, config, fetchImpl = fetch) {
38
+ if (texts.length === 0)
39
+ return { embeddings: [] };
40
+ const url = `${config.baseUrl.replace(/\/$/, '')}/embeddings`;
41
+ let response;
42
+ try {
43
+ response = await fetchImpl(url, {
44
+ method: 'POST',
45
+ headers: {
46
+ 'Content-Type': 'application/json',
47
+ Authorization: `Bearer ${config.apiKey}`,
48
+ },
49
+ body: JSON.stringify({ model: config.model, input: texts }),
50
+ });
51
+ }
52
+ catch (err) {
53
+ return { error: `network error contacting ${url}: ${err instanceof Error ? err.message : String(err)}` };
54
+ }
55
+ if (!response.ok) {
56
+ const body = await response.text().catch(() => '');
57
+ return { error: `HTTP ${response.status} from ${url}: ${body.slice(0, 160)}` };
58
+ }
59
+ let json;
60
+ try {
61
+ json = await response.json();
62
+ }
63
+ catch (err) {
64
+ return { error: `invalid JSON from ${url}: ${err instanceof Error ? err.message : String(err)}` };
65
+ }
66
+ const embeddings = parseEmbeddingsResponse(json, texts.length);
67
+ if (!embeddings)
68
+ return { error: `unexpected embeddings response shape from ${url}` };
69
+ return { embeddings };
70
+ }
71
+ //# sourceMappingURL=embeddings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../../../src/cli/semantic/embeddings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * semantic/index — build, persist, and query a repo's semantic code index.
3
+ *
4
+ * Orchestrates the pure store (chunk/cosine/rank) with an injectable embed
5
+ * function. Persistence is a JSON file under the user's state dir, keyed by
6
+ * repo root — no native vector DB, brute-force cosine, which is plenty for
7
+ * repo-scale indexes.
8
+ */
9
+ import { promises as fs, existsSync, readFileSync } from 'node:fs';
10
+ import { homedir } from 'node:os';
11
+ import path from 'node:path';
12
+ import { createHash } from 'node:crypto';
13
+ import { chunkFile, searchIndex, } from './store.js';
14
+ const SOURCE_EXTENSIONS = new Set([
15
+ '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.py', '.go', '.rs', '.java',
16
+ '.rb', '.php', '.c', '.h', '.cc', '.cpp', '.hpp', '.cs', '.swift', '.kt',
17
+ '.scala', '.sh', '.md',
18
+ ]);
19
+ const IGNORE_DIRS = new Set([
20
+ 'node_modules', '.git', 'dist', 'build', 'out', 'coverage', '.next',
21
+ '.turbo', '.cache', 'vendor', '__pycache__', '.venv', 'venv', '.tmp',
22
+ ]);
23
+ export function getIndexPath(root) {
24
+ const hash = createHash('sha1').update(path.resolve(root)).digest('hex').slice(0, 16);
25
+ return (process.env.ZELARI_SEMANTIC_FILE ??
26
+ path.join(homedir(), '.tmp', 'zelari-code', 'semantic', `${hash}.json`));
27
+ }
28
+ /** Walk `root` collecting indexable source files (bounded by `maxFiles`). */
29
+ export async function collectSourceFiles(root, maxFiles = 1500) {
30
+ const out = [];
31
+ const walk = async (dir) => {
32
+ if (out.length >= maxFiles)
33
+ return;
34
+ let entries;
35
+ try {
36
+ entries = await fs.readdir(dir, { withFileTypes: true });
37
+ }
38
+ catch {
39
+ return;
40
+ }
41
+ for (const entry of entries) {
42
+ if (out.length >= maxFiles)
43
+ return;
44
+ if (entry.name.startsWith('.') && entry.name !== '.') {
45
+ // Skip dotfiles/dotdirs except allow walking the root itself.
46
+ if (entry.isDirectory() && IGNORE_DIRS.has(entry.name))
47
+ continue;
48
+ if (entry.isDirectory())
49
+ continue;
50
+ }
51
+ const full = path.join(dir, entry.name);
52
+ if (entry.isDirectory()) {
53
+ if (IGNORE_DIRS.has(entry.name))
54
+ continue;
55
+ await walk(full);
56
+ }
57
+ else if (SOURCE_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) {
58
+ out.push(full);
59
+ }
60
+ }
61
+ };
62
+ await walk(root);
63
+ return out;
64
+ }
65
+ /**
66
+ * Build an index over `files` using `embed`. Chunks are embedded in batches;
67
+ * if any batch fails, the whole build fails (a partial index would give
68
+ * misleading results).
69
+ */
70
+ export async function buildIndex(files, embed, options) {
71
+ const batchSize = options.batchSize ?? 64;
72
+ const maxChunkChars = options.maxChunkChars ?? 8000;
73
+ const chunks = [];
74
+ let filesIndexed = 0;
75
+ for (const file of files) {
76
+ let text;
77
+ try {
78
+ text = await fs.readFile(file, 'utf8');
79
+ }
80
+ catch {
81
+ continue;
82
+ }
83
+ const fileChunks = chunkFile(file, text, options).filter((c) => c.text.length <= maxChunkChars);
84
+ if (fileChunks.length > 0)
85
+ filesIndexed += 1;
86
+ chunks.push(...fileChunks);
87
+ }
88
+ if (chunks.length === 0) {
89
+ return { error: 'no indexable source found', filesIndexed: 0, chunksIndexed: 0 };
90
+ }
91
+ const indexed = [];
92
+ for (let i = 0; i < chunks.length; i += batchSize) {
93
+ const batch = chunks.slice(i, i + batchSize);
94
+ const res = await embed(batch.map((c) => c.text));
95
+ if ('error' in res) {
96
+ return { error: res.error, filesIndexed, chunksIndexed: 0 };
97
+ }
98
+ if (res.length !== batch.length) {
99
+ return { error: 'embedding count mismatch', filesIndexed, chunksIndexed: 0 };
100
+ }
101
+ batch.forEach((c, j) => indexed.push({ ...c, embedding: res[j] }));
102
+ }
103
+ const data = {
104
+ model: options.model,
105
+ dim: indexed[0]?.embedding.length ?? 0,
106
+ chunks: indexed,
107
+ builtAt: Date.now(),
108
+ };
109
+ return { data, filesIndexed, chunksIndexed: indexed.length };
110
+ }
111
+ /** Persist an index atomically. */
112
+ export async function saveIndex(root, data) {
113
+ const file = getIndexPath(root);
114
+ await fs.mkdir(path.dirname(file), { recursive: true });
115
+ const tmp = `${file}.tmp-${process.pid}`;
116
+ await fs.writeFile(tmp, JSON.stringify(data), 'utf8');
117
+ await fs.rename(tmp, file);
118
+ }
119
+ /** Load a persisted index (sync — used at query time). Null if missing/corrupt. */
120
+ export function loadIndex(root) {
121
+ const file = getIndexPath(root);
122
+ if (!existsSync(file))
123
+ return null;
124
+ try {
125
+ const parsed = JSON.parse(readFileSync(file, 'utf8'));
126
+ if (parsed && Array.isArray(parsed.chunks))
127
+ return parsed;
128
+ }
129
+ catch {
130
+ /* corrupt */
131
+ }
132
+ return null;
133
+ }
134
+ /** Embed a query and rank the persisted index against it. */
135
+ export async function semanticSearch(root, query, embed, k = 8) {
136
+ const data = loadIndex(root);
137
+ if (!data)
138
+ return { error: 'no semantic index — run /index first' };
139
+ const res = await embed([query]);
140
+ if ('error' in res)
141
+ return { error: res.error };
142
+ const vector = res[0];
143
+ if (!vector)
144
+ return { error: 'query embedding failed' };
145
+ return { hits: searchIndex(data, vector, k) };
146
+ }
147
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/semantic/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * semantic/provider — build an EmbedFn from the active chat provider.
3
+ *
4
+ * Reuses the same base URL + API key resolution as chat (providerFromEnv), so
5
+ * `/index` and `semantic_search` hit the provider the user is already logged
6
+ * into. The embedding model is configurable via ZELARI_EMBED_MODEL.
7
+ */
8
+ import { providerFromEnv } from '../provider/openai-compatible.js';
9
+ import { embedTexts } from './embeddings.js';
10
+ export const DEFAULT_EMBED_MODEL = 'text-embedding-3-small';
11
+ export function embedModel() {
12
+ return process.env.ZELARI_EMBED_MODEL ?? DEFAULT_EMBED_MODEL;
13
+ }
14
+ /**
15
+ * Build an EmbedFn bound to the active provider, or null if no provider/API
16
+ * key is configured. The returned fn normalizes embedTexts' result shape into
17
+ * the `number[][] | { error }` the index expects.
18
+ */
19
+ export async function buildProviderEmbedFn() {
20
+ const cfg = await providerFromEnv();
21
+ if (!cfg)
22
+ return null;
23
+ const embedCfg = { apiKey: cfg.apiKey, baseUrl: cfg.baseUrl, model: embedModel() };
24
+ return async (texts) => {
25
+ const res = await embedTexts(texts, embedCfg);
26
+ return 'error' in res ? { error: res.error } : res.embeddings;
27
+ };
28
+ }
29
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/cli/semantic/provider.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,wBAAwB,CAAC;AAE5D,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,mBAAmB,CAAC;AAC/D,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,MAAM,GAAG,GAAG,MAAM,eAAe,EAAE,CAAC;IACpC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,QAAQ,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC;IACnF,OAAO,KAAK,EAAE,KAAe,EAAE,EAAE;QAC/B,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC9C,OAAO,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC;IAChE,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * semantic/store — the pure core of the semantic code index.
3
+ *
4
+ * Chunk source files into overlapping line windows, hold each chunk's
5
+ * embedding vector, and rank chunks against a query vector by cosine
6
+ * similarity. No I/O, no embeddings API — those live in embeddings.ts /
7
+ * index.ts — so all of this is deterministic and unit-testable.
8
+ *
9
+ * Persistence is a plain JSON file (no native vector DB): fine for
10
+ * repo-scale indexes (thousands of chunks) with a brute-force cosine scan.
11
+ */
12
+ /**
13
+ * Split a file's text into overlapping line-window chunks. Blank-only chunks
14
+ * are dropped. Overlap keeps a symbol that straddles a boundary retrievable
15
+ * from either side.
16
+ */
17
+ export function chunkFile(file, text, opts = {}) {
18
+ const maxLines = Math.max(1, opts.maxLines ?? 40);
19
+ const overlap = Math.max(0, Math.min(opts.overlap ?? 8, maxLines - 1));
20
+ const lines = text.split('\n');
21
+ const step = maxLines - overlap;
22
+ const chunks = [];
23
+ for (let start = 0; start < lines.length; start += step) {
24
+ const end = Math.min(start + maxLines, lines.length);
25
+ const slice = lines.slice(start, end);
26
+ if (slice.join('').trim().length > 0) {
27
+ chunks.push({
28
+ file,
29
+ startLine: start + 1,
30
+ endLine: end,
31
+ text: slice.join('\n'),
32
+ });
33
+ }
34
+ if (end >= lines.length)
35
+ break;
36
+ }
37
+ return chunks;
38
+ }
39
+ /** Cosine similarity of two equal-length vectors. Returns 0 on mismatch/zero. */
40
+ export function cosineSimilarity(a, b) {
41
+ if (a.length === 0 || a.length !== b.length)
42
+ return 0;
43
+ let dot = 0;
44
+ let na = 0;
45
+ let nb = 0;
46
+ for (let i = 0; i < a.length; i += 1) {
47
+ dot += a[i] * b[i];
48
+ na += a[i] * a[i];
49
+ nb += b[i] * b[i];
50
+ }
51
+ if (na === 0 || nb === 0)
52
+ return 0;
53
+ return dot / (Math.sqrt(na) * Math.sqrt(nb));
54
+ }
55
+ /** Rank indexed chunks against a query embedding, returning the top-k hits. */
56
+ export function searchIndex(data, queryEmbedding, k = 8) {
57
+ const scored = [];
58
+ for (const chunk of data.chunks) {
59
+ const score = cosineSimilarity(queryEmbedding, chunk.embedding);
60
+ scored.push({
61
+ file: chunk.file,
62
+ startLine: chunk.startLine,
63
+ endLine: chunk.endLine,
64
+ text: chunk.text,
65
+ score,
66
+ });
67
+ }
68
+ scored.sort((a, b) => b.score - a.score);
69
+ return scored.slice(0, Math.max(0, k));
70
+ }
71
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/cli/semantic/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAoCH;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAAY,EAAE,OAAqB,EAAE;IAC3E,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,QAAQ,GAAG,OAAO,CAAC;IAChC,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,IAAI,EAAE,CAAC;QACxD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACrD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACtC,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrC,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI;gBACJ,SAAS,EAAE,KAAK,GAAG,CAAC;gBACpB,OAAO,EAAE,GAAG;gBACZ,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;aACvB,CAAC,CAAC;QACL,CAAC;QACD,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM;YAAE,MAAM;IACjC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,gBAAgB,CAAC,CAAoB,EAAE,CAAoB;IACzE,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IACtD,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAClB,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IACD,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACnC,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,WAAW,CACzB,IAAuB,EACvB,cAAiC,EACjC,CAAC,GAAG,CAAC;IAEL,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,gBAAgB,CAAC,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAChE,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * semantic/tools — the `semantic_search` agent tool.
3
+ *
4
+ * Concept-level code search: "where is retry/backoff handled?" finds the
5
+ * relevant chunks even when they share no literal keyword with the query —
6
+ * where grep can't. Requires a prior index (`/index`). Read-only.
7
+ *
8
+ * The EmbedFn is injectable so the tool is testable without hitting a real
9
+ * embeddings endpoint; in production it's bound to the active provider.
10
+ */
11
+ import path from 'node:path';
12
+ import { z } from 'zod';
13
+ import { typedOk } from '@zelari/core/harness/tools/toolTypes';
14
+ import { semanticSearch, loadIndex } from './index.js';
15
+ import { buildProviderEmbedFn } from './provider.js';
16
+ export function createSemanticTool(deps) {
17
+ const buildEmbedFn = deps.buildEmbedFn ?? buildProviderEmbedFn;
18
+ return {
19
+ name: 'semantic_search',
20
+ description: 'Concept-level search over the indexed codebase: describe what you are ' +
21
+ 'looking for in plain language ("where is rate-limit backoff handled?") and ' +
22
+ 'get the most relevant code chunks (file:line + snippet), even when they ' +
23
+ 'share no exact keyword with your query. Requires an index — if none exists, ' +
24
+ 'ask the user to run /index. Complements grep_content (exact matches).',
25
+ permissions: ['read'],
26
+ inputSchema: z.object({
27
+ query: z.string().min(1).describe('Natural-language description of the code you want.'),
28
+ k: z.number().int().positive().max(25).optional().describe('Max results (default 8).'),
29
+ }),
30
+ execute: async (args) => {
31
+ const { query, k } = args;
32
+ if (!loadIndex(deps.root)) {
33
+ return typedOk({ results: [], note: 'no semantic index yet — run /index to build one, then retry' });
34
+ }
35
+ const embed = await buildEmbedFn();
36
+ if (!embed) {
37
+ return typedOk({ results: [], note: 'no provider/API key configured for embeddings' });
38
+ }
39
+ const res = await semanticSearch(deps.root, query, embed, k ?? 8);
40
+ if ('error' in res) {
41
+ return typedOk({ results: [], note: `semantic search unavailable: ${res.error}` });
42
+ }
43
+ return typedOk({
44
+ count: res.hits.length,
45
+ results: res.hits.map((h) => ({
46
+ location: `${path.relative(deps.root, h.file) || h.file}:${h.startLine}-${h.endLine}`,
47
+ score: Number(h.score.toFixed(3)),
48
+ preview: h.text.length > 400 ? `${h.text.slice(0, 400)}…` : h.text,
49
+ })),
50
+ });
51
+ },
52
+ };
53
+ }
54
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../src/cli/semantic/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAuB,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,SAAS,EAAgB,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AASrD,MAAM,UAAU,kBAAkB,CAAC,IAAsB;IACvD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,oBAAoB,CAAC;IAC/D,OAAO;QACL,IAAI,EAAE,iBAAiB;QACvB,WAAW,EACT,wEAAwE;YACxE,6EAA6E;YAC7E,0EAA0E;YAC1E,8EAA8E;YAC9E,uEAAuE;QACzE,WAAW,EAAE,CAAC,MAAM,CAAC;QACrB,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,oDAAoD,CAAC;YACvF,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;SACvF,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACtB,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAqC,CAAC;YAC3D,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,6DAA6D,EAAE,CAAC,CAAC;YACvG,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,YAAY,EAAE,CAAC;YACnC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,+CAA+C,EAAE,CAAC,CAAC;YACzF,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;YAClE,IAAI,OAAO,IAAI,GAAG,EAAE,CAAC;gBACnB,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,gCAAgC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACrF,CAAC;YACD,OAAO,OAAO,CAAC;gBACb,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM;gBACtB,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBAC5B,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,OAAO,EAAE;oBACrF,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;oBACjC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;iBACnE,CAAC,CAAC;aACJ,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -73,7 +73,7 @@ export function handleSlashCommand(text, availableSkills) {
73
73
  kind: 'help',
74
74
  message: `Available commands:\n /login <provider> — authenticate with provider (grok, minimax, glm, custom)\n /model — pick the active model from a list (auto-discovers, v0.7.10)\n /model <name> — switch the active model directly\n /model show — print the current model\n /models — list discovered models for the active provider (v3-U)\n /models refresh (or /discover) — re-discover models for the active provider\n /provider — pick the active provider from a list (v0.7.10)\n /provider <name> — switch the active provider directly\n /provider custom <baseUrl> — point the active provider at a self-hosted endpoint (Ollama, LM Studio, vLLM, ...)\n /provider custom clear — clear the custom endpoint override\n /skill <name> [input] — invoke a skill (autocomplete with /skill <TAB>)\n /skill-stats [name] — show invocation stats (success rate, avg duration, total tokens)\n /council <input> — invoke the multi-agent council on input\n /zelari <input> — run an autonomous mission (multi-run council until the MVP slice is complete)\n /council-feedback <memberId> <1-5> [note] — rate a council member for future ranking (Task I.2)
75
75
  /promote-member <memberId> — promote a council member to a standalone skill (v3-K)
76
- /update [--yes|-y] — check for zelari-code updates; --yes performs the update (v3-N)\n /steer <text> — enqueue a follow-up prompt on the active run (Task 18.2)\n /steer --interrupt <text> — cancel current run + enqueue <text> for next dispatch (Task C.3.2)\n /compact — compact the session transcript\n /clear — clear the visible transcript (session is preserved)\n /sessions — list past sessions\n /resume <id> — load a past session\n /branch <name> — snapshot the current session into a new branch\n /branches — list branches\n /checkout <name> — switch the active branch\n /new — start a fresh session\n /diff [--staged] — show uncommitted changes (or staged with --staged)\n /undo [--yes] — revert working-tree changes (destructive! requires --yes)\n /checkpoint [label] — snapshot the working tree as a restore point\n /rollback [id|latest] — restore the working tree to a checkpoint (no arg: list)\n /help — show this help\n /exit — exit the CLI\n\n${formatSkillList(availableSkills)}`,
76
+ /update [--yes|-y] — check for zelari-code updates; --yes performs the update (v3-N)\n /steer <text> — enqueue a follow-up prompt on the active run (Task 18.2)\n /steer --interrupt <text> — cancel current run + enqueue <text> for next dispatch (Task C.3.2)\n /compact — compact the session transcript\n /clear — clear the visible transcript (session is preserved)\n /sessions — list past sessions\n /resume <id> — load a past session\n /branch <name> — snapshot the current session into a new branch\n /branches — list branches\n /checkout <name> — switch the active branch\n /new — start a fresh session\n /diff [--staged] — show uncommitted changes (or staged with --staged)\n /undo [--yes] — revert working-tree changes (destructive! requires --yes)\n /checkpoint [label] — snapshot the working tree as a restore point\n /rollback [id|latest] — restore the working tree to a checkpoint (no arg: list)\n /index [status] — build the semantic code index for semantic_search\n /mode [agent|council|zelari] — switch dispatch mode (same as shift+tab; no arg cycles)\n /help — show this help\n /exit — exit the CLI\n\n${formatSkillList(availableSkills)}`,
77
77
  };
78
78
  case 'exit':
79
79
  return { handled: true, kind: 'exit', message: 'Goodbye.' };
@@ -448,6 +448,27 @@ export function handleSlashCommand(text, availableSkills) {
448
448
  'and unstages everything.\nUse `/undo --yes` (or `/undo -y`) to confirm.',
449
449
  };
450
450
  }
451
+ case 'mode': {
452
+ // `/mode` → cycle agent → council → zelari
453
+ // `/mode <agent|council|zelari>` → set directly
454
+ // Terminal-independent fallback for shift+tab (some terminals don't
455
+ // emit a shift+Tab sequence).
456
+ const target = args[0]?.trim().toLowerCase();
457
+ if (target && ['agent', 'council', 'zelari'].includes(target)) {
458
+ return { handled: true, kind: 'mode_set', modeTarget: target };
459
+ }
460
+ if (target) {
461
+ return { handled: true, kind: 'mode_set', message: `[mode] unknown: ${target}. Use agent, council, or zelari (or /mode to cycle).` };
462
+ }
463
+ return { handled: true, kind: 'mode_set' };
464
+ }
465
+ case 'index': {
466
+ // `/index` → build/rebuild the semantic code index
467
+ // `/index status` → show current index stats
468
+ return args[0] === 'status'
469
+ ? { handled: true, kind: 'index_status' }
470
+ : { handled: true, kind: 'index_build' };
471
+ }
451
472
  case 'checkpoint': {
452
473
  // `/checkpoint [label]` — snapshot the working tree now.
453
474
  const label = args.join(' ').trim();