claude-code-memory-explorer 0.13.0 → 0.14.0
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 +1 -1
- package/lib/open-editor.js +4 -1
- package/package.json +1 -1
- package/public/app.js +4 -3
- package/server.js +15 -8
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ PORT=8080 Custom port (default: 3544, falls back if busy)
|
|
|
35
35
|
--open Open browser on start
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
The config dir can also be set via the `CLAUDE_CONFIG_DIR` environment variable. Switch projects at runtime with the picker (Shift+P) or `?project=/path`.
|
|
38
|
+
The config dir can also be set via the `CLAUDE_CONFIG_DIR` environment variable; the analyzer's headless `claude` run inherits it. Switch projects at runtime with the picker (Shift+P) or `?project=/path`.
|
|
39
39
|
|
|
40
40
|
## License
|
|
41
41
|
|
package/lib/open-editor.js
CHANGED
|
@@ -221,6 +221,7 @@ function execNoShell(cmd, args, opts = {}) {
|
|
|
221
221
|
let argv = args;
|
|
222
222
|
const spawnOpts = {
|
|
223
223
|
cwd: opts.cwd || undefined,
|
|
224
|
+
env: opts.env,
|
|
224
225
|
timeout: opts.timeout,
|
|
225
226
|
windowsHide: true,
|
|
226
227
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -258,7 +259,9 @@ function execNoShell(cmd, args, opts = {}) {
|
|
|
258
259
|
child.on('error', reject);
|
|
259
260
|
child.on('close', (code, signal) => {
|
|
260
261
|
if (signal) return reject(new Error(`Command terminated by ${signal}: ${stderr.trim()}`));
|
|
261
|
-
|
|
262
|
+
// The claude CLI reports its own failures on stdout, so stderr alone is
|
|
263
|
+
// often empty and the caller would surface only the exit code.
|
|
264
|
+
if (code !== 0) return reject(new Error(stderr.trim() || stdout.trim() || `Command exited with code ${code}`));
|
|
262
265
|
resolve({ stdout, stderr });
|
|
263
266
|
});
|
|
264
267
|
});
|
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -1361,7 +1361,7 @@ function findingPrompt(f) {
|
|
|
1361
1361
|
} else if (paths.length > 1) {
|
|
1362
1362
|
header = 'In the Claude Code instruction files listed below, apply this fix:';
|
|
1363
1363
|
} else if (f.scope === 'user') {
|
|
1364
|
-
const path = stackData.find((s) => s.id === 'user-claude-md')?.path ||
|
|
1364
|
+
const path = stackData.find((s) => s.id === 'user-claude-md')?.path || summaryData.userClaudeMd;
|
|
1365
1365
|
header = `In the user-level Claude Code instructions at ${path}, apply this fix:`;
|
|
1366
1366
|
} else {
|
|
1367
1367
|
const dir = memoryDirPath();
|
|
@@ -2098,8 +2098,9 @@ document.addEventListener('keydown', (e) => {
|
|
|
2098
2098
|
e.preventDefault();
|
|
2099
2099
|
fwd(e);
|
|
2100
2100
|
}
|
|
2101
|
-
//
|
|
2102
|
-
|
|
2101
|
+
// Own branch: the Alt+digit case below requires !ctrlKey. The hub owns the Ctrl+Alt+letter
|
|
2102
|
+
// keymap and ignores unbound letters.
|
|
2103
|
+
if (e.ctrlKey && e.altKey && !e.shiftKey && !e.metaKey && /^[a-z]$/i.test(e.key)) {
|
|
2103
2104
|
e.preventDefault();
|
|
2104
2105
|
fwd(e);
|
|
2105
2106
|
}
|
package/server.js
CHANGED
|
@@ -25,7 +25,13 @@ const expandHome = (p) => (typeof p === 'string' ? p : '').replace(/^~/, os.home
|
|
|
25
25
|
const PORT = getArg('port') || process.env.PORT || 3544;
|
|
26
26
|
const AUTO_OPEN = process.argv.includes('--open');
|
|
27
27
|
const claudeDirArg = getArg('dir') || process.env.CLAUDE_CONFIG_DIR || process.env.CLAUDE_DIR;
|
|
28
|
-
const
|
|
28
|
+
const DEFAULT_CLAUDE_DIR = path.join(os.homedir(), '.claude');
|
|
29
|
+
const CLAUDE_DIR = claudeDirArg ? expandHome(claudeDirArg) : DEFAULT_CLAUDE_DIR;
|
|
30
|
+
const USER_CLAUDE_MD = path.join(CLAUDE_DIR, 'CLAUDE.md');
|
|
31
|
+
// Claude Code keeps .claude.json beside ~/.claude by default but inside CLAUDE_CONFIG_DIR
|
|
32
|
+
// when that is set, so the default dir must stay unset for the CLI rather than be spelled out.
|
|
33
|
+
const IS_DEFAULT_CLAUDE_DIR = path.resolve(CLAUDE_DIR) === path.resolve(DEFAULT_CLAUDE_DIR);
|
|
34
|
+
const CLI_ENV = IS_DEFAULT_CLAUDE_DIR ? process.env : { ...process.env, CLAUDE_CONFIG_DIR: CLAUDE_DIR };
|
|
29
35
|
const projectDirArg = getArg('project');
|
|
30
36
|
|
|
31
37
|
// #endregion CLI_ARGS
|
|
@@ -233,8 +239,7 @@ function discoverMemorySources(projectPath) {
|
|
|
233
239
|
}
|
|
234
240
|
|
|
235
241
|
// 2. User CLAUDE.md
|
|
236
|
-
const
|
|
237
|
-
const userInfo = fileInfo(userClaudeMd);
|
|
242
|
+
const userInfo = fileInfo(USER_CLAUDE_MD);
|
|
238
243
|
if (userInfo) {
|
|
239
244
|
sources.push({
|
|
240
245
|
id: 'user-claude-md',
|
|
@@ -843,7 +848,7 @@ const RUBRIC_CLAUDE_MD = [
|
|
|
843
848
|
const RUBRIC_CLAUDE_MD_USER = [
|
|
844
849
|
'User-level CLAUDE.md criteria (global instructions, loaded in every session of every project):',
|
|
845
850
|
'- Universality: every rule must hold on any project. Flag rules referencing one specific repo, stack, tool chain, or project path — they belong in that project\'s CLAUDE.md (kind: demote).',
|
|
846
|
-
|
|
851
|
+
`- Machine verification only: check that tools the file names exist on PATH (command -v / which / where) and that files, skills, or agents it references exist under ${CLAUDE_DIR}. NEVER verify against the working directory's repository — it is an arbitrary project and proves nothing about a global rule.`,
|
|
847
852
|
'- Self-consistency: flag sections, tags, or mnemonics that contradict or duplicate each other within the file and its imports.',
|
|
848
853
|
'- Executability: flag standards name-dropped without concrete behaviors ("follow style guide X") and rules the model cannot act on — require the 2-3 specific behaviors the user actually wants.',
|
|
849
854
|
'- Flag emphasis inflation (all-caps, NEVER/ALWAYS without stakes) and vague or no-op rules the model cannot act on.',
|
|
@@ -970,7 +975,7 @@ const REVIEWER_COMMON = [
|
|
|
970
975
|
// directory is an arbitrary project and must not influence a global-file audit.
|
|
971
976
|
const REVIEWER_COMMON_USER = [
|
|
972
977
|
'You review the user-level (global) CLAUDE.md that loads in every session of every project; the parent passes its name and full content.',
|
|
973
|
-
|
|
978
|
+
`Verify before judging, against the machine only: check tools the file names with \`command -v\` / \`which\` / \`where\` (the only Bash commands you may run), and check files, skills, or agents it references under ${CLAUDE_DIR} with your read tools. NEVER read or judge against the repository in your working directory — it is one arbitrary project and proves nothing about a global rule.`,
|
|
974
979
|
'Also judge whether the content is still needed at all: if the tool it references is gone from the machine or the file it points to no longer exists, say so explicitly.',
|
|
975
980
|
'A check you could not run is not evidence of absence. If a probe is denied by permissions or errors out, report that claim as UNCHECKED and say why — never turn a failed check into a stale or invalidate finding.',
|
|
976
981
|
];
|
|
@@ -1019,7 +1024,7 @@ const ANALYSIS_AGENTS = {
|
|
|
1019
1024
|
[RUBRIC_CLAUDE_MD, RUBRIC_IMPORTS],
|
|
1020
1025
|
),
|
|
1021
1026
|
'claude-md-user-reviewer': reviewerAgent(
|
|
1022
|
-
|
|
1027
|
+
`Reviews and verifies the user-level (global) CLAUDE.md against the user rubric, PATH, and ${CLAUDE_DIR} — never against the working repository.`,
|
|
1023
1028
|
[RUBRIC_CLAUDE_MD_USER, RUBRIC_IMPORTS],
|
|
1024
1029
|
REVIEWER_COMMON_USER,
|
|
1025
1030
|
),
|
|
@@ -1039,7 +1044,7 @@ function runClaudeAnalysis(prompt, model, cwd) {
|
|
|
1039
1044
|
try {
|
|
1040
1045
|
// cwd = the project root so the agent can verify claims against the actual
|
|
1041
1046
|
// files with its read-only tools (Read/Glob/Grep need no permission grants).
|
|
1042
|
-
child = spawn('claude', args, { cwd: cwd || os.tmpdir(), windowsHide: true });
|
|
1047
|
+
child = spawn('claude', args, { cwd: cwd || os.tmpdir(), env: CLI_ENV, windowsHide: true });
|
|
1043
1048
|
} catch (e) {
|
|
1044
1049
|
return resolve({ ok: false, error: `Failed to spawn claude: ${e.message}` });
|
|
1045
1050
|
}
|
|
@@ -1313,7 +1318,9 @@ app.get('/api/summary', (_req, res) => {
|
|
|
1313
1318
|
for (const f of sources) scopeChars[f.scope] = (scopeChars[f.scope] || 0) + (f.chars || 0);
|
|
1314
1319
|
const totalChars = sources.reduce((s, f) => s + (f.chars || 0), 0) + skillDesc.chars;
|
|
1315
1320
|
// ids: which sources make up the footprint, so the client can highlight them
|
|
1316
|
-
|
|
1321
|
+
// userClaudeMd: where the user-level file lives even when it does not exist yet, so the
|
|
1322
|
+
// client can name it in fix prompts without assuming ~/.claude
|
|
1323
|
+
res.json({ totalFiles, totalLines, totalBytes, totalChars, scopeChars, skillDesc, alwaysLoaded, conditional, onDemand, ids: sources.map(s => s.id), userClaudeMd: USER_CLAUDE_MD });
|
|
1317
1324
|
});
|
|
1318
1325
|
|
|
1319
1326
|
app.get('/api/stack', (_req, res) => {
|