ts-reviewer 1.2.1 → 2.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/README.md +39 -5
- package/dist/cli.js +11 -16
- package/dist/cli.js.map +1 -1
- package/dist/index.js +18 -54
- package/dist/index.js.map +1 -1
- package/dist/io.js +10 -28
- package/dist/io.js.map +1 -1
- package/dist/paths.js +17 -24
- package/dist/paths.js.map +1 -1
- package/dist/prompt.js +17 -23
- package/dist/prompt.js.map +1 -1
- package/package.json +7 -5
- package/ts-reviewer/SKILL.md +177 -318
- package/ts-reviewer/references/architecture.md +84 -140
- package/ts-reviewer/references/async-patterns.md +46 -106
- package/ts-reviewer/references/boundary-validation.md +29 -56
- package/ts-reviewer/references/code-quality.md +63 -112
- package/ts-reviewer/references/dependency-hygiene.md +31 -47
- package/ts-reviewer/references/error-handling.md +27 -49
- package/ts-reviewer/references/fix-workflow.md +92 -295
- package/ts-reviewer/references/modernization.md +58 -138
- package/ts-reviewer/references/security.md +56 -118
- package/ts-reviewer/references/tsconfig.md +42 -92
- package/ts-reviewer/references/type-safety.md +66 -148
- package/dist/hash.js +0 -11
- package/dist/hash.js.map +0 -1
- package/dist/incoming.js +0 -34
- package/dist/incoming.js.map +0 -1
- package/dist/meta.js +0 -27
- package/dist/meta.js.map +0 -1
- package/dist/updatePolicy.js +0 -18
- package/dist/updatePolicy.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
An AI skill for deep code review and auto-fix of pure TypeScript codebases. Finds bugs, type safety holes, security vulnerabilities, async anti-patterns, outdated practices, and code smells — then fixes them with regression tests and verification.
|
|
4
4
|
|
|
5
|
-
Built for **TypeScript 5.9
|
|
5
|
+
Built for one fixed stack — **TypeScript 5.9.x, ES2024, Node 24** — without any framework-specific checks (no React, Vue, Angular, etc.). Anything below the stack is a finding, anything above it is never recommended.
|
|
6
6
|
|
|
7
7
|
## What It Does
|
|
8
8
|
|
|
@@ -44,7 +44,7 @@ The installer prints a short summary before installation:
|
|
|
44
44
|
```text
|
|
45
45
|
TypeScript Code Reviewer
|
|
46
46
|
Checks: type safety, security, async patterns, boundary validation, error handling, modernization, code quality, tsconfig, dependency hygiene
|
|
47
|
-
Target
|
|
47
|
+
Target stack: TypeScript 5.9.x, ES2024, Node 24
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
Then it asks which AI agents to install for. Use Up/Down arrows to move, Space to toggle, and Enter to confirm.
|
|
@@ -92,6 +92,7 @@ By default, only the nine core domains run. Use flags to control which domains a
|
|
|
92
92
|
| *(none)* | Type Safety, Security, Async, Modernization, Code Quality, Config, Boundary Validation, Error Handling, Dependency Hygiene |
|
|
93
93
|
| `--arch` | Architecture only (shallow modules, coupling, dependency direction, seams) |
|
|
94
94
|
| `--full` | All ten domains |
|
|
95
|
+
| `--no-arch` | The nine core domains — overrides `--arch`, `--full`, and any phrase that would enable architecture |
|
|
95
96
|
|
|
96
97
|
Examples:
|
|
97
98
|
|
|
@@ -175,11 +176,24 @@ Architecture findings use the same scale. Each candidate also carries a **Fixabi
|
|
|
175
176
|
## Project Structure
|
|
176
177
|
|
|
177
178
|
```
|
|
179
|
+
AGENTS.md # How to edit the review rules — read before changing anything below
|
|
180
|
+
CLAUDE.md # Pointer to AGENTS.md, picked up automatically by Claude Code
|
|
181
|
+
|
|
178
182
|
src/ # npm/npx installer source
|
|
179
183
|
├── cli.ts # CLI entrypoint and provider prompt
|
|
180
184
|
├── prompt.ts # raw-mode keyboard multi-select
|
|
181
185
|
└── paths.ts # target directories and skill asset loading
|
|
182
186
|
|
|
187
|
+
cnlp/ # the CNL-P format the skill files are written in
|
|
188
|
+
├── cnlp-format.md # the standard: forms, line rules, lexicon
|
|
189
|
+
├── cnlp.js # the checker — Node builtins only, no dependencies
|
|
190
|
+
├── skill-format.test.js # the conformance test, run by `npm test`
|
|
191
|
+
└── profiles/ # what each kind of document may contain
|
|
192
|
+
├── skill.md # → ts-reviewer/SKILL.md
|
|
193
|
+
├── reference.md # → ts-reviewer/references/*.md
|
|
194
|
+
├── guide.md # → AGENTS.md
|
|
195
|
+
└── profile.md # → the profiles themselves
|
|
196
|
+
|
|
183
197
|
ts-reviewer/
|
|
184
198
|
├── SKILL.md # Main skill file — mode routing, workflow orchestration
|
|
185
199
|
└── references/
|
|
@@ -188,7 +202,7 @@ ts-reviewer/
|
|
|
188
202
|
├── async-patterns.md # Checklist: floating promises, races, timeouts, retries, cancellation
|
|
189
203
|
├── boundary-validation.md # Checklist: runtime validation at edges, DTO/domain separation
|
|
190
204
|
├── error-handling.md # Checklist: silent failures, throw hygiene, failure design
|
|
191
|
-
├── modernization.md # Checklist:
|
|
205
|
+
├── modernization.md # Checklist: patterns below the stack, ??/?. , satisfies, using, toSorted
|
|
192
206
|
├── code-quality.md # Checklist: complexity, dead code, debug artifacts, testability
|
|
193
207
|
├── tsconfig.md # Checklist: strict flags, target/lib, module resolution, deprecated
|
|
194
208
|
├── dependency-hygiene.md # Checklist: lockfiles, versions, npm audit, dependency choice
|
|
@@ -200,6 +214,26 @@ ts-reviewer/
|
|
|
200
214
|
|
|
201
215
|
**Reference files** contain the detailed checklists and protocols. Each analysis agent reads only the reference file relevant to its domain, keeping context focused. Architecture analysis is opt-in and loaded only when the domain is active.
|
|
202
216
|
|
|
217
|
+
## Editing the Rules
|
|
218
|
+
|
|
219
|
+
As of 2.0.0, `SKILL.md` and every reference file are written in **CNL-P** — a block-structured format an agent reads as instructions rather than prose. One rule per line, one term per concept, no headings, a hard 250-character line limit.
|
|
220
|
+
|
|
221
|
+
A check line looks like this:
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
- injection — `eval()` and `new Function()` executing a dynamic string: Highest, use a lookup table, a strategy, or a safe parser
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
The format is enforced, not merely recommended:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
npm test # typecheck + 5 conformance tests
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The test catches a check line that lost its severity, a block the profile does not declare, blocks out of order, a banned vague word, and a line over the limit — each reported with its file and line number.
|
|
234
|
+
|
|
235
|
+
**Before adding or changing a rule, read [`AGENTS.md`](AGENTS.md).** It states which file owns which domain, the shape of a check line, the severity scale, which block takes what, and the line rules. Claude Code picks this up on its own through `CLAUDE.md`; for another agent, point it at `AGENTS.md` explicitly.
|
|
236
|
+
|
|
203
237
|
## How It Works Under the Hood
|
|
204
238
|
|
|
205
239
|
### Scan mode
|
|
@@ -232,9 +266,9 @@ ts-reviewer/
|
|
|
232
266
|
|
|
233
267
|
## Requirements
|
|
234
268
|
|
|
235
|
-
- TypeScript 5.9
|
|
269
|
+
- TypeScript 5.9.x project targeting ES2024 on Node 24
|
|
236
270
|
- Git repository (for scoped modes and safe revert during fix)
|
|
237
|
-
- Node
|
|
271
|
+
- Node 24 with `npx` available (for tsc, linter)
|
|
238
272
|
- Claude Code (recommended) or any Claude interface with skill support
|
|
239
273
|
|
|
240
274
|
## License
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const prompt_1 = require("./prompt");
|
|
6
|
-
const paths_1 = require("./paths");
|
|
2
|
+
import { scaffoldTsReviewerSkill } from "./index.js";
|
|
3
|
+
import { multiSelect } from "./prompt.js";
|
|
4
|
+
import { AI_PROVIDERS, skillTargetDir } from "./paths.js";
|
|
7
5
|
const SKILL_NAME = "ts-reviewer";
|
|
8
|
-
const
|
|
6
|
+
const TARGET_STACK = "TypeScript 5.9.x, ES2024, Node 24";
|
|
9
7
|
function printHelp() {
|
|
10
8
|
process.stdout.write([
|
|
11
9
|
"ts-reviewer: install TypeScript review skill\n",
|
|
@@ -27,26 +25,23 @@ async function main() {
|
|
|
27
25
|
const skillName = SKILL_NAME;
|
|
28
26
|
process.stdout.write([
|
|
29
27
|
"TypeScript Code Reviewer\n",
|
|
30
|
-
`Checks: type safety, async patterns,
|
|
31
|
-
`Target
|
|
28
|
+
`Checks: type safety, security, async patterns, boundary validation, error handling, modernization, code quality, tsconfig, dependency hygiene\n`,
|
|
29
|
+
`Target stack: ${TARGET_STACK}\n`,
|
|
32
30
|
"\n",
|
|
33
31
|
].join(""));
|
|
34
|
-
const selected = await
|
|
32
|
+
const selected = await multiSelect("Install for which AI agents? (Space = toggle, Enter = confirm)", AI_PROVIDERS);
|
|
35
33
|
if (selected.length === 0) {
|
|
36
34
|
process.stdout.write("No agents selected. Exiting.\n");
|
|
37
35
|
return;
|
|
38
36
|
}
|
|
39
37
|
for (const provider of selected) {
|
|
40
|
-
const targetDir =
|
|
41
|
-
const providerLabel =
|
|
38
|
+
const targetDir = skillTargetDir(cwd, provider, skillName);
|
|
39
|
+
const providerLabel = AI_PROVIDERS.find((p) => p.value === provider).label;
|
|
42
40
|
process.stdout.write(`\n[${providerLabel}]\n`);
|
|
43
|
-
const result = await
|
|
41
|
+
const result = await scaffoldTsReviewerSkill({ cwd, skillName, targetDir });
|
|
44
42
|
for (const e of result.entries) {
|
|
45
|
-
|
|
46
|
-
process.stdout.write(`${e.action}\t${e.relativePath}${suffix}\n`);
|
|
43
|
+
process.stdout.write(`${e.action}\t${e.relativePath}\n`);
|
|
47
44
|
}
|
|
48
|
-
if (result.hadConflicts)
|
|
49
|
-
process.exitCode = 2;
|
|
50
45
|
}
|
|
51
46
|
}
|
|
52
47
|
main().catch((err) => {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAmB,MAAM,YAAY,CAAC;AAE3E,MAAM,UAAU,GAAG,aAAa,CAAC;AACjC,MAAM,YAAY,GAAG,mCAAmC,CAAC;AAEzD,SAAS,SAAS;IAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB;QACE,gDAAgD;QAChD,IAAI;QACJ,UAAU;QACV,qBAAqB;QACrB,IAAI;QACJ,YAAY;QACZ,+BAA+B;KAChC,CAAC,IAAI,CAAC,EAAE,CAAC,CACX,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnD,SAAS,EAAE,CAAC;QACZ,OAAO;IACT,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,SAAS,GAAG,UAAU,CAAC;IAE7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB;QACE,4BAA4B;QAC5B,iJAAiJ;QACjJ,iBAAiB,YAAY,IAAI;QACjC,IAAI;KACL,CAAC,IAAI,CAAC,EAAE,CAAC,CACX,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,WAAW,CAChC,gEAAgE,EAChE,YAAY,CACb,CAAC;IAEF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACvD,OAAO;IACT,CAAC;IAED,KAAK,MAAM,QAAQ,IAAI,QAAwB,EAAE,CAAC;QAChD,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC3D,MAAM,aAAa,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAE,CAAC,KAAK,CAAC;QAE5E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,aAAa,KAAK,CAAC,CAAC;QAE/C,MAAM,MAAM,GAAG,MAAM,uBAAuB,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;QAE5E,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAE,GAAa,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAE,GAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACzF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,60 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const updatePolicy_1 = require("./updatePolicy");
|
|
13
|
-
const incoming_1 = require("./incoming");
|
|
14
|
-
async function scaffoldTsReviewerSkill(opts) {
|
|
15
|
-
const skillName = opts.skillName;
|
|
16
|
-
const targetDir = opts.targetDir;
|
|
17
|
-
await (0, io_1.ensureDir)(targetDir);
|
|
18
|
-
const metaPath = node_path_1.default.join(targetDir, ".scaffold-meta.json");
|
|
19
|
-
const meta = (await (0, meta_1.readMeta)(metaPath)) ?? { version: 1, files: {} };
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { listSkillAssetTemplates } from "./paths.js";
|
|
5
|
+
import { ensureDir, atomicWriteFile } from "./io.js";
|
|
6
|
+
export async function scaffoldTsReviewerSkill(opts) {
|
|
7
|
+
const { cwd, skillName, targetDir } = opts;
|
|
8
|
+
await ensureDir(targetDir);
|
|
9
|
+
// Provenance file written by <=2.0.0, when the installer tracked hashes to avoid
|
|
10
|
+
// overwriting local edits. Skill files are generated, so they are always replaced now.
|
|
11
|
+
await fs.rm(path.join(targetDir, ".scaffold-meta.json"), { force: true });
|
|
20
12
|
const entries = [];
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
const desired = t.content;
|
|
26
|
-
const desiredHash = (0, hash_1.sha256Hex)(desired);
|
|
27
|
-
const existing = await (0, io_1.readFileIfExists)(dstPath);
|
|
28
|
-
const existingHash = existing === null ? null : (0, hash_1.sha256Hex)(existing);
|
|
29
|
-
const lastWrittenHash = meta.files[t.fileName]?.lastWrittenHash ?? null;
|
|
30
|
-
const decision = (0, updatePolicy_1.decideUpdate)({ existingHash, lastWrittenHash });
|
|
31
|
-
if (decision.kind === "write") {
|
|
32
|
-
await (0, io_1.atomicWriteFile)(dstPath, desired);
|
|
33
|
-
meta.files[t.fileName] = { lastWrittenHash: desiredHash };
|
|
34
|
-
entries.push({ action: "WROTE", relativePath });
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
if (decision.kind === "overwrite") {
|
|
38
|
-
await (0, io_1.atomicWriteFile)(dstPath, desired);
|
|
39
|
-
meta.files[t.fileName] = { lastWrittenHash: desiredHash };
|
|
40
|
-
entries.push({ action: "UPDATED", relativePath });
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (decision.kind === "skip") {
|
|
44
|
-
entries.push({ action: "SKIPPED", relativePath });
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
// conflict
|
|
48
|
-
hadConflicts = true;
|
|
49
|
-
const incomingPath = await (0, incoming_1.findAvailableIncomingPath)(dstPath);
|
|
50
|
-
await (0, io_1.atomicWriteFile)(incomingPath, desired);
|
|
13
|
+
for (const t of listSkillAssetTemplates(skillName)) {
|
|
14
|
+
const dstPath = path.join(targetDir, t.fileName);
|
|
15
|
+
const action = existsSync(dstPath) ? "UPDATED" : "WROTE";
|
|
16
|
+
await atomicWriteFile(dstPath, t.content);
|
|
51
17
|
entries.push({
|
|
52
|
-
action
|
|
53
|
-
relativePath,
|
|
54
|
-
detail: node_path_1.default.basename(incomingPath),
|
|
18
|
+
action,
|
|
19
|
+
relativePath: path.relative(cwd, dstPath).replace(/\\/g, "/"),
|
|
55
20
|
});
|
|
56
21
|
}
|
|
57
|
-
|
|
58
|
-
return { entries, hadConflicts };
|
|
22
|
+
return { entries };
|
|
59
23
|
}
|
|
60
24
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAarD,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,IAA2D;IACvG,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC3C,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IAE3B,iFAAiF;IACjF,uFAAuF;IACvF,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,qBAAqB,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1E,MAAM,OAAO,GAAoB,EAAE,CAAC;IAEpC,KAAK,MAAM,CAAC,IAAI,uBAAuB,CAAC,SAAS,CAAC,EAAE,CAAC;QACnD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;QACjD,MAAM,MAAM,GAAmB,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;QAEzE,MAAM,eAAe,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QAE1C,OAAO,CAAC,IAAI,CAAC;YACX,MAAM;YACN,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SAC9D,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC"}
|
package/dist/io.js
CHANGED
|
@@ -1,39 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ensureDir = ensureDir;
|
|
7
|
-
exports.readFileIfExists = readFileIfExists;
|
|
8
|
-
exports.atomicWriteFile = atomicWriteFile;
|
|
9
|
-
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
10
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
-
async function ensureDir(dirPath) {
|
|
12
|
-
await promises_1.default.mkdir(dirPath, { recursive: true });
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export async function ensureDir(dirPath) {
|
|
4
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
13
5
|
}
|
|
14
|
-
async function
|
|
15
|
-
|
|
16
|
-
return await promises_1.default.readFile(filePath, "utf8");
|
|
17
|
-
}
|
|
18
|
-
catch (e) {
|
|
19
|
-
if (e && (e.code === "ENOENT" || e.code === "ENOTDIR"))
|
|
20
|
-
return null;
|
|
21
|
-
throw e;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
async function atomicWriteFile(filePath, content) {
|
|
25
|
-
const dir = node_path_1.default.dirname(filePath);
|
|
6
|
+
export async function atomicWriteFile(filePath, content) {
|
|
7
|
+
const dir = path.dirname(filePath);
|
|
26
8
|
await ensureDir(dir);
|
|
27
|
-
const tmp =
|
|
28
|
-
await
|
|
9
|
+
const tmp = path.join(dir, `.${path.basename(filePath)}.tmp-${process.pid}-${Date.now()}`);
|
|
10
|
+
await fs.writeFile(tmp, content, "utf8");
|
|
29
11
|
try {
|
|
30
12
|
// Windows cannot atomically replace existing; remove first.
|
|
31
|
-
await
|
|
13
|
+
await fs.unlink(filePath);
|
|
32
14
|
}
|
|
33
15
|
catch (e) {
|
|
34
16
|
if (!(e && e.code === "ENOENT"))
|
|
35
17
|
throw e;
|
|
36
18
|
}
|
|
37
|
-
await
|
|
19
|
+
await fs.rename(tmp, filePath);
|
|
38
20
|
}
|
|
39
21
|
//# sourceMappingURL=io.js.map
|
package/dist/io.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"io.js","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"io.js","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe;IAC7C,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB,EAAE,OAAe;IACrE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;IAErB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC3F,MAAM,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAEzC,IAAI,CAAC;QACH,4DAA4D;QAC5D,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAChB,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;YAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AACjC,CAAC"}
|
package/dist/paths.js
CHANGED
|
@@ -1,54 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.AI_PROVIDERS = void 0;
|
|
7
|
-
exports.skillTargetDir = skillTargetDir;
|
|
8
|
-
exports.skillAssetsDir = skillAssetsDir;
|
|
9
|
-
exports.listSkillAssetTemplates = listSkillAssetTemplates;
|
|
10
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
-
exports.AI_PROVIDERS = [
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export const AI_PROVIDERS = [
|
|
13
4
|
{ label: "Claude Code", value: "claude-code" },
|
|
14
5
|
{ label: "Codex", value: "codex" },
|
|
15
6
|
{ label: "Antigravity", value: "antigravity" },
|
|
16
7
|
];
|
|
17
8
|
const PROVIDER_BASE = {
|
|
18
9
|
"claude-code": ".claude",
|
|
19
|
-
"
|
|
10
|
+
// Project-local Codex skills belong in ".agents"; "~/.codex" is the global user dir.
|
|
11
|
+
// Codex also reads ".codex" implicitly, but ".agents" is the documented location.
|
|
12
|
+
"codex": ".agents",
|
|
20
13
|
"antigravity": ".agent",
|
|
21
14
|
};
|
|
22
|
-
function skillTargetDir(projectRoot, provider, skillName) {
|
|
23
|
-
return
|
|
15
|
+
export function skillTargetDir(projectRoot, provider, skillName) {
|
|
16
|
+
return path.join(projectRoot, PROVIDER_BASE[provider], "skills", skillName);
|
|
24
17
|
}
|
|
25
18
|
function packageRootDir() {
|
|
26
19
|
// dist/* at runtime; src/* in dev. In both cases, package root is one level up.
|
|
27
|
-
return
|
|
20
|
+
return path.resolve(import.meta.dirname, "..");
|
|
28
21
|
}
|
|
29
|
-
function skillAssetsDir(skillName) {
|
|
30
|
-
return
|
|
22
|
+
export function skillAssetsDir(skillName) {
|
|
23
|
+
return path.join(packageRootDir(), skillName);
|
|
31
24
|
}
|
|
32
25
|
function readAsset(skillName, fileName) {
|
|
33
|
-
const p =
|
|
34
|
-
return
|
|
26
|
+
const p = path.join(skillAssetsDir(skillName), fileName);
|
|
27
|
+
return fs.readFileSync(p, "utf8");
|
|
35
28
|
}
|
|
36
29
|
function walkAssetFiles(rootDir, currentDir = rootDir) {
|
|
37
|
-
const entries =
|
|
30
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
38
31
|
const files = [];
|
|
39
32
|
for (const entry of entries) {
|
|
40
|
-
const absolutePath =
|
|
33
|
+
const absolutePath = path.join(currentDir, entry.name);
|
|
41
34
|
if (entry.isDirectory()) {
|
|
42
35
|
files.push(...walkAssetFiles(rootDir, absolutePath));
|
|
43
36
|
continue;
|
|
44
37
|
}
|
|
45
38
|
if (entry.isFile()) {
|
|
46
|
-
files.push(
|
|
39
|
+
files.push(path.relative(rootDir, absolutePath).replace(/\\/g, "/"));
|
|
47
40
|
}
|
|
48
41
|
}
|
|
49
42
|
return files.sort();
|
|
50
43
|
}
|
|
51
|
-
function listSkillAssetTemplates(skillName) {
|
|
44
|
+
export function listSkillAssetTemplates(skillName) {
|
|
52
45
|
return walkAssetFiles(skillAssetsDir(skillName)).map((fileName) => ({
|
|
53
46
|
fileName,
|
|
54
47
|
content: readAsset(skillName, fileName),
|
package/dist/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,MAAM,CAAC,MAAM,YAAY,GAA2C;IAClE,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE;IAC9C,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;IAClC,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE;CAC/C,CAAC;AAEF,MAAM,aAAa,GAA+B;IAChD,aAAa,EAAE,SAAS;IACxB,qFAAqF;IACrF,kFAAkF;IAClF,OAAO,EAAE,SAAS;IAClB,aAAa,EAAE,QAAQ;CACxB,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,WAAmB,EAAE,QAAoB,EAAE,SAAiB;IACzF,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;AAC9E,CAAC;AAID,SAAS,cAAc;IACrB,gFAAgF;IAChF,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,SAAS,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,SAAS,CAAC,SAAiB,EAAE,QAAgB;IACpD,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC;IACzD,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,cAAc,CAAC,OAAe,EAAE,UAAU,GAAG,OAAO;IAC3D,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;YACrD,SAAS;QACX,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,SAAiB;IACvD,OAAO,cAAc,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAClE,QAAQ;QACR,OAAO,EAAE,SAAS,CAAC,SAAS,EAAE,QAAQ,CAAC;KACxC,CAAC,CAAC,CAAC;AACN,CAAC"}
|
package/dist/prompt.js
CHANGED
|
@@ -1,30 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.multiSelect = multiSelect;
|
|
7
|
-
const node_readline_1 = __importDefault(require("node:readline"));
|
|
8
|
-
const node_process_1 = __importDefault(require("node:process"));
|
|
1
|
+
import readline from "node:readline";
|
|
2
|
+
import process from "node:process";
|
|
9
3
|
function render(message, choices, cursor, selected, isFirst) {
|
|
10
4
|
if (!isFirst) {
|
|
11
|
-
|
|
5
|
+
process.stdout.write(`\x1b[${choices.length + 1}A`);
|
|
12
6
|
}
|
|
13
|
-
|
|
7
|
+
process.stdout.write(`\x1b[2K${message}\n`);
|
|
14
8
|
for (let i = 0; i < choices.length; i++) {
|
|
15
9
|
const mark = selected.has(i) ? "[x]" : "[ ]";
|
|
16
10
|
const arrow = i === cursor ? ">" : " ";
|
|
17
|
-
|
|
11
|
+
process.stdout.write(`\x1b[2K${arrow} ${mark} ${choices[i].label}\n`);
|
|
18
12
|
}
|
|
19
13
|
}
|
|
20
|
-
async function multiSelect(message, choices) {
|
|
21
|
-
if (!
|
|
14
|
+
export async function multiSelect(message, choices) {
|
|
15
|
+
if (!process.stdin.isTTY) {
|
|
22
16
|
return choices.map((c) => c.value);
|
|
23
17
|
}
|
|
24
18
|
const selected = new Set();
|
|
25
19
|
let cursor = 0;
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
readline.emitKeypressEvents(process.stdin);
|
|
21
|
+
process.stdin.setRawMode(true);
|
|
28
22
|
render(message, choices, cursor, selected, true);
|
|
29
23
|
return new Promise((resolve) => {
|
|
30
24
|
const onKey = (_str, key) => {
|
|
@@ -46,19 +40,19 @@ async function multiSelect(message, choices) {
|
|
|
46
40
|
render(message, choices, cursor, selected, false);
|
|
47
41
|
}
|
|
48
42
|
else if (key.name === "return") {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
process.stdin.removeListener("keypress", onKey);
|
|
44
|
+
process.stdin.setRawMode(false);
|
|
45
|
+
process.stdin.pause();
|
|
52
46
|
resolve(choices.filter((_, i) => selected.has(i)).map((c) => c.value));
|
|
53
47
|
}
|
|
54
48
|
else if (key.ctrl && key.name === "c") {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
process.stdin.removeListener("keypress", onKey);
|
|
50
|
+
process.stdin.setRawMode(false);
|
|
51
|
+
process.stdin.pause();
|
|
52
|
+
process.exit(0);
|
|
59
53
|
}
|
|
60
54
|
};
|
|
61
|
-
|
|
55
|
+
process.stdin.on("keypress", onKey);
|
|
62
56
|
});
|
|
63
57
|
}
|
|
64
58
|
//# sourceMappingURL=prompt.js.map
|
package/dist/prompt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,OAAO,MAAM,cAAc,CAAC;AAInC,SAAS,MAAM,CAAC,OAAe,EAAE,OAAiB,EAAE,MAAc,EAAE,QAAqB,EAAE,OAAgB;IACzG,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,OAAO,IAAI,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;QAC7C,MAAM,KAAK,GAAG,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,IAAI,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe,EAAE,OAAiB;IAClE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,QAAQ,CAAC,kBAAkB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3C,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAE/B,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IAEjD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,GAAoC,EAAE,EAAE;YACnE,IAAI,CAAC,GAAG;gBAAE,OAAO;YAEjB,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;gBACtB,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;gBACxD,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;YACpD,CAAC;iBAAM,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC/B,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;gBACvC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;YACpD,CAAC;iBAAM,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAChC,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC;oBAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;;oBAC7C,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC1B,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;YACpD,CAAC;iBAAM,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACjC,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBAChD,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;gBAChC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACtB,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YACzE,CAAC;iBAAM,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;gBACxC,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBAChD,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;gBAChC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACtB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC,CAAC;QAEF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-reviewer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Install the TypeScript Code Reviewer skill for Claude Code, Codex, or Antigravity",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "https://github.com/VirtualMaestro/ts-reviewer"
|
|
@@ -15,12 +16,13 @@
|
|
|
15
16
|
"ts-reviewer/**"
|
|
16
17
|
],
|
|
17
18
|
"engines": {
|
|
18
|
-
"node": "
|
|
19
|
+
"node": "^24.0.0"
|
|
19
20
|
},
|
|
20
21
|
"scripts": {
|
|
21
22
|
"build": "tsc -p tsconfig.json",
|
|
23
|
+
"prepublishOnly": "npm run build && npm test",
|
|
22
24
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
23
|
-
"test": "npm run typecheck",
|
|
25
|
+
"test": "npm run typecheck && node --test cnlp/skill-format.test.js",
|
|
24
26
|
"dev": "npm run build && node --enable-source-maps dist/cli.js"
|
|
25
27
|
},
|
|
26
28
|
"keywords": [
|
|
@@ -33,7 +35,7 @@
|
|
|
33
35
|
"ts-reviewer"
|
|
34
36
|
],
|
|
35
37
|
"devDependencies": {
|
|
36
|
-
"@types/node": "^
|
|
37
|
-
"typescript": "
|
|
38
|
+
"@types/node": "^24.13.3",
|
|
39
|
+
"typescript": "~5.9.0"
|
|
38
40
|
}
|
|
39
41
|
}
|