pikiloom 0.4.40 → 0.4.41
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/dashboard/dist/assets/{AgentTab-Cj7sekcS.js → AgentTab-B4ZC9QFL.js} +1 -1
- package/dashboard/dist/assets/{ConnectionModal-g_6IHAWE.js → ConnectionModal-CAlACYKM.js} +1 -1
- package/dashboard/dist/assets/{DirBrowser-DVe6q12N.js → DirBrowser-D3KGIof1.js} +1 -1
- package/dashboard/dist/assets/{ExtensionsTab-GqxD61Pp.js → ExtensionsTab-C_69Y7CR.js} +1 -1
- package/dashboard/dist/assets/{IMAccessTab-DIqTzZJ9.js → IMAccessTab-B5_6IS4X.js} +1 -1
- package/dashboard/dist/assets/{Modal-CIYrMwm0.js → Modal-BjZifpga.js} +1 -1
- package/dashboard/dist/assets/{Modals-B-Afu2rD.js → Modals-B_V24pNA.js} +1 -1
- package/dashboard/dist/assets/{Select-B1UL27gw.js → Select-D7xW38wq.js} +1 -1
- package/dashboard/dist/assets/{SessionPanel-BHuG5hxj.js → SessionPanel-BbrfUDLg.js} +1 -1
- package/dashboard/dist/assets/{SystemTab-Bv1uT5-h.js → SystemTab-i5nmYweA.js} +1 -1
- package/dashboard/dist/assets/{index-BOEW0RIx.js → index-A_dL4aEz.js} +4 -4
- package/dashboard/dist/assets/{index-qKexKdJl.js → index-D18pCeqv.js} +2 -2
- package/dashboard/dist/assets/{shared-624PaUfm.js → shared-BKwEgcmy.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dist/bot/render-shared.js +2 -1
- package/dist/core/config/runtime-config.js +14 -22
- package/package.json +1 -1
- package/packages/kernel/README.md +35 -0
- package/packages/kernel/dist/contracts/driver.d.ts +15 -0
- package/packages/kernel/dist/contracts/ports.d.ts +2 -0
- package/packages/kernel/dist/drivers/claude.d.ts +5 -1
- package/packages/kernel/dist/drivers/claude.js +4 -0
- package/packages/kernel/dist/drivers/codex.d.ts +5 -1
- package/packages/kernel/dist/drivers/codex.js +4 -0
- package/packages/kernel/dist/drivers/gemini.d.ts +5 -1
- package/packages/kernel/dist/drivers/gemini.js +4 -0
- package/packages/kernel/dist/index.d.ts +2 -1
- package/packages/kernel/dist/index.js +2 -0
- package/packages/kernel/dist/ports/defaults.js +7 -1
- package/packages/kernel/dist/runtime/loom.d.ts +10 -0
- package/packages/kernel/dist/runtime/loom.js +18 -2
- package/packages/kernel/dist/workspace/index.d.ts +5 -0
- package/packages/kernel/dist/workspace/index.js +7 -0
- package/packages/kernel/dist/workspace/mcp.d.ts +44 -0
- package/packages/kernel/dist/workspace/mcp.js +82 -0
- package/packages/kernel/dist/workspace/native.d.ts +14 -0
- package/packages/kernel/dist/workspace/native.js +340 -0
- package/packages/kernel/dist/workspace/paths.d.ts +33 -0
- package/packages/kernel/dist/workspace/paths.js +40 -0
- package/packages/kernel/dist/workspace/sessions.d.ts +49 -0
- package/packages/kernel/dist/workspace/sessions.js +130 -0
- package/packages/kernel/dist/workspace/skills.d.ts +44 -0
- package/packages/kernel/dist/workspace/skills.js +173 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const DEFAULT_AGENT_SKILL_DIRS = ['.claude/skills', '.agents/skills'];
|
|
4
|
+
function parseSkillMeta(content) {
|
|
5
|
+
let label = null;
|
|
6
|
+
let description = null;
|
|
7
|
+
let mcpRequires;
|
|
8
|
+
const fm = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
9
|
+
if (fm) {
|
|
10
|
+
const lm = fm[1].match(/^label:\s*(.+)/m);
|
|
11
|
+
if (lm)
|
|
12
|
+
label = lm[1].trim();
|
|
13
|
+
const dm = fm[1].match(/^description:\s*(.+)/m);
|
|
14
|
+
if (dm)
|
|
15
|
+
description = dm[1].trim();
|
|
16
|
+
const mr = fm[1].match(/^mcp_requires:\s*\n((?:\s+-\s+.+\n?)+)/m);
|
|
17
|
+
if (mr) {
|
|
18
|
+
mcpRequires = mr[1].split('\n').map(l => l.replace(/^\s*-\s*/, '').replace(/["']/g, '').trim()).filter(Boolean);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (!label) {
|
|
22
|
+
const hm = content.match(/^#\s+(.+)$/m);
|
|
23
|
+
if (hm)
|
|
24
|
+
label = hm[1].trim();
|
|
25
|
+
}
|
|
26
|
+
return { label, description, mcpRequires };
|
|
27
|
+
}
|
|
28
|
+
function realPathOrNull(p) {
|
|
29
|
+
try {
|
|
30
|
+
return fs.realpathSync(p);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Make `linkPath` a symlink to `targetDir` (idempotent; replaces a stale link/dir). */
|
|
37
|
+
export function ensureDirSymlink(linkPath, targetDir) {
|
|
38
|
+
const desiredTarget = path.relative(path.dirname(linkPath), targetDir) || '.';
|
|
39
|
+
try {
|
|
40
|
+
const stat = fs.lstatSync(linkPath);
|
|
41
|
+
if (stat.isSymbolicLink()) {
|
|
42
|
+
const currentTarget = fs.readlinkSync(linkPath);
|
|
43
|
+
const currentReal = realPathOrNull(path.resolve(path.dirname(linkPath), currentTarget));
|
|
44
|
+
const desiredReal = realPathOrNull(targetDir);
|
|
45
|
+
if (currentTarget === desiredTarget || (currentReal && desiredReal && currentReal === desiredReal))
|
|
46
|
+
return;
|
|
47
|
+
fs.unlinkSync(linkPath);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
fs.rmSync(linkPath, { recursive: true, force: true });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch { /* nothing there yet */ }
|
|
54
|
+
fs.mkdirSync(path.dirname(linkPath), { recursive: true });
|
|
55
|
+
try {
|
|
56
|
+
fs.symlinkSync(desiredTarget, linkPath, process.platform === 'win32' ? 'junction' : 'dir');
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
if (err?.code !== 'EEXIST' || fs.readlinkSync(linkPath) !== desiredTarget)
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function discoverSkillsFromDir(dir, scope, seen) {
|
|
64
|
+
let entries;
|
|
65
|
+
try {
|
|
66
|
+
entries = fs.readdirSync(dir).sort((a, b) => a.localeCompare(b, 'en', { sensitivity: 'base' }));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
const out = [];
|
|
72
|
+
for (const name of entries) {
|
|
73
|
+
if (!name || seen.has(name))
|
|
74
|
+
continue;
|
|
75
|
+
const skillDir = path.join(dir, name);
|
|
76
|
+
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
77
|
+
try {
|
|
78
|
+
if (!fs.statSync(skillDir).isDirectory())
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
if (!fs.statSync(skillFile).isFile())
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
let meta = { label: null, description: null };
|
|
92
|
+
try {
|
|
93
|
+
meta = parseSkillMeta(fs.readFileSync(skillFile, 'utf8'));
|
|
94
|
+
}
|
|
95
|
+
catch { /* keep defaults */ }
|
|
96
|
+
out.push({ name, label: meta.label, description: meta.description, scope, path: skillDir, mcpRequires: meta.mcpRequires });
|
|
97
|
+
seen.add(name);
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
export class SkillsManager {
|
|
102
|
+
paths;
|
|
103
|
+
agentSkillDirs;
|
|
104
|
+
log;
|
|
105
|
+
constructor(opts) {
|
|
106
|
+
this.paths = opts.paths;
|
|
107
|
+
this.agentSkillDirs = opts.agentSkillDirs?.length ? opts.agentSkillDirs : DEFAULT_AGENT_SKILL_DIRS;
|
|
108
|
+
this.log = opts.log;
|
|
109
|
+
}
|
|
110
|
+
/** The canonical skills dir for a scope, e.g. <workdir>/.pikiloom/skills or ~/.pikiloom/skills. */
|
|
111
|
+
canonicalDir(scope, workdir) {
|
|
112
|
+
return this.paths.skillsDir(scope, workdir);
|
|
113
|
+
}
|
|
114
|
+
/** Absolute paths of the per-agent skill dirs that link to the canonical dir for a scope. */
|
|
115
|
+
agentLinkPaths(scope, workdir) {
|
|
116
|
+
const base = this.paths.agentHome(scope, workdir);
|
|
117
|
+
return this.agentSkillDirs.map(rel => path.join(base, rel));
|
|
118
|
+
}
|
|
119
|
+
/** Ensure the canonical dir exists and each agent skills dir symlinks to it. */
|
|
120
|
+
ensureLinks(scope, workdir) {
|
|
121
|
+
const canonical = this.canonicalDir(scope, workdir);
|
|
122
|
+
fs.mkdirSync(canonical, { recursive: true });
|
|
123
|
+
for (const link of this.agentLinkPaths(scope, workdir)) {
|
|
124
|
+
try {
|
|
125
|
+
ensureDirSymlink(link, canonical);
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
this.log?.(`[skills] link ${link} -> ${canonical} failed: ${e?.message || e}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** List skills. scope 'all' (default) = workspace (if workdir) then global, project wins on name clash. */
|
|
133
|
+
list(opts = {}) {
|
|
134
|
+
const scope = opts.scope ?? 'all';
|
|
135
|
+
const seen = new Set();
|
|
136
|
+
const out = [];
|
|
137
|
+
if ((scope === 'workspace' || scope === 'all') && opts.workdir) {
|
|
138
|
+
out.push(...discoverSkillsFromDir(this.paths.skillsDir('workspace', opts.workdir), 'workspace', seen));
|
|
139
|
+
}
|
|
140
|
+
if (scope === 'global' || scope === 'all') {
|
|
141
|
+
out.push(...discoverSkillsFromDir(this.paths.skillsDir('global'), 'global', seen));
|
|
142
|
+
}
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
/** Search installable skills on the npm registry (best-effort; [] on failure). */
|
|
146
|
+
async search(query, limit = 20) {
|
|
147
|
+
const q = (query || '').trim();
|
|
148
|
+
const text = encodeURIComponent(`agent skill ${q}`.trim());
|
|
149
|
+
const url = `https://registry.npmjs.org/-/v1/search?text=${text}&size=${Math.max(1, Math.min(50, limit))}`;
|
|
150
|
+
try {
|
|
151
|
+
const res = await fetch(url, { headers: { accept: 'application/json' } });
|
|
152
|
+
if (!res.ok)
|
|
153
|
+
return [];
|
|
154
|
+
const data = await res.json();
|
|
155
|
+
const objects = Array.isArray(data?.objects) ? data.objects : [];
|
|
156
|
+
return objects.map((o) => {
|
|
157
|
+
const pkg = o?.package ?? {};
|
|
158
|
+
return {
|
|
159
|
+
name: String(pkg.name ?? ''),
|
|
160
|
+
description: pkg.description ?? null,
|
|
161
|
+
source: 'npm',
|
|
162
|
+
author: pkg.publisher?.username ?? pkg.author?.name ?? null,
|
|
163
|
+
homepage: pkg.links?.homepage ?? pkg.links?.npm ?? null,
|
|
164
|
+
version: pkg.version ?? null,
|
|
165
|
+
};
|
|
166
|
+
}).filter(s => s.name);
|
|
167
|
+
}
|
|
168
|
+
catch (e) {
|
|
169
|
+
this.log?.(`[skills] search failed: ${e?.message || e}`);
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|