@ionivetech/mugiwara 0.6.5 → 0.6.6
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -3
- package/.codex-plugin/plugin.json +1 -2
- package/.cursor-plugin/plugin.json +1 -2
- package/.kimi-plugin/plugin.json +1 -2
- package/.opencode/commands/mugiwara-continue.md +44 -31
- package/.opencode/commands/mugiwara-execute.md +1 -1
- package/.opencode/commands/mugiwara-heal.md +1 -1
- package/.opencode/commands/mugiwara-onboard.md +8 -9
- package/.opencode/commands/mugiwara-plan.md +1 -1
- package/.opencode/commands/mugiwara-review.md +1 -1
- package/.opencode/commands/mugiwara-security.md +1 -1
- package/.opencode/commands/mugiwara-ship.md +1 -1
- package/.opencode/commands/mugiwara.md +2 -2
- package/.opencode/plugins/mugiwara.mjs +1 -2
- package/AGENTS.md +1 -2
- package/README.md +33 -31
- package/content/agents/brook-healing.md +5 -8
- package/content/agents/chopper-checkpoint.md +8 -11
- package/content/agents/eval-runner.md +2 -5
- package/content/agents/franky-gates.md +4 -7
- package/content/agents/jinbe-security.md +4 -7
- package/content/agents/luffy-orchestrator.md +13 -13
- package/content/agents/memory-keeper.md +5 -8
- package/content/agents/nami-planner.md +4 -7
- package/content/agents/resume-coordinator.md +7 -10
- package/content/agents/robin-reviewer.md +6 -9
- package/content/agents/sanji-quality.md +7 -10
- package/content/agents/skeptic-verifier.md +5 -8
- package/content/agents/usopp-brainstorm.md +3 -6
- package/content/agents/zoro-execution.md +7 -10
- package/content/skills/mugiwara-backend/SKILL.md +2 -0
- package/content/skills/mugiwara-checkpoint/SKILL.md +13 -11
- package/content/skills/mugiwara-checkpoint/references/ledger-format.md +4 -4
- package/content/skills/mugiwara-context-budget/SKILL.md +5 -3
- package/content/skills/mugiwara-contract-first/SKILL.md +2 -0
- package/content/skills/mugiwara-execution/SKILL.md +20 -20
- package/content/skills/mugiwara-execution/references/dispatch.md +2 -2
- package/content/skills/mugiwara-execution/references/resume-batching.md +1 -1
- package/content/skills/mugiwara-frontend/SKILL.md +2 -4
- package/content/skills/mugiwara-gates/SKILL.md +8 -7
- package/content/skills/mugiwara-git/SKILL.md +7 -2
- package/content/skills/mugiwara-healing/SKILL.md +6 -4
- package/content/skills/mugiwara-healing/references/workers.md +2 -2
- package/content/skills/mugiwara-orchestration/SKILL.md +26 -24
- package/content/skills/mugiwara-orchestration/references/check-ins.md +16 -15
- package/content/skills/mugiwara-orchestration/references/closure.md +5 -5
- package/content/skills/mugiwara-orchestration/references/output-contract.md +9 -9
- package/content/skills/mugiwara-orchestration/references/triage-escalation.md +22 -22
- package/content/skills/mugiwara-planning/SKILL.md +3 -1
- package/content/skills/mugiwara-planning/references/plan-template.md +2 -2
- package/content/skills/mugiwara-pr/SKILL.md +6 -6
- package/content/skills/mugiwara-pr/references/verdict-format.md +3 -3
- package/content/skills/mugiwara-quality/SKILL.md +7 -7
- package/content/skills/mugiwara-resume/SKILL.md +18 -20
- package/content/skills/mugiwara-review/SKILL.md +5 -1
- package/content/skills/mugiwara-review/references/five-axis-worksheet.md +1 -1
- package/content/skills/mugiwara-root-cause/SKILL.md +2 -0
- package/content/skills/mugiwara-security/SKILL.md +5 -1
- package/content/skills/mugiwara-ship/SKILL.md +1 -1
- package/content/skills/mugiwara-ship/references/cleanup.md +1 -1
- package/content/skills/mugiwara-testcases/SKILL.md +5 -3
- package/content/skills/mugiwara-workflow/SKILL.md +27 -27
- package/content/skills/mugiwara-workflow/references/workspace-layout.md +15 -15
- package/content/skills/using-mugiwara/SKILL.md +5 -5
- package/dist/mugiwara.js +808 -33
- package/gemini-extension.json +1 -1
- package/hooks/auto-savepoint.js +106 -0
- package/hooks/auto-savepoint.ts +102 -0
- package/hooks/engagement-marker.js +59 -0
- package/hooks/engagement-marker.ts +97 -0
- package/hooks/hooks.json +41 -2
- package/hooks/mugiwara-mode-tracker.js +79 -0
- package/hooks/pipeline-guard.js +182 -0
- package/hooks/pipeline-guard.ts +267 -0
- package/hooks/session-start.js +106 -0
- package/hooks/session-start.ts +44 -32
- package/package.json +7 -3
- package/plugin.json +1 -1
- package/references/agent-protocol.md +15 -0
- package/references/complexity.md +54 -0
- package/references/definition-of-done.md +1 -1
- package/references/multi-actor.md +5 -5
- package/references/skill-versioning.md +1 -1
- package/references/token-budget.md +30 -8
- package/references/wave-banners.md +16 -16
- package/scripts/build-hooks.ts +56 -0
- package/scripts/conformance.ts +17 -0
- package/scripts/coverage-gate.ts +179 -0
- package/scripts/install.ps1 +5 -4
- package/scripts/install.sh +4 -4
- package/scripts/lane.sh +6 -2
- package/scripts/lib/patterns.sh +84 -0
- package/scripts/mission-report.sh +12 -11
- package/scripts/savepoint.sh +141 -37
- package/scripts/verify-install.ts +154 -44
- package/src/args.ts +1 -1
- package/src/cli.ts +217 -0
- package/src/continue.ts +243 -0
- package/src/installer.ts +39 -0
- package/src/onboard.ts +207 -0
- package/src/run.ts +82 -0
- package/src/targets/claude.ts +140 -2
- package/src/targets/copilot.ts +20 -1
- package/src/targets/generic.ts +9 -4
- package/src/targets/opencode.ts +0 -1
- package/content/agents/onboarding-guide.md +0 -124
- package/content/skills/mugiwara-security/references/owasp-mapping.md +0 -30
- package/scripts/onboard.ts +0 -266
- package/scripts/probe.ts +0 -40
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# OWASP Top 10 Mapping
|
|
2
|
-
|
|
3
|
-
Required when the project handles payments, health data, or PII.
|
|
4
|
-
|
|
5
|
-
| Code | Category | Review area |
|
|
6
|
-
|------|----------|-------------|
|
|
7
|
-
| A01 | Broken access control | authz gaps, IDOR, missing server-side checks |
|
|
8
|
-
| A02 | Cryptographic failures | PII in transit/at rest, weak crypto, exposed secrets |
|
|
9
|
-
| A03 | Injection | SQL/NoSQL/OS/template injection, unsanitized input to exec/render |
|
|
10
|
-
| A04 | Insecure design | missing threat model, trust-boundary failures |
|
|
11
|
-
| A05 | Misconfiguration | default creds, verbose errors, permissive headers, debug on |
|
|
12
|
-
| A06 | Vulnerable components | dependency audit, known-vuln check, outdated libs |
|
|
13
|
-
| A07 | Authn failures | broken sessions, brute-forceable login, credential reuse |
|
|
14
|
-
| A08 | Integrity | insecure deserialization, supply-chain tamper |
|
|
15
|
-
| A09 | Logging/monitoring | PII in logs, missing audit trail, silent failures |
|
|
16
|
-
| A10 | SSRF | server-side requests to attacker-controlled targets, URL validation |
|
|
17
|
-
|
|
18
|
-
## How to map
|
|
19
|
-
|
|
20
|
-
For each security check run, record which OWASP category it covers. A handled
|
|
21
|
-
category with no mapping row = documentation gap in the security report.
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
| OWASP | Check | Verdict |
|
|
25
|
-
|-------|-------|---------|
|
|
26
|
-
| A01 | Authz on /api/admin routes | PASS |
|
|
27
|
-
| A02 | TLS enforced, no hardcoded secrets | PASS |
|
|
28
|
-
| A03 | SQL params in user query handler | PASS |
|
|
29
|
-
| A06 | npm audit: 0 critical/high | PASS |
|
|
30
|
-
```
|
package/scripts/onboard.ts
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
// scripts/onboard.ts — Mugiwara onboarding wizard (10 fixed questions, no network)
|
|
3
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { createInterface } from "node:readline";
|
|
6
|
-
|
|
7
|
-
const root = join(import.meta.dirname, "..");
|
|
8
|
-
const mugiwaraDir = join(root, ".mugiwara");
|
|
9
|
-
const configPath = join(mugiwaraDir, "config");
|
|
10
|
-
|
|
11
|
-
function ask(rl: import("node:readline").Interface, prompt: string): Promise<string> {
|
|
12
|
-
return new Promise((resolve) => {
|
|
13
|
-
rl.question(prompt, (answer) => resolve(answer.trim()));
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async function confirm(rl: import("node:readline").Interface, prompt: string): Promise<boolean> {
|
|
18
|
-
const a = await ask(rl, prompt);
|
|
19
|
-
return a.toLowerCase() === "y" || a.toLowerCase() === "yes";
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async function pick(
|
|
23
|
-
rl: import("node:readline").Interface,
|
|
24
|
-
prompt: string,
|
|
25
|
-
options: string[],
|
|
26
|
-
): Promise<number> {
|
|
27
|
-
while (true) {
|
|
28
|
-
const a = await ask(rl, prompt);
|
|
29
|
-
const n = Number.parseInt(a, 10);
|
|
30
|
-
if (!Number.isNaN(n) && n >= 1 && n <= options.length) return n;
|
|
31
|
-
console.log(` Enter a number 1–${options.length}`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function main() {
|
|
36
|
-
// --help / no-tty guard
|
|
37
|
-
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
38
|
-
console.log("Usage: bun scripts/onboard.ts");
|
|
39
|
-
console.log("Runs the Mugiwara onboarding wizard (10 fixed questions).");
|
|
40
|
-
console.log("Writes .mugiwara/config.");
|
|
41
|
-
process.exit(0);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
console.log("");
|
|
48
|
-
console.log("╔══════════════════════════════════════╗");
|
|
49
|
-
console.log("║ Mugiwara Onboarding Wizard ║");
|
|
50
|
-
console.log("╚══════════════════════════════════════╝");
|
|
51
|
-
console.log("");
|
|
52
|
-
|
|
53
|
-
// Check for existing config
|
|
54
|
-
if (existsSync(configPath)) {
|
|
55
|
-
const redo = await confirm(
|
|
56
|
-
rl,
|
|
57
|
-
"Existing .mugiwara/config found. Re-onboard? (y/n): ",
|
|
58
|
-
);
|
|
59
|
-
if (!redo) {
|
|
60
|
-
console.log("Onboarding cancelled. Config preserved.");
|
|
61
|
-
process.exit(0);
|
|
62
|
-
}
|
|
63
|
-
console.log("");
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ---- Phase 1: Project Context ----
|
|
67
|
-
console.log("── Phase 1: Project Context ──");
|
|
68
|
-
console.log("");
|
|
69
|
-
|
|
70
|
-
const q1 = await pick(rl, "Q1 — Project type:\n [1] Web application\n [2] Mobile application\n [3] CLI tool\n [4] Library/SDK\n [5] Backend service / API\n [6] Other\n > ", [
|
|
71
|
-
"Web application",
|
|
72
|
-
"Mobile application",
|
|
73
|
-
"CLI tool",
|
|
74
|
-
"Library/SDK",
|
|
75
|
-
"Backend service / API",
|
|
76
|
-
"Other",
|
|
77
|
-
]);
|
|
78
|
-
console.log("");
|
|
79
|
-
|
|
80
|
-
const q2 = await pick(rl, "Q2 — Primary language:\n [1] TypeScript\n [2] JavaScript\n [3] Python\n [4] Go\n [5] Rust\n [6] Java\n [7] Other\n > ", [
|
|
81
|
-
"TypeScript",
|
|
82
|
-
"JavaScript",
|
|
83
|
-
"Python",
|
|
84
|
-
"Go",
|
|
85
|
-
"Rust",
|
|
86
|
-
"Java",
|
|
87
|
-
"Other",
|
|
88
|
-
]);
|
|
89
|
-
console.log("");
|
|
90
|
-
|
|
91
|
-
const q3 = await pick(rl, "Q3 — Team size:\n [1] Solo\n [2] 2–5\n [3] 6–15\n [4] 16+\n > ", [
|
|
92
|
-
"Solo",
|
|
93
|
-
"2–5",
|
|
94
|
-
"6–15",
|
|
95
|
-
"16+",
|
|
96
|
-
]);
|
|
97
|
-
console.log("");
|
|
98
|
-
|
|
99
|
-
const q4 = await pick(rl, "Q4 — Git workflow:\n [1] Trunk-based (feature/{type}-{issue}-{slug})\n [2] GitFlow (feature/{slug})\n [3] GitHub Flow (feat/{slug})\n [4] Other (feature/{slug})\n > ", [
|
|
100
|
-
"trunk-based",
|
|
101
|
-
"gitflow",
|
|
102
|
-
"github-flow",
|
|
103
|
-
"other",
|
|
104
|
-
]);
|
|
105
|
-
console.log("");
|
|
106
|
-
|
|
107
|
-
const q5 = await pick(rl, "Q5 — CI/CD platform:\n [1] GitHub Actions\n [2] GitLab CI\n [3] CircleCI\n [4] Jenkins\n [5] None / manual\n [6] Other\n > ", [
|
|
108
|
-
"GitHub Actions",
|
|
109
|
-
"GitLab CI",
|
|
110
|
-
"CircleCI",
|
|
111
|
-
"Jenkins",
|
|
112
|
-
"None / manual",
|
|
113
|
-
"Other",
|
|
114
|
-
]);
|
|
115
|
-
console.log("");
|
|
116
|
-
|
|
117
|
-
// ---- Phase 2: Mugiwara Preferences ----
|
|
118
|
-
console.log("── Phase 2: Mugiwara Preferences ──");
|
|
119
|
-
console.log("");
|
|
120
|
-
|
|
121
|
-
const q6 = await pick(rl, "Q6 — Autonomy mode:\n [1] guided — ask before every wave transition\n [2] semi — auto-advance through waves, pause on failures\n [3] auto — full auto-pilot\n > ", [
|
|
122
|
-
"guided",
|
|
123
|
-
"semi",
|
|
124
|
-
"auto",
|
|
125
|
-
]);
|
|
126
|
-
console.log("");
|
|
127
|
-
|
|
128
|
-
const q8a = await pick(rl, "Q7 — Code review depth:\n [1] full — breaking-change map, five-axis review, ≤3 cycles\n [2] standard — five-axis review, 1 cycle\n [3] quick — diff-only, no caller-map\n > ", [
|
|
129
|
-
"full",
|
|
130
|
-
"standard",
|
|
131
|
-
"quick",
|
|
132
|
-
]);
|
|
133
|
-
console.log("");
|
|
134
|
-
|
|
135
|
-
const q8b = await pick(rl, "Q8 — Quality check depth:\n [1] full — format, lint, typecheck, test, build\n [2] standard — lint, typecheck, test\n [3] quick — test only\n > ", [
|
|
136
|
-
"full",
|
|
137
|
-
"standard",
|
|
138
|
-
"quick",
|
|
139
|
-
]);
|
|
140
|
-
console.log("");
|
|
141
|
-
|
|
142
|
-
console.log("Q9 — Test coverage threshold:");
|
|
143
|
-
const q9 = await pick(rl, " [1] 90/80 — new code 90%, modified 80%\n [2] 80/70 — new code 80%, modified 70%\n [3] custom — enter your own values\n [4] none — 0/0, no coverage enforcement\n > ", [
|
|
144
|
-
"90/80",
|
|
145
|
-
"80/70",
|
|
146
|
-
"custom",
|
|
147
|
-
"none",
|
|
148
|
-
]);
|
|
149
|
-
let coverageNew = 90;
|
|
150
|
-
let coverageModified = 80;
|
|
151
|
-
if (q9 === 1) {
|
|
152
|
-
coverageNew = 90;
|
|
153
|
-
coverageModified = 80;
|
|
154
|
-
} else if (q9 === 2) {
|
|
155
|
-
coverageNew = 80;
|
|
156
|
-
coverageModified = 70;
|
|
157
|
-
} else if (q9 === 3) {
|
|
158
|
-
const cNew = await ask(rl, " Enter coverage threshold for new code (%): ");
|
|
159
|
-
const cMod = await ask(rl, " Enter coverage threshold for modified code (%): ");
|
|
160
|
-
coverageNew = Number.parseInt(cNew, 10) || 0;
|
|
161
|
-
coverageModified = Number.parseInt(cMod, 10) || 0;
|
|
162
|
-
} else {
|
|
163
|
-
coverageNew = 0;
|
|
164
|
-
coverageModified = 0;
|
|
165
|
-
}
|
|
166
|
-
console.log("");
|
|
167
|
-
|
|
168
|
-
const q10 = await pick(rl, "Q10 — Commit style:\n [1] Conventional Commits (feat:, fix:, chore:, docs:)\n [2] Semantic (type(scope): message)\n [3] Free-form\n > ", [
|
|
169
|
-
"conventional",
|
|
170
|
-
"semantic",
|
|
171
|
-
"free-form",
|
|
172
|
-
]);
|
|
173
|
-
console.log("");
|
|
174
|
-
|
|
175
|
-
// ---- Branch name format ----
|
|
176
|
-
const branchFormats: Record<number, string> = {
|
|
177
|
-
1: "feature/{type}-{issue}-{slug}",
|
|
178
|
-
2: "feature/{slug}",
|
|
179
|
-
3: "feat/{slug}",
|
|
180
|
-
4: "feature/{slug}",
|
|
181
|
-
};
|
|
182
|
-
const branch = branchFormats[q4] || "feature/{slug}";
|
|
183
|
-
|
|
184
|
-
// ---- Autonomy mode ----
|
|
185
|
-
const modes: Record<number, string> = { 1: "guided", 2: "semi", 3: "auto" };
|
|
186
|
-
const mode = modes[q6] || "semi";
|
|
187
|
-
|
|
188
|
-
// ---- Commit style ----
|
|
189
|
-
const commitStyles: Record<number, string> = { 1: "conventional", 2: "semantic", 3: "free-form" };
|
|
190
|
-
const commit = commitStyles[q10] || "conventional";
|
|
191
|
-
|
|
192
|
-
// ---- Review depth ----
|
|
193
|
-
const reviewDepths: Record<number, string> = { 1: "full", 2: "standard", 3: "quick" };
|
|
194
|
-
const reviewDepth = reviewDepths[q8a] || "standard";
|
|
195
|
-
|
|
196
|
-
// ---- Quality depth ----
|
|
197
|
-
const qualityDepths: Record<number, string> = { 1: "full", 2: "standard", 3: "quick" };
|
|
198
|
-
const qualityDepth = qualityDepths[q8b] || "standard";
|
|
199
|
-
|
|
200
|
-
// ---- Write config ----
|
|
201
|
-
if (!existsSync(mugiwaraDir)) mkdirSync(mugiwaraDir, { recursive: true });
|
|
202
|
-
|
|
203
|
-
const config = {
|
|
204
|
-
mode,
|
|
205
|
-
branch,
|
|
206
|
-
coverage_new: coverageNew,
|
|
207
|
-
coverage_modified: coverageModified,
|
|
208
|
-
commit,
|
|
209
|
-
review_depth: reviewDepth,
|
|
210
|
-
quality_depth: qualityDepth,
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
214
|
-
|
|
215
|
-
// ---- Summary ----
|
|
216
|
-
const qLabels: Record<string, Record<number, string>> = {
|
|
217
|
-
project_type: {
|
|
218
|
-
1: "Web application",
|
|
219
|
-
2: "Mobile application",
|
|
220
|
-
3: "CLI tool",
|
|
221
|
-
4: "Library/SDK",
|
|
222
|
-
5: "Backend service / API",
|
|
223
|
-
6: "Other",
|
|
224
|
-
},
|
|
225
|
-
primary_language: {
|
|
226
|
-
1: "TypeScript",
|
|
227
|
-
2: "JavaScript",
|
|
228
|
-
3: "Python",
|
|
229
|
-
4: "Go",
|
|
230
|
-
5: "Rust",
|
|
231
|
-
6: "Java",
|
|
232
|
-
7: "Other",
|
|
233
|
-
},
|
|
234
|
-
team_size: { 1: "Solo", 2: "2–5", 3: "6–15", 4: "16+" },
|
|
235
|
-
git_workflow: { 1: "Trunk-based", 2: "GitFlow", 3: "GitHub Flow", 4: "Other" },
|
|
236
|
-
ci_cd: { 1: "GitHub Actions", 2: "GitLab CI", 3: "CircleCI", 4: "Jenkins", 5: "None", 6: "Other" },
|
|
237
|
-
autonomy_mode: { 1: "guided", 2: "semi", 3: "auto" },
|
|
238
|
-
review_depth: { 1: "full", 2: "standard", 3: "quick" },
|
|
239
|
-
quality_depth: { 1: "full", 2: "standard", 3: "quick" },
|
|
240
|
-
coverage_threshold: { 1: "90/80", 2: "80/70", 3: "custom", 4: "none" },
|
|
241
|
-
commit_style: { 1: "Conventional", 2: "Semantic", 3: "Free-form" },
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
console.log("╔══════════════════════════════════════╗");
|
|
245
|
-
console.log("║ Onboarding Complete ║");
|
|
246
|
-
console.log("╚══════════════════════════════════════╝");
|
|
247
|
-
console.log("");
|
|
248
|
-
console.log(" Project type: ", qLabels.project_type[q1]);
|
|
249
|
-
console.log(" Primary language: ", qLabels.primary_language[q2]);
|
|
250
|
-
console.log(" Team size: ", qLabels.team_size[q3]);
|
|
251
|
-
console.log(" Git workflow: ", qLabels.git_workflow[q4]);
|
|
252
|
-
console.log(" CI/CD: ", qLabels.ci_cd[q5]);
|
|
253
|
-
console.log(" Autonomy mode: ", qLabels.autonomy_mode[q6]);
|
|
254
|
-
console.log(" Review depth: ", qLabels.review_depth[q8a]);
|
|
255
|
-
console.log(" Quality depth: ", qLabels.quality_depth[q8b]);
|
|
256
|
-
console.log(" Coverage: ", q9 === 3 ? `${coverageNew}/${coverageModified}` : qLabels.coverage_threshold[q9]);
|
|
257
|
-
console.log(" Commit style: ", qLabels.commit_style[q10]);
|
|
258
|
-
console.log("");
|
|
259
|
-
console.log(` Config written: ${configPath}`);
|
|
260
|
-
console.log("");
|
|
261
|
-
} finally {
|
|
262
|
-
rl.close();
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
main();
|
package/scripts/probe.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
// Ad-hoc retrieval probe. Usage: bun scripts/probe.ts "your prompt here"
|
|
3
|
-
|
|
4
|
-
import { readdirSync, readFileSync, existsSync, statSync } from 'node:fs';
|
|
5
|
-
import { join } from 'node:path';
|
|
6
|
-
import { parseFrontmatter } from '../src/frontmatter.ts';
|
|
7
|
-
|
|
8
|
-
const skillsDir = join(import.meta.dirname, '..', 'content', 'skills');
|
|
9
|
-
const STOP = ['the', 'and', 'for', 'use', 'when', 'that', 'with', 'this', 'from', 'its', 'not', 'are', 'has'];
|
|
10
|
-
const tok = (t: string) => t.toLowerCase().replace(/[^a-z0-9\s-]/g, ' ')
|
|
11
|
-
.split(/\s+/).filter(x => x.length > 1 && !STOP.includes(x));
|
|
12
|
-
|
|
13
|
-
const terms = new Map<string, number>();
|
|
14
|
-
const docs = new Map<string, Map<string, number>>();
|
|
15
|
-
for (const d of readdirSync(skillsDir).filter(d => statSync(join(skillsDir, d)).isDirectory())) {
|
|
16
|
-
const f = join(skillsDir, d, 'SKILL.md');
|
|
17
|
-
if (!existsSync(f)) continue;
|
|
18
|
-
const { data } = parseFrontmatter(readFileSync(f, 'utf8'));
|
|
19
|
-
const tf = new Map<string, number>();
|
|
20
|
-
for (const t of tok(data.description ?? '')) {
|
|
21
|
-
tf.set(t, (tf.get(t) || 0) + 1);
|
|
22
|
-
terms.set(t, (terms.get(t) || 0) + 1);
|
|
23
|
-
}
|
|
24
|
-
docs.set(d, tf);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const N = docs.size;
|
|
28
|
-
const prompt = process.argv.slice(2).join(' ');
|
|
29
|
-
const ranked = [...docs.keys()].map(d => {
|
|
30
|
-
let s = 0;
|
|
31
|
-
for (const t of tok(prompt)) {
|
|
32
|
-
const tf = docs.get(d)!.get(t) || 0;
|
|
33
|
-
if (tf) s += tf * Math.log(N / (terms.get(t) || 1));
|
|
34
|
-
}
|
|
35
|
-
return { d, s };
|
|
36
|
-
}).sort((a, b) => b.s - a.s);
|
|
37
|
-
|
|
38
|
-
console.log(`"${prompt}"\n`);
|
|
39
|
-
ranked.slice(0, 8).forEach((r, i) =>
|
|
40
|
-
console.log(` ${String(i + 1).padStart(2)}. ${r.d.padEnd(28)} ${r.s.toFixed(2)}`));
|