@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.
@@ -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
- * - QMDManager: Vector search (if QMD is available)
7
+ * - VectorStore: Embedded vector search (sql.js + local embeddings)
8
8
  *
9
9
  * Features:
10
- * - memory_search: Unified search across notes, ontology, and QMD
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 directories
37
- sigmaMemory.init().catch(error => {
38
- console.warn("Failed to initialize sigma-memory:", error);
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 + QMD vector search)",
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 === 'qmd') {
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
- // Use notes manager to write
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, QMD)",
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
- // QMD status
240
- statusText += `## QMD Vector Search\n`;
241
- statusText += `- Available: ${status.qmd.available ? 'Yes' : 'No'}\n`;
242
- if (status.qmd.available && status.qmd.status) {
243
- statusText += `- Files indexed: ${status.qmd.status.files}\n`;
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.qmd.available) parts.push("QMD active");
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.1",
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": "^0.1.2",
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",
@@ -58,7 +58,18 @@ for (const pkg of sigmaPackages) {
58
58
  createLink(srcPkg, destLink, pkg);
59
59
  }
60
60
 
61
- // 3. Ensure settings.json has quietStartup: true
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 = {};