zelari-code 1.23.0 → 1.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/bin/zelari-code.js +44 -12
- package/dist/cli/atMentions.js +179 -0
- package/dist/cli/atMentions.js.map +1 -0
- package/dist/cli/companion/config.js +172 -0
- package/dist/cli/companion/config.js.map +1 -0
- package/dist/cli/companion/runManager.js +250 -0
- package/dist/cli/companion/runManager.js.map +1 -0
- package/dist/cli/companion/serve.js +376 -0
- package/dist/cli/companion/serve.js.map +1 -0
- package/dist/cli/components/InputBar.js +1 -1
- package/dist/cli/components/InputBar.js.map +1 -1
- package/dist/cli/generateSkillFromUrl.js +236 -0
- package/dist/cli/generateSkillFromUrl.js.map +1 -0
- package/dist/cli/headless.js +6 -0
- package/dist/cli/headless.js.map +1 -1
- package/dist/cli/hooks/useSlashDispatch.js +19 -6
- package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
- package/dist/cli/main.bundled.js +2617 -769
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +155 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/missionSlice.js +19 -0
- package/dist/cli/missionSlice.js.map +1 -1
- package/dist/cli/provider/openai-compatible.js +164 -41
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/runHeadless.js +33 -0
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/skillCategories.js +15 -0
- package/dist/cli/skillCategories.js.map +1 -0
- package/dist/cli/skillConfigIo.js +285 -0
- package/dist/cli/skillConfigIo.js.map +1 -0
- package/dist/cli/slashCommands.js +14 -5
- package/dist/cli/slashCommands.js.map +1 -1
- package/dist/cli/slashHandlers/skills.js +30 -0
- package/dist/cli/slashHandlers/skills.js.map +1 -1
- package/dist/cli/traceStore.js +84 -0
- package/dist/cli/traceStore.js.map +1 -0
- package/dist/cli/triggerLock.js +79 -0
- package/dist/cli/triggerLock.js.map +1 -0
- package/dist/cli/zelariMission.js +77 -0
- package/dist/cli/zelariMission.js.map +1 -1
- package/dist/cli/zelariMission.test.js +44 -0
- package/dist/cli/zelariMission.test.js.map +1 -0
- package/package.json +4 -3
- package/scripts/start-companion-serve.ps1 +43 -0
package/README.md
CHANGED
|
@@ -280,6 +280,9 @@ Disable auto-check: `ANATHEMA_DEV=1 zelari-code`
|
|
|
280
280
|
- 🔌 **MCP** — external MCP servers via `.zelari/mcp.json` or `~/.zelari-code/mcp.json`; Desktop **Extensions** store for one-click install
|
|
281
281
|
- 🔐 **SSH targets** — configure hosts for deploy/monitor (password, agent, or key); tools `ssh_status` / `ssh_run` with allowlist; `ZELARI_SSH=0` kill switch
|
|
282
282
|
- 🖥️ **Zelari Desktop** — Tauri shell with chat UI, project tree, Settings, dual updates (app vs npm CLI), multi-turn history, optional overlay HUD
|
|
283
|
+
- 📱 **Companion host + Android** — `zelari-code serve` (SSE over Tailscale/LAN) + Settings → Start companion serve; MVP app in `apps/companion-android/`
|
|
284
|
+
- 🧩 **Skills store (Desktop)** — create/remove user & project `SKILL.md`; import skill from URL with the selected model; `/skills` picker in TUI
|
|
285
|
+
- 📎 **@-tag paths** — tag workspace files/folders in Desktop composer and CLI prompts
|
|
283
286
|
- ◇◆ **Plan / build phase** — `/plan` explores without project writes; `/build` implements with full tools (independent of agent/council/zelari mode)
|
|
284
287
|
- 🆕 **Self-update** — `/update` slash command + silent registry check on startup
|
|
285
288
|
|
package/bin/zelari-code.js
CHANGED
|
@@ -33,25 +33,57 @@ const compiledMain = path.join(pkgRoot, 'dist', 'cli', 'main.js');
|
|
|
33
33
|
const compiledApp = path.join(pkgRoot, 'dist', 'cli', 'app.js');
|
|
34
34
|
const tsSource = path.join(pkgRoot, 'src', 'cli', 'main.ts');
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Prefer TypeScript source only when explicitly requested.
|
|
38
|
+
*
|
|
39
|
+
* Do NOT key off ANATHEMA_DEV: Desktop always sets ANATHEMA_DEV=1 on CLI
|
|
40
|
+
* spawns (to skip background update checks). Forcing tsx there breaks
|
|
41
|
+
* get_app_config / --print-config at startup with "Failed to load provider
|
|
42
|
+
* config" when tsx is unavailable or cwd/node resolution differs.
|
|
43
|
+
*
|
|
44
|
+
* Stale-bundle risk (new flags missing) is handled by `desktop:dev` running
|
|
45
|
+
* `build:cli` first. Override with ZELARI_CLI_SOURCE=1 for pure source runs.
|
|
46
|
+
*/
|
|
47
|
+
function preferSourceOverDist() {
|
|
48
|
+
return (
|
|
49
|
+
process.env.ZELARI_CLI_SOURCE === '1' ||
|
|
50
|
+
process.env.ZELARI_CLI_SOURCE === 'true'
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function loadFromSource() {
|
|
44
55
|
try {
|
|
45
56
|
require('tsx/esm/api');
|
|
46
57
|
await import(toImportURL(tsSource));
|
|
47
|
-
} catch {
|
|
58
|
+
} catch (err) {
|
|
59
|
+
// Fall back to dist if present — better than hard-fail for Desktop.
|
|
60
|
+
if (existsSync(compiledBundled)) {
|
|
61
|
+
console.error(
|
|
62
|
+
'[zelari-code] tsx source load failed; falling back to dist bundle.\n' +
|
|
63
|
+
(err instanceof Error ? err.message : String(err)),
|
|
64
|
+
);
|
|
65
|
+
await import(toImportURL(compiledBundled));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
48
68
|
console.error(
|
|
49
|
-
'[zelari-code]
|
|
50
|
-
'
|
|
51
|
-
|
|
69
|
+
'[zelari-code] Failed to load TypeScript source via tsx.\n' +
|
|
70
|
+
'Run `npm run build:cli` to use dist, or install tsx.\n' +
|
|
71
|
+
(err instanceof Error ? err.message : String(err)),
|
|
52
72
|
);
|
|
53
73
|
process.exit(1);
|
|
54
74
|
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (preferSourceOverDist() && existsSync(tsSource)) {
|
|
78
|
+
await loadFromSource();
|
|
79
|
+
} else if (existsSync(compiledBundled)) {
|
|
80
|
+
// Production / Desktop path: esbuild-bundled self-contained ESM
|
|
81
|
+
await import(toImportURL(compiledBundled));
|
|
82
|
+
} else if (existsSync(compiledMain) && existsSync(compiledApp)) {
|
|
83
|
+
// Production path (legacy): tsc-only emit
|
|
84
|
+
await import(toImportURL(compiledMain));
|
|
85
|
+
} else if (existsSync(tsSource)) {
|
|
86
|
+
await loadFromSource();
|
|
55
87
|
} else {
|
|
56
88
|
console.error(
|
|
57
89
|
'[zelari-code] Cannot locate CLI entrypoint.\n' +
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expand @path tags in a user prompt into attached file/dir context.
|
|
3
|
+
*
|
|
4
|
+
* Tokens look like `@src/cli/main.ts` or `@apps/desktop` (relative to cwd).
|
|
5
|
+
* Skips email-like tokens (`user@host`) and pure `@` noise.
|
|
6
|
+
*
|
|
7
|
+
* Used by CLI free-form prompts and headless tasks so Desktop/CLI share
|
|
8
|
+
* the same @-tag semantics.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
11
|
+
import { isAbsolute, relative, resolve, sep } from 'node:path';
|
|
12
|
+
const ATTACH_TEXT_MAX = 48_000;
|
|
13
|
+
const ATTACH_FILE_MAX_BYTES = 512_000;
|
|
14
|
+
/** Match @path tokens: @foo, @foo/bar, @./x, @../y — not emails (word@word). */
|
|
15
|
+
const AT_PATH_RE = /(^|[\s([{])@((?:\.{1,2}\/)?[A-Za-z0-9_.+-]+(?:[\\/][A-Za-z0-9_.+-]+)*)/g;
|
|
16
|
+
function isProbablyText(name, head) {
|
|
17
|
+
if (/\.(txt|md|markdown|json|jsonc|ts|tsx|js|jsx|mjs|cjs|css|scss|html|htm|xml|yml|yaml|toml|ini|cfg|conf|rs|go|py|java|kt|swift|c|cc|cpp|h|hpp|cs|rb|php|sh|bash|zsh|ps1|sql|graphql|env|gitignore|dockerfile|makefile|cmake|lock|svg)$/i.test(name)) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
return !head.includes('\0') && /[\x09\x0a\x0d\x20-\x7e]/.test(head.slice(0, 200));
|
|
21
|
+
}
|
|
22
|
+
function underRoot(abs, root) {
|
|
23
|
+
const a = resolve(abs);
|
|
24
|
+
const r = resolve(root);
|
|
25
|
+
const prefix = r.endsWith(sep) ? r : r + sep;
|
|
26
|
+
return a === r || a.startsWith(prefix);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Extract unique @path tokens from text (order preserved).
|
|
30
|
+
*/
|
|
31
|
+
export function extractAtMentions(text) {
|
|
32
|
+
const out = [];
|
|
33
|
+
const seen = new Set();
|
|
34
|
+
AT_PATH_RE.lastIndex = 0;
|
|
35
|
+
let m;
|
|
36
|
+
while ((m = AT_PATH_RE.exec(text)) !== null) {
|
|
37
|
+
const token = (m[2] ?? '').trim();
|
|
38
|
+
if (!token || token.includes('@'))
|
|
39
|
+
continue;
|
|
40
|
+
// Skip email-like leftovers: if the char before @ was word char, the
|
|
41
|
+
// regex already requires whitespace/start — good.
|
|
42
|
+
const key = token.replace(/\\/g, '/').toLowerCase();
|
|
43
|
+
if (seen.has(key))
|
|
44
|
+
continue;
|
|
45
|
+
seen.add(key);
|
|
46
|
+
out.push(token);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
function resolveMention(token, cwd) {
|
|
51
|
+
const abs = isAbsolute(token) ? resolve(token) : resolve(cwd, token);
|
|
52
|
+
if (!underRoot(abs, cwd) && !isAbsolute(token)) {
|
|
53
|
+
// relative path that escapes cwd after resolve
|
|
54
|
+
if (!underRoot(abs, cwd)) {
|
|
55
|
+
return {
|
|
56
|
+
raw: token,
|
|
57
|
+
path: token,
|
|
58
|
+
absolute: abs,
|
|
59
|
+
isDir: false,
|
|
60
|
+
note: 'outside project root — skipped',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (isAbsolute(token) && !underRoot(abs, cwd)) {
|
|
65
|
+
return {
|
|
66
|
+
raw: token,
|
|
67
|
+
path: token,
|
|
68
|
+
absolute: abs,
|
|
69
|
+
isDir: false,
|
|
70
|
+
note: 'outside project root — skipped',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (!existsSync(abs)) {
|
|
74
|
+
return {
|
|
75
|
+
raw: token,
|
|
76
|
+
path: token,
|
|
77
|
+
absolute: abs,
|
|
78
|
+
isDir: false,
|
|
79
|
+
note: 'not found',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
let st;
|
|
83
|
+
try {
|
|
84
|
+
st = statSync(abs);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return {
|
|
88
|
+
raw: token,
|
|
89
|
+
path: token,
|
|
90
|
+
absolute: abs,
|
|
91
|
+
isDir: false,
|
|
92
|
+
note: 'unreadable',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const rel = relative(cwd, abs).replace(/\\/g, '/') || '.';
|
|
96
|
+
if (st.isDirectory()) {
|
|
97
|
+
return {
|
|
98
|
+
raw: token,
|
|
99
|
+
path: rel,
|
|
100
|
+
absolute: abs,
|
|
101
|
+
isDir: true,
|
|
102
|
+
note: 'directory — list/read with tools as needed',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (st.size > ATTACH_FILE_MAX_BYTES) {
|
|
106
|
+
return {
|
|
107
|
+
raw: token,
|
|
108
|
+
path: rel,
|
|
109
|
+
absolute: abs,
|
|
110
|
+
isDir: false,
|
|
111
|
+
note: `too large (${Math.round(st.size / 1024)} KB) — path only`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const buf = readFileSync(abs);
|
|
116
|
+
const head = buf.subarray(0, 800).toString('utf8');
|
|
117
|
+
if (!isProbablyText(abs, head)) {
|
|
118
|
+
return {
|
|
119
|
+
raw: token,
|
|
120
|
+
path: rel,
|
|
121
|
+
absolute: abs,
|
|
122
|
+
isDir: false,
|
|
123
|
+
note: 'binary — path only',
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
let text = buf.toString('utf8');
|
|
127
|
+
if (text.charCodeAt(0) === 0xfeff)
|
|
128
|
+
text = text.slice(1);
|
|
129
|
+
if (text.length > ATTACH_TEXT_MAX) {
|
|
130
|
+
text =
|
|
131
|
+
text.slice(0, ATTACH_TEXT_MAX) +
|
|
132
|
+
`\n\n… [truncated, ${text.length - ATTACH_TEXT_MAX} more chars]`;
|
|
133
|
+
}
|
|
134
|
+
return { raw: token, path: rel, absolute: abs, isDir: false, text };
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
return {
|
|
138
|
+
raw: token,
|
|
139
|
+
path: rel,
|
|
140
|
+
absolute: abs,
|
|
141
|
+
isDir: false,
|
|
142
|
+
note: err instanceof Error ? err.message : 'read failed',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Expand @path tags: keep user text, append [Tagged paths] blocks with
|
|
148
|
+
* content when available (mirrors Desktop attach format).
|
|
149
|
+
*/
|
|
150
|
+
export function expandAtMentions(userText, cwd = process.cwd()) {
|
|
151
|
+
const tokens = extractAtMentions(userText);
|
|
152
|
+
if (tokens.length === 0) {
|
|
153
|
+
return { text: userText, hits: [] };
|
|
154
|
+
}
|
|
155
|
+
const hits = [];
|
|
156
|
+
for (const t of tokens) {
|
|
157
|
+
const hit = resolveMention(t, cwd);
|
|
158
|
+
if (hit)
|
|
159
|
+
hits.push(hit);
|
|
160
|
+
}
|
|
161
|
+
if (hits.length === 0) {
|
|
162
|
+
return { text: userText, hits: [] };
|
|
163
|
+
}
|
|
164
|
+
const blocks = hits.map((h) => {
|
|
165
|
+
const label = h.path || h.raw;
|
|
166
|
+
if (h.text != null && h.text.length > 0) {
|
|
167
|
+
return `--- File: ${label} ---\n${h.text}\n--- End file ---`;
|
|
168
|
+
}
|
|
169
|
+
const extra = h.note ? ` (${h.note})` : '';
|
|
170
|
+
return `--- ${h.isDir ? 'Dir' : 'File'}: ${label}${extra} ---`;
|
|
171
|
+
});
|
|
172
|
+
const text = `${userText.trim()}\n\n[Tagged paths]\n${blocks.join('\n\n')}`;
|
|
173
|
+
return { text, hits };
|
|
174
|
+
}
|
|
175
|
+
/** True if the prompt contains at least one resolvable @path-looking token. */
|
|
176
|
+
export function hasAtMentions(text) {
|
|
177
|
+
return extractAtMentions(text).length > 0;
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=atMentions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atMentions.js","sourceRoot":"","sources":["../../src/cli/atMentions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Companion host config + token (for Android / remote clients over Tailscale).
|
|
3
|
+
*
|
|
4
|
+
* Files under ~/.zelari-code/:
|
|
5
|
+
* companion.json — projects allowlist, bind/port defaults
|
|
6
|
+
* companion.token — bearer token (created on first serve if missing)
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { homedir } from 'node:os';
|
|
11
|
+
import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
12
|
+
export const DEFAULT_COMPANION_PORT = 7421;
|
|
13
|
+
export const DEFAULT_COMPANION_BIND = '127.0.0.1';
|
|
14
|
+
export function getZelariHome() {
|
|
15
|
+
return join(homedir(), '.zelari-code');
|
|
16
|
+
}
|
|
17
|
+
export function getCompanionConfigPath() {
|
|
18
|
+
return join(getZelariHome(), 'companion.json');
|
|
19
|
+
}
|
|
20
|
+
export function getCompanionTokenPath() {
|
|
21
|
+
return join(getZelariHome(), 'companion.token');
|
|
22
|
+
}
|
|
23
|
+
function ensureHome() {
|
|
24
|
+
const home = getZelariHome();
|
|
25
|
+
if (!existsSync(home)) {
|
|
26
|
+
mkdirSync(home, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function loadCompanionConfig() {
|
|
30
|
+
const path = getCompanionConfigPath();
|
|
31
|
+
if (!existsSync(path)) {
|
|
32
|
+
return { projects: [] };
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
36
|
+
const projects = Array.isArray(raw.projects)
|
|
37
|
+
? raw.projects
|
|
38
|
+
.filter((p) => p &&
|
|
39
|
+
typeof p.path === 'string' &&
|
|
40
|
+
p.path.trim() &&
|
|
41
|
+
typeof (p.id ?? p.name) === 'string')
|
|
42
|
+
.map((p) => ({
|
|
43
|
+
id: String(p.id || p.name)
|
|
44
|
+
.trim()
|
|
45
|
+
.toLowerCase()
|
|
46
|
+
.replace(/[^a-z0-9-_]+/g, '-')
|
|
47
|
+
.slice(0, 64),
|
|
48
|
+
name: String(p.name || p.id || 'project').trim() || 'project',
|
|
49
|
+
path: String(p.path).trim(),
|
|
50
|
+
}))
|
|
51
|
+
: [];
|
|
52
|
+
return {
|
|
53
|
+
projects,
|
|
54
|
+
bind: typeof raw.bind === 'string' ? raw.bind : undefined,
|
|
55
|
+
port: typeof raw.port === 'number' ? raw.port : undefined,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return { projects: [] };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function saveCompanionConfig(cfg) {
|
|
63
|
+
ensureHome();
|
|
64
|
+
writeFileSync(getCompanionConfigPath(), JSON.stringify({
|
|
65
|
+
bind: cfg.bind,
|
|
66
|
+
port: cfg.port,
|
|
67
|
+
projects: cfg.projects,
|
|
68
|
+
}, null, 2) + '\n', 'utf8');
|
|
69
|
+
}
|
|
70
|
+
/** Load token or create a new one (printed once by serve). */
|
|
71
|
+
export function loadOrCreateToken(explicit) {
|
|
72
|
+
if (explicit && explicit.trim()) {
|
|
73
|
+
return { token: explicit.trim(), created: false };
|
|
74
|
+
}
|
|
75
|
+
ensureHome();
|
|
76
|
+
const path = getCompanionTokenPath();
|
|
77
|
+
if (existsSync(path)) {
|
|
78
|
+
const t = readFileSync(path, 'utf8').trim();
|
|
79
|
+
if (t)
|
|
80
|
+
return { token: t, created: false };
|
|
81
|
+
}
|
|
82
|
+
const token = randomBytes(24).toString('base64url');
|
|
83
|
+
writeFileSync(path, token + '\n', 'utf8');
|
|
84
|
+
try {
|
|
85
|
+
// Best-effort restrict (POSIX); ignored on Windows.
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
87
|
+
const fs = require('node:fs');
|
|
88
|
+
fs.chmodSync?.(path, 0o600);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
/* ignore */
|
|
92
|
+
}
|
|
93
|
+
return { token, created: true };
|
|
94
|
+
}
|
|
95
|
+
export function tokenMatches(expected, provided) {
|
|
96
|
+
if (!provided)
|
|
97
|
+
return false;
|
|
98
|
+
const a = createHash('sha256').update(expected).digest();
|
|
99
|
+
const b = createHash('sha256').update(provided).digest();
|
|
100
|
+
try {
|
|
101
|
+
return timingSafeEqual(a, b);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function slugFromPath(p) {
|
|
108
|
+
const base = p.replace(/[/\\]+$/, '').split(/[/\\]/).pop() || 'project';
|
|
109
|
+
return base
|
|
110
|
+
.toLowerCase()
|
|
111
|
+
.replace(/[^a-z0-9-_]+/g, '-')
|
|
112
|
+
.replace(/^-+|-+$/g, '')
|
|
113
|
+
.slice(0, 48) || 'project';
|
|
114
|
+
}
|
|
115
|
+
/** Merge CLI --project paths into config (in-memory; optionally persist). */
|
|
116
|
+
export function mergeProjects(cfg, extraPaths) {
|
|
117
|
+
const byId = new Map();
|
|
118
|
+
for (const p of cfg.projects) {
|
|
119
|
+
byId.set(p.id, p);
|
|
120
|
+
}
|
|
121
|
+
for (const raw of extraPaths) {
|
|
122
|
+
const path = raw.trim();
|
|
123
|
+
if (!path)
|
|
124
|
+
continue;
|
|
125
|
+
let id = slugFromPath(path);
|
|
126
|
+
let n = 2;
|
|
127
|
+
while (byId.has(id) && byId.get(id).path !== path) {
|
|
128
|
+
id = `${slugFromPath(path)}-${n++}`;
|
|
129
|
+
}
|
|
130
|
+
byId.set(id, {
|
|
131
|
+
id,
|
|
132
|
+
name: slugFromPath(path),
|
|
133
|
+
path,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return [...byId.values()];
|
|
137
|
+
}
|
|
138
|
+
export function resolveProjectPath(projects, cwdOrId) {
|
|
139
|
+
if (!projects.length) {
|
|
140
|
+
return {
|
|
141
|
+
ok: false,
|
|
142
|
+
error: 'No projects configured. Pass --project <path> or edit ~/.zelari-code/companion.json',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if (!cwdOrId || !String(cwdOrId).trim()) {
|
|
146
|
+
return { ok: true, project: projects[0] };
|
|
147
|
+
}
|
|
148
|
+
const key = String(cwdOrId).trim();
|
|
149
|
+
const byId = projects.find((p) => p.id === key || p.name === key);
|
|
150
|
+
if (byId)
|
|
151
|
+
return { ok: true, project: byId };
|
|
152
|
+
const norm = key.replace(/\\/g, '/').toLowerCase();
|
|
153
|
+
const byPath = projects.find((p) => p.path.replace(/\\/g, '/').toLowerCase() === norm);
|
|
154
|
+
if (byPath)
|
|
155
|
+
return { ok: true, project: byPath };
|
|
156
|
+
// Prefix match under an allowlisted root
|
|
157
|
+
const under = projects.find((p) => {
|
|
158
|
+
const root = p.path.replace(/\\/g, '/').toLowerCase().replace(/\/$/, '');
|
|
159
|
+
return norm === root || norm.startsWith(root + '/');
|
|
160
|
+
});
|
|
161
|
+
if (under) {
|
|
162
|
+
return {
|
|
163
|
+
ok: true,
|
|
164
|
+
project: { ...under, path: key },
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
ok: false,
|
|
169
|
+
error: `cwd/project not in allowlist: ${key}. Allowed: ${projects.map((p) => p.id).join(', ')}`,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/cli/companion/config.ts"],"names":[],"mappings":"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"}
|