@iworkforces/tracelattice 2.0.8 → 2.1.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/README.md +89 -64
- package/dist/ServerConfig.d.ts +1 -1
- package/dist/ServerConfig.js.map +1 -1
- package/dist/cli.js +18 -73
- package/dist/cli.js.map +1 -1
- package/dist/config/ConfigLoader.d.ts +29 -26
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +40 -36
- package/dist/config/ConfigLoader.js.map +1 -1
- package/dist/contracts/PersistenceBackend.d.ts +7 -108
- package/dist/contracts/PersistenceBackend.d.ts.map +1 -1
- package/dist/contracts/PersistenceBackend.js +1 -6
- package/dist/contracts/calibrator.d.ts +6 -6
- package/dist/contracts/calibrator.d.ts.map +1 -1
- package/dist/contracts/ids.d.ts +1 -8
- package/dist/contracts/ids.d.ts.map +1 -1
- package/dist/contracts/ids.js +2 -3
- package/dist/contracts/ids.js.map +1 -1
- package/dist/contracts/interfaces.d.ts +4 -6
- package/dist/contracts/interfaces.d.ts.map +1 -1
- package/dist/contracts/persistence-work.d.ts +2 -3
- package/dist/contracts/persistence-work.d.ts.map +1 -1
- package/dist/core/HistoryManager.d.ts +12 -16
- package/dist/core/HistoryManager.d.ts.map +1 -1
- package/dist/core/HistoryManager.js +19 -40
- package/dist/core/HistoryManager.js.map +1 -1
- package/dist/core/IHistoryManager.d.ts +22 -30
- package/dist/core/IHistoryManager.d.ts.map +1 -1
- package/dist/core/InputNormalizer.d.ts +7 -24
- package/dist/core/InputNormalizer.d.ts.map +1 -1
- package/dist/core/InputNormalizer.js +3 -9
- package/dist/core/InputNormalizer.js.map +1 -1
- package/dist/core/PersistenceBuffer.d.ts +3 -20
- package/dist/core/PersistenceBuffer.d.ts.map +1 -1
- package/dist/core/PersistenceBuffer.js +1 -4
- package/dist/core/PersistenceBuffer.js.map +1 -1
- package/dist/core/PersistenceRestore.d.ts.map +1 -1
- package/dist/core/PersistenceRestore.js +4 -10
- package/dist/core/PersistenceRestore.js.map +1 -1
- package/dist/core/PersistenceWriter.d.ts.map +1 -1
- package/dist/core/PersistenceWriter.js +3 -12
- package/dist/core/PersistenceWriter.js.map +1 -1
- package/dist/core/SessionErrors.d.ts +0 -11
- package/dist/core/SessionErrors.d.ts.map +1 -1
- package/dist/core/SessionErrors.js +1 -14
- package/dist/core/SessionErrors.js.map +1 -1
- package/dist/core/SessionLock.d.ts +4 -5
- package/dist/core/SessionLock.d.ts.map +1 -1
- package/dist/core/SessionLock.js +5 -10
- package/dist/core/SessionLock.js.map +1 -1
- package/dist/core/SessionManager.d.ts +0 -2
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +3 -5
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/SessionResetCoordinator.d.ts +10 -7
- package/dist/core/SessionResetCoordinator.d.ts.map +1 -1
- package/dist/core/SessionResetCoordinator.js +10 -28
- package/dist/core/SessionResetCoordinator.js.map +1 -1
- package/dist/core/ThoughtEvaluator.d.ts +5 -5
- package/dist/core/ThoughtEvaluator.d.ts.map +1 -1
- package/dist/core/ThoughtEvaluator.js +4 -40
- package/dist/core/ThoughtEvaluator.js.map +1 -1
- package/dist/core/ThoughtProcessor.d.ts +10 -3
- package/dist/core/ThoughtProcessor.d.ts.map +1 -1
- package/dist/core/ThoughtProcessor.js +34 -38
- package/dist/core/ThoughtProcessor.js.map +1 -1
- package/dist/core/VerificationOutcomeAdmission.d.ts.map +1 -1
- package/dist/core/VerificationOutcomeAdmission.js +5 -7
- package/dist/core/VerificationOutcomeAdmission.js.map +1 -1
- package/dist/core/evaluator/Calibrator.d.ts +1 -1
- package/dist/core/evaluator/Calibrator.d.ts.map +1 -1
- package/dist/core/evaluator/Calibrator.js +2 -5
- package/dist/core/evaluator/Calibrator.js.map +1 -1
- package/dist/core/evaluator/internals.d.ts +0 -4
- package/dist/core/evaluator/internals.d.ts.map +1 -1
- package/dist/core/evaluator/internals.js +1 -3
- package/dist/core/evaluator/internals.js.map +1 -1
- package/dist/core/graph/Edge.d.ts +1 -1
- package/dist/core/graph/EdgeEmitter.d.ts +2 -4
- package/dist/core/graph/EdgeEmitter.d.ts.map +1 -1
- package/dist/core/graph/EdgeEmitter.js +2 -4
- package/dist/core/graph/EdgeEmitter.js.map +1 -1
- package/dist/core/reasoning/OutcomeRecorder.d.ts +0 -1
- package/dist/core/reasoning/OutcomeRecorder.d.ts.map +1 -1
- package/dist/core/reasoning/OutcomeRecorder.js.map +1 -1
- package/dist/core/reasoning/strategies/SequentialStrategy.d.ts +3 -3
- package/dist/core/reasoning/strategies/SequentialStrategy.js.map +1 -1
- package/dist/core/thought.d.ts +2 -1
- package/dist/core/thought.d.ts.map +1 -1
- package/dist/errors.d.ts +4 -20
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -28
- package/dist/errors.js.map +1 -1
- package/dist/lib.d.ts +13 -20
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +4 -7
- package/dist/lib.js.map +1 -1
- package/dist/logger/NullLogger.d.ts +9 -11
- package/dist/logger/NullLogger.d.ts.map +1 -1
- package/dist/logger/NullLogger.js.map +1 -1
- package/dist/persistence/FilePersistence.d.ts +3 -12
- package/dist/persistence/FilePersistence.d.ts.map +1 -1
- package/dist/persistence/FilePersistence.js +9 -61
- package/dist/persistence/FilePersistence.js.map +1 -1
- package/dist/persistence/FileSnapshotV2.d.ts +2 -2
- package/dist/persistence/FileSnapshotV2.js +2 -2
- package/dist/persistence/FileSnapshotV2.js.map +1 -1
- package/dist/persistence/FileWriter.d.ts +1 -0
- package/dist/persistence/FileWriter.d.ts.map +1 -1
- package/dist/persistence/FileWriter.js +3 -3
- package/dist/persistence/FileWriter.js.map +1 -1
- package/dist/persistence/MemoryPersistence.d.ts +5 -15
- package/dist/persistence/MemoryPersistence.d.ts.map +1 -1
- package/dist/persistence/MemoryPersistence.js +6 -40
- package/dist/persistence/MemoryPersistence.js.map +1 -1
- package/dist/persistence/PersistenceCodec.js +1 -3
- package/dist/persistence/PersistenceCodec.js.map +1 -1
- package/dist/persistence/PersistenceErrors.d.ts +0 -6
- package/dist/persistence/PersistenceErrors.d.ts.map +1 -1
- package/dist/persistence/PersistenceErrors.js +1 -9
- package/dist/persistence/PersistenceErrors.js.map +1 -1
- package/dist/persistence/PersistenceScope.d.ts.map +1 -1
- package/dist/persistence/PersistenceScope.js +2 -2
- package/dist/persistence/PersistenceScope.js.map +1 -1
- package/dist/persistence/SqlitePersistence.d.ts +3 -10
- package/dist/persistence/SqlitePersistence.d.ts.map +1 -1
- package/dist/persistence/SqlitePersistence.js +2 -26
- package/dist/persistence/SqlitePersistence.js.map +1 -1
- package/dist/persistence/SqliteSchemaV2.d.ts.map +1 -1
- package/dist/persistence/SqliteSchemaV2.js +3 -12
- package/dist/persistence/SqliteSchemaV2.js.map +1 -1
- package/dist/pool/ConnectionPool.d.ts +3 -12
- package/dist/pool/ConnectionPool.d.ts.map +1 -1
- package/dist/pool/ConnectionPool.js +5 -8
- package/dist/pool/ConnectionPool.js.map +1 -1
- package/dist/pool/IConnectionPool.d.ts +3 -9
- package/dist/pool/IConnectionPool.d.ts.map +1 -1
- package/dist/registry/BaseRegistry.d.ts +10 -2
- package/dist/registry/BaseRegistry.d.ts.map +1 -1
- package/dist/registry/BaseRegistry.js +128 -81
- package/dist/registry/BaseRegistry.js.map +1 -1
- package/dist/registry/SkillRegistry.d.ts +1 -7
- package/dist/registry/SkillRegistry.d.ts.map +1 -1
- package/dist/registry/SkillRegistry.js +0 -15
- package/dist/registry/SkillRegistry.js.map +1 -1
- package/dist/registry/ToolRegistry.d.ts +1 -9
- package/dist/registry/ToolRegistry.d.ts.map +1 -1
- package/dist/registry/ToolRegistry.js +0 -21
- package/dist/registry/ToolRegistry.js.map +1 -1
- package/dist/schema.d.ts +2 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +3 -3
- package/dist/schema.js.map +1 -1
- package/dist/transport/BaseTransport.d.ts +0 -9
- package/dist/transport/BaseTransport.d.ts.map +1 -1
- package/dist/transport/BaseTransport.js +0 -11
- package/dist/transport/BaseTransport.js.map +1 -1
- package/dist/watchers/SkillWatcher.d.ts.map +1 -1
- package/dist/watchers/SkillWatcher.js +1 -1
- package/dist/watchers/SkillWatcher.js.map +1 -1
- package/package.json +1 -1
- package/dist/contracts/PersistenceBackend.js.map +0 -1
- package/dist/persistence/FileLegacyImport.d.ts +0 -2
- package/dist/persistence/FileLegacyImport.d.ts.map +0 -1
- package/dist/persistence/FileLegacyImport.js +0 -183
- package/dist/persistence/FileLegacyImport.js.map +0 -1
- package/dist/persistence/SessionScopedPersistence.d.ts +0 -4
- package/dist/persistence/SessionScopedPersistence.d.ts.map +0 -1
- package/dist/persistence/SessionScopedPersistence.js +0 -9
- package/dist/persistence/SessionScopedPersistence.js.map +0 -1
- package/dist/persistence/SqliteLegacyImport.d.ts +0 -14
- package/dist/persistence/SqliteLegacyImport.d.ts.map +0 -1
- package/dist/persistence/SqliteLegacyImport.js +0 -136
- package/dist/persistence/SqliteLegacyImport.js.map +0 -1
- package/dist/persistence/SqliteLegacySchema.d.ts +0 -15
- package/dist/persistence/SqliteLegacySchema.d.ts.map +0 -1
- package/dist/persistence/SqliteLegacySchema.js +0 -72
- package/dist/persistence/SqliteLegacySchema.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/BaseRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAG5D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,kCAAkC;IAClC,KAAK,CAAC,EAAE,cAAc,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEzC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"BaseRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/BaseRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,7 +14,10 @@ class BaseRegistry {
|
|
|
14
14
|
_searchDirs;
|
|
15
15
|
_discovered = false;
|
|
16
16
|
_discoveryPromise = null;
|
|
17
|
-
|
|
17
|
+
_activeDiscoveryEpoch = null;
|
|
18
|
+
_queuedRefreshEpoch = null;
|
|
19
|
+
_replacementEpoch = 0;
|
|
20
|
+
_discoveredCount = 0;
|
|
18
21
|
constructor(options){
|
|
19
22
|
this._items = new Map();
|
|
20
23
|
this._manualItems = new Map();
|
|
@@ -41,9 +44,11 @@ class BaseRegistry {
|
|
|
41
44
|
}
|
|
42
45
|
remove(name) {
|
|
43
46
|
if (!this._items.has(name)) throw this._createNotFoundError(name, 'remove');
|
|
44
|
-
this.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
const removedManualItem = this._manualItems.delete(name);
|
|
48
|
+
if (!removedManualItem) {
|
|
49
|
+
for (const [filePath, item] of this._discoveredItemsByPath)if (item.name === name) this._discoveredItemsByPath.delete(filePath);
|
|
50
|
+
}
|
|
51
|
+
this._rebuildVisibleItems();
|
|
47
52
|
this.log(`Removed ${this._entityName}: ${name}`, {
|
|
48
53
|
[`${this._entityName}Name`]: name
|
|
49
54
|
});
|
|
@@ -52,14 +57,16 @@ class BaseRegistry {
|
|
|
52
57
|
}
|
|
53
58
|
update(name, updates) {
|
|
54
59
|
if (!this._items.has(name)) throw this._createNotFoundError(name, 'update');
|
|
60
|
+
const isManualItem = this._manualItems.has(name);
|
|
55
61
|
const existing = this._items.get(name);
|
|
56
62
|
const updated = {
|
|
57
63
|
...existing,
|
|
58
|
-
...updates
|
|
64
|
+
...updates,
|
|
65
|
+
name
|
|
59
66
|
};
|
|
60
67
|
this._items.set(name, updated);
|
|
61
|
-
if (
|
|
62
|
-
for (const [filePath, item] of this._discoveredItemsByPath)if (item.name === name) this._discoveredItemsByPath.set(filePath, updated);
|
|
68
|
+
if (isManualItem) this._manualItems.set(name, updated);
|
|
69
|
+
else for (const [filePath, item] of this._discoveredItemsByPath)if (item.name === name) this._discoveredItemsByPath.set(filePath, updated);
|
|
63
70
|
this.log(`Updated ${this._entityName}: ${name}`, {
|
|
64
71
|
[`${this._entityName}Name`]: name
|
|
65
72
|
});
|
|
@@ -85,110 +92,150 @@ class BaseRegistry {
|
|
|
85
92
|
return Array.from(this._items.keys());
|
|
86
93
|
}
|
|
87
94
|
clear() {
|
|
88
|
-
this.
|
|
89
|
-
this._manualItems.clear();
|
|
90
|
-
this._discoveredItemsByPath.clear();
|
|
95
|
+
this._beginReplacement();
|
|
91
96
|
this.log(`Cleared all ${this._entityName}s`);
|
|
92
|
-
this._cache?.clear();
|
|
93
97
|
}
|
|
94
98
|
size() {
|
|
95
99
|
return this._items.size;
|
|
96
100
|
}
|
|
97
101
|
async discoverAsync() {
|
|
98
|
-
if (this._discoveryPromise)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return cached?.length ?? 0;
|
|
102
|
+
if (this._discoveryPromise) {
|
|
103
|
+
if (!this._discovered && this._activeDiscoveryEpoch !== this._replacementEpoch) this._queuedRefreshEpoch = this._replacementEpoch;
|
|
104
|
+
return this._discoveryPromise;
|
|
102
105
|
}
|
|
106
|
+
if (this._discovered) return this._discoveredCount;
|
|
103
107
|
return this._beginDiscovery();
|
|
104
108
|
}
|
|
105
109
|
async refreshAsync() {
|
|
106
|
-
this.
|
|
107
|
-
|
|
110
|
+
if (this._discoveryPromise) {
|
|
111
|
+
this._queuedRefreshEpoch = this._replacementEpoch;
|
|
112
|
+
return this._discoveryPromise;
|
|
113
|
+
}
|
|
114
|
+
return this._beginDiscovery();
|
|
108
115
|
}
|
|
109
116
|
_beginDiscovery() {
|
|
110
117
|
const operation = this._runDiscoveryLoop();
|
|
111
118
|
this._discoveryPromise = operation;
|
|
112
|
-
const clearOperation = ()=>{
|
|
113
|
-
if (this._discoveryPromise === operation) this._discoveryPromise = null;
|
|
114
|
-
};
|
|
115
|
-
operation.then(clearOperation, clearOperation);
|
|
116
119
|
return operation;
|
|
117
120
|
}
|
|
118
121
|
async _runDiscoveryLoop() {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
let passEpoch = this._replacementEpoch;
|
|
123
|
+
try {
|
|
124
|
+
while(true){
|
|
125
|
+
this._activeDiscoveryEpoch = passEpoch;
|
|
126
|
+
let count;
|
|
127
|
+
try {
|
|
128
|
+
count = await this._performDiscovery(passEpoch);
|
|
129
|
+
} finally{
|
|
130
|
+
this._activeDiscoveryEpoch = null;
|
|
131
|
+
}
|
|
132
|
+
const queuedEpoch = this._queuedRefreshEpoch;
|
|
133
|
+
this._queuedRefreshEpoch = null;
|
|
134
|
+
if (null === queuedEpoch) return count;
|
|
135
|
+
passEpoch = queuedEpoch;
|
|
136
|
+
}
|
|
137
|
+
} finally{
|
|
138
|
+
this._discoveryPromise = null;
|
|
124
139
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
140
|
+
}
|
|
141
|
+
async _performDiscovery(epoch = this._replacementEpoch) {
|
|
142
|
+
const scan = {
|
|
143
|
+
itemsByPath: new Map(),
|
|
144
|
+
claimedNames: new Set(),
|
|
145
|
+
previousItemsByPath: new Map(this._discoveredItemsByPath),
|
|
146
|
+
warnings: []
|
|
147
|
+
};
|
|
148
|
+
for (const dir of this._searchDirs)await this._scanDirectory(dir, scan);
|
|
149
|
+
return this._publishDiscovery(epoch, scan);
|
|
150
|
+
}
|
|
151
|
+
async _scanDirectory(directory, scan) {
|
|
152
|
+
try {
|
|
153
|
+
if (!existsSync(directory)) return;
|
|
154
|
+
const entries = await readdir(directory, {
|
|
133
155
|
withFileTypes: true
|
|
134
156
|
});
|
|
135
|
-
for (const entry of entries)if (!this._shouldSkipFile(entry.name)) {
|
|
136
|
-
if (
|
|
137
|
-
const filePath = join(dir, entry.name);
|
|
138
|
-
try {
|
|
139
|
-
const content = await readFile(filePath, 'utf-8');
|
|
140
|
-
const parsed = this._parseFrontmatter(content);
|
|
141
|
-
if (parsed._error) {
|
|
142
|
-
this._retainLastKnownGood(filePath, parsed._error, discoveredItems, claimedNames);
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
if (parsed.name) {
|
|
146
|
-
const item = this._buildItem(parsed);
|
|
147
|
-
if (item && !claimedNames.has(item.name)) {
|
|
148
|
-
discoveredItems.set(filePath, item);
|
|
149
|
-
claimedNames.add(item.name);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
} catch (readError) {
|
|
153
|
-
this._retainLastKnownGood(filePath, getErrorMessage(readError), discoveredItems, claimedNames);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
157
|
+
for (const entry of entries)if (!this._shouldSkipFile(entry.name) && entry.isFile()) {
|
|
158
|
+
if (this._fileExtensions.some((extension)=>entry.name.endsWith(extension))) await this._scanFile(join(directory, entry.name), scan);
|
|
156
159
|
}
|
|
157
160
|
} catch (error) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
+
scan.warnings.push({
|
|
162
|
+
message: `Failed to scan ${this._entityName} directory`,
|
|
163
|
+
meta: {
|
|
164
|
+
directory,
|
|
165
|
+
error: getErrorMessage(error)
|
|
166
|
+
}
|
|
161
167
|
});
|
|
162
|
-
this._retainDirectory(
|
|
168
|
+
this._retainDirectory(directory, scan);
|
|
163
169
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
170
|
+
}
|
|
171
|
+
async _scanFile(filePath, scan) {
|
|
172
|
+
try {
|
|
173
|
+
const content = await readFile(filePath, 'utf-8');
|
|
174
|
+
const parsed = this._parseFrontmatter(content);
|
|
175
|
+
if (parsed._error) return void this._retainLastKnownGood(filePath, parsed._error, scan);
|
|
176
|
+
if (!parsed.name) return;
|
|
177
|
+
const item = this._buildItem(parsed);
|
|
178
|
+
if (item && !scan.claimedNames.has(item.name)) {
|
|
179
|
+
scan.itemsByPath.set(filePath, item);
|
|
180
|
+
scan.claimedNames.add(item.name);
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
this._retainLastKnownGood(filePath, getErrorMessage(error), scan);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
_retainLastKnownGood(filePath, reason, scan) {
|
|
187
|
+
const previous = scan.previousItemsByPath.get(filePath);
|
|
188
|
+
if (previous && !scan.claimedNames.has(previous.name)) {
|
|
189
|
+
scan.itemsByPath.set(filePath, previous);
|
|
190
|
+
scan.claimedNames.add(previous.name);
|
|
191
|
+
}
|
|
192
|
+
scan.warnings.push({
|
|
193
|
+
message: `Invalid ${this._entityName} discovery file`,
|
|
194
|
+
meta: {
|
|
195
|
+
filePath,
|
|
196
|
+
reason,
|
|
197
|
+
retainedLastKnownGood: void 0 !== previous
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
_retainDirectory(directory, scan) {
|
|
202
|
+
for (const [filePath, item] of scan.previousItemsByPath)if (dirname(filePath) === directory && !scan.claimedNames.has(item.name)) {
|
|
203
|
+
scan.itemsByPath.set(filePath, item);
|
|
204
|
+
scan.claimedNames.add(item.name);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
_publishDiscovery(epoch, scan) {
|
|
208
|
+
if (epoch !== this._replacementEpoch) return this._discoveredCount;
|
|
209
|
+
this._discoveredItemsByPath = scan.itemsByPath;
|
|
210
|
+
this._rebuildVisibleItems();
|
|
167
211
|
this._discovered = true;
|
|
168
212
|
this._cache.clear();
|
|
169
213
|
this._cache.set('all', Array.from(this._items.values()));
|
|
170
|
-
|
|
171
|
-
|
|
214
|
+
for (const warning of scan.warnings)this._logger.warn(warning.message, warning.meta);
|
|
215
|
+
this.log(`Discovery complete: found ${this._discoveredCount} ${this._entityName}s`, {
|
|
216
|
+
discoveredCount: this._discoveredCount
|
|
172
217
|
});
|
|
173
|
-
return
|
|
218
|
+
return this._discoveredCount;
|
|
174
219
|
}
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
220
|
+
_rebuildVisibleItems() {
|
|
221
|
+
const visibleItems = new Map(this._manualItems);
|
|
222
|
+
let visibleDiscoveredCount = 0;
|
|
223
|
+
for (const item of this._discoveredItemsByPath.values())if (!visibleItems.has(item.name)) {
|
|
224
|
+
visibleItems.set(item.name, item);
|
|
225
|
+
visibleDiscoveredCount++;
|
|
180
226
|
}
|
|
181
|
-
this.
|
|
182
|
-
|
|
183
|
-
reason,
|
|
184
|
-
retainedLastKnownGood: void 0 !== previous
|
|
185
|
-
});
|
|
227
|
+
this._items = visibleItems;
|
|
228
|
+
this._discoveredCount = visibleDiscoveredCount;
|
|
186
229
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
230
|
+
_beginReplacement() {
|
|
231
|
+
this._replacementEpoch++;
|
|
232
|
+
this._queuedRefreshEpoch = null;
|
|
233
|
+
this._items.clear();
|
|
234
|
+
this._manualItems.clear();
|
|
235
|
+
this._discoveredItemsByPath.clear();
|
|
236
|
+
this._discovered = false;
|
|
237
|
+
this._discoveredCount = 0;
|
|
238
|
+
this._cache.clear();
|
|
192
239
|
}
|
|
193
240
|
_extractFrontmatter(content) {
|
|
194
241
|
const match = content.match(/^---\n([\s\S]+?)\n---/);
|
|
@@ -196,7 +243,7 @@ class BaseRegistry {
|
|
|
196
243
|
return parse(match[1]);
|
|
197
244
|
}
|
|
198
245
|
setAll(items) {
|
|
199
|
-
this.
|
|
246
|
+
this._beginReplacement();
|
|
200
247
|
for (const item of items)try {
|
|
201
248
|
this.add(item);
|
|
202
249
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry/BaseRegistry.js","sources":["../../src/registry/BaseRegistry.ts"],"sourcesContent":["/**\n * Base registry providing shared CRUD, caching, and discovery logic.\n *\n * This abstract generic class extracts the common patterns from `ToolRegistry`\n * and `SkillRegistry` into a single reusable base. Subclasses only need to\n * implement item-specific parsing, file filtering, and error construction.\n *\n * @template T - The registry item type (must have a `name` property)\n * @module registry\n */\n\nimport { existsSync } from 'node:fs';\nimport { readdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { DiscoveryCache } from '../cache/DiscoveryCache.js';\nimport { NullLogger } from '../logger/NullLogger.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport { getErrorMessage } from '../errors.js';\n\n/**\n * Configuration options for creating a `BaseRegistry` instance.\n */\nexport interface BaseRegistryOptions {\n\t/** Optional logger for diagnostics. */\n\tlogger?: Logger;\n\n\t/** Optional cache for lookups. */\n\tcache?: DiscoveryCache<{ name: string }>;\n\n\t/**\n\t * Directory paths to search for items.\n\t */\n\tsearchDirs?: string[];\n\n\t/**\n\t * Enable lazy discovery (discover on first access instead of startup).\n\t * @default false\n\t */\n\tlazyDiscovery?: boolean;\n}\n\n/**\n * Abstract base registry for managing named items with discovery and caching.\n *\n * Provides shared CRUD operations, filesystem discovery with deduplication,\n * and optional LRU caching. Subclasses implement item-specific parsing logic\n * and error construction.\n *\n * @template T - The registry item type (must have a `name` property)\n */\nexport abstract class BaseRegistry<T extends { name: string }> {\n\t/** Internal storage for items indexed by name. */\n\tprotected _items: Map<string, T>;\n\tprivate _manualItems: Map<string, T>;\n\tprivate _discoveredItemsByPath: Map<string, T>;\n\n\t/** Logger for diagnostics. */\n\tprotected _logger: Logger;\n\n\t/** Optional cache for lookups. */\n\tprotected _cache: DiscoveryCache<T>;\n\n\t/** Directory paths to search for items. */\n\tprotected _searchDirs: string[];\n\n\t/** Whether discovery has been performed. */\n\tprotected _discovered: boolean = false;\n\n\t/** Promise for in-progress discovery (null if not in progress). */\n\tprotected _discoveryPromise: Promise<number> | null = null;\n\tprivate _refreshRequested = false;\n\n\t/** File extensions to match during discovery. */\n\tprotected abstract readonly _fileExtensions: string[];\n\n\t/**\n\t * Creates an error for invalid item data.\n\t * @param reason - The reason for the validation failure\n\t */\n\tprotected abstract _createInvalidError(reason: string): Error;\n\n\t/**\n\t * Creates an error for duplicate items.\n\t * @param name - The name of the duplicate item\n\t */\n\tprotected abstract _createDuplicateError(name: string): Error;\n\n\t/**\n\t * Creates an error for items not found.\n\t * @param name - The name of the missing item\n\t * @param action - The action that was attempted\n\t */\n\tprotected abstract _createNotFoundError(name: string, action: string): Error;\n\n\t/**\n\t * Parses frontmatter content into a partial item.\n\t * @param content - The file content to parse\n\t * @returns A partial item, with an `_error` property if parsing failed\n\t */\n\tprotected abstract _parseFrontmatter(content: string): Partial<T> & { _error?: string };\n\n\t/**\n\t * Determines whether a file should be skipped during discovery.\n\t * @param fileName - The name of the file to check\n\t * @returns true if the file should be skipped\n\t */\n\tprotected abstract _shouldSkipFile(fileName: string): boolean;\n\n\t/**\n\t * Constructs a complete item from parsed frontmatter data.\n\t * Returns null if the parsed data is insufficient.\n\t * @param parsed - The parsed frontmatter data\n\t * @returns A complete item, or null if data is insufficient\n\t */\n\tprotected abstract _buildItem(parsed: Partial<T>): T | null;\n\n\t/**\n\t * The entity name used in log messages (e.g., 'tool', 'skill').\n\t */\n\tprotected abstract readonly _entityName: string;\n\n\tconstructor(options: BaseRegistryOptions) {\n\t\tthis._items = new Map();\n\t\tthis._manualItems = new Map();\n\t\tthis._discoveredItemsByPath = new Map();\n\t\tthis._logger = (options.logger ?? new NullLogger()) as Logger;\n\t\tthis._cache = (options.cache ||\n\t\t\tnew DiscoveryCache<T>({ maxSize: 50, ttl: 300000 })) as DiscoveryCache<T>;\n\t\tthis._searchDirs = (options.searchDirs ?? []) as string[];\n\t}\n\n\t/**\n\t * Internal logging method.\n\t * @param message - The message to log\n\t * @param meta - Optional metadata\n\t */\n\tprotected log(message: string, meta?: Record<string, unknown>): void {\n\t\tthis._logger.info(message, meta);\n\t}\n\n\t/**\n\t * Adds an item to the registry.\n\t *\n\t * @param item - The item to add\n\t * @throws If item already exists or name is invalid\n\t */\n\tpublic add(item: T): void {\n\t\tif (!item.name) {\n\t\t\tthrow this._createInvalidError(`${this._entityName} must have a valid name`);\n\t\t}\n\t\tif (this._items.has(item.name)) {\n\t\t\tthrow this._createDuplicateError(item.name);\n\t\t}\n\t\tthis._items.set(item.name, item);\n\t\tthis._manualItems.set(item.name, item);\n\t\tthis.log(`Added ${this._entityName}: ${item.name}`, { [`${this._entityName}Name`]: item.name });\n\t\t// Invalidate cache when adding a new item\n\t\tthis._cache?.invalidate('all');\n\t}\n\n\t/**\n\t * Removes an item from the registry.\n\t *\n\t * @param name - The name of the item to remove\n\t * @throws If item not found\n\t */\n\tpublic remove(name: string): void {\n\t\tif (!this._items.has(name)) {\n\t\t\tthrow this._createNotFoundError(name, 'remove');\n\t\t}\n\t\tthis._items.delete(name);\n\t\tthis._manualItems.delete(name);\n\t\tfor (const [filePath, item] of this._discoveredItemsByPath) {\n\t\t\tif (item.name === name) this._discoveredItemsByPath.delete(filePath);\n\t\t}\n\t\tthis.log(`Removed ${this._entityName}: ${name}`, { [`${this._entityName}Name`]: name });\n\t\t// Invalidate cache when removing an item\n\t\tthis._cache?.invalidate('all');\n\t\tthis._cache?.invalidate(name);\n\t}\n\n\t/**\n\t * Updates an existing item with partial data.\n\t *\n\t * @param name - The name of the item to update\n\t * @param updates - Partial item data with fields to update\n\t * @throws If item not found\n\t */\n\tpublic update(name: string, updates: Partial<T>): void {\n\t\tif (!this._items.has(name)) {\n\t\t\tthrow this._createNotFoundError(name, 'update');\n\t\t}\n\t\tconst existing = this._items.get(name)!;\n\t\tconst updated = { ...existing, ...updates };\n\t\tthis._items.set(name, updated);\n\t\tif (this._manualItems.has(name)) this._manualItems.set(name, updated);\n\t\tfor (const [filePath, item] of this._discoveredItemsByPath) {\n\t\t\tif (item.name === name) this._discoveredItemsByPath.set(filePath, updated);\n\t\t}\n\t\tthis.log(`Updated ${this._entityName}: ${name}`, { [`${this._entityName}Name`]: name });\n\t\t// Invalidate cache when updating an item\n\t\tthis._cache?.invalidate('all');\n\t\tthis._cache?.invalidate(name);\n\t}\n\n\t/**\n\t * Gets an item by name.\n\t *\n\t * @param name - The name of the item to get\n\t * @returns The item if found, undefined otherwise\n\t */\n\tpublic get(name: string): T | undefined {\n\t\treturn this._items.get(name);\n\t}\n\n\t/**\n\t * Gets all items as an array.\n\t *\n\t * Uses cache if available for performance.\n\t *\n\t * @returns An array of all registered items\n\t */\n\tpublic getAll(): T[] {\n\t\t// Check cache first\n\t\tif (this._cache) {\n\t\t\tconst cached = this._cache.get('all');\n\t\t\tif (cached) {\n\t\t\t\treturn cached;\n\t\t\t}\n\t\t}\n\t\t// Get from storage\n\t\tconst items = Array.from(this._items.values());\n\t\t// Cache the result\n\t\tthis._cache?.set('all', items);\n\t\treturn items;\n\t}\n\n\t/**\n\t * Checks if an item exists in the registry.\n\t *\n\t * @param name - The name of the item to check\n\t * @returns true if the item exists, false otherwise\n\t */\n\tpublic has(name: string): boolean {\n\t\treturn this._items.has(name);\n\t}\n\n\t/**\n\t * Gets all item names as an array.\n\t *\n\t * @returns An array of item names\n\t */\n\tpublic getNames(): string[] {\n\t\treturn Array.from(this._items.keys());\n\t}\n\n\t/**\n\t * Clears all items from the registry.\n\t */\n\tpublic clear(): void {\n\t\tthis._items.clear();\n\t\tthis._manualItems.clear();\n\t\tthis._discoveredItemsByPath.clear();\n\t\tthis.log(`Cleared all ${this._entityName}s`);\n\t\t// Invalidate cache when clearing all items\n\t\tthis._cache?.clear();\n\t}\n\n\t/**\n\t * Gets the number of items in the registry.\n\t *\n\t * @returns The count of registered items\n\t */\n\tpublic size(): number {\n\t\treturn this._items.size;\n\t}\n\n\t/**\n\t * Asynchronously discovers items from the configured directories.\n\t *\n\t * Multiple concurrent calls share the same discovery promise.\n\t * Subsequent calls return cached results if discovery has already completed.\n\t *\n\t * @returns A Promise resolving to the number of items discovered\n\t */\n\tpublic async discoverAsync(): Promise<number> {\n\t\tif (this._discoveryPromise) {\n\t\t\treturn this._discoveryPromise;\n\t\t}\n\n\t\tif (this._discovered) {\n\t\t\tconst cached = this._cache.get('all');\n\t\t\treturn cached?.length ?? 0;\n\t\t}\n\n\t\treturn this._beginDiscovery();\n\t}\n\n\t/**\n\t * Re-scans configured directories and atomically reconciles discovered items.\n\t *\n\t * Calls made during an active scan share its promise and request at most one\n\t * follow-up pass, ensuring an event that arrives mid-scan is not lost.\n\t *\n\t * @returns A Promise resolving to the number of discovered filesystem items\n\t */\n\tpublic async refreshAsync(): Promise<number> {\n\t\tthis._refreshRequested = true;\n\t\treturn this._discoveryPromise ?? this._beginDiscovery();\n\t}\n\n\tprivate _beginDiscovery(): Promise<number> {\n\t\tconst operation = this._runDiscoveryLoop();\n\t\tthis._discoveryPromise = operation;\n\t\tconst clearOperation = (): void => {\n\t\t\tif (this._discoveryPromise === operation) this._discoveryPromise = null;\n\t\t};\n\t\toperation.then(clearOperation, clearOperation);\n\t\treturn operation;\n\t}\n\n\tprivate async _runDiscoveryLoop(): Promise<number> {\n\t\tthis._refreshRequested = false;\n\t\tlet count = await this._performDiscovery();\n\t\twhile (this._refreshRequested) {\n\t\t\tthis._refreshRequested = false;\n\t\t\tcount = await this._performDiscovery();\n\t\t}\n\t\treturn count;\n\t}\n\n\t/**\n\t * Performs the actual discovery operation.\n\t *\n\t * Scans configured directories for item files, parses their frontmatter,\n\t * and adds valid items to the registry.\n\t *\n\t * @returns A Promise resolving to the number of items discovered\n\t */\n\tprotected async _performDiscovery(): Promise<number> {\n\t\tconst discoveredItems = new Map<string, T>();\n\t\tconst claimedNames = new Set(this._manualItems.keys());\n\n\t\tfor (const dir of this._searchDirs) {\n\t\t\ttry {\n\t\t\t\tif (!existsSync(dir)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst entries = await readdir(dir, { withFileTypes: true });\n\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\tif (this._shouldSkipFile(entry.name)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (entry.isFile() && this._fileExtensions.some((ext) => entry.name.endsWith(ext))) {\n\t\t\t\t\t\tconst filePath = join(dir, entry.name);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst content = await readFile(filePath, 'utf-8');\n\t\t\t\t\t\t\tconst parsed = this._parseFrontmatter(content);\n\t\t\t\t\t\t\tif (parsed._error) {\n\t\t\t\t\t\t\t\tthis._retainLastKnownGood(filePath, parsed._error, discoveredItems, claimedNames);\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (parsed.name) {\n\t\t\t\t\t\t\t\tconst item = this._buildItem(parsed);\n\t\t\t\t\t\t\t\tif (item && !claimedNames.has(item.name)) {\n\t\t\t\t\t\t\t\t\tdiscoveredItems.set(filePath, item);\n\t\t\t\t\t\t\t\t\tclaimedNames.add(item.name);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (readError) {\n\t\t\t\t\t\t\tthis._retainLastKnownGood(\n\t\t\t\t\t\t\t\tfilePath,\n\t\t\t\t\t\t\t\tgetErrorMessage(readError),\n\t\t\t\t\t\t\t\tdiscoveredItems,\n\t\t\t\t\t\t\t\tclaimedNames\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis._logger.warn(`Failed to scan ${this._entityName} directory`, {\n\t\t\t\t\tdirectory: dir,\n\t\t\t\t\terror: getErrorMessage(error),\n\t\t\t\t});\n\t\t\t\tthis._retainDirectory(dir, discoveredItems, claimedNames);\n\t\t\t}\n\t\t}\n\n\t\tthis._discoveredItemsByPath = discoveredItems;\n\t\tthis._items = new Map(this._manualItems);\n\t\tfor (const item of discoveredItems.values()) {\n\t\t\tif (!this._items.has(item.name)) this._items.set(item.name, item);\n\t\t}\n\t\tthis._discovered = true;\n\t\tthis._cache.clear();\n\t\tthis._cache.set('all', Array.from(this._items.values()));\n\t\tthis.log(`Discovery complete: found ${discoveredItems.size} ${this._entityName}s`, {\n\t\t\tdiscoveredCount: discoveredItems.size,\n\t\t});\n\t\treturn discoveredItems.size;\n\t}\n\n\tprivate _retainLastKnownGood(\n\t\tfilePath: string,\n\t\treason: string,\n\t\tdiscoveredItems: Map<string, T>,\n\t\tclaimedNames: Set<string>\n\t): void {\n\t\tconst previous = this._discoveredItemsByPath.get(filePath);\n\t\tif (previous && !claimedNames.has(previous.name)) {\n\t\t\tdiscoveredItems.set(filePath, previous);\n\t\t\tclaimedNames.add(previous.name);\n\t\t}\n\t\tthis._logger.warn(`Invalid ${this._entityName} discovery file`, {\n\t\t\tfilePath,\n\t\t\treason,\n\t\t\tretainedLastKnownGood: previous !== undefined,\n\t\t});\n\t}\n\n\tprivate _retainDirectory(\n\t\tdirectory: string,\n\t\tdiscoveredItems: Map<string, T>,\n\t\tclaimedNames: Set<string>\n\t): void {\n\t\tfor (const [filePath, item] of this._discoveredItemsByPath) {\n\t\t\tif (dirname(filePath) === directory && !claimedNames.has(item.name)) {\n\t\t\t\tdiscoveredItems.set(filePath, item);\n\t\t\t\tclaimedNames.add(item.name);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Parses YAML frontmatter from file content.\n\t *\n\t * This is a shared utility for subclasses that parse YAML frontmatter.\n\t *\n\t * @param content - The file content to parse\n\t * @returns The parsed YAML object, or null if no frontmatter found\n\t */\n\tprotected _extractFrontmatter(content: string): Record<string, unknown> | null {\n\t\tconst match = content.match(/^---\\n([\\s\\S]+?)\\n---/);\n\t\tif (!match) {\n\t\t\treturn null;\n\t\t}\n\t\treturn parseYaml(match[1]!) as Record<string, unknown>;\n\t}\n\n\t/**\n\t * Sets items from an external source.\n\t *\n\t * Clears existing items and adds new ones from the provided array.\n\t *\n\t * @param items - Array of items from an external source\n\t */\n\tpublic setAll(items: T[]): void {\n\t\tthis.clear();\n\t\tfor (const item of items) {\n\t\t\ttry {\n\t\t\t\tthis.add(item);\n\t\t\t} catch (error) {\n\t\t\t\tthis.log(`Error adding ${this._entityName} '${item.name}':`, {\n\t\t\t\t\t[`${this._entityName}Name`]: item.name,\n\t\t\t\t\terror: getErrorMessage(error),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis.log(`Set ${items.length} ${this._entityName}s from external source`, {\n\t\t\t[`${this._entityName}Count`]: items.length,\n\t\t});\n\t}\n}\n"],"names":["BaseRegistry","options","Map","NullLogger","DiscoveryCache","message","meta","item","name","filePath","updates","existing","updated","cached","items","Array","operation","clearOperation","count","discoveredItems","claimedNames","Set","dir","existsSync","entries","readdir","entry","ext","join","content","readFile","parsed","readError","getErrorMessage","error","reason","previous","undefined","directory","dirname","match","parseYaml"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"registry/BaseRegistry.js","sources":["../../src/registry/BaseRegistry.ts"],"sourcesContent":["/**\n * Base registry providing shared CRUD, caching, and discovery logic.\n *\n * This abstract generic class extracts the common patterns from `ToolRegistry`\n * and `SkillRegistry` into a single reusable base. Subclasses only need to\n * implement item-specific parsing, file filtering, and error construction.\n *\n * @template T - The registry item type (must have a `name` property)\n * @module registry\n */\n\nimport { existsSync } from 'node:fs';\nimport { readdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { DiscoveryCache } from '../cache/DiscoveryCache.js';\nimport { NullLogger } from '../logger/NullLogger.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport { getErrorMessage } from '../errors.js';\n\n/**\n * Configuration options for creating a `BaseRegistry` instance.\n */\nexport interface BaseRegistryOptions {\n\t/** Optional logger for diagnostics. */\n\tlogger?: Logger;\n\n\t/** Optional cache for lookups. */\n\tcache?: DiscoveryCache<{ name: string }>;\n\n\t/**\n\t * Directory paths to search for items.\n\t */\n\tsearchDirs?: string[];\n\n\t/**\n\t * Enable lazy discovery (discover on first access instead of startup).\n\t * @default false\n\t */\n\tlazyDiscovery?: boolean;\n}\n\ninterface DiscoveryWarning {\n\treadonly message: string;\n\treadonly meta: Record<string, unknown>;\n}\n\ninterface DiscoveryScan<T> {\n\treadonly itemsByPath: Map<string, T>;\n\treadonly claimedNames: Set<string>;\n\treadonly previousItemsByPath: ReadonlyMap<string, T>;\n\treadonly warnings: DiscoveryWarning[];\n}\n\n/**\n * Abstract base registry for managing named items with discovery and caching.\n *\n * Provides shared CRUD operations, filesystem discovery with deduplication,\n * and optional LRU caching. Subclasses implement item-specific parsing logic\n * and error construction.\n *\n * @template T - The registry item type (must have a `name` property)\n */\nexport abstract class BaseRegistry<T extends { name: string }> {\n\t/** Internal storage for items indexed by name. */\n\tprotected _items: Map<string, T>;\n\tprivate _manualItems: Map<string, T>;\n\tprivate _discoveredItemsByPath: Map<string, T>;\n\n\t/** Logger for diagnostics. */\n\tprotected _logger: Logger;\n\n\t/** Optional cache for lookups. */\n\tprotected _cache: DiscoveryCache<T>;\n\n\t/** Directory paths to search for items. */\n\tprotected _searchDirs: string[];\n\n\t/** Whether discovery has been performed. */\n\tprotected _discovered: boolean = false;\n\n\t/** Promise for in-progress discovery (null if not in progress). */\n\tprotected _discoveryPromise: Promise<number> | null = null;\n\tprivate _activeDiscoveryEpoch: number | null = null;\n\tprivate _queuedRefreshEpoch: number | null = null;\n\tprivate _replacementEpoch = 0;\n\tprivate _discoveredCount = 0;\n\n\t/** File extensions to match during discovery. */\n\tprotected abstract readonly _fileExtensions: string[];\n\n\t/**\n\t * Creates an error for invalid item data.\n\t * @param reason - The reason for the validation failure\n\t */\n\tprotected abstract _createInvalidError(reason: string): Error;\n\n\t/**\n\t * Creates an error for duplicate items.\n\t * @param name - The name of the duplicate item\n\t */\n\tprotected abstract _createDuplicateError(name: string): Error;\n\n\t/**\n\t * Creates an error for items not found.\n\t * @param name - The name of the missing item\n\t * @param action - The action that was attempted\n\t */\n\tprotected abstract _createNotFoundError(name: string, action: string): Error;\n\n\t/**\n\t * Parses frontmatter content into a partial item.\n\t * @param content - The file content to parse\n\t * @returns A partial item, with an `_error` property if parsing failed\n\t */\n\tprotected abstract _parseFrontmatter(content: string): Partial<T> & { _error?: string };\n\n\t/**\n\t * Determines whether a file should be skipped during discovery.\n\t * @param fileName - The name of the file to check\n\t * @returns true if the file should be skipped\n\t */\n\tprotected abstract _shouldSkipFile(fileName: string): boolean;\n\n\t/**\n\t * Constructs a complete item from parsed frontmatter data.\n\t * Returns null if the parsed data is insufficient.\n\t * @param parsed - The parsed frontmatter data\n\t * @returns A complete item, or null if data is insufficient\n\t */\n\tprotected abstract _buildItem(parsed: Partial<T>): T | null;\n\n\t/**\n\t * The entity name used in log messages (e.g., 'tool', 'skill').\n\t */\n\tprotected abstract readonly _entityName: string;\n\n\tconstructor(options: BaseRegistryOptions) {\n\t\tthis._items = new Map();\n\t\tthis._manualItems = new Map();\n\t\tthis._discoveredItemsByPath = new Map();\n\t\tthis._logger = (options.logger ?? new NullLogger()) as Logger;\n\t\tthis._cache = (options.cache ||\n\t\t\tnew DiscoveryCache<T>({ maxSize: 50, ttl: 300000 })) as DiscoveryCache<T>;\n\t\tthis._searchDirs = (options.searchDirs ?? []) as string[];\n\t}\n\n\t/**\n\t * Internal logging method.\n\t * @param message - The message to log\n\t * @param meta - Optional metadata\n\t */\n\tprotected log(message: string, meta?: Record<string, unknown>): void {\n\t\tthis._logger.info(message, meta);\n\t}\n\n\t/**\n\t * Adds an item to the registry.\n\t *\n\t * @param item - The item to add\n\t * @throws If item already exists or name is invalid\n\t */\n\tpublic add(item: T): void {\n\t\tif (!item.name) {\n\t\t\tthrow this._createInvalidError(`${this._entityName} must have a valid name`);\n\t\t}\n\t\tif (this._items.has(item.name)) {\n\t\t\tthrow this._createDuplicateError(item.name);\n\t\t}\n\t\tthis._items.set(item.name, item);\n\t\tthis._manualItems.set(item.name, item);\n\t\tthis.log(`Added ${this._entityName}: ${item.name}`, { [`${this._entityName}Name`]: item.name });\n\t\t// Invalidate cache when adding a new item\n\t\tthis._cache?.invalidate('all');\n\t}\n\n\t/**\n\t * Removes an item from the registry.\n\t *\n\t * @param name - The name of the item to remove\n\t * @throws If item not found\n\t */\n\tpublic remove(name: string): void {\n\t\tif (!this._items.has(name)) {\n\t\t\tthrow this._createNotFoundError(name, 'remove');\n\t\t}\n\t\tconst removedManualItem = this._manualItems.delete(name);\n\t\tif (!removedManualItem) {\n\t\t\tfor (const [filePath, item] of this._discoveredItemsByPath) {\n\t\t\t\tif (item.name === name) this._discoveredItemsByPath.delete(filePath);\n\t\t\t}\n\t\t}\n\t\tthis._rebuildVisibleItems();\n\t\tthis.log(`Removed ${this._entityName}: ${name}`, { [`${this._entityName}Name`]: name });\n\t\t// Invalidate cache when removing an item\n\t\tthis._cache?.invalidate('all');\n\t\tthis._cache?.invalidate(name);\n\t}\n\n\t/**\n\t * Updates an existing item with partial data.\n\t *\n\t * @param name - The name of the item to update\n\t * @param updates - Partial item data with fields to update\n\t * @throws If item not found\n\t */\n\tpublic update(name: string, updates: Partial<T>): void {\n\t\tif (!this._items.has(name)) {\n\t\t\tthrow this._createNotFoundError(name, 'update');\n\t\t}\n\t\tconst isManualItem = this._manualItems.has(name);\n\t\tconst existing = this._items.get(name)!;\n\t\tconst updated = { ...existing, ...updates, name };\n\t\tthis._items.set(name, updated);\n\t\tif (isManualItem) {\n\t\t\tthis._manualItems.set(name, updated);\n\t\t} else {\n\t\t\tfor (const [filePath, item] of this._discoveredItemsByPath) {\n\t\t\t\tif (item.name === name) this._discoveredItemsByPath.set(filePath, updated);\n\t\t\t}\n\t\t}\n\t\tthis.log(`Updated ${this._entityName}: ${name}`, { [`${this._entityName}Name`]: name });\n\t\t// Invalidate cache when updating an item\n\t\tthis._cache?.invalidate('all');\n\t\tthis._cache?.invalidate(name);\n\t}\n\n\t/**\n\t * Gets an item by name.\n\t *\n\t * @param name - The name of the item to get\n\t * @returns The item if found, undefined otherwise\n\t */\n\tpublic get(name: string): T | undefined {\n\t\treturn this._items.get(name);\n\t}\n\n\t/**\n\t * Gets all items as an array.\n\t *\n\t * Uses cache if available for performance.\n\t *\n\t * @returns An array of all registered items\n\t */\n\tpublic getAll(): T[] {\n\t\t// Check cache first\n\t\tif (this._cache) {\n\t\t\tconst cached = this._cache.get('all');\n\t\t\tif (cached) {\n\t\t\t\treturn cached;\n\t\t\t}\n\t\t}\n\t\t// Get from storage\n\t\tconst items = Array.from(this._items.values());\n\t\t// Cache the result\n\t\tthis._cache?.set('all', items);\n\t\treturn items;\n\t}\n\n\t/**\n\t * Checks if an item exists in the registry.\n\t *\n\t * @param name - The name of the item to check\n\t * @returns true if the item exists, false otherwise\n\t */\n\tpublic has(name: string): boolean {\n\t\treturn this._items.has(name);\n\t}\n\n\t/**\n\t * Gets all item names as an array.\n\t *\n\t * @returns An array of item names\n\t */\n\tpublic getNames(): string[] {\n\t\treturn Array.from(this._items.keys());\n\t}\n\n\t/**\n\t * Clears all items from the registry.\n\t */\n\tpublic clear(): void {\n\t\tthis._beginReplacement();\n\t\tthis.log(`Cleared all ${this._entityName}s`);\n\t}\n\n\t/**\n\t * Gets the number of items in the registry.\n\t *\n\t * @returns The count of registered items\n\t */\n\tpublic size(): number {\n\t\treturn this._items.size;\n\t}\n\n\t/**\n\t * Asynchronously discovers items from the configured directories.\n\t *\n\t * Multiple concurrent calls share the same discovery promise.\n\t * Subsequent calls return cached results if discovery has already completed.\n\t *\n\t * @returns A Promise resolving to the number of items discovered\n\t */\n\tpublic async discoverAsync(): Promise<number> {\n\t\tif (this._discoveryPromise) {\n\t\t\tif (\n\t\t\t\t!this._discovered &&\n\t\t\t\tthis._activeDiscoveryEpoch !== this._replacementEpoch\n\t\t\t) {\n\t\t\t\tthis._queuedRefreshEpoch = this._replacementEpoch;\n\t\t\t}\n\t\t\treturn this._discoveryPromise;\n\t\t}\n\n\t\tif (this._discovered) {\n\t\t\treturn this._discoveredCount;\n\t\t}\n\n\t\treturn this._beginDiscovery();\n\t}\n\n\t/**\n\t * Re-scans configured directories and atomically reconciles discovered items.\n\t *\n\t * Calls made during an active scan share its promise and request at most one\n\t * follow-up pass, ensuring an event that arrives mid-scan is not lost.\n\t *\n\t * @returns A Promise resolving to the number of discovered filesystem items\n\t */\n\tpublic async refreshAsync(): Promise<number> {\n\t\tif (this._discoveryPromise) {\n\t\t\tthis._queuedRefreshEpoch = this._replacementEpoch;\n\t\t\treturn this._discoveryPromise;\n\t\t}\n\t\treturn this._beginDiscovery();\n\t}\n\n\tprivate _beginDiscovery(): Promise<number> {\n\t\tconst operation = this._runDiscoveryLoop();\n\t\tthis._discoveryPromise = operation;\n\t\treturn operation;\n\t}\n\n\tprivate async _runDiscoveryLoop(): Promise<number> {\n\t\tlet passEpoch = this._replacementEpoch;\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tthis._activeDiscoveryEpoch = passEpoch;\n\t\t\t\tlet count: number;\n\t\t\t\ttry {\n\t\t\t\t\tcount = await this._performDiscovery(passEpoch);\n\t\t\t\t} finally {\n\t\t\t\t\tthis._activeDiscoveryEpoch = null;\n\t\t\t\t}\n\n\t\t\t\tconst queuedEpoch = this._queuedRefreshEpoch;\n\t\t\t\tthis._queuedRefreshEpoch = null;\n\t\t\t\tif (queuedEpoch === null) return count;\n\t\t\t\tpassEpoch = queuedEpoch;\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._discoveryPromise = null;\n\t\t}\n\t}\n\n\t/**\n\t * Performs the actual discovery operation.\n\t *\n\t * Scans configured directories for item files, parses their frontmatter,\n\t * and adds valid items to the registry.\n\t *\n\t * @returns A Promise resolving to the number of items discovered\n\t */\n\tprotected async _performDiscovery(epoch = this._replacementEpoch): Promise<number> {\n\t\tconst scan: DiscoveryScan<T> = {\n\t\t\titemsByPath: new Map(),\n\t\t\tclaimedNames: new Set(),\n\t\t\tpreviousItemsByPath: new Map(this._discoveredItemsByPath),\n\t\t\twarnings: [],\n\t\t};\n\t\tfor (const dir of this._searchDirs) {\n\t\t\tawait this._scanDirectory(dir, scan);\n\t\t}\n\t\treturn this._publishDiscovery(epoch, scan);\n\t}\n\n\tprivate async _scanDirectory(directory: string, scan: DiscoveryScan<T>): Promise<void> {\n\t\ttry {\n\t\t\tif (!existsSync(directory)) return;\n\t\t\tconst entries = await readdir(directory, { withFileTypes: true });\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (this._shouldSkipFile(entry.name) || !entry.isFile()) continue;\n\t\t\t\tif (!this._fileExtensions.some((extension) => entry.name.endsWith(extension))) continue;\n\t\t\t\tawait this._scanFile(join(directory, entry.name), scan);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tscan.warnings.push({\n\t\t\t\tmessage: `Failed to scan ${this._entityName} directory`,\n\t\t\t\tmeta: { directory, error: getErrorMessage(error) },\n\t\t\t});\n\t\t\tthis._retainDirectory(directory, scan);\n\t\t}\n\t}\n\n\tprivate async _scanFile(filePath: string, scan: DiscoveryScan<T>): Promise<void> {\n\t\ttry {\n\t\t\tconst content = await readFile(filePath, 'utf-8');\n\t\t\tconst parsed = this._parseFrontmatter(content);\n\t\t\tif (parsed._error) {\n\t\t\t\tthis._retainLastKnownGood(filePath, parsed._error, scan);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!parsed.name) return;\n\t\t\tconst item = this._buildItem(parsed);\n\t\t\tif (item && !scan.claimedNames.has(item.name)) {\n\t\t\t\tscan.itemsByPath.set(filePath, item);\n\t\t\t\tscan.claimedNames.add(item.name);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis._retainLastKnownGood(filePath, getErrorMessage(error), scan);\n\t\t}\n\t}\n\n\tprivate _retainLastKnownGood(filePath: string, reason: string, scan: DiscoveryScan<T>): void {\n\t\tconst previous = scan.previousItemsByPath.get(filePath);\n\t\tif (previous && !scan.claimedNames.has(previous.name)) {\n\t\t\tscan.itemsByPath.set(filePath, previous);\n\t\t\tscan.claimedNames.add(previous.name);\n\t\t}\n\t\tscan.warnings.push({\n\t\t\tmessage: `Invalid ${this._entityName} discovery file`,\n\t\t\tmeta: { filePath, reason, retainedLastKnownGood: previous !== undefined },\n\t\t});\n\t}\n\n\tprivate _retainDirectory(directory: string, scan: DiscoveryScan<T>): void {\n\t\tfor (const [filePath, item] of scan.previousItemsByPath) {\n\t\t\tif (dirname(filePath) === directory && !scan.claimedNames.has(item.name)) {\n\t\t\t\tscan.itemsByPath.set(filePath, item);\n\t\t\t\tscan.claimedNames.add(item.name);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate _publishDiscovery(epoch: number, scan: DiscoveryScan<T>): number {\n\t\tif (epoch !== this._replacementEpoch) return this._discoveredCount;\n\n\t\tthis._discoveredItemsByPath = scan.itemsByPath;\n\t\tthis._rebuildVisibleItems();\n\t\tthis._discovered = true;\n\t\tthis._cache.clear();\n\t\tthis._cache.set('all', Array.from(this._items.values()));\n\t\tfor (const warning of scan.warnings) this._logger.warn(warning.message, warning.meta);\n\t\tthis.log(`Discovery complete: found ${this._discoveredCount} ${this._entityName}s`, {\n\t\t\tdiscoveredCount: this._discoveredCount,\n\t\t});\n\t\treturn this._discoveredCount;\n\t}\n\n\tprivate _rebuildVisibleItems(): void {\n\t\tconst visibleItems = new Map(this._manualItems);\n\t\tlet visibleDiscoveredCount = 0;\n\t\tfor (const item of this._discoveredItemsByPath.values()) {\n\t\t\tif (visibleItems.has(item.name)) continue;\n\t\t\tvisibleItems.set(item.name, item);\n\t\t\tvisibleDiscoveredCount++;\n\t\t}\n\t\tthis._items = visibleItems;\n\t\tthis._discoveredCount = visibleDiscoveredCount;\n\t}\n\n\tprivate _beginReplacement(): void {\n\t\tthis._replacementEpoch++;\n\t\tthis._queuedRefreshEpoch = null;\n\t\tthis._items.clear();\n\t\tthis._manualItems.clear();\n\t\tthis._discoveredItemsByPath.clear();\n\t\tthis._discovered = false;\n\t\tthis._discoveredCount = 0;\n\t\tthis._cache.clear();\n\t}\n\n\t/**\n\t * Parses YAML frontmatter from file content.\n\t *\n\t * This is a shared utility for subclasses that parse YAML frontmatter.\n\t *\n\t * @param content - The file content to parse\n\t * @returns The parsed YAML object, or null if no frontmatter found\n\t */\n\tprotected _extractFrontmatter(content: string): Record<string, unknown> | null {\n\t\tconst match = content.match(/^---\\n([\\s\\S]+?)\\n---/);\n\t\tif (!match) {\n\t\t\treturn null;\n\t\t}\n\t\treturn parseYaml(match[1]!) as Record<string, unknown>;\n\t}\n\n\t/**\n\t * Sets items from an external source.\n\t *\n\t * Clears existing items and adds new ones from the provided array.\n\t *\n\t * @param items - Array of items from an external source\n\t */\n\tpublic setAll(items: T[]): void {\n\t\tthis._beginReplacement();\n\t\tfor (const item of items) {\n\t\t\ttry {\n\t\t\t\tthis.add(item);\n\t\t\t} catch (error) {\n\t\t\t\tthis.log(`Error adding ${this._entityName} '${item.name}':`, {\n\t\t\t\t\t[`${this._entityName}Name`]: item.name,\n\t\t\t\t\terror: getErrorMessage(error),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis.log(`Set ${items.length} ${this._entityName}s from external source`, {\n\t\t\t[`${this._entityName}Count`]: items.length,\n\t\t});\n\t}\n}\n"],"names":["BaseRegistry","options","Map","NullLogger","DiscoveryCache","message","meta","item","name","removedManualItem","filePath","updates","isManualItem","existing","updated","cached","items","Array","operation","passEpoch","count","queuedEpoch","epoch","scan","Set","dir","directory","existsSync","entries","readdir","entry","extension","join","error","getErrorMessage","content","readFile","parsed","reason","previous","undefined","dirname","warning","visibleItems","visibleDiscoveredCount","match","parseYaml"],"mappings":"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"}
|
|
@@ -43,8 +43,7 @@ export interface SkillRegistryOptions {
|
|
|
43
43
|
/**
|
|
44
44
|
* Registry for managing Claude Code skill operations.
|
|
45
45
|
*
|
|
46
|
-
* Extends `BaseRegistry<Skill>` with skill-specific frontmatter parsing
|
|
47
|
-
* and backward-compatible aliases (`addSkill`, etc.).
|
|
46
|
+
* Extends `BaseRegistry<Skill>` with skill-specific frontmatter parsing.
|
|
48
47
|
*/
|
|
49
48
|
export declare class SkillRegistry extends BaseRegistry<Skill> {
|
|
50
49
|
protected readonly _fileExtensions: string[];
|
|
@@ -58,10 +57,5 @@ export declare class SkillRegistry extends BaseRegistry<Skill> {
|
|
|
58
57
|
_error?: string;
|
|
59
58
|
};
|
|
60
59
|
protected _buildItem(parsed: Partial<Skill>): Skill | null;
|
|
61
|
-
addSkill(skill: Skill): void;
|
|
62
|
-
updateSkill(name: string, updates: Partial<Skill>): void;
|
|
63
|
-
hasSkill(name: string): boolean;
|
|
64
|
-
getSkill(name: string): Skill | undefined;
|
|
65
|
-
setSkills(skills: Skill[]): void;
|
|
66
60
|
}
|
|
67
61
|
//# sourceMappingURL=SkillRegistry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SkillRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/SkillRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACpC,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,wCAAwC;IACxC,KAAK,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IAE9B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED
|
|
1
|
+
{"version":3,"file":"SkillRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/SkillRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACpC,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,wCAAwC;IACxC,KAAK,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IAE9B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,YAAY,CAAC,KAAK,CAAC;IACrD,mBAA4B,eAAe,WAA4B;IACvE,mBAA4B,WAAW,WAAW;gBAEtC,OAAO,GAAE,oBAAyB;cAgB3B,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK;cAI1C,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK;cAI1C,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK;cAMzD,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;cAI1C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;cA6BxE,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,IAAI;CAWnE"}
|
|
@@ -66,21 +66,6 @@ class SkillRegistry extends BaseRegistry {
|
|
|
66
66
|
allowed_tools: parsed.allowed_tools
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
-
addSkill(skill) {
|
|
70
|
-
this.add(skill);
|
|
71
|
-
}
|
|
72
|
-
updateSkill(name, updates) {
|
|
73
|
-
this.update(name, updates);
|
|
74
|
-
}
|
|
75
|
-
hasSkill(name) {
|
|
76
|
-
return this.has(name);
|
|
77
|
-
}
|
|
78
|
-
getSkill(name) {
|
|
79
|
-
return this.get(name);
|
|
80
|
-
}
|
|
81
|
-
setSkills(skills) {
|
|
82
|
-
this.setAll(skills);
|
|
83
|
-
}
|
|
84
69
|
}
|
|
85
70
|
export { SkillRegistry };
|
|
86
71
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry/SkillRegistry.js","sources":["../../src/registry/SkillRegistry.ts"],"sourcesContent":["/**\n * Skill registry for managing and discovering Claude Code skills.\n *\n * This module provides the `SkillRegistry` class which manages skill registration,\n * discovery from filesystem directories, and CRUD operations. Skills are higher-level\n * workflows that coordinate multiple tools and operations.\n *\n * @module registry\n */\n\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { DiscoveryCache } from '../cache/DiscoveryCache.js';\nimport { DuplicateSkillError, InvalidSkillError, SkillNotFoundError } from '../errors.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport type { Skill } from '../types/skill.js';\nimport { BaseRegistry } from './BaseRegistry.js';\n\n/**\n * Configuration options for creating a `SkillRegistry` instance.\n *\n * @example\n * ```typescript\n * const options: SkillRegistryOptions = {\n * logger: new StructuredLogger({ context: 'SkillRegistry' }),\n * cache: new DiscoveryCache({ ttl: 300000, maxSize: 100 }),\n * skillDirs: ['./custom-skills', '~/.claude/skills'],\n * lazyDiscovery: true\n * };\n * ```\n */\nexport interface SkillRegistryOptions {\n\t/** Optional logger for diagnostics. */\n\tlogger?: Logger;\n\n\t/** Optional cache for skill lookups. */\n\tcache?: DiscoveryCache<Skill>;\n\n\t/**\n\t * Directory paths to search for skills.\n\t * @default ['.claude/skills', '~/.claude/skills', '.agents/skills', '~/.agents/skills']\n\t */\n\tskillDirs?: string[];\n\n\t/**\n\t * Enable lazy discovery (discover on first access instead of startup).\n\t * @default false\n\t */\n\tlazyDiscovery?: boolean;\n}\n\n/**\n * Registry for managing Claude Code skill operations.\n *\n * Extends `BaseRegistry<Skill>` with skill-specific frontmatter parsing
|
|
1
|
+
{"version":3,"file":"registry/SkillRegistry.js","sources":["../../src/registry/SkillRegistry.ts"],"sourcesContent":["/**\n * Skill registry for managing and discovering Claude Code skills.\n *\n * This module provides the `SkillRegistry` class which manages skill registration,\n * discovery from filesystem directories, and CRUD operations. Skills are higher-level\n * workflows that coordinate multiple tools and operations.\n *\n * @module registry\n */\n\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { DiscoveryCache } from '../cache/DiscoveryCache.js';\nimport { DuplicateSkillError, InvalidSkillError, SkillNotFoundError } from '../errors.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport type { Skill } from '../types/skill.js';\nimport { BaseRegistry } from './BaseRegistry.js';\n\n/**\n * Configuration options for creating a `SkillRegistry` instance.\n *\n * @example\n * ```typescript\n * const options: SkillRegistryOptions = {\n * logger: new StructuredLogger({ context: 'SkillRegistry' }),\n * cache: new DiscoveryCache({ ttl: 300000, maxSize: 100 }),\n * skillDirs: ['./custom-skills', '~/.claude/skills'],\n * lazyDiscovery: true\n * };\n * ```\n */\nexport interface SkillRegistryOptions {\n\t/** Optional logger for diagnostics. */\n\tlogger?: Logger;\n\n\t/** Optional cache for skill lookups. */\n\tcache?: DiscoveryCache<Skill>;\n\n\t/**\n\t * Directory paths to search for skills.\n\t * @default ['.claude/skills', '~/.claude/skills', '.agents/skills', '~/.agents/skills']\n\t */\n\tskillDirs?: string[];\n\n\t/**\n\t * Enable lazy discovery (discover on first access instead of startup).\n\t * @default false\n\t */\n\tlazyDiscovery?: boolean;\n}\n\n/**\n * Registry for managing Claude Code skill operations.\n *\n * Extends `BaseRegistry<Skill>` with skill-specific frontmatter parsing.\n */\nexport class SkillRegistry extends BaseRegistry<Skill> {\n\tprotected override readonly _fileExtensions = ['.md', '.yml', '.yaml'];\n\tprotected override readonly _entityName = 'skill';\n\n\tconstructor(options: SkillRegistryOptions = {}) {\n\t\tsuper({\n\t\t\tlogger: options.logger,\n\t\t\tcache: options.cache,\n\t\t\tsearchDirs: options.skillDirs ?? [\n\t\t\t\t'.claude/skills',\n\t\t\t\tjoin(homedir(), '.claude/skills'),\n\t\t\t\t'.agents/skills',\n\t\t\t\tjoin(homedir(), '.agents/skills'),\n\t\t\t],\n\t\t\tlazyDiscovery: options.lazyDiscovery,\n\t\t});\n\t}\n\n\t// --- Error factories ---\n\n\tprotected override _createInvalidError(reason: string): Error {\n\t\treturn new InvalidSkillError(reason);\n\t}\n\n\tprotected override _createDuplicateError(name: string): Error {\n\t\treturn new DuplicateSkillError(name);\n\t}\n\n\tprotected override _createNotFoundError(name: string, action: string): Error {\n\t\treturn new SkillNotFoundError(name, action);\n\t}\n\n\t// --- Discovery ---\n\n\tprotected override _shouldSkipFile(fileName: string): boolean {\n\t\treturn fileName === '.DS_Store';\n\t}\n\n\tprotected override _parseFrontmatter(content: string): Partial<Skill> & { _error?: string } {\n\t\tconst frontmatter = this._extractFrontmatter(content);\n\t\tif (!frontmatter) {\n\t\t\treturn {};\n\t\t}\n\n\t\ttry {\n\t\t\tconst result: Partial<Skill> = {\n\t\t\t\tname: typeof frontmatter.name === 'string' ? frontmatter.name : undefined,\n\t\t\t\tdescription: typeof frontmatter.description === 'string' ? frontmatter.description : '',\n\t\t\t\tuser_invocable: frontmatter['user-invocable'] === true,\n\t\t\t\tallowed_tools: Array.isArray(frontmatter['allowed-tools'])\n\t\t\t\t\t? frontmatter['allowed-tools'].map(String)\n\t\t\t\t\t: undefined,\n\t\t\t};\n\n\t\t\tif (!result.name) {\n\t\t\t\treturn { _error: 'Missing required field: name' };\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tthis.log('Error parsing YAML frontmatter:', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t\treturn { _error: 'YAML parse error' };\n\t\t}\n\t}\n\n\tprotected override _buildItem(parsed: Partial<Skill>): Skill | null {\n\t\tif (!parsed.name) {\n\t\t\treturn null;\n\t\t}\n\t\treturn {\n\t\t\tname: parsed.name,\n\t\t\tdescription: parsed.description || '',\n\t\t\tuser_invocable: parsed.user_invocable ?? false,\n\t\t\tallowed_tools: parsed.allowed_tools,\n\t\t};\n\t}\n}\n"],"names":["SkillRegistry","BaseRegistry","options","join","homedir","reason","InvalidSkillError","name","DuplicateSkillError","action","SkillNotFoundError","fileName","content","frontmatter","result","undefined","Array","String","error","Error","parsed"],"mappings":";;;;AAwDO,MAAMA,sBAAsBC;IACN,kBAAkB;QAAC;QAAO;QAAQ;KAAQ,CAAC;IAC3C,cAAc,QAAQ;IAElD,YAAYC,UAAgC,CAAC,CAAC,CAAE;QAC/C,KAAK,CAAC;YACL,QAAQA,QAAQ,MAAM;YACtB,OAAOA,QAAQ,KAAK;YACpB,YAAYA,QAAQ,SAAS,IAAI;gBAChC;gBACAC,KAAKC,WAAW;gBAChB;gBACAD,KAAKC,WAAW;aAChB;YACD,eAAeF,QAAQ,aAAa;QACrC;IACD;IAImB,oBAAoBG,MAAc,EAAS;QAC7D,OAAO,IAAIC,kBAAkBD;IAC9B;IAEmB,sBAAsBE,IAAY,EAAS;QAC7D,OAAO,IAAIC,oBAAoBD;IAChC;IAEmB,qBAAqBA,IAAY,EAAEE,MAAc,EAAS;QAC5E,OAAO,IAAIC,mBAAmBH,MAAME;IACrC;IAImB,gBAAgBE,QAAgB,EAAW;QAC7D,OAAOA,AAAa,gBAAbA;IACR;IAEmB,kBAAkBC,OAAe,EAAwC;QAC3F,MAAMC,cAAc,IAAI,CAAC,mBAAmB,CAACD;QAC7C,IAAI,CAACC,aACJ,OAAO,CAAC;QAGT,IAAI;YACH,MAAMC,SAAyB;gBAC9B,MAAM,AAA4B,YAA5B,OAAOD,YAAY,IAAI,GAAgBA,YAAY,IAAI,GAAGE;gBAChE,aAAa,AAAmC,YAAnC,OAAOF,YAAY,WAAW,GAAgBA,YAAY,WAAW,GAAG;gBACrF,gBAAgBA,AAAkC,SAAlCA,WAAW,CAAC,iBAAiB;gBAC7C,eAAeG,MAAM,OAAO,CAACH,WAAW,CAAC,gBAAgB,IACtDA,WAAW,CAAC,gBAAgB,CAAC,GAAG,CAACI,UACjCF;YACJ;YAEA,IAAI,CAACD,OAAO,IAAI,EACf,OAAO;gBAAE,QAAQ;YAA+B;YAGjD,OAAOA;QACR,EAAE,OAAOI,OAAO;YACf,IAAI,CAAC,GAAG,CAAC,mCAAmC;gBAC3C,OAAOA,iBAAiBC,QAAQD,MAAM,OAAO,GAAGD,OAAOC;YACxD;YACA,OAAO;gBAAE,QAAQ;YAAmB;QACrC;IACD;IAEmB,WAAWE,MAAsB,EAAgB;QACnE,IAAI,CAACA,OAAO,IAAI,EACf,OAAO;QAER,OAAO;YACN,MAAMA,OAAO,IAAI;YACjB,aAAaA,OAAO,WAAW,IAAI;YACnC,gBAAgBA,OAAO,cAAc,IAAI;YACzC,eAAeA,OAAO,aAAa;QACpC;IACD;AACD"}
|
|
@@ -43,8 +43,7 @@ export interface ToolRegistryOptions {
|
|
|
43
43
|
/**
|
|
44
44
|
* Registry for managing MCP tool operations.
|
|
45
45
|
*
|
|
46
|
-
* Extends `BaseRegistry<Tool>` with tool-specific frontmatter parsing
|
|
47
|
-
* and backward-compatible aliases (`addTool`, `removeTool`, etc.).
|
|
46
|
+
* Extends `BaseRegistry<Tool>` with tool-specific frontmatter parsing.
|
|
48
47
|
*/
|
|
49
48
|
export declare class ToolRegistry extends BaseRegistry<Tool> {
|
|
50
49
|
protected readonly _fileExtensions: string[];
|
|
@@ -59,12 +58,5 @@ export declare class ToolRegistry extends BaseRegistry<Tool> {
|
|
|
59
58
|
};
|
|
60
59
|
protected _buildItem(parsed: Partial<Tool>): Tool | null;
|
|
61
60
|
get(name: string): Tool | undefined;
|
|
62
|
-
addTool(tool: Tool): void;
|
|
63
|
-
removeTool(name: string): void;
|
|
64
|
-
updateTool(name: string, updates: Partial<Tool>): void;
|
|
65
|
-
hasTool(name: string): boolean;
|
|
66
|
-
getTool(name: string): Tool | undefined;
|
|
67
|
-
list(): string[];
|
|
68
|
-
setTools(tools: Tool[]): void;
|
|
69
61
|
}
|
|
70
62
|
//# sourceMappingURL=ToolRegistry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/ToolRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,mBAAmB;IACnC,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,uCAAuC;IACvC,KAAK,CAAC,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC;IAE7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED
|
|
1
|
+
{"version":3,"file":"ToolRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/ToolRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,mBAAmB;IACnC,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,uCAAuC;IACvC,KAAK,CAAC,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC;IAE7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,YAAY,CAAC,IAAI,CAAC;IACnD,mBAA4B,eAAe,WAAgB;IAC3D,mBAA4B,WAAW,UAAU;gBAErC,OAAO,GAAE,mBAAwB;cAW1B,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK;cAI1C,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK;cAI1C,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK;cAMzD,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;cAI3C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;cA0BvE,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI;IAajD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;CASnD"}
|
|
@@ -69,27 +69,6 @@ class ToolRegistry extends BaseRegistry {
|
|
|
69
69
|
}
|
|
70
70
|
return this._items.get(name);
|
|
71
71
|
}
|
|
72
|
-
addTool(tool) {
|
|
73
|
-
this.add(tool);
|
|
74
|
-
}
|
|
75
|
-
removeTool(name) {
|
|
76
|
-
this.remove(name);
|
|
77
|
-
}
|
|
78
|
-
updateTool(name, updates) {
|
|
79
|
-
this.update(name, updates);
|
|
80
|
-
}
|
|
81
|
-
hasTool(name) {
|
|
82
|
-
return this.has(name);
|
|
83
|
-
}
|
|
84
|
-
getTool(name) {
|
|
85
|
-
return this.get(name);
|
|
86
|
-
}
|
|
87
|
-
list() {
|
|
88
|
-
return this.getNames();
|
|
89
|
-
}
|
|
90
|
-
setTools(tools) {
|
|
91
|
-
this.setAll(tools);
|
|
92
|
-
}
|
|
93
72
|
}
|
|
94
73
|
export { ToolRegistry };
|
|
95
74
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry/ToolRegistry.js","sources":["../../src/registry/ToolRegistry.ts"],"sourcesContent":["/**\n * Tool registry for managing MCP tool CRUD operations.\n *\n * This module provides the `ToolRegistry` class which manages the registration,\n * retrieval, update, and removal of MCP tools. It supports optional caching\n * for improved performance, filesystem discovery, and integrates with the logging system.\n *\n * @module registry\n */\n\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { DiscoveryCache } from '../cache/DiscoveryCache.js';\nimport { DuplicateToolError, InvalidToolError, ToolNotFoundError } from '../errors.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport type { Tool } from '../types/tool.js';\nimport { BaseRegistry } from './BaseRegistry.js';\n\n/**\n * Configuration options for creating a `ToolRegistry` instance.\n *\n * @example\n * ```typescript\n * const options: ToolRegistryOptions = {\n * logger: new StructuredLogger({ context: 'ToolRegistry' }),\n * cache: new DiscoveryCache({ ttl: 300000, maxSize: 100 }),\n * toolDirs: ['./custom-tools', '~/.claude/tools'],\n * lazyDiscovery: true\n * };\n * ```\n */\nexport interface ToolRegistryOptions {\n\t/** Optional logger for diagnostics. */\n\tlogger?: Logger;\n\n\t/** Optional cache for tool lookups. */\n\tcache?: DiscoveryCache<Tool>;\n\n\t/**\n\t * Directory paths to search for tools.\n\t * @default ['.claude/tools', '~/.claude/tools']\n\t */\n\ttoolDirs?: string[];\n\n\t/**\n\t * Enable lazy discovery (discover on first access instead of startup).\n\t * @default false\n\t */\n\tlazyDiscovery?: boolean;\n}\n\n/**\n * Registry for managing MCP tool operations.\n *\n * Extends `BaseRegistry<Tool>` with tool-specific frontmatter parsing
|
|
1
|
+
{"version":3,"file":"registry/ToolRegistry.js","sources":["../../src/registry/ToolRegistry.ts"],"sourcesContent":["/**\n * Tool registry for managing MCP tool CRUD operations.\n *\n * This module provides the `ToolRegistry` class which manages the registration,\n * retrieval, update, and removal of MCP tools. It supports optional caching\n * for improved performance, filesystem discovery, and integrates with the logging system.\n *\n * @module registry\n */\n\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { DiscoveryCache } from '../cache/DiscoveryCache.js';\nimport { DuplicateToolError, InvalidToolError, ToolNotFoundError } from '../errors.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport type { Tool } from '../types/tool.js';\nimport { BaseRegistry } from './BaseRegistry.js';\n\n/**\n * Configuration options for creating a `ToolRegistry` instance.\n *\n * @example\n * ```typescript\n * const options: ToolRegistryOptions = {\n * logger: new StructuredLogger({ context: 'ToolRegistry' }),\n * cache: new DiscoveryCache({ ttl: 300000, maxSize: 100 }),\n * toolDirs: ['./custom-tools', '~/.claude/tools'],\n * lazyDiscovery: true\n * };\n * ```\n */\nexport interface ToolRegistryOptions {\n\t/** Optional logger for diagnostics. */\n\tlogger?: Logger;\n\n\t/** Optional cache for tool lookups. */\n\tcache?: DiscoveryCache<Tool>;\n\n\t/**\n\t * Directory paths to search for tools.\n\t * @default ['.claude/tools', '~/.claude/tools']\n\t */\n\ttoolDirs?: string[];\n\n\t/**\n\t * Enable lazy discovery (discover on first access instead of startup).\n\t * @default false\n\t */\n\tlazyDiscovery?: boolean;\n}\n\n/**\n * Registry for managing MCP tool operations.\n *\n * Extends `BaseRegistry<Tool>` with tool-specific frontmatter parsing.\n */\nexport class ToolRegistry extends BaseRegistry<Tool> {\n\tprotected override readonly _fileExtensions = ['.tool.md'];\n\tprotected override readonly _entityName = 'tool';\n\n\tconstructor(options: ToolRegistryOptions = {}) {\n\t\tsuper({\n\t\t\tlogger: options.logger,\n\t\t\tcache: options.cache,\n\t\t\tsearchDirs: options.toolDirs ?? ['.claude/tools', join(homedir(), '.claude/tools')],\n\t\t\tlazyDiscovery: options.lazyDiscovery,\n\t\t});\n\t}\n\n\t// --- Error factories ---\n\n\tprotected override _createInvalidError(reason: string): Error {\n\t\treturn new InvalidToolError(reason);\n\t}\n\n\tprotected override _createDuplicateError(name: string): Error {\n\t\treturn new DuplicateToolError(name);\n\t}\n\n\tprotected override _createNotFoundError(name: string, action: string): Error {\n\t\treturn new ToolNotFoundError(name, action);\n\t}\n\n\t// --- Discovery ---\n\n\tprotected override _shouldSkipFile(_fileName: string): boolean {\n\t\treturn false;\n\t}\n\n\tprotected override _parseFrontmatter(content: string): Partial<Tool> & { _error?: string } {\n\t\tconst frontmatter = this._extractFrontmatter(content);\n\t\tif (!frontmatter) {\n\t\t\treturn { _error: 'No YAML frontmatter found' };\n\t\t}\n\n\t\ttry {\n\t\t\tconst result: Partial<Tool> = {\n\t\t\t\tname: typeof frontmatter.name === 'string' ? frontmatter.name : undefined,\n\t\t\t\tdescription: typeof frontmatter.description === 'string' ? frontmatter.description : '',\n\t\t\t\tinputSchema: frontmatter.inputSchema as Tool['inputSchema'],\n\t\t\t};\n\n\t\t\tif (!result.name) {\n\t\t\t\treturn { _error: 'Missing required field: name' };\n\t\t\t}\n\t\t\tif (!result.inputSchema) {\n\t\t\t\treturn { _error: 'Missing required field: inputSchema' };\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch {\n\t\t\treturn { _error: 'YAML parse error' };\n\t\t}\n\t}\n\n\tprotected override _buildItem(parsed: Partial<Tool>): Tool | null {\n\t\tif (!parsed.name || !parsed.inputSchema) {\n\t\t\treturn null;\n\t\t}\n\t\treturn {\n\t\t\tname: parsed.name,\n\t\t\tdescription: parsed.description || '',\n\t\t\tinputSchema: parsed.inputSchema,\n\t\t};\n\t}\n\n\t// --- Tool-specific get with cache lookup ---\n\n\tpublic override get(name: string): Tool | undefined {\n\t\tif (this._cache) {\n\t\t\tconst cached = this._cache.get(`tool:${name}`);\n\t\t\tif (cached && cached.length > 0) {\n\t\t\t\treturn cached[0];\n\t\t\t}\n\t\t}\n\t\treturn this._items.get(name);\n\t}\n}\n"],"names":["ToolRegistry","BaseRegistry","options","join","homedir","reason","InvalidToolError","name","DuplicateToolError","action","ToolNotFoundError","_fileName","content","frontmatter","result","undefined","parsed","cached"],"mappings":";;;;AAwDO,MAAMA,qBAAqBC;IACL,kBAAkB;QAAC;KAAW,CAAC;IAC/B,cAAc,OAAO;IAEjD,YAAYC,UAA+B,CAAC,CAAC,CAAE;QAC9C,KAAK,CAAC;YACL,QAAQA,QAAQ,MAAM;YACtB,OAAOA,QAAQ,KAAK;YACpB,YAAYA,QAAQ,QAAQ,IAAI;gBAAC;gBAAiBC,KAAKC,WAAW;aAAiB;YACnF,eAAeF,QAAQ,aAAa;QACrC;IACD;IAImB,oBAAoBG,MAAc,EAAS;QAC7D,OAAO,IAAIC,iBAAiBD;IAC7B;IAEmB,sBAAsBE,IAAY,EAAS;QAC7D,OAAO,IAAIC,mBAAmBD;IAC/B;IAEmB,qBAAqBA,IAAY,EAAEE,MAAc,EAAS;QAC5E,OAAO,IAAIC,kBAAkBH,MAAME;IACpC;IAImB,gBAAgBE,SAAiB,EAAW;QAC9D,OAAO;IACR;IAEmB,kBAAkBC,OAAe,EAAuC;QAC1F,MAAMC,cAAc,IAAI,CAAC,mBAAmB,CAACD;QAC7C,IAAI,CAACC,aACJ,OAAO;YAAE,QAAQ;QAA4B;QAG9C,IAAI;YACH,MAAMC,SAAwB;gBAC7B,MAAM,AAA4B,YAA5B,OAAOD,YAAY,IAAI,GAAgBA,YAAY,IAAI,GAAGE;gBAChE,aAAa,AAAmC,YAAnC,OAAOF,YAAY,WAAW,GAAgBA,YAAY,WAAW,GAAG;gBACrF,aAAaA,YAAY,WAAW;YACrC;YAEA,IAAI,CAACC,OAAO,IAAI,EACf,OAAO;gBAAE,QAAQ;YAA+B;YAEjD,IAAI,CAACA,OAAO,WAAW,EACtB,OAAO;gBAAE,QAAQ;YAAsC;YAGxD,OAAOA;QACR,EAAE,OAAM;YACP,OAAO;gBAAE,QAAQ;YAAmB;QACrC;IACD;IAEmB,WAAWE,MAAqB,EAAe;QACjE,IAAI,CAACA,OAAO,IAAI,IAAI,CAACA,OAAO,WAAW,EACtC,OAAO;QAER,OAAO;YACN,MAAMA,OAAO,IAAI;YACjB,aAAaA,OAAO,WAAW,IAAI;YACnC,aAAaA,OAAO,WAAW;QAChC;IACD;IAIgB,IAAIT,IAAY,EAAoB;QACnD,IAAI,IAAI,CAAC,MAAM,EAAE;YAChB,MAAMU,SAAS,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAEV,MAAM;YAC7C,IAAIU,UAAUA,OAAO,MAAM,GAAG,GAC7B,OAAOA,MAAM,CAAC,EAAE;QAElB;QACA,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAACV;IACxB;AACD"}
|
package/dist/schema.d.ts
CHANGED
|
@@ -315,6 +315,7 @@ export declare const PartialStepRecommendationSchema: v.ObjectSchema<{
|
|
|
315
315
|
* thought: 'I need to analyze the problem',
|
|
316
316
|
* thought_number: 1,
|
|
317
317
|
* total_thoughts: 5,
|
|
318
|
+
* session_id: 'analysis-task',
|
|
318
319
|
* next_thought_needed: true,
|
|
319
320
|
* available_mcp_tools: ['Read', 'Write', 'Grep']
|
|
320
321
|
* });
|
|
@@ -479,7 +480,7 @@ export declare const SequentialThinkingSchema: v.ObjectSchema<{
|
|
|
479
480
|
readonly merge_branch_ids: v.OptionalSchema<v.SchemaWithPipe<readonly [v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, undefined>, v.MaxLengthAction<string, 50, undefined>]>, undefined>, v.DescriptionAction<string[], "Branch IDs being merged into current context">]>, undefined>;
|
|
480
481
|
readonly meta_observation: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.DescriptionAction<string, "Metacognitive observation about reasoning process">]>, undefined>;
|
|
481
482
|
readonly reasoning_depth: v.OptionalSchema<v.SchemaWithPipe<readonly [v.PicklistSchema<["shallow", "moderate", "deep"], undefined>, v.DescriptionAction<"shallow" | "moderate" | "deep", "Effort signal: how deep reasoning should go">]>, undefined>;
|
|
482
|
-
readonly session_id: v.
|
|
483
|
+
readonly session_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "Session ID must contain only letters, numbers, hyphens, and underscores">, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 100, undefined>, v.NotValueAction<string, "__global__", "Session ID '__global__' is retired; use an explicit named session">, v.DescriptionAction<string, "Required session identifier for state isolation. Thought history, branches, and statistics are scoped to this explicit named session.">]>;
|
|
483
484
|
readonly reset_state: v.OptionalSchema<v.SchemaWithPipe<readonly [v.BooleanSchema<undefined>, v.DescriptionAction<boolean, "When true, clears all state for the target session before processing this thought. The thought is then processed as the first in a fresh session.">]>, undefined>;
|
|
484
485
|
readonly tool_name: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.DescriptionAction<string, "Name of the tool being invoked (for tool_call thoughts)">]>, undefined>;
|
|
485
486
|
readonly tool_arguments: v.OptionalSchema<v.SchemaWithPipe<readonly [v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, v.DescriptionAction<{
|