any-doctor 0.0.1
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/CONTEXT.md +128 -0
- package/README.md +68 -0
- package/bin/capabilities.d.ts +15 -0
- package/bin/capabilities.js +131 -0
- package/bin/cli.d.ts +2 -0
- package/bin/cli.js +426 -0
- package/bin/clipboard.d.ts +1 -0
- package/bin/clipboard.js +10 -0
- package/bin/contract.d.ts +134 -0
- package/bin/contract.js +70 -0
- package/bin/dashboard.d.ts +108 -0
- package/bin/dashboard.js +718 -0
- package/bin/discover.d.ts +24 -0
- package/bin/discover.js +87 -0
- package/bin/doctor-loader.d.mts +1 -0
- package/bin/doctor-loader.mjs +161 -0
- package/bin/engine.d.ts +18 -0
- package/bin/engine.js +22 -0
- package/bin/fuzzy.d.ts +2 -0
- package/bin/fuzzy.js +31 -0
- package/bin/import-guard.mjs +31 -0
- package/bin/keys.d.ts +2 -0
- package/bin/keys.js +72 -0
- package/bin/palette.d.ts +7 -0
- package/bin/palette.js +14 -0
- package/bin/picker.d.ts +12 -0
- package/bin/picker.js +82 -0
- package/bin/report.d.ts +18 -0
- package/bin/report.js +159 -0
- package/bin/runner.d.ts +58 -0
- package/bin/runner.js +271 -0
- package/bin/score.d.ts +13 -0
- package/bin/score.js +39 -0
- package/bin/sdk.d.ts +5 -0
- package/bin/sdk.js +95 -0
- package/bin/search-host.d.ts +6 -0
- package/bin/search-host.js +56 -0
- package/bin/select.d.ts +35 -0
- package/bin/select.js +45 -0
- package/bin/tty.d.ts +38 -0
- package/bin/tty.js +94 -0
- package/docs/REPAIR-LOG.md +45 -0
- package/docs/RESULTS.md +70 -0
- package/docs/decisions.md +450 -0
- package/docs/example-catalog.md +122 -0
- package/docs/features.md +67 -0
- package/docs/first-shot-results.md +18 -0
- package/docs/intents.md +21 -0
- package/docs/kill-test.md +54 -0
- package/docs/research.md +66 -0
- package/docs/vision.md +83 -0
- package/doctors/AGENTS.md +103 -0
- package/doctors/api-route-files-do-import.fixtures.mjs +61 -0
- package/doctors/api-route-files-do-import.mjs +26 -0
- package/doctors/async-doctor.fixtures.mjs +147 -0
- package/doctors/async-doctor.mjs +295 -0
- package/doctors/convex-doctor.fixtures.mjs +177 -0
- package/doctors/convex-doctor.mjs +223 -0
- package/doctors/date-now-used-inside-effect.fixtures.mjs +46 -0
- package/doctors/date-now-used-inside-effect.mjs +132 -0
- package/doctors/json-parse-calls-llm-api.fixtures.mjs +37 -0
- package/doctors/json-parse-calls-llm-api.mjs +85 -0
- package/doctors/route-handlers-touch-database-before.fixtures.mjs +58 -0
- package/doctors/route-handlers-touch-database-before.mjs +98 -0
- package/doctors/z-record-called-with-single.fixtures.mjs +28 -0
- package/doctors/z-record-called-with-single.mjs +19 -0
- package/fixtures/sample-app/src/hooks/useChat.ts +15 -0
- package/fixtures/sample-app/src/lib/ai/client.ts +5 -0
- package/fixtures/sample-app/src/schemas/user.ts +6 -0
- package/fixtures/sample-app/src/services/chat.ts +17 -0
- package/fixtures/sample-app/src/services/user.ts +10 -0
- package/fixtures/sample-app/src/utils/sync.ts +16 -0
- package/package.json +42 -0
- package/skill/any-doctor.skill.md +188 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# any-doctor — doctor generation skill
|
|
2
|
+
|
|
3
|
+
You are writing a **doctor program**: a small analysis tool that detects one
|
|
4
|
+
codebase convention, plus the fixtures that prove it works. The INTENT is
|
|
5
|
+
the entire specification. Derive everything from the intent alone — never
|
|
6
|
+
scan a target repository to tune your doctor against its code.
|
|
7
|
+
|
|
8
|
+
## The contract
|
|
9
|
+
|
|
10
|
+
Two files, exactly:
|
|
11
|
+
|
|
12
|
+
`doctors/<slug>.mjs` (slug = short kebab-case from the intent):
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
export const meta = {
|
|
16
|
+
id: "<slug>", // kebab-case, matches the filename
|
|
17
|
+
description: "<one-line finding text, shown in reports>",
|
|
18
|
+
severity: "warning", // default: "error" | "warning" | "info"
|
|
19
|
+
category: "<bugs | performance | security | style | ...>",
|
|
20
|
+
blindSpots: [ // what this approach CANNOT see — required
|
|
21
|
+
"<honest limitation>",
|
|
22
|
+
],
|
|
23
|
+
checks: [ // optional: multiple related checks in ONE doctor
|
|
24
|
+
{
|
|
25
|
+
id: "<check-id>", // short kebab noun phrase, [a-z0-9-], unique per doctor; name the defect
|
|
26
|
+
description: "<finding text>",
|
|
27
|
+
severity: "warning",
|
|
28
|
+
impact: "<one line: what goes wrong for the user if this ships>",
|
|
29
|
+
why: "<one line: what in the code triggers this>",
|
|
30
|
+
fix: "<one line: the corrective action>",
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export async function doctor(ctx) {
|
|
36
|
+
// inspect the codebase, emit findings
|
|
37
|
+
// ctx.report.finding({ rule: "<check-id>", file, line, column?, message?, severity? })
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`doctors/<slug>.fixtures.mjs`:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
export const fixtures = [
|
|
45
|
+
{
|
|
46
|
+
name: "<what this case proves>",
|
|
47
|
+
seed: { "src/example.ts": "...inline file contents..." },
|
|
48
|
+
expected: [{ file: "src/example.ts", line: 2 }], // exact set on (file, line)
|
|
49
|
+
},
|
|
50
|
+
// include cases where expected: [] — innocent lookalikes must stay silent
|
|
51
|
+
];
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`ctx` is the only capability a doctor has:
|
|
55
|
+
|
|
56
|
+
- `ctx.files.list(exts?)` → relative paths (default .ts/.tsx/.js/.jsx/.mjs)
|
|
57
|
+
- `ctx.files.read(rel)` → file contents
|
|
58
|
+
- `ctx.search.pattern(pattern, language?)` → `[{ file, line, column, text }]`
|
|
59
|
+
(ast-grep pattern syntax, e.g. `"fetch($URL)"`; requires ast-grep installed)
|
|
60
|
+
- `ctx.report.finding({ file, line, column?, message?, severity? })`
|
|
61
|
+
|
|
62
|
+
Zero dependencies, zero imports — a doctor is one self-contained file;
|
|
63
|
+
everything reaches it through `ctx`. Node >= 18.
|
|
64
|
+
|
|
65
|
+
## One doctor, many checks
|
|
66
|
+
|
|
67
|
+
If the intent covers several related patterns (an SDK migration with
|
|
68
|
+
multiple outdated calls, a family of anti-patterns, "all the stripe
|
|
69
|
+
practices we deprecated"), build ONE doctor with multiple checks — not one
|
|
70
|
+
doctor per pattern:
|
|
71
|
+
|
|
72
|
+
- Declare each check in `meta.checks` (`{ id, description, severity? }`).
|
|
73
|
+
- Emit each finding with `rule: "<check-id>"` matching a declared check.
|
|
74
|
+
- The report groups findings under each check's description, under the
|
|
75
|
+
doctor's umbrella.
|
|
76
|
+
|
|
77
|
+
Bundle when the checks share a theme or a codebase area. Stay separate
|
|
78
|
+
when the checks are unrelated concerns that would be deleted or shared
|
|
79
|
+
independently.
|
|
80
|
+
|
|
81
|
+
## Severity judgment
|
|
82
|
+
|
|
83
|
+
If the intent dictates criticality ("make the findings critical"), follow
|
|
84
|
+
it. Otherwise judge by consequence:
|
|
85
|
+
|
|
86
|
+
- `error` — will break, lose, or expose data at runtime
|
|
87
|
+
- `warning` — wrong or risky but non-fatal
|
|
88
|
+
- `info` — stylistic or informational
|
|
89
|
+
|
|
90
|
+
Per-check severity lives in `meta.checks`; a finding may override with its
|
|
91
|
+
own `severity` only for exceptions.
|
|
92
|
+
|
|
93
|
+
## Hard workflow
|
|
94
|
+
|
|
95
|
+
1. Write both files.
|
|
96
|
+
2. Run: `any-doctor verify doctors/<slug>.mjs` (or `node bin/cli.js verify doctors/<slug>.mjs`).
|
|
97
|
+
3. ALL fixtures must pass. Iterate until green — a failed fixture is the
|
|
98
|
+
system telling you your rule or your expectations are wrong; fix the rule,
|
|
99
|
+
or fix the expectation if the expectation itself was wrong.
|
|
100
|
+
4. Report: id, what it detects, declared blind spots, fixture count.
|
|
101
|
+
|
|
102
|
+
## Fixture discipline
|
|
103
|
+
|
|
104
|
+
- Matching is exact-set on (file, line): a missing expected finding fails
|
|
105
|
+
(recall); an unexpected extra finding fails (precision). Get both sides right.
|
|
106
|
+
- Every doctor needs at least one `expected: []` fixture containing code that
|
|
107
|
+
LOOKS like a violation but isn't — the lookalike that a naive implementation
|
|
108
|
+
would wrongly flag.
|
|
109
|
+
- Cover the simplest violation and the sneakiest one. Vary quote styles where
|
|
110
|
+
syntax allows.
|
|
111
|
+
- Findings are locations. Wording lives in `meta.description`; use
|
|
112
|
+
per-finding `message` only when one violation needs its own explanation.
|
|
113
|
+
|
|
114
|
+
## Authoring for the report
|
|
115
|
+
|
|
116
|
+
The report's detail pane shows, per check: category · severity · location,
|
|
117
|
+
then **Impact**, the code frame, and **Fix**. Write `impact`, `why`, and
|
|
118
|
+
`fix` on every check — one line each, plain language, no code in them.
|
|
119
|
+
They are what the user reads while deciding whether to care. `impact` =
|
|
120
|
+
consequence if it ships; `why` = the code shape that triggers the check;
|
|
121
|
+
`fix` = the corrective action in one sentence.
|
|
122
|
+
|
|
123
|
+
## Honesty rules
|
|
124
|
+
|
|
125
|
+
- `blindSpots` is required and is data: declare what your approach cannot
|
|
126
|
+
see (aliased imports, cross-file references, dynamic constructs).
|
|
127
|
+
- Never silently narrow the intent. If the intent is not fully statically
|
|
128
|
+
checkable, implement the closest honest version and declare the gap.
|
|
129
|
+
- A clean run must mean clean. Never swallow errors into empty findings.
|
|
130
|
+
|
|
131
|
+
## Capability rules (the runtime gate enforces these)
|
|
132
|
+
|
|
133
|
+
A doctor's entire world is the target repo through `ctx`. Anything else is
|
|
134
|
+
out of contract: the capability gate scans the program before it ever runs
|
|
135
|
+
and refuses to execute it — there is no override:
|
|
136
|
+
|
|
137
|
+
- **A doctor is one self-contained file.** No imports at all — not node
|
|
138
|
+
builtins, not local helpers, not npm packages. Everything reaches you
|
|
139
|
+
through `ctx`; every import is refused at runtime.
|
|
140
|
+
- **No network calls. Ever.** No fetch, http/https, net, tls, dns, dgram.
|
|
141
|
+
There is no legitimate reason for a doctor to reach the network — and the
|
|
142
|
+
network globals are deleted from the process before your code runs.
|
|
143
|
+
- **No file writes.** Doctors read; they never write, delete, or rename.
|
|
144
|
+
- **No subprocesses.** Searching goes through `ctx.search` — never spawn
|
|
145
|
+
anything yourself.
|
|
146
|
+
- Read files through `ctx.files.read` (it is repo-scoped); it is the only
|
|
147
|
+
way to read — imports are refused outright.
|
|
148
|
+
- `process.env` is flagged too. If you need configuration, it does not
|
|
149
|
+
belong in a doctor — the target repo is the input.
|
|
150
|
+
|
|
151
|
+
The scan is layered with runtime enforcement: on runtimes that support it,
|
|
152
|
+
doctors execute under Node's permission model, so file writes and
|
|
153
|
+
subprocesses are denied by the process itself — even code the scan cannot
|
|
154
|
+
see. Worker threads exist only as the import guard's carrier and inherit
|
|
155
|
+
every denial.
|
|
156
|
+
|
|
157
|
+
One helper recurs across doctor programs by design: the comment/string
|
|
158
|
+
masking function that blanks non-code before pattern matching. The
|
|
159
|
+
single-file law forbids importing it, so copy it in — that duplication is
|
|
160
|
+
the contract working, not a smell to fix.
|
|
161
|
+
|
|
162
|
+
If you cannot implement the intent without breaking a rule, the intent is
|
|
163
|
+
out of scope for a doctor: say so in your final report instead.
|
|
164
|
+
|
|
165
|
+
## Failure lessons (each of these shipped in a real doctor — do not repeat)
|
|
166
|
+
|
|
167
|
+
1. `String.replace(text, x)` replaces only the FIRST occurrence. When
|
|
168
|
+
substituting placeholders into a regex source string, use a global regex:
|
|
169
|
+
`.replace(/NAME/g, escaped)`.
|
|
170
|
+
2. Escape anything variable you interpolate into a RegExp
|
|
171
|
+
(`s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")`).
|
|
172
|
+
3. Match the innermost construct once. `await fetch(x)` contains a
|
|
173
|
+
`fetch(x)` node — a pattern for the inner call already matches it;
|
|
174
|
+
adding wrapper variants double-reports.
|
|
175
|
+
4. Read each file once per pass. Don't re-read inside loops.
|
|
176
|
+
5. If the intent is not fully statically checkable (cross-file references,
|
|
177
|
+
runtime behavior, types), implement the closest honest version and
|
|
178
|
+
declare the gap in `blindSpots`. Never drop an intent condition silently.
|
|
179
|
+
6. Your own `console.log` goes to stderr, not the report. Don't rely on
|
|
180
|
+
stdout — stdout carries only the structured result.
|
|
181
|
+
|
|
182
|
+
## What NOT to do
|
|
183
|
+
|
|
184
|
+
- Don't touch any file other than the two contract files.
|
|
185
|
+
- Don't add dependencies — and don't import anything at all, not even
|
|
186
|
+
`node:` builtins; every import is refused at runtime.
|
|
187
|
+
- Don't scan a target repository to "tune" the doctor against its code.
|
|
188
|
+
- Don't mark a fixture green by weakening the expectation — strengthen the doctor.
|