pmem-ai 0.7.1 → 0.7.2

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 (51) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +40 -2
  3. package/dist/commands/integration.js +1 -1
  4. package/dist/commands/mcp.d.ts +9 -0
  5. package/dist/commands/mcp.d.ts.map +1 -0
  6. package/dist/commands/mcp.js +64 -0
  7. package/dist/commands/mcp.js.map +1 -0
  8. package/dist/commands/milestone.d.ts +5 -0
  9. package/dist/commands/milestone.d.ts.map +1 -0
  10. package/dist/commands/milestone.js +197 -0
  11. package/dist/commands/milestone.js.map +1 -0
  12. package/dist/commands/update.d.ts +2 -0
  13. package/dist/commands/update.d.ts.map +1 -1
  14. package/dist/commands/update.js +87 -3
  15. package/dist/commands/update.js.map +1 -1
  16. package/dist/commands/verify.d.ts.map +1 -1
  17. package/dist/commands/verify.js +19 -8
  18. package/dist/commands/verify.js.map +1 -1
  19. package/dist/core/consistency.d.ts.map +1 -1
  20. package/dist/core/consistency.js +6 -0
  21. package/dist/core/consistency.js.map +1 -1
  22. package/dist/core/query/ask.d.ts +20 -0
  23. package/dist/core/query/ask.d.ts.map +1 -0
  24. package/dist/core/query/ask.js +256 -0
  25. package/dist/core/query/ask.js.map +1 -0
  26. package/dist/core/query/recall.d.ts +21 -0
  27. package/dist/core/query/recall.d.ts.map +1 -0
  28. package/dist/core/query/recall.js +148 -0
  29. package/dist/core/query/recall.js.map +1 -0
  30. package/dist/core/query/related.d.ts +29 -0
  31. package/dist/core/query/related.d.ts.map +1 -0
  32. package/dist/core/query/related.js +106 -0
  33. package/dist/core/query/related.js.map +1 -0
  34. package/dist/core/query/status.d.ts +24 -0
  35. package/dist/core/query/status.d.ts.map +1 -0
  36. package/dist/core/query/status.js +236 -0
  37. package/dist/core/query/status.js.map +1 -0
  38. package/dist/index.js +19 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/mcp/security.d.ts +21 -0
  41. package/dist/mcp/security.d.ts.map +1 -0
  42. package/dist/mcp/security.js +150 -0
  43. package/dist/mcp/security.js.map +1 -0
  44. package/dist/mcp/server.d.ts +2 -0
  45. package/dist/mcp/server.d.ts.map +1 -0
  46. package/dist/mcp/server.js +177 -0
  47. package/dist/mcp/server.js.map +1 -0
  48. package/docs/dogfooding.md +98 -0
  49. package/docs/pmem-rt.md +137 -0
  50. package/package.json +3 -2
  51. package/skills/pmem/SKILL.md +43 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  All notable changes to pmem are documented here.
4
4
 
5
+ ## v0.7.2 — pmem-rt v1 MCP Adapter + Dogfooding Usability Fixes
6
+
7
+ ### Added
8
+
9
+ - **pmem-rt v1 MCP Server**: New `pmem mcp` command starts a read-only stdio MCP server (`src/mcp/server.ts`) with 4 tools: `pmem_recall`, `pmem_ask`, `pmem_related`, `pmem_status`. All card content carries `content_trust: "untrusted_project_data"`. Uses `@modelcontextprotocol/sdk` (pure ESM, loaded via dynamic import).
10
+ - **Query layer** (`src/core/query/`): Extracted pure query functions (`recallQuery`, `askQuery`, `relatedQuery`, `statusQuery`) shared by CLI and MCP server. No console.log, no process.exit(), no Commander.js dependency.
11
+ - **MCP security** (`src/mcp/security.ts`): `validatePathScope` (realpath + path.sep comparison to prevent symlink escape and prefix confusion), `enforceBudget` (token budget truncation), `addContentTrust` (marks all card objects).
12
+ - **`pmem milestone <version>`**: Records release milestones as memory cards with auto git-tag detection and manifest type registration.
13
+ - **`pmem update --confirm --refresh-verified <ids>`**: Bumps `last_verified` on specified cards during confirm, before rebuild.
14
+ - **`pmem mark-dirty --card <id...>`**: Explicit per-card dirty marking, bypassing git diff.
15
+
16
+ ### Fixed
17
+
18
+ - **Exclude `.pmem/**` from stale_memory check**: Prevent false-positive warnings from `pmem update --confirm` rewriting manifest.yml / next.md / state.md / index.md.
19
+ - **Separate `--fix` and `--fix-stale` semantics**: `--fix` handles structural index issues only; `--fix-stale` additionally refreshes stale_memory `last_verified` timestamps.
20
+ - **`docs/dogfooding.md`**: Documents self-referential stale patterns, cleanup cadence, and new command workflows.
21
+
22
+ ### Version
23
+
24
+ Bumped from 0.7.1 → 0.7.2.
25
+
5
26
  ## 0.7.0 - Universal Agent Memory (presets, custom card types, and domain neutrality)
6
27
 
7
28
  ### Added
package/README.md CHANGED
@@ -346,14 +346,18 @@ pmem related <id> [--depth N] [--type <edge-type>] [--format compact|json] [--so
346
346
  pmem trace <id>
347
347
 
348
348
  pmem status [--since <timestamp>] [--format compact|json]
349
- pmem mark-dirty [-r <reason>] [--auto]
349
+ pmem mark-dirty [-r <reason>] [--auto] [--card <id...>]
350
350
  pmem update [--auto] [--suggest] [--apply-suggestion <id>] [--confirm] [--force]
351
351
  [-s <summary>] [-n <next>] [--format compact|json] [--include-history]
352
352
  [--accept-edges <ids>] [--reject-edges <ids>]
353
+ [--refresh-verified <ids>]
354
+ pmem sync -s "<summary>" [-n "<next>"]
355
+
356
+ pmem milestone <version> [-m <message>] [--tag <name>]
353
357
 
354
358
  pmem distill [--suggest] [--confirm] [--apply-suggestion <id>] [--suggest-splits]
355
359
  pmem rebuild [--changed] [--full] [--card <id>]
356
- pmem verify [--fix] [--fix-locks] [--relaxed]
360
+ pmem verify [--fix] [--fix-stale] [--fix-locks] [--relaxed]
357
361
  pmem doctor [--format compact|json]
358
362
  pmem new <type> <title>
359
363
  pmem rename --find <pattern> --replace <replacement> [--write]
@@ -362,6 +366,7 @@ pmem session start [-a <agent-name>]
362
366
  pmem session end [-s <summary>]
363
367
  pmem integration list|install <framework>|verify
364
368
  pmem install [--skills] [--claude] [--codex] [--gemini] [--all]
369
+ pmem mcp
365
370
  ```
366
371
 
367
372
  ## Exit Codes
@@ -379,6 +384,39 @@ Agents should parse structured JSON output (`--format json`) to decide next step
379
384
 
380
385
  > **Breaking change from v0.6.1:** `pmem update --suggest` and `pmem distill --suggest` previously exited with code `1` when suggestions existed. Scripts that checked `$? -eq 1` must now parse JSON output instead.
381
386
 
387
+ ## pmem-rt — MCP Runtime for AI Agents (v0.7.2)
388
+
389
+ pmem-rt is a read-only stdio MCP adapter that lets AI coding agents use pmem as a low-latency memory backend directly in their tool loop.
390
+
391
+ ### Quick Start
392
+
393
+ ```bash
394
+ # In your project
395
+ npm install pmem-ai
396
+ npx pmem init --guided
397
+ npx pmem rebuild
398
+
399
+ # Configure your agent (e.g. Claude Code settings.json):
400
+ # {
401
+ # "mcpServers": {
402
+ # "pmem": { "command": "npx", "args": ["pmem", "mcp"], "cwd": "/absolute/path/to/your-project" }
403
+ # }
404
+ # }
405
+ ```
406
+
407
+ ### MCP Tools
408
+
409
+ | Tool | Description |
410
+ |------|-------------|
411
+ | `pmem_recall` | Restore project context: name, stage, focus, next, active cards, recent updates |
412
+ | `pmem_ask` | 6-step search: ID → alias → tag → graph expansion → FTS5 → LIKE |
413
+ | `pmem_related` | Graph neighbors of a card, grouped by edge type with direction/confidence |
414
+ | `pmem_status` | Changed files → affected memory cards |
415
+
416
+ All tools are read-only. Writes continue through the CLI (`pmem update --confirm`, `pmem sync`). Every card carries `content_trust: "untrusted_project_data"`.
417
+
418
+ [Full integration guide →](docs/pmem-rt.md)
419
+
382
420
  ## Project Layout
383
421
 
384
422
  ```txt
@@ -375,7 +375,7 @@ function installIntegration(pmemPath, manifest, framework) {
375
375
  console.log(`✓ Integration "${framework}" installed.`);
376
376
  console.log(` Template: .pmem/integrations/${framework}/`);
377
377
  }
378
- const CURRENT_TEMPLATE_VERSION = '0.7.1';
378
+ const CURRENT_TEMPLATE_VERSION = '0.7.2';
379
379
  function verifyIntegrations(pmemPath, manifest) {
380
380
  if (!manifest)
381
381
  return;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Start a read-only stdio MCP server for agent tool integration.
3
+ *
4
+ * IMPORTANT: This command MUST NOT output any console.log — stdout is the
5
+ * MCP protocol transport channel. Any non-JSON output will cause protocol
6
+ * errors. Use stderr for diagnostics if absolutely necessary.
7
+ */
8
+ export declare function mcpCommand(): Promise<void>;
9
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAmBhD"}
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.mcpCommand = mcpCommand;
37
+ const path = __importStar(require("path"));
38
+ const fs_1 = require("../core/fs");
39
+ const security_1 = require("../mcp/security");
40
+ const server_1 = require("../mcp/server");
41
+ /**
42
+ * Start a read-only stdio MCP server for agent tool integration.
43
+ *
44
+ * IMPORTANT: This command MUST NOT output any console.log — stdout is the
45
+ * MCP protocol transport channel. Any non-JSON output will cause protocol
46
+ * errors. Use stderr for diagnostics if absolutely necessary.
47
+ */
48
+ async function mcpCommand() {
49
+ const cwd = process.cwd();
50
+ const pmemPath = path.join(cwd, '.pmem');
51
+ if (!(0, fs_1.fileExists)(pmemPath)) {
52
+ process.stderr.write('Error: No .pmem directory found. Run `pmem init` first.\n');
53
+ process.exit(2);
54
+ }
55
+ const dbPath = path.join(pmemPath, 'pmem.db');
56
+ if (!(0, fs_1.fileExists)(dbPath)) {
57
+ process.stderr.write('Error: No .pmem/pmem.db found. Run `pmem rebuild` first.\n');
58
+ process.exit(2);
59
+ }
60
+ // Security: validate path scope before starting
61
+ (0, security_1.validatePathScope)(pmemPath);
62
+ await (0, server_1.startMcpServer)(pmemPath);
63
+ }
64
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,gCAmBC;AA/BD,2CAA6B;AAC7B,mCAAwC;AACxC,8CAAoD;AACpD,0CAA+C;AAE/C;;;;;;GAMG;AACI,KAAK,UAAU,UAAU;IAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAEzC,IAAI,CAAC,IAAA,eAAU,EAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAClF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,IAAA,eAAU,EAAC,MAAM,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;QACnF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,gDAAgD;IAChD,IAAA,4BAAiB,EAAC,QAAQ,CAAC,CAAC;IAE5B,MAAM,IAAA,uBAAc,EAAC,QAAQ,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function milestoneCommand(version: string, options?: {
2
+ message?: string;
3
+ tag?: string;
4
+ }): void;
5
+ //# sourceMappingURL=milestone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"milestone.d.ts","sourceRoot":"","sources":["../../src/commands/milestone.ts"],"names":[],"mappings":"AAQA,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,IAAI,CAyIxG"}
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.milestoneCommand = milestoneCommand;
37
+ const path = __importStar(require("path"));
38
+ const child_process_1 = require("child_process");
39
+ const fs_1 = require("../core/fs");
40
+ const manifest_1 = require("../core/manifest");
41
+ const db_1 = require("../core/db");
42
+ const PMEM_DIR = '.pmem';
43
+ function milestoneCommand(version, options = {}) {
44
+ const cwd = process.cwd();
45
+ const pmemPath = path.join(cwd, PMEM_DIR);
46
+ if (!(0, fs_1.fileExists)(pmemPath)) {
47
+ console.log('No .pmem directory found. Run `pmem init` first.');
48
+ process.exit(2);
49
+ }
50
+ const manifest = (0, manifest_1.loadManifest)(pmemPath);
51
+ if (!manifest) {
52
+ console.log('No manifest found. Run `pmem init` first.');
53
+ process.exit(2);
54
+ }
55
+ const config = (0, manifest_1.resolveConfig)(manifest);
56
+ // Determine the directory for milestone/trace cards
57
+ const traceDirName = config.type_dirs['trace'] || config.type_dirs['milestone'] || 'traces';
58
+ const traceDir = path.join(pmemPath, traceDirName);
59
+ (0, fs_1.ensureDir)(traceDir);
60
+ const versionClean = version.replace(/^v/, '');
61
+ const milestoneId = `milestone.v${versionClean}`;
62
+ const message = options.message || `Released version ${version}`;
63
+ const tag = options.tag || `v${versionClean}`;
64
+ // Check if git tag exists
65
+ let gitInfo = '';
66
+ try {
67
+ const gitDir = (0, child_process_1.execSync)('git rev-parse --git-dir', { cwd, stdio: 'ignore', encoding: 'utf8' }).trim();
68
+ if (gitDir) {
69
+ try {
70
+ const tagSha = (0, child_process_1.execSync)(`git rev-parse refs/tags/${tag}`, { cwd, stdio: 'pipe', encoding: 'utf8' }).trim();
71
+ gitInfo = `\n## Git Tag\n- Tag: \`${tag}\`\n- Commit: \`${tagSha.slice(0, 8)}\``;
72
+ }
73
+ catch {
74
+ try {
75
+ const headSha = (0, child_process_1.execSync)('git rev-parse HEAD', { cwd, encoding: 'utf8' }).trim();
76
+ gitInfo = `\n## Git\n- HEAD: \`${headSha.slice(0, 8)}\`\n- Tag \`${tag}\` not yet created (use \`git tag ${tag}\` to create it)`;
77
+ }
78
+ catch {
79
+ // no git info available
80
+ }
81
+ }
82
+ }
83
+ }
84
+ catch {
85
+ // not a git repo
86
+ }
87
+ const milestoneFile = path.join(traceDir, `milestone.${milestoneId}.md`);
88
+ if ((0, fs_1.fileExists)(milestoneFile)) {
89
+ console.log(`Milestone card already exists: ${path.relative(cwd, milestoneFile)}`);
90
+ console.log(` Edit it directly or use a different version.`);
91
+ process.exit(2);
92
+ }
93
+ (0, fs_1.atomicWrite)(milestoneFile, `---
94
+ id: ${milestoneId}
95
+ type: milestone
96
+ status: shipped
97
+ created: ${new Date().toISOString().split('T')[0]}
98
+ tags: [release, ${tag}]
99
+ ---
100
+
101
+ # Milestone: ${version}
102
+
103
+ ## What
104
+ ${message}
105
+
106
+ ## When
107
+ Released ${new Date().toISOString().split('T')[0]}.
108
+ ${gitInfo}
109
+ ## Next
110
+ Continue development toward the next milestone.
111
+ `);
112
+ console.log(`Milestone recorded: ${path.relative(cwd, milestoneFile)}`);
113
+ console.log(` Version: ${version}`);
114
+ console.log(` ID: ${milestoneId}`);
115
+ // Register 'milestone' in manifest if not already present (review item #3)
116
+ registerMilestoneType(manifest, config, pmemPath);
117
+ // Index the card and link to related feature cards
118
+ let didRebuild = false;
119
+ const dbPath = path.join(pmemPath, 'pmem.db');
120
+ if ((0, fs_1.fileExists)(dbPath)) {
121
+ try {
122
+ const db = (0, db_1.openDatabase)(pmemPath);
123
+ (0, db_1.createSchema)(db);
124
+ // Rebuild to index the new card
125
+ const { rebuildCommand } = require('./rebuild');
126
+ rebuildCommand({ card: milestoneId });
127
+ didRebuild = true;
128
+ // Link to feature cards whose ID contains the version (tightened: match
129
+ // `feature.v<version>` prefix or `v<version_underscored>` segment).
130
+ const versionUnderscored = versionClean.replace(/\./g, '_');
131
+ const featureCards = db.prepare(`SELECT id FROM cards
132
+ WHERE type = 'feature'
133
+ AND (id LIKE ? OR id LIKE ?)
134
+ AND is_deleted = 0`).all(`feature.v${versionClean}%`, `%v${versionUnderscored}%`);
135
+ const now = new Date().toISOString();
136
+ for (const fc of featureCards) {
137
+ (0, db_1.insertEdge)(db, {
138
+ from_id: milestoneId,
139
+ to_id: fc.id,
140
+ type: 'implements',
141
+ source: 'inferred',
142
+ confidence: 0.9,
143
+ created_at: now,
144
+ updated_at: now,
145
+ });
146
+ }
147
+ if (featureCards.length > 0) {
148
+ console.log(` Linked to ${featureCards.length} feature card(s): ${featureCards.map(f => f.id).join(', ')}`);
149
+ }
150
+ (0, db_1.closeDatabase)();
151
+ }
152
+ catch {
153
+ // DB operations are best-effort
154
+ }
155
+ }
156
+ // Only suggest a manual rebuild when the DB path didn't already do one (review item #4)
157
+ if (!didRebuild) {
158
+ console.log(`\nRun \`pmem rebuild\` to index the new milestone card.`);
159
+ }
160
+ process.exit(0);
161
+ }
162
+ /**
163
+ * Ensure 'milestone' is registered in the manifest's card type whitelist
164
+ * so that verify / rebuild don't produce card_id_violation warnings.
165
+ */
166
+ function registerMilestoneType(manifest, config, pmemPath) {
167
+ let changed = false;
168
+ // v0.7.0+ projects: add to schema.card_types
169
+ if (manifest.schema && Array.isArray(manifest.schema.card_types)) {
170
+ if (!manifest.schema.card_types.includes('milestone')) {
171
+ manifest.schema.card_types.push('milestone');
172
+ changed = true;
173
+ }
174
+ // Also add type_dirs entry if missing
175
+ if (!manifest.schema.type_dirs) {
176
+ manifest.schema.type_dirs = {};
177
+ }
178
+ if (!manifest.schema.type_dirs['milestone']) {
179
+ manifest.schema.type_dirs['milestone'] = 'traces';
180
+ changed = true;
181
+ }
182
+ }
183
+ // v0.6.x projects: add milestone to id_pattern alternation
184
+ if (manifest.card_policy?.id_pattern) {
185
+ const pattern = manifest.card_policy.id_pattern;
186
+ if (!pattern.includes('milestone')) {
187
+ // Insert milestone before the closing parenthesis of the type alternation
188
+ manifest.card_policy.id_pattern = pattern.replace(/(\([^)]+)\)/, '$1|milestone)');
189
+ changed = true;
190
+ }
191
+ }
192
+ if (changed) {
193
+ (0, manifest_1.saveManifest)(pmemPath, manifest);
194
+ console.log(' Registered "milestone" type in manifest.');
195
+ }
196
+ }
197
+ //# sourceMappingURL=milestone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"milestone.js","sourceRoot":"","sources":["../../src/commands/milestone.ts"],"names":[],"mappings":"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"}
@@ -10,8 +10,10 @@ export declare function updateCommand(options: {
10
10
  includeHistory?: boolean;
11
11
  acceptEdges?: string;
12
12
  rejectEdges?: string;
13
+ refreshVerified?: string;
13
14
  }): void;
14
15
  export declare function markDirtyCommand(reason: string, options?: {
15
16
  auto?: boolean;
17
+ cardIds?: string[];
16
18
  }): void;
17
19
  //# sourceMappingURL=update.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAaA,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACrC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,IAAI,CA+CP;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,IAAI,CAiGvF"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAaA,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACrC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,IAAI,CA+CP;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAO,GAAG,IAAI,CA0I3G"}
@@ -78,7 +78,7 @@ function updateCommand(options) {
78
78
  }
79
79
  // --confirm or --force: write changes
80
80
  if (options.confirm || options.force) {
81
- confirmUpdate(pmemPath, options.summary, options.next);
81
+ confirmUpdate(pmemPath, options.summary, options.next, options.refreshVerified);
82
82
  return;
83
83
  }
84
84
  // no flag: show current dirty state
@@ -91,6 +91,44 @@ function markDirtyCommand(reason, options = {}) {
91
91
  console.log('No .pmem directory found. Run `pmem init` first.');
92
92
  return;
93
93
  }
94
+ // --card <id>: explicitly mark specific cards as dirty
95
+ if (options.cardIds && options.cardIds.length > 0) {
96
+ const dbPath = path.join(pmemPath, 'pmem.db');
97
+ if (!(0, fs_1.fileExists)(dbPath)) {
98
+ console.log('No SQLite database found. Run `pmem rebuild` first.');
99
+ process.exit(2);
100
+ }
101
+ try {
102
+ const db = (0, db_1.openDatabase)(pmemPath);
103
+ const activeSession = (0, db_1.getActiveSession)(db);
104
+ for (const cardId of options.cardIds) {
105
+ const card = db.prepare('SELECT id FROM cards WHERE id = ? AND is_deleted = 0').get(cardId);
106
+ if (card) {
107
+ (0, db_1.insertDirtyFlag)(db, 'card', cardId, reason, activeSession?.id);
108
+ console.log(`Marked card dirty: ${cardId}`);
109
+ }
110
+ else {
111
+ console.log(`Card not found or deleted: ${cardId}`);
112
+ }
113
+ }
114
+ // Also mark project-level dirty
115
+ const manifest = (0, manifest_1.loadManifest)(pmemPath);
116
+ if (manifest) {
117
+ const timestamp = new Date().toISOString();
118
+ manifest.memory_status.dirty = true;
119
+ manifest.memory_status.dirty_reason = reason;
120
+ manifest.memory_status.dirty_since = timestamp;
121
+ (0, manifest_1.saveManifest)(pmemPath, manifest);
122
+ (0, db_1.insertDirtyFlag)(db, 'project', '.pmem', reason, activeSession?.id);
123
+ }
124
+ (0, db_1.closeDatabase)();
125
+ return;
126
+ }
127
+ catch (err) {
128
+ console.error('Could not mark cards as dirty:', err);
129
+ process.exit(2);
130
+ }
131
+ }
94
132
  // --auto: detect changed files via git and mark related cards as dirty
95
133
  if (options.auto) {
96
134
  const dbPath = path.join(pmemPath, 'pmem.db');
@@ -251,7 +289,7 @@ function autoUpdate(pmemPath, manifest) {
251
289
  }
252
290
  }
253
291
  }
254
- function confirmUpdate(pmemPath, summary, next) {
292
+ function confirmUpdate(pmemPath, summary, next, refreshVerified) {
255
293
  const lockPath = path.join(pmemPath, '.lock');
256
294
  if (!(0, fs_1.acquireLock)(lockPath)) {
257
295
  console.log('Failed to acquire pmem lock after 3s.');
@@ -335,7 +373,53 @@ ${next || 'Continue as planned.'}
335
373
  manifest.memory_status.dirty_since = null;
336
374
  (0, manifest_1.saveManifest)(pmemPath, manifest);
337
375
  }
338
- // Rebuild indexes
376
+ // --refresh-verified: bump last_verified on specified cards.
377
+ // MUST run BEFORE rebuildCommand() so the updated frontmatter
378
+ // is picked up by the rebuild and SQLite hashes stay in sync.
379
+ if (refreshVerified) {
380
+ const cardIds = refreshVerified.split(',').map(s => s.trim()).filter(Boolean);
381
+ const refreshDbPath = path.join(pmemPath, 'pmem.db');
382
+ if ((0, fs_1.fileExists)(refreshDbPath) && cardIds.length > 0) {
383
+ try {
384
+ const refreshDb = (0, db_1.openDatabase)(pmemPath); // open once
385
+ const refreshed = [];
386
+ for (const cardId of cardIds) {
387
+ const card = refreshDb.prepare('SELECT file_path FROM cards WHERE id = ?').get(cardId);
388
+ if (card) {
389
+ const cardFilePath = path.join(process.cwd(), card.file_path);
390
+ if ((0, fs_1.fileExists)(cardFilePath)) {
391
+ const content = (0, fs_1.readFile)(cardFilePath);
392
+ if (content) {
393
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
394
+ if (match) {
395
+ const frontmatterText = match[1];
396
+ const nowStr = new Date().toISOString();
397
+ let newFmText = frontmatterText;
398
+ const regex = /^last_verified:.*$/m;
399
+ if (regex.test(frontmatterText)) {
400
+ newFmText = frontmatterText.replace(regex, `last_verified: "${nowStr}"`);
401
+ }
402
+ else {
403
+ newFmText = frontmatterText.trimEnd() + `\nlast_verified: "${nowStr}"`;
404
+ }
405
+ const newContent = content.replace(/^---\n([\s\S]*?)\n---/, `---\n${newFmText}\n---`);
406
+ (0, fs_1.writeFile)(cardFilePath, newContent);
407
+ refreshed.push(cardId);
408
+ }
409
+ }
410
+ }
411
+ }
412
+ }
413
+ if (refreshed.length > 0) {
414
+ console.log(`Refreshed last_verified for: ${refreshed.join(', ')}`);
415
+ }
416
+ }
417
+ catch {
418
+ // skip cards that can't be refreshed
419
+ }
420
+ }
421
+ }
422
+ // Rebuild indexes — picks up frontmatter changes from --refresh-verified above
339
423
  console.log('Rebuilding indexes...');
340
424
  (0, rebuild_1.rebuildCommand)();
341
425
  console.log('\n✓ Memory updated.');