@cryptiklemur/lattice 5.10.0 → 5.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/assets/{angular-html-BDIcxkJq.js → angular-html-BoFzmWT8.js} +1 -1
- package/dist/client/assets/{angular-ts-Bt22ouNH.js → angular-ts-DZnI8rKE.js} +1 -1
- package/dist/client/assets/{apl-p8qkxzEK.js → apl-DstVmncE.js} +1 -1
- package/dist/client/assets/{astro-CIaMc49M.js → astro-DTPCjzEx.js} +1 -1
- package/dist/client/assets/{blade-BR56EAMD.js → blade-6q42Ss3F.js} +1 -1
- package/dist/client/assets/{c-Dli0HzAh.js → c-BQDGJ-nQ.js} +1 -1
- package/dist/client/assets/{cobol-Cad15ECy.js → cobol-Dlh0WvsZ.js} +1 -1
- package/dist/client/assets/{coffee-DpyATEbF.js → coffee-DdQv129j.js} +1 -1
- package/dist/client/assets/{cpp-KN8_NFsf.js → cpp-DhbQJIv4.js} +1 -1
- package/dist/client/assets/{crystal-CuyGv0kh.js → crystal-C22kERUB.js} +1 -1
- package/dist/client/assets/{css-Cm3q4bxn.js → css-n31O5kHj.js} +1 -1
- package/dist/client/assets/{dist-BjxsMc4u.js → dist-D8okl7lw.js} +2 -2
- package/dist/client/assets/{edge-B6S7CSbx.js → edge-Cgwx-o_7.js} +1 -1
- package/dist/client/assets/{elixir-CNUy9H8T.js → elixir-DAGM2WKD.js} +1 -1
- package/dist/client/assets/{elm-CNfcWmb9.js → elm-BLw_7oO9.js} +1 -1
- package/dist/client/assets/{erb-DWebzDaI.js → erb-DCaNhYa7.js} +1 -1
- package/dist/client/assets/{git-rebase-B_Pt2ZBK.js → git-rebase-CNNhb8-g.js} +1 -1
- package/dist/client/assets/{glimmer-js-CVwoOd72.js → glimmer-js-BnZd88Wi.js} +1 -1
- package/dist/client/assets/{glimmer-ts-CjtFSxjz.js → glimmer-ts-DvFNbZu-.js} +1 -1
- package/dist/client/assets/{glsl-CP4rggAA.js → glsl-Dnrk_Jnx.js} +1 -1
- package/dist/client/assets/{graphql-Dbm6sAtp.js → graphql-DlWTPvCG.js} +1 -1
- package/dist/client/assets/{hack-Bj9y3SGf.js → hack-DQg1Ek33.js} +1 -1
- package/dist/client/assets/{haml-DRGrdf3f.js → haml-DSk45qIE.js} +1 -1
- package/dist/client/assets/{handlebars-CFKjcBMg.js → handlebars-DuLvATB2.js} +1 -1
- package/dist/client/assets/{html-Vcd4eHHg.js → html-D4DiUnLg.js} +1 -1
- package/dist/client/assets/{html-derivative-BF0YbD4L.js → html-derivative-CS5MZ6d9.js} +1 -1
- package/dist/client/assets/{http-CGVTa2NT.js → http-CkDncfer.js} +1 -1
- package/dist/client/assets/{hurl-B0GrsGqd.js → hurl-DU39oO3U.js} +1 -1
- package/dist/client/assets/{index-CX1tudsF.js → index-CHPfE1Zl.js} +129 -129
- package/dist/client/assets/index-DHUKmLLC.css +2 -0
- package/dist/client/assets/{java-BJHQqHsm.js → java-lntACKEu.js} +1 -1
- package/dist/client/assets/{javascript-CmuMsKrc.js → javascript-CxkFc6nV.js} +1 -1
- package/dist/client/assets/{jinja-JxCLeq1j.js → jinja-DolO2zO7.js} +1 -1
- package/dist/client/assets/{jison-BdgAUhei.js → jison-Cok5FPev.js} +1 -1
- package/dist/client/assets/{json-DtPissHL.js → json-BebuQPrq.js} +1 -1
- package/dist/client/assets/{jsx-DUAxxDkP.js → jsx-iLBaUyXr.js} +1 -1
- package/dist/client/assets/{julia-DxDlbL6e.js → julia-C5Dsc7cH.js} +1 -1
- package/dist/client/assets/{just-CVmAAx2R.js → just-DJYqq_9R.js} +1 -1
- package/dist/client/assets/{latex-uwxggTWA.js → latex-BTTYiKj1.js} +1 -1
- package/dist/client/assets/{liquid-xsETAJJy.js → liquid-DpAKCrOB.js} +1 -1
- package/dist/client/assets/{lua-B2Hh8PgD.js → lua-BZ6b1hko.js} +1 -1
- package/dist/client/assets/{marko-yDeGxD87.js → marko-D8VK6iGt.js} +1 -1
- package/dist/client/assets/{mdc-QMp4ieYR.js → mdc-Paa3XzwY.js} +1 -1
- package/dist/client/assets/{nginx-7gmRmcqz.js → nginx-C5k9mWtJ.js} +1 -1
- package/dist/client/assets/{nim-CA8SNY_7.js → nim-Dst6YSnE.js} +1 -1
- package/dist/client/assets/{perl-lx5nW4VC.js → perl-XhiCjgBp.js} +1 -1
- package/dist/client/assets/{php-DgHiW953.js → php-BcsPLnLU.js} +1 -1
- package/dist/client/assets/{pug-CbbB1vwb.js → pug-GLH9-eAJ.js} +1 -1
- package/dist/client/assets/{qml-COrzwCIh.js → qml-Cj_lJioE.js} +1 -1
- package/dist/client/assets/{r-Dv7pZJDH.js → r-B70aGYK5.js} +1 -1
- package/dist/client/assets/{razor-D2m8EDP5.js → razor-R3gub_zy.js} +1 -1
- package/dist/client/assets/{regexp-BXLT-jPc.js → regexp-itC0dIUJ.js} +1 -1
- package/dist/client/assets/{rst-_S6rrUYh.js → rst-DdyoV8E2.js} +1 -1
- package/dist/client/assets/{ruby-C3XO7tYY.js → ruby-BYBZsv66.js} +1 -1
- package/dist/client/assets/{sas-DP2k4iuN.js → sas-fqfqXqj1.js} +1 -1
- package/dist/client/assets/{scss-lhLFMXGn.js → scss-B-ELv6mu.js} +1 -1
- package/dist/client/assets/{shellscript-BYlBPHen.js → shellscript-BgB8TNw6.js} +1 -1
- package/dist/client/assets/{shellsession-CbVyQKWZ.js → shellsession-BLK2Dgkm.js} +1 -1
- package/dist/client/assets/{soy-Be8a0lHq.js → soy-C7_RmNrp.js} +1 -1
- package/dist/client/assets/{sql-2KxvU9YS.js → sql-AUgbUJq4.js} +1 -1
- package/dist/client/assets/{stata-BxlWftTS.js → stata-CIVqSIOr.js} +1 -1
- package/dist/client/assets/{surrealql-CJ-q86nR.js → surrealql-BzRQzc5S.js} +1 -1
- package/dist/client/assets/{svelte-Q1ml0OiY.js → svelte-BCIwEwtb.js} +1 -1
- package/dist/client/assets/{templ-BbfPZhtu.js → templ-C1hbwe4u.js} +1 -1
- package/dist/client/assets/{tex-Dcth4Gi6.js → tex-CI4tIsaP.js} +1 -1
- package/dist/client/assets/{ts-tags-BKhSOXI3.js → ts-tags-SUeikhEp.js} +1 -1
- package/dist/client/assets/{tsx-CS6iQ0XH.js → tsx-xkp7aIZs.js} +1 -1
- package/dist/client/assets/{twig-BHp31ZxS.js → twig-CGgBSAyc.js} +1 -1
- package/dist/client/assets/{typescript-16YJBTaO.js → typescript-O2YMTl_s.js} +1 -1
- package/dist/client/assets/{vue-CMKwTi4r.js → vue-DsNRxos1.js} +1 -1
- package/dist/client/assets/{vue-html-Dr8VUA2G.js → vue-html-CuY3t7bs.js} +1 -1
- package/dist/client/assets/{vue-vine-DZUqDerl.js → vue-vine-C6kSCKwY.js} +1 -1
- package/dist/client/assets/{xml-CBbBKKDC.js → xml-DafwzOLY.js} +1 -1
- package/dist/client/assets/{xsl-DWEX6PKX.js → xsl-1SGGZibr.js} +1 -1
- package/dist/client/assets/{yaml-DvKvvh3X.js → yaml-DSVhzmhr.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/sw.js +1 -1
- package/dist/server/analytics/engine.js +241 -241
- package/dist/server/assets.js +4 -4
- package/dist/server/auth/passphrase.js +13 -13
- package/dist/server/config.js +7 -7
- package/dist/server/daemon.js +93 -93
- package/dist/server/features/brainstorm.js +42 -42
- package/dist/server/features/ralph-loop.js +33 -33
- package/dist/server/features/scheduler.js +53 -53
- package/dist/server/features/specs.js +54 -54
- package/dist/server/features/sticky-notes.js +17 -17
- package/dist/server/features/superpowers.js +24 -24
- package/dist/server/handlers/analytics.js +1 -1
- package/dist/server/handlers/attachment.js +32 -32
- package/dist/server/handlers/bookmarks.js +4 -4
- package/dist/server/handlers/brainstorm.js +4 -4
- package/dist/server/handlers/chat.js +54 -54
- package/dist/server/handlers/editor.js +13 -13
- package/dist/server/handlers/fs.js +51 -51
- package/dist/server/handlers/hooks.js +20 -20
- package/dist/server/handlers/loop.js +6 -6
- package/dist/server/handlers/memory.js +44 -44
- package/dist/server/handlers/mesh.js +60 -60
- package/dist/server/handlers/notes.js +7 -7
- package/dist/server/handlers/plugins.js +174 -174
- package/dist/server/handlers/project-settings.js +26 -26
- package/dist/server/handlers/scheduler.js +6 -6
- package/dist/server/handlers/session.js +24 -24
- package/dist/server/handlers/settings.js +21 -21
- package/dist/server/handlers/skills.js +91 -91
- package/dist/server/handlers/specs.js +51 -28
- package/dist/server/handlers/terminal.js +13 -13
- package/dist/server/handlers/themes.js +21 -21
- package/dist/server/handlers/update.js +17 -17
- package/dist/server/hooks/event_forward.sh +34 -0
- package/dist/server/hooks/post_tool_use.sh +26 -0
- package/dist/server/hooks/statusline.sh +26 -0
- package/dist/server/identity.js +6 -6
- package/dist/server/index.js +111 -111
- package/dist/server/logger.js +1 -1
- package/dist/server/mesh/connector.js +78 -78
- package/dist/server/mesh/crypto.js +20 -20
- package/dist/server/mesh/discovery.js +14 -14
- package/dist/server/mesh/pairing.js +30 -30
- package/dist/server/mesh/peers.js +10 -10
- package/dist/server/mesh/proxy.js +14 -14
- package/dist/server/mesh/session-sync.js +23 -23
- package/dist/server/project/bookmarks.js +11 -11
- package/dist/server/project/context-breakdown.js +70 -70
- package/dist/server/project/file-browser.js +17 -17
- package/dist/server/project/project-files.js +68 -68
- package/dist/server/project/registry.js +10 -10
- package/dist/server/project/sdk-bridge.js +157 -157
- package/dist/server/project/session.js +201 -199
- package/dist/server/project/terminal.js +15 -15
- package/dist/server/project/warmup.js +37 -37
- package/dist/server/push.js +11 -11
- package/dist/server/runtime.js +1 -1
- package/dist/server/tls.js +15 -15
- package/dist/server/tui.js +15 -15
- package/dist/server/update-checker.js +21 -21
- package/dist/server/ws/broadcast.js +18 -18
- package/dist/server/ws/router.js +17 -17
- package/dist/shared/constants.js +8 -8
- package/package.json +2 -2
- package/dist/client/assets/index-DlfI20Gn.css +0 -2
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
import { readdir, readFile as fsReadFile, stat, writeFile as fsWriteFile } from "node:fs/promises";
|
|
2
2
|
import { join, resolve, relative } from "node:path";
|
|
3
|
-
|
|
3
|
+
const MAX_FILE_SIZE = 512 * 1024;
|
|
4
4
|
export function validatePath(projectPath, relativePath) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const resolved = resolve(projectPath, relativePath);
|
|
6
|
+
const normalizedRoot = resolve(projectPath);
|
|
7
7
|
if (!resolved.startsWith(normalizedRoot + "/") && resolved !== normalizedRoot) {
|
|
8
8
|
return null;
|
|
9
9
|
}
|
|
10
10
|
return resolved;
|
|
11
11
|
}
|
|
12
12
|
export async function listDirectory(projectPath, relativePath) {
|
|
13
|
-
|
|
13
|
+
const fullPath = validatePath(projectPath, relativePath);
|
|
14
14
|
if (!fullPath) {
|
|
15
15
|
return [];
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const entries = [];
|
|
18
|
+
let names;
|
|
19
19
|
try {
|
|
20
20
|
names = await readdir(fullPath);
|
|
21
21
|
}
|
|
22
22
|
catch {
|
|
23
23
|
return [];
|
|
24
24
|
}
|
|
25
|
-
for (
|
|
26
|
-
|
|
25
|
+
for (let i = 0; i < names.length; i++) {
|
|
26
|
+
const name = names[i];
|
|
27
27
|
if (name.startsWith(".")) {
|
|
28
28
|
continue;
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
const entryFull = join(fullPath, name);
|
|
31
31
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const entryStat = await stat(entryFull);
|
|
33
|
+
const entryRelative = relative(projectPath, entryFull);
|
|
34
34
|
entries.push({
|
|
35
35
|
name: name,
|
|
36
36
|
path: entryRelative,
|
|
@@ -53,11 +53,11 @@ export async function listDirectory(projectPath, relativePath) {
|
|
|
53
53
|
return entries;
|
|
54
54
|
}
|
|
55
55
|
export async function readFile(projectPath, relativePath) {
|
|
56
|
-
|
|
56
|
+
const fullPath = validatePath(projectPath, relativePath);
|
|
57
57
|
if (!fullPath) {
|
|
58
58
|
return null;
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
let fileStat;
|
|
61
61
|
try {
|
|
62
62
|
fileStat = await stat(fullPath);
|
|
63
63
|
}
|
|
@@ -67,15 +67,15 @@ export async function readFile(projectPath, relativePath) {
|
|
|
67
67
|
if (fileStat.size > MAX_FILE_SIZE) {
|
|
68
68
|
return null;
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
let buf;
|
|
71
71
|
try {
|
|
72
72
|
buf = await fsReadFile(fullPath);
|
|
73
73
|
}
|
|
74
74
|
catch {
|
|
75
75
|
return null;
|
|
76
76
|
}
|
|
77
|
-
for (
|
|
78
|
-
|
|
77
|
+
for (let i = 0; i < Math.min(buf.length, 8192); i++) {
|
|
78
|
+
const byte = buf[i];
|
|
79
79
|
if (byte === 0) {
|
|
80
80
|
return null;
|
|
81
81
|
}
|
|
@@ -83,7 +83,7 @@ export async function readFile(projectPath, relativePath) {
|
|
|
83
83
|
return buf.toString("utf-8");
|
|
84
84
|
}
|
|
85
85
|
export async function writeFile(projectPath, relativePath, content) {
|
|
86
|
-
|
|
86
|
+
const fullPath = validatePath(projectPath, relativePath);
|
|
87
87
|
if (!fullPath) {
|
|
88
88
|
return false;
|
|
89
89
|
}
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, unlink
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
export function readProjectClaudeMd(projectPath) {
|
|
5
|
-
|
|
5
|
+
const filePath = join(projectPath, "CLAUDE.md");
|
|
6
6
|
if (!existsSync(filePath))
|
|
7
7
|
return "";
|
|
8
8
|
try {
|
|
@@ -16,7 +16,7 @@ export function writeProjectClaudeMd(projectPath, content) {
|
|
|
16
16
|
writeFileSync(join(projectPath, "CLAUDE.md"), content, "utf-8");
|
|
17
17
|
}
|
|
18
18
|
export function readProjectClaudeSettings(projectPath) {
|
|
19
|
-
|
|
19
|
+
const filePath = join(projectPath, ".claude", "settings.json");
|
|
20
20
|
if (!existsSync(filePath))
|
|
21
21
|
return {};
|
|
22
22
|
try {
|
|
@@ -27,24 +27,24 @@ export function readProjectClaudeSettings(projectPath) {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
export function mergeProjectClaudeSettings(projectPath, updates) {
|
|
30
|
-
|
|
30
|
+
const existing = readProjectClaudeSettings(projectPath);
|
|
31
31
|
Object.assign(existing, updates);
|
|
32
|
-
|
|
32
|
+
const dir = join(projectPath, ".claude");
|
|
33
33
|
mkdirSync(dir, { recursive: true });
|
|
34
34
|
writeFileSync(join(dir, "settings.json"), JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
35
35
|
}
|
|
36
36
|
export function readProjectRules(projectPath) {
|
|
37
|
-
|
|
37
|
+
const dir = join(projectPath, ".claude", "rules");
|
|
38
38
|
if (!existsSync(dir))
|
|
39
39
|
return [];
|
|
40
|
-
|
|
40
|
+
const results = [];
|
|
41
41
|
try {
|
|
42
|
-
|
|
43
|
-
for (
|
|
42
|
+
const files = readdirSync(dir);
|
|
43
|
+
for (const file of files) {
|
|
44
44
|
if (!file.endsWith(".md"))
|
|
45
45
|
continue;
|
|
46
46
|
try {
|
|
47
|
-
|
|
47
|
+
const content = readFileSync(join(dir, file), "utf-8");
|
|
48
48
|
results.push({ filename: file, content });
|
|
49
49
|
}
|
|
50
50
|
catch {
|
|
@@ -58,15 +58,15 @@ export function readProjectRules(projectPath) {
|
|
|
58
58
|
return results;
|
|
59
59
|
}
|
|
60
60
|
export function writeProjectRules(projectPath, rules) {
|
|
61
|
-
|
|
61
|
+
const dir = join(projectPath, ".claude", "rules");
|
|
62
62
|
mkdirSync(dir, { recursive: true });
|
|
63
|
-
|
|
64
|
-
for (
|
|
63
|
+
const incoming = new Set();
|
|
64
|
+
for (const rule of rules) {
|
|
65
65
|
incoming.add(rule.filename);
|
|
66
66
|
}
|
|
67
67
|
try {
|
|
68
|
-
|
|
69
|
-
for (
|
|
68
|
+
const existing = readdirSync(dir);
|
|
69
|
+
for (const file of existing) {
|
|
70
70
|
if (file.endsWith(".md") && !incoming.has(file)) {
|
|
71
71
|
unlinkSync(join(dir, file));
|
|
72
72
|
}
|
|
@@ -75,16 +75,16 @@ export function writeProjectRules(projectPath, rules) {
|
|
|
75
75
|
catch {
|
|
76
76
|
// dir just created, nothing to delete
|
|
77
77
|
}
|
|
78
|
-
for (
|
|
78
|
+
for (const rule of rules) {
|
|
79
79
|
writeFileSync(join(dir, rule.filename), rule.content, "utf-8");
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
export function readProjectMcpServers(projectPath) {
|
|
83
|
-
|
|
83
|
+
const filePath = join(projectPath, ".mcp.json");
|
|
84
84
|
if (!existsSync(filePath))
|
|
85
85
|
return {};
|
|
86
86
|
try {
|
|
87
|
-
|
|
87
|
+
const parsed = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
88
88
|
return parsed?.mcpServers ?? {};
|
|
89
89
|
}
|
|
90
90
|
catch {
|
|
@@ -92,24 +92,24 @@ export function readProjectMcpServers(projectPath) {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
export function writeProjectMcpServers(projectPath, servers) {
|
|
95
|
-
|
|
95
|
+
const filePath = join(projectPath, ".mcp.json");
|
|
96
96
|
writeFileSync(filePath, JSON.stringify({ mcpServers: servers }, null, 2) + "\n", "utf-8");
|
|
97
97
|
}
|
|
98
98
|
export function readProjectSkills(projectPath) {
|
|
99
99
|
return scanSkillsDir(join(projectPath, ".claude", "skills"));
|
|
100
100
|
}
|
|
101
101
|
export function readGlobalRules() {
|
|
102
|
-
|
|
102
|
+
const dir = join(homedir(), ".claude", "rules");
|
|
103
103
|
if (!existsSync(dir))
|
|
104
104
|
return [];
|
|
105
|
-
|
|
105
|
+
const results = [];
|
|
106
106
|
try {
|
|
107
|
-
|
|
108
|
-
for (
|
|
107
|
+
const files = readdirSync(dir);
|
|
108
|
+
for (const file of files) {
|
|
109
109
|
if (!file.endsWith(".md"))
|
|
110
110
|
continue;
|
|
111
111
|
try {
|
|
112
|
-
|
|
112
|
+
const content = readFileSync(join(dir, file), "utf-8");
|
|
113
113
|
results.push({ filename: file, content });
|
|
114
114
|
}
|
|
115
115
|
catch {
|
|
@@ -123,20 +123,20 @@ export function readGlobalRules() {
|
|
|
123
123
|
return results;
|
|
124
124
|
}
|
|
125
125
|
export function readGlobalPermissions() {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
const settings = readJsonFile(join(homedir(), ".claude", "settings.json"));
|
|
127
|
+
const local = readJsonFile(join(homedir(), ".claude", "settings.local.json"));
|
|
128
|
+
const allow = new Set();
|
|
129
|
+
const deny = new Set();
|
|
130
130
|
collectPermissions(settings, allow, deny);
|
|
131
131
|
collectPermissions(local, allow, deny);
|
|
132
132
|
return { allow: Array.from(allow), deny: Array.from(deny) };
|
|
133
133
|
}
|
|
134
134
|
export function readGlobalMcpServers() {
|
|
135
|
-
|
|
135
|
+
const filePath = join(homedir(), ".claude.json");
|
|
136
136
|
if (!existsSync(filePath))
|
|
137
137
|
return {};
|
|
138
138
|
try {
|
|
139
|
-
|
|
139
|
+
const parsed = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
140
140
|
return parsed?.mcpServers ?? {};
|
|
141
141
|
}
|
|
142
142
|
catch {
|
|
@@ -144,8 +144,8 @@ export function readGlobalMcpServers() {
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
export function writeGlobalMcpServers(servers) {
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
const filePath = join(homedir(), ".claude.json");
|
|
148
|
+
let existing = {};
|
|
149
149
|
if (existsSync(filePath)) {
|
|
150
150
|
try {
|
|
151
151
|
existing = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
@@ -156,14 +156,14 @@ export function writeGlobalMcpServers(servers) {
|
|
|
156
156
|
writeFileSync(filePath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
157
157
|
}
|
|
158
158
|
export function readGlobalSkills() {
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
const results = [];
|
|
160
|
+
const dirs = [
|
|
161
161
|
join(homedir(), ".claude", "skills"),
|
|
162
162
|
join(homedir(), ".agents", "skills"),
|
|
163
163
|
];
|
|
164
|
-
for (
|
|
165
|
-
|
|
166
|
-
for (
|
|
164
|
+
for (const dir of dirs) {
|
|
165
|
+
const skills = scanSkillsDir(dir);
|
|
166
|
+
for (const skill of skills) {
|
|
167
167
|
results.push(skill);
|
|
168
168
|
}
|
|
169
169
|
}
|
|
@@ -180,38 +180,38 @@ function readJsonFile(filePath) {
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
function collectPermissions(obj, allow, deny) {
|
|
183
|
-
|
|
183
|
+
const perms = obj.permissions;
|
|
184
184
|
if (!perms)
|
|
185
185
|
return;
|
|
186
|
-
|
|
187
|
-
|
|
186
|
+
const allowArr = perms.allow;
|
|
187
|
+
const denyArr = perms.deny;
|
|
188
188
|
if (Array.isArray(allowArr)) {
|
|
189
|
-
for (
|
|
189
|
+
for (const item of allowArr) {
|
|
190
190
|
if (typeof item === "string")
|
|
191
191
|
allow.add(item);
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
if (Array.isArray(denyArr)) {
|
|
195
|
-
for (
|
|
195
|
+
for (const item of denyArr) {
|
|
196
196
|
if (typeof item === "string")
|
|
197
197
|
deny.add(item);
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
function parseFrontmatter(content) {
|
|
202
|
-
|
|
202
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
203
203
|
if (!match)
|
|
204
204
|
return { name: "", description: "" };
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
for (
|
|
210
|
-
|
|
211
|
-
|
|
205
|
+
const yaml = match[1];
|
|
206
|
+
let name = "";
|
|
207
|
+
let desc = "";
|
|
208
|
+
const lines = yaml.split(/\r?\n/);
|
|
209
|
+
for (let i = 0; i < lines.length; i++) {
|
|
210
|
+
const line = lines[i];
|
|
211
|
+
const nameMatch = line.match(/^name:\s*(.+)/);
|
|
212
212
|
if (nameMatch)
|
|
213
213
|
name = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
214
|
-
|
|
214
|
+
const descMatch = line.match(/^description:\s*(.+)/);
|
|
215
215
|
if (descMatch)
|
|
216
216
|
desc = descMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
217
217
|
}
|
|
@@ -220,21 +220,21 @@ function parseFrontmatter(content) {
|
|
|
220
220
|
function scanSkillsDir(dir) {
|
|
221
221
|
if (!existsSync(dir))
|
|
222
222
|
return [];
|
|
223
|
-
|
|
223
|
+
const results = [];
|
|
224
224
|
try {
|
|
225
|
-
|
|
226
|
-
for (
|
|
227
|
-
|
|
225
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
226
|
+
for (const entry of entries) {
|
|
227
|
+
const entryPath = join(dir, entry.name);
|
|
228
228
|
if (entry.isDirectory()) {
|
|
229
229
|
try {
|
|
230
|
-
|
|
231
|
-
for (
|
|
230
|
+
const subFiles = readdirSync(entryPath);
|
|
231
|
+
for (const subFile of subFiles) {
|
|
232
232
|
if (subFile.toUpperCase().startsWith("SKILL")) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
233
|
+
const skillPath = join(entryPath, subFile);
|
|
234
|
+
const content = readFileSync(skillPath, "utf-8");
|
|
235
|
+
const meta = parseFrontmatter(content);
|
|
236
|
+
const skillName = meta.name || entry.name;
|
|
237
|
+
const description = meta.description || firstNonEmptyLine(content.replace(/^---[\s\S]*?---\s*/, ""));
|
|
238
238
|
results.push({ name: skillName, description, path: skillPath });
|
|
239
239
|
break;
|
|
240
240
|
}
|
|
@@ -246,10 +246,10 @@ function scanSkillsDir(dir) {
|
|
|
246
246
|
}
|
|
247
247
|
else if (entry.name.toUpperCase().startsWith("SKILL")) {
|
|
248
248
|
try {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
249
|
+
const content = readFileSync(entryPath, "utf-8");
|
|
250
|
+
const meta = parseFrontmatter(content);
|
|
251
|
+
const name = meta.name || entry.name.replace(/\.[^.]+$/, "");
|
|
252
|
+
const description = meta.description || firstNonEmptyLine(content.replace(/^---[\s\S]*?---\s*/, ""));
|
|
253
253
|
results.push({ name, description, path: entryPath });
|
|
254
254
|
}
|
|
255
255
|
catch {
|
|
@@ -264,9 +264,9 @@ function scanSkillsDir(dir) {
|
|
|
264
264
|
return results;
|
|
265
265
|
}
|
|
266
266
|
function firstNonEmptyLine(text) {
|
|
267
|
-
|
|
268
|
-
for (
|
|
269
|
-
|
|
267
|
+
const lines = text.split("\n");
|
|
268
|
+
for (const line of lines) {
|
|
269
|
+
const trimmed = line.trim();
|
|
270
270
|
if (trimmed.length > 0)
|
|
271
271
|
return trimmed;
|
|
272
272
|
}
|
|
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { basename } from "node:path";
|
|
3
3
|
import { loadConfig, saveConfig } from "../config.js";
|
|
4
4
|
export function listProjects(nodeId) {
|
|
5
|
-
|
|
5
|
+
const config = loadConfig();
|
|
6
6
|
return config.projects.map(function (p) {
|
|
7
7
|
return {
|
|
8
8
|
slug: p.slug,
|
|
@@ -15,18 +15,18 @@ export function listProjects(nodeId) {
|
|
|
15
15
|
export function addProject(path, title) {
|
|
16
16
|
if (!existsSync(path))
|
|
17
17
|
return null;
|
|
18
|
-
|
|
18
|
+
const config = loadConfig();
|
|
19
19
|
if (config.projects.some(function (p) { return p.path === path; }))
|
|
20
20
|
return null;
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const slug = generateSlug(basename(path), config.projects.map(function (p) { return p.slug; }));
|
|
22
|
+
const project = { path: path, slug: slug, title: title || basename(path), env: {} };
|
|
23
23
|
config.projects.push(project);
|
|
24
24
|
saveConfig(config);
|
|
25
25
|
return { slug: project.slug, path: project.path, title: project.title, nodeId: "" };
|
|
26
26
|
}
|
|
27
27
|
export function removeProject(slug) {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const config = loadConfig();
|
|
29
|
+
const idx = config.projects.findIndex(function (p) { return p.slug === slug; });
|
|
30
30
|
if (idx === -1)
|
|
31
31
|
return false;
|
|
32
32
|
config.projects.splice(idx, 1);
|
|
@@ -34,15 +34,15 @@ export function removeProject(slug) {
|
|
|
34
34
|
return true;
|
|
35
35
|
}
|
|
36
36
|
export function getProjectBySlug(slug) {
|
|
37
|
-
|
|
37
|
+
const config = loadConfig();
|
|
38
38
|
return config.projects.find(function (p) { return p.slug === slug; });
|
|
39
39
|
}
|
|
40
40
|
export function generateSlug(name, existing) {
|
|
41
|
-
|
|
41
|
+
let slug = name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
42
42
|
if (!slug)
|
|
43
43
|
slug = "project";
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
let candidate = slug;
|
|
45
|
+
let counter = 1;
|
|
46
46
|
while (existing.includes(candidate)) {
|
|
47
47
|
candidate = slug + "-" + counter;
|
|
48
48
|
counter++;
|