@evandu/super-pi 0.1.111 → 0.1.113

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.
Files changed (35) hide show
  1. package/dist/.super-pi-check-inputs.json +2 -2
  2. package/dist/client/.super-pi-client-sources.json +8 -7
  3. package/dist/client/assets/{CodeMirrorEditor-CaF1aTpR.js → CodeMirrorEditor-moFTk5SE.js} +2 -2
  4. package/dist/client/assets/{dist-aC6cFQVF.js → dist-BR4s0dNg.js} +1 -1
  5. package/dist/client/assets/{dist-Bl_iPEld.js → dist-BUuPRG15.js} +1 -1
  6. package/dist/client/assets/{dist-CPo4IuhO.js → dist-Bds_PUYK.js} +1 -1
  7. package/dist/client/assets/{dist-BydrWuS9.js → dist-BsO40qxQ.js} +1 -1
  8. package/dist/client/assets/{dist-jDX3NY00.js → dist-CCYnr6ul.js} +1 -1
  9. package/dist/client/assets/{dist-f78PQc3f.js → dist-CL65aMXY.js} +1 -1
  10. package/dist/client/assets/{dist-BjrDHYro.js → dist-D9m04q-s.js} +1 -1
  11. package/dist/client/assets/{dist-o-3gMx5l.js → dist-DIfmMr8s.js} +1 -1
  12. package/dist/client/assets/{dist-CJHSsi_2.js → dist-DPDGd2jl.js} +1 -1
  13. package/dist/client/assets/{dist-C9gmrpfb.js → dist-DPvy57Ph.js} +1 -1
  14. package/dist/client/assets/{dist-CKziNS6e.js → dist-DQjJMys5.js} +1 -1
  15. package/dist/client/assets/{dist-CbkXScEJ.js → dist-Dnr5BxAi.js} +1 -1
  16. package/dist/client/assets/{dist-C2JgfsQ0.js → dist-DrbUrVWB.js} +1 -1
  17. package/dist/client/assets/{dist-BG1mkRxC.js → dist-DuWhzonJ.js} +1 -1
  18. package/dist/client/assets/{dist-DnwnynHW.js → dist-TgNJUr7p.js} +1 -1
  19. package/dist/client/assets/{dist-Bv-QAejS.js → dist-rhPxFuD1.js} +1 -1
  20. package/dist/client/assets/{index-CME3BwBz.js → index-BixdV7aQ.js} +23 -23
  21. package/dist/client/assets/index-CGXMw9LP.css +1 -0
  22. package/dist/client/index.html +2 -2
  23. package/dist/server/application/run-service.d.ts +7 -0
  24. package/dist/server/application/run-service.js +16 -0
  25. package/dist/server/file-name-index.d.ts +144 -0
  26. package/dist/server/file-name-index.js +324 -0
  27. package/dist/server/http-server.js +112 -3
  28. package/dist/server/index.js +10 -0
  29. package/dist/server/server-context.d.ts +9 -0
  30. package/dist/server/skills.d.ts +23 -0
  31. package/dist/server/skills.js +171 -0
  32. package/dist/server/types.d.ts +7 -0
  33. package/package.json +3 -1
  34. package/scripts/patch-brace-expansion.mjs +1 -1
  35. package/dist/client/assets/index-CauO59H-.css +0 -1
@@ -0,0 +1,23 @@
1
+ import type { SkillSummary } from "./types.js";
2
+ import type { ActiveMemory } from "./active-memory.js";
3
+ /**
4
+ * Loads and returns all available skills for a given workspace and global environment.
5
+ * Priority is given to Pi SDK's DefaultResourceLoader which scans project and user skills.
6
+ * Falls back to direct filesystem scanning if Pi SDK is unavailable.
7
+ */
8
+ export declare function getAvailableSkills(workspacePath?: string, options?: {
9
+ memory?: ActiveMemory;
10
+ workspaceId?: string;
11
+ }): Promise<SkillSummary[]>;
12
+ /**
13
+ * Reads the markdown content of a specific skill by name or ID.
14
+ */
15
+ export declare function readSkill(nameOrId: string, workspacePath?: string, options?: {
16
+ memory?: ActiveMemory;
17
+ workspaceId?: string;
18
+ }): Promise<{
19
+ id: string;
20
+ name: string;
21
+ description: string;
22
+ content: string;
23
+ } | null>;
@@ -0,0 +1,171 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readdir, readFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ /**
6
+ * Loads and returns all available skills for a given workspace and global environment.
7
+ * Priority is given to Pi SDK's DefaultResourceLoader which scans project and user skills.
8
+ * Falls back to direct filesystem scanning if Pi SDK is unavailable.
9
+ */
10
+ export async function getAvailableSkills(workspacePath, options) {
11
+ const cwd = workspacePath && existsSync(workspacePath) ? workspacePath : process.cwd();
12
+ const skillsMap = new Map();
13
+ // 1. Try loading via Pi SDK DefaultResourceLoader
14
+ try {
15
+ const sdk = await import("@earendil-works/pi-coding-agent").catch(() => null);
16
+ if (sdk?.DefaultResourceLoader) {
17
+ const agentDir = join(homedir(), ".pi", "agent");
18
+ const loader = new sdk.DefaultResourceLoader({ cwd, agentDir });
19
+ await loader.reload();
20
+ const res = loader.getSkills();
21
+ if (Array.isArray(res?.skills)) {
22
+ for (const s of res.skills) {
23
+ if (!s.name)
24
+ continue;
25
+ const scope = s.sourceInfo?.scope === "project" ? "workspace" : "global";
26
+ skillsMap.set(s.name, {
27
+ id: `skill:${s.name}`,
28
+ name: s.name,
29
+ description: s.description || "",
30
+ scope,
31
+ filePath: s.filePath,
32
+ });
33
+ }
34
+ }
35
+ }
36
+ }
37
+ catch {
38
+ // Ignore and proceed to filesystem fallback
39
+ }
40
+ // 2. Fallback: Direct directory scan if SDK loaded nothing or was unavailable
41
+ if (skillsMap.size === 0) {
42
+ const scanDirs = [
43
+ { dir: join(cwd, ".pi", "skills"), scope: "workspace" },
44
+ { dir: join(cwd, ".agents", "skills"), scope: "workspace" },
45
+ { dir: join(homedir(), ".pi", "agent", "skills"), scope: "global" },
46
+ { dir: join(homedir(), ".agents", "skills"), scope: "global" },
47
+ ];
48
+ for (const { dir, scope } of scanDirs) {
49
+ if (!existsSync(dir))
50
+ continue;
51
+ try {
52
+ const entries = await readdir(dir, { withFileTypes: true });
53
+ for (const entry of entries) {
54
+ let skillFile = "";
55
+ let skillName = entry.name;
56
+ if (entry.isDirectory()) {
57
+ const candidate = join(dir, entry.name, "SKILL.md");
58
+ if (existsSync(candidate)) {
59
+ skillFile = candidate;
60
+ skillName = entry.name;
61
+ }
62
+ }
63
+ else if (entry.isFile() && entry.name.endsWith(".md")) {
64
+ skillFile = join(dir, entry.name);
65
+ skillName = entry.name.replace(/\.md$/, "");
66
+ }
67
+ if (skillFile) {
68
+ try {
69
+ const text = await readFile(skillFile, "utf-8");
70
+ const frontmatterMatch = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
71
+ let description = "";
72
+ let parsedName = skillName;
73
+ if (frontmatterMatch) {
74
+ const nameMatch = /^name:\s*(.+)$/m.exec(frontmatterMatch[1]);
75
+ if (nameMatch)
76
+ parsedName = nameMatch[1].trim();
77
+ const descMatch = /^description:\s*(.+)$/m.exec(frontmatterMatch[1]);
78
+ if (descMatch)
79
+ description = descMatch[1].trim();
80
+ }
81
+ if (!skillsMap.has(parsedName)) {
82
+ skillsMap.set(parsedName, {
83
+ id: `skill:${parsedName}`,
84
+ name: parsedName,
85
+ description,
86
+ scope,
87
+ filePath: skillFile,
88
+ });
89
+ }
90
+ }
91
+ catch {
92
+ // Ignore unreadable file
93
+ }
94
+ }
95
+ }
96
+ }
97
+ catch {
98
+ // Ignore unreadable directory
99
+ }
100
+ }
101
+ }
102
+ // 3. Optional: merge active memory notes skills if available and not colliding
103
+ if (options?.memory && options?.workspaceId) {
104
+ try {
105
+ const memoryEntries = await options.memory.notes.apply(options.workspaceId, "", { target: "skills", action: "list" });
106
+ if (Array.isArray(memoryEntries)) {
107
+ for (const entry of memoryEntries) {
108
+ const firstLine = entry.content.split(/\r?\n/, 1)[0].replace(/^#+\s*/, "").trim();
109
+ const name = firstLine || "unnamed-skill";
110
+ if (!skillsMap.has(name) && !skillsMap.has(entry.id)) {
111
+ skillsMap.set(`mem:${entry.id}`, {
112
+ id: `skill:${entry.id}`,
113
+ name,
114
+ description: "Active memory skill",
115
+ scope: "memory",
116
+ });
117
+ }
118
+ }
119
+ }
120
+ }
121
+ catch {
122
+ // Memory may not be ready or active
123
+ }
124
+ }
125
+ return Array.from(skillsMap.values()).sort((a, b) => a.name.localeCompare(b.name));
126
+ }
127
+ /**
128
+ * Reads the markdown content of a specific skill by name or ID.
129
+ */
130
+ export async function readSkill(nameOrId, workspacePath, options) {
131
+ const cleanId = nameOrId.startsWith("skill:") ? nameOrId.slice("skill:".length) : nameOrId;
132
+ const skills = await getAvailableSkills(workspacePath, options);
133
+ const found = skills.find((s) => s.name === cleanId || s.name === nameOrId || s.id === nameOrId || s.id === `skill:${cleanId}`);
134
+ if (!found)
135
+ return null;
136
+ // If it's a file on disk
137
+ if (found.filePath && existsSync(found.filePath)) {
138
+ try {
139
+ const content = await readFile(found.filePath, "utf-8");
140
+ return {
141
+ id: found.id,
142
+ name: found.name,
143
+ description: found.description,
144
+ content,
145
+ };
146
+ }
147
+ catch {
148
+ return null;
149
+ }
150
+ }
151
+ // If it's stored in active memory
152
+ if (found.scope === "memory" && options?.memory && options?.workspaceId) {
153
+ try {
154
+ const entryId = found.id.startsWith("skill:") ? found.id.slice("skill:".length) : found.id;
155
+ const memoryEntries = await options.memory.notes.apply(options.workspaceId, "", { target: "skills", action: "list" });
156
+ const entry = memoryEntries.find((e) => e.id === entryId || e.id === found.name);
157
+ if (entry) {
158
+ return {
159
+ id: found.id,
160
+ name: found.name,
161
+ description: found.description,
162
+ content: entry.content,
163
+ };
164
+ }
165
+ }
166
+ catch {
167
+ return null;
168
+ }
169
+ }
170
+ return null;
171
+ }
@@ -164,6 +164,13 @@ export type MessageAttachment = {
164
164
  content: string;
165
165
  };
166
166
  export type MessageAttachments = MessageAttachment[];
167
+ export interface SkillSummary {
168
+ id: string;
169
+ name: string;
170
+ description: string;
171
+ scope?: "workspace" | "global" | "memory";
172
+ filePath?: string;
173
+ }
167
174
  /**
168
175
  * ONE PARAMETER OF A TOOL CALL, as a call block lists it, and the boundary that keeps Pi's arguments out
169
176
  * of `state.json`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evandu/super-pi",
3
- "version": "0.1.111",
3
+ "version": "0.1.113",
4
4
  "description": "Web control plane and companion for Pi",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -89,6 +89,7 @@
89
89
  "scripts/files-missing-path.test.mjs",
90
90
  "scripts/file-changes.test.mjs",
91
91
  "scripts/file-index.test.mjs",
92
+ "scripts/file-name-index.test.mjs",
92
93
  "scripts/file-content-search.test.mjs",
93
94
  "scripts/file-diff-client.test.mjs",
94
95
  "scripts/task-tool.test.mjs",
@@ -116,6 +117,7 @@
116
117
  "scripts/static-client-assets.test.mjs",
117
118
  "scripts/cli.test.mjs",
118
119
  "scripts/check-runner.test.mjs",
120
+ "scripts/skills-discovery.test.mjs",
119
121
  "scripts/shared-chrome.test.mjs"
120
122
  ],
121
123
  "check:browser": [
@@ -220,7 +220,7 @@ function findVulnerableCopies() {
220
220
  try { version = JSON.parse(readFileSync(manifest, "utf8")).version ?? ""; } catch { continue; }
221
221
  if (!version) continue;
222
222
  if (compareVersions(version, VULNERABLE_BELOW) >= 0) continue;
223
- found.push({ directory, version, lockKey: lockKeyOf(directory) });
223
+ found.push({ directory, version });
224
224
  }
225
225
  return found;
226
226
  }