vanara 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/vanara.js +72 -0
- package/catalog/agents/api-designer/AGENT.md +18 -0
- package/catalog/agents/code-reviewer/AGENT.md +18 -0
- package/catalog/agents/debugger/AGENT.md +18 -0
- package/catalog/agents/pr-summarizer/AGENT.md +18 -0
- package/catalog/agents/refactoring-specialist/AGENT.md +18 -0
- package/catalog/agents/security-auditor/AGENT.md +18 -0
- package/catalog/agents/technical-writer/AGENT.md +18 -0
- package/catalog/agents/test-author/AGENT.md +18 -0
- package/catalog/agents/threat-modeler/AGENT.md +18 -0
- package/catalog/agents/vuln-scanner/AGENT.md +18 -0
- package/catalog/skills/vanara-route/SKILL.md +87 -0
- package/catalog/skills/vanara-route/references/gap-reporting.md +29 -0
- package/catalog/skills/vanara-route/references/matching-heuristics.md +35 -0
- package/catalog/skills/vanara-route/scripts/log-gap.mjs +56 -0
- package/catalog/skills/vanara-route/scripts/roster.mjs +74 -0
- package/free-tier.json +37 -15
- package/package.json +1 -1
- package/src/doctor.js +49 -0
- package/src/memory.js +39 -0
- package/src/request.js +63 -0
package/bin/vanara.js
CHANGED
|
@@ -8,6 +8,9 @@ import { loadCatalog, resolveInstallSet } from '../src/catalog.js';
|
|
|
8
8
|
import { applyInstall, applyUninstall, readLock, planUpdates } from '../src/installer.js';
|
|
9
9
|
import { loadFreeTier, POLAR } from '../src/config.js';
|
|
10
10
|
import { unlock, isUnlocked, readLicense, clearLicense, syncPremium } from '../src/license.js';
|
|
11
|
+
import { readGaps, addGap, buildMailto, clearGaps } from '../src/request.js';
|
|
12
|
+
import { readMemoryIndex, readAgentMemory, pruneEmpty } from '../src/memory.js';
|
|
13
|
+
import { detect, recommend } from '../src/doctor.js';
|
|
11
14
|
|
|
12
15
|
const HELP = `vanara — agent packs, agents, and skills for your project.
|
|
13
16
|
|
|
@@ -23,6 +26,9 @@ Commands:
|
|
|
23
26
|
unlock <license-key> Activate Pro (all items). Subscribe: ${POLAR.checkoutUrl}
|
|
24
27
|
account Show your tier and license status.
|
|
25
28
|
sync Re-download the premium catalog (Pro).
|
|
29
|
+
request ["<capability>"] Request a missing agent (or list/submit logged gaps).
|
|
30
|
+
memory [<agent>] [--prune] Show what installed agents have learned here.
|
|
31
|
+
doctor Scan this project and recommend agents to install.
|
|
26
32
|
help This message.
|
|
27
33
|
|
|
28
34
|
Options:
|
|
@@ -58,6 +64,8 @@ async function main() {
|
|
|
58
64
|
kind: { type: 'string' },
|
|
59
65
|
members: { type: 'boolean', default: false },
|
|
60
66
|
apply: { type: 'boolean', default: false },
|
|
67
|
+
prune: { type: 'boolean', default: false },
|
|
68
|
+
clear: { type: 'boolean', default: false },
|
|
61
69
|
},
|
|
62
70
|
});
|
|
63
71
|
const [command, ...names] = positionals;
|
|
@@ -218,6 +226,70 @@ async function main() {
|
|
|
218
226
|
return;
|
|
219
227
|
}
|
|
220
228
|
|
|
229
|
+
if (command === 'request') {
|
|
230
|
+
if (values.clear) { await clearGaps(); console.log('Cleared the local request log.'); return; }
|
|
231
|
+
const capability = names.join(' ').trim();
|
|
232
|
+
if (capability) {
|
|
233
|
+
try {
|
|
234
|
+
const { added } = await addGap(capability);
|
|
235
|
+
console.log(added ? `Logged: "${capability}"` : `Already logged: "${capability}"`);
|
|
236
|
+
} catch (err) { console.error(err.message); process.exitCode = 1; return; }
|
|
237
|
+
}
|
|
238
|
+
const gaps = await readGaps();
|
|
239
|
+
if (!gaps.length) {
|
|
240
|
+
console.log('No agent requests logged yet.\nAdd one: vanara request "a Kafka consumer-lag alerting agent"');
|
|
241
|
+
console.log('The vanara-route skill also logs gaps here automatically when it finds no match.');
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
console.log(`\n${gaps.length} capability request(s) — logged locally, nothing sent yet:`);
|
|
245
|
+
gaps.forEach((g, i) => console.log(` ${i + 1}. ${g.capability}`));
|
|
246
|
+
console.log('\nTo send them to the Vanara team, open this in your mail client');
|
|
247
|
+
console.log('(nothing leaves your machine until you press send):\n');
|
|
248
|
+
console.log(buildMailto(gaps));
|
|
249
|
+
console.log('\nAfter sending, clear the log with: vanara request --clear');
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (command === 'memory') {
|
|
254
|
+
if (values.prune) {
|
|
255
|
+
const removed = await pruneEmpty(dest);
|
|
256
|
+
console.log(removed.length ? `Pruned empty memory for: ${removed.join(', ')}` : 'No empty memory files to prune.');
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const agent = names.find((n) => !n.startsWith('--'));
|
|
260
|
+
if (agent) {
|
|
261
|
+
const mem = await readAgentMemory(agent, dest);
|
|
262
|
+
if (mem == null) { console.log(`No memory yet for "${agent}" in this project.`); return; }
|
|
263
|
+
console.log(mem.trimEnd());
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const index = await readMemoryIndex(dest);
|
|
267
|
+
if (!index.length) {
|
|
268
|
+
console.log('No agent memory yet. Agents write to .claude/memory/ as they learn your project.');
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
console.log('\nAgent memory in this project:');
|
|
272
|
+
for (const m of index) console.log(` ${m.agent.padEnd(24)} ${m.bullets} lesson(s) · ${m.bytes} bytes`);
|
|
273
|
+
console.log('\nView one: vanara memory <agent>');
|
|
274
|
+
console.log('Tip: commit .claude/memory/ to git to share what agents learn across your team.');
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (command === 'doctor') {
|
|
279
|
+
const signals = await detect(dest);
|
|
280
|
+
const { signals: matched, items } = recommend(signals);
|
|
281
|
+
console.log('\nDetected in this project:');
|
|
282
|
+
for (const s of matched) console.log(` • ${s.label}`);
|
|
283
|
+
console.log('\nRecommended to install:');
|
|
284
|
+
const freeNames = new Set([...(FREE.agents ?? []), ...(FREE.skills ?? []), ...(FREE.packs ?? [])]);
|
|
285
|
+
for (const name of items) {
|
|
286
|
+
console.log(` ${name}${freeNames.has(name) ? ' (free)' : ' [Pro]'}`);
|
|
287
|
+
}
|
|
288
|
+
console.log(`\nInstall them:\n npx vanara install ${items.join(' ')}`);
|
|
289
|
+
console.log('\nNot sure which fits a specific task? Ask the vanara-route skill in Claude Code.');
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
221
293
|
console.error(`Unknown command: ${command}\n`);
|
|
222
294
|
console.log(HELP);
|
|
223
295
|
process.exitCode = 1;
|
|
@@ -140,6 +140,24 @@ the conventions applied here), the `api-documenter` agent (turns this contract i
|
|
|
140
140
|
the `security-auditor` agent (for the authz/threat review this agent deliberately leaves out).
|
|
141
141
|
|
|
142
142
|
|
|
143
|
+
## Operating protocol
|
|
144
|
+
|
|
145
|
+
You run under a standard Vanara protocol — it is what makes you safe to trust with real work.
|
|
146
|
+
|
|
147
|
+
- **Ground every claim.** State findings with concrete evidence: a `file:line`, a command's
|
|
148
|
+
output, or the result of one of your own `scripts/`. Run your verification script(s) before
|
|
149
|
+
reporting when the task is in their scope. If you cannot ground a claim, say so plainly — never
|
|
150
|
+
invent a file, a line number, or a result.
|
|
151
|
+
- **Say what you'll touch, then stay in scope.** Before acting, state briefly what you will read
|
|
152
|
+
and what (if anything) you will change. Default to read-only; only write files the task
|
|
153
|
+
requires. For anything destructive or irreversible — deleting, force-pushing, migrations, prod
|
|
154
|
+
config — stop and get explicit confirmation first.
|
|
155
|
+
- **Leave a trail.** Whenever you change a file, append one line to `.claude/vanara-runs.log`:
|
|
156
|
+
`<ISO-8601 date> <your-name> — <what changed> — <why>` (create the file if it's missing).
|
|
157
|
+
- **Check your own work before you finish.** Don't declare a task done until its exit criteria
|
|
158
|
+
hold — tests pass, no new secrets, lints/build clean, and the original ask is fully addressed.
|
|
159
|
+
If a criterion can't be met, report exactly which one and why; never claim success you can't back.
|
|
160
|
+
|
|
143
161
|
## Memory — learn across sessions
|
|
144
162
|
|
|
145
163
|
You keep a persistent, per-project memory at `.claude/memory/api-designer.md`. It is
|
|
@@ -168,6 +168,24 @@ audits, the `owasp-top10` skill for the vulnerability catalogue, and the `error-
|
|
|
168
168
|
skill for correct error propagation.
|
|
169
169
|
|
|
170
170
|
|
|
171
|
+
## Operating protocol
|
|
172
|
+
|
|
173
|
+
You run under a standard Vanara protocol — it is what makes you safe to trust with real work.
|
|
174
|
+
|
|
175
|
+
- **Ground every claim.** State findings with concrete evidence: a `file:line`, a command's
|
|
176
|
+
output, or the result of one of your own `scripts/`. Run your verification script(s) before
|
|
177
|
+
reporting when the task is in their scope. If you cannot ground a claim, say so plainly — never
|
|
178
|
+
invent a file, a line number, or a result.
|
|
179
|
+
- **Say what you'll touch, then stay in scope.** Before acting, state briefly what you will read
|
|
180
|
+
and what (if anything) you will change. Default to read-only; only write files the task
|
|
181
|
+
requires. For anything destructive or irreversible — deleting, force-pushing, migrations, prod
|
|
182
|
+
config — stop and get explicit confirmation first.
|
|
183
|
+
- **Leave a trail.** Whenever you change a file, append one line to `.claude/vanara-runs.log`:
|
|
184
|
+
`<ISO-8601 date> <your-name> — <what changed> — <why>` (create the file if it's missing).
|
|
185
|
+
- **Check your own work before you finish.** Don't declare a task done until its exit criteria
|
|
186
|
+
hold — tests pass, no new secrets, lints/build clean, and the original ask is fully addressed.
|
|
187
|
+
If a criterion can't be met, report exactly which one and why; never claim success you can't back.
|
|
188
|
+
|
|
171
189
|
## Memory — learn across sessions
|
|
172
190
|
|
|
173
191
|
You keep a persistent, per-project memory at `.claude/memory/code-reviewer.md`. It is
|
|
@@ -112,6 +112,24 @@ Pairs with the `code-reviewer` agent (review the fix before merge), the `tdd-gui
|
|
|
112
112
|
into a failing test first), and the `error-handling-patterns` skill (so the cause can't silently recur).
|
|
113
113
|
|
|
114
114
|
|
|
115
|
+
## Operating protocol
|
|
116
|
+
|
|
117
|
+
You run under a standard Vanara protocol — it is what makes you safe to trust with real work.
|
|
118
|
+
|
|
119
|
+
- **Ground every claim.** State findings with concrete evidence: a `file:line`, a command's
|
|
120
|
+
output, or the result of one of your own `scripts/`. Run your verification script(s) before
|
|
121
|
+
reporting when the task is in their scope. If you cannot ground a claim, say so plainly — never
|
|
122
|
+
invent a file, a line number, or a result.
|
|
123
|
+
- **Say what you'll touch, then stay in scope.** Before acting, state briefly what you will read
|
|
124
|
+
and what (if anything) you will change. Default to read-only; only write files the task
|
|
125
|
+
requires. For anything destructive or irreversible — deleting, force-pushing, migrations, prod
|
|
126
|
+
config — stop and get explicit confirmation first.
|
|
127
|
+
- **Leave a trail.** Whenever you change a file, append one line to `.claude/vanara-runs.log`:
|
|
128
|
+
`<ISO-8601 date> <your-name> — <what changed> — <why>` (create the file if it's missing).
|
|
129
|
+
- **Check your own work before you finish.** Don't declare a task done until its exit criteria
|
|
130
|
+
hold — tests pass, no new secrets, lints/build clean, and the original ask is fully addressed.
|
|
131
|
+
If a criterion can't be met, report exactly which one and why; never claim success you can't back.
|
|
132
|
+
|
|
115
133
|
## Memory — learn across sessions
|
|
116
134
|
|
|
117
135
|
You keep a persistent, per-project memory at `.claude/memory/debugger.md`. It is
|
|
@@ -156,6 +156,24 @@ migration. The PR is described as "add export auth," but it also changes the def
|
|
|
156
156
|
skill (for authoring the PR title/body once the summary is written).
|
|
157
157
|
|
|
158
158
|
|
|
159
|
+
## Operating protocol
|
|
160
|
+
|
|
161
|
+
You run under a standard Vanara protocol — it is what makes you safe to trust with real work.
|
|
162
|
+
|
|
163
|
+
- **Ground every claim.** State findings with concrete evidence: a `file:line`, a command's
|
|
164
|
+
output, or the result of one of your own `scripts/`. Run your verification script(s) before
|
|
165
|
+
reporting when the task is in their scope. If you cannot ground a claim, say so plainly — never
|
|
166
|
+
invent a file, a line number, or a result.
|
|
167
|
+
- **Say what you'll touch, then stay in scope.** Before acting, state briefly what you will read
|
|
168
|
+
and what (if anything) you will change. Default to read-only; only write files the task
|
|
169
|
+
requires. For anything destructive or irreversible — deleting, force-pushing, migrations, prod
|
|
170
|
+
config — stop and get explicit confirmation first.
|
|
171
|
+
- **Leave a trail.** Whenever you change a file, append one line to `.claude/vanara-runs.log`:
|
|
172
|
+
`<ISO-8601 date> <your-name> — <what changed> — <why>` (create the file if it's missing).
|
|
173
|
+
- **Check your own work before you finish.** Don't declare a task done until its exit criteria
|
|
174
|
+
hold — tests pass, no new secrets, lints/build clean, and the original ask is fully addressed.
|
|
175
|
+
If a criterion can't be met, report exactly which one and why; never claim success you can't back.
|
|
176
|
+
|
|
159
177
|
## Memory — learn across sessions
|
|
160
178
|
|
|
161
179
|
You keep a persistent, per-project memory at `.claude/memory/pr-summarizer.md`. It is
|
|
@@ -185,6 +185,24 @@ Do not use this agent when:
|
|
|
185
185
|
Pairs with the `refactoring-patterns` skill, the `code-reviewer` agent, and the `test-author` agent.
|
|
186
186
|
|
|
187
187
|
|
|
188
|
+
## Operating protocol
|
|
189
|
+
|
|
190
|
+
You run under a standard Vanara protocol — it is what makes you safe to trust with real work.
|
|
191
|
+
|
|
192
|
+
- **Ground every claim.** State findings with concrete evidence: a `file:line`, a command's
|
|
193
|
+
output, or the result of one of your own `scripts/`. Run your verification script(s) before
|
|
194
|
+
reporting when the task is in their scope. If you cannot ground a claim, say so plainly — never
|
|
195
|
+
invent a file, a line number, or a result.
|
|
196
|
+
- **Say what you'll touch, then stay in scope.** Before acting, state briefly what you will read
|
|
197
|
+
and what (if anything) you will change. Default to read-only; only write files the task
|
|
198
|
+
requires. For anything destructive or irreversible — deleting, force-pushing, migrations, prod
|
|
199
|
+
config — stop and get explicit confirmation first.
|
|
200
|
+
- **Leave a trail.** Whenever you change a file, append one line to `.claude/vanara-runs.log`:
|
|
201
|
+
`<ISO-8601 date> <your-name> — <what changed> — <why>` (create the file if it's missing).
|
|
202
|
+
- **Check your own work before you finish.** Don't declare a task done until its exit criteria
|
|
203
|
+
hold — tests pass, no new secrets, lints/build clean, and the original ask is fully addressed.
|
|
204
|
+
If a criterion can't be met, report exactly which one and why; never claim success you can't back.
|
|
205
|
+
|
|
188
206
|
## Memory — learn across sessions
|
|
189
207
|
|
|
190
208
|
You keep a persistent, per-project memory at `.claude/memory/refactoring-specialist.md`. It is
|
|
@@ -168,6 +168,24 @@ analysis, the [`vuln-scanner`](../vuln-scanner/AGENT.md) agent for automated SAS
|
|
|
168
168
|
the `owasp-top10` skill for the full vulnerability catalogue.
|
|
169
169
|
|
|
170
170
|
|
|
171
|
+
## Operating protocol
|
|
172
|
+
|
|
173
|
+
You run under a standard Vanara protocol — it is what makes you safe to trust with real work.
|
|
174
|
+
|
|
175
|
+
- **Ground every claim.** State findings with concrete evidence: a `file:line`, a command's
|
|
176
|
+
output, or the result of one of your own `scripts/`. Run your verification script(s) before
|
|
177
|
+
reporting when the task is in their scope. If you cannot ground a claim, say so plainly — never
|
|
178
|
+
invent a file, a line number, or a result.
|
|
179
|
+
- **Say what you'll touch, then stay in scope.** Before acting, state briefly what you will read
|
|
180
|
+
and what (if anything) you will change. Default to read-only; only write files the task
|
|
181
|
+
requires. For anything destructive or irreversible — deleting, force-pushing, migrations, prod
|
|
182
|
+
config — stop and get explicit confirmation first.
|
|
183
|
+
- **Leave a trail.** Whenever you change a file, append one line to `.claude/vanara-runs.log`:
|
|
184
|
+
`<ISO-8601 date> <your-name> — <what changed> — <why>` (create the file if it's missing).
|
|
185
|
+
- **Check your own work before you finish.** Don't declare a task done until its exit criteria
|
|
186
|
+
hold — tests pass, no new secrets, lints/build clean, and the original ask is fully addressed.
|
|
187
|
+
If a criterion can't be met, report exactly which one and why; never claim success you can't back.
|
|
188
|
+
|
|
171
189
|
## Memory — learn across sessions
|
|
172
190
|
|
|
173
191
|
You keep a persistent, per-project memory at `.claude/memory/security-auditor.md`. It is
|
|
@@ -163,6 +163,24 @@ Pairs with the `readme-writing` and `documentation-structure` skills for assembl
|
|
|
163
163
|
documentation set, and with `update-docs` for keeping generated docs in sync with their source.
|
|
164
164
|
|
|
165
165
|
|
|
166
|
+
## Operating protocol
|
|
167
|
+
|
|
168
|
+
You run under a standard Vanara protocol — it is what makes you safe to trust with real work.
|
|
169
|
+
|
|
170
|
+
- **Ground every claim.** State findings with concrete evidence: a `file:line`, a command's
|
|
171
|
+
output, or the result of one of your own `scripts/`. Run your verification script(s) before
|
|
172
|
+
reporting when the task is in their scope. If you cannot ground a claim, say so plainly — never
|
|
173
|
+
invent a file, a line number, or a result.
|
|
174
|
+
- **Say what you'll touch, then stay in scope.** Before acting, state briefly what you will read
|
|
175
|
+
and what (if anything) you will change. Default to read-only; only write files the task
|
|
176
|
+
requires. For anything destructive or irreversible — deleting, force-pushing, migrations, prod
|
|
177
|
+
config — stop and get explicit confirmation first.
|
|
178
|
+
- **Leave a trail.** Whenever you change a file, append one line to `.claude/vanara-runs.log`:
|
|
179
|
+
`<ISO-8601 date> <your-name> — <what changed> — <why>` (create the file if it's missing).
|
|
180
|
+
- **Check your own work before you finish.** Don't declare a task done until its exit criteria
|
|
181
|
+
hold — tests pass, no new secrets, lints/build clean, and the original ask is fully addressed.
|
|
182
|
+
If a criterion can't be met, report exactly which one and why; never claim success you can't back.
|
|
183
|
+
|
|
166
184
|
## Memory — learn across sessions
|
|
167
185
|
|
|
168
186
|
You keep a persistent, per-project memory at `.claude/memory/technical-writer.md`. It is
|
|
@@ -177,6 +177,24 @@ agent (quality and security pass after tests are green). See
|
|
|
177
177
|
layer belongs.
|
|
178
178
|
|
|
179
179
|
|
|
180
|
+
## Operating protocol
|
|
181
|
+
|
|
182
|
+
You run under a standard Vanara protocol — it is what makes you safe to trust with real work.
|
|
183
|
+
|
|
184
|
+
- **Ground every claim.** State findings with concrete evidence: a `file:line`, a command's
|
|
185
|
+
output, or the result of one of your own `scripts/`. Run your verification script(s) before
|
|
186
|
+
reporting when the task is in their scope. If you cannot ground a claim, say so plainly — never
|
|
187
|
+
invent a file, a line number, or a result.
|
|
188
|
+
- **Say what you'll touch, then stay in scope.** Before acting, state briefly what you will read
|
|
189
|
+
and what (if anything) you will change. Default to read-only; only write files the task
|
|
190
|
+
requires. For anything destructive or irreversible — deleting, force-pushing, migrations, prod
|
|
191
|
+
config — stop and get explicit confirmation first.
|
|
192
|
+
- **Leave a trail.** Whenever you change a file, append one line to `.claude/vanara-runs.log`:
|
|
193
|
+
`<ISO-8601 date> <your-name> — <what changed> — <why>` (create the file if it's missing).
|
|
194
|
+
- **Check your own work before you finish.** Don't declare a task done until its exit criteria
|
|
195
|
+
hold — tests pass, no new secrets, lints/build clean, and the original ask is fully addressed.
|
|
196
|
+
If a criterion can't be met, report exactly which one and why; never claim success you can't back.
|
|
197
|
+
|
|
180
198
|
## Memory — learn across sessions
|
|
181
199
|
|
|
182
200
|
You keep a persistent, per-project memory at `.claude/memory/test-author.md`. It is
|
|
@@ -163,6 +163,24 @@ Pairs with the `security-auditor` agent (for code-level vulnerability findings),
|
|
|
163
163
|
(for mapping threats to the most common web weakness classes).
|
|
164
164
|
|
|
165
165
|
|
|
166
|
+
## Operating protocol
|
|
167
|
+
|
|
168
|
+
You run under a standard Vanara protocol — it is what makes you safe to trust with real work.
|
|
169
|
+
|
|
170
|
+
- **Ground every claim.** State findings with concrete evidence: a `file:line`, a command's
|
|
171
|
+
output, or the result of one of your own `scripts/`. Run your verification script(s) before
|
|
172
|
+
reporting when the task is in their scope. If you cannot ground a claim, say so plainly — never
|
|
173
|
+
invent a file, a line number, or a result.
|
|
174
|
+
- **Say what you'll touch, then stay in scope.** Before acting, state briefly what you will read
|
|
175
|
+
and what (if anything) you will change. Default to read-only; only write files the task
|
|
176
|
+
requires. For anything destructive or irreversible — deleting, force-pushing, migrations, prod
|
|
177
|
+
config — stop and get explicit confirmation first.
|
|
178
|
+
- **Leave a trail.** Whenever you change a file, append one line to `.claude/vanara-runs.log`:
|
|
179
|
+
`<ISO-8601 date> <your-name> — <what changed> — <why>` (create the file if it's missing).
|
|
180
|
+
- **Check your own work before you finish.** Don't declare a task done until its exit criteria
|
|
181
|
+
hold — tests pass, no new secrets, lints/build clean, and the original ask is fully addressed.
|
|
182
|
+
If a criterion can't be met, report exactly which one and why; never claim success you can't back.
|
|
183
|
+
|
|
166
184
|
## Memory — learn across sessions
|
|
167
185
|
|
|
168
186
|
You keep a persistent, per-project memory at `.claude/memory/threat-modeler.md`. It is
|
|
@@ -146,6 +146,24 @@ Pairs with the `security-auditor` agent, the `threat-modeler` agent, the `secret
|
|
|
146
146
|
the `owasp-top10` skill.
|
|
147
147
|
|
|
148
148
|
|
|
149
|
+
## Operating protocol
|
|
150
|
+
|
|
151
|
+
You run under a standard Vanara protocol — it is what makes you safe to trust with real work.
|
|
152
|
+
|
|
153
|
+
- **Ground every claim.** State findings with concrete evidence: a `file:line`, a command's
|
|
154
|
+
output, or the result of one of your own `scripts/`. Run your verification script(s) before
|
|
155
|
+
reporting when the task is in their scope. If you cannot ground a claim, say so plainly — never
|
|
156
|
+
invent a file, a line number, or a result.
|
|
157
|
+
- **Say what you'll touch, then stay in scope.** Before acting, state briefly what you will read
|
|
158
|
+
and what (if anything) you will change. Default to read-only; only write files the task
|
|
159
|
+
requires. For anything destructive or irreversible — deleting, force-pushing, migrations, prod
|
|
160
|
+
config — stop and get explicit confirmation first.
|
|
161
|
+
- **Leave a trail.** Whenever you change a file, append one line to `.claude/vanara-runs.log`:
|
|
162
|
+
`<ISO-8601 date> <your-name> — <what changed> — <why>` (create the file if it's missing).
|
|
163
|
+
- **Check your own work before you finish.** Don't declare a task done until its exit criteria
|
|
164
|
+
hold — tests pass, no new secrets, lints/build clean, and the original ask is fully addressed.
|
|
165
|
+
If a criterion can't be met, report exactly which one and why; never claim success you can't back.
|
|
166
|
+
|
|
149
167
|
## Memory — learn across sessions
|
|
150
168
|
|
|
151
169
|
You keep a persistent, per-project memory at `.claude/memory/vuln-scanner.md`. It is
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vanara-route
|
|
3
|
+
description: Given a task, find the best-fit installed Vanara agent and run it. Reads the installed agent roster, scores each by how well its stated purpose matches the task, delegates to the strongest match, and — when nothing fits — records the gap so you can request the missing agent. Use when you are not sure which specialist should handle a job, or you want the toolkit to pick for you.
|
|
4
|
+
type: skill
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
updated: 2026-07-13
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# vanara-route — the dispatcher
|
|
10
|
+
|
|
11
|
+
You are routing a task to the **best-capable installed agent**, then handing off to it.
|
|
12
|
+
Claude Code already auto-selects agents from their descriptions; this skill adds two things
|
|
13
|
+
on top: an *explicit* "pick the strongest specialist for this exact task" step you can invoke
|
|
14
|
+
on demand, and a **gap-capture** path for when the toolkit has nothing that fits.
|
|
15
|
+
|
|
16
|
+
## When to use
|
|
17
|
+
|
|
18
|
+
- The user asks "which agent should handle X?" or "use the right agent for this."
|
|
19
|
+
- A task spans several specialties and you want the single best owner (or a short list).
|
|
20
|
+
- You suspect no installed agent covers the task and want that recorded, not silently dropped.
|
|
21
|
+
|
|
22
|
+
## The routing procedure
|
|
23
|
+
|
|
24
|
+
1. **Enumerate the roster.** Run `node .claude/skills/vanara-route/scripts/roster.mjs` (or read
|
|
25
|
+
`.claude/agents/*.md` directly) to list every installed agent with its `name` and
|
|
26
|
+
`description` — the path is from your project root, where Claude Code runs. The description
|
|
27
|
+
is the agent's own statement of when it should be used — treat it as the primary signal.
|
|
28
|
+
|
|
29
|
+
2. **Score each candidate against the task.** For every agent, judge fit on:
|
|
30
|
+
- **Domain match** — does the task fall in this agent's stated domain? (a Terraform change
|
|
31
|
+
→ `iac-author`; a failing test → `test-author`; a slow query → `database-administrator`).
|
|
32
|
+
- **Verb match** — does the agent *do* what the task needs (review vs. write vs. audit vs.
|
|
33
|
+
plan)? A read-only reviewer is the wrong pick for "implement".
|
|
34
|
+
- **Proactivity cues** — descriptions that say "Use PROACTIVELY when …" are strong matches
|
|
35
|
+
for exactly those triggers.
|
|
36
|
+
Assign each a confidence: **strong / partial / none**.
|
|
37
|
+
|
|
38
|
+
3. **Act on the best match.**
|
|
39
|
+
- **One strong match** → delegate the task to that agent (invoke it via the Task tool) and
|
|
40
|
+
return its result. Say which agent you chose and why in one line.
|
|
41
|
+
- **Several strong matches** (e.g. review + security on the same diff) → run them in the
|
|
42
|
+
order the work implies, or present the short list and let the user pick.
|
|
43
|
+
- **Only partial matches** → name the closest agent, state plainly that it is an approximate
|
|
44
|
+
fit, and ask before running it.
|
|
45
|
+
- **No match** → do **not** improvise a fake specialist. Record the gap (next section).
|
|
46
|
+
|
|
47
|
+
4. **Never fabricate a specialist.** If the roster has no agent for the job, routing has
|
|
48
|
+
failed on purpose — that failure is a signal, not something to paper over.
|
|
49
|
+
|
|
50
|
+
## Recording a gap (when nothing fits)
|
|
51
|
+
|
|
52
|
+
When no installed agent covers the task, append one line to the local gap log so it is not
|
|
53
|
+
lost. Do this with the helper (it is append-only and creates the file if needed):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
node .claude/skills/vanara-route/scripts/log-gap.mjs "one-line capability that was missing"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This writes to `~/.vanara/gaps.jsonl` **on the user's machine only** — nothing is sent
|
|
60
|
+
anywhere. Then tell the user, in one sentence, that they can submit the request with
|
|
61
|
+
`vanara request` (which prepares an email to the Vanara team; the user sends it, or not).
|
|
62
|
+
See [references/gap-reporting.md](references/gap-reporting.md) for the privacy model.
|
|
63
|
+
|
|
64
|
+
## Edge cases and failure modes
|
|
65
|
+
|
|
66
|
+
- **Empty roster** (nothing installed yet): tell the user to `npx vanara install <name>` or
|
|
67
|
+
`vanara list` first; there is nothing to route to.
|
|
68
|
+
- **Over-eager matching:** a vaguely-related description is a *partial* match, not a strong
|
|
69
|
+
one. Prefer asking over running the wrong specialist on a real task.
|
|
70
|
+
- **Ambiguous verbs:** "fix the auth bug" implies reproduce → test → patch → review, which is
|
|
71
|
+
an *orchestration* job — prefer an orchestrator pack if one is installed over a single agent.
|
|
72
|
+
- **Secrets:** never write task contents, code, credentials, or customer data into the gap
|
|
73
|
+
log — only a short, generic description of the missing capability.
|
|
74
|
+
- **Do not loop:** route once. If the chosen agent itself can't finish, report that back to
|
|
75
|
+
the user rather than re-routing endlessly.
|
|
76
|
+
|
|
77
|
+
## Example
|
|
78
|
+
|
|
79
|
+
> **Task:** "our Terraform plan keeps showing drift on the prod VPC — figure out why."
|
|
80
|
+
>
|
|
81
|
+
> Roster scan → `iac-author` (writes/refactors IaC) is a *partial* verb match (this is
|
|
82
|
+
> diagnosis, not authoring); no dedicated drift-diagnosis agent exists. Route names
|
|
83
|
+
> `iac-author` as the closest fit, flags it as approximate, and logs the gap
|
|
84
|
+
> *"Terraform drift diagnosis / state reconciliation"* so it can be requested.
|
|
85
|
+
|
|
86
|
+
See [references/matching-heuristics.md](references/matching-heuristics.md) for the full scoring
|
|
87
|
+
rubric and worked examples.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Gap reporting — the privacy model
|
|
2
|
+
|
|
3
|
+
When the router finds no installed agent for a task, that is a useful signal: it tells the
|
|
4
|
+
Vanara team which specialist to build next. But a task description can contain proprietary
|
|
5
|
+
code, customer data, or business context — so **nothing is ever sent automatically.**
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
1. **Local log.** The router appends a one-line, generic description of the *missing
|
|
10
|
+
capability* (not the task contents) to `~/.vanara/gaps.jsonl` on your machine. Example line:
|
|
11
|
+
```json
|
|
12
|
+
{"ts":"2026-07-13T18:00:00Z","capability":"Terraform drift diagnosis / state reconciliation"}
|
|
13
|
+
```
|
|
14
|
+
2. **You decide.** Running `vanara request` reads that log and prepares an email to the Vanara
|
|
15
|
+
team (`support@vanaraagents.com`) with the capability phrases pre-filled. You review it and
|
|
16
|
+
**you** send it — or edit it, or delete a line, or send nothing.
|
|
17
|
+
3. **No telemetry.** There is no background upload, no phone-home, no account tie-in. If you
|
|
18
|
+
never run `vanara request`, nothing leaves your machine.
|
|
19
|
+
|
|
20
|
+
## What to record — and what never to
|
|
21
|
+
|
|
22
|
+
| Record (generic capability) | Never record |
|
|
23
|
+
|---|---|
|
|
24
|
+
| "Kafka consumer-lag alerting agent" | the task's actual code or config |
|
|
25
|
+
| "Stripe webhook reconciliation reviewer" | customer names, emails, secrets |
|
|
26
|
+
| "ARM template (Bicep) linter" | proprietary business logic |
|
|
27
|
+
|
|
28
|
+
The rule is one short phrase naming the *kind of agent* that was missing. If in doubt, write
|
|
29
|
+
less. You can always add detail yourself when you send the request.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Matching heuristics — scoring an agent against a task
|
|
2
|
+
|
|
3
|
+
The router's job is to map a task to the installed agent whose *stated purpose* fits best.
|
|
4
|
+
The signal is each agent's `description` frontmatter — agents are authored to declare exactly
|
|
5
|
+
when they should be used. Score on three axes, then combine.
|
|
6
|
+
|
|
7
|
+
## The three axes
|
|
8
|
+
|
|
9
|
+
| Axis | Question | Strong signal | Weak / no signal |
|
|
10
|
+
|---|---|---|---|
|
|
11
|
+
| **Domain** | Is the task in this agent's field? | task nouns match the agent's domain (schema → database, WCAG → accessibility) | domain is adjacent but not the same |
|
|
12
|
+
| **Verb** | Does the agent *do* the needed action? | review↔reviewer, write↔author, audit↔auditor, plan↔planner | agent only reads when the task needs writing |
|
|
13
|
+
| **Trigger** | Does the description name this situation? | "Use PROACTIVELY when \<exactly this\>" | generic description with no trigger |
|
|
14
|
+
|
|
15
|
+
Confidence = **strong** only when Domain and Verb both match. Domain-only or Verb-only is
|
|
16
|
+
**partial**. Neither is **none**.
|
|
17
|
+
|
|
18
|
+
## Worked examples
|
|
19
|
+
|
|
20
|
+
- *"add pagination to the orders endpoint"* → `api-designer` **partial** (domain match, but it
|
|
21
|
+
advises rather than implements) + `feature-builder` **strong** (writes the code). Route to
|
|
22
|
+
`feature-builder`; mention `api-designer` for the contract if the user wants review.
|
|
23
|
+
- *"is this dependency bump safe?"* → `dependency-upgrader` **strong**. One clear owner.
|
|
24
|
+
- *"our SOC 2 auditor asked for an access-review policy"* → `compliance-auditor` **strong**.
|
|
25
|
+
- *"make the game's boss fight less frustrating"* → `game-systems-designer` **strong** (design),
|
|
26
|
+
`game-developer` **partial** (implements once the design is decided).
|
|
27
|
+
|
|
28
|
+
## Anti-patterns
|
|
29
|
+
|
|
30
|
+
- **Keyword collision:** the word "review" appears in many descriptions — confirm the *domain*
|
|
31
|
+
too before routing a security task to a code reviewer.
|
|
32
|
+
- **One agent for an orchestration job:** "fix the failing build and stop it regressing" is
|
|
33
|
+
reproduce → test → patch → review. Prefer an installed orchestrator pack over a lone agent.
|
|
34
|
+
- **Forcing a fit:** a partial match on a real, consequential task is a reason to ask the user,
|
|
35
|
+
or to log a gap — not to run the wrong specialist and hope.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Appends a missing-capability gap to ~/.vanara/gaps.jsonl — LOCAL ONLY, nothing
|
|
3
|
+
// is sent anywhere. `vanara request` later lets the user submit these on purpose.
|
|
4
|
+
// node log-gap.mjs "short capability that was missing" | --selftest
|
|
5
|
+
import { appendFileSync, mkdirSync, readFileSync, existsSync, mkdtempSync, rmSync, realpathSync } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
// Windows-safe direct-run check (see roster.mjs).
|
|
11
|
+
function isDirectRun() {
|
|
12
|
+
try {
|
|
13
|
+
return Boolean(process.argv[1]) &&
|
|
14
|
+
realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
|
15
|
+
} catch { return false; }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function gapsPath(home = os.homedir()) {
|
|
19
|
+
return path.join(home, ".vanara", "gaps.jsonl");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function logGap(capability, { home = os.homedir(), now = new Date() } = {}) {
|
|
23
|
+
const cap = String(capability ?? "").trim();
|
|
24
|
+
if (!cap) throw new Error("empty capability");
|
|
25
|
+
if (cap.length > 200) throw new Error("capability too long — keep it a short phrase");
|
|
26
|
+
const file = gapsPath(home);
|
|
27
|
+
mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
28
|
+
// De-dupe: skip if the same capability was already logged.
|
|
29
|
+
if (existsSync(file)) {
|
|
30
|
+
for (const line of readFileSync(file, "utf8").split(/\r?\n/)) {
|
|
31
|
+
if (!line.trim()) continue;
|
|
32
|
+
try { if (JSON.parse(line).capability === cap) return { added: false, file }; } catch {}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
appendFileSync(file, `${JSON.stringify({ ts: now.toISOString(), capability: cap })}\n`, "utf8");
|
|
36
|
+
return { added: true, file };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function selftest() {
|
|
40
|
+
const home = mkdtempSync(path.join(os.tmpdir(), "gap-"));
|
|
41
|
+
const a = logGap("drift diagnosis", { home });
|
|
42
|
+
const b = logGap("drift diagnosis", { home }); // dupe
|
|
43
|
+
const ok = a.added === true && b.added === false;
|
|
44
|
+
rmSync(home, { recursive: true, force: true });
|
|
45
|
+
if (ok) { console.log("selftest ok"); process.exit(0); }
|
|
46
|
+
console.error("selftest FAILED"); process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (process.argv.includes("--selftest")) selftest();
|
|
50
|
+
else if (isDirectRun()) {
|
|
51
|
+
const cap = process.argv.slice(2).join(" ").trim();
|
|
52
|
+
if (!cap) { console.error('usage: node log-gap.mjs "missing capability"'); process.exit(1); }
|
|
53
|
+
const r = logGap(cap);
|
|
54
|
+
console.log(r.added ? `logged gap → ${r.file}` : "already logged (skipped duplicate)");
|
|
55
|
+
console.log("submit it any time with: vanara request");
|
|
56
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Lists installed Vanara agents (name + description) so the router can score
|
|
3
|
+
// them against a task. Reads .claude/agents/*.md in the current project.
|
|
4
|
+
// node roster.mjs [--json] [--dir <project-root>] | --selftest
|
|
5
|
+
import { readdirSync, readFileSync, existsSync, statSync, mkdtempSync, writeFileSync, mkdirSync, rmSync, realpathSync } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
// Cross-platform "is this file being run directly?" — compare canonical paths.
|
|
11
|
+
// A plain string/URL compare breaks on Windows (backslashes, drive-letter case,
|
|
12
|
+
// 8.3 short names in TEMP); realpathSync normalizes both sides.
|
|
13
|
+
function isDirectRun() {
|
|
14
|
+
try {
|
|
15
|
+
return Boolean(process.argv[1]) &&
|
|
16
|
+
realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
|
17
|
+
} catch { return false; }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function frontmatter(md) {
|
|
21
|
+
const m = md.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
22
|
+
if (!m) return {};
|
|
23
|
+
const out = {};
|
|
24
|
+
for (const line of m[1].split(/\r?\n/)) {
|
|
25
|
+
const i = line.indexOf(":");
|
|
26
|
+
if (i === -1) continue;
|
|
27
|
+
out[line.slice(0, i).trim()] = line.slice(i + 1).trim().replace(/^["']|["']$/g, "");
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function readRoster(root = process.cwd()) {
|
|
33
|
+
const dir = path.join(root, ".claude", "agents");
|
|
34
|
+
if (!existsSync(dir)) return [];
|
|
35
|
+
const agents = [];
|
|
36
|
+
for (const entry of readdirSync(dir)) {
|
|
37
|
+
const full = path.join(dir, entry);
|
|
38
|
+
let file = null;
|
|
39
|
+
if (entry.endsWith(".md")) file = full;
|
|
40
|
+
else if (statSync(full).isDirectory()) {
|
|
41
|
+
const a = path.join(full, "AGENT.md");
|
|
42
|
+
if (existsSync(a)) file = a;
|
|
43
|
+
}
|
|
44
|
+
if (!file) continue;
|
|
45
|
+
const fm = frontmatter(readFileSync(file, "utf8"));
|
|
46
|
+
if (fm.name) agents.push({ name: fm.name, description: fm.description ?? "" });
|
|
47
|
+
}
|
|
48
|
+
return agents.sort((a, b) => a.name.localeCompare(b.name));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function selftest() {
|
|
52
|
+
const tmp = mkdtempSync(path.join(os.tmpdir(), "roster-"));
|
|
53
|
+
mkdirSync(path.join(tmp, ".claude", "agents"), { recursive: true });
|
|
54
|
+
writeFileSync(
|
|
55
|
+
path.join(tmp, ".claude", "agents", "code-reviewer.md"),
|
|
56
|
+
"---\nname: code-reviewer\ndescription: Reviews diffs.\n---\nbody",
|
|
57
|
+
);
|
|
58
|
+
const r = readRoster(tmp);
|
|
59
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
60
|
+
if (r.length === 1 && r[0].name === "code-reviewer") { console.log("selftest ok"); process.exit(0); }
|
|
61
|
+
console.error("selftest FAILED", r); process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (process.argv.includes("--selftest")) selftest();
|
|
65
|
+
else if (isDirectRun()) {
|
|
66
|
+
const dirFlag = process.argv.indexOf("--dir");
|
|
67
|
+
const root = dirFlag !== -1 ? process.argv[dirFlag + 1] : process.cwd();
|
|
68
|
+
const roster = readRoster(root);
|
|
69
|
+
if (process.argv.includes("--json")) console.log(JSON.stringify(roster, null, 2));
|
|
70
|
+
else {
|
|
71
|
+
if (!roster.length) console.log("(no installed agents — run: npx vanara install <name>)");
|
|
72
|
+
for (const a of roster) console.log(`${a.name} — ${a.description.slice(0, 100)}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
package/free-tier.json
CHANGED
|
@@ -1,15 +1,37 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Items installable without a license. Everything else in the bundled catalog requires `vanara unlock <license-key>` (Pro subscription). Packs listed here imply their members are free too.",
|
|
3
|
+
"packs": [
|
|
4
|
+
"code-review-pack",
|
|
5
|
+
"security-pack"
|
|
6
|
+
],
|
|
7
|
+
"agents": [
|
|
8
|
+
"code-reviewer",
|
|
9
|
+
"pr-summarizer",
|
|
10
|
+
"threat-modeler",
|
|
11
|
+
"security-auditor",
|
|
12
|
+
"vuln-scanner",
|
|
13
|
+
"debugger",
|
|
14
|
+
"test-author",
|
|
15
|
+
"api-designer",
|
|
16
|
+
"refactoring-specialist",
|
|
17
|
+
"technical-writer"
|
|
18
|
+
],
|
|
19
|
+
"skills": [
|
|
20
|
+
"git-collaboration-workflows",
|
|
21
|
+
"conventional-commits",
|
|
22
|
+
"sql-index-tuning",
|
|
23
|
+
"owasp-top10",
|
|
24
|
+
"secure-auth",
|
|
25
|
+
"secrets-management",
|
|
26
|
+
"rest-api-design",
|
|
27
|
+
"api-pagination",
|
|
28
|
+
"error-handling-patterns",
|
|
29
|
+
"test-plan-design",
|
|
30
|
+
"readme-writing",
|
|
31
|
+
"refactoring-patterns",
|
|
32
|
+
"caching-strategies",
|
|
33
|
+
"database-migrations",
|
|
34
|
+
"prompt-engineering",
|
|
35
|
+
"vanara-route"
|
|
36
|
+
]
|
|
37
|
+
}
|
package/package.json
CHANGED
package/src/doctor.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// `vanara doctor` — inspect a project and recommend which agents/packs to install.
|
|
2
|
+
// Pure heuristics over files + package.json; no network. Every recommended name
|
|
3
|
+
// is a real catalog item. The inverse of routing: proactive at setup time.
|
|
4
|
+
import { readFile, readdir, access } from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
|
|
7
|
+
const exists = async (p) => { try { await access(p); return true; } catch { return false; } };
|
|
8
|
+
|
|
9
|
+
// signal -> { detect, recommend } ; recommend names must exist in the catalog.
|
|
10
|
+
const SIGNALS = [
|
|
11
|
+
{ id: 'always', label: 'any codebase', recommend: ['code-review-pack', 'test-author', 'vanara-route'] },
|
|
12
|
+
{ id: 'web', label: 'frontend / web UI', recommend: ['accessibility-auditor', 'code-reviewer', 'component-builder'] },
|
|
13
|
+
{ id: 'backend', label: 'backend / API service', recommend: ['backend-essentials-pack', 'api-designer', 'database-administrator'] },
|
|
14
|
+
{ id: 'infra', label: 'containers / IaC', recommend: ['iac-author', 'cicd-engineer', 'sre-reliability-pack'] },
|
|
15
|
+
{ id: 'security', label: 'auth / payments / secrets', recommend: ['security-pack', 'threat-modeler', 'security-auditor'] },
|
|
16
|
+
{ id: 'data', label: 'data / ML', recommend: ['data-pipeline-engineer', 'ml-model-reviewer'] },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export async function detect(root = process.cwd()) {
|
|
20
|
+
const found = new Set(['always']);
|
|
21
|
+
let deps = {};
|
|
22
|
+
try {
|
|
23
|
+
const pkg = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8'));
|
|
24
|
+
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
25
|
+
} catch { /* no package.json */ }
|
|
26
|
+
const dep = (re) => Object.keys(deps).some((d) => re.test(d));
|
|
27
|
+
|
|
28
|
+
let files = [];
|
|
29
|
+
try { files = await readdir(root); } catch { /* unreadable */ }
|
|
30
|
+
const has = async (...names) => (await Promise.all(names.map((n) => exists(path.join(root, n))))).some(Boolean);
|
|
31
|
+
const ext = (re) => files.some((f) => re.test(f));
|
|
32
|
+
|
|
33
|
+
if (dep(/^(react|next|vue|svelte|@angular)/) || ext(/\.(tsx|jsx|vue|svelte)$/)) found.add('web');
|
|
34
|
+
if (dep(/^(express|fastify|koa|@nestjs|django|flask|fastapi)/) || (await has('go.mod', 'requirements.txt', 'pyproject.toml', 'Gemfile'))) found.add('backend');
|
|
35
|
+
if (await has('Dockerfile', 'docker-compose.yml', 'compose.yaml') || ext(/\.tf$/) || (await has('.github', 'k8s', 'helm'))) found.add('infra');
|
|
36
|
+
if (dep(/(auth|passport|jsonwebtoken|bcrypt|stripe|paypal|oauth)/)) found.add('security');
|
|
37
|
+
if (dep(/(tensorflow|torch|scikit|pandas|numpy|langchain)/) || ext(/\.ipynb$/)) found.add('data');
|
|
38
|
+
return [...found];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function recommend(signalIds) {
|
|
42
|
+
const seen = new Set();
|
|
43
|
+
const items = [];
|
|
44
|
+
for (const s of SIGNALS) {
|
|
45
|
+
if (!signalIds.includes(s.id)) continue;
|
|
46
|
+
for (const name of s.recommend) if (!seen.has(name)) { seen.add(name); items.push(name); }
|
|
47
|
+
}
|
|
48
|
+
return { signals: SIGNALS.filter((s) => signalIds.includes(s.id)), items };
|
|
49
|
+
}
|
package/src/memory.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// `vanara memory` — inspect what installed agents have learned for this project.
|
|
2
|
+
// Agents write to .claude/memory/<name>.md (see the memory protocol). Committing
|
|
3
|
+
// that folder to git shares the accumulated knowledge across a whole team.
|
|
4
|
+
import { readdir, readFile, stat, rm } from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
|
|
7
|
+
const MEMORY_DIR = path.join('.claude', 'memory');
|
|
8
|
+
|
|
9
|
+
/** List memory files with entry/line counts. Returns [] if none. */
|
|
10
|
+
export async function readMemoryIndex(root = process.cwd()) {
|
|
11
|
+
const dir = path.join(root, MEMORY_DIR);
|
|
12
|
+
let entries;
|
|
13
|
+
try { entries = await readdir(dir); }
|
|
14
|
+
catch (err) { if (err.code === 'ENOENT') return []; throw err; }
|
|
15
|
+
const out = [];
|
|
16
|
+
for (const name of entries) {
|
|
17
|
+
if (!name.endsWith('.md')) continue;
|
|
18
|
+
const file = path.join(dir, name);
|
|
19
|
+
const [text, st] = await Promise.all([readFile(file, 'utf8'), stat(file)]);
|
|
20
|
+
const bullets = (text.match(/^\s*[-*]\s+/gm) ?? []).length;
|
|
21
|
+
out.push({ agent: name.replace(/\.md$/, ''), bullets, bytes: st.size, mtime: st.mtime, file });
|
|
22
|
+
}
|
|
23
|
+
return out.sort((a, b) => a.agent.localeCompare(b.agent));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Read one agent's memory text, or null if absent. */
|
|
27
|
+
export async function readAgentMemory(agent, root = process.cwd()) {
|
|
28
|
+
try { return await readFile(path.join(root, MEMORY_DIR, `${agent}.md`), 'utf8'); }
|
|
29
|
+
catch (err) { if (err.code === 'ENOENT') return null; throw err; }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Remove empty (zero-bullet) memory files. Returns the removed agent names. */
|
|
33
|
+
export async function pruneEmpty(root = process.cwd()) {
|
|
34
|
+
const removed = [];
|
|
35
|
+
for (const m of await readMemoryIndex(root)) {
|
|
36
|
+
if (m.bullets === 0) { await rm(m.file, { force: true }); removed.push(m.agent); }
|
|
37
|
+
}
|
|
38
|
+
return removed;
|
|
39
|
+
}
|
package/src/request.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// `vanara request` — submit missing-capability requests to the Vanara team.
|
|
2
|
+
// Gaps are logged LOCALLY (by the vanara-route skill or by this command) to
|
|
3
|
+
// ~/.vanara/gaps.jsonl. Submission is always user-initiated: this builds a
|
|
4
|
+
// pre-filled email to support@vanaraagents.com — nothing is sent automatically,
|
|
5
|
+
// no telemetry, no phone-home. See catalog/skills/vanara-route/references.
|
|
6
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import os from 'node:os';
|
|
9
|
+
|
|
10
|
+
const SUPPORT = 'support@vanaraagents.com';
|
|
11
|
+
const MAX_CAP = 200;
|
|
12
|
+
|
|
13
|
+
export const gapsPath = (home = os.homedir()) => path.join(home, '.vanara', 'gaps.jsonl');
|
|
14
|
+
|
|
15
|
+
export async function readGaps(fileOverride) {
|
|
16
|
+
const file = fileOverride ?? gapsPath();
|
|
17
|
+
let text;
|
|
18
|
+
try { text = await readFile(file, 'utf8'); }
|
|
19
|
+
catch (err) { if (err.code === 'ENOENT') return []; throw err; }
|
|
20
|
+
const gaps = [];
|
|
21
|
+
for (const line of text.split(/\r?\n/)) {
|
|
22
|
+
if (!line.trim()) continue;
|
|
23
|
+
try {
|
|
24
|
+
const g = JSON.parse(line);
|
|
25
|
+
if (g && typeof g.capability === 'string') gaps.push(g);
|
|
26
|
+
} catch { /* skip malformed line */ }
|
|
27
|
+
}
|
|
28
|
+
return gaps;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function addGap(capability, { fileOverride, now = new Date() } = {}) {
|
|
32
|
+
const cap = String(capability ?? '').trim();
|
|
33
|
+
if (!cap) throw new Error('Describe the missing capability in a short phrase.');
|
|
34
|
+
if (cap.length > MAX_CAP) throw new Error(`Keep it under ${MAX_CAP} characters.`);
|
|
35
|
+
const file = fileOverride ?? gapsPath();
|
|
36
|
+
const existing = await readGaps(file);
|
|
37
|
+
if (existing.some((g) => g.capability === cap)) return { added: false, gaps: existing };
|
|
38
|
+
await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
39
|
+
const next = [...existing, { ts: now.toISOString(), capability: cap }];
|
|
40
|
+
await writeFile(file, `${next.map((g) => JSON.stringify(g)).join('\n')}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
41
|
+
return { added: true, gaps: next };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Build a mailto: URL the user can open to send the request themselves. */
|
|
45
|
+
export function buildMailto(gaps) {
|
|
46
|
+
const lines = gaps.map((g, i) => `${i + 1}. ${g.capability}`);
|
|
47
|
+
const subject = `Vanara agent request (${gaps.length})`;
|
|
48
|
+
const body = [
|
|
49
|
+
'Hi Vanara team,',
|
|
50
|
+
'',
|
|
51
|
+
'These are capabilities I looked for but the toolkit did not have an agent for:',
|
|
52
|
+
'',
|
|
53
|
+
...lines,
|
|
54
|
+
'',
|
|
55
|
+
'(Sent by me via `vanara request` — feel free to reach out.)',
|
|
56
|
+
].join('\n');
|
|
57
|
+
return `mailto:${SUPPORT}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function clearGaps(fileOverride) {
|
|
61
|
+
const file = fileOverride ?? gapsPath();
|
|
62
|
+
await writeFile(file, '', 'utf8');
|
|
63
|
+
}
|