@thelogicatelier/sylva 1.0.11 → 1.0.13
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/dist/awareness/braveSearch.js +6 -3
- package/dist/awareness/detector.d.ts +1 -0
- package/dist/awareness/detector.js +13 -33
- package/dist/awareness/index.js +165 -5
- package/dist/awareness/manifestParsers/goParsers.js +2 -11
- package/dist/awareness/manifestParsers/javaParsers.js +4 -22
- package/dist/awareness/manifestParsers/openclawParser.d.ts +16 -2
- package/dist/awareness/manifestParsers/openclawParser.js +532 -19
- package/dist/awareness/manifestParsers/packageJsonParser.js +2 -24
- package/dist/awareness/manifestParsers/pythonParsers.js +5 -21
- package/dist/awareness/manifestParsers/rustParsers.js +2 -12
- package/dist/awareness/manifestScanner.js +10 -69
- package/dist/awareness/sourceScanner.d.ts +5 -0
- package/dist/awareness/sourceScanner.js +179 -0
- package/dist/awareness/types.d.ts +1 -1
- package/dist/constants.d.ts +44 -0
- package/dist/constants.js +328 -1
- package/package.json +3 -2
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* OpenClaw manifest parser.
|
|
4
|
-
* Parses openclaw.json to extract
|
|
3
|
+
* OpenClaw manifest parser — deep extraction.
|
|
4
|
+
* Parses openclaw.json / .openclaw.json to extract:
|
|
5
|
+
* - Orchestrator signal with version from meta
|
|
6
|
+
* - Agent config (models, workspace, concurrency)
|
|
7
|
+
* - Hooks (internal event scripts with paths + descriptions)
|
|
8
|
+
* - Plugins (enabled extensions)
|
|
9
|
+
* - Gateway config (port, auth, denied commands)
|
|
10
|
+
* - Channels (policies, stream modes)
|
|
11
|
+
* - Tools (web search, fetch)
|
|
12
|
+
* - Commands / messages config
|
|
13
|
+
*
|
|
14
|
+
* Also scans the OpenClaw workspace directory for:
|
|
15
|
+
* - Skills (reusable .md workflows)
|
|
16
|
+
* - Subagents (background agent directories)
|
|
17
|
+
* - Workspace .md files (AGENTS.md, IDENTITY.md, HEARTBEAT.md, etc.)
|
|
18
|
+
* - Hook scripts
|
|
5
19
|
*/
|
|
6
20
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
21
|
if (k2 === undefined) k2 = k;
|
|
@@ -40,6 +54,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
40
54
|
exports.parseOpenclawJson = parseOpenclawJson;
|
|
41
55
|
const fs = __importStar(require("fs"));
|
|
42
56
|
const path = __importStar(require("path"));
|
|
57
|
+
// ---------------------
|
|
58
|
+
// Deep JSON extraction
|
|
59
|
+
// ---------------------
|
|
43
60
|
function parseOpenclawJson(manifest) {
|
|
44
61
|
const signals = [];
|
|
45
62
|
const content = fs.readFileSync(manifest.absolutePath, "utf-8");
|
|
@@ -54,50 +71,546 @@ function parseOpenclawJson(manifest) {
|
|
|
54
71
|
return signals;
|
|
55
72
|
}
|
|
56
73
|
const rootPath = path.dirname(manifest.relativePath) || ".";
|
|
57
|
-
//
|
|
74
|
+
// --- Extract OpenClaw version from meta ---
|
|
75
|
+
const meta = config.meta;
|
|
76
|
+
const openclawVersion = meta?.lastTouchedVersion
|
|
77
|
+
? {
|
|
78
|
+
value: String(meta.lastTouchedVersion),
|
|
79
|
+
certainty: "exact",
|
|
80
|
+
sourceFile: manifest.relativePath,
|
|
81
|
+
notes: "From meta.lastTouchedVersion in openclaw.json",
|
|
82
|
+
}
|
|
83
|
+
: undefined;
|
|
84
|
+
// --- Primary orchestrator signal ---
|
|
58
85
|
signals.push({
|
|
59
86
|
kind: "orchestrator",
|
|
60
87
|
frameworkId: "openclaw",
|
|
61
88
|
frameworkName: "OpenClaw",
|
|
89
|
+
...(openclawVersion ? { version: openclawVersion } : {}),
|
|
62
90
|
evidence: {
|
|
63
91
|
file: manifest.relativePath,
|
|
64
92
|
reason: "openclaw.json configuration file found",
|
|
65
|
-
excerpt:
|
|
93
|
+
excerpt: openclawVersion
|
|
94
|
+
? `OpenClaw version ${openclawVersion.value}`
|
|
95
|
+
: "OpenClaw orchestrator (version not specified in config)",
|
|
96
|
+
},
|
|
97
|
+
scope: { pathRoot: rootPath },
|
|
98
|
+
});
|
|
99
|
+
// --- Agent config ---
|
|
100
|
+
extractAgentConfig(config, manifest, rootPath, signals);
|
|
101
|
+
// --- Hooks ---
|
|
102
|
+
extractHooks(config, manifest, rootPath, signals);
|
|
103
|
+
// --- Plugins ---
|
|
104
|
+
extractPlugins(config, manifest, rootPath, signals);
|
|
105
|
+
// --- Gateway ---
|
|
106
|
+
extractGateway(config, manifest, rootPath, signals);
|
|
107
|
+
// --- Channels (expanded) ---
|
|
108
|
+
extractChannels(config, manifest, rootPath, signals);
|
|
109
|
+
// --- Tools (expanded) ---
|
|
110
|
+
extractTools(config, manifest, rootPath, signals);
|
|
111
|
+
// --- Commands + messages ---
|
|
112
|
+
extractCommandsAndMessages(config, manifest, rootPath, signals);
|
|
113
|
+
// --- Workspace scanning ---
|
|
114
|
+
scanWorkspace(config, manifest, rootPath, signals);
|
|
115
|
+
return signals;
|
|
116
|
+
}
|
|
117
|
+
// ---------------------
|
|
118
|
+
// Section extractors
|
|
119
|
+
// ---------------------
|
|
120
|
+
function extractAgentConfig(config, manifest, rootPath, signals) {
|
|
121
|
+
const agents = config.agents;
|
|
122
|
+
if (!agents || typeof agents !== "object")
|
|
123
|
+
return;
|
|
124
|
+
const defaults = agents.defaults;
|
|
125
|
+
if (!defaults || typeof defaults !== "object")
|
|
126
|
+
return;
|
|
127
|
+
// Primary model
|
|
128
|
+
const model = defaults.model;
|
|
129
|
+
const primaryModel = model?.primary ? String(model.primary) : undefined;
|
|
130
|
+
// Available models
|
|
131
|
+
const models = defaults.models;
|
|
132
|
+
const modelCatalog = models ? Object.keys(models) : [];
|
|
133
|
+
// Workspace path
|
|
134
|
+
const workspace = defaults.workspace ? String(defaults.workspace) : undefined;
|
|
135
|
+
// Concurrency
|
|
136
|
+
const maxConcurrent = typeof defaults.maxConcurrent === "number" ? defaults.maxConcurrent : undefined;
|
|
137
|
+
const subagentConfig = defaults.subagents;
|
|
138
|
+
const subagentMaxConcurrent = subagentConfig && typeof subagentConfig.maxConcurrent === "number"
|
|
139
|
+
? subagentConfig.maxConcurrent
|
|
140
|
+
: undefined;
|
|
141
|
+
// Compaction
|
|
142
|
+
const compaction = defaults.compaction;
|
|
143
|
+
const compactionMode = compaction?.mode ? String(compaction.mode) : undefined;
|
|
144
|
+
const details = [];
|
|
145
|
+
if (primaryModel)
|
|
146
|
+
details.push(`primary model: ${primaryModel}`);
|
|
147
|
+
if (modelCatalog.length > 0)
|
|
148
|
+
details.push(`${modelCatalog.length} model(s) available`);
|
|
149
|
+
if (workspace)
|
|
150
|
+
details.push(`workspace: ${workspace}`);
|
|
151
|
+
if (maxConcurrent)
|
|
152
|
+
details.push(`maxConcurrent: ${maxConcurrent}`);
|
|
153
|
+
if (subagentMaxConcurrent)
|
|
154
|
+
details.push(`subagent maxConcurrent: ${subagentMaxConcurrent}`);
|
|
155
|
+
if (compactionMode)
|
|
156
|
+
details.push(`compaction: ${compactionMode}`);
|
|
157
|
+
signals.push({
|
|
158
|
+
kind: "agent",
|
|
159
|
+
frameworkId: "openclaw-agent-config",
|
|
160
|
+
frameworkName: "OpenClaw Agent Config",
|
|
161
|
+
evidence: {
|
|
162
|
+
file: manifest.relativePath,
|
|
163
|
+
reason: "Agent configuration in openclaw.json",
|
|
164
|
+
excerpt: details.join("; "),
|
|
66
165
|
},
|
|
67
166
|
scope: { pathRoot: rootPath },
|
|
68
167
|
});
|
|
69
|
-
//
|
|
168
|
+
// Emit individual model entries for the LLM to know available models
|
|
169
|
+
if (modelCatalog.length > 0) {
|
|
170
|
+
signals.push({
|
|
171
|
+
kind: "agent",
|
|
172
|
+
frameworkId: "openclaw-model-catalog",
|
|
173
|
+
frameworkName: "OpenClaw Model Catalog",
|
|
174
|
+
evidence: {
|
|
175
|
+
file: manifest.relativePath,
|
|
176
|
+
reason: "Available LLM models configured in openclaw.json",
|
|
177
|
+
excerpt: modelCatalog.join(", "),
|
|
178
|
+
},
|
|
179
|
+
scope: { pathRoot: rootPath },
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function extractHooks(config, manifest, rootPath, signals) {
|
|
184
|
+
const hooks = config.hooks;
|
|
185
|
+
if (!hooks || typeof hooks !== "object")
|
|
186
|
+
return;
|
|
187
|
+
const internal = hooks.internal;
|
|
188
|
+
if (!internal || typeof internal !== "object")
|
|
189
|
+
return;
|
|
190
|
+
if (internal.enabled === false)
|
|
191
|
+
return;
|
|
192
|
+
const entries = internal.entries;
|
|
193
|
+
if (!entries || typeof entries !== "object")
|
|
194
|
+
return;
|
|
195
|
+
for (const [hookName, hookConfig] of Object.entries(entries)) {
|
|
196
|
+
if (!hookConfig || typeof hookConfig !== "object")
|
|
197
|
+
continue;
|
|
198
|
+
const enabled = hookConfig.enabled !== false; // default true
|
|
199
|
+
const hookPath = hookConfig.path ? String(hookConfig.path) : undefined;
|
|
200
|
+
const description = hookConfig.description ? String(hookConfig.description) : undefined;
|
|
201
|
+
const details = [`enabled: ${enabled}`];
|
|
202
|
+
if (hookPath)
|
|
203
|
+
details.push(`path: ${hookPath}`);
|
|
204
|
+
if (description)
|
|
205
|
+
details.push(`desc: ${description}`);
|
|
206
|
+
signals.push({
|
|
207
|
+
kind: "hook",
|
|
208
|
+
frameworkId: `openclaw-hook-${hookName}`,
|
|
209
|
+
frameworkName: `OpenClaw Hook: ${hookName}`,
|
|
210
|
+
evidence: {
|
|
211
|
+
file: manifest.relativePath,
|
|
212
|
+
reason: description || `Hook '${hookName}' configured in openclaw.json`,
|
|
213
|
+
excerpt: details.join("; "),
|
|
214
|
+
},
|
|
215
|
+
scope: { pathRoot: rootPath },
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function extractPlugins(config, manifest, rootPath, signals) {
|
|
220
|
+
const plugins = config.plugins;
|
|
221
|
+
if (!plugins || typeof plugins !== "object")
|
|
222
|
+
return;
|
|
223
|
+
const entries = plugins.entries;
|
|
224
|
+
if (!entries || typeof entries !== "object")
|
|
225
|
+
return;
|
|
226
|
+
for (const [pluginName, pluginConfig] of Object.entries(entries)) {
|
|
227
|
+
if (!pluginConfig || typeof pluginConfig !== "object")
|
|
228
|
+
continue;
|
|
229
|
+
const enabled = pluginConfig.enabled !== false;
|
|
230
|
+
signals.push({
|
|
231
|
+
kind: "plugin",
|
|
232
|
+
frameworkId: `openclaw-plugin-${pluginName}`,
|
|
233
|
+
frameworkName: `OpenClaw Plugin: ${pluginName}`,
|
|
234
|
+
evidence: {
|
|
235
|
+
file: manifest.relativePath,
|
|
236
|
+
reason: `Plugin '${pluginName}' ${enabled ? "enabled" : "disabled"} in openclaw.json`,
|
|
237
|
+
excerpt: `enabled: ${enabled}`,
|
|
238
|
+
},
|
|
239
|
+
scope: { pathRoot: rootPath },
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function extractGateway(config, manifest, rootPath, signals) {
|
|
244
|
+
const gateway = config.gateway;
|
|
245
|
+
if (!gateway || typeof gateway !== "object")
|
|
246
|
+
return;
|
|
247
|
+
const details = [];
|
|
248
|
+
if (gateway.port)
|
|
249
|
+
details.push(`port: ${gateway.port}`);
|
|
250
|
+
if (gateway.mode)
|
|
251
|
+
details.push(`mode: ${gateway.mode}`);
|
|
252
|
+
if (gateway.bind)
|
|
253
|
+
details.push(`bind: ${gateway.bind}`);
|
|
254
|
+
const auth = gateway.auth;
|
|
255
|
+
if (auth?.mode)
|
|
256
|
+
details.push(`auth: ${auth.mode}`);
|
|
257
|
+
const tailscale = gateway.tailscale;
|
|
258
|
+
if (tailscale?.mode)
|
|
259
|
+
details.push(`tailscale: ${tailscale.mode}`);
|
|
260
|
+
const nodes = gateway.nodes;
|
|
261
|
+
const denyCommands = nodes?.denyCommands;
|
|
262
|
+
if (denyCommands && Array.isArray(denyCommands)) {
|
|
263
|
+
details.push(`denied commands: ${denyCommands.join(", ")}`);
|
|
264
|
+
}
|
|
265
|
+
signals.push({
|
|
266
|
+
kind: "tooling",
|
|
267
|
+
frameworkId: "openclaw-gateway",
|
|
268
|
+
frameworkName: "OpenClaw Gateway",
|
|
269
|
+
evidence: {
|
|
270
|
+
file: manifest.relativePath,
|
|
271
|
+
reason: "Gateway configuration in openclaw.json",
|
|
272
|
+
excerpt: details.join("; "),
|
|
273
|
+
},
|
|
274
|
+
scope: { pathRoot: rootPath },
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
function extractChannels(config, manifest, rootPath, signals) {
|
|
278
|
+
const channels = config.channels;
|
|
279
|
+
if (!channels || typeof channels !== "object")
|
|
280
|
+
return;
|
|
281
|
+
for (const [channelName, channelConfig] of Object.entries(channels)) {
|
|
282
|
+
if (!channelConfig || typeof channelConfig !== "object")
|
|
283
|
+
continue;
|
|
284
|
+
const ch = channelConfig;
|
|
285
|
+
const details = [];
|
|
286
|
+
if (ch.enabled === false)
|
|
287
|
+
details.push("disabled");
|
|
288
|
+
if (ch.dmPolicy)
|
|
289
|
+
details.push(`dmPolicy: ${ch.dmPolicy}`);
|
|
290
|
+
if (ch.groupPolicy)
|
|
291
|
+
details.push(`groupPolicy: ${ch.groupPolicy}`);
|
|
292
|
+
if (ch.streamMode)
|
|
293
|
+
details.push(`streamMode: ${ch.streamMode}`);
|
|
294
|
+
if (ch.selfChatMode !== undefined)
|
|
295
|
+
details.push(`selfChat: ${ch.selfChatMode}`);
|
|
296
|
+
if (ch.debounceMs !== undefined)
|
|
297
|
+
details.push(`debounce: ${ch.debounceMs}ms`);
|
|
298
|
+
if (ch.mediaMaxMb)
|
|
299
|
+
details.push(`mediaMax: ${ch.mediaMaxMb}MB`);
|
|
300
|
+
signals.push({
|
|
301
|
+
kind: "tooling",
|
|
302
|
+
frameworkId: `openclaw-channel-${channelName}`,
|
|
303
|
+
frameworkName: `OpenClaw Channel: ${channelName}`,
|
|
304
|
+
evidence: {
|
|
305
|
+
file: manifest.relativePath,
|
|
306
|
+
reason: `Channel '${channelName}' configured in openclaw.json`,
|
|
307
|
+
excerpt: details.length > 0 ? details.join("; ") : `channel ${channelName} configured`,
|
|
308
|
+
},
|
|
309
|
+
scope: { pathRoot: rootPath },
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function extractTools(config, manifest, rootPath, signals) {
|
|
70
314
|
const tools = config.tools;
|
|
71
|
-
if (tools
|
|
72
|
-
|
|
315
|
+
if (!tools || typeof tools !== "object")
|
|
316
|
+
return;
|
|
317
|
+
for (const [toolName, toolConfig] of Object.entries(tools)) {
|
|
318
|
+
if (!toolConfig || typeof toolConfig !== "object")
|
|
319
|
+
continue;
|
|
320
|
+
// Build a summary of the tool's sub-capabilities
|
|
321
|
+
const subCapabilities = [];
|
|
322
|
+
for (const [subName, subConfig] of Object.entries(toolConfig)) {
|
|
323
|
+
if (subConfig && typeof subConfig === "object") {
|
|
324
|
+
const sc = subConfig;
|
|
325
|
+
const enabled = sc.enabled !== false;
|
|
326
|
+
subCapabilities.push(`${subName}: ${enabled ? "enabled" : "disabled"}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
signals.push({
|
|
330
|
+
kind: "tooling",
|
|
331
|
+
frameworkId: `openclaw-tool-${toolName}`,
|
|
332
|
+
frameworkName: `OpenClaw Tool: ${toolName}`,
|
|
333
|
+
evidence: {
|
|
334
|
+
file: manifest.relativePath,
|
|
335
|
+
reason: `Tool '${toolName}' configured in openclaw.json`,
|
|
336
|
+
excerpt: subCapabilities.length > 0 ? subCapabilities.join("; ") : `tool ${toolName} configured`,
|
|
337
|
+
},
|
|
338
|
+
scope: { pathRoot: rootPath },
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function extractCommandsAndMessages(config, manifest, rootPath, signals) {
|
|
343
|
+
const commands = config.commands;
|
|
344
|
+
if (commands && typeof commands === "object") {
|
|
345
|
+
const details = [];
|
|
346
|
+
if (commands.native)
|
|
347
|
+
details.push(`native: ${commands.native}`);
|
|
348
|
+
if (commands.nativeSkills)
|
|
349
|
+
details.push(`nativeSkills: ${commands.nativeSkills}`);
|
|
350
|
+
if (details.length > 0) {
|
|
73
351
|
signals.push({
|
|
74
352
|
kind: "tooling",
|
|
75
|
-
frameworkId:
|
|
76
|
-
frameworkName:
|
|
353
|
+
frameworkId: "openclaw-commands",
|
|
354
|
+
frameworkName: "OpenClaw Commands",
|
|
77
355
|
evidence: {
|
|
78
356
|
file: manifest.relativePath,
|
|
79
|
-
reason:
|
|
80
|
-
excerpt:
|
|
357
|
+
reason: "Command configuration in openclaw.json",
|
|
358
|
+
excerpt: details.join("; "),
|
|
81
359
|
},
|
|
82
360
|
scope: { pathRoot: rootPath },
|
|
83
361
|
});
|
|
84
362
|
}
|
|
85
363
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
364
|
+
const messages = config.messages;
|
|
365
|
+
if (messages && typeof messages === "object") {
|
|
366
|
+
const details = [];
|
|
367
|
+
if (messages.ackReactionScope)
|
|
368
|
+
details.push(`ackReactionScope: ${messages.ackReactionScope}`);
|
|
369
|
+
if (details.length > 0) {
|
|
90
370
|
signals.push({
|
|
91
371
|
kind: "tooling",
|
|
92
|
-
frameworkId:
|
|
93
|
-
frameworkName:
|
|
372
|
+
frameworkId: "openclaw-messages",
|
|
373
|
+
frameworkName: "OpenClaw Messages",
|
|
94
374
|
evidence: {
|
|
95
375
|
file: manifest.relativePath,
|
|
96
|
-
reason:
|
|
376
|
+
reason: "Message configuration in openclaw.json",
|
|
377
|
+
excerpt: details.join("; "),
|
|
97
378
|
},
|
|
98
379
|
scope: { pathRoot: rootPath },
|
|
99
380
|
});
|
|
100
381
|
}
|
|
101
382
|
}
|
|
102
|
-
|
|
383
|
+
}
|
|
384
|
+
// ---------------------
|
|
385
|
+
// Workspace scanner
|
|
386
|
+
// ---------------------
|
|
387
|
+
function scanWorkspace(config, manifest, rootPath, signals) {
|
|
388
|
+
// Resolve workspace directory:
|
|
389
|
+
// 1. From agents.defaults.workspace in config
|
|
390
|
+
// 2. Fallback to sibling "workspace/" directory relative to the config file
|
|
391
|
+
const agents = config.agents;
|
|
392
|
+
const defaults = agents?.defaults;
|
|
393
|
+
const configuredWorkspace = defaults?.workspace ? String(defaults.workspace) : undefined;
|
|
394
|
+
const configDir = path.dirname(manifest.absolutePath);
|
|
395
|
+
let workspaceDir;
|
|
396
|
+
if (configuredWorkspace) {
|
|
397
|
+
// Could be absolute or relative — try both
|
|
398
|
+
const candidate = path.isAbsolute(configuredWorkspace)
|
|
399
|
+
? configuredWorkspace
|
|
400
|
+
: path.resolve(configDir, configuredWorkspace);
|
|
401
|
+
if (existsAndIsDir(candidate)) {
|
|
402
|
+
workspaceDir = candidate;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (!workspaceDir) {
|
|
406
|
+
// Fallback: sibling "workspace/" directory
|
|
407
|
+
const fallback = path.join(configDir, "workspace");
|
|
408
|
+
if (existsAndIsDir(fallback)) {
|
|
409
|
+
workspaceDir = fallback;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (!workspaceDir)
|
|
413
|
+
return;
|
|
414
|
+
const workspaceRel = path.relative(path.dirname(manifest.absolutePath), workspaceDir);
|
|
415
|
+
// Scan workspace .md files (identity layer)
|
|
416
|
+
scanWorkspaceMdFiles(workspaceDir, workspaceRel, manifest, rootPath, signals);
|
|
417
|
+
// Scan skills
|
|
418
|
+
scanSkills(workspaceDir, workspaceRel, manifest, rootPath, signals);
|
|
419
|
+
// Scan subagents
|
|
420
|
+
scanSubagents(workspaceDir, workspaceRel, manifest, rootPath, signals);
|
|
421
|
+
}
|
|
422
|
+
function scanWorkspaceMdFiles(workspaceDir, workspaceRel, manifest, rootPath, signals) {
|
|
423
|
+
const WORKSPACE_FILES = {
|
|
424
|
+
"AGENTS.md": "Agent behavioral instructions and skill routing",
|
|
425
|
+
"IDENTITY.md": "Agent identity, personality, and avatar",
|
|
426
|
+
"HEARTBEAT.md": "Periodic awareness checklist (cron-like proactive checks)",
|
|
427
|
+
"MEMORY.md": "Long-term curated memory store",
|
|
428
|
+
"SOUL.md": "Agent personality and core values",
|
|
429
|
+
"USER.md": "User profile and preferences",
|
|
430
|
+
"TOOLS.md": "Tool configuration and capabilities",
|
|
431
|
+
"OPTIMIZATION.md": "Performance and cost optimization rules",
|
|
432
|
+
};
|
|
433
|
+
for (const [filename, description] of Object.entries(WORKSPACE_FILES)) {
|
|
434
|
+
const filePath = path.join(workspaceDir, filename);
|
|
435
|
+
if (!existsAndIsFile(filePath))
|
|
436
|
+
continue;
|
|
437
|
+
// For HEARTBEAT.md, check if it has actual tasks (non-empty, non-comment content)
|
|
438
|
+
if (filename === "HEARTBEAT.md") {
|
|
439
|
+
const heartbeatContent = safeReadFile(filePath);
|
|
440
|
+
const hasActiveTasks = heartbeatContent
|
|
441
|
+
? heartbeatContent.split("\n").some((line) => line.trim() && !line.trim().startsWith("#"))
|
|
442
|
+
: false;
|
|
443
|
+
signals.push({
|
|
444
|
+
kind: "heartbeat",
|
|
445
|
+
frameworkId: "openclaw-heartbeat",
|
|
446
|
+
frameworkName: "OpenClaw Heartbeat",
|
|
447
|
+
evidence: {
|
|
448
|
+
file: path.join(workspaceRel, filename),
|
|
449
|
+
reason: hasActiveTasks
|
|
450
|
+
? "HEARTBEAT.md found with active periodic tasks"
|
|
451
|
+
: "HEARTBEAT.md found but no active tasks configured",
|
|
452
|
+
excerpt: hasActiveTasks ? "Status: ACTIVE" : "Status: INACTIVE (empty/comments only)",
|
|
453
|
+
},
|
|
454
|
+
scope: { pathRoot: rootPath },
|
|
455
|
+
});
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
// For IDENTITY.md, extract the agent name if present
|
|
459
|
+
let excerpt = description;
|
|
460
|
+
if (filename === "IDENTITY.md") {
|
|
461
|
+
const identityContent = safeReadFile(filePath);
|
|
462
|
+
if (identityContent) {
|
|
463
|
+
const nameMatch = identityContent.match(/\*\*Name:\*\*\s*(.+)/);
|
|
464
|
+
if (nameMatch) {
|
|
465
|
+
excerpt = `Agent name: ${nameMatch[1].trim()} — ${description}`;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
signals.push({
|
|
470
|
+
kind: "agent",
|
|
471
|
+
frameworkId: `openclaw-workspace-${filename.replace(".md", "").toLowerCase()}`,
|
|
472
|
+
frameworkName: `OpenClaw Workspace: ${filename}`,
|
|
473
|
+
evidence: {
|
|
474
|
+
file: path.join(workspaceRel, filename),
|
|
475
|
+
reason: `Workspace file ${filename} found`,
|
|
476
|
+
excerpt,
|
|
477
|
+
},
|
|
478
|
+
scope: { pathRoot: rootPath },
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
function scanSkills(workspaceDir, workspaceRel, manifest, rootPath, signals) {
|
|
483
|
+
const skillsDir = path.join(workspaceDir, "skills");
|
|
484
|
+
if (!existsAndIsDir(skillsDir))
|
|
485
|
+
return;
|
|
486
|
+
let entries;
|
|
487
|
+
try {
|
|
488
|
+
entries = fs.readdirSync(skillsDir);
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
for (const entry of entries) {
|
|
494
|
+
if (!entry.endsWith(".md"))
|
|
495
|
+
continue;
|
|
496
|
+
if (entry === "SKILL_INDEX.md")
|
|
497
|
+
continue; // Index file, not a skill itself
|
|
498
|
+
const skillPath = path.join(skillsDir, entry);
|
|
499
|
+
if (!existsAndIsFile(skillPath))
|
|
500
|
+
continue;
|
|
501
|
+
// Read first meaningful line for description
|
|
502
|
+
const content = safeReadFile(skillPath);
|
|
503
|
+
let description = `Skill workflow: ${entry.replace(".md", "")}`;
|
|
504
|
+
if (content) {
|
|
505
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
506
|
+
const firstLine = lines[0] || "";
|
|
507
|
+
if (firstLine.startsWith("#")) {
|
|
508
|
+
description = firstLine.replace(/^#+\s*/, "").trim();
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
const skillName = entry
|
|
512
|
+
.replace(".md", "")
|
|
513
|
+
.replace(/-/g, " ")
|
|
514
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
515
|
+
signals.push({
|
|
516
|
+
kind: "skill",
|
|
517
|
+
frameworkId: `openclaw-skill-${entry.replace(".md", "")}`,
|
|
518
|
+
frameworkName: `OpenClaw Skill: ${skillName}`,
|
|
519
|
+
evidence: {
|
|
520
|
+
file: path.join(workspaceRel, "skills", entry),
|
|
521
|
+
reason: description,
|
|
522
|
+
excerpt: `Skill file: ${entry}`,
|
|
523
|
+
},
|
|
524
|
+
scope: { pathRoot: rootPath },
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function scanSubagents(workspaceDir, workspaceRel, manifest, rootPath, signals) {
|
|
529
|
+
const subagentsDir = path.join(workspaceDir, "subagents");
|
|
530
|
+
if (!existsAndIsDir(subagentsDir))
|
|
531
|
+
return;
|
|
532
|
+
let entries;
|
|
533
|
+
try {
|
|
534
|
+
entries = fs.readdirSync(subagentsDir);
|
|
535
|
+
}
|
|
536
|
+
catch {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
for (const entry of entries) {
|
|
540
|
+
const entryPath = path.join(subagentsDir, entry);
|
|
541
|
+
if (!existsAndIsDir(entryPath))
|
|
542
|
+
continue;
|
|
543
|
+
// List files in the subagent directory
|
|
544
|
+
let subFiles;
|
|
545
|
+
try {
|
|
546
|
+
subFiles = fs.readdirSync(entryPath).filter((f) => {
|
|
547
|
+
const fp = path.join(entryPath, f);
|
|
548
|
+
try {
|
|
549
|
+
return fs.statSync(fp).isFile() && !f.startsWith(".");
|
|
550
|
+
}
|
|
551
|
+
catch {
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
catch {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
// Filter out __pycache__ etc
|
|
560
|
+
const meaningfulFiles = subFiles.filter((f) => !f.endsWith(".pyc") && f !== "__pycache__");
|
|
561
|
+
// Try to read protocol.md for description
|
|
562
|
+
let description = `Subagent: ${entry}`;
|
|
563
|
+
const protocolPath = path.join(entryPath, "protocol.md");
|
|
564
|
+
if (existsAndIsFile(protocolPath)) {
|
|
565
|
+
const content = safeReadFile(protocolPath);
|
|
566
|
+
if (content) {
|
|
567
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
568
|
+
const firstLine = lines[0] || "";
|
|
569
|
+
if (firstLine.startsWith("#")) {
|
|
570
|
+
description = firstLine.replace(/^#+\s*/, "").trim();
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const subagentName = entry.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
575
|
+
signals.push({
|
|
576
|
+
kind: "subagent",
|
|
577
|
+
frameworkId: `openclaw-subagent-${entry}`,
|
|
578
|
+
frameworkName: `OpenClaw Subagent: ${subagentName}`,
|
|
579
|
+
evidence: {
|
|
580
|
+
file: path.join(workspaceRel, "subagents", entry),
|
|
581
|
+
reason: description,
|
|
582
|
+
excerpt: meaningfulFiles.length > 0
|
|
583
|
+
? `Files: ${meaningfulFiles.join(", ")}`
|
|
584
|
+
: "Empty subagent directory",
|
|
585
|
+
},
|
|
586
|
+
scope: { pathRoot: rootPath },
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
// ---------------------
|
|
591
|
+
// Helpers
|
|
592
|
+
// ---------------------
|
|
593
|
+
function existsAndIsDir(p) {
|
|
594
|
+
try {
|
|
595
|
+
return fs.statSync(p).isDirectory();
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function existsAndIsFile(p) {
|
|
602
|
+
try {
|
|
603
|
+
return fs.statSync(p).isFile();
|
|
604
|
+
}
|
|
605
|
+
catch {
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
function safeReadFile(p) {
|
|
610
|
+
try {
|
|
611
|
+
return fs.readFileSync(p, "utf-8");
|
|
612
|
+
}
|
|
613
|
+
catch {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
103
616
|
}
|
|
@@ -41,29 +41,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
41
41
|
exports.parsePackageJson = parsePackageJson;
|
|
42
42
|
const fs = __importStar(require("fs"));
|
|
43
43
|
const path = __importStar(require("path"));
|
|
44
|
-
|
|
45
|
-
const FRAMEWORK_DETECTION_RULES = [
|
|
46
|
-
{ depPattern: "@angular/core", frameworkId: "angular", frameworkName: "Angular" },
|
|
47
|
-
{ depPattern: "react", frameworkId: "react", frameworkName: "React" },
|
|
48
|
-
{ depPattern: "react-dom", frameworkId: "react-dom", frameworkName: "React DOM" },
|
|
49
|
-
{ depPattern: "next", frameworkId: "nextjs", frameworkName: "Next.js" },
|
|
50
|
-
{ depPattern: "vue", frameworkId: "vue", frameworkName: "Vue.js" },
|
|
51
|
-
{ depPattern: "nuxt", frameworkId: "nuxt", frameworkName: "Nuxt" },
|
|
52
|
-
{ depPattern: "@sveltejs/kit", frameworkId: "sveltekit", frameworkName: "SvelteKit" },
|
|
53
|
-
{ depPattern: "svelte", frameworkId: "svelte", frameworkName: "Svelte" },
|
|
54
|
-
{ depPattern: "express", frameworkId: "express", frameworkName: "Express" },
|
|
55
|
-
{ depPattern: "@nestjs/core", frameworkId: "nestjs", frameworkName: "NestJS" },
|
|
56
|
-
{ depPattern: "fastify", frameworkId: "fastify", frameworkName: "Fastify" },
|
|
57
|
-
{ depPattern: "koa", frameworkId: "koa", frameworkName: "Koa" },
|
|
58
|
-
{ depPattern: "typescript", frameworkId: "typescript", frameworkName: "TypeScript" },
|
|
59
|
-
{ depPattern: "@ax-llm/ax", frameworkId: "ax-llm", frameworkName: "Ax-LLM" },
|
|
60
|
-
{ depPattern: "electron", frameworkId: "electron", frameworkName: "Electron" },
|
|
61
|
-
{ depPattern: "react-native", frameworkId: "react-native", frameworkName: "React Native" },
|
|
62
|
-
{ depPattern: "tailwindcss", frameworkId: "tailwindcss", frameworkName: "Tailwind CSS" },
|
|
63
|
-
{ depPattern: "vite", frameworkId: "vite", frameworkName: "Vite" },
|
|
64
|
-
{ depPattern: "webpack", frameworkId: "webpack", frameworkName: "Webpack" },
|
|
65
|
-
{ depPattern: "esbuild", frameworkId: "esbuild", frameworkName: "esbuild" },
|
|
66
|
-
];
|
|
44
|
+
const constants_1 = require("../../constants");
|
|
67
45
|
/**
|
|
68
46
|
* Determine version certainty from a semver-like string.
|
|
69
47
|
*/
|
|
@@ -126,7 +104,7 @@ function parsePackageJson(manifest) {
|
|
|
126
104
|
scope: { pathRoot: rootPath },
|
|
127
105
|
});
|
|
128
106
|
// Detect frameworks from dependencies
|
|
129
|
-
for (const rule of
|
|
107
|
+
for (const rule of constants_1.NPM_FRAMEWORKS) {
|
|
130
108
|
const depName = typeof rule.depPattern === "string" ? rule.depPattern : undefined;
|
|
131
109
|
let matchedDep;
|
|
132
110
|
let matchedVersion;
|