@phi-code-admin/phi-code 0.63.1 → 0.63.3
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/extensions/phi/memory.ts +155 -27
- package/package.json +2 -2
- package/scripts/postinstall.cjs +12 -1
package/extensions/phi/memory.ts
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Now powered by sigma-memory package which provides:
|
|
5
5
|
* - NotesManager: Markdown files management
|
|
6
6
|
* - OntologyManager: Knowledge graph with entities and relations
|
|
7
|
-
* -
|
|
7
|
+
* - VectorStore: Embedded vector search (sql.js + local embeddings)
|
|
8
8
|
*
|
|
9
9
|
* Features:
|
|
10
|
-
* - memory_search: Unified search across notes, ontology, and
|
|
10
|
+
* - memory_search: Unified search across notes, ontology, and vector store
|
|
11
11
|
* - memory_write: Write content to memory files
|
|
12
12
|
* - memory_read: Read specific memory files or list available ones
|
|
13
13
|
* - memory_status: Get status of all memory subsystems
|
|
@@ -26,16 +26,12 @@ import { readFileSync } from "node:fs";
|
|
|
26
26
|
import { SigmaMemory } from "sigma-memory";
|
|
27
27
|
|
|
28
28
|
export default function memoryExtension(pi: ExtensionAPI) {
|
|
29
|
-
// Initialize sigma-memory
|
|
30
|
-
const sigmaMemory = new SigmaMemory(
|
|
31
|
-
// Default configuration, can be overridden
|
|
32
|
-
qmdEnabled: true,
|
|
33
|
-
qmdCommand: 'qmd'
|
|
34
|
-
});
|
|
29
|
+
// Initialize sigma-memory with embedded vector store
|
|
30
|
+
const sigmaMemory = new SigmaMemory();
|
|
35
31
|
|
|
36
|
-
// Initialize memory
|
|
37
|
-
sigmaMemory.init().catch(
|
|
38
|
-
|
|
32
|
+
// Initialize memory + vector store (lazy model download on first search)
|
|
33
|
+
sigmaMemory.init().catch(() => {
|
|
34
|
+
// Non-critical — memory works without vectors
|
|
39
35
|
});
|
|
40
36
|
|
|
41
37
|
/**
|
|
@@ -44,12 +40,15 @@ export default function memoryExtension(pi: ExtensionAPI) {
|
|
|
44
40
|
pi.registerTool({
|
|
45
41
|
name: "memory_search",
|
|
46
42
|
label: "Memory Search",
|
|
47
|
-
description: "Search for content in memory using unified search (notes + ontology +
|
|
43
|
+
description: "Search for content in memory using unified search (notes + ontology + vector search)",
|
|
48
44
|
promptSnippet: "Search project memory (notes, ontology, vector search). ALWAYS call before answering questions about prior work, decisions, or project context.",
|
|
49
45
|
promptGuidelines: [
|
|
50
46
|
"Before answering questions about prior work, architecture, decisions, or project context: call memory_search first.",
|
|
51
47
|
"When starting work on a topic, search memory for existing notes and learnings.",
|
|
52
48
|
"After completing important work or learning something new, use memory_write to save it.",
|
|
49
|
+
"When a command fails or produces an unexpected error, document the error and fix in memory_write (self-improvement).",
|
|
50
|
+
"When the user corrects you, save the correction in memory_write so you never repeat the mistake.",
|
|
51
|
+
"After a significant debugging session, write a summary of root cause and solution to memory.",
|
|
53
52
|
],
|
|
54
53
|
parameters: Type.Object({
|
|
55
54
|
query: Type.String({ description: "Search query to find in memory" }),
|
|
@@ -96,7 +95,7 @@ export default function memoryExtension(pi: ExtensionAPI) {
|
|
|
96
95
|
resultText += `Relation: ${data.type} (${data.from} → ${data.to})\n`;
|
|
97
96
|
resultText += `Properties: ${JSON.stringify(data.properties)}\n\n`;
|
|
98
97
|
}
|
|
99
|
-
} else if (result.source === '
|
|
98
|
+
} else if (result.source === 'vectors') {
|
|
100
99
|
const data = result.data;
|
|
101
100
|
resultText += `File: ${data.file} (line ${data.line})\n`;
|
|
102
101
|
resultText += `> ${data.content}\n\n`;
|
|
@@ -136,14 +135,18 @@ export default function memoryExtension(pi: ExtensionAPI) {
|
|
|
136
135
|
const { content, file } = params as { content: string; file?: string };
|
|
137
136
|
|
|
138
137
|
try {
|
|
139
|
-
//
|
|
138
|
+
// Write to notes
|
|
140
139
|
sigmaMemory.notes.write(content, file);
|
|
141
|
-
|
|
142
140
|
const filename = file || new Date().toISOString().split('T')[0] + '.md';
|
|
141
|
+
|
|
142
|
+
// Auto-index in vector store (non-blocking)
|
|
143
|
+
sigmaMemory.vectors.addDocument(filename, content).catch(() => {
|
|
144
|
+
// Vector indexing failed silently — notes still saved
|
|
145
|
+
});
|
|
143
146
|
|
|
144
147
|
return {
|
|
145
|
-
content: [{ type: "text", text: `Content written to ${filename}` }],
|
|
146
|
-
details: { filename, contentLength: content.length }
|
|
148
|
+
content: [{ type: "text", text: `Content written to ${filename} (indexed for vector search)` }],
|
|
149
|
+
details: { filename, contentLength: content.length, vectorIndexed: true }
|
|
147
150
|
};
|
|
148
151
|
|
|
149
152
|
} catch (error) {
|
|
@@ -208,13 +211,141 @@ export default function memoryExtension(pi: ExtensionAPI) {
|
|
|
208
211
|
},
|
|
209
212
|
});
|
|
210
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Ontology tool - Add entities and relations to the knowledge graph
|
|
216
|
+
*/
|
|
217
|
+
pi.registerTool({
|
|
218
|
+
name: "ontology_add",
|
|
219
|
+
label: "Ontology Add",
|
|
220
|
+
description: "Add an entity or relation to the project knowledge graph. Entities represent things (projects, files, services, people). Relations connect them.",
|
|
221
|
+
promptGuidelines: [
|
|
222
|
+
"When discovering project architecture (services, databases, APIs), add entities and relations to the ontology.",
|
|
223
|
+
"When learning about how components connect, add relations (e.g. 'api-server' → 'uses' → 'postgres-db').",
|
|
224
|
+
],
|
|
225
|
+
parameters: Type.Object({
|
|
226
|
+
type: Type.Union([Type.Literal("entity"), Type.Literal("relation")], { description: "What to add: 'entity' or 'relation'" }),
|
|
227
|
+
// Entity fields
|
|
228
|
+
entityType: Type.Optional(Type.String({ description: "Entity type (e.g. Project, Service, Database, File, Person, Tool)" })),
|
|
229
|
+
name: Type.Optional(Type.String({ description: "Entity name (e.g. 'my-api', 'postgres-db')" })),
|
|
230
|
+
properties: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "Key-value properties (e.g. {language: 'TypeScript', port: '3000'})" })),
|
|
231
|
+
// Relation fields
|
|
232
|
+
from: Type.Optional(Type.String({ description: "Source entity ID" })),
|
|
233
|
+
to: Type.Optional(Type.String({ description: "Target entity ID" })),
|
|
234
|
+
relationType: Type.Optional(Type.String({ description: "Relation type (e.g. 'uses', 'depends-on', 'deployed-on', 'created-by')" })),
|
|
235
|
+
}),
|
|
236
|
+
|
|
237
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
238
|
+
const p = params as any;
|
|
239
|
+
try {
|
|
240
|
+
if (p.type === "entity") {
|
|
241
|
+
if (!p.entityType || !p.name) {
|
|
242
|
+
return { content: [{ type: "text", text: "Entity requires 'entityType' and 'name'" }], isError: true };
|
|
243
|
+
}
|
|
244
|
+
const id = sigmaMemory.ontology.addEntity({
|
|
245
|
+
type: p.entityType,
|
|
246
|
+
name: p.name,
|
|
247
|
+
properties: p.properties || {},
|
|
248
|
+
});
|
|
249
|
+
return {
|
|
250
|
+
content: [{ type: "text", text: `Entity added: **${p.name}** (${p.entityType}) — ID: \`${id}\`` }],
|
|
251
|
+
details: { id, type: p.entityType, name: p.name },
|
|
252
|
+
};
|
|
253
|
+
} else if (p.type === "relation") {
|
|
254
|
+
if (!p.from || !p.to || !p.relationType) {
|
|
255
|
+
return { content: [{ type: "text", text: "Relation requires 'from', 'to', and 'relationType'" }], isError: true };
|
|
256
|
+
}
|
|
257
|
+
const id = sigmaMemory.ontology.addRelation({
|
|
258
|
+
from: p.from,
|
|
259
|
+
to: p.to,
|
|
260
|
+
type: p.relationType,
|
|
261
|
+
properties: p.properties || {},
|
|
262
|
+
});
|
|
263
|
+
return {
|
|
264
|
+
content: [{ type: "text", text: `Relation added: \`${p.from}\` → **${p.relationType}** → \`${p.to}\` — ID: \`${id}\`` }],
|
|
265
|
+
details: { id, from: p.from, to: p.to, type: p.relationType },
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
return { content: [{ type: "text", text: "Type must be 'entity' or 'relation'" }], isError: true };
|
|
269
|
+
} catch (error) {
|
|
270
|
+
return { content: [{ type: "text", text: `Ontology error: ${error}` }], isError: true };
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Ontology query tool - Query the knowledge graph
|
|
277
|
+
*/
|
|
278
|
+
pi.registerTool({
|
|
279
|
+
name: "ontology_query",
|
|
280
|
+
label: "Ontology Query",
|
|
281
|
+
description: "Query the project knowledge graph. Find entities by type/name, get relations, find paths between entities, or get stats.",
|
|
282
|
+
parameters: Type.Object({
|
|
283
|
+
action: Type.Union([
|
|
284
|
+
Type.Literal("find"),
|
|
285
|
+
Type.Literal("relations"),
|
|
286
|
+
Type.Literal("path"),
|
|
287
|
+
Type.Literal("stats"),
|
|
288
|
+
Type.Literal("graph"),
|
|
289
|
+
], { description: "Query action: find (entities), relations (of entity), path (between entities), stats, graph (full export)" }),
|
|
290
|
+
entityType: Type.Optional(Type.String({ description: "Filter by entity type (for 'find' action)" })),
|
|
291
|
+
name: Type.Optional(Type.String({ description: "Filter by name (partial match, for 'find' action)" })),
|
|
292
|
+
entityId: Type.Optional(Type.String({ description: "Entity ID (for 'relations' action)" })),
|
|
293
|
+
fromId: Type.Optional(Type.String({ description: "Source entity ID (for 'path' action)" })),
|
|
294
|
+
toId: Type.Optional(Type.String({ description: "Target entity ID (for 'path' action)" })),
|
|
295
|
+
}),
|
|
296
|
+
|
|
297
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
298
|
+
const p = params as any;
|
|
299
|
+
try {
|
|
300
|
+
switch (p.action) {
|
|
301
|
+
case "find": {
|
|
302
|
+
const results = sigmaMemory.ontology.findEntity({ type: p.entityType, name: p.name });
|
|
303
|
+
if (results.length === 0) return { content: [{ type: "text", text: "No entities found." }] };
|
|
304
|
+
const text = results.map(e => `- **${e.name}** (${e.type}) ID:\`${e.id}\` ${JSON.stringify(e.properties)}`).join("\n");
|
|
305
|
+
return { content: [{ type: "text", text: `Found ${results.length} entities:\n${text}` }] };
|
|
306
|
+
}
|
|
307
|
+
case "relations": {
|
|
308
|
+
if (!p.entityId) return { content: [{ type: "text", text: "'entityId' required" }], isError: true };
|
|
309
|
+
const rels = sigmaMemory.ontology.findRelations(p.entityId);
|
|
310
|
+
if (rels.length === 0) return { content: [{ type: "text", text: "No relations found." }] };
|
|
311
|
+
const text = rels.map(r => `- \`${r.from}\` → **${r.type}** → \`${r.to}\``).join("\n");
|
|
312
|
+
return { content: [{ type: "text", text: `Found ${rels.length} relations:\n${text}` }] };
|
|
313
|
+
}
|
|
314
|
+
case "path": {
|
|
315
|
+
if (!p.fromId || !p.toId) return { content: [{ type: "text", text: "'fromId' and 'toId' required" }], isError: true };
|
|
316
|
+
const path = sigmaMemory.ontology.queryPath(p.fromId, p.toId);
|
|
317
|
+
if (!path) return { content: [{ type: "text", text: "No path found between these entities." }] };
|
|
318
|
+
const text = path.map(s => `${s.entity.name}${s.relation ? ` → [${s.relation.type}]` : ""}`).join(" → ");
|
|
319
|
+
return { content: [{ type: "text", text: `Path: ${text}` }] };
|
|
320
|
+
}
|
|
321
|
+
case "stats": {
|
|
322
|
+
const stats = sigmaMemory.ontology.stats();
|
|
323
|
+
const graph = sigmaMemory.ontology.getGraph();
|
|
324
|
+
let text = `**Ontology Stats:**\n- Entities: ${graph.entities.length}\n- Relations: ${graph.relations.length}\n`;
|
|
325
|
+
text += `\nBy type:\n`;
|
|
326
|
+
for (const [type, count] of Object.entries(stats.entitiesByType)) text += ` - ${type}: ${count}\n`;
|
|
327
|
+
return { content: [{ type: "text", text }] };
|
|
328
|
+
}
|
|
329
|
+
case "graph": {
|
|
330
|
+
const graph = sigmaMemory.ontology.export();
|
|
331
|
+
return { content: [{ type: "text", text: JSON.stringify(graph, null, 2) }] };
|
|
332
|
+
}
|
|
333
|
+
default:
|
|
334
|
+
return { content: [{ type: "text", text: "Action must be: find, relations, path, stats, graph" }], isError: true };
|
|
335
|
+
}
|
|
336
|
+
} catch (error) {
|
|
337
|
+
return { content: [{ type: "text", text: `Ontology query error: ${error}` }], isError: true };
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
|
|
211
342
|
/**
|
|
212
343
|
* Memory status tool - Get status of all memory subsystems
|
|
213
344
|
*/
|
|
214
345
|
pi.registerTool({
|
|
215
346
|
name: "memory_status",
|
|
216
347
|
label: "Memory Status",
|
|
217
|
-
description: "Get status of all memory subsystems (notes, ontology,
|
|
348
|
+
description: "Get status of all memory subsystems (notes, ontology, vector search)",
|
|
218
349
|
parameters: Type.Object({}),
|
|
219
350
|
|
|
220
351
|
async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) {
|
|
@@ -236,14 +367,11 @@ export default function memoryExtension(pi: ExtensionAPI) {
|
|
|
236
367
|
statusText += `- Entities by type: ${JSON.stringify(status.ontology.entitiesByType)}\n`;
|
|
237
368
|
statusText += `- Relations by type: ${JSON.stringify(status.ontology.relationsByType)}\n\n`;
|
|
238
369
|
|
|
239
|
-
//
|
|
240
|
-
statusText += `##
|
|
241
|
-
statusText += `-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
statusText += `- Chunks: ${status.qmd.status.chunks}\n`;
|
|
245
|
-
statusText += `- Last update: ${status.qmd.status.lastUpdate || 'Never'}\n`;
|
|
246
|
-
}
|
|
370
|
+
// Vector store status
|
|
371
|
+
statusText += `## Vector Search (embedded)\n`;
|
|
372
|
+
statusText += `- Documents: ${status.vectors.documentCount}\n`;
|
|
373
|
+
statusText += `- Chunks: ${status.vectors.chunkCount}\n`;
|
|
374
|
+
statusText += `- Last update: ${status.vectors.lastUpdate || 'Never'}\n`;
|
|
247
375
|
|
|
248
376
|
return {
|
|
249
377
|
content: [{ type: "text", text: statusText }],
|
|
@@ -292,7 +420,7 @@ export default function memoryExtension(pi: ExtensionAPI) {
|
|
|
292
420
|
const parts: string[] = [];
|
|
293
421
|
if (status.notes.count > 0) parts.push(`${status.notes.count} notes`);
|
|
294
422
|
if (status.ontology.entities > 0) parts.push(`${status.ontology.entities} entities`);
|
|
295
|
-
if (status.
|
|
423
|
+
if (status.vectors.chunkCount > 0) parts.push(`${status.vectors.chunkCount} vectors`);
|
|
296
424
|
if (parts.length > 0) {
|
|
297
425
|
ctx.ui.notify(`🧠 Memory: ${parts.join(", ")}`, "info");
|
|
298
426
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phi-code-admin/phi-code",
|
|
3
|
-
"version": "0.63.
|
|
3
|
+
"version": "0.63.3",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"phi-code-tui": "^0.56.3",
|
|
62
62
|
"proper-lockfile": "^4.1.2",
|
|
63
63
|
"sigma-agents": "^0.1.6",
|
|
64
|
-
"sigma-memory": "
|
|
64
|
+
"sigma-memory": "0.2.0",
|
|
65
65
|
"sigma-skills": "^0.1.2",
|
|
66
66
|
"strip-ansi": "^7.1.0",
|
|
67
67
|
"undici": "^7.19.1",
|
package/scripts/postinstall.cjs
CHANGED
|
@@ -58,7 +58,18 @@ for (const pkg of sigmaPackages) {
|
|
|
58
58
|
createLink(srcPkg, destLink, pkg);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
// 3. Ensure
|
|
61
|
+
// 3. Ensure memory directories exist (vector store DB created on first use)
|
|
62
|
+
const memoryDir = join(homedir(), ".phi", "memory");
|
|
63
|
+
const memoryNotesDir = join(memoryDir, "notes");
|
|
64
|
+
const memoryOntologyDir = join(memoryDir, "ontology");
|
|
65
|
+
for (const dir of [memoryDir, memoryNotesDir, memoryOntologyDir]) {
|
|
66
|
+
if (!existsSync(dir)) {
|
|
67
|
+
mkdirSync(dir, { recursive: true });
|
|
68
|
+
console.log(` Φ Created ${dir}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 4. Ensure settings.json has quietStartup: true
|
|
62
73
|
const settingsPath = join(agentDir, "settings.json");
|
|
63
74
|
try {
|
|
64
75
|
let settings = {};
|