agent-harness-kit 0.7.0 → 0.9.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/bin/cli.mjs +26 -0
- package/package.json +1 -1
- package/src/core/doctor.mjs +47 -0
- package/src/core/render-templates.mjs +119 -5
- package/src/core/upgrade.mjs +81 -60
- package/src/templates/.claude/agents/api-consistency-reviewer.md.vi +37 -0
- package/src/templates/.claude/agents/architecture-reviewer.md.vi.hbs +45 -0
- package/src/templates/.claude/agents/performance-reviewer.md.vi +39 -0
- package/src/templates/.claude/agents/reliability-reviewer.md.vi +42 -0
- package/src/templates/.claude/agents/security-reviewer.md.vi +43 -0
- package/src/templates/.claude/hooks/hooks.json +46 -0
- package/src/templates/.claude/output-styles/harness-terse.md +42 -0
- package/src/templates/.claude/settings.json.hbs +2 -1
- package/src/templates/.claude/skills/add-adr/SKILL.md.vi +64 -0
- package/src/templates/.claude/skills/add-feature/SKILL.md.vi.hbs +50 -0
- package/src/templates/.claude/skills/debug-flow/SKILL.md.vi.hbs +42 -0
- package/src/templates/.claude/skills/doc-drift-scan/SKILL.md +15 -10
- package/src/templates/.claude/skills/doc-drift-scan/SKILL.md.vi +52 -0
- package/src/templates/.claude/skills/doc-drift-scan/scripts/scan-paths.mjs +64 -0
- package/src/templates/.claude/skills/eval-runner/SKILL.md.vi +59 -0
- package/src/templates/.claude/skills/garbage-collection/SKILL.md.hbs +14 -5
- package/src/templates/.claude/skills/garbage-collection/SKILL.md.vi.hbs +58 -0
- package/src/templates/.claude/skills/garbage-collection/scripts/gc-classify.mjs +77 -0
- package/src/templates/.claude/skills/i18n-add-locale/SKILL.md +52 -0
- package/src/templates/.claude/skills/i18n-add-locale/SKILL.md.vi +56 -0
- package/src/templates/.claude/skills/i18n-add-locale/scripts/locale-scaffold.mjs +120 -0
- package/src/templates/.claude/skills/inspect-app/SKILL.md.vi +61 -0
- package/src/templates/.claude/skills/inspect-module/SKILL.md.hbs +17 -14
- package/src/templates/.claude/skills/inspect-module/SKILL.md.vi.hbs +57 -0
- package/src/templates/.claude/skills/inspect-module/scripts/module-summary.mjs +144 -0
- package/src/templates/.claude/skills/map-domain/SKILL.md +42 -0
- package/src/templates/.claude/skills/map-domain/SKILL.md.vi +42 -0
- package/src/templates/.claude/skills/map-domain/scripts/domain-map.mjs +145 -0
- package/src/templates/.claude/skills/propose-harness-improvement/SKILL.md.vi +49 -0
- package/src/templates/.claude/skills/propose-harness-improvement/scripts/improvement-bundle.mjs +172 -0
- package/src/templates/.claude/skills/refactor-feature/SKILL.md +60 -0
- package/src/templates/.claude/skills/refactor-feature/SKILL.md.vi +64 -0
- package/src/templates/.claude/skills/refactor-feature/scripts/feature-diff.mjs +146 -0
- package/src/templates/.claude/skills/review-this-pr/SKILL.md +59 -0
- package/src/templates/.claude/skills/review-this-pr/SKILL.md.vi +63 -0
- package/src/templates/.claude/skills/review-this-pr/scripts/pr-review-driver.mjs +152 -0
- package/src/templates/.claude/skills/structural-test-author/SKILL.md.vi.hbs +50 -0
- package/src/templates/.claude/skills/write-skill/SKILL.md.vi +43 -0
- package/src/templates/.harness/eval/rubrics/feature-step-done.mjs +148 -0
- package/src/templates/.harness/eval/tasks/feature-step-done.answer.md +53 -0
- package/src/templates/.harness/eval/tasks/feature-step-done.json +10 -0
- package/src/templates/.harness/eval/tasks/feature-step-done.prompt.md +43 -0
- package/src/templates/.mcp.json.example +35 -0
- package/src/templates/CLAUDE.md.hbs +9 -5
- package/src/templates/CLAUDE.md.vi.hbs +9 -5
- package/src/templates/scripts/notify-on-block.sh.hbs +73 -0
- package/src/templates/scripts/pretooluse-edit-guard.sh.hbs +115 -0
- package/src/templates/scripts/session-end.sh.hbs +6 -0
- package/src/templates/scripts/session-rollup.mjs +96 -0
- package/src/templates/scripts/session-start.sh.hbs +25 -0
- package/src/templates/scripts/statusline.mjs +63 -0
- package/src/templates/scripts/subagent-stop.sh.hbs +76 -0
- package/src/templates/scripts/userprompt-guard.sh.hbs +100 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: garbage-collection
|
|
3
|
+
description: Use this skill on Fridays, before tagging a release, or when the user mentions "cleanup", "tech debt", "AI slop", "GC", or "garbage collection". Runs the deterministic linters, structural tests, and doc-drift scans, then proposes the top-3 highest-leverage cleanups (with risk/cost/benefit) — does NOT auto-merge. This is the solo-dev shrunk version of OpenAI's Friday garbage-collection ritual.
|
|
4
|
+
allowed-tools: Read, Glob, Grep, Bash(npm run:*), Bash(pytest:*), Bash(ruff:*), Bash(git:*), Bash(gh:*), Bash(node .claude/skills/garbage-collection/scripts/gc-classify.mjs:*)
|
|
5
|
+
suggested-turns: 15
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Các bước
|
|
9
|
+
|
|
10
|
+
1. **Lấy baseline.** Chạy toàn bộ test suite và lưu output:
|
|
11
|
+
- {{#if isPython}}`python -m harness.structural_test`{{else}}`npm run harness:check`{{/if}}
|
|
12
|
+
- {{#if isPython}}`ruff check . --output-format json`{{else}}`npm run lint -- --format json`{{/if}}
|
|
13
|
+
- Lưu vào `.harness/gc-<date>.json`.
|
|
14
|
+
2. **Phân loại vi phạm.** Với mỗi finding:
|
|
15
|
+
- **Vi phạm layer** → sửa.
|
|
16
|
+
- **Duplicate utility** (cùng function body ở 2+ chỗ) → đề xuất
|
|
17
|
+
trích xuất sang `src/shared/`.
|
|
18
|
+
- **Dead import** → xóa.
|
|
19
|
+
- **Doc drift** (đường dẫn trong `docs/architecture.md` không còn tồn tại) →
|
|
20
|
+
gọi skill `doc-drift-scan`.
|
|
21
|
+
- **Helper viết tay** trùng với shared utility đã có → đề xuất thay thế.
|
|
22
|
+
3. **Chấm điểm** từng candidate fix trên ba thang điểm 1–5 qua side-car
|
|
23
|
+
script (thay thế LLM-scored turn trước đây — deterministic và có thể
|
|
24
|
+
kiểm chứng):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
node .claude/skills/garbage-collection/scripts/gc-classify.mjs \
|
|
28
|
+
--baseline .harness/gc-<date>.json \
|
|
29
|
+
--history .harness/gc-history.json
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Script áp dụng rubric cơ học: `risk = 1 + ceil(touched/3)`,
|
|
33
|
+
`cost = 1 + ceil(lines/30)`, `benefit = recurrenceCount(class)`. Đọc
|
|
34
|
+
`candidates[]` JSON đã sort theo `(benefit desc, cost asc, risk asc)`.
|
|
35
|
+
4. **Chỉ đề xuất top 3** cleanup (mức cap cho solo-dev; OpenAI làm hàng chục,
|
|
36
|
+
bạn làm 3). Mở thành các PR riêng bằng `gh pr create --label gc --draft`.
|
|
37
|
+
5. **Append một row** vào `.harness/gc-history.json`:
|
|
38
|
+
`{ "date": "...", "violations_found": N, "fixes_opened": M, "total_tokens": K }`.
|
|
39
|
+
6. **Dừng lại.** Không merge gì cả. Phải có human review ở scale solo.
|
|
40
|
+
|
|
41
|
+
## Output contract
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
### GC run: <date>
|
|
45
|
+
### Violations found: <N>
|
|
46
|
+
### Top 3 fixes proposed:
|
|
47
|
+
1. <slug> — risk:<1-5> cost:<1-5> benefit:<1-5> — PR #<n>
|
|
48
|
+
2. ...
|
|
49
|
+
3. ...
|
|
50
|
+
### Fixes deferred (not in top 3): <count>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Anti-patterns
|
|
54
|
+
|
|
55
|
+
- Không mở quá 3 PR trong một lần chạy. Cap đó là feature, không phải bug.
|
|
56
|
+
- Không auto-merge — toàn bộ ý nghĩa của GC ở solo scale là human review.
|
|
57
|
+
- Không bỏ qua các findings điểm thấp; record chúng vào
|
|
58
|
+
`.harness/gc-history.json` để xu hướng nổi lên theo thời gian.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// gc-classify.mjs — deterministic scoring step for /garbage-collection.
|
|
3
|
+
// Replaces "LLM-scored risk/cost/benefit" turn with mechanical rubric.
|
|
4
|
+
//
|
|
5
|
+
// Usage:
|
|
6
|
+
// gc-classify.mjs --baseline <gc-snapshot.json> [--history <hist.json>] [--out <file>]
|
|
7
|
+
//
|
|
8
|
+
// Rubric:
|
|
9
|
+
// risk = 1 + ceil(touched_files / 3) capped at 5
|
|
10
|
+
// cost = 1 + ceil(lines_to_change / 30) capped at 5
|
|
11
|
+
// benefit = recurrenceCount(class) from gc-history capped at 5
|
|
12
|
+
|
|
13
|
+
import { readFileSync, existsSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { resolve } from "node:path";
|
|
15
|
+
|
|
16
|
+
function loadJSON(p, fallback = null) {
|
|
17
|
+
try { return JSON.parse(readFileSync(p, "utf8")); } catch { return fallback; }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseArgs(argv) {
|
|
21
|
+
const out = { baseline: null, history: ".harness/gc-history.json", out: null };
|
|
22
|
+
for (let i = 0; i < argv.length; i++) {
|
|
23
|
+
if (argv[i] === "--baseline") out.baseline = argv[++i];
|
|
24
|
+
else if (argv[i] === "--history") out.history = argv[++i];
|
|
25
|
+
else if (argv[i] === "--out") out.out = argv[++i];
|
|
26
|
+
}
|
|
27
|
+
if (!out.baseline) {
|
|
28
|
+
console.error("usage: gc-classify.mjs --baseline <gc-snapshot.json> [--history <hist.json>] [--out <file>]");
|
|
29
|
+
process.exit(2);
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function recurrenceCount(history, klass) {
|
|
35
|
+
if (!history?.runs) return 1;
|
|
36
|
+
let n = 0;
|
|
37
|
+
for (const run of history.runs) {
|
|
38
|
+
if (Array.isArray(run.classes_seen) && run.classes_seen.includes(klass)) n++;
|
|
39
|
+
}
|
|
40
|
+
return n;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function cap5(n) { return Math.max(1, Math.min(5, n)); }
|
|
44
|
+
|
|
45
|
+
function classify(baseline, history) {
|
|
46
|
+
const violations = Array.isArray(baseline?.violations) ? baseline.violations : [];
|
|
47
|
+
return violations.map((v) => {
|
|
48
|
+
const touched = Number(v.files_touched) || 1;
|
|
49
|
+
const lines = Number(v.lines_estimate) || 5;
|
|
50
|
+
return {
|
|
51
|
+
class: v.class || "unknown",
|
|
52
|
+
path: v.path || "(unspecified)",
|
|
53
|
+
summary: v.summary || `${v.class} at ${v.path || "(unspecified)"}`,
|
|
54
|
+
risk: cap5(1 + Math.ceil(touched / 3)),
|
|
55
|
+
cost: cap5(1 + Math.ceil(lines / 30)),
|
|
56
|
+
benefit: cap5(recurrenceCount(history, v.class)),
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function main() {
|
|
62
|
+
const { baseline, history: histPath, out } = parseArgs(process.argv.slice(2));
|
|
63
|
+
const base = loadJSON(resolve(baseline));
|
|
64
|
+
if (!base) {
|
|
65
|
+
console.error(`gc-classify: cannot read baseline at ${baseline}`);
|
|
66
|
+
process.exit(2);
|
|
67
|
+
}
|
|
68
|
+
const hist = existsSync(resolve(histPath)) ? loadJSON(resolve(histPath), { runs: [] }) : { runs: [] };
|
|
69
|
+
const scored = classify(base, hist);
|
|
70
|
+
scored.sort((a, b) => b.benefit - a.benefit || a.cost - b.cost || a.risk - b.risk);
|
|
71
|
+
const payload = { total: scored.length, candidates: scored };
|
|
72
|
+
const text = JSON.stringify(payload, null, 2);
|
|
73
|
+
if (out) writeFileSync(resolve(out), text + "\n");
|
|
74
|
+
else process.stdout.write(text + "\n");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
main();
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: i18n-add-locale
|
|
3
|
+
description: Use this skill to scaffold a new human-language locale for the kit's skills/agents/CLAUDE.md. Mirrors every existing SKILL.md.hbs into a SKILL.md.<locale>.hbs stub so a translator (or LLM) can edit copy without touching machine-readable frontmatter. Default locale codes — vi, ja, fr, es, de — but accepts any 2-5 char code.
|
|
4
|
+
allowed-tools: Read, Write, Bash(node scripts/locale-scaffold.mjs:*)
|
|
5
|
+
suggested-turns: 4
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## When to invoke
|
|
9
|
+
|
|
10
|
+
- Adding a new human language to the kit (or a fork's downstream).
|
|
11
|
+
- After upstream adds a new English skill — re-running this skill scaffolds
|
|
12
|
+
the locale stubs for the new file, leaving translated files untouched.
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
|
|
16
|
+
1. **Pick a locale.** Two-to-five char ISO code (vi, ja, fr-CA, etc.).
|
|
17
|
+
2. **Dry-run the scan.**
|
|
18
|
+
```
|
|
19
|
+
node .claude/skills/i18n-add-locale/scripts/locale-scaffold.mjs \
|
|
20
|
+
--locale <code> --dry-run
|
|
21
|
+
```
|
|
22
|
+
Lists every SKILL.md / SKILL.md.hbs that lacks a `.<locale>.hbs` sibling.
|
|
23
|
+
3. **Materialize stubs.**
|
|
24
|
+
```
|
|
25
|
+
node .claude/skills/i18n-add-locale/scripts/locale-scaffold.mjs \
|
|
26
|
+
--locale <code>
|
|
27
|
+
```
|
|
28
|
+
For each missing sibling, copies the English master and prepends a
|
|
29
|
+
`<!-- LOCALE_TODO: translate body --> ` banner so the translator can grep
|
|
30
|
+
for pending work.
|
|
31
|
+
4. **Register the locale.** Edit `src/core/render-templates.mjs` and add the
|
|
32
|
+
code to `SUPPORTED_HUMAN_LANGS`. The renderer picks the variant via
|
|
33
|
+
`--locale <code>` or `HARNESS_LOCALE` env.
|
|
34
|
+
5. **Verify rendering** by running `agent-harness-kit init --locale <code>`
|
|
35
|
+
in a scratch dir and grepping the output for `LOCALE_TODO` (must be
|
|
36
|
+
zero before publishing).
|
|
37
|
+
|
|
38
|
+
## Output contract
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
locale: <code>
|
|
42
|
+
scaffolded: <N>
|
|
43
|
+
already-present: <M>
|
|
44
|
+
register-in: src/core/render-templates.mjs (SUPPORTED_HUMAN_LANGS)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Anti-patterns
|
|
48
|
+
|
|
49
|
+
- Don't translate the YAML frontmatter — the renderer + Claude Code parse
|
|
50
|
+
it as machine-readable. Translate only the body markdown.
|
|
51
|
+
- Don't drop the master `.hbs` file. The locale stub *augments*; the
|
|
52
|
+
renderer falls back to the master when the locale variant is missing.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<!-- LOCALE_TODO: translate body to vi -->
|
|
2
|
+
<!-- Source: .claude/skills/i18n-add-locale/SKILL.md -->
|
|
3
|
+
<!-- Edit only the markdown body — keep frontmatter verbatim so the kit's renderer + Claude Code parse it identically across locales. -->
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
name: i18n-add-locale
|
|
7
|
+
description: Use this skill to scaffold a new human-language locale for the kit's skills/agents/CLAUDE.md. Mirrors every existing SKILL.md.hbs into a SKILL.md.<locale>.hbs stub so a translator (or LLM) can edit copy without touching machine-readable frontmatter. Default locale codes — vi, ja, fr, es, de — but accepts any 2-5 char code.
|
|
8
|
+
allowed-tools: Read, Write, Bash(node scripts/locale-scaffold.mjs:*)
|
|
9
|
+
suggested-turns: 4
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## When to invoke
|
|
13
|
+
|
|
14
|
+
- Adding a new human language to the kit (or a fork's downstream).
|
|
15
|
+
- After upstream adds a new English skill — re-running this skill scaffolds
|
|
16
|
+
the locale stubs for the new file, leaving translated files untouched.
|
|
17
|
+
|
|
18
|
+
## Steps
|
|
19
|
+
|
|
20
|
+
1. **Pick a locale.** Two-to-five char ISO code (vi, ja, fr-CA, etc.).
|
|
21
|
+
2. **Dry-run the scan.**
|
|
22
|
+
```
|
|
23
|
+
node .claude/skills/i18n-add-locale/scripts/locale-scaffold.mjs \
|
|
24
|
+
--locale <code> --dry-run
|
|
25
|
+
```
|
|
26
|
+
Lists every SKILL.md / SKILL.md.hbs that lacks a `.<locale>.hbs` sibling.
|
|
27
|
+
3. **Materialize stubs.**
|
|
28
|
+
```
|
|
29
|
+
node .claude/skills/i18n-add-locale/scripts/locale-scaffold.mjs \
|
|
30
|
+
--locale <code>
|
|
31
|
+
```
|
|
32
|
+
For each missing sibling, copies the English master and prepends a
|
|
33
|
+
`<!-- LOCALE_TODO: translate body --> ` banner so the translator can grep
|
|
34
|
+
for pending work.
|
|
35
|
+
4. **Register the locale.** Edit `src/core/render-templates.mjs` and add the
|
|
36
|
+
code to `SUPPORTED_HUMAN_LANGS`. The renderer picks the variant via
|
|
37
|
+
`--locale <code>` or `HARNESS_LOCALE` env.
|
|
38
|
+
5. **Verify rendering** by running `agent-harness-kit init --locale <code>`
|
|
39
|
+
in a scratch dir and grepping the output for `LOCALE_TODO` (must be
|
|
40
|
+
zero before publishing).
|
|
41
|
+
|
|
42
|
+
## Output contract
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
locale: <code>
|
|
46
|
+
scaffolded: <N>
|
|
47
|
+
already-present: <M>
|
|
48
|
+
register-in: src/core/render-templates.mjs (SUPPORTED_HUMAN_LANGS)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Anti-patterns
|
|
52
|
+
|
|
53
|
+
- Don't translate the YAML frontmatter — the renderer + Claude Code parse
|
|
54
|
+
it as machine-readable. Translate only the body markdown.
|
|
55
|
+
- Don't drop the master `.hbs` file. The locale stub *augments*; the
|
|
56
|
+
renderer falls back to the master when the locale variant is missing.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// locale-scaffold.mjs — deterministic step for /i18n-add-locale.
|
|
3
|
+
// Walks .claude/skills/* (and a small whitelist of other files) and scaffolds
|
|
4
|
+
// missing `.<locale>.hbs` siblings from their English masters.
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// locale-scaffold.mjs --locale vi [--dry-run] [--root .claude/skills]
|
|
8
|
+
//
|
|
9
|
+
// A "master" file is either:
|
|
10
|
+
// - <stem>.md.hbs → scaffold sibling <stem>.md.<locale>.hbs
|
|
11
|
+
// - <stem>.md → scaffold sibling <stem>.md.<locale>.hbs
|
|
12
|
+
// (rare; only when no .hbs counterpart exists)
|
|
13
|
+
//
|
|
14
|
+
// Idempotent: if the sibling already exists, the script leaves it alone.
|
|
15
|
+
|
|
16
|
+
import { readFileSync, existsSync, writeFileSync, readdirSync, statSync } from "node:fs";
|
|
17
|
+
import { resolve, join, dirname, basename, relative } from "node:path";
|
|
18
|
+
|
|
19
|
+
const ROOT = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
20
|
+
|
|
21
|
+
function parseArgs(argv) {
|
|
22
|
+
const out = { locale: null, dryRun: false, roots: [".claude/skills", ".claude/agents"] };
|
|
23
|
+
for (let i = 0; i < argv.length; i++) {
|
|
24
|
+
if (argv[i] === "--locale") out.locale = argv[++i];
|
|
25
|
+
else if (argv[i] === "--dry-run") out.dryRun = true;
|
|
26
|
+
else if (argv[i] === "--root") out.roots = [argv[++i]];
|
|
27
|
+
}
|
|
28
|
+
if (!out.locale || !/^[a-z]{2,5}(-[A-Z]{2})?$/.test(out.locale)) {
|
|
29
|
+
console.error("usage: locale-scaffold.mjs --locale <code> [--dry-run] [--root <dir>]");
|
|
30
|
+
console.error(" <code> = 2-5 lowercase letters, optional region (e.g. vi, ja, fr-CA)");
|
|
31
|
+
process.exit(2);
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function* walk(dir) {
|
|
37
|
+
let entries;
|
|
38
|
+
try { entries = readdirSync(dir, { withFileTypes: true }); }
|
|
39
|
+
catch { return; }
|
|
40
|
+
for (const e of entries) {
|
|
41
|
+
const full = join(dir, e.name);
|
|
42
|
+
if (e.isDirectory()) yield* walk(full);
|
|
43
|
+
else yield full;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isMaster(path, locale) {
|
|
48
|
+
// English masters: *.md or *.md.hbs (but NOT *.md.<locale>.hbs already).
|
|
49
|
+
const name = basename(path);
|
|
50
|
+
if (!/\.md(?:\.hbs)?$/.test(name)) return false;
|
|
51
|
+
// Skip locale-specific files of any code.
|
|
52
|
+
if (/\.md\.[a-z]{2,5}(?:-[A-Z]{2})?\.hbs$/.test(name)) return false;
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function siblingPath(masterPath, locale) {
|
|
57
|
+
// Preserve the master's Handlebars-ness in the variant. Master .md.hbs
|
|
58
|
+
// → variant .md.<lang>.hbs (Handlebars-active). Master .md (plain) →
|
|
59
|
+
// variant .md.<lang> (no Handlebars). This matters because plain-md
|
|
60
|
+
// masters often contain literal `{{...}}` strings as examples (e.g.
|
|
61
|
+
// XSS demos in security-reviewer.md); promoting them to .hbs would
|
|
62
|
+
// make Handlebars choke on the example text.
|
|
63
|
+
const name = basename(masterPath);
|
|
64
|
+
if (name.endsWith(".md.hbs")) {
|
|
65
|
+
return join(dirname(masterPath), name.replace(/\.md\.hbs$/, `.md.${locale}.hbs`));
|
|
66
|
+
}
|
|
67
|
+
if (name.endsWith(".md")) {
|
|
68
|
+
return join(dirname(masterPath), name.replace(/\.md$/, `.md.${locale}`));
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function scaffold(masterPath, siblingPathAbs, locale, dryRun) {
|
|
74
|
+
if (existsSync(siblingPathAbs)) return { status: "skip", reason: "exists" };
|
|
75
|
+
if (dryRun) return { status: "would-create" };
|
|
76
|
+
const body = readFileSync(masterPath, "utf8");
|
|
77
|
+
const banner =
|
|
78
|
+
`<!-- LOCALE_TODO: translate body to ${locale} -->
|
|
79
|
+
<!-- Source: ${relative(ROOT, masterPath)} -->
|
|
80
|
+
<!-- Edit only the markdown body — keep frontmatter verbatim so the kit's renderer + Claude Code parse it identically across locales. -->
|
|
81
|
+
|
|
82
|
+
`;
|
|
83
|
+
writeFileSync(siblingPathAbs, banner + body);
|
|
84
|
+
return { status: "created" };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function main() {
|
|
88
|
+
const { locale, dryRun, roots } = parseArgs(process.argv.slice(2));
|
|
89
|
+
const masters = [];
|
|
90
|
+
for (const r of roots) {
|
|
91
|
+
const abs = resolve(ROOT, r);
|
|
92
|
+
if (!existsSync(abs)) continue;
|
|
93
|
+
for (const f of walk(abs)) {
|
|
94
|
+
if (isMaster(f, locale)) masters.push(f);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
let created = 0;
|
|
98
|
+
let skipped = 0;
|
|
99
|
+
const wouldCreate = [];
|
|
100
|
+
for (const m of masters) {
|
|
101
|
+
const sib = siblingPath(m, locale);
|
|
102
|
+
if (!sib) continue;
|
|
103
|
+
const res = scaffold(m, sib, locale, dryRun);
|
|
104
|
+
if (res.status === "created") created++;
|
|
105
|
+
else if (res.status === "would-create") wouldCreate.push(relative(ROOT, sib));
|
|
106
|
+
else skipped++;
|
|
107
|
+
}
|
|
108
|
+
const payload = {
|
|
109
|
+
locale,
|
|
110
|
+
dry_run: dryRun,
|
|
111
|
+
scaffolded: dryRun ? wouldCreate.length : created,
|
|
112
|
+
already_present: skipped,
|
|
113
|
+
scanned_masters: masters.length,
|
|
114
|
+
would_create: dryRun ? wouldCreate : undefined,
|
|
115
|
+
register_in: "src/core/render-templates.mjs#SUPPORTED_HUMAN_LANGS",
|
|
116
|
+
};
|
|
117
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
main();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<!-- LOCALE_TODO: translate body to vi -->
|
|
2
|
+
<!-- Source: .claude/skills/inspect-app/SKILL.md -->
|
|
3
|
+
<!-- Edit only the markdown body — keep frontmatter verbatim so the kit's renderer + Claude Code parse it identically across locales. -->
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
name: inspect-app
|
|
7
|
+
description: Use this skill whenever the user asks to "test the UI", "check what the app looks like", "inspect the page", "verify the dev server is up", or before claiming a UI feature is done. Boots the dev server via scripts/dev-up.sh and drives the failing flow through Playwright MCP if installed (else falls back to curl + lightweight HTML capture). Mirrors the OpenAI Chrome-DevTools-Protocol-into-runtime pattern at solo scale — verify the running app, don't trust the type checker alone.
|
|
8
|
+
allowed-tools: Read, Bash(scripts/dev-up.sh), Bash(curl:*), Bash(playwright:*), Bash(node:*)
|
|
9
|
+
suggested-turns: 12
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## When to use
|
|
13
|
+
|
|
14
|
+
The user said any of: "what does the page look like", "test the UI flow",
|
|
15
|
+
"is the dev server up", "before merging the UI work", or invoked this skill
|
|
16
|
+
explicitly via `/inspect-app`. Also auto-invokes from `/debug-flow` when the
|
|
17
|
+
bug is UI-shaped.
|
|
18
|
+
|
|
19
|
+
## Steps
|
|
20
|
+
|
|
21
|
+
1. **Detect dev server.** Read `harness.config.json` for the framework.
|
|
22
|
+
- If a process is already listening on the expected port (3000 / 8000 /
|
|
23
|
+
5000 depending on framework), reuse it.
|
|
24
|
+
- Else: `bash scripts/dev-up.sh &` in the background and wait up to 30s
|
|
25
|
+
for the readiness probe.
|
|
26
|
+
2. **Capture mode — Playwright MCP (preferred).** If `mcp__playwright__*`
|
|
27
|
+
tools are available:
|
|
28
|
+
- `mcp__playwright__browser_navigate` to the target URL
|
|
29
|
+
- `mcp__playwright__browser_snapshot` for accessibility tree
|
|
30
|
+
- `mcp__playwright__browser_take_screenshot` for a visual
|
|
31
|
+
- Optionally drive a single user flow (click → fill → click → wait)
|
|
32
|
+
3. **Capture mode — curl fallback.** If MCP is unavailable:
|
|
33
|
+
- `curl -i -s -o response.body "http://localhost:$PORT$PATH"` for headers + body
|
|
34
|
+
- `wc -l response.body` to size-check
|
|
35
|
+
- `grep -E '<title>|<h1>' response.body | head -5` for sanity
|
|
36
|
+
4. **Diff against expectation.** If the user gave an expected element /
|
|
37
|
+
text, grep for it. If not, just report what's on the page.
|
|
38
|
+
5. **Cleanup.** Kill the dev server we started (don't kill ones already
|
|
39
|
+
running before this session).
|
|
40
|
+
|
|
41
|
+
## Output contract
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
### App inspection
|
|
45
|
+
**URL:** http://localhost:<port><path>
|
|
46
|
+
**Status:** <HTTP status>
|
|
47
|
+
**Title:** <page title or first H1>
|
|
48
|
+
**Mode:** playwright-mcp | curl-fallback
|
|
49
|
+
**Screenshot:** <path or "n/a">
|
|
50
|
+
**Findings:** <bulleted list of matches/mismatches against expectation>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Anti-patterns
|
|
54
|
+
|
|
55
|
+
- Don't claim a UI feature is done without running this skill once.
|
|
56
|
+
- Don't leave a dev server running after the inspection — kill what you
|
|
57
|
+
started.
|
|
58
|
+
- Don't grep for "Error" alone as a failure signal — many pages legitimately
|
|
59
|
+
contain that word. Match against specific expected text instead.
|
|
60
|
+
- Don't take screenshots of pages with secrets or test fixtures with PII —
|
|
61
|
+
the screenshot lands on disk.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: inspect-module
|
|
3
3
|
description: Use this skill whenever the user mentions "explore", "inspect", "understand", "what does X do", "where is Y", or before adding a new feature in an unfamiliar area. Produces a structured map of one module — files, exports, dependencies, layer assignment, and recent commits — without reading the entire codebase. Always invoke this skill before editing an unfamiliar module so the agent has accurate context, not guesses.
|
|
4
|
-
allowed-tools: Read, Glob, Grep, Bash(git log:*), Bash(git ls-tree:*), Bash(tree:*)
|
|
4
|
+
allowed-tools: Read, Glob, Grep, Bash(git log:*), Bash(git ls-tree:*), Bash(tree:*), Bash(node .claude/skills/inspect-module/scripts/module-summary.mjs:*)
|
|
5
5
|
suggested-turns: 6
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -15,19 +15,22 @@ feature Y, what's in the area?", "explore <path>", "show me the shape of
|
|
|
15
15
|
|
|
16
16
|
1. **Resolve the target.** If the user gave a feature name (not a path), grep
|
|
17
17
|
`feature_list.json` for it. If multiple paths match, ask the user which.
|
|
18
|
-
2. **
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
2. **One-shot summary (deterministic).** Run the side-car script — bundles
|
|
19
|
+
exports + inbound + outbound deps + layer + recent commits into one JSON
|
|
20
|
+
blob, replacing three LLM turns of grep:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
node .claude/skills/inspect-module/scripts/module-summary.mjs <target>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Read the JSON. If `layer` is `null`, the file is outside any configured
|
|
27
|
+
layer root — flag that and ask whether the user wants to add it.
|
|
28
|
+
3. **Forward-only check.** Walk `outbound[]` and verify each crosses layers
|
|
29
|
+
forward only (never backward). The structural test enforces this
|
|
30
|
+
mechanically too, but flagging here short-circuits a wasted write step.
|
|
31
|
+
4. **Risks.** Flag any of: dynamic imports, eval, shell-out with
|
|
32
|
+
interpolation, missing tests for an exported function. (LLM judgment —
|
|
33
|
+
the side-car reports facts, not risks.)
|
|
31
34
|
|
|
32
35
|
## Output contract
|
|
33
36
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: inspect-module
|
|
3
|
+
description: Use this skill whenever the user mentions "explore", "inspect", "understand", "what does X do", "where is Y", or before adding a new feature in an unfamiliar area. Produces a structured map of one module — files, exports, dependencies, layer assignment, and recent commits — without reading the entire codebase. Always invoke this skill before editing an unfamiliar module so the agent has accurate context, not guesses.
|
|
4
|
+
allowed-tools: Read, Glob, Grep, Bash(git log:*), Bash(git ls-tree:*), Bash(tree:*), Bash(node .claude/skills/inspect-module/scripts/module-summary.mjs:*)
|
|
5
|
+
suggested-turns: 6
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Khi nào dùng
|
|
9
|
+
|
|
10
|
+
User hỏi bất cứ câu nào dạng: "X hoạt động như thế nào", "trong src/foo
|
|
11
|
+
có gì", "trước khi thêm feature Y, khu vực này có gì?", "explore <path>",
|
|
12
|
+
"hiện cho tôi shape của <module>".
|
|
13
|
+
|
|
14
|
+
## Các bước
|
|
15
|
+
|
|
16
|
+
1. **Resolve target.** Nếu user đưa tên feature (không phải path), grep
|
|
17
|
+
`feature_list.json` để tìm. Nếu nhiều path khớp, hỏi user chọn cái nào.
|
|
18
|
+
2. **Tóm tắt one-shot (deterministic).** Chạy side-car script — bundles
|
|
19
|
+
exports + inbound + outbound deps + layer + recent commits vào một JSON
|
|
20
|
+
blob, thay thế ba LLM turn grep:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
node .claude/skills/inspect-module/scripts/module-summary.mjs <target>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Đọc JSON. Nếu `layer` là `null`, file nằm ngoài layer root đã config —
|
|
27
|
+
flag điều đó và hỏi user có muốn thêm vào layer không.
|
|
28
|
+
3. **Forward-only check.** Đi qua `outbound[]` và xác minh mỗi outbound
|
|
29
|
+
cross layer theo chiều tiến (không bao giờ ngược). Structural test sẽ
|
|
30
|
+
enforce điều này cơ học rồi, nhưng flag ở đây tránh được một write step
|
|
31
|
+
bị lãng phí.
|
|
32
|
+
4. **Rủi ro.** Flag bất kỳ trường hợp nào: dynamic imports, eval, shell-out
|
|
33
|
+
với interpolation, missing tests cho function được export. (LLM judgment
|
|
34
|
+
— side-car report facts, không report risks.)
|
|
35
|
+
|
|
36
|
+
## Output contract
|
|
37
|
+
|
|
38
|
+
Tạo Markdown report với các section sau, theo thứ tự này:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
### Module: <path>
|
|
42
|
+
### Layer: <layer-name>
|
|
43
|
+
### Public surface: <list>
|
|
44
|
+
### Inbound deps: <list of paths>
|
|
45
|
+
### Outbound deps: <list of paths or external packages>
|
|
46
|
+
### Recent changes: <top 3 commit messages>
|
|
47
|
+
### Risks: <bulleted list, "none" nếu clean>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Kết thúc bằng: "Tôi sẵn sàng thay đổi `<module>`. Architecture-reviewer
|
|
51
|
+
subagent sẽ được gọi khi hoàn tất."
|
|
52
|
+
|
|
53
|
+
## Anti-patterns
|
|
54
|
+
|
|
55
|
+
- Không đọc mọi file trong module — sample exports, rồi chỉ drill in
|
|
56
|
+
vào nơi mà task của user chỉ tới.
|
|
57
|
+
- Không đề xuất changes trong skill này. Đây là read-only context-gathering.
|