@aabadin/project-memory-context 0.2.4 → 0.2.6

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.
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ const PACKAGE_ROOT = resolve(__dirname, '..');
8
+
9
+ const GRAPH_EXPLORER_PATH = resolve(PACKAGE_ROOT, '../pmc-graph-explorer/server.mjs');
10
+
11
+ const child = spawn(process.execPath, [GRAPH_EXPLORER_PATH], {
12
+ stdio: 'inherit',
13
+ detached: true,
14
+ shell: true,
15
+ });
16
+
17
+ child.unref();
package/cli/context.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { access } from 'node:fs/promises';
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
3
3
  import { dirname, join, resolve } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
 
@@ -22,7 +22,7 @@ export async function findProjectRoot(startDir = process.cwd()) {
22
22
 
23
23
  while (true) {
24
24
  const installPath = join(currentDir, '.planning', 'project-memory-context', 'install.json');
25
- if (await fileExists(installPath)) {
25
+ if (existsSync(installPath)) {
26
26
  return currentDir;
27
27
  }
28
28
 
@@ -35,6 +35,23 @@ export async function findProjectRoot(startDir = process.cwd()) {
35
35
  }
36
36
  }
37
37
 
38
+ async function markContext(projectRoot, nodeIds) {
39
+ if (!nodeIds || nodeIds.length === 0) return;
40
+ const trackerPath = join(projectRoot, '.planning', 'project-memory-context', 'context-tracker.json');
41
+ let tracker = { activeNodeIds: [] };
42
+ try {
43
+ if (existsSync(trackerPath)) {
44
+ tracker = JSON.parse(readFileSync(trackerPath, 'utf-8'));
45
+ }
46
+ } catch {}
47
+ const existing = new Set(tracker.activeNodeIds || []);
48
+ nodeIds.forEach((id) => existing.add(id));
49
+ tracker.activeNodeIds = [...existing];
50
+ const dir = dirname(trackerPath);
51
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
52
+ writeFileSync(trackerPath, JSON.stringify(tracker, null, 2));
53
+ }
54
+
38
55
  export function parseArgs(args) {
39
56
  const DEPTH_VALUES = ['compact', 'extended', 'deep', 'disk'];
40
57
  const FOCUS_VALUES = ['dependencies', 'callers', 'containment', 'impact', 'all'];
@@ -190,12 +207,12 @@ export function buildRenderInput(engine, resolved, { depth, focus }) {
190
207
  };
191
208
  }
192
209
 
193
- export async function runTargetContext({ projectRoot, target, explicitMode, depth, focus }) {
194
- const artifacts = await loadArtifacts(projectRoot);
210
+ export async function runTargetContext({ projectRoot, target, explicitMode, depth, focus, artifacts }) {
211
+ const artfs = artifacts ?? await loadArtifacts(projectRoot);
195
212
  const engine = createQueryEngine({
196
- graph: artifacts.graph,
197
- symbolIndex: artifacts.symbolIndex,
198
- worklist: artifacts.worklist,
213
+ graph: artfs.graph,
214
+ symbolIndex: artfs.symbolIndex,
215
+ worklist: artfs.worklist,
199
216
  enrichmentDir: join(projectRoot, '.planning', 'project-memory-context', 'enrichment'),
200
217
  projectSlug: 'project',
201
218
  });
@@ -205,7 +222,7 @@ export async function runTargetContext({ projectRoot, target, explicitMode, dept
205
222
  const input = buildRenderInput(engine, resolved, { depth, focus });
206
223
  const output = renderTargetContext(input);
207
224
 
208
- return { output, resolved, input };
225
+ return { output, resolved, input, artifacts: artfs };
209
226
  }
210
227
 
211
228
  export async function runProjectContext(projectRoot = process.cwd(), refresh = false) {
@@ -301,7 +318,8 @@ export async function main(args = process.argv.slice(2)) {
301
318
  return 1;
302
319
  }
303
320
 
304
- const { output } = await runTargetContext({
321
+ const artifacts = await loadArtifacts(projectRoot);
322
+ const { output, resolved } = await runTargetContext({
305
323
  projectRoot,
306
324
  target: parsed.target,
307
325
  explicitMode: parsed.explicitMode,
@@ -309,6 +327,17 @@ export async function main(args = process.argv.slice(2)) {
309
327
  focus: parsed.focus,
310
328
  });
311
329
 
330
+ const nodeIdsToMark = [];
331
+ if (resolved.symbolKey) {
332
+ const entry = artifacts.symbolIndex[resolved.symbolKey];
333
+ if (entry?.graphNodeId) nodeIdsToMark.push(entry.graphNodeId);
334
+ } else if (resolved.target && (resolved.mode === 'file' || resolved.mode === 'symbol-missing')) {
335
+ const fileNodeId = resolved.target.replace(/[/\\]/g, '_').replace(/[:.]/g, '_');
336
+ nodeIdsToMark.push(fileNodeId);
337
+ }
338
+
339
+ await markContext(projectRoot, nodeIdsToMark);
340
+
312
341
  console.log(output);
313
342
  return 0;
314
343
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aabadin/project-memory-context",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Portable project memory context CLI — bootstraps semantic enrichment workflows for any AI coding agent.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "type": "module",
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "bin": {
14
14
  "pmc": "bin/pmc.mjs",
15
- "pmc-query-server": "mcp/pmc-query-server.mjs"
15
+ "pmc-query-server": "mcp/pmc-query-server.mjs",
16
+ "pmc-view-context": "bin/pmc-view-context.mjs"
16
17
  },
17
18
  "scripts": {
18
19
  "test": "node --test tests/*.test.mjs",
@@ -26,6 +27,7 @@
26
27
  "plugin/",
27
28
  "src/",
28
29
  "templates/",
30
+ "tools/pmc-graph-explorer/",
29
31
  "README.md",
30
32
  "LICENSE"
31
33
  ],
@@ -19,6 +19,7 @@ const COMMANDS = new Map([
19
19
  ['sanitize', 'cli/sanitize.mjs'],
20
20
  ['setup', 'cli/setup.mjs'],
21
21
  ['status', 'cli/status.mjs'],
22
+ ['view-context', 'bin/pmc-view-context.mjs'],
22
23
  ]);
23
24
 
24
25
  function usageText() {
@@ -109,6 +109,7 @@ async function installOpencode({ projectRoot, packageRoot, placeholders, globalC
109
109
  'opencode/commands/doctor.md',
110
110
  'opencode/commands/init-project.md',
111
111
  'opencode/commands/retry-errors.md',
112
+ 'opencode/commands/view-context.md',
112
113
  ];
113
114
 
114
115
  for (const tpl of commandTemplates) {
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: view-context
3
+ description: Open the PMC Graph Explorer web UI to visualize the enrichment graph with active context highlighting.
4
+ argument-hint: ""
5
+ allowed-tools:
6
+ - Bash
7
+ ---
8
+
9
+ <objective>
10
+ Open the PMC Graph Explorer to visualize the enrichment graph. The server runs on port 3001 and shows nodes consulted via /get-context with a cyan glow.
11
+ </objective>
12
+
13
+ <execution>
14
+ Start the graph explorer server:
15
+
16
+ ```bash
17
+ npx @aabadin/project-memory-context view-context
18
+ ```
19
+
20
+ Or if PMC is installed globally:
21
+
22
+ ```bash
23
+ pmc-view-context
24
+ ```
25
+
26
+ Then open http://localhost:3001 in your browser.
27
+ </execution>