pi-hermes-memory 0.7.5 → 0.7.7

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/README.md CHANGED
@@ -69,7 +69,7 @@ The extension manages three types of knowledge:
69
69
  |---|---|---|---|
70
70
  | **Memory** (MEMORY.md) | Facts — env details, project conventions, tool quirks | 5,000 chars max | Searchable by default |
71
71
  | **User Profile** (USER.md) | Who you are — name, preferences, communication style | 5,000 chars max | Searchable by default |
72
- | **Skills** (skills/*.md) | Procedures — *how* to do something, reusable across sessions | Unlimited | Available through skill tool |
72
+ | **Skills** (Pi-native `SKILL.md`) | Procedures — *how* to do something, reusable across sessions | Unlimited | Discoverable by Pi + manageable via the skill tool |
73
73
 
74
74
  ![Memory + Skills Architecture](docs/images/memory-architecture.svg)
75
75
 
@@ -119,7 +119,7 @@ System Prompt
119
119
  └─────────────────────────────────────────┘
120
120
  ```
121
121
 
122
- Set `"memoryPolicyStyle"` to `"full"`, `"compact"`, `"custom"`, or `"none"` to choose policy verbosity while keeping policy-only mode. Set `"memoryMode": "legacy-inject"` to restore the old behavior that injects MEMORY.md, USER.md, project memory, recent failures, and the skill index into the prompt.
122
+ Set `"memoryPolicyStyle"` to `"full"`, `"compact"`, `"custom"`, or `"none"` to choose policy verbosity while keeping policy-only mode. Set `"memoryMode": "legacy-inject"` to restore the old behavior that injects MEMORY.md, USER.md, project memory, and recent failures into the prompt.
123
123
 
124
124
  ## Failure Memory
125
125
 
@@ -177,13 +177,29 @@ The agent also gets a `skill` tool for saving reusable procedures:
177
177
 
178
178
  | Action | What it does |
179
179
  |---|---|
180
- | `create` | Save a new skill (name, description, step-by-step body) |
181
- | `view` | Read a skill's full content, or list all skills if no name given |
182
- | `patch` | Update one section of an existing skill (e.g., just the Procedure section) |
183
- | `edit` | Replace the description and/or full body of a skill |
184
- | `delete` | Remove a skill |
180
+ | `create` | Save a new skill (name, description, step-by-step body, optional `scope`) |
181
+ | `view` | Read a skill's full content by `skill_id`, or list all skills if no id is given |
182
+ | `patch` | Update one section of an existing skill by `skill_id` |
183
+ | `edit` | Replace the description and/or full body of a skill by `skill_id` |
184
+ | `delete` | Remove a skill by `skill_id` |
185
185
 
186
- Skills are stored as `SKILL.md` files in `~/.pi/agent/memory/skills/`. Each has a structured body:
186
+ Skills are stored in Pi-native locations:
187
+
188
+ - Global skills: `~/.pi/agent/skills/<slug>/SKILL.md`
189
+ - Project skills: `~/.pi/agent/projects-memory/<project>/skills/<slug>/SKILL.md`
190
+
191
+ The extension classifies new skills automatically:
192
+
193
+ - `global` for transferable procedures
194
+ - `project` for repo-specific workflows tied to local paths, scripts, architecture, deploy steps, or conventions
195
+
196
+ Global skill creation also has duplicate/similarity guards:
197
+
198
+ - exact slug match → blocked (update existing via `patch`/`edit`)
199
+ - near-name + high description similarity → blocked as similar (enhance existing)
200
+ - near-name + low description similarity → blocked as name collision (rename to a clearer distinct skill name)
201
+
202
+ Each skill uses a structured `SKILL.md` body:
187
203
 
188
204
  ```markdown
189
205
  ---
@@ -209,13 +225,23 @@ When you see TypeScript compilation errors, especially in monorepo setups.
209
225
  Run `tsc --noEmit` and confirm zero errors.
210
226
  ```
211
227
 
228
+ ### Project Skill Discovery (`resources_discover`)
229
+
230
+ Project-scoped skills are loaded via Pi's `resources_discover` hook.
231
+
232
+ On discovery, the extension returns the active project's skills directory as a skill path:
233
+
234
+ - `~/.pi/agent/projects-memory/<project>/skills/`
235
+
236
+ This lets Pi discover project skills as native skills without copying them into the global skills folder.
237
+
212
238
  ### Memory vs User Profile vs Skills
213
239
 
214
240
  | Store | File | What goes here | Limit |
215
241
  |---|---|---|---|
216
242
  | **memory** | `MEMORY.md` | Agent's notes — env facts, project conventions, tool quirks, lessons learned | 5,000 chars |
217
243
  | **user** | `USER.md` | User profile — name, preferences, communication style, habits | 5,000 chars |
218
- | **skills** | `skills/*.md` | Procedures — *how* to debug, deploy, test, or fix something | Unlimited |
244
+ | **skills** | `~/.pi/agent/skills/<slug>/SKILL.md` or `projects-memory/<project>/skills/<slug>/SKILL.md` | Procedures — *how* to debug, deploy, test, or fix something | Unlimited |
219
245
  | **extended** | `sessions.db` | Searchable memories beyond the core limit | Unlimited |
220
246
  | **sessions** | `sessions.db` | Past conversation history (searchable via FTS5) | Unlimited |
221
247
 
@@ -295,13 +321,13 @@ This means skills build up naturally over time without you having to ask.
295
321
  | Command | What it does |
296
322
  |---|---|
297
323
  | `/memory-insights` | Shows everything stored in memory and user profile |
298
- | `/memory-skills` | Lists all agent-created skills |
324
+ | `/memory-skills` | Lists global and active-project skills with their ids |
299
325
  | `/memory-consolidate` | Manually trigger memory consolidation to free space |
300
326
  | `/memory-interview` | Answer a few questions to pre-fill your user profile |
301
327
  | `/memory-switch-project` | List all project memories and their entry counts |
302
328
  | `/memory-index-sessions` | Import past Pi sessions into the search database |
303
329
  | `/memory-sync-markdown` | Backfill Markdown memories into the SQLite search store |
304
- | `/memory-preview-context` | Preview the memory policy or legacy memory/skill blocks appended to the system prompt |
330
+ | `/memory-preview-context` | Preview the memory policy or legacy memory blocks appended to the system prompt |
305
331
  | `/learn-memory-tool` | Skill that teaches users how to use the memory system |
306
332
 
307
333
  ### `/memory-insights` Output
@@ -331,13 +357,17 @@ This means skills build up naturally over time without you having to ask.
331
357
  ║ 🧠 Procedural Skills ║
332
358
  ╚══════════════════════════════════════════════╝
333
359
 
360
+ Global Skills
361
+ ─────────────
334
362
  📄 debug-typescript-errors
335
363
  Step-by-step approach to debugging TS errors in monorepos
336
- file: debug-typescript-errors.md
364
+ id: global:debug-typescript-errors
337
365
 
366
+ Project Skills (pi-hermes-memory)
367
+ ────────────────────────────────
338
368
  📄 deploy-checklist
339
369
  Pre-deploy verification steps for this project
340
- file: deploy-checklist.md
370
+ id: project:pi-hermes-memory:deploy-checklist
341
371
  ```
342
372
 
343
373
  ## Configuration
@@ -363,6 +393,7 @@ Create `~/.pi/agent/hermes-memory-config.json`:
363
393
  "failureInjectionEnabled": true,
364
394
  "failureInjectionMaxAgeDays": 7,
365
395
  "failureInjectionMaxEntries": 5,
396
+ "consolidationTimeoutMs": 60000,
366
397
  "flushOnCompact": true,
367
398
  "flushOnShutdown": true,
368
399
  "flushMinTurns": 6,
@@ -372,7 +403,7 @@ Create `~/.pi/agent/hermes-memory-config.json`:
372
403
 
373
404
  | Setting | Default | Description |
374
405
  |---|---|---|
375
- | `memoryMode` | `policy-only` | Prompt behavior: `policy-only` injects only memory policy; `legacy-inject` restores full memory/skill prompt injection |
406
+ | `memoryMode` | `policy-only` | Prompt behavior: `policy-only` injects only memory policy; `legacy-inject` restores full memory prompt injection |
376
407
  | `memoryPolicyStyle` | `full` | Policy text used in `policy-only` mode: `full` preserves the default v0.7 policy; `compact` uses shorter built-in guidance; `custom` uses `memoryPolicyCustomText`; `none` injects no policy text |
377
408
  | `memoryPolicyCustomText` | unset | Custom policy text used when `memoryPolicyStyle` is `custom`; blank or missing text falls back to `compact` |
378
409
  | `memoryCharLimit` | `5000` | Max characters in MEMORY.md |
@@ -386,6 +417,7 @@ Create `~/.pi/agent/hermes-memory-config.json`:
386
417
  | `reviewEnabled` | `true` | Enable/disable background learning loop |
387
418
  | `memoryOverflowStrategy` | `auto-consolidate` | Behavior when MEMORY.md, USER.md, or project-scoped memory reaches its character limit: `auto-consolidate` runs the existing consolidation flow; `reject` returns an error; `fifo-evict` rotates older entries in file order until the new entry fits |
388
419
  | `autoConsolidate` | `true` | Legacy alias for `memoryOverflowStrategy` when `memoryOverflowStrategy` is not set (`true` = `auto-consolidate`, `false` = `reject`) |
420
+ | `consolidationTimeoutMs` | `60000` | Maximum time in milliseconds for auto-consolidation to complete |
389
421
  | `correctionDetection` | `true` | Detect user corrections and save immediately |
390
422
  | `correctionStrongPatterns` | unset | Optional case-insensitive regex sources replacing strong correction patterns; omitted preserves defaults, invalid entries are ignored |
391
423
  | `correctionWeakPatterns` | unset | Optional case-insensitive regex sources replacing weak correction patterns; omitted preserves defaults, invalid entries are ignored |
@@ -403,23 +435,29 @@ Create `~/.pi/agent/hermes-memory-config.json`:
403
435
 
404
436
  ```
405
437
  ~/.pi/agent/
438
+ ├── skills/ ← Global Pi-native skills
439
+ │ ├── debug-typescript-errors/
440
+ │ │ └── SKILL.md
441
+ │ └── testing-checklist/
442
+ │ └── SKILL.md
406
443
  ├── projects-memory/ ← ALL project-scoped memories (one subfolder per project)
407
444
  │ ├── my-project/
408
- │ │ └── MEMORY.md
445
+ │ │ ├── MEMORY.md
446
+ │ │ └── skills/
447
+ │ │ └── deploy-checklist/
448
+ │ │ └── SKILL.md
409
449
  │ └── another-project/
410
450
  │ └── MEMORY.md
411
451
  ├── memory/ ← Global memory
412
452
  │ ├── MEMORY.md ← Agent's personal notes (env facts, patterns, lessons)
413
453
  │ ├── USER.md ← User profile (name, preferences, habits)
414
454
  │ ├── sessions.db ← SQLite database (session history + extended memory)
415
- │ └── skills/
416
- │ ├── debug-typescript-errors.md
417
- │ └── deploy-checklist.md
455
+ │ └── .skills-migrated-to-pi-native
418
456
  ├── hermes-memory-config.json
419
457
  └── ...
420
458
  ```
421
459
 
422
- These are plain markdown files. You can read and edit them directly if you want to curate what the agent remembers. Memory entries are separated by `§` (section sign). Skills use standard SKILL.md format with frontmatter.
460
+ These are plain markdown files. You can read and edit them directly if you want to curate what the agent remembers. Memory entries are separated by `§` (section sign). Skills use Pi-compatible `SKILL.md` files with frontmatter.
423
461
 
424
462
  If you are upgrading from a version that stored project memory directly at `~/.pi/agent/<project>/MEMORY.md`, the extension copies or merges those entries into `~/.pi/agent/projects-memory/<project>/MEMORY.md` on startup. The old folders are left in place as a backup.
425
463
 
@@ -433,7 +471,8 @@ The `sessions.db` SQLite database stores session history and extended memory ent
433
471
  - **Older Markdown memories may need backfill**: If you saved memories before the SQLite mirror existed or search looks stale, run `/memory-sync-markdown`.
434
472
  - **Core memory limits still apply**: SQLite search mirroring does not bypass the 5,000-char core Markdown limit. If consolidation cannot free space, the write fails instead of becoming SQLite-only memory invisibly.
435
473
  - **System prompts are invisible**: Pi's TUI does not display the system prompt. Use `/memory-preview-context` to inspect whether policy-only or legacy memory injection is active.
436
- - **Skills are agent-generated**: Skills are created by the agent based on its experience. They may not always be perfectly structured. You can edit or delete them in `~/.pi/agent/memory/skills/`.
474
+ - **Project skill visibility depends on Pi discovery cycles**: project skills are exposed through `resources_discover` using the active project's `skills/` path. If a new skill doesn't show up immediately in a running session, trigger a reload/new session so Pi refreshes discovered resources.
475
+ - **Skills are agent-generated**: Skills are created by the agent based on its experience. They may not always be perfectly structured. You can edit or delete them in `~/.pi/agent/skills/` or the active project's `skills/` folder.
437
476
 
438
477
  ## Architecture
439
478
 
@@ -45,7 +45,7 @@ Target state:
45
45
  "image": "https://raw.githubusercontent.com/chandra447/pi-hermes-memory/main/docs/assets/hermes-memory-preview.png"
46
46
  },
47
47
  "peerDependencies": {
48
- "@mariozechner/pi-coding-agent": "*"
48
+ "@earendil-works/pi-coding-agent": "*"
49
49
  }
50
50
  }
51
51
  ```
package/docs/ROADMAP.md CHANGED
@@ -131,7 +131,7 @@ Hermes creates skills after complex tasks (5+ tool calls). Skills are SKILL.md f
131
131
  **Key insight**: Pi already has a skill system. Our skill tool should write SKILL.md files that are compatible with Pi's skill discovery. This means our skills are immediately usable as Pi slash commands — no separate ecosystem needed.
132
132
 
133
133
  - [ ] `skill` tool — register via `pi.registerTool()` with actions: `create`, `patch`, `edit`, `delete`
134
- - [ ] Skill storage in `~/.pi/agent/memory/skills/` (not `~/.pi/agent/skills/` avoid conflicting with user's own skills)
134
+ - [x] Skill storage split by scope: global skills in `~/.pi/agent/skills/<slug>/SKILL.md`, project skills in `~/.pi/agent/projects-memory/<project>/skills/<slug>/SKILL.md` (discovered via `resources_discover`)
135
135
  - [ ] SKILL.md format — compatible with Pi's SKILL.md spec (frontmatter + markdown body)
136
136
  - [ ] Progressive disclosure — skill index (name + description only) injected into system prompt, full content loaded on demand via `skill_view` action
137
137
  - [ ] Auto-trigger after complex tasks — track tool calls per turn, trigger skill extraction at 5+ tool calls
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-hermes-memory",
3
- "version": "0.7.5",
3
+ "version": "0.7.7",
4
4
  "description": "🧠 Persistent memory + 🔍 session search + 🛡️ secret scanning for Pi. Token-aware policy-only memory by default, SQLite FTS5 search, auto-consolidation, procedural skills. 368 tests. Ported from Hermes agent.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -45,11 +45,11 @@
45
45
  "url": "https://github.com/chandra447/pi-hermes-memory"
46
46
  },
47
47
  "peerDependencies": {
48
- "@mariozechner/pi-coding-agent": ">=0.70.0"
48
+ "@earendil-works/pi-coding-agent": ">=0.74.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@mariozechner/pi-ai": "^0.70.0",
52
- "@mariozechner/pi-coding-agent": "^0.70.0",
51
+ "@earendil-works/pi-ai": "^0.74.0",
52
+ "@earendil-works/pi-coding-agent": "^0.74.0",
53
53
  "@types/better-sqlite3": "^7.6.13",
54
54
  "tsx": "^4.21.0",
55
55
  "typebox": "^1.1.33",
package/src/config.ts CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  DEFAULT_NUDGE_TOOL_CALLS,
13
13
  DEFAULT_REVIEW_RECENT_MESSAGES,
14
14
  DEFAULT_FLUSH_RECENT_MESSAGES,
15
+ DEFAULT_CONSOLIDATION_TIMEOUT_MS,
15
16
  DEFAULT_FAILURE_INJECTION_MAX_AGE_DAYS,
16
17
  DEFAULT_FAILURE_INJECTION_MAX_ENTRIES,
17
18
  } from "./constants.js";
@@ -41,6 +42,7 @@ const DEFAULT_CONFIG: MemoryConfig = {
41
42
  failureInjectionEnabled: true,
42
43
  failureInjectionMaxAgeDays: DEFAULT_FAILURE_INJECTION_MAX_AGE_DAYS,
43
44
  failureInjectionMaxEntries: DEFAULT_FAILURE_INJECTION_MAX_ENTRIES,
45
+ consolidationTimeoutMs: DEFAULT_CONSOLIDATION_TIMEOUT_MS,
44
46
  nudgeToolCalls: DEFAULT_NUDGE_TOOL_CALLS,
45
47
  projectsMemoryDir: DEFAULT_PROJECTS_MEMORY_DIR,
46
48
  };
@@ -97,6 +99,7 @@ export function loadConfig(configPath = DEFAULT_CONFIG_PATH): MemoryConfig {
97
99
  if (isStringArray(parsed.correctionWeakPatterns)) config.correctionWeakPatterns = parsed.correctionWeakPatterns;
98
100
  if (isStringArray(parsed.correctionNegativePatterns)) config.correctionNegativePatterns = parsed.correctionNegativePatterns;
99
101
  if (isStringArray(parsed.correctionDirectiveWords)) config.correctionDirectiveWords = parsed.correctionDirectiveWords;
102
+ if (typeof parsed.consolidationTimeoutMs === "number") config.consolidationTimeoutMs = parsed.consolidationTimeoutMs;
100
103
  if (typeof parsed.failureInjectionEnabled === "boolean") config.failureInjectionEnabled = parsed.failureInjectionEnabled;
101
104
  if (typeof parsed.failureInjectionMaxAgeDays === "number") config.failureInjectionMaxAgeDays = parsed.failureInjectionMaxAgeDays;
102
105
  if (typeof parsed.failureInjectionMaxEntries === "number") config.failureInjectionMaxEntries = parsed.failureInjectionMaxEntries;
package/src/constants.ts CHANGED
@@ -23,6 +23,7 @@ export const DEFAULT_NUDGE_TOOL_CALLS = 15;
23
23
  export const DEFAULT_REVIEW_RECENT_MESSAGES = 0;
24
24
  export const DEFAULT_FLUSH_RECENT_MESSAGES = 0;
25
25
  export const DEFAULT_SKILL_TRIGGER_TOOL_CALLS = 8;
26
+ export const DEFAULT_CONSOLIDATION_TIMEOUT_MS = 60000;
26
27
  export const DEFAULT_FAILURE_INJECTION_MAX_AGE_DAYS = 7;
27
28
  export const DEFAULT_FAILURE_INJECTION_MAX_ENTRIES = 5;
28
29
 
@@ -133,7 +134,7 @@ export const COMBINED_REVIEW_PROMPT = `Review the conversation above and conside
133
134
 
134
135
  For failures, include: what was tried, why it failed, what error occurred, and what worked instead.
135
136
 
136
- **Skills**: Was a complex, non-trivial approach used to complete a task — one that required trial and error, multiple tool calls, or changing course? If so, save a reusable procedure using the skill tool with action 'create'. Include: when to use it, step-by-step procedure, pitfalls to avoid, and how to verify success. If a related skill already exists, use action 'patch' to update it instead of creating a duplicate.
137
+ **Skills**: Was a complex, non-trivial approach used to complete a task — one that required trial and error, multiple tool calls, or changing course? If so, save a reusable procedure using the skill tool with action 'create'. Always pass scope explicitly on create (scope='global' or scope='project'). Choose scope='global' for transferable procedures and scope='project' when the workflow depends on this repo's paths, scripts, architecture, deploy steps, or conventions. Include: when to use it, step-by-step procedure, pitfalls to avoid, and how to verify success. If a related skill already exists, use action 'patch' with its skill_id instead of creating a duplicate.
137
138
 
138
139
  Only act if there's something genuinely worth saving. If nothing stands out, just say 'Nothing to save.' and stop.`;
139
140
 
@@ -220,13 +221,17 @@ Priority:
220
221
  Use the memory tool to save. If this contradicts an existing entry, use 'replace' to update it.`;
221
222
 
222
223
  // ─── Skill tool description ───
223
- export const SKILL_TOOL_DESCRIPTION = `Save reusable procedures and patterns as skills that survive across sessions. Skills are procedural memory — they capture HOW to do something, not just what happened.
224
+ export const SKILL_TOOL_DESCRIPTION = `Save reusable procedures and patterns as Pi-native skills that survive across sessions. Skills are procedural memory — they capture HOW to do something, not just what happened.
224
225
 
225
226
  WHEN TO CREATE A SKILL:
226
227
  - After completing a complex task that required trial and error or multiple tool calls
227
228
  - When you discover a non-obvious approach that could be reused
228
229
  - When the user teaches you a specific workflow or procedure
229
230
 
231
+ SCOPE:
232
+ - 'global': transferable procedures that can be reused across repositories
233
+ - 'project': procedures tied to this repo's paths, scripts, architecture, deploy flow, or conventions
234
+
230
235
  WHEN TO UPDATE A SKILL (use 'patch'):
231
236
  - You discover a better approach for an existing skill
232
237
  - A pitfall or edge case not covered by the skill
@@ -237,7 +242,7 @@ SKILL FORMAT:
237
242
  - description: one-line summary of when to use it
238
243
  - body: structured with sections — ## When to Use, ## Procedure, ## Pitfalls, ## Verification
239
244
 
240
- ACTIONS: create (new skill), view (read full content), patch (update a section), edit (replace description + body), delete (remove skill).`;
245
+ ACTIONS: create (new skill), view (read full content or list), patch (update a section by skill_id), edit (replace description + body by skill_id), delete (remove by skill_id).`;
241
246
 
242
247
  // ─── Interview prompt (onboarding) ───
243
248
  export const INTERVIEW_PROMPT = `You are conducting a brief onboarding interview with a new user. Your goal is to pre-fill their USER PROFILE so future sessions start with context instead of a blank slate.
@@ -7,7 +7,7 @@
7
7
  * from disk after consolidation completes.
8
8
  */
9
9
 
10
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
10
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
11
11
  import { MemoryStore } from "../store/memory-store.js";
12
12
  import { CONSOLIDATION_PROMPT, ENTRY_DELIMITER } from "../constants.js";
13
13
  import type { ConsolidationResult } from "../types.js";
@@ -17,6 +17,7 @@ export async function triggerConsolidation(
17
17
  store: MemoryStore,
18
18
  target: "memory" | "user" | "failure",
19
19
  signal?: AbortSignal,
20
+ timeoutMs: number = 60000,
20
21
  ): Promise<ConsolidationResult> {
21
22
  const entries =
22
23
  target === "memory" ? store.getMemoryEntries() : store.getUserEntries();
@@ -34,7 +35,7 @@ export async function triggerConsolidation(
34
35
  try {
35
36
  const result = await pi.exec("pi", ["-p", "--no-session", prompt], {
36
37
  signal,
37
- timeout: 60000,
38
+ timeout: timeoutMs,
38
39
  });
39
40
 
40
41
  if (result.code === 0) {
@@ -58,6 +59,7 @@ export async function triggerConsolidation(
58
59
  export function registerConsolidateCommand(
59
60
  pi: ExtensionAPI,
60
61
  store: MemoryStore,
62
+ timeoutMs: number = 60000,
61
63
  ): void {
62
64
  pi.registerCommand("memory-consolidate", {
63
65
  description: "Manually trigger memory consolidation to free up space",
@@ -75,7 +77,7 @@ export function registerConsolidateCommand(
75
77
  continue;
76
78
  }
77
79
 
78
- const result = await triggerConsolidation(pi, store, target, ctx.signal);
80
+ const result = await triggerConsolidation(pi, store, target, ctx.signal, timeoutMs);
79
81
 
80
82
  if (result.consolidated) {
81
83
  await store.loadFromDisk();
@@ -7,7 +7,7 @@
7
7
  * keeping us within Pi's intended extension API.
8
8
  */
9
9
 
10
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
10
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
11
11
  import { MemoryStore } from "../store/memory-store.js";
12
12
  import { COMBINED_REVIEW_PROMPT } from "../constants.js";
13
13
  import type { MemoryConfig } from "../types.js";
@@ -8,7 +8,7 @@
8
8
  * - Negative patterns: suppress even if a positive pattern matched
9
9
  */
10
10
 
11
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
11
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
12
12
  import { MemoryStore } from "../store/memory-store.js";
13
13
  import { DatabaseManager } from "../store/db.js";
14
14
  import {
@@ -5,7 +5,7 @@
5
5
  import path from 'node:path';
6
6
  import fs from 'node:fs';
7
7
  import os from 'node:os';
8
- import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
8
+ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
9
9
  import { DatabaseManager } from '../store/db.js';
10
10
  import { indexAllSessions, getSessionStats } from '../store/session-indexer.js';
11
11
 
@@ -2,7 +2,7 @@
2
2
  * Insights command — /memory-insights shows what's stored in persistent memory.
3
3
  */
4
4
 
5
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
5
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
6
  import { MemoryStore } from "../store/memory-store.js";
7
7
 
8
8
  export function registerInsightsCommand(pi: ExtensionAPI, store: MemoryStore, projectStore: MemoryStore | null, projectName: string): void {
@@ -6,7 +6,7 @@
6
6
  * zero value until multiple sessions accumulate facts organically.
7
7
  */
8
8
 
9
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
9
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
10
10
  import { MemoryStore } from "../store/memory-store.js";
11
11
  import { INTERVIEW_PROMPT } from "../constants.js";
12
12
 
@@ -2,7 +2,7 @@
2
2
  * Learn memory tool command — /learn-memory-tool teaches users about the memory system.
3
3
  */
4
4
 
5
- import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
5
+ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
6
6
 
7
7
  export function registerLearnMemoryCommand(pi: ExtensionAPI): void {
8
8
  pi.registerCommand("learn-memory-tool", {
@@ -34,7 +34,7 @@ export function registerLearnMemoryCommand(pi: ExtensionAPI): void {
34
34
  lines.push(" 🧠 Memory │ MEMORY.md │ 5,000 chars");
35
35
  lines.push(" 👤 User Profile │ USER.md │ 5,000 chars");
36
36
  lines.push(" ⚠️ Failures │ failures.md │ 10,000 chars");
37
- lines.push(" 📚 Skills │ skills/*.md │ Unlimited");
37
+ lines.push(" 📚 Skills │ Pi-native skill dirs │ Unlimited");
38
38
  lines.push(" 💾 Extended │ sessions.db │ Unlimited");
39
39
  lines.push("");
40
40
  lines.push(" Memory: Facts — env details, project conventions, tool quirks");
@@ -126,7 +126,7 @@ export function registerLearnMemoryCommand(pi: ExtensionAPI): void {
126
126
  lines.push(" 8. Session ends → Final flush");
127
127
  lines.push("");
128
128
  lines.push(" Legacy mode: set memoryMode=\"legacy-inject\" to restore full");
129
- lines.push(" MEMORY.md, USER.md, project memory, failure, and skill prompt blocks.");
129
+ lines.push(" MEMORY.md, USER.md, project memory, and failure prompt blocks.");
130
130
  }
131
131
 
132
132
  if (section.startsWith("🏗️")) {
@@ -153,7 +153,7 @@ export function registerLearnMemoryCommand(pi: ExtensionAPI): void {
153
153
  lines.push(" │ memory_search(\"auth\", cat:\"failure\")│");
154
154
  lines.push(" └─────────────────────────────────────┘");
155
155
  lines.push("");
156
- lines.push(" Legacy mode can still inject full memory/skill blocks for users");
156
+ lines.push(" Legacy mode can still inject full memory blocks for users");
157
157
  lines.push(" who explicitly opt into memoryMode=\"legacy-inject\".");
158
158
  }
159
159
 
@@ -1,11 +1,10 @@
1
1
  /**
2
2
  * Preview context command — /memory-preview-context shows the policy-only prompt
3
- * or legacy memory/skill blocks appended to the system prompt.
3
+ * or legacy memory blocks appended to the system prompt.
4
4
  */
5
5
 
6
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
6
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
7
7
  import { MemoryStore } from "../store/memory-store.js";
8
- import { SkillStore } from "../store/skill-store.js";
9
8
  import { resolveMemoryPolicyPrompt } from "../prompt-context.js";
10
9
  import type { MemoryConfig } from "../types.js";
11
10
 
@@ -13,12 +12,11 @@ export function registerPreviewContextCommand(
13
12
  pi: ExtensionAPI,
14
13
  store: MemoryStore,
15
14
  projectStore: MemoryStore | null,
16
- skillStore: SkillStore,
17
15
  projectName: string,
18
16
  config: Pick<MemoryConfig, "memoryMode" | "memoryPolicyStyle" | "memoryPolicyCustomText"> = { memoryMode: "policy-only" },
19
17
  ): void {
20
18
  pi.registerCommand("memory-preview-context", {
21
- description: "Preview the memory policy or legacy memory/skill context blocks",
19
+ description: "Preview the memory policy or legacy memory context blocks",
22
20
  handler: async (_args, ctx) => {
23
21
  if (config.memoryMode === "policy-only") {
24
22
  const policyPrompt = resolveMemoryPolicyPrompt(config);
@@ -48,7 +46,6 @@ export function registerPreviewContextCommand(
48
46
 
49
47
  const memoryBlock = store.formatForSystemPrompt();
50
48
  const projectBlock = projectStore ? projectStore.formatProjectBlock(projectName) : "";
51
- const skillIndex = await skillStore.formatIndexForSystemPrompt();
52
49
 
53
50
  const lines: string[] = [];
54
51
  lines.push("");
@@ -56,7 +53,7 @@ export function registerPreviewContextCommand(
56
53
  lines.push(" ║ 👀 Injected Context Preview ║");
57
54
  lines.push(" ╚══════════════════════════════════════════════╝");
58
55
  lines.push("");
59
- lines.push(" This is the memory/skill context appended to the system prompt.");
56
+ lines.push(" This is the memory context appended to the system prompt.");
60
57
  lines.push(" (Core hidden system instructions are NOT shown.)");
61
58
  lines.push("");
62
59
 
@@ -76,16 +73,9 @@ export function registerPreviewContextCommand(
76
73
  lines.push("");
77
74
  }
78
75
 
79
- if (skillIndex) {
80
- blockCount++;
81
- lines.push(" ── SKILL INDEX ─────────────────────────────────────────────");
82
- lines.push(skillIndex);
83
- lines.push("");
84
- }
85
-
86
76
  if (blockCount === 0) {
87
77
  lines.push(" No memory context blocks are currently injected.");
88
- lines.push(" Add memory entries or skills, then run this command again.");
78
+ lines.push(" Add memory entries, then run this command again.");
89
79
  lines.push("");
90
80
  }
91
81
 
@@ -4,7 +4,7 @@
4
4
  * See PLAN.md → "Hermes Source File Reference Map" for source lines.
5
5
  */
6
6
 
7
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
7
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
8
8
  import { MemoryStore } from "../store/memory-store.js";
9
9
  import { FLUSH_PROMPT } from "../constants.js";
10
10
  import type { MemoryConfig } from "../types.js";
@@ -5,13 +5,22 @@
5
5
  * This implements Hermes' "self-evaluation checkpoint" pattern.
6
6
  */
7
7
 
8
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
8
+ import * as path from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
9
11
  import { MemoryStore } from "../store/memory-store.js";
10
12
  import { SkillStore } from "../store/skill-store.js";
11
13
  import { COMBINED_REVIEW_PROMPT, DEFAULT_SKILL_TRIGGER_TOOL_CALLS, ENTRY_DELIMITER } from "../constants.js";
12
14
  import type { MemoryConfig } from "../types.js";
13
15
  import { getMessageText } from "../types.js";
14
16
 
17
+ const proceduralSkillCreatorPath = path.join(
18
+ path.dirname(fileURLToPath(import.meta.url)),
19
+ "..",
20
+ "skills",
21
+ "procedural-skill-creator",
22
+ );
23
+
15
24
  export function setupSkillAutoTrigger(
16
25
  pi: ExtensionAPI,
17
26
  store: MemoryStore,
@@ -71,10 +80,21 @@ export function setupSkillAutoTrigger(
71
80
 
72
81
  const currentMemory = store.getMemoryEntries().join(ENTRY_DELIMITER);
73
82
  const skillIndex = await skillStore.loadIndex();
74
- const skillSummary = skillIndex.map((s) => `${s.fileName}: ${s.name} - ${s.description}`).join("\n");
83
+ const skillSummary = skillIndex
84
+ .map((skill) => `${skill.skillId} [${skill.scope}]: ${skill.displayName || skill.name} - ${skill.description}`)
85
+ .join("\n");
86
+
87
+ const activeProjectName = skillStore.getProjectName();
88
+ const scopeLine = activeProjectName
89
+ ? `Active project context: '${activeProjectName}'. If workflow details depend on this project, use scope='project'; otherwise use scope='global'.`
90
+ : "No active project context detected. Use scope='global' unless the workflow is clearly project-specific.";
75
91
 
76
92
  const prompt = [
77
93
  "This was a complex task that required multiple tool calls. Extract any reusable procedures as skills.",
94
+ "A bundled skill named procedural-skill-creator is loaded for you. Read and follow it before deciding whether to create or patch a skill.",
95
+ "Always pass scope explicitly when creating a skill: scope='global' or scope='project'.",
96
+ "Choose scope='global' for transferable procedures and scope='project' when the workflow depends on this repo's paths, scripts, architecture, deploy steps, or conventions.",
97
+ scopeLine,
78
98
  "",
79
99
  "--- Existing Skills ---",
80
100
  skillSummary || "(none)",
@@ -86,11 +106,11 @@ export function setupSkillAutoTrigger(
86
106
  recentParts.join("\n\n"),
87
107
  "",
88
108
  "If a skill should be created, use the skill tool with action 'create'.",
89
- "If a related skill already exists, use 'patch' to update it.",
109
+ "If a related skill already exists, use 'patch' with its skill_id to update it.",
90
110
  "If nothing reusable happened, say 'Nothing to extract.' and stop.",
91
111
  ].join("\n");
92
112
 
93
- const result = await pi.exec("pi", ["-p", "--no-session", prompt], {
113
+ const result = await pi.exec("pi", ["-p", "--no-session", "--skill", proceduralSkillCreatorPath, prompt], {
94
114
  signal: ctx.signal,
95
115
  timeout: 60000,
96
116
  });
@@ -2,7 +2,7 @@
2
2
  * Skills command — /memory-skills lists all agent-created skills.
3
3
  */
4
4
 
5
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
5
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
6
  import { SkillStore } from "../store/skill-store.js";
7
7
 
8
8
  export function registerSkillsCommand(pi: ExtensionAPI, store: SkillStore): void {
@@ -10,6 +10,9 @@ export function registerSkillsCommand(pi: ExtensionAPI, store: SkillStore): void
10
10
  description: "List all agent-created skills (procedural memory)",
11
11
  handler: async (_args, ctx) => {
12
12
  const skills = await store.loadIndex();
13
+ const globalSkills = skills.filter((skill) => skill.scope === "global");
14
+ const projectSkills = skills.filter((skill) => skill.scope === "project");
15
+ const projectName = store.getProjectName();
13
16
 
14
17
  const lines: string[] = [];
15
18
  lines.push("");
@@ -24,11 +27,28 @@ export function registerSkillsCommand(pi: ExtensionAPI, store: SkillStore): void
24
27
  lines.push(" Skills are auto-created after complex tasks,");
25
28
  lines.push(" or you can ask the agent to create one.");
26
29
  } else {
27
- for (const skill of skills) {
28
- lines.push(` 📄 ${skill.name}`);
29
- lines.push(` ${skill.description}`);
30
- lines.push(` file: ${skill.fileName}`);
31
- lines.push("");
30
+ if (globalSkills.length > 0) {
31
+ lines.push(" Global Skills");
32
+ lines.push(" ─────────────");
33
+ for (const skill of globalSkills) {
34
+ lines.push(` 📄 ${skill.displayName || skill.name}`);
35
+ lines.push(` ${skill.description}`);
36
+ lines.push(` id: ${skill.skillId}`);
37
+ lines.push(` path: ${skill.path}`);
38
+ lines.push("");
39
+ }
40
+ }
41
+
42
+ if (projectSkills.length > 0) {
43
+ lines.push(` Project Skills${projectName ? ` (${projectName})` : ""}`);
44
+ lines.push(" ──────────────");
45
+ for (const skill of projectSkills) {
46
+ lines.push(` 📄 ${skill.displayName || skill.name}`);
47
+ lines.push(` ${skill.description}`);
48
+ lines.push(` id: ${skill.skillId}`);
49
+ lines.push(` path: ${skill.path}`);
50
+ lines.push("");
51
+ }
32
52
  }
33
53
  }
34
54
 
@@ -7,7 +7,7 @@
7
7
  * for a project they're not currently in.
8
8
  */
9
9
 
10
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
10
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
11
11
  import type { MemoryConfig } from "../types.js";
12
12
  import * as fs from "node:fs/promises";
13
13
  import * as path from "node:path";
@@ -5,7 +5,7 @@
5
5
 
6
6
  import fs from 'node:fs';
7
7
  import path from 'node:path';
8
- import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
8
+ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
9
9
  import { DatabaseManager } from '../store/db.js';
10
10
  import {
11
11
  parseMarkdownMemoryEntry,