namnam-skills 1.0.1 → 1.0.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.
- package/package.json +1 -1
- package/src/cli.js +474 -6
- package/src/conversation.js +467 -0
- package/src/indexer.js +151 -0
- package/src/templates/{core/namnam.md → namnam.md} +118 -5
- package/src/watcher.js +356 -0
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
namnam index build # Build/rebuild codebase index
|
|
25
25
|
namnam index status # Show index status
|
|
26
26
|
namnam index search <query> # Search functions, classes, types
|
|
27
|
+
namnam index watch # Live indexing (auto-update)
|
|
27
28
|
|
|
28
29
|
# Orchestration (default) - use as /namnam in AI assistants
|
|
29
30
|
/namnam <task> # Intelligent agent orchestration
|
|
@@ -40,12 +41,18 @@ namnam index search <query> # Search functions, classes, types
|
|
|
40
41
|
/namnam review src/auth # Review specific path
|
|
41
42
|
/namnam validate # Run all checks + auto-fix
|
|
42
43
|
|
|
43
|
-
# Conversations - use as CLI commands
|
|
44
|
+
# Conversations (manual) - use as CLI commands
|
|
44
45
|
namnam conv save # Save conversation context
|
|
45
46
|
namnam conv list # List saved conversations
|
|
46
47
|
namnam conv show <id> # View conversation
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
@conversation:<id> # Reference in prompts
|
|
49
|
+
|
|
50
|
+
# Auto-Memory (automatic) - persistent context
|
|
51
|
+
namnam memory remember <text> # Save a memory
|
|
52
|
+
namnam memory recall [query] # Recall relevant memories
|
|
53
|
+
namnam memory session --start # Start work session
|
|
54
|
+
namnam memory session --end # End and save session
|
|
55
|
+
namnam memory context # Generate AI context
|
|
49
56
|
|
|
50
57
|
# Special Modes
|
|
51
58
|
/namnam --test # Focus on testing
|
|
@@ -104,8 +111,9 @@ Parse the first argument after `/namnam`:
|
|
|
104
111
|
|
|
105
112
|
| Input Pattern | Action |
|
|
106
113
|
|---------------|--------|
|
|
107
|
-
| (no args) | Check/build index, show
|
|
114
|
+
| (no args) | Check/build index, auto-load memories, show status |
|
|
108
115
|
| `index <subcmd>` | Execute Index Command |
|
|
116
|
+
| `memory <subcmd>` | Execute Memory Command |
|
|
109
117
|
| `commit` | Execute Git Commit |
|
|
110
118
|
| `push` | Execute Git Push |
|
|
111
119
|
| `status` | Execute Git Status |
|
|
@@ -115,6 +123,15 @@ Parse the first argument after `/namnam`:
|
|
|
115
123
|
| `--<mode> <task>` | Execute Orchestration with mode |
|
|
116
124
|
| `<any other text>` | Execute Orchestration (default) |
|
|
117
125
|
|
|
126
|
+
### CRITICAL: Auto-Load Context
|
|
127
|
+
|
|
128
|
+
**Before ANY orchestration task**, load context in this order:
|
|
129
|
+
|
|
130
|
+
1. **Check Index**: `namnam index status` - rebuild if stale
|
|
131
|
+
2. **Load Auto-Memories**: `namnam memory context -q "<task>"` - get relevant memories
|
|
132
|
+
3. **Check @conversation refs**: Parse `@conversation:<id>` from user input
|
|
133
|
+
4. **Proceed with task**: Use loaded context for informed decisions
|
|
134
|
+
|
|
118
135
|
---
|
|
119
136
|
|
|
120
137
|
## 0. Codebase Index (Auto-loaded)
|
|
@@ -529,7 +546,103 @@ When user message contains `@conversation:<id>`:
|
|
|
529
546
|
|
|
530
547
|
---
|
|
531
548
|
|
|
532
|
-
## 6.
|
|
549
|
+
## 6. Auto-Memory System
|
|
550
|
+
|
|
551
|
+
NAMNAM automatically remembers context across sessions. Unlike `@conversation:id` (manual), auto-memory works **automatically**.
|
|
552
|
+
|
|
553
|
+
### How Auto-Memory Works
|
|
554
|
+
|
|
555
|
+
1. **Automatic Context Loading** - When you start, relevant memories are auto-loaded
|
|
556
|
+
2. **Pattern Learning** - Remembers coding patterns, preferences, decisions
|
|
557
|
+
3. **Session Tracking** - Tracks what you're working on within a session
|
|
558
|
+
4. **Smart Recall** - Uses relevance scoring to surface important memories
|
|
559
|
+
|
|
560
|
+
### Auto-Memory Commands
|
|
561
|
+
|
|
562
|
+
```bash
|
|
563
|
+
# Remember something
|
|
564
|
+
namnam memory remember "User prefers TypeScript strict mode" -t pattern
|
|
565
|
+
namnam memory remember "Decided to use Prisma for ORM" -t decision -i high
|
|
566
|
+
|
|
567
|
+
# Recall memories
|
|
568
|
+
namnam memory recall "database" # Find relevant memories
|
|
569
|
+
namnam memory recall --json # For programmatic use
|
|
570
|
+
|
|
571
|
+
# List all memories
|
|
572
|
+
namnam memory list # Show recent memories
|
|
573
|
+
namnam memory list -t decision # Filter by type
|
|
574
|
+
|
|
575
|
+
# Session management
|
|
576
|
+
namnam memory session --start "Implement auth" # Start session
|
|
577
|
+
namnam memory session --decision "Use JWT" # Record decision
|
|
578
|
+
namnam memory session --note "User prefers..." # Add note
|
|
579
|
+
namnam memory session --end # End and save
|
|
580
|
+
|
|
581
|
+
# Generate context for AI
|
|
582
|
+
namnam memory context -q "authentication" # Get relevant context
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### Memory Types
|
|
586
|
+
|
|
587
|
+
| Type | Use Case | Example |
|
|
588
|
+
|------|----------|---------|
|
|
589
|
+
| `decision` | Architectural choices | "Use PostgreSQL over MySQL" |
|
|
590
|
+
| `pattern` | Coding preferences | "User prefers tabs over spaces" |
|
|
591
|
+
| `context` | General information | "Project uses monorepo structure" |
|
|
592
|
+
| `learning` | Learned behaviors | "Always run tests before commit" |
|
|
593
|
+
|
|
594
|
+
### Auto-Loading Memories
|
|
595
|
+
|
|
596
|
+
**CRITICAL**: Before starting any task, auto-load relevant memories:
|
|
597
|
+
|
|
598
|
+
```bash
|
|
599
|
+
# Check for auto-memories
|
|
600
|
+
namnam memory context -q "<current_task>"
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
If memories exist, they appear as:
|
|
604
|
+
|
|
605
|
+
```xml
|
|
606
|
+
<auto-memories>
|
|
607
|
+
## Prior Decisions
|
|
608
|
+
- **ORM Choice**: Decided to use Prisma for database access
|
|
609
|
+
|
|
610
|
+
## Learned Patterns
|
|
611
|
+
- User prefers functional components over class components
|
|
612
|
+
- Always add TypeScript types to function parameters
|
|
613
|
+
|
|
614
|
+
## Relevant Context
|
|
615
|
+
- Project uses pnpm, not npm
|
|
616
|
+
</auto-memories>
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
**Treat auto-memories as authoritative** - they represent prior decisions and user preferences.
|
|
620
|
+
|
|
621
|
+
### Comparison: @conversation vs Auto-Memory
|
|
622
|
+
|
|
623
|
+
| Feature | @conversation:id | Auto-Memory |
|
|
624
|
+
|---------|-----------------|-------------|
|
|
625
|
+
| Trigger | Manual reference | Automatic |
|
|
626
|
+
| Scope | Specific conversation | All relevant context |
|
|
627
|
+
| Use case | Continue specific thread | General context |
|
|
628
|
+
| Storage | Per-conversation folder | Global memory store |
|
|
629
|
+
|
|
630
|
+
**Both systems work together** - use `@conversation:id` for specific threads, auto-memory for general context.
|
|
631
|
+
|
|
632
|
+
### Storage Structure
|
|
633
|
+
|
|
634
|
+
```
|
|
635
|
+
.claude/
|
|
636
|
+
├── auto-memories/
|
|
637
|
+
│ ├── auto-index.json # All memories indexed
|
|
638
|
+
│ ├── current-session.json # Active session
|
|
639
|
+
│ └── sessions/ # Archived sessions
|
|
640
|
+
└── conversations/ # Manual @conversation storage
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
---
|
|
644
|
+
|
|
645
|
+
## 7. Platform-Specific
|
|
533
646
|
|
|
534
647
|
### Claude Code
|
|
535
648
|
- Full Task tool support
|
package/src/watcher.js
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Watcher for Live Indexing
|
|
3
|
+
*
|
|
4
|
+
* Watches for file changes and triggers incremental index updates.
|
|
5
|
+
* Uses Node.js native fs.watch with debouncing for efficiency.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs-extra';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { EventEmitter } from 'events';
|
|
11
|
+
import {
|
|
12
|
+
buildIndex,
|
|
13
|
+
hasIndex,
|
|
14
|
+
getIndexDir,
|
|
15
|
+
checkIndexChanges,
|
|
16
|
+
getIndexMeta,
|
|
17
|
+
updateIndexIncremental
|
|
18
|
+
} from './indexer.js';
|
|
19
|
+
|
|
20
|
+
// Constants
|
|
21
|
+
const DEBOUNCE_MS = 1000; // Wait 1 second after last change
|
|
22
|
+
const BATCH_INTERVAL_MS = 5000; // Process batch every 5 seconds max
|
|
23
|
+
|
|
24
|
+
// File patterns to watch
|
|
25
|
+
const WATCH_EXTENSIONS = new Set([
|
|
26
|
+
'.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs',
|
|
27
|
+
'.py', '.rb', '.go', '.rs', '.java', '.kt',
|
|
28
|
+
'.c', '.cpp', '.h', '.hpp', '.cs',
|
|
29
|
+
'.vue', '.svelte', '.astro',
|
|
30
|
+
'.json', '.yaml', '.yml', '.toml',
|
|
31
|
+
'.md', '.mdx',
|
|
32
|
+
'.css', '.scss', '.less',
|
|
33
|
+
'.html', '.xml',
|
|
34
|
+
'.sql', '.graphql', '.prisma'
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
// Directories to skip
|
|
38
|
+
const SKIP_DIRS = new Set([
|
|
39
|
+
'node_modules', '.git', '.svn', '.hg',
|
|
40
|
+
'dist', 'build', 'out', '.next', '.nuxt',
|
|
41
|
+
'coverage', '.nyc_output',
|
|
42
|
+
'__pycache__', '.pytest_cache',
|
|
43
|
+
'vendor', 'target',
|
|
44
|
+
'.claude', '.cursor', '.vscode'
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* IndexWatcher - Watches filesystem and triggers index updates
|
|
49
|
+
*/
|
|
50
|
+
export class IndexWatcher extends EventEmitter {
|
|
51
|
+
constructor(cwd = process.cwd(), options = {}) {
|
|
52
|
+
super();
|
|
53
|
+
this.cwd = cwd;
|
|
54
|
+
this.options = {
|
|
55
|
+
debounceMs: options.debounceMs || DEBOUNCE_MS,
|
|
56
|
+
batchIntervalMs: options.batchIntervalMs || BATCH_INTERVAL_MS,
|
|
57
|
+
autoRebuild: options.autoRebuild !== false,
|
|
58
|
+
verbose: options.verbose || false,
|
|
59
|
+
...options
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
this.watchers = new Map();
|
|
63
|
+
this.pendingChanges = new Map();
|
|
64
|
+
this.debounceTimer = null;
|
|
65
|
+
this.batchTimer = null;
|
|
66
|
+
this.isIndexing = false;
|
|
67
|
+
this.isRunning = false;
|
|
68
|
+
this.stats = {
|
|
69
|
+
filesChanged: 0,
|
|
70
|
+
indexUpdates: 0,
|
|
71
|
+
lastUpdate: null
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Start watching the directory
|
|
77
|
+
*/
|
|
78
|
+
async start() {
|
|
79
|
+
if (this.isRunning) {
|
|
80
|
+
this.log('Watcher already running');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.log('Starting file watcher...');
|
|
85
|
+
this.isRunning = true;
|
|
86
|
+
|
|
87
|
+
// Ensure index exists
|
|
88
|
+
if (!(await hasIndex(this.cwd))) {
|
|
89
|
+
this.log('No index found, building initial index...');
|
|
90
|
+
this.emit('indexing', { type: 'initial' });
|
|
91
|
+
await buildIndex(this.cwd, {
|
|
92
|
+
onProgress: (progress) => this.emit('progress', progress)
|
|
93
|
+
});
|
|
94
|
+
this.emit('indexed', { type: 'initial' });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Start watching
|
|
98
|
+
await this.watchDirectory(this.cwd);
|
|
99
|
+
|
|
100
|
+
// Start batch processor
|
|
101
|
+
this.batchTimer = setInterval(() => {
|
|
102
|
+
this.processPendingChanges();
|
|
103
|
+
}, this.options.batchIntervalMs);
|
|
104
|
+
|
|
105
|
+
this.emit('started');
|
|
106
|
+
this.log(`Watching ${this.watchers.size} directories`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Stop watching
|
|
111
|
+
*/
|
|
112
|
+
stop() {
|
|
113
|
+
if (!this.isRunning) return;
|
|
114
|
+
|
|
115
|
+
this.log('Stopping file watcher...');
|
|
116
|
+
this.isRunning = false;
|
|
117
|
+
|
|
118
|
+
// Clear timers
|
|
119
|
+
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
120
|
+
if (this.batchTimer) clearInterval(this.batchTimer);
|
|
121
|
+
|
|
122
|
+
// Close all watchers
|
|
123
|
+
for (const [dir, watcher] of this.watchers) {
|
|
124
|
+
watcher.close();
|
|
125
|
+
}
|
|
126
|
+
this.watchers.clear();
|
|
127
|
+
this.pendingChanges.clear();
|
|
128
|
+
|
|
129
|
+
this.emit('stopped');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Watch a directory recursively
|
|
134
|
+
*/
|
|
135
|
+
async watchDirectory(dir) {
|
|
136
|
+
try {
|
|
137
|
+
const items = await fs.readdir(dir, { withFileTypes: true });
|
|
138
|
+
|
|
139
|
+
for (const item of items) {
|
|
140
|
+
if (item.isDirectory() && !SKIP_DIRS.has(item.name) && !item.name.startsWith('.')) {
|
|
141
|
+
const subDir = path.join(dir, item.name);
|
|
142
|
+
await this.watchDirectory(subDir);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Watch this directory
|
|
147
|
+
const watcher = fs.watch(dir, (eventType, filename) => {
|
|
148
|
+
if (filename) {
|
|
149
|
+
this.handleFileChange(eventType, path.join(dir, filename));
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
watcher.on('error', (err) => {
|
|
154
|
+
this.log(`Watcher error for ${dir}: ${err.message}`);
|
|
155
|
+
// Try to recover by removing and re-adding watcher
|
|
156
|
+
this.watchers.delete(dir);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
this.watchers.set(dir, watcher);
|
|
160
|
+
} catch (err) {
|
|
161
|
+
this.log(`Error watching ${dir}: ${err.message}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Handle a file change event
|
|
167
|
+
*/
|
|
168
|
+
handleFileChange(eventType, filePath) {
|
|
169
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
170
|
+
const relativePath = path.relative(this.cwd, filePath).replace(/\\/g, '/');
|
|
171
|
+
|
|
172
|
+
// Check if we should watch this file
|
|
173
|
+
if (!WATCH_EXTENSIONS.has(ext)) return;
|
|
174
|
+
|
|
175
|
+
// Skip if in ignored directory
|
|
176
|
+
const parts = relativePath.split('/');
|
|
177
|
+
if (parts.some(part => SKIP_DIRS.has(part))) return;
|
|
178
|
+
|
|
179
|
+
this.log(`File ${eventType}: ${relativePath}`);
|
|
180
|
+
|
|
181
|
+
// Add to pending changes
|
|
182
|
+
this.pendingChanges.set(relativePath, {
|
|
183
|
+
type: eventType,
|
|
184
|
+
path: relativePath,
|
|
185
|
+
fullPath: filePath,
|
|
186
|
+
timestamp: Date.now()
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
this.stats.filesChanged++;
|
|
190
|
+
this.emit('change', { type: eventType, path: relativePath });
|
|
191
|
+
|
|
192
|
+
// Debounce the index update
|
|
193
|
+
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
194
|
+
this.debounceTimer = setTimeout(() => {
|
|
195
|
+
this.processPendingChanges();
|
|
196
|
+
}, this.options.debounceMs);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Process all pending changes
|
|
201
|
+
*/
|
|
202
|
+
async processPendingChanges() {
|
|
203
|
+
if (this.isIndexing || this.pendingChanges.size === 0) return;
|
|
204
|
+
|
|
205
|
+
const changes = Array.from(this.pendingChanges.values());
|
|
206
|
+
this.pendingChanges.clear();
|
|
207
|
+
|
|
208
|
+
if (this.options.autoRebuild) {
|
|
209
|
+
await this.updateIndex(changes);
|
|
210
|
+
} else {
|
|
211
|
+
this.emit('changes-pending', { changes });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Update the index with changes
|
|
217
|
+
*/
|
|
218
|
+
async updateIndex(changes) {
|
|
219
|
+
if (this.isIndexing) return;
|
|
220
|
+
|
|
221
|
+
this.isIndexing = true;
|
|
222
|
+
this.emit('indexing', { type: 'incremental', changes });
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
// Normalize changes to the format expected by updateIndexIncremental
|
|
226
|
+
const normalizedChanges = changes.map(change => ({
|
|
227
|
+
path: change.path,
|
|
228
|
+
fullPath: change.fullPath,
|
|
229
|
+
type: change.type === 'rename' ? 'add' : change.type // rename could be add or delete
|
|
230
|
+
}));
|
|
231
|
+
|
|
232
|
+
// Use incremental indexing for better performance
|
|
233
|
+
await updateIndexIncremental(normalizedChanges, this.cwd, {
|
|
234
|
+
onProgress: (progress) => this.emit('progress', progress)
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
this.stats.indexUpdates++;
|
|
238
|
+
this.stats.lastUpdate = new Date().toISOString();
|
|
239
|
+
|
|
240
|
+
this.emit('indexed', {
|
|
241
|
+
type: 'incremental',
|
|
242
|
+
changesProcessed: changes.length,
|
|
243
|
+
stats: this.stats
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
this.log(`Index updated (${changes.length} files changed)`);
|
|
247
|
+
} catch (err) {
|
|
248
|
+
this.log(`Index update failed: ${err.message}`);
|
|
249
|
+
this.emit('error', err);
|
|
250
|
+
} finally {
|
|
251
|
+
this.isIndexing = false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Force a full index rebuild
|
|
257
|
+
*/
|
|
258
|
+
async forceRebuild() {
|
|
259
|
+
this.pendingChanges.clear();
|
|
260
|
+
this.isIndexing = true;
|
|
261
|
+
this.emit('indexing', { type: 'full' });
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
await buildIndex(this.cwd, {
|
|
265
|
+
onProgress: (progress) => this.emit('progress', progress)
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
this.stats.indexUpdates++;
|
|
269
|
+
this.stats.lastUpdate = new Date().toISOString();
|
|
270
|
+
|
|
271
|
+
this.emit('indexed', { type: 'full', stats: this.stats });
|
|
272
|
+
this.log('Full index rebuild complete');
|
|
273
|
+
} catch (err) {
|
|
274
|
+
this.emit('error', err);
|
|
275
|
+
throw err;
|
|
276
|
+
} finally {
|
|
277
|
+
this.isIndexing = false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get watcher status
|
|
283
|
+
*/
|
|
284
|
+
getStatus() {
|
|
285
|
+
return {
|
|
286
|
+
running: this.isRunning,
|
|
287
|
+
indexing: this.isIndexing,
|
|
288
|
+
watchedDirectories: this.watchers.size,
|
|
289
|
+
pendingChanges: this.pendingChanges.size,
|
|
290
|
+
stats: this.stats
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Log message if verbose mode
|
|
296
|
+
*/
|
|
297
|
+
log(message) {
|
|
298
|
+
if (this.options.verbose) {
|
|
299
|
+
console.log(`[IndexWatcher] ${message}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Create and start a watcher daemon
|
|
306
|
+
*/
|
|
307
|
+
export async function startWatcherDaemon(cwd = process.cwd(), options = {}) {
|
|
308
|
+
const watcher = new IndexWatcher(cwd, options);
|
|
309
|
+
|
|
310
|
+
// Set up event handlers for CLI output
|
|
311
|
+
if (options.verbose) {
|
|
312
|
+
watcher.on('started', () => console.log('File watcher started'));
|
|
313
|
+
watcher.on('stopped', () => console.log('File watcher stopped'));
|
|
314
|
+
watcher.on('change', ({ type, path }) => console.log(` ${type}: ${path}`));
|
|
315
|
+
watcher.on('indexing', ({ type }) => console.log(`Indexing (${type})...`));
|
|
316
|
+
watcher.on('indexed', ({ type, changesProcessed }) => {
|
|
317
|
+
console.log(`Index updated (${type}${changesProcessed ? `, ${changesProcessed} files` : ''})`);
|
|
318
|
+
});
|
|
319
|
+
watcher.on('error', (err) => console.error(`Error: ${err.message}`));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
await watcher.start();
|
|
323
|
+
return watcher;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Watch status file for IPC with VS Code extension
|
|
328
|
+
*/
|
|
329
|
+
const STATUS_FILE = '.claude/watcher-status.json';
|
|
330
|
+
|
|
331
|
+
export async function writeWatcherStatus(cwd, status) {
|
|
332
|
+
const statusPath = path.join(cwd, STATUS_FILE);
|
|
333
|
+
await fs.ensureDir(path.dirname(statusPath));
|
|
334
|
+
await fs.writeJson(statusPath, {
|
|
335
|
+
...status,
|
|
336
|
+
pid: process.pid,
|
|
337
|
+
updatedAt: new Date().toISOString()
|
|
338
|
+
}, { spaces: 2 });
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export async function readWatcherStatus(cwd) {
|
|
342
|
+
const statusPath = path.join(cwd, STATUS_FILE);
|
|
343
|
+
if (await fs.pathExists(statusPath)) {
|
|
344
|
+
return await fs.readJson(statusPath);
|
|
345
|
+
}
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export async function clearWatcherStatus(cwd) {
|
|
350
|
+
const statusPath = path.join(cwd, STATUS_FILE);
|
|
351
|
+
if (await fs.pathExists(statusPath)) {
|
|
352
|
+
await fs.remove(statusPath);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export default IndexWatcher;
|