@piecemaker-legal/piecemaker 2.0.17 → 2.0.19
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/dist/assets/{FindReplaceDialog-KU5TEIBJ-B4N6W1_z.js → FindReplaceDialog-KU5TEIBJ-D-E_iQrz.js} +1 -1
- package/dist/assets/{FootnotePropertiesDialog-W3YHM4BV-D_X_JkAZ.js → FootnotePropertiesDialog-W3YHM4BV-aF_8ncE9.js} +1 -1
- package/dist/assets/{HyperlinkDialog-ZYH7ZHRF-EbyukgDx.js → HyperlinkDialog-ZYH7ZHRF-BvhTCTt_.js} +4 -4
- package/dist/assets/{ImagePositionDialog-HEWYR6Q3-D9jEaxlE.js → ImagePositionDialog-HEWYR6Q3-CC7jJcbZ.js} +1 -1
- package/dist/assets/{ImagePropertiesDialog-RAB47C6M-DvA1kZnM.js → ImagePropertiesDialog-RAB47C6M-C5fs5OZU.js} +1 -1
- package/dist/assets/{PageSetupDialog-AIGIXO3E-ChfsKF43.js → PageSetupDialog-AIGIXO3E-DhHTvZ6Q.js} +1 -1
- package/dist/assets/{SplitCellDialog-IFBKYCD6-uM26FNXt.js → SplitCellDialog-IFBKYCD6-BoFyEVsb.js} +1 -1
- package/dist/assets/{TablePropertiesDialog-TMLKGOOW-By6EQI5w.js → TablePropertiesDialog-TMLKGOOW-CBb7GEtl.js} +1 -1
- package/dist/assets/{WatermarkDialog-KRL7WMIS-Bss9F6Ej.js → WatermarkDialog-KRL7WMIS-BJbsMnmA.js} +1 -1
- package/dist/assets/{abnfDiagram-VCTEODGH-yLjNPHY7.js → abnfDiagram-VCTEODGH-C6ojlqY3.js} +1 -1
- package/dist/assets/{arc-CMQiMSh4.js → arc-BoDdi4D9.js} +1 -1
- package/dist/assets/{architectureDiagram-5GKGNRK7-Cyna8IJZ.js → architectureDiagram-5GKGNRK7-CWIahr5B.js} +1 -1
- package/dist/assets/{blockDiagram-I7D4REHJ-DHAdjRYY.js → blockDiagram-I7D4REHJ-CLEp2VLU.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-qqZ0w4LC.js → c4Diagram-7LVT6UL2-BnPyO1ST.js} +1 -1
- package/dist/assets/channel-DV0VwszZ.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-NS0halN6.js → chunk-2Q5K7J3B-C7HwgL9V.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-BF6-6Cah.js → chunk-5VM5RSS4-DXEfMQFg.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-Dnj2jFRB.js → chunk-F27PBJKO-L8IW1AJ2.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-CmRgl1EE.js → chunk-IMKFNOWR-M0u3FR4R.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BwC19UFc.js → chunk-JWPE2WC7-BTrrWfya.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-C1YWVD4-.js → chunk-POPQ4Y6H-DKzOkm9u.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-D7XQTPRq.js → chunk-SVP7TREG-ChWxgGeB.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-BkmTGk1O.js → chunk-TICWLB2K-FU0TG8_V.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-CYW7YgXL.js → chunk-XXDRQBXY-D--n_LHM.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-QmelLege.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-QmelLege.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-BSktt6pW.js → cose-bilkent-JH36ORCC-VcyzT4Am.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-BNMRWXNG.js → cynefin-OW5HDTMX-CF_CzN2W.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-Dk59YHuO.js → cynefinDiagram-5FMLGOSQ-tYmbUXoj.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-DFhWeh0x.js → dagre-GXQ25YYZ-Dl-3sgkS.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-BH_8x-q5.js → diagram-S7CK7UJ4-DyYBtOKJ.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-DmxmlXg5.js → diagram-UQ7AKVKN-BfibAHRJ.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-DpG29cD4.js → diagram-VSXAHHWV-BvuChn2K.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-CvvYXTie.js → diagram-VX7I27RA-ZSTD84JR.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-xOXaaZqs.js → diagram-Z3DM3KII-CYWcW_j4.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-CtfFGqJB.js → ebnfDiagram-PWID7BFC-CKssYpWu.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-AoCjoHUY.js → erDiagram-RLTQ6QDP-H6Rihy54.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-Bl9_mq95.js → flowDiagram-HODETNUW-BYcs_7fG.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-DREJQ2BY.js → ganttDiagram-EL5Y4UJY-Dt0zBRmP.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-C1q5-iep.js → gitGraphDiagram-WWUBYQGX-DJ4ZVb5_.js} +1 -1
- package/dist/assets/{index-RKNvOXcq.js → index-skgP2UD3.js} +4 -4
- package/dist/assets/{infoDiagram-27XIBGKW-Cz2KUKWU.js → infoDiagram-27XIBGKW-DZmrJj_e.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-BU6SdWsw.js → ishikawaDiagram-5VMMS53U-zTPWVhqW.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-CMMoiPf0.js → journeyDiagram-3NMN7TZE-CFxtAX_q.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-KpKnC3dd.js → kanban-definition-UXKFOSKX-BIBQGYrN.js} +1 -1
- package/dist/assets/{layout-6C8d-KNC.js → layout-5xNw8gsu.js} +1 -1
- package/dist/assets/{linear-CUINCO59.js → linear-CW6Efbjk.js} +1 -1
- package/dist/assets/{mermaid.core-ClgFTKfs.js → mermaid.core-CV1UELnp.js} +6 -6
- package/dist/assets/{mindmap-definition-YA3MSWOX-oywqmpy6.js → mindmap-definition-YA3MSWOX-zpWSRS8g.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-Uk65qoVL.js → pegDiagram-XKGWAZYB-4u0O4rTZ.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-XxQhMRKu.js → pieDiagram-E7YTZNPT-CXmymaRq.js} +1 -1
- package/dist/assets/{quadrantDiagram-AXDQQJYC-Djngo9IG.js → quadrantDiagram-AXDQQJYC-DpCRdfYk.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-C4I8SNPI.js → railroadDiagram-O6MQD6OU-DisvzZ3q.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-D62oiVFl.js → requirementDiagram-BXWQKSXE-DvU7Z8z5.js} +1 -1
- package/dist/assets/{sankeyDiagram-P5KCCOFB-HZuSub58.js → sankeyDiagram-P5KCCOFB-Df5nogtS.js} +1 -1
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-B8_t7SDx.js → sequenceDiagram-WJ2MYXX4-B5BKf6O8.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-D2Lk0zlm.js → sizeCapture-INFHLROL-1EZabZaY.js} +1 -1
- package/dist/assets/{stateDiagram-D77RDMKH-DC3NnV2S.js → stateDiagram-D77RDMKH-DCSKdcyr.js} +1 -1
- package/dist/assets/{stateDiagram-v2-MP3YSRHH-DSsQOGCi.js → stateDiagram-v2-MP3YSRHH-Db5HOoa1.js} +1 -1
- package/dist/assets/{swimlanes-42K2YHIH-6RzklsF9.js → swimlanes-42K2YHIH-CmrUx1kp.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-B6BaxAGn.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-Beuacf_x.js → timeline-definition-24CTP7MA-D1e5FRLS.js} +1 -1
- package/dist/assets/{vennDiagram-4TSXK5OY-CMqthbQr.js → vennDiagram-4TSXK5OY-DbclmB6P.js} +1 -1
- package/dist/assets/{wardleyDiagram-VM6X3IG4-DKrdN08q.js → wardleyDiagram-VM6X3IG4-BL3cDBgt.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-BtMW2Qlh.js → xychartDiagram-S5SC5T6Z-BnyVnEkb.js} +1 -1
- package/dist/index.html +1 -1
- package/dist/sw.js +1 -1
- package/dist-server/server/piecemaker/library/connector-installation.js +68 -17
- package/dist-server/server/piecemaker/library/connector-installation.js.map +1 -1
- package/dist-server/server/piecemaker/library/global-state.js +60 -0
- package/dist-server/server/piecemaker/library/global-state.js.map +1 -0
- package/dist-server/server/piecemaker/library/index.js +10 -0
- package/dist-server/server/piecemaker/library/index.js.map +1 -1
- package/dist-server/server/piecemaker/library/marketplace.js +61 -42
- package/dist-server/server/piecemaker/library/marketplace.js.map +1 -1
- package/dist-server/server/piecemaker/library/migrate.js +0 -35
- package/dist-server/server/piecemaker/library/migrate.js.map +1 -1
- package/dist-server/server/piecemaker/library/routes.js +6 -2
- package/dist-server/server/piecemaker/library/routes.js.map +1 -1
- package/dist-server/server/piecemaker/library/store.js +95 -67
- package/dist-server/server/piecemaker/library/store.js.map +1 -1
- package/dist-server/server/piecemaker/library/workspace-installation.js +172 -87
- package/dist-server/server/piecemaker/library/workspace-installation.js.map +1 -1
- package/package.json +1 -1
- package/server/piecemaker/library/connector-installation.ts +65 -16
- package/server/piecemaker/library/global-state.ts +55 -0
- package/server/piecemaker/library/index.ts +7 -0
- package/server/piecemaker/library/marketplace.ts +52 -48
- package/server/piecemaker/library/migrate.ts +0 -35
- package/server/piecemaker/library/routes.ts +9 -2
- package/server/piecemaker/library/store.ts +94 -57
- package/server/piecemaker/library/workspace-installation.ts +180 -77
- package/dist/assets/channel-B3AYuE-l.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-CE2Zkr7h.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-CE2Zkr7h.js +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-ZU4NRSFy.js +0 -8
- package/dist-server/server/piecemaker/library/migrate-cli.js +0 -13
- package/dist-server/server/piecemaker/library/migrate-cli.js.map +0 -1
- package/server/piecemaker/library/migrate-cli.ts +0 -11
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { listUserProviderConnectors } from './provider-connectors.js';
|
|
5
|
+
|
|
6
|
+
const GLOBAL_DIRECTORIES = [
|
|
7
|
+
['.claude/skills', 'Claude'],
|
|
8
|
+
['.claude/agents', 'Claude'],
|
|
9
|
+
['.codex/skills', 'Codex'],
|
|
10
|
+
['.codex/agents', 'Codex'],
|
|
11
|
+
['.agents/skills', 'Codex'],
|
|
12
|
+
['.cursor/skills', 'Cursor'],
|
|
13
|
+
['.cursor/agents', 'Cursor'],
|
|
14
|
+
['.config/opencode/skills', 'OpenCode'],
|
|
15
|
+
['.config/opencode/agent', 'OpenCode'],
|
|
16
|
+
['.config/opencode/agents', 'OpenCode'],
|
|
17
|
+
['.grok/skills', 'Grok'],
|
|
18
|
+
['.grok/agents', 'Grok'],
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
const GLOBAL_CONNECTOR_FILES = [
|
|
22
|
+
['.claude.json', 'Claude'],
|
|
23
|
+
['.codex/config.toml', 'Codex'],
|
|
24
|
+
['.cursor/mcp.json', 'Cursor'],
|
|
25
|
+
['.config/opencode/opencode.json', 'OpenCode'],
|
|
26
|
+
['.config/opencode/opencode.jsonc', 'OpenCode'],
|
|
27
|
+
['.grok/config.toml', 'Grok'],
|
|
28
|
+
] as const;
|
|
29
|
+
|
|
30
|
+
export function globalProviderIndex(userHome: string) {
|
|
31
|
+
const providers = new Map<string, Set<string>>();
|
|
32
|
+
const record = (source: string, provider: string) => {
|
|
33
|
+
providers.set(source, (providers.get(source) ?? new Set()).add(provider));
|
|
34
|
+
};
|
|
35
|
+
for (const [directory, provider] of GLOBAL_DIRECTORIES) {
|
|
36
|
+
const root = path.join(userHome, directory);
|
|
37
|
+
let children: string[];
|
|
38
|
+
try { children = fs.readdirSync(root); } catch { continue; }
|
|
39
|
+
for (const child of children) {
|
|
40
|
+
for (const candidate of [path.join(root, child), path.join(root, child, 'SKILL.md')]) {
|
|
41
|
+
try {
|
|
42
|
+
if (!fs.statSync(candidate).isFile()) continue;
|
|
43
|
+
record(candidate, provider);
|
|
44
|
+
record(fs.realpathSync(candidate), provider);
|
|
45
|
+
} catch {}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const connector of listUserProviderConnectors(userHome)) {
|
|
50
|
+
const file = connector.source.slice(0, connector.source.lastIndexOf('#'));
|
|
51
|
+
const match = GLOBAL_CONNECTOR_FILES.find(([name]) => path.join(userHome, name) === file);
|
|
52
|
+
if (match) record(connector.source, match[1]);
|
|
53
|
+
}
|
|
54
|
+
return (sources: string[]) => [...new Set(sources.flatMap((source) => [...(providers.get(source) ?? [])]))].sort();
|
|
55
|
+
}
|
|
@@ -7,6 +7,7 @@ import express from 'express';
|
|
|
7
7
|
import { createLibraryStore } from './store.js';
|
|
8
8
|
import { createLibraryRouter } from './routes.js';
|
|
9
9
|
import { createLibraryMarketplaceRouter, scanInstalledLibraryCollections } from './marketplace.js';
|
|
10
|
+
import { importLibraryDirectory } from './migrate.js';
|
|
10
11
|
import { scanAndPersistLibraryProviderAgents } from './provider-agents.js';
|
|
11
12
|
import { scanAndPersistLibraryProviderConnectors } from './provider-connectors.js';
|
|
12
13
|
import { scanAndPersistLibraryProviderSkills } from './provider-skills.js';
|
|
@@ -22,6 +23,12 @@ export async function openLibrary(home: string, applicationRoot: string) {
|
|
|
22
23
|
try { scanAndPersistLibraryProviderConnectors(store, os.homedir()); } catch {}
|
|
23
24
|
try { scanInstalledLibraryCollections(store, os.homedir()); } catch {}
|
|
24
25
|
try { fs.unlinkSync(path.join(store.directory, 'connection.json')); } catch {}
|
|
26
|
+
const bundled = path.join(applicationRoot, 'server/piecemaker/vendor/piecemaker-plugin');
|
|
27
|
+
const defaults: string[] = [];
|
|
28
|
+
for (const [folder, kind] of [['skills', 'skill'], ['agents', 'agent']] as const) {
|
|
29
|
+
try { defaults.push(...importLibraryDirectory(store, path.join(bundled, folder), kind).map((entry) => entry.id)); } catch {}
|
|
30
|
+
}
|
|
31
|
+
store.setDefaultEntries(defaults);
|
|
25
32
|
const router = express.Router();
|
|
26
33
|
router.use((req, res, next) => {
|
|
27
34
|
const length = Number(req.headers['content-length'] || 0);
|
|
@@ -6,7 +6,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
6
6
|
import express from 'express';
|
|
7
7
|
|
|
8
8
|
import type { createLibraryStore } from './store.js';
|
|
9
|
-
import { normalizeProviderConnector } from './connector-installation.js';
|
|
9
|
+
import { normalizeProviderConnector, setWorkspaceClaudePlugin, workspaceClaudePlugins } from './connector-installation.js';
|
|
10
10
|
import { importLibraryDirectory } from './migrate.js';
|
|
11
11
|
import { scanAndPersistLibraryProviderAgents } from './provider-agents.js';
|
|
12
12
|
import { scanAndPersistLibraryProviderConnectors } from './provider-connectors.js';
|
|
@@ -45,7 +45,16 @@ function marketplacePackageKinds(userHome: string, marketplaceName: string, plug
|
|
|
45
45
|
return [...kinds];
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
function
|
|
48
|
+
function userClaudePlugins(userHome: string) {
|
|
49
|
+
const settingsFilename = path.join(userHome, '.claude/settings.json');
|
|
50
|
+
if (!fs.existsSync(settingsFilename)) return {};
|
|
51
|
+
const enabledPlugins = JSON.parse(fs.readFileSync(settingsFilename, 'utf8')).enabledPlugins;
|
|
52
|
+
return enabledPlugins && typeof enabledPlugins === 'object' && !Array.isArray(enabledPlugins)
|
|
53
|
+
? enabledPlugins as Record<string, unknown>
|
|
54
|
+
: {};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function setNativeClaudePlugin(userHome: string, id: string, enabled: boolean) {
|
|
49
58
|
const settingsFilename = path.join(userHome, '.claude/settings.json');
|
|
50
59
|
const settings = fs.existsSync(settingsFilename)
|
|
51
60
|
? JSON.parse(fs.readFileSync(settingsFilename, 'utf8'))
|
|
@@ -53,8 +62,8 @@ function disableNativeClaudePlugin(userHome: string, id: string) {
|
|
|
53
62
|
const enabledPlugins = settings.enabledPlugins && typeof settings.enabledPlugins === 'object' && !Array.isArray(settings.enabledPlugins)
|
|
54
63
|
? settings.enabledPlugins
|
|
55
64
|
: {};
|
|
56
|
-
if (enabledPlugins[id] ===
|
|
57
|
-
settings.enabledPlugins = { ...enabledPlugins, [id]:
|
|
65
|
+
if (enabledPlugins[id] === enabled) return;
|
|
66
|
+
settings.enabledPlugins = { ...enabledPlugins, [id]: enabled };
|
|
58
67
|
fs.mkdirSync(path.dirname(settingsFilename), { recursive: true, mode: 0o700 });
|
|
59
68
|
const temporary = `${settingsFilename}.library.tmp`;
|
|
60
69
|
fs.writeFileSync(temporary, JSON.stringify(settings, null, 2), { mode: 0o600 });
|
|
@@ -129,39 +138,6 @@ export function scanInstalledLibraryCollections(store: ReturnType<typeof createL
|
|
|
129
138
|
}
|
|
130
139
|
}
|
|
131
140
|
|
|
132
|
-
function readInstalledClaudePlugins(userHome: string) {
|
|
133
|
-
const filename = path.join(userHome, '.claude/plugins/installed_plugins.json');
|
|
134
|
-
if (!fs.existsSync(filename)) return {};
|
|
135
|
-
const config = JSON.parse(fs.readFileSync(filename, 'utf8'));
|
|
136
|
-
return config.plugins && typeof config.plugins === 'object' && !Array.isArray(config.plugins)
|
|
137
|
-
? config.plugins as Record<string, unknown>
|
|
138
|
-
: {};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function synchronizeInstalledPluginActivation(
|
|
142
|
-
store: ReturnType<typeof createLibraryStore>,
|
|
143
|
-
userHome: string,
|
|
144
|
-
workspacePath: string,
|
|
145
|
-
) {
|
|
146
|
-
const settingsFilename = path.join(userHome, '.claude/settings.json');
|
|
147
|
-
if (!fs.existsSync(settingsFilename)) return store.listCollections(workspacePath);
|
|
148
|
-
const settings = JSON.parse(fs.readFileSync(settingsFilename, 'utf8'));
|
|
149
|
-
const enabledPlugins = settings.enabledPlugins && typeof settings.enabledPlugins === 'object' && !Array.isArray(settings.enabledPlugins)
|
|
150
|
-
? settings.enabledPlugins as Record<string, unknown>
|
|
151
|
-
: {};
|
|
152
|
-
const installedPlugins = readInstalledClaudePlugins(userHome);
|
|
153
|
-
const collections = store.listCollections(workspacePath);
|
|
154
|
-
|
|
155
|
-
for (const id of Object.keys(installedPlugins)) {
|
|
156
|
-
const collection = collections.find((entry) => entry.id === id);
|
|
157
|
-
const enabled = enabledPlugins[id];
|
|
158
|
-
if (!collection || collection.componentCount === 0 || typeof enabled !== 'boolean' || collection.enabled === enabled) continue;
|
|
159
|
-
store.setCollectionEnabled(workspacePath, id, enabled);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return store.listCollections(workspacePath);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
141
|
export function createLibraryMarketplaceRouter(store: ReturnType<typeof createLibraryStore>, applicationRoot: string, userHome: string) {
|
|
166
142
|
const require = createRequire(import.meta.url);
|
|
167
143
|
const { listMarketplaceConnectors, registerOfficialMarketplace } = require(path.join(applicationRoot, 'server/piecemaker/vendor/websocket-server/admin-routes.cjs'));
|
|
@@ -181,6 +157,19 @@ export function createLibraryMarketplaceRouter(store: ReturnType<typeof createLi
|
|
|
181
157
|
return fs.existsSync(filename) ? JSON.parse(fs.readFileSync(filename, 'utf8')).plugins || {} : {};
|
|
182
158
|
};
|
|
183
159
|
const importInstalledCollections = () => scanInstalledLibraryCollections(store, userHome);
|
|
160
|
+
const describeCollections = (workspacePath?: string) => {
|
|
161
|
+
const collections = store.listCollections(workspacePath);
|
|
162
|
+
const registry = installed();
|
|
163
|
+
const everywhere = userClaudePlugins(userHome);
|
|
164
|
+
const here = workspacePath ? workspaceClaudePlugins(fs.realpathSync(workspacePath)) : {};
|
|
165
|
+
return collections.map((collection) => {
|
|
166
|
+
const native = Boolean(registry[collection.id]);
|
|
167
|
+
if (!native) return { ...collection, native, global: false };
|
|
168
|
+
const global = everywhere[collection.id] === true;
|
|
169
|
+
const local = here[collection.id];
|
|
170
|
+
return { ...collection, native, global, partial: false, enabled: Boolean(workspacePath) && (typeof local === 'boolean' ? local : global) };
|
|
171
|
+
});
|
|
172
|
+
};
|
|
184
173
|
router.get('/plugin/marketplace', (req, res) => {
|
|
185
174
|
try {
|
|
186
175
|
const marketplace = scope(req.query.scope);
|
|
@@ -202,7 +191,7 @@ export function createLibraryMarketplaceRouter(store: ReturnType<typeof createLi
|
|
|
202
191
|
router.get('/plugins', (req, res) => {
|
|
203
192
|
try {
|
|
204
193
|
const workspacePath = typeof req.query.workspacePath === 'string' ? req.query.workspacePath : undefined;
|
|
205
|
-
res.json({ plugins:
|
|
194
|
+
res.json({ plugins: describeCollections(workspacePath) });
|
|
206
195
|
} catch (error) { res.status(400).json({ error: (error as Error).message }); }
|
|
207
196
|
});
|
|
208
197
|
router.post('/plugins/sync', async (req, res) => {
|
|
@@ -212,12 +201,7 @@ export function createLibraryMarketplaceRouter(store: ReturnType<typeof createLi
|
|
|
212
201
|
scanAndPersistLibraryProviderAgents(store, userHome);
|
|
213
202
|
scanAndPersistLibraryProviderConnectors(store, userHome);
|
|
214
203
|
scanInstalledLibraryCollections(store, userHome);
|
|
215
|
-
res.json({
|
|
216
|
-
ok: true,
|
|
217
|
-
plugins: workspacePath
|
|
218
|
-
? synchronizeInstalledPluginActivation(store, userHome, workspacePath)
|
|
219
|
-
: store.listCollections(),
|
|
220
|
-
});
|
|
204
|
+
res.json({ ok: true, plugins: describeCollections(workspacePath) });
|
|
221
205
|
} catch (error) { res.status(400).json({ error: (error as Error).message }); }
|
|
222
206
|
});
|
|
223
207
|
router.post('/plugins/scan', (_req, res) => {
|
|
@@ -237,8 +221,28 @@ export function createLibraryMarketplaceRouter(store: ReturnType<typeof createLi
|
|
|
237
221
|
catch (error) { res.status(409).json({ error: (error as Error).message }); }
|
|
238
222
|
});
|
|
239
223
|
router.put('/plugins/:id/activation', (req, res) => {
|
|
240
|
-
try {
|
|
241
|
-
|
|
224
|
+
try {
|
|
225
|
+
const id = String(req.params.id);
|
|
226
|
+
const workspacePath = req.body?.workspacePath;
|
|
227
|
+
const enabled = req.body?.enabled;
|
|
228
|
+
if (typeof workspacePath !== 'string') throw new Error('Dossier absolu requis.');
|
|
229
|
+
if (typeof enabled !== 'boolean') throw new Error('Activation booléenne requise.');
|
|
230
|
+
const collection = store.listCollections(workspacePath).find((entry) => entry.id === id);
|
|
231
|
+
const native = Boolean(installed()[id]);
|
|
232
|
+
if (!collection || (!native && !collection.componentCount)) throw new Error('Plugin introuvable.');
|
|
233
|
+
if (collection.componentCount) store.setCollectionEnabled(workspacePath, id, enabled, { skipClaude: native });
|
|
234
|
+
if (native) setWorkspaceClaudePlugin(fs.realpathSync(workspacePath), id, enabled ? true : userClaudePlugins(userHome)[id] === true ? false : null);
|
|
235
|
+
res.json(describeCollections(workspacePath).find((entry) => entry.id === id));
|
|
236
|
+
} catch (error) { res.status(400).json({ error: (error as Error).message }); }
|
|
237
|
+
});
|
|
238
|
+
router.put('/plugins/:id/global', (req, res) => {
|
|
239
|
+
try {
|
|
240
|
+
const id = String(req.params.id);
|
|
241
|
+
if (typeof req.body?.enabled !== 'boolean') throw new Error('Activation booléenne requise.');
|
|
242
|
+
if (!installed()[id]) throw new Error('Plugin absent de Claude Code.');
|
|
243
|
+
setNativeClaudePlugin(userHome, id, req.body.enabled);
|
|
244
|
+
res.json({ ok: true, global: req.body.enabled });
|
|
245
|
+
} catch (error) { res.status(400).json({ error: (error as Error).message }); }
|
|
242
246
|
});
|
|
243
247
|
router.post('/plugin/marketplace/register', (req, res) => {
|
|
244
248
|
try { res.json(registerOfficialMarketplace(run, scope(req.body?.scope))); }
|
|
@@ -252,10 +256,10 @@ export function createLibraryMarketplaceRouter(store: ReturnType<typeof createLi
|
|
|
252
256
|
if (installed()[id]) throw new Error('Déjà installé. Son activation se règle par dossier.');
|
|
253
257
|
const catalog = listMarketplaceConnectors(run, { marketplaceName: marketplace.name });
|
|
254
258
|
if (!catalog.plugins.some((plugin: { id: string }) => plugin.id === id)) throw new Error('Élément absent du catalogue.');
|
|
255
|
-
|
|
259
|
+
setNativeClaudePlugin(userHome, id, false);
|
|
256
260
|
let result;
|
|
257
261
|
try { result = run('claude', ['plugin', 'install', id, '--scope', 'user']); }
|
|
258
|
-
finally {
|
|
262
|
+
finally { setNativeClaudePlugin(userHome, id, false); }
|
|
259
263
|
if (!result.ok) throw new Error(result.output || 'Installation impossible.');
|
|
260
264
|
importInstalledCollections();
|
|
261
265
|
res.json({ ok: true, enabled: false });
|
|
@@ -19,38 +19,3 @@ export function importLibraryDirectory(store: ReturnType<typeof createLibrarySto
|
|
|
19
19
|
}
|
|
20
20
|
return imported;
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
export function migratePersonalLibrary(store: ReturnType<typeof createLibraryStore>, userHome: string, applicationRoot: string, withdraw = false) {
|
|
24
|
-
const imported = [];
|
|
25
|
-
for (const folder of ['assistant-workflows', 'tabular-review-workflows']) {
|
|
26
|
-
imported.push(...importLibraryDirectory(store, path.join(applicationRoot, 'server/piecemaker/vendor/mike-defaults-fr', folder), 'skill'));
|
|
27
|
-
}
|
|
28
|
-
for (const [directory, kind] of [
|
|
29
|
-
[path.join(userHome, '.claude', 'skills'), 'skill'],
|
|
30
|
-
[path.join(userHome, '.codex', 'skills'), 'skill'],
|
|
31
|
-
[path.join(userHome, '.agents', 'skills'), 'skill'],
|
|
32
|
-
[path.join(userHome, '.cursor', 'skills'), 'skill'],
|
|
33
|
-
[path.join(userHome, '.config', 'opencode', 'skills'), 'skill'],
|
|
34
|
-
[path.join(userHome, '.grok', 'skills'), 'skill'],
|
|
35
|
-
[path.join(userHome, '.claude', 'agents'), 'agent'],
|
|
36
|
-
[path.join(userHome, '.cursor', 'agents'), 'agent'],
|
|
37
|
-
[path.join(userHome, '.grok', 'agents'), 'agent'],
|
|
38
|
-
[path.join(userHome, '.config', 'opencode', 'agents'), 'agent'],
|
|
39
|
-
[path.join(userHome, '.config', 'opencode', 'agent'), 'agent'],
|
|
40
|
-
[path.join(userHome, '.piecemaker', 'library', 'skills'), 'skill'],
|
|
41
|
-
[path.join(userHome, '.piecemaker', 'library', 'agents'), 'agent'],
|
|
42
|
-
] as const) imported.push(...importLibraryDirectory(store, directory, kind));
|
|
43
|
-
if (withdraw) {
|
|
44
|
-
const candidates = imported.filter((entry) => ['.claude', '.codex', '.agents', '.cursor', '.grok', path.join('.config', 'opencode')].some((provider) => entry.source.startsWith(path.join(userHome, provider) + path.sep)));
|
|
45
|
-
const backup = path.join(store.directory, `migration-${Date.now()}`);
|
|
46
|
-
fs.mkdirSync(backup, { recursive: true, mode: 0o700 });
|
|
47
|
-
fs.writeFileSync(path.join(backup, 'manifest.json'), JSON.stringify(candidates, null, 2), { mode: 0o600 });
|
|
48
|
-
for (const entry of candidates.sort((a, b) => Number(b.linked) - Number(a.linked))) {
|
|
49
|
-
const destination = path.join(backup, path.relative(userHome, entry.source));
|
|
50
|
-
fs.mkdirSync(path.dirname(destination), { recursive: true, mode: 0o700 });
|
|
51
|
-
fs.renameSync(entry.source, destination);
|
|
52
|
-
}
|
|
53
|
-
fs.writeFileSync(path.join(store.directory, 'centralized.json'), JSON.stringify({ migratedAt: new Date().toISOString(), count: candidates.length }), { mode: 0o600 });
|
|
54
|
-
}
|
|
55
|
-
return imported;
|
|
56
|
-
}
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
|
|
1
3
|
import express from 'express';
|
|
2
4
|
|
|
3
5
|
import type { createLibraryStore } from './store.js';
|
|
6
|
+
import { globalProviderIndex } from './global-state.js';
|
|
4
7
|
import { listLibraryProviderSkills, scanAndPersistLibraryProviderSkills } from './provider-skills.js';
|
|
5
8
|
|
|
6
|
-
export function createLibraryRouter(store: ReturnType<typeof createLibraryStore
|
|
9
|
+
export function createLibraryRouter(store: ReturnType<typeof createLibraryStore>, userHome = os.homedir()) {
|
|
7
10
|
const router = express.Router();
|
|
8
11
|
router.get('/catalog', (req, res) => {
|
|
9
|
-
try {
|
|
12
|
+
try {
|
|
13
|
+
const globalProviders = globalProviderIndex(userHome);
|
|
14
|
+
const entries = store.list(typeof req.query.workspacePath === 'string' ? req.query.workspacePath : undefined);
|
|
15
|
+
res.json({ entries: entries.map(({ sources, ...entry }) => ({ ...entry, globalProviders: globalProviders(sources) })) });
|
|
16
|
+
}
|
|
10
17
|
catch (error) { res.status(400).json({ error: (error as Error).message }); }
|
|
11
18
|
});
|
|
12
19
|
router.post('/catalog', (req, res) => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { createHash, randomBytes } from 'node:crypto';
|
|
4
5
|
|
|
@@ -7,7 +8,16 @@ import Database from 'better-sqlite3';
|
|
|
7
8
|
import { parseFrontMatter } from '@/shared/frontmatter.js';
|
|
8
9
|
|
|
9
10
|
import { parseConnectorConfig, prepareConnectorInstallation, type LibraryConnectorConfig } from './connector-installation.js';
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
componentFileHashes,
|
|
13
|
+
componentFolderName,
|
|
14
|
+
prepareWorkspaceInstallation,
|
|
15
|
+
readLibraryManifest,
|
|
16
|
+
removeLegacyLinks,
|
|
17
|
+
writeLibraryManifest,
|
|
18
|
+
type LibraryComponent,
|
|
19
|
+
type LibraryManifestEntry,
|
|
20
|
+
} from './workspace-installation.js';
|
|
11
21
|
|
|
12
22
|
type StoredLibraryEntry = {
|
|
13
23
|
id: string;
|
|
@@ -69,6 +79,25 @@ function installationFolder(content: string, fallback = '') {
|
|
|
69
79
|
return componentFolderName(String(data.name || data.metadata?.title || fallback));
|
|
70
80
|
}
|
|
71
81
|
|
|
82
|
+
function installableComponent(entry: LibraryEntry): LibraryComponent {
|
|
83
|
+
const slug = installationFolder(entry.content, entry.name);
|
|
84
|
+
if (entry.kind === 'skill') {
|
|
85
|
+
const files = Object.fromEntries(Object.entries(entry.assets).map(([name, bytes]) => [name, Buffer.from(bytes, 'base64')]));
|
|
86
|
+
return { kind: 'skill', slug, files: { ...files, 'SKILL.md': Buffer.from(entry.content, 'utf8') } };
|
|
87
|
+
}
|
|
88
|
+
const { data, content } = parseFrontMatter(entry.content);
|
|
89
|
+
const toml = (fields: Record<string, string>) => `${Object.entries(fields).map(([key, value]) => `${key} = ${JSON.stringify(value)}`).join('\n')}\n`;
|
|
90
|
+
const name = String(data.name || entry.name);
|
|
91
|
+
const description = entry.description || entry.name;
|
|
92
|
+
return {
|
|
93
|
+
kind: 'agent',
|
|
94
|
+
slug,
|
|
95
|
+
markdown: entry.content,
|
|
96
|
+
codex: toml({ name, description, developer_instructions: content }),
|
|
97
|
+
vibe: toml({ display_name: name, description, agent_type: 'subagent', instructions: content }),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
72
101
|
function collectionMainPath(entry: LibraryEntry, rootPath: string) {
|
|
73
102
|
return entry.kind === 'skill' && !rootPath.toLowerCase().endsWith('.md') ? `${rootPath}/SKILL.md` : rootPath;
|
|
74
103
|
}
|
|
@@ -109,6 +138,7 @@ export function createLibraryStore(home: string) {
|
|
|
109
138
|
`);
|
|
110
139
|
const originColumns = db.prepare('PRAGMA table_info(origins)').all() as Array<{ name: string }>;
|
|
111
140
|
if (!originColumns.some((column) => column.name === 'source_hash')) db.exec('ALTER TABLE origins ADD COLUMN source_hash TEXT');
|
|
141
|
+
let defaultEntries: string[] = [];
|
|
112
142
|
|
|
113
143
|
function workspace(value: unknown) {
|
|
114
144
|
if (typeof value !== 'string' || !path.isAbsolute(value)) throw new Error('Dossier absolu requis.');
|
|
@@ -123,14 +153,54 @@ export function createLibraryStore(home: string) {
|
|
|
123
153
|
return { ...entry, assets: JSON.parse(entry.assets) as Record<string, string> };
|
|
124
154
|
}
|
|
125
155
|
|
|
156
|
+
function activeEntries(selected: string) {
|
|
157
|
+
return (db.prepare('SELECT entry_id AS entryId FROM activation WHERE workspace = ?').all(selected) as Array<{ entryId: string }>).map((row) => row.entryId);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function reconcile(selected: string) {
|
|
161
|
+
const known = (id: string) => Boolean(db.prepare('SELECT 1 FROM entries WHERE id = ?').get(id));
|
|
162
|
+
const manifest = readLibraryManifest(selected);
|
|
163
|
+
if (manifest) {
|
|
164
|
+
const expected = manifest.map((entry) => entry.id).filter(known);
|
|
165
|
+
const current = activeEntries(selected);
|
|
166
|
+
if (expected.length === current.length && expected.every((id) => current.includes(id))) return;
|
|
167
|
+
db.transaction(() => {
|
|
168
|
+
db.prepare('DELETE FROM activation WHERE workspace = ?').run(selected);
|
|
169
|
+
for (const id of expected) db.prepare('INSERT OR IGNORE INTO activation VALUES (?, ?)').run(selected, id);
|
|
170
|
+
})();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const inherited = activeEntries(selected);
|
|
174
|
+
const defaults = selected === fs.realpathSync(os.homedir()) ? [] : defaultEntries.filter((id) => known(id) && !inherited.includes(id));
|
|
175
|
+
if (!inherited.length && !defaults.length) return;
|
|
176
|
+
const legacy = path.join(directory, 'active');
|
|
177
|
+
try {
|
|
178
|
+
removeLegacyLinks(selected, legacy);
|
|
179
|
+
writeLibraryManifest(selected, inherited.map((id) => {
|
|
180
|
+
const entry = document(id);
|
|
181
|
+
const slug = entry.kind === 'connector' ? entry.name : installationFolder(entry.content, entry.name);
|
|
182
|
+
const files = entry.kind === 'connector' ? {} : componentFileHashes(selected, installableComponent(entry), false);
|
|
183
|
+
return { id, kind: entry.kind, name: entry.name, slug, files, skipClaude: false };
|
|
184
|
+
}));
|
|
185
|
+
} catch { return; }
|
|
186
|
+
db.prepare('DELETE FROM activation WHERE workspace = ?').run(selected);
|
|
187
|
+
for (const id of [...inherited, ...defaults]) {
|
|
188
|
+
try { setEnabled(selected, id, true); }
|
|
189
|
+
catch { writeLibraryManifest(selected, (readLibraryManifest(selected) ?? []).filter((entry) => entry.id !== id)); }
|
|
190
|
+
}
|
|
191
|
+
fs.rmSync(path.join(legacy, createHash('sha256').update(selected).digest('hex')), { recursive: true, force: true });
|
|
192
|
+
}
|
|
193
|
+
|
|
126
194
|
function list(workspacePath?: string) {
|
|
127
195
|
const selected = workspacePath ? workspace(workspacePath) : '';
|
|
196
|
+
if (selected) reconcile(selected);
|
|
128
197
|
return db.prepare(`SELECT e.id, e.kind, e.name, e.description,
|
|
129
198
|
EXISTS(SELECT 1 FROM activation a WHERE a.entry_id = e.id AND a.workspace = ?) AS enabled,
|
|
130
|
-
(SELECT ce.collection_id FROM collection_entries ce WHERE ce.entry_id = e.id LIMIT 1) AS collectionId
|
|
199
|
+
(SELECT ce.collection_id FROM collection_entries ce WHERE ce.entry_id = e.id LIMIT 1) AS collectionId,
|
|
200
|
+
(SELECT json_group_array(o.source) FROM origins o WHERE o.entry_id = e.id) AS sources
|
|
131
201
|
FROM entries e ORDER BY e.name COLLATE NOCASE`).all(selected).map((row) => {
|
|
132
|
-
const entry = row as Omit<StoredLibraryEntry, 'content' | 'assets'> & { enabled: number; collectionId: string | null };
|
|
133
|
-
return { ...entry, enabled: Boolean(entry.enabled), collectionId: entry.collectionId || null };
|
|
202
|
+
const entry = row as Omit<StoredLibraryEntry, 'content' | 'assets'> & { enabled: number; collectionId: string | null; sources: string };
|
|
203
|
+
return { ...entry, enabled: Boolean(entry.enabled), collectionId: entry.collectionId || null, sources: JSON.parse(entry.sources) as string[] };
|
|
134
204
|
});
|
|
135
205
|
}
|
|
136
206
|
|
|
@@ -225,9 +295,6 @@ export function createLibraryStore(home: string) {
|
|
|
225
295
|
const entry = document(id);
|
|
226
296
|
if (entry.content !== previousContent) throw new Error('Le document a été modifié ailleurs. Rouvrez-le avant d’enregistrer.');
|
|
227
297
|
const active = db.prepare('SELECT workspace FROM activation WHERE entry_id = ?').all(id) as Array<{ workspace: string }>;
|
|
228
|
-
if (entry.kind !== 'connector' && installationFolder(content) !== installationFolder(entry.content)) {
|
|
229
|
-
for (const { workspace: selected } of active) setEnabled(selected, id, false);
|
|
230
|
-
}
|
|
231
298
|
if (entry.kind === 'connector') {
|
|
232
299
|
const config = parseConnectorConfig(content);
|
|
233
300
|
const description = config.url || config.command || entry.name;
|
|
@@ -305,6 +372,7 @@ export function createLibraryStore(home: string) {
|
|
|
305
372
|
|
|
306
373
|
function listCollections(workspacePath?: string) {
|
|
307
374
|
const selected = workspacePath ? workspace(workspacePath) : '';
|
|
375
|
+
if (selected) reconcile(selected);
|
|
308
376
|
return db.prepare(`SELECT c.id, c.name, c.description, c.source,
|
|
309
377
|
COUNT(ce.entry_id) AS entryCount,
|
|
310
378
|
SUM(CASE WHEN a.entry_id IS NOT NULL THEN 1 ELSE 0 END) AS enabledCount
|
|
@@ -442,7 +510,7 @@ export function createLibraryStore(home: string) {
|
|
|
442
510
|
throw new Error('Fichier introuvable.');
|
|
443
511
|
}
|
|
444
512
|
|
|
445
|
-
function setCollectionEnabled(workspacePath: unknown, id: string, enabled: unknown) {
|
|
513
|
+
function setCollectionEnabled(workspacePath: unknown, id: string, enabled: unknown, options: { skipClaude?: boolean } = {}) {
|
|
446
514
|
if (typeof enabled !== 'boolean') throw new Error('Activation booléenne requise.');
|
|
447
515
|
const selected = workspace(workspacePath);
|
|
448
516
|
const mappings = collectionEntries(id);
|
|
@@ -452,7 +520,7 @@ export function createLibraryStore(home: string) {
|
|
|
452
520
|
try {
|
|
453
521
|
for (const mapping of mappings) {
|
|
454
522
|
if (active.has(mapping.entryId) === enabled) continue;
|
|
455
|
-
setEnabled(selected, mapping.entryId, enabled);
|
|
523
|
+
setEnabled(selected, mapping.entryId, enabled, options);
|
|
456
524
|
changed.push(mapping.entryId);
|
|
457
525
|
}
|
|
458
526
|
} catch (error) {
|
|
@@ -462,59 +530,26 @@ export function createLibraryStore(home: string) {
|
|
|
462
530
|
return listCollections(selected).find((collection) => collection.id === id);
|
|
463
531
|
}
|
|
464
532
|
|
|
465
|
-
function setEnabled(workspacePath: unknown, id: string, enabled: unknown) {
|
|
533
|
+
function setEnabled(workspacePath: unknown, id: string, enabled: unknown, options: { skipClaude?: boolean } = {}) {
|
|
466
534
|
if (typeof enabled !== 'boolean') throw new Error('Activation booléenne requise.');
|
|
467
535
|
const selected = workspace(workspacePath);
|
|
468
536
|
const entry = document(id);
|
|
537
|
+
const manifest = readLibraryManifest(selected) ?? [];
|
|
538
|
+
const previous = manifest.find((item) => item.id === id);
|
|
539
|
+
const skipClaude = options.skipClaude ?? previous?.skipClaude ?? false;
|
|
540
|
+
const installed: LibraryManifestEntry = { id, kind: entry.kind, name: entry.name, slug: entry.name, files: {}, skipClaude };
|
|
469
541
|
if (entry.kind === 'connector') {
|
|
470
|
-
prepareConnectorInstallation(selected, entry.name, parseConnectorConfig(entry.content), enabled)();
|
|
471
|
-
if (enabled) db.prepare('INSERT OR IGNORE INTO activation VALUES (?, ?)').run(selected, id);
|
|
472
|
-
else db.prepare('DELETE FROM activation WHERE workspace = ? AND entry_id = ?').run(selected, id);
|
|
473
|
-
return { ok: true, enabled };
|
|
474
|
-
}
|
|
475
|
-
const packageRoot = path.join(directory, 'active', createHash('sha256').update(selected).digest('hex'), id);
|
|
476
|
-
const install = prepareWorkspaceInstallation(selected, id, installationFolder(entry.content, entry.name), packageRoot, entry.kind, enabled);
|
|
477
|
-
if (enabled) {
|
|
478
|
-
fs.mkdirSync(path.dirname(packageRoot), { recursive: true, mode: 0o700 });
|
|
479
|
-
if (!fs.realpathSync(path.dirname(packageRoot)).startsWith(fs.realpathSync(directory) + path.sep)) throw new Error('Répertoire d’activation non autorisé.');
|
|
480
|
-
const staging = fs.mkdtempSync(path.join(directory, '.activation-'));
|
|
481
|
-
const backup = `${staging}.previous`;
|
|
482
|
-
try {
|
|
483
|
-
for (const [relative, bytes] of Object.entries(entry.assets)) {
|
|
484
|
-
const target = path.resolve(staging, relative);
|
|
485
|
-
if (!target.startsWith(staging + path.sep)) throw new Error('Chemin associé invalide.');
|
|
486
|
-
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
487
|
-
fs.writeFileSync(target, Buffer.from(bytes, 'base64'), { mode: 0o600 });
|
|
488
|
-
}
|
|
489
|
-
if (entry.kind === 'skill') fs.writeFileSync(path.join(staging, 'SKILL.md'), entry.content, { mode: 0o600 });
|
|
490
|
-
else {
|
|
491
|
-
fs.writeFileSync(path.join(staging, 'agent.md'), entry.content, { mode: 0o600 });
|
|
492
|
-
const { data, content } = parseFrontMatter(entry.content);
|
|
493
|
-
const toml = Object.entries({ name: String(data.name || entry.name), description: entry.description || entry.name, developer_instructions: content })
|
|
494
|
-
.map(([key, value]) => `${key} = ${JSON.stringify(value)}`).join('\n');
|
|
495
|
-
fs.writeFileSync(path.join(staging, 'agent.toml'), `${toml}\n`, { mode: 0o600 });
|
|
496
|
-
}
|
|
497
|
-
if (fs.existsSync(packageRoot)) fs.renameSync(packageRoot, backup);
|
|
498
|
-
try {
|
|
499
|
-
fs.renameSync(staging, packageRoot);
|
|
500
|
-
install();
|
|
501
|
-
}
|
|
502
|
-
catch (error) {
|
|
503
|
-
fs.rmSync(packageRoot, { recursive: true, force: true });
|
|
504
|
-
if (fs.existsSync(backup)) fs.renameSync(backup, packageRoot);
|
|
505
|
-
throw error;
|
|
506
|
-
}
|
|
507
|
-
} finally {
|
|
508
|
-
fs.rmSync(staging, { recursive: true, force: true });
|
|
509
|
-
fs.rmSync(backup, { recursive: true, force: true });
|
|
510
|
-
}
|
|
511
|
-
} else if (fs.existsSync(path.dirname(packageRoot))) {
|
|
512
|
-
if (!fs.realpathSync(path.dirname(packageRoot)).startsWith(fs.realpathSync(directory) + path.sep)) throw new Error('Répertoire d’activation non autorisé.');
|
|
513
|
-
install();
|
|
514
|
-
fs.rmSync(packageRoot, { recursive: true, force: true });
|
|
542
|
+
prepareConnectorInstallation(selected, entry.name, parseConnectorConfig(entry.content), enabled, skipClaude)();
|
|
515
543
|
} else {
|
|
516
|
-
|
|
544
|
+
const component = installableComponent(entry);
|
|
545
|
+
const installation = prepareWorkspaceInstallation(selected, previous, enabled ? { component, skipClaude } : null);
|
|
546
|
+
installation.commit();
|
|
547
|
+
installed.slug = component.slug;
|
|
548
|
+
installed.files = installation.files;
|
|
517
549
|
}
|
|
550
|
+
const others = manifest.filter((item) => item.id !== id);
|
|
551
|
+
if (enabled) writeLibraryManifest(selected, [...others, installed]);
|
|
552
|
+
else if (previous) writeLibraryManifest(selected, others);
|
|
518
553
|
if (enabled) db.prepare('INSERT OR IGNORE INTO activation VALUES (?, ?)').run(selected, id);
|
|
519
554
|
else db.prepare('DELETE FROM activation WHERE workspace = ? AND entry_id = ?').run(selected, id);
|
|
520
555
|
return { ok: true, enabled };
|
|
@@ -522,13 +557,14 @@ export function createLibraryStore(home: string) {
|
|
|
522
557
|
|
|
523
558
|
function instructions(workspacePath: string) {
|
|
524
559
|
const selected = workspace(workspacePath);
|
|
560
|
+
reconcile(selected);
|
|
525
561
|
const entries = db.prepare(`SELECT e.* FROM entries e JOIN activation a ON a.entry_id = e.id
|
|
526
562
|
WHERE a.workspace = ? AND e.kind IN ('skill', 'agent') ORDER BY e.name`).all(selected) as StoredLibraryEntry[];
|
|
527
563
|
return entries.map((entry) => {
|
|
528
564
|
const assets = JSON.parse(entry.assets) as Record<string, string>;
|
|
529
565
|
const references = Object.entries(assets).filter(([name]) => name === 'table-columns.yaml')
|
|
530
566
|
.map(([name, bytes]) => `### ${name}\n${Buffer.from(bytes, 'base64').toString('utf8')}`);
|
|
531
|
-
const packageRoot = path.join(
|
|
567
|
+
const packageRoot = path.join(selected, '.agents', 'skills', installationFolder(entry.content, entry.name));
|
|
532
568
|
return `## ${entry.kind === 'agent' ? 'Instructions de rôle' : 'Skill'} : ${entry.name}\n${entry.content}\n${references.join('\n\n')}\nFichiers associés disponibles dans : ${packageRoot}\n${Object.keys(assets).join('\n')}`;
|
|
533
569
|
}).join('\n\n');
|
|
534
570
|
}
|
|
@@ -552,6 +588,7 @@ export function createLibraryStore(home: string) {
|
|
|
552
588
|
collectionFile,
|
|
553
589
|
updateCollectionFile,
|
|
554
590
|
setCollectionEnabled,
|
|
591
|
+
setDefaultEntries: (ids: string[]) => { defaultEntries = ids; },
|
|
555
592
|
close: () => db.close(),
|
|
556
593
|
};
|
|
557
594
|
}
|