codemini-cli 0.6.3 → 0.6.5
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/codemini-web/dist/assets/{AboutDialog-jgqGjQgl.js → AboutDialog-BUp8EzDg.js} +2 -2
- package/codemini-web/dist/assets/CodeWikiPanel-Fp0VKdzo.js +1 -0
- package/codemini-web/dist/assets/ConfigDialog-DIpj779O.js +1 -0
- package/codemini-web/dist/assets/GitDiffDialog-ZLEuX8Qm.js +3 -0
- package/codemini-web/dist/assets/{MemoryDialog-BhxQgG0I.js → MemoryDialog-D2YbENVd.js} +3 -3
- package/codemini-web/dist/assets/MessageBubble-BIgpZsLn.js +12 -0
- package/codemini-web/dist/assets/PatchDiff-CvKNaHsw.js +230 -0
- package/codemini-web/dist/assets/ProjectSelector-DXIep3lE.js +1 -0
- package/codemini-web/dist/assets/{SkillDialog-DxS43NpR.js → SkillDialog-DjPF-XBx.js} +4 -4
- package/codemini-web/dist/assets/SoulDialog-BfIoKETs.js +1 -0
- package/codemini-web/dist/assets/chevron-right-CfNZHlyU.js +1 -0
- package/codemini-web/dist/assets/{chunk-BO2N2NFS-Budy_hfO.js → chunk-BO2N2NFS-DMUdjM9q.js} +6 -6
- package/codemini-web/dist/assets/{highlighted-body-OFNGDK62-CQS1PAvD.js → highlighted-body-OFNGDK62-8ch0jz7Z.js} +1 -1
- package/codemini-web/dist/assets/index-BhMtCC8_.js +65 -0
- package/codemini-web/dist/assets/index-DRXwJ-n_.css +2 -0
- package/codemini-web/dist/assets/input-CYpdNDlR.js +1 -0
- package/codemini-web/dist/assets/lib-BXWizt13.js +1 -0
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-KBEtMEB9.js +1 -0
- package/codemini-web/dist/assets/{pencil-Ce_LFiEh.js → pencil-BdA2cEeE.js} +1 -1
- package/codemini-web/dist/assets/{refresh-cw-BKL-AZu5.js → refresh-cw-CJGgUGiS.js} +1 -1
- package/codemini-web/dist/assets/select-BLOccU1M.js +1 -0
- package/codemini-web/dist/assets/{trash-2-KmAlCwXd.js → trash-2-CQzNOch5.js} +1 -1
- package/codemini-web/dist/index.html +2 -2
- package/codemini-web/lib/runtime-bridge.js +332 -296
- package/codemini-web/server.js +319 -243
- package/package.json +1 -1
- package/src/core/agent-loop.js +188 -100
- package/src/core/chat-runtime.js +676 -571
- package/src/core/config-store.js +9 -3
- package/src/core/git-oplog-change-tracker.js +468 -0
- package/src/core/non-git-backup.js +116 -0
- package/src/core/paths.js +123 -123
- package/src/core/session-store.js +148 -99
- package/src/core/tools.js +555 -434
- package/src/tui/chat-app.js +196 -56
- package/codemini-web/dist/assets/CodeWikiPanel-EPuoerNv.js +0 -1
- package/codemini-web/dist/assets/ConfigDialog-B5IGZCc9.js +0 -1
- package/codemini-web/dist/assets/GitDiffDialog-Bb_Tw5ZK.js +0 -222
- package/codemini-web/dist/assets/MessageBubble-wUff4GP4.js +0 -6
- package/codemini-web/dist/assets/ProjectSelector-C0leTf6f.js +0 -1
- package/codemini-web/dist/assets/SoulDialog-XDTEGWvH.js +0 -1
- package/codemini-web/dist/assets/chevron-right-Dbzw7YzA.js +0 -1
- package/codemini-web/dist/assets/index-D0EGtNPr.js +0 -65
- package/codemini-web/dist/assets/index-wOUf3WkN.css +0 -2
- package/codemini-web/dist/assets/input-CNQgbKe6.js +0 -1
- package/codemini-web/dist/assets/lib-BOngVP_M.js +0 -11
- package/codemini-web/dist/assets/lib-DrOTTm_N.js +0 -1
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-DrBu5KyC.js +0 -1
- package/codemini-web/dist/assets/select-BZXfigic.js +0 -1
package/codemini-web/server.js
CHANGED
|
@@ -8,11 +8,11 @@ import { loadConfig, saveConfig, setConfigValue, getConfigValue } from '../src/c
|
|
|
8
8
|
import { createChatRuntime } from '../src/core/chat-runtime.js';
|
|
9
9
|
import { createSession, loadSession, listSessions, resolveSession, deleteSession } from '../src/core/session-store.js';
|
|
10
10
|
import { buildDefaultSystemPrompt } from '../src/core/default-system-prompt.js';
|
|
11
|
-
import { RuntimeBridge } from './lib/runtime-bridge.js';
|
|
12
|
-
import { installSkillSource, listSkillEntries } from '../src/commands/skill.js';
|
|
13
|
-
import { computeFileSha256, readSkillRegistry, upsertSkillRegistryEntry, writeSkillRegistry } from '../src/core/skill-registry.js';
|
|
14
|
-
import { forgetMemory, listMemories, searchMemories } from '../src/core/memory-store.js';
|
|
15
|
-
import { getReplyLanguage } from '../src/core/reply-language.js';
|
|
11
|
+
import { RuntimeBridge } from './lib/runtime-bridge.js';
|
|
12
|
+
import { installSkillSource, listSkillEntries } from '../src/commands/skill.js';
|
|
13
|
+
import { computeFileSha256, readSkillRegistry, upsertSkillRegistryEntry, writeSkillRegistry } from '../src/core/skill-registry.js';
|
|
14
|
+
import { forgetMemory, listMemories, searchMemories } from '../src/core/memory-store.js';
|
|
15
|
+
import { getReplyLanguage } from '../src/core/reply-language.js';
|
|
16
16
|
import { getBaseConfigDir, getFileIndexPath, getProjectSkillsDir, getSkillsDir } from '../src/core/paths.js';
|
|
17
17
|
import { initializeProjectIndex } from '../src/core/project-index.js';
|
|
18
18
|
import { INDEX_SKIP_DIRS } from '../src/core/constants.js';
|
|
@@ -23,19 +23,19 @@ const GENERAL_PROJECT_DIR = (() => {
|
|
|
23
23
|
return path.join(base, 'workspace');
|
|
24
24
|
})();
|
|
25
25
|
|
|
26
|
-
const SKILL_CATALOG_FILE = 'codemini.skills.json';
|
|
27
|
-
const SKILL_MODES = new Set(['always', 'auto_attach', 'agent_requested', 'manual']);
|
|
28
|
-
const SKILL_SCOPES = new Set(['project', 'global']);
|
|
29
|
-
const MEMORY_SCOPES = new Set(['user', 'global', 'project']);
|
|
30
|
-
|
|
31
|
-
function normalizeSkillScope(scope) {
|
|
32
|
-
return SKILL_SCOPES.has(scope) ? scope : 'project';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function normalizeMemoryScope(scope) {
|
|
36
|
-
const value = String(scope || '').trim().toLowerCase();
|
|
37
|
-
return MEMORY_SCOPES.has(value) ? value : 'user';
|
|
38
|
-
}
|
|
26
|
+
const SKILL_CATALOG_FILE = 'codemini.skills.json';
|
|
27
|
+
const SKILL_MODES = new Set(['always', 'auto_attach', 'agent_requested', 'manual']);
|
|
28
|
+
const SKILL_SCOPES = new Set(['project', 'global']);
|
|
29
|
+
const MEMORY_SCOPES = new Set(['user', 'global', 'project']);
|
|
30
|
+
|
|
31
|
+
function normalizeSkillScope(scope) {
|
|
32
|
+
return SKILL_SCOPES.has(scope) ? scope : 'project';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalizeMemoryScope(scope) {
|
|
36
|
+
const value = String(scope || '').trim().toLowerCase();
|
|
37
|
+
return MEMORY_SCOPES.has(value) ? value : 'user';
|
|
38
|
+
}
|
|
39
39
|
|
|
40
40
|
function isSafeSkillName(name = '') {
|
|
41
41
|
return /^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(name);
|
|
@@ -272,107 +272,107 @@ async function serveStatic(res, filePath) {
|
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
function normalizeProjectPath(value) {
|
|
276
|
-
const raw = String(value || '').trim();
|
|
277
|
-
if (!raw) return '';
|
|
278
|
-
const win = raw.match(/^([A-Za-z]):[\\/](.*)$/);
|
|
275
|
+
function normalizeProjectPath(value) {
|
|
276
|
+
const raw = String(value || '').trim();
|
|
277
|
+
if (!raw) return '';
|
|
278
|
+
const win = raw.match(/^([A-Za-z]):[\\/](.*)$/);
|
|
279
279
|
if (win && process.platform !== 'win32') {
|
|
280
280
|
return path.join('/mnt', win[1].toLowerCase(), win[2].replace(/[\\/]+/g, '/'));
|
|
281
281
|
}
|
|
282
|
-
return path.resolve(raw);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function projectNameForDir(projectDir) {
|
|
286
|
-
if (isGeneralProjectDir(projectDir)) return '__codemini_general__';
|
|
287
|
-
return path.basename(path.resolve(projectDir || '')) || projectDir || '';
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
async function validProjectDir(value) {
|
|
291
|
-
const normalized = normalizeProjectPath(value);
|
|
292
|
-
if (!normalized) return '';
|
|
293
|
-
try {
|
|
294
|
-
const stat = await fs.stat(normalized);
|
|
295
|
-
return stat.isDirectory() ? normalized : '';
|
|
296
|
-
} catch {
|
|
297
|
-
return '';
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
async function resolveRequestProjectDir(value, fallbackDir) {
|
|
302
|
-
const resolved = await validProjectDir(value);
|
|
303
|
-
return resolved || fallbackDir;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
async function parseProjectDirsParam(url, fallbackDir) {
|
|
307
|
-
const raw = url.searchParams.get('projects');
|
|
308
|
-
const parsed = raw ? tryParseJson(raw) : [];
|
|
309
|
-
const values = Array.isArray(parsed) ? parsed : [];
|
|
310
|
-
const seen = new Set();
|
|
311
|
-
const dirs = [];
|
|
312
|
-
for (const candidate of [fallbackDir, ...values]) {
|
|
313
|
-
const resolved = await validProjectDir(candidate);
|
|
314
|
-
if (!resolved || seen.has(resolved)) continue;
|
|
315
|
-
seen.add(resolved);
|
|
316
|
-
dirs.push(resolved);
|
|
317
|
-
}
|
|
318
|
-
if (dirs.length === 0 && fallbackDir) {
|
|
319
|
-
dirs.push(fallbackDir);
|
|
320
|
-
}
|
|
321
|
-
return dirs;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
async function listSkillsForProjectDirs(projectDirs, fallbackDir) {
|
|
325
|
-
const dirs = projectDirs.length > 0 ? projectDirs : [fallbackDir];
|
|
326
|
-
const seen = new Set();
|
|
327
|
-
const results = [];
|
|
328
|
-
for (let index = 0; index < dirs.length; index += 1) {
|
|
329
|
-
const projectDir = dirs[index];
|
|
330
|
-
const entries = await listSkillEntries({ scope: 'all', cwd: projectDir });
|
|
331
|
-
for (const entry of entries) {
|
|
332
|
-
if (entry.scope !== 'project') {
|
|
333
|
-
const globalKey = `${entry.scope}:${entry.name}:${entry.path || ''}`;
|
|
334
|
-
if (seen.has(globalKey)) continue;
|
|
335
|
-
seen.add(globalKey);
|
|
336
|
-
results.push(entry);
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
const projectKey = `project:${projectDir}:${entry.name}:${entry.path || ''}`;
|
|
340
|
-
if (seen.has(projectKey)) continue;
|
|
341
|
-
seen.add(projectKey);
|
|
342
|
-
results.push({
|
|
343
|
-
...entry,
|
|
344
|
-
projectDir,
|
|
345
|
-
projectName: projectNameForDir(projectDir)
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
return results.sort((a, b) => {
|
|
350
|
-
const left = `${a.scope}:${a.projectName || ''}:${a.name}`;
|
|
351
|
-
const right = `${b.scope}:${b.projectName || ''}:${b.name}`;
|
|
352
|
-
return left.localeCompare(right);
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
async function listMemoriesForProjectDirs({ scope, query, projectDirs, fallbackDir }) {
|
|
357
|
-
if (scope !== 'project') {
|
|
358
|
-
const items = query
|
|
359
|
-
? await searchMemories({ scope, query, workspaceRoot: fallbackDir })
|
|
360
|
-
: await listMemories({ scope, workspaceRoot: fallbackDir });
|
|
361
|
-
return items;
|
|
362
|
-
}
|
|
363
|
-
const dirs = projectDirs.length > 0 ? projectDirs : [fallbackDir];
|
|
364
|
-
const chunks = await Promise.all(dirs.map(async (projectDir) => {
|
|
365
|
-
const items = query
|
|
366
|
-
? await searchMemories({ scope, query, workspaceRoot: projectDir })
|
|
367
|
-
: await listMemories({ scope, workspaceRoot: projectDir });
|
|
368
|
-
return (items || []).map((item) => ({
|
|
369
|
-
...item,
|
|
370
|
-
projectDir,
|
|
371
|
-
projectName: projectNameForDir(projectDir)
|
|
372
|
-
}));
|
|
373
|
-
}));
|
|
374
|
-
return chunks.flat();
|
|
375
|
-
}
|
|
282
|
+
return path.resolve(raw);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function projectNameForDir(projectDir) {
|
|
286
|
+
if (isGeneralProjectDir(projectDir)) return '__codemini_general__';
|
|
287
|
+
return path.basename(path.resolve(projectDir || '')) || projectDir || '';
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function validProjectDir(value) {
|
|
291
|
+
const normalized = normalizeProjectPath(value);
|
|
292
|
+
if (!normalized) return '';
|
|
293
|
+
try {
|
|
294
|
+
const stat = await fs.stat(normalized);
|
|
295
|
+
return stat.isDirectory() ? normalized : '';
|
|
296
|
+
} catch {
|
|
297
|
+
return '';
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async function resolveRequestProjectDir(value, fallbackDir) {
|
|
302
|
+
const resolved = await validProjectDir(value);
|
|
303
|
+
return resolved || fallbackDir;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async function parseProjectDirsParam(url, fallbackDir) {
|
|
307
|
+
const raw = url.searchParams.get('projects');
|
|
308
|
+
const parsed = raw ? tryParseJson(raw) : [];
|
|
309
|
+
const values = Array.isArray(parsed) ? parsed : [];
|
|
310
|
+
const seen = new Set();
|
|
311
|
+
const dirs = [];
|
|
312
|
+
for (const candidate of [fallbackDir, ...values]) {
|
|
313
|
+
const resolved = await validProjectDir(candidate);
|
|
314
|
+
if (!resolved || seen.has(resolved)) continue;
|
|
315
|
+
seen.add(resolved);
|
|
316
|
+
dirs.push(resolved);
|
|
317
|
+
}
|
|
318
|
+
if (dirs.length === 0 && fallbackDir) {
|
|
319
|
+
dirs.push(fallbackDir);
|
|
320
|
+
}
|
|
321
|
+
return dirs;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function listSkillsForProjectDirs(projectDirs, fallbackDir) {
|
|
325
|
+
const dirs = projectDirs.length > 0 ? projectDirs : [fallbackDir];
|
|
326
|
+
const seen = new Set();
|
|
327
|
+
const results = [];
|
|
328
|
+
for (let index = 0; index < dirs.length; index += 1) {
|
|
329
|
+
const projectDir = dirs[index];
|
|
330
|
+
const entries = await listSkillEntries({ scope: 'all', cwd: projectDir });
|
|
331
|
+
for (const entry of entries) {
|
|
332
|
+
if (entry.scope !== 'project') {
|
|
333
|
+
const globalKey = `${entry.scope}:${entry.name}:${entry.path || ''}`;
|
|
334
|
+
if (seen.has(globalKey)) continue;
|
|
335
|
+
seen.add(globalKey);
|
|
336
|
+
results.push(entry);
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
const projectKey = `project:${projectDir}:${entry.name}:${entry.path || ''}`;
|
|
340
|
+
if (seen.has(projectKey)) continue;
|
|
341
|
+
seen.add(projectKey);
|
|
342
|
+
results.push({
|
|
343
|
+
...entry,
|
|
344
|
+
projectDir,
|
|
345
|
+
projectName: projectNameForDir(projectDir)
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return results.sort((a, b) => {
|
|
350
|
+
const left = `${a.scope}:${a.projectName || ''}:${a.name}`;
|
|
351
|
+
const right = `${b.scope}:${b.projectName || ''}:${b.name}`;
|
|
352
|
+
return left.localeCompare(right);
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async function listMemoriesForProjectDirs({ scope, query, projectDirs, fallbackDir }) {
|
|
357
|
+
if (scope !== 'project') {
|
|
358
|
+
const items = query
|
|
359
|
+
? await searchMemories({ scope, query, workspaceRoot: fallbackDir })
|
|
360
|
+
: await listMemories({ scope, workspaceRoot: fallbackDir });
|
|
361
|
+
return items;
|
|
362
|
+
}
|
|
363
|
+
const dirs = projectDirs.length > 0 ? projectDirs : [fallbackDir];
|
|
364
|
+
const chunks = await Promise.all(dirs.map(async (projectDir) => {
|
|
365
|
+
const items = query
|
|
366
|
+
? await searchMemories({ scope, query, workspaceRoot: projectDir })
|
|
367
|
+
: await listMemories({ scope, workspaceRoot: projectDir });
|
|
368
|
+
return (items || []).map((item) => ({
|
|
369
|
+
...item,
|
|
370
|
+
projectDir,
|
|
371
|
+
projectName: projectNameForDir(projectDir)
|
|
372
|
+
}));
|
|
373
|
+
}));
|
|
374
|
+
return chunks.flat();
|
|
375
|
+
}
|
|
376
376
|
|
|
377
377
|
async function resolveCodeWikiProjectDir(url, fallbackDir) {
|
|
378
378
|
const requested = normalizeProjectPath(url.searchParams.get('project') || '');
|
|
@@ -738,20 +738,34 @@ async function main() {
|
|
|
738
738
|
jsonResponse(res, { ok: true });
|
|
739
739
|
return;
|
|
740
740
|
}
|
|
741
|
-
if (req.method === 'POST' && url.pathname === '/api/execution-mode') {
|
|
742
|
-
const { mode } = await readBody(req);
|
|
743
|
-
if (!mode || !['normal', '
|
|
744
|
-
jsonResponse(res, { error: true, message: 'Invalid mode' }, 400);
|
|
745
|
-
return;
|
|
746
|
-
}
|
|
741
|
+
if (req.method === 'POST' && url.pathname === '/api/execution-mode') {
|
|
742
|
+
const { mode } = await readBody(req);
|
|
743
|
+
if (!mode || !['normal', 'plan'].includes(mode)) {
|
|
744
|
+
jsonResponse(res, { error: true, message: 'Invalid mode' }, 400);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
747
|
if (bridge.isBusy()) {
|
|
748
748
|
jsonResponse(res, { error: true, message: 'Cannot switch execution mode while a request is running' }, 409);
|
|
749
749
|
return;
|
|
750
750
|
}
|
|
751
751
|
const ok = await bridge.setExecutionMode(mode);
|
|
752
|
-
jsonResponse(res, { ok });
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
752
|
+
jsonResponse(res, { ok });
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
if (req.method === 'POST' && url.pathname === '/api/approval-mode') {
|
|
756
|
+
const { mode } = await readBody(req);
|
|
757
|
+
if (!mode || !['review', 'auto', 'full_access'].includes(mode)) {
|
|
758
|
+
jsonResponse(res, { error: true, message: 'Invalid approval mode' }, 400);
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
if (bridge.isBusy()) {
|
|
762
|
+
jsonResponse(res, { error: true, message: 'Cannot switch approval mode while a request is running' }, 409);
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
const ok = await bridge.setApprovalMode(mode);
|
|
766
|
+
jsonResponse(res, { ok });
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
755
769
|
if (req.method === 'POST' && url.pathname === '/api/approval') {
|
|
756
770
|
const { id, approved } = await readBody(req);
|
|
757
771
|
jsonResponse(res, { ok: bridge.handleApproval(id, !!approved) });
|
|
@@ -968,7 +982,11 @@ async function main() {
|
|
|
968
982
|
|
|
969
983
|
// ── Session management ──
|
|
970
984
|
if (req.method === 'GET' && url.pathname === '/api/sessions') {
|
|
971
|
-
const
|
|
985
|
+
const requestedLimit = Number(url.searchParams.get('limit') || 200);
|
|
986
|
+
const limit = Number.isFinite(requestedLimit)
|
|
987
|
+
? Math.max(1, Math.min(1000, Math.round(requestedLimit)))
|
|
988
|
+
: 200;
|
|
989
|
+
const sessions = await listSessions(limit);
|
|
972
990
|
const enriched = sessions.map(s => ({ ...s, isGeneral: isGeneralProjectDir(s.projectDir) }));
|
|
973
991
|
jsonResponse(res, enriched);
|
|
974
992
|
return;
|
|
@@ -1008,7 +1026,17 @@ async function main() {
|
|
|
1008
1026
|
});
|
|
1009
1027
|
await bridge.switchRuntime(newRuntime);
|
|
1010
1028
|
currentProjectDir = process.cwd();
|
|
1011
|
-
jsonResponse(res, {
|
|
1029
|
+
jsonResponse(res, {
|
|
1030
|
+
ok: true,
|
|
1031
|
+
sessionId,
|
|
1032
|
+
cwd: currentProjectDir,
|
|
1033
|
+
isGeneral: isGeneralProjectDir(currentProjectDir),
|
|
1034
|
+
state: { ...bridge.getState(), cwd: currentProjectDir, isGeneral: isGeneralProjectDir(currentProjectDir) },
|
|
1035
|
+
sessionData: {
|
|
1036
|
+
messages: bridge.getSessionMessages(),
|
|
1037
|
+
compact: bridge.getSessionCompactMeta()
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1012
1040
|
} catch (err) {
|
|
1013
1041
|
jsonResponse(res, { error: true, message: err.message }, 500);
|
|
1014
1042
|
}
|
|
@@ -1028,7 +1056,7 @@ async function main() {
|
|
|
1028
1056
|
let nextSessionId = bridge.getSessionId();
|
|
1029
1057
|
let cwd = currentProjectDir;
|
|
1030
1058
|
if (deletingCurrent) {
|
|
1031
|
-
const remaining = await listSessions(
|
|
1059
|
+
const remaining = await listSessions(1);
|
|
1032
1060
|
const next = remaining.find((session) => session.id !== sessionId);
|
|
1033
1061
|
const built = next
|
|
1034
1062
|
? await buildRuntimeForSession({ sessionId: next.id, model: bridge.getState().model })
|
|
@@ -1038,7 +1066,20 @@ async function main() {
|
|
|
1038
1066
|
nextSessionId = built.session.id;
|
|
1039
1067
|
cwd = currentProjectDir;
|
|
1040
1068
|
}
|
|
1041
|
-
jsonResponse(res, {
|
|
1069
|
+
jsonResponse(res, {
|
|
1070
|
+
ok: true,
|
|
1071
|
+
removed: result.removed,
|
|
1072
|
+
sessionId: nextSessionId,
|
|
1073
|
+
cwd,
|
|
1074
|
+
isGeneral: isGeneralProjectDir(currentProjectDir),
|
|
1075
|
+
...(deletingCurrent ? {
|
|
1076
|
+
state: { ...bridge.getState(), cwd: currentProjectDir, isGeneral: isGeneralProjectDir(currentProjectDir) },
|
|
1077
|
+
sessionData: {
|
|
1078
|
+
messages: bridge.getSessionMessages(),
|
|
1079
|
+
compact: bridge.getSessionCompactMeta()
|
|
1080
|
+
}
|
|
1081
|
+
} : {})
|
|
1082
|
+
});
|
|
1042
1083
|
} catch (err) {
|
|
1043
1084
|
jsonResponse(res, { error: true, message: err.message }, 500);
|
|
1044
1085
|
}
|
|
@@ -1068,7 +1109,7 @@ async function main() {
|
|
|
1068
1109
|
}
|
|
1069
1110
|
return;
|
|
1070
1111
|
}
|
|
1071
|
-
if (req.method === 'GET' && url.pathname === '/api/git-diff') {
|
|
1112
|
+
if (req.method === 'GET' && url.pathname === '/api/git-diff') {
|
|
1072
1113
|
try {
|
|
1073
1114
|
let patch;
|
|
1074
1115
|
try {
|
|
@@ -1105,9 +1146,44 @@ async function main() {
|
|
|
1105
1146
|
} catch {
|
|
1106
1147
|
jsonResponse(res, { patch: '', files: [] });
|
|
1107
1148
|
}
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
|
-
if (req.method === '
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
if (req.method === 'GET' && url.pathname === '/api/session-changes') {
|
|
1152
|
+
try {
|
|
1153
|
+
jsonResponse(res, { changes: await bridge.getChangeSets() });
|
|
1154
|
+
} catch (err) {
|
|
1155
|
+
jsonResponse(res, { error: true, message: err?.message || 'Failed to read session changes' }, 500);
|
|
1156
|
+
}
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
if (req.method === 'GET' && url.pathname.startsWith('/api/session-changes/') && url.pathname.endsWith('/patch')) {
|
|
1160
|
+
const id = decodeURIComponent(url.pathname.slice('/api/session-changes/'.length, -'/patch'.length));
|
|
1161
|
+
try {
|
|
1162
|
+
jsonResponse(res, { id, patch: await bridge.getChangeSetPatch(id) });
|
|
1163
|
+
} catch (err) {
|
|
1164
|
+
jsonResponse(res, { error: true, message: err?.message || 'Failed to read change patch' }, 404);
|
|
1165
|
+
}
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
if (req.method === 'POST' && url.pathname !== '/api/session-changes/undo' && url.pathname.startsWith('/api/session-changes/') && url.pathname.endsWith('/undo')) {
|
|
1169
|
+
const id = decodeURIComponent(url.pathname.slice('/api/session-changes/'.length, -'/undo'.length));
|
|
1170
|
+
try {
|
|
1171
|
+
jsonResponse(res, await bridge.undoChangeSet(id));
|
|
1172
|
+
} catch (err) {
|
|
1173
|
+
jsonResponse(res, { error: true, message: err?.message || 'Failed to undo change' }, 409);
|
|
1174
|
+
}
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
if (req.method === 'POST' && url.pathname === '/api/session-changes/undo') {
|
|
1178
|
+
const { ids } = await readBody(req);
|
|
1179
|
+
try {
|
|
1180
|
+
jsonResponse(res, await bridge.undoChangeSets(ids));
|
|
1181
|
+
} catch (err) {
|
|
1182
|
+
jsonResponse(res, { error: true, message: err?.message || 'Failed to undo changes' }, 409);
|
|
1183
|
+
}
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
if (req.method === 'POST' && url.pathname === '/api/git-batch') {
|
|
1111
1187
|
const { dirs } = await readBody(req);
|
|
1112
1188
|
const result = {};
|
|
1113
1189
|
for (const dir of (Array.isArray(dirs) ? dirs : [])) {
|
|
@@ -1213,64 +1289,64 @@ async function main() {
|
|
|
1213
1289
|
}
|
|
1214
1290
|
return;
|
|
1215
1291
|
}
|
|
1216
|
-
if (req.method === 'GET' && url.pathname.startsWith('/api/config/get/')) {
|
|
1217
|
-
const key = url.pathname.slice('/api/config/get/'.length);
|
|
1218
|
-
const value = await getConfigValue(key);
|
|
1219
|
-
jsonResponse(res, { key, value });
|
|
1220
|
-
return;
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
// ── Memory management ──
|
|
1224
|
-
if (req.method === 'GET' && url.pathname === '/api/memory') {
|
|
1225
|
-
const scope = normalizeMemoryScope(url.searchParams.get('scope'));
|
|
1226
|
-
const query = String(url.searchParams.get('q') || '').trim();
|
|
1227
|
-
try {
|
|
1228
|
-
const projectDirs = await parseProjectDirsParam(url, currentProjectDir);
|
|
1229
|
-
const items = await listMemoriesForProjectDirs({
|
|
1230
|
-
scope,
|
|
1231
|
-
query,
|
|
1232
|
-
projectDirs,
|
|
1233
|
-
fallbackDir: currentProjectDir
|
|
1234
|
-
});
|
|
1235
|
-
jsonResponse(res, { scope, query, items });
|
|
1236
|
-
} catch (err) {
|
|
1237
|
-
jsonResponse(res, { error: true, message: err.message }, 500);
|
|
1238
|
-
}
|
|
1239
|
-
return;
|
|
1240
|
-
}
|
|
1241
|
-
if (req.method === 'DELETE' && url.pathname.startsWith('/api/memory/')) {
|
|
1242
|
-
const id = decodeURIComponent(url.pathname.slice('/api/memory/'.length));
|
|
1243
|
-
const scope = normalizeMemoryScope(url.searchParams.get('scope'));
|
|
1244
|
-
if (!id) { jsonResponse(res, { error: true, message: 'Missing memory id' }, 400); return; }
|
|
1245
|
-
try {
|
|
1246
|
-
const workspaceRoot = scope === 'project'
|
|
1247
|
-
? await resolveRequestProjectDir(url.searchParams.get('projectDir'), currentProjectDir)
|
|
1248
|
-
: currentProjectDir;
|
|
1249
|
-
const result = await forgetMemory({ scope, id, workspaceRoot });
|
|
1250
|
-
jsonResponse(res, { ok: true, scope, ...result });
|
|
1251
|
-
} catch (err) {
|
|
1252
|
-
jsonResponse(res, { error: true, message: err.message }, 500);
|
|
1253
|
-
}
|
|
1254
|
-
return;
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
// ── Skills management ──
|
|
1258
|
-
if (req.method === 'GET' && url.pathname === '/api/skills') {
|
|
1259
|
-
try {
|
|
1260
|
-
const projectDirs = await parseProjectDirsParam(url, currentProjectDir);
|
|
1261
|
-
const skills = await listSkillsForProjectDirs(projectDirs, currentProjectDir);
|
|
1262
|
-
jsonResponse(res, skills);
|
|
1263
|
-
} catch (err) { jsonResponse(res, { error: true, message: err.message }, 500); }
|
|
1264
|
-
return;
|
|
1265
|
-
}
|
|
1266
|
-
if (req.method === 'GET' && url.pathname.startsWith('/api/skills/') && url.pathname.endsWith('/content')) {
|
|
1267
|
-
const name = decodeURIComponent(url.pathname.slice('/api/skills/'.length, -'/content'.length));
|
|
1268
|
-
try {
|
|
1269
|
-
const targetProjectDir = await resolveRequestProjectDir(url.searchParams.get('projectDir'), currentProjectDir);
|
|
1270
|
-
const entries = await listSkillEntries({ scope: 'all', cwd: targetProjectDir });
|
|
1271
|
-
const skill = entries.find(s => s.name === name);
|
|
1272
|
-
if (!skill) { jsonResponse(res, { error: true, message: 'Skill not found' }, 404); return; }
|
|
1273
|
-
const content = await fs.readFile(skill.path, 'utf8');
|
|
1292
|
+
if (req.method === 'GET' && url.pathname.startsWith('/api/config/get/')) {
|
|
1293
|
+
const key = url.pathname.slice('/api/config/get/'.length);
|
|
1294
|
+
const value = await getConfigValue(key);
|
|
1295
|
+
jsonResponse(res, { key, value });
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// ── Memory management ──
|
|
1300
|
+
if (req.method === 'GET' && url.pathname === '/api/memory') {
|
|
1301
|
+
const scope = normalizeMemoryScope(url.searchParams.get('scope'));
|
|
1302
|
+
const query = String(url.searchParams.get('q') || '').trim();
|
|
1303
|
+
try {
|
|
1304
|
+
const projectDirs = await parseProjectDirsParam(url, currentProjectDir);
|
|
1305
|
+
const items = await listMemoriesForProjectDirs({
|
|
1306
|
+
scope,
|
|
1307
|
+
query,
|
|
1308
|
+
projectDirs,
|
|
1309
|
+
fallbackDir: currentProjectDir
|
|
1310
|
+
});
|
|
1311
|
+
jsonResponse(res, { scope, query, items });
|
|
1312
|
+
} catch (err) {
|
|
1313
|
+
jsonResponse(res, { error: true, message: err.message }, 500);
|
|
1314
|
+
}
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
if (req.method === 'DELETE' && url.pathname.startsWith('/api/memory/')) {
|
|
1318
|
+
const id = decodeURIComponent(url.pathname.slice('/api/memory/'.length));
|
|
1319
|
+
const scope = normalizeMemoryScope(url.searchParams.get('scope'));
|
|
1320
|
+
if (!id) { jsonResponse(res, { error: true, message: 'Missing memory id' }, 400); return; }
|
|
1321
|
+
try {
|
|
1322
|
+
const workspaceRoot = scope === 'project'
|
|
1323
|
+
? await resolveRequestProjectDir(url.searchParams.get('projectDir'), currentProjectDir)
|
|
1324
|
+
: currentProjectDir;
|
|
1325
|
+
const result = await forgetMemory({ scope, id, workspaceRoot });
|
|
1326
|
+
jsonResponse(res, { ok: true, scope, ...result });
|
|
1327
|
+
} catch (err) {
|
|
1328
|
+
jsonResponse(res, { error: true, message: err.message }, 500);
|
|
1329
|
+
}
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// ── Skills management ──
|
|
1334
|
+
if (req.method === 'GET' && url.pathname === '/api/skills') {
|
|
1335
|
+
try {
|
|
1336
|
+
const projectDirs = await parseProjectDirsParam(url, currentProjectDir);
|
|
1337
|
+
const skills = await listSkillsForProjectDirs(projectDirs, currentProjectDir);
|
|
1338
|
+
jsonResponse(res, skills);
|
|
1339
|
+
} catch (err) { jsonResponse(res, { error: true, message: err.message }, 500); }
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
if (req.method === 'GET' && url.pathname.startsWith('/api/skills/') && url.pathname.endsWith('/content')) {
|
|
1343
|
+
const name = decodeURIComponent(url.pathname.slice('/api/skills/'.length, -'/content'.length));
|
|
1344
|
+
try {
|
|
1345
|
+
const targetProjectDir = await resolveRequestProjectDir(url.searchParams.get('projectDir'), currentProjectDir);
|
|
1346
|
+
const entries = await listSkillEntries({ scope: 'all', cwd: targetProjectDir });
|
|
1347
|
+
const skill = entries.find(s => s.name === name);
|
|
1348
|
+
if (!skill) { jsonResponse(res, { error: true, message: 'Skill not found' }, 404); return; }
|
|
1349
|
+
const content = await fs.readFile(skill.path, 'utf8');
|
|
1274
1350
|
jsonResponse(res, { name: skill.name, content, scope: skill.scope });
|
|
1275
1351
|
} catch (err) { jsonResponse(res, { error: true, message: err.message }, 500); }
|
|
1276
1352
|
return;
|
|
@@ -1327,40 +1403,40 @@ async function main() {
|
|
|
1327
1403
|
} catch (err) { jsonResponse(res, { error: true, message: err.message }, 500); }
|
|
1328
1404
|
return;
|
|
1329
1405
|
}
|
|
1330
|
-
if (req.method === 'PUT' && url.pathname.startsWith('/api/skills/') && url.pathname.endsWith('/content')) {
|
|
1331
|
-
const name = decodeURIComponent(url.pathname.slice('/api/skills/'.length, -'/content'.length));
|
|
1332
|
-
const { content, projectDir } = await readBody(req);
|
|
1333
|
-
if (!content) { jsonResponse(res, { error: true, message: 'Missing content' }, 400); return; }
|
|
1334
|
-
try {
|
|
1335
|
-
const targetProjectDir = await resolveRequestProjectDir(projectDir, currentProjectDir);
|
|
1336
|
-
const entries = await listSkillEntries({ scope: 'all', cwd: targetProjectDir });
|
|
1337
|
-
const skill = entries.find(s => s.name === name);
|
|
1338
|
-
if (!skill) { jsonResponse(res, { error: true, message: 'Skill not found' }, 404); return; }
|
|
1339
|
-
if (skill.scope === 'builtin') { jsonResponse(res, { error: true, message: 'Cannot edit builtin skill' }, 403); return; }
|
|
1406
|
+
if (req.method === 'PUT' && url.pathname.startsWith('/api/skills/') && url.pathname.endsWith('/content')) {
|
|
1407
|
+
const name = decodeURIComponent(url.pathname.slice('/api/skills/'.length, -'/content'.length));
|
|
1408
|
+
const { content, projectDir } = await readBody(req);
|
|
1409
|
+
if (!content) { jsonResponse(res, { error: true, message: 'Missing content' }, 400); return; }
|
|
1410
|
+
try {
|
|
1411
|
+
const targetProjectDir = await resolveRequestProjectDir(projectDir, currentProjectDir);
|
|
1412
|
+
const entries = await listSkillEntries({ scope: 'all', cwd: targetProjectDir });
|
|
1413
|
+
const skill = entries.find(s => s.name === name);
|
|
1414
|
+
if (!skill) { jsonResponse(res, { error: true, message: 'Skill not found' }, 404); return; }
|
|
1415
|
+
if (skill.scope === 'builtin') { jsonResponse(res, { error: true, message: 'Cannot edit builtin skill' }, 403); return; }
|
|
1340
1416
|
await fs.writeFile(skill.path, content, 'utf8');
|
|
1341
1417
|
await bridge.reloadCommandsAndSkills();
|
|
1342
1418
|
jsonResponse(res, { ok: true });
|
|
1343
1419
|
} catch (err) { jsonResponse(res, { error: true, message: err.message }, 500); }
|
|
1344
1420
|
return;
|
|
1345
1421
|
}
|
|
1346
|
-
if (req.method === 'DELETE' && url.pathname.startsWith('/api/skills/')) {
|
|
1347
|
-
const name = decodeURIComponent(url.pathname.slice('/api/skills/'.length));
|
|
1348
|
-
try {
|
|
1349
|
-
const targetProjectDir = await resolveRequestProjectDir(url.searchParams.get('projectDir'), currentProjectDir);
|
|
1350
|
-
const entries = await listSkillEntries({ scope: 'all', cwd: targetProjectDir });
|
|
1351
|
-
const skill = entries.find(s => s.name === name);
|
|
1352
|
-
if (!skill) { jsonResponse(res, { error: true, message: 'Skill not found' }, 404); return; }
|
|
1353
|
-
if (skill.scope === 'builtin') { jsonResponse(res, { error: true, message: 'Cannot delete builtin skill' }, 403); return; }
|
|
1422
|
+
if (req.method === 'DELETE' && url.pathname.startsWith('/api/skills/')) {
|
|
1423
|
+
const name = decodeURIComponent(url.pathname.slice('/api/skills/'.length));
|
|
1424
|
+
try {
|
|
1425
|
+
const targetProjectDir = await resolveRequestProjectDir(url.searchParams.get('projectDir'), currentProjectDir);
|
|
1426
|
+
const entries = await listSkillEntries({ scope: 'all', cwd: targetProjectDir });
|
|
1427
|
+
const skill = entries.find(s => s.name === name);
|
|
1428
|
+
if (!skill) { jsonResponse(res, { error: true, message: 'Skill not found' }, 404); return; }
|
|
1429
|
+
if (skill.scope === 'builtin') { jsonResponse(res, { error: true, message: 'Cannot delete builtin skill' }, 403); return; }
|
|
1354
1430
|
const dir = path.dirname(skill.path);
|
|
1355
1431
|
await fs.rm(dir, { recursive: true, force: true });
|
|
1356
1432
|
const registry = await readSkillRegistry();
|
|
1357
1433
|
registry.skills = (registry.skills || []).filter(s => s.name !== name);
|
|
1358
1434
|
await writeSkillRegistry(undefined, registry);
|
|
1359
|
-
const catalog = await readProjectSkillCatalog(targetProjectDir);
|
|
1360
|
-
if (catalog.skills?.[name]) {
|
|
1361
|
-
delete catalog.skills[name];
|
|
1362
|
-
await writeProjectSkillCatalog(targetProjectDir, catalog);
|
|
1363
|
-
}
|
|
1435
|
+
const catalog = await readProjectSkillCatalog(targetProjectDir);
|
|
1436
|
+
if (catalog.skills?.[name]) {
|
|
1437
|
+
delete catalog.skills[name];
|
|
1438
|
+
await writeProjectSkillCatalog(targetProjectDir, catalog);
|
|
1439
|
+
}
|
|
1364
1440
|
await deleteSkillCatalogMetadata(getSkillsDir(), name);
|
|
1365
1441
|
const config = await loadConfig();
|
|
1366
1442
|
if (config.skills?.enabled) delete config.skills.enabled[name];
|
|
@@ -1370,13 +1446,13 @@ async function main() {
|
|
|
1370
1446
|
} catch (err) { jsonResponse(res, { error: true, message: err.message }, 500); }
|
|
1371
1447
|
return;
|
|
1372
1448
|
}
|
|
1373
|
-
if (req.method === 'PUT' && url.pathname.startsWith('/api/skills/') && url.pathname.endsWith('/metadata')) {
|
|
1374
|
-
const name = decodeURIComponent(url.pathname.slice('/api/skills/'.length, -'/metadata'.length));
|
|
1375
|
-
const body = await readBody(req);
|
|
1376
|
-
try {
|
|
1377
|
-
const targetProjectDir = await resolveRequestProjectDir(body?.projectDir, currentProjectDir);
|
|
1378
|
-
const entries = await listSkillEntries({ scope: 'all', cwd: targetProjectDir });
|
|
1379
|
-
const skill = entries.find(s => s.name === name);
|
|
1449
|
+
if (req.method === 'PUT' && url.pathname.startsWith('/api/skills/') && url.pathname.endsWith('/metadata')) {
|
|
1450
|
+
const name = decodeURIComponent(url.pathname.slice('/api/skills/'.length, -'/metadata'.length));
|
|
1451
|
+
const body = await readBody(req);
|
|
1452
|
+
try {
|
|
1453
|
+
const targetProjectDir = await resolveRequestProjectDir(body?.projectDir, currentProjectDir);
|
|
1454
|
+
const entries = await listSkillEntries({ scope: 'all', cwd: targetProjectDir });
|
|
1455
|
+
const skill = entries.find(s => s.name === name);
|
|
1380
1456
|
if (!skill) { jsonResponse(res, { error: true, message: 'Skill not found' }, 404); return; }
|
|
1381
1457
|
if (skill.scope === 'builtin' && body?.scope && body.scope !== 'builtin') {
|
|
1382
1458
|
jsonResponse(res, { error: true, message: 'Cannot move builtin skill' }, 403);
|
|
@@ -1388,15 +1464,15 @@ async function main() {
|
|
|
1388
1464
|
let nextScope = skill.scope;
|
|
1389
1465
|
|
|
1390
1466
|
if (skill.scope !== 'builtin' && requestedScope !== skill.scope) {
|
|
1391
|
-
const sourceDir = path.dirname(skill.path);
|
|
1392
|
-
const targetBaseDir = skillBaseDirForScope(requestedScope, targetProjectDir);
|
|
1393
|
-
const targetDir = path.join(targetBaseDir, name);
|
|
1467
|
+
const sourceDir = path.dirname(skill.path);
|
|
1468
|
+
const targetBaseDir = skillBaseDirForScope(requestedScope, targetProjectDir);
|
|
1469
|
+
const targetDir = path.join(targetBaseDir, name);
|
|
1394
1470
|
await fs.rm(targetDir, { recursive: true, force: true });
|
|
1395
1471
|
await fs.mkdir(path.dirname(targetDir), { recursive: true });
|
|
1396
1472
|
await fs.cp(sourceDir, targetDir, { recursive: true, force: true });
|
|
1397
1473
|
await fs.rm(sourceDir, { recursive: true, force: true });
|
|
1398
|
-
if (requestedScope === 'global') {
|
|
1399
|
-
await deleteSkillCatalogMetadata(getProjectSkillsDir(targetProjectDir), name);
|
|
1474
|
+
if (requestedScope === 'global') {
|
|
1475
|
+
await deleteSkillCatalogMetadata(getProjectSkillsDir(targetProjectDir), name);
|
|
1400
1476
|
await upsertSkillRegistryEntry(undefined, {
|
|
1401
1477
|
name,
|
|
1402
1478
|
version: skill.version || '0.0.0',
|
|
@@ -1423,13 +1499,13 @@ async function main() {
|
|
|
1423
1499
|
...(metadataPatch.enabled !== undefined ? { enabled: metadataPatch.enabled } : {})
|
|
1424
1500
|
});
|
|
1425
1501
|
metadata = await upsertSkillCatalogMetadata(getSkillsDir(), name, body || {});
|
|
1426
|
-
} else if (nextScope === 'project') {
|
|
1427
|
-
metadata = await upsertProjectSkillMetadata(targetProjectDir, name, body || {});
|
|
1428
|
-
} else if (skill.scope !== 'builtin') {
|
|
1429
|
-
metadata = await upsertProjectSkillMetadata(targetProjectDir, name, body || {});
|
|
1430
|
-
} else {
|
|
1431
|
-
metadata = await upsertProjectSkillMetadata(targetProjectDir, name, body || {});
|
|
1432
|
-
}
|
|
1502
|
+
} else if (nextScope === 'project') {
|
|
1503
|
+
metadata = await upsertProjectSkillMetadata(targetProjectDir, name, body || {});
|
|
1504
|
+
} else if (skill.scope !== 'builtin') {
|
|
1505
|
+
metadata = await upsertProjectSkillMetadata(targetProjectDir, name, body || {});
|
|
1506
|
+
} else {
|
|
1507
|
+
metadata = await upsertProjectSkillMetadata(targetProjectDir, name, body || {});
|
|
1508
|
+
}
|
|
1433
1509
|
if (skill.scope !== 'builtin' && body?.enabled !== undefined) {
|
|
1434
1510
|
const config = await loadConfig();
|
|
1435
1511
|
config.skills = config.skills || {};
|
|
@@ -1445,16 +1521,16 @@ async function main() {
|
|
|
1445
1521
|
} catch (err) { jsonResponse(res, { error: true, message: err.message }, 500); }
|
|
1446
1522
|
return;
|
|
1447
1523
|
}
|
|
1448
|
-
if (req.method === 'POST' && url.pathname.startsWith('/api/skills/') && url.pathname.endsWith('/toggle')) {
|
|
1449
|
-
const name = decodeURIComponent(url.pathname.slice('/api/skills/'.length, -'/toggle'.length));
|
|
1450
|
-
const { enabled, projectDir } = await readBody(req);
|
|
1451
|
-
try {
|
|
1452
|
-
const targetProjectDir = await resolveRequestProjectDir(projectDir, currentProjectDir);
|
|
1453
|
-
const entries = await listSkillEntries({ scope: 'all', cwd: targetProjectDir });
|
|
1454
|
-
const skill = entries.find(s => s.name === name);
|
|
1455
|
-
if (!skill) { jsonResponse(res, { error: true, message: 'Skill not found' }, 404); return; }
|
|
1456
|
-
if (skill.scope === 'builtin') {
|
|
1457
|
-
const metadata = await upsertProjectSkillMetadata(targetProjectDir, name, { enabled });
|
|
1524
|
+
if (req.method === 'POST' && url.pathname.startsWith('/api/skills/') && url.pathname.endsWith('/toggle')) {
|
|
1525
|
+
const name = decodeURIComponent(url.pathname.slice('/api/skills/'.length, -'/toggle'.length));
|
|
1526
|
+
const { enabled, projectDir } = await readBody(req);
|
|
1527
|
+
try {
|
|
1528
|
+
const targetProjectDir = await resolveRequestProjectDir(projectDir, currentProjectDir);
|
|
1529
|
+
const entries = await listSkillEntries({ scope: 'all', cwd: targetProjectDir });
|
|
1530
|
+
const skill = entries.find(s => s.name === name);
|
|
1531
|
+
if (!skill) { jsonResponse(res, { error: true, message: 'Skill not found' }, 404); return; }
|
|
1532
|
+
if (skill.scope === 'builtin') {
|
|
1533
|
+
const metadata = await upsertProjectSkillMetadata(targetProjectDir, name, { enabled });
|
|
1458
1534
|
await bridge.reloadCommandsAndSkills();
|
|
1459
1535
|
jsonResponse(res, { ok: true, name, metadata });
|
|
1460
1536
|
return;
|