@misterhuydo/cairn-mcp 1.7.5 → 1.8.1
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/bin/cairn-cli.js +169 -94
- package/package.json +1 -1
- package/src/tools/checkpoint.js +11 -6
- package/src/tools/resume.js +19 -19
package/bin/cairn-cli.js
CHANGED
|
@@ -14,91 +14,148 @@ const MINIFY_LANGS = new Set(['java', 'typescript', 'javascript', 'vue', 'python
|
|
|
14
14
|
|
|
15
15
|
// Memory sync scripts deployed to ~/.claude/scripts/ during global install.
|
|
16
16
|
// Using forward slashes in hook commands — bash on Windows handles them fine.
|
|
17
|
-
const SYNC_FORWARD_SCRIPT =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
import
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
17
|
+
const SYNC_FORWARD_SCRIPT = `#!/usr/bin/env node
|
|
18
|
+
// Forward sync: Claude Code auto-memory -> .cairn/memory/auto-memory/
|
|
19
|
+
// Triggered as a PostToolUse hook on Write.
|
|
20
|
+
// Only fires when the written file is inside Claude's projects memory dir
|
|
21
|
+
// and the current project has a .cairn/ folder.
|
|
22
|
+
import fs from 'node:fs';
|
|
23
|
+
import os from 'node:os';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
|
|
26
|
+
let stdinData = '';
|
|
27
|
+
process.stdin.setEncoding('utf8');
|
|
28
|
+
process.stdin.on('data', c => { stdinData += c; });
|
|
29
|
+
process.stdin.on('end', () => {
|
|
30
|
+
let fp;
|
|
31
|
+
try { fp = JSON.parse(stdinData)?.tool_input?.file_path || ''; } catch { process.exit(0); }
|
|
32
|
+
if (!fp) process.exit(0);
|
|
33
|
+
|
|
34
|
+
const claudeProjects = path.normalize(path.join(os.homedir(), '.claude', 'projects'));
|
|
35
|
+
const fpNorm = path.normalize(fp);
|
|
36
|
+
if (!fpNorm.startsWith(claudeProjects) || !fpNorm.includes('memory')) process.exit(0);
|
|
37
|
+
|
|
38
|
+
const cwd = process.cwd();
|
|
39
|
+
if (!fs.existsSync(path.join(cwd, '.cairn'))) process.exit(0);
|
|
40
|
+
|
|
41
|
+
const memoryDir = path.dirname(fpNorm);
|
|
42
|
+
const cairnMemory = path.join(cwd, '.cairn', 'memory');
|
|
43
|
+
const dest = path.join(cairnMemory, 'auto-memory');
|
|
44
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
45
|
+
|
|
46
|
+
for (const f of fs.readdirSync(memoryDir)) {
|
|
47
|
+
// Skip files that exist in canonical top-level — those are mirrored from cairn_memo,
|
|
48
|
+
// not out-of-band auto-memory writes, so backing them up would duplicate canonical content.
|
|
49
|
+
if (fs.existsSync(path.join(cairnMemory, f))) continue;
|
|
50
|
+
const src = path.join(memoryDir, f);
|
|
51
|
+
let st;
|
|
52
|
+
try { st = fs.statSync(src); } catch { continue; }
|
|
53
|
+
if (!st.isFile()) continue;
|
|
54
|
+
const dst = path.join(dest, f);
|
|
55
|
+
fs.copyFileSync(src, dst);
|
|
56
|
+
try { fs.utimesSync(dst, st.atime, st.mtime); } catch {}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
48
59
|
`;
|
|
49
60
|
|
|
50
|
-
const SYNC_RESTORE_SCRIPT =
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
import
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
encoded = cwd.replace(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
61
|
+
const SYNC_RESTORE_SCRIPT = `#!/usr/bin/env node
|
|
62
|
+
// Restore sync: .cairn/memory/ -> Claude Code memory dir for current location.
|
|
63
|
+
// Triggered as a UserPromptSubmit hook on each new session prompt.
|
|
64
|
+
// Three passes:
|
|
65
|
+
// 0. Bootstrap: capture any auto-memory files newer/missing in .cairn/memory/auto-memory/
|
|
66
|
+
// (catches files pre-dating the forward-sync hook install).
|
|
67
|
+
// 1. New-machine restore: copy .cairn/memory/auto-memory/ -> Claude memory dir
|
|
68
|
+
// (only when target is empty, preserves Claude-native memory on new drive)
|
|
69
|
+
// 2. Always: mtime-mirror .cairn/memory/*.md -> Claude memory dir
|
|
70
|
+
// (so cairn_memo writes are reflected)
|
|
71
|
+
// Path encoding: H:\\Projects\\Cairn -> H--Projects-Cairn
|
|
72
|
+
// (':', '\\', '/' all map to '-')
|
|
73
|
+
import fs from 'node:fs';
|
|
74
|
+
import os from 'node:os';
|
|
75
|
+
import path from 'node:path';
|
|
76
|
+
|
|
77
|
+
const cwd = process.cwd();
|
|
78
|
+
const cairnMemory = path.join(cwd, '.cairn', 'memory');
|
|
79
|
+
if (!fs.existsSync(cairnMemory)) process.exit(0);
|
|
80
|
+
|
|
81
|
+
const encoded = cwd.replace(/[:/\\\\]/g, '-').replace(/^-+/, '');
|
|
82
|
+
const memoryDir = path.join(os.homedir(), '.claude', 'projects', encoded, 'memory');
|
|
83
|
+
fs.mkdirSync(memoryDir, { recursive: true });
|
|
84
|
+
|
|
85
|
+
const cairnAutoMemory = path.join(cairnMemory, 'auto-memory');
|
|
86
|
+
|
|
87
|
+
function copyWithMtime(src, dst, srcStat) {
|
|
88
|
+
fs.copyFileSync(src, dst);
|
|
89
|
+
try { fs.utimesSync(dst, srcStat.atime, srcStat.mtime); } catch {}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Pass 0: bootstrap — capture auto-memory files missing/newer in .cairn/memory/auto-memory/
|
|
93
|
+
// (catches files that pre-date the forward-sync hook install)
|
|
94
|
+
// Skip files present in canonical top-level: they are mirrored from cairn_memo, not auto-memory.
|
|
95
|
+
const claudeFiles = fs.readdirSync(memoryDir);
|
|
96
|
+
let bootstrapped = 0;
|
|
97
|
+
if (claudeFiles.length > 0) {
|
|
98
|
+
fs.mkdirSync(cairnAutoMemory, { recursive: true });
|
|
99
|
+
for (const f of claudeFiles) {
|
|
100
|
+
if (fs.existsSync(path.join(cairnMemory, f))) continue;
|
|
101
|
+
const src = path.join(memoryDir, f);
|
|
102
|
+
let srcStat;
|
|
103
|
+
try { srcStat = fs.statSync(src); } catch { continue; }
|
|
104
|
+
if (!srcStat.isFile()) continue;
|
|
105
|
+
const dst = path.join(cairnAutoMemory, f);
|
|
106
|
+
let needCopy = !fs.existsSync(dst);
|
|
107
|
+
if (!needCopy) {
|
|
108
|
+
try { needCopy = srcStat.mtimeMs > fs.statSync(dst).mtimeMs; } catch { needCopy = true; }
|
|
109
|
+
}
|
|
110
|
+
if (needCopy) {
|
|
111
|
+
copyWithMtime(src, dst, srcStat);
|
|
112
|
+
bootstrapped++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (bootstrapped > 0) {
|
|
117
|
+
process.stdout.write(\`[cairn] Bootstrapped \${bootstrapped} auto-memory file(s) into .cairn/memory/auto-memory/\\n\`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Pass 1: new-machine restore from auto-memory backup (only when target empty)
|
|
121
|
+
if (fs.existsSync(cairnAutoMemory)) {
|
|
122
|
+
const targetEmpty = !fs.readdirSync(memoryDir).some(f => f.endsWith('.md'));
|
|
123
|
+
if (targetEmpty) {
|
|
124
|
+
let restored = 0;
|
|
125
|
+
for (const f of fs.readdirSync(cairnAutoMemory)) {
|
|
126
|
+
const src = path.join(cairnAutoMemory, f);
|
|
127
|
+
let srcStat;
|
|
128
|
+
try { srcStat = fs.statSync(src); } catch { continue; }
|
|
129
|
+
if (!srcStat.isFile()) continue;
|
|
130
|
+
copyWithMtime(src, path.join(memoryDir, f), srcStat);
|
|
131
|
+
restored++;
|
|
132
|
+
}
|
|
133
|
+
if (restored > 0) {
|
|
134
|
+
process.stdout.write(\`[cairn] Restored \${restored} memory files from .cairn/memory/auto-memory/ (new project location detected)\\n\`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Pass 2: mtime-mirror top-level .cairn/memory/*.md -> Claude memory dir
|
|
140
|
+
let synced = 0;
|
|
141
|
+
for (const f of fs.readdirSync(cairnMemory)) {
|
|
142
|
+
const src = path.join(cairnMemory, f);
|
|
143
|
+
let srcStat;
|
|
144
|
+
try { srcStat = fs.statSync(src); } catch { continue; }
|
|
145
|
+
if (!srcStat.isFile()) continue; // skip subdirs like auto-memory/
|
|
146
|
+
const dst = path.join(memoryDir, f);
|
|
147
|
+
let needCopy = !fs.existsSync(dst);
|
|
148
|
+
if (!needCopy) {
|
|
149
|
+
try { needCopy = srcStat.mtimeMs > fs.statSync(dst).mtimeMs; } catch { needCopy = true; }
|
|
150
|
+
}
|
|
151
|
+
if (needCopy) {
|
|
152
|
+
copyWithMtime(src, dst, srcStat);
|
|
153
|
+
synced++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (synced > 0) {
|
|
157
|
+
process.stdout.write(\`[cairn] Synced \${synced} memory file(s) from .cairn/memory/ to Claude memory dir\\n\`);
|
|
158
|
+
}
|
|
102
159
|
`;
|
|
103
160
|
|
|
104
161
|
process.on('uncaughtException', (e) => {
|
|
@@ -509,10 +566,10 @@ if (subcommand === '--version' || subcommand === '-v') {
|
|
|
509
566
|
console.log(' PreToolUse[Read] -> cairn minify (compress source files for reading)');
|
|
510
567
|
console.log(' PreToolUse[Edit] -> cairn edit-guard (block Edit until re-read with full content)')
|
|
511
568
|
console.log(' PreToolUse[Write] -> cairn edit-guard (clear minify state before full file overwrite)');
|
|
512
|
-
console.log(' PostToolUse[Write] -> sync-memory-forward.
|
|
569
|
+
console.log(' PostToolUse[Write] -> sync-memory-forward.mjs (sync ~/.claude/memory -> .cairn/memory/auto-memory/)');
|
|
513
570
|
console.log(' Stop -> cairn checkpoint --auto (auto-save session)');
|
|
514
571
|
console.log(' UserPromptSubmit -> cairn resume-hint (remind Claude of prior session)');
|
|
515
|
-
console.log(' UserPromptSubmit -> sync-memory-restore.
|
|
572
|
+
console.log(' UserPromptSubmit -> sync-memory-restore.mjs (sync .cairn/memory/ -> ~/.claude/memory, mtime-based)');
|
|
516
573
|
console.log('');
|
|
517
574
|
console.log('Restart Claude Code to activate the MCP server, then open any project and start working.');
|
|
518
575
|
console.log('Upgrade any time with: npm install -g @misterhuydo/cairn-mcp');
|
|
@@ -532,12 +589,12 @@ if (subcommand === '--version' || subcommand === '-v') {
|
|
|
532
589
|
console.log(' PreToolUse[Edit] -> cairn edit-guard (block Edit until re-read with full content)')
|
|
533
590
|
console.log(' PreToolUse[Write] -> cairn edit-guard (clear minify state before full file overwrite)');
|
|
534
591
|
if (isGlobal) {
|
|
535
|
-
console.log(' PostToolUse[Write] -> sync-memory-forward.
|
|
592
|
+
console.log(' PostToolUse[Write] -> sync-memory-forward.mjs (sync ~/.claude/memory -> .cairn/memory/auto-memory/)');
|
|
536
593
|
}
|
|
537
594
|
console.log(' Stop -> cairn checkpoint --auto (auto-save session)');
|
|
538
595
|
console.log(' UserPromptSubmit -> cairn resume-hint (remind Claude of prior session)');
|
|
539
596
|
if (isGlobal) {
|
|
540
|
-
console.log(' UserPromptSubmit -> sync-memory-restore.
|
|
597
|
+
console.log(' UserPromptSubmit -> sync-memory-restore.mjs (sync .cairn/memory/ -> ~/.claude/memory, mtime-based)');
|
|
541
598
|
}
|
|
542
599
|
process.exit(0);
|
|
543
600
|
|
|
@@ -579,32 +636,50 @@ function applyHooks(settingsDir, isGlobal) {
|
|
|
579
636
|
}
|
|
580
637
|
|
|
581
638
|
// UserPromptSubmit: cairn resume-hint
|
|
582
|
-
|
|
639
|
+
let submitHooks = settings.hooks.UserPromptSubmit || [];
|
|
583
640
|
if (!submitHooks.some(h => h.hooks?.some(hh => hh.command === 'cairn resume-hint'))) {
|
|
584
641
|
submitHooks.push({ hooks: [{ type: 'command', command: 'cairn resume-hint' }] });
|
|
585
642
|
}
|
|
586
643
|
|
|
587
644
|
if (isGlobal) {
|
|
588
|
-
// Deploy memory sync scripts to ~/.claude/scripts/
|
|
645
|
+
// Deploy memory sync scripts to ~/.claude/scripts/ (Node — cross-platform, no python3 required)
|
|
589
646
|
const scriptsDir = path.join(settingsDir, 'scripts');
|
|
590
647
|
fs.mkdirSync(scriptsDir, { recursive: true });
|
|
591
|
-
const forwardScript = path.join(scriptsDir, 'sync-memory-forward.
|
|
592
|
-
const restoreScript = path.join(scriptsDir, 'sync-memory-restore.
|
|
648
|
+
const forwardScript = path.join(scriptsDir, 'sync-memory-forward.mjs');
|
|
649
|
+
const restoreScript = path.join(scriptsDir, 'sync-memory-restore.mjs');
|
|
593
650
|
fs.writeFileSync(forwardScript, SYNC_FORWARD_SCRIPT, 'utf8');
|
|
594
651
|
fs.writeFileSync(restoreScript, SYNC_RESTORE_SCRIPT, 'utf8');
|
|
595
652
|
|
|
653
|
+
// Clean up legacy Python scripts from prior installs
|
|
654
|
+
for (const legacy of ['sync-memory-forward.py', 'sync-memory-restore.py']) {
|
|
655
|
+
const p = path.join(scriptsDir, legacy);
|
|
656
|
+
if (fs.existsSync(p)) { try { fs.unlinkSync(p); } catch {} }
|
|
657
|
+
}
|
|
658
|
+
|
|
596
659
|
// Use forward slashes in hook commands — works across bash on Windows and Unix
|
|
597
|
-
const
|
|
598
|
-
const
|
|
660
|
+
const scriptsDirFwd = scriptsDir.replace(/\\/g, '/');
|
|
661
|
+
const fwdCmd = `node "${scriptsDirFwd}/sync-memory-forward.mjs"`;
|
|
662
|
+
const rstCmd = `node "${scriptsDirFwd}/sync-memory-restore.mjs"`;
|
|
663
|
+
|
|
664
|
+
// Strip stale python3 .py references from prior installs (avoids duplicate hooks firing)
|
|
665
|
+
const isLegacyPyCmd = (cmd) => typeof cmd === 'string' &&
|
|
666
|
+
/python3?\s+.*sync-memory-(forward|restore)\.py/.test(cmd);
|
|
667
|
+
const stripLegacy = (entries) => entries
|
|
668
|
+
.map(entry => ({
|
|
669
|
+
...entry,
|
|
670
|
+
hooks: (entry.hooks || []).filter(h => !isLegacyPyCmd(h.command)),
|
|
671
|
+
}))
|
|
672
|
+
.filter(entry => entry.hooks.length > 0);
|
|
599
673
|
|
|
600
674
|
// PostToolUse[Write]: forward sync Claude memory -> .cairn/memory/auto-memory/
|
|
601
|
-
|
|
675
|
+
let postHooks = stripLegacy(settings.hooks.PostToolUse || []);
|
|
602
676
|
if (!postHooks.some(h => h.matcher === 'Write' && h.hooks?.some(hh => hh.command === fwdCmd))) {
|
|
603
677
|
postHooks.push({ matcher: 'Write', hooks: [{ type: 'command', command: fwdCmd }] });
|
|
604
678
|
}
|
|
605
679
|
settings.hooks.PostToolUse = postHooks;
|
|
606
680
|
|
|
607
681
|
// UserPromptSubmit: sync .cairn/memory/ -> Claude memory dir (always, mtime-based)
|
|
682
|
+
submitHooks = stripLegacy(submitHooks);
|
|
608
683
|
if (!submitHooks.some(h => h.hooks?.some(hh => hh.command === rstCmd))) {
|
|
609
684
|
submitHooks.push({ hooks: [{ type: 'command', command: rstCmd }] });
|
|
610
685
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@misterhuydo/cairn-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "MCP server that gives Claude Code persistent memory across sessions. Index your codebase once, search symbols, bundle source, scan for vulnerabilities, and checkpoint/resume work — across Java, TypeScript, Vue, Python, SQL and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
package/src/tools/checkpoint.js
CHANGED
|
@@ -47,14 +47,19 @@ export function checkpoint(_db, { message, active_files = [], notes = [] }) {
|
|
|
47
47
|
const sessionPath = path.join(cairnDir, 'session.json');
|
|
48
48
|
const dateStr = new Date().toISOString().slice(0, 10);
|
|
49
49
|
|
|
50
|
-
// Snapshot mtimes of
|
|
50
|
+
// Snapshot mtimes of all indexed files so resume can detect changes accurately.
|
|
51
|
+
// Without a full snapshot, resume labels every non-active file as "new" and
|
|
52
|
+
// triggers a full reindex on every call.
|
|
51
53
|
const mtimeSnapshot = {};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// File may not exist yet
|
|
54
|
+
try {
|
|
55
|
+
const rows = _db.prepare('SELECT path FROM main.files').all();
|
|
56
|
+
for (const { path: filePath } of rows) {
|
|
57
|
+
try { mtimeSnapshot[filePath] = fs.statSync(filePath).mtimeMs; } catch { }
|
|
57
58
|
}
|
|
59
|
+
} catch { /* DB may not exist on first run */ }
|
|
60
|
+
// Also include active_files in case they are not yet indexed
|
|
61
|
+
for (const filePath of active_files) {
|
|
62
|
+
try { mtimeSnapshot[filePath] = fs.statSync(filePath).mtimeMs; } catch { }
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
// Process all notes: long strings get filed, short ones stay inline, refs pass through
|
package/src/tools/resume.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import {
|
|
3
|
+
import { exec } from 'child_process';
|
|
4
|
+
import { promisify } from 'util';
|
|
5
|
+
const execAsync = promisify(exec);
|
|
4
6
|
import { getCairnDir, getProjectRoot, getMemoryDir } from '../graph/cwd.js';
|
|
5
7
|
import { walkRepo } from '../indexer/fileWalker.js';
|
|
6
8
|
import { getAttachedSchemas } from '../graph/db.js';
|
|
@@ -172,28 +174,26 @@ export async function resume(db) {
|
|
|
172
174
|
result.migrated = `Removed ${gitSnapshotNotes.length} legacy git-snapshot note(s). Original session backed up to session.bak.json.`;
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
// Git state —
|
|
176
|
-
// regardless of checkpoint staleness.
|
|
177
|
+
// Git state — run all three queries in parallel to avoid sequential git startup overhead.
|
|
177
178
|
const gitRoot = getProjectRoot();
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
})
|
|
179
|
+
const since = new Date(session.checkpoint_at).toISOString();
|
|
180
|
+
const [logResult, diffResult, statusResult] = await Promise.allSettled([
|
|
181
|
+
execAsync(`git log --oneline --after="${since}"`, { cwd: gitRoot, timeout: 5000 }),
|
|
182
|
+
execAsync('git diff --name-only HEAD', { cwd: gitRoot, timeout: 5000 }),
|
|
183
|
+
execAsync('git status --short', { cwd: gitRoot, timeout: 5000 }),
|
|
184
|
+
]);
|
|
185
|
+
if (logResult.status === 'fulfilled') {
|
|
186
|
+
const raw = logResult.value.stdout.trim();
|
|
183
187
|
if (raw) result.commits_since_checkpoint = raw.split('\n');
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const raw =
|
|
187
|
-
cwd: gitRoot, encoding: 'utf8', timeout: 5000,
|
|
188
|
-
}).trim();
|
|
188
|
+
}
|
|
189
|
+
if (diffResult.status === 'fulfilled') {
|
|
190
|
+
const raw = diffResult.value.stdout.trim();
|
|
189
191
|
if (raw) result.uncommitted_changes = raw.split('\n');
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const raw =
|
|
193
|
-
cwd: gitRoot, encoding: 'utf8', timeout: 5000,
|
|
194
|
-
}).trim();
|
|
192
|
+
}
|
|
193
|
+
if (statusResult.status === 'fulfilled') {
|
|
194
|
+
const raw = statusResult.value.stdout.trim();
|
|
195
195
|
if (raw) result.git_status = raw;
|
|
196
|
-
}
|
|
196
|
+
}
|
|
197
197
|
|
|
198
198
|
if (relocation) result.relocated = relocation;
|
|
199
199
|
if (preferences.length > 0) {
|