ts-reviewer 3.5.0 → 3.6.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 +32 -25
- package/dist/cli.js +25 -45
- package/dist/cli.js.map +1 -1
- package/dist/installer/assets.js +53 -0
- package/dist/installer/assets.js.map +1 -0
- package/dist/installer/frontmatter.js +19 -0
- package/dist/installer/frontmatter.js.map +1 -0
- package/dist/installer/fsx.js +119 -0
- package/dist/installer/fsx.js.map +1 -0
- package/dist/installer/index.js +248 -0
- package/dist/installer/index.js.map +1 -0
- package/dist/installer/ops.js +213 -0
- package/dist/installer/ops.js.map +1 -0
- package/dist/installer/prompt.js +66 -0
- package/dist/installer/prompt.js.map +1 -0
- package/dist/installer/providers.js +130 -0
- package/dist/installer/providers.js.map +1 -0
- package/dist/installer/toml.js +37 -0
- package/dist/installer/toml.js.map +1 -0
- package/dist/tool.js +16 -0
- package/dist/tool.js.map +1 -0
- package/package.json +8 -9
- package/dist/index.js +0 -24
- package/dist/index.js.map +0 -1
- package/dist/io.js +0 -21
- package/dist/io.js.map +0 -1
- package/dist/paths.js +0 -50
- package/dist/paths.js.map +0 -1
- package/dist/prompt.js +0 -58
- package/dist/prompt.js.map +0 -1
- /package/{ts-reviewer → assets/skills/ts-reviewer}/SKILL.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/architecture.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/async-patterns.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/boundary-validation.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/code-quality.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/dependency-hygiene.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/error-handling.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/fix-design.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/fix-workflow.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/investigate.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/modernization.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/security.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/stack-cost.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/tsconfig.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/references/type-safety.md +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/build-report.mjs +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/check-passes.mjs +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/classify-run.mjs +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/co-change.mjs +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/discover-projects.mjs +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/eslint.config.mjs +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/lint-pass.mjs +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/lint-rules.mjs +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/pass-prompts.mjs +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/run-cruise.mjs +0 -0
- /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/validate-report.mjs +0 -0
package/README.md
CHANGED
|
@@ -17,6 +17,10 @@ Four modes, one skill:
|
|
|
17
17
|
|
|
18
18
|
## What's New
|
|
19
19
|
|
|
20
|
+
**3.6.1 — the agent menu waits.** On Windows, after you picked the install scope, the agent menu took no keys: it confirmed both agents unasked or quit without installing. It now waits for your choice.
|
|
21
|
+
|
|
22
|
+
**3.6.0 — install once for every project.** `npx ts-reviewer@latest install` asks whether to install globally or into the project, and `update` brings every install to the latest version without questions. Antigravity is no longer a target.
|
|
23
|
+
|
|
20
24
|
**3.5.0 — pick what runs, and sturdier large runs.** `--domains security,boundary-validation` runs only those domains, and `--pick` asks you in a multi-select. The skill lint drops findings outside the pick, and skips itself when no picked domain owns a lint line. A new module, such as a future framework checklist, joins the menu through its `pass_groups` row. From a run on a 98-file monorepo: a database row typed by a generic and an untyped `JSON.parse` now grade High, a Next.js package is out of scope, `tools/check-passes.mjs` repairs and checks the pass files before the merge, every pass gets a fresh agent, and every Recurring Pattern row lists its sites. The main agent now writes 1 pass plan and its decisions, and 2 tools write the pass prompts and the report: on the recall corpus the main agent spends 35% less, on the monorepo 37% less, and every report validates on the first try.
|
|
21
25
|
|
|
22
26
|
**3.4.0 — a cheaper scan.** A pinned ESLint + typescript-eslint config now runs inside the scan (1 approval, through `npx`), and the 49 checklist lines it decides by rule leave the AI passes. A default scan runs 5 pass groups instead of 9 domain passes, the async and error group reads only the files that can hold its patterns, and the config and dependency group reads no `.ts` file. The pass model is yours to choose once per project. The report format is unchanged. Measured on a 98-file monorepo, the scan spent 8.6% less and finished 10 minutes sooner with the skill lint. On the recall corpus in `fixtures/recall-corpus/`, Sonnet passes halved what the passes cost and kept every High and Highest finding.
|
|
@@ -46,37 +50,40 @@ The review covers nine domains by default, each with its own detailed checklist.
|
|
|
46
50
|
|
|
47
51
|
### Install with npx
|
|
48
52
|
|
|
49
|
-
From the root of the project where you want to install the skill:
|
|
50
|
-
|
|
51
53
|
```bash
|
|
52
|
-
npx ts-reviewer
|
|
54
|
+
npx ts-reviewer@latest install
|
|
53
55
|
```
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
It asks where to install and for which AI agents: Up/Down to move, Space to toggle, Enter to confirm. Pick **Global** once and the skill works in every project you open, with nothing added to the project.
|
|
58
|
+
|
|
59
|
+
| AI agent | Global | Project |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| Claude Code | `~/.claude/skills/ts-reviewer/` | `.claude/skills/ts-reviewer/` |
|
|
62
|
+
| Codex | `~/.agents/skills/ts-reviewer/` | `.agents/skills/ts-reviewer/` |
|
|
63
|
+
|
|
64
|
+
`CLAUDE_CONFIG_DIR` and `CODEX_HOME` move the global targets, as they do for the agents themselves.
|
|
56
65
|
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
```bash
|
|
67
|
+
npx ts-reviewer@latest update # every install, same place and agents, no questions
|
|
68
|
+
npx ts-reviewer@latest status # installed version, newer one on npm, changed files
|
|
69
|
+
npx ts-reviewer@latest uninstall
|
|
61
70
|
```
|
|
62
71
|
|
|
63
|
-
|
|
72
|
+
Keep `@latest`: without it `npx` may run a copy it cached earlier. `update` refuses to replace a newer install with that older copy unless you pass `--force`.
|
|
64
73
|
|
|
65
|
-
|
|
74
|
+
Without a terminal (CI, an AI agent, Git Bash under MinTTY) nothing is asked and flags decide:
|
|
66
75
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
| Codex | `.agents/skills/ts-reviewer/` |
|
|
71
|
-
| Antigravity | `.agent/skills/ts-reviewer/` |
|
|
76
|
+
```bash
|
|
77
|
+
npx ts-reviewer@latest install --global --agents claude-code,codex --yes
|
|
78
|
+
```
|
|
72
79
|
|
|
73
|
-
|
|
80
|
+
`--dry-run` prints the plan and changes nothing. Each install records what it wrote in `.ai-tools/ts-reviewer.json` (in the home directory or the project root): `update` uses it, removes files the new version no longer ships, and keeps a file you edited unless you pass `--force`.
|
|
74
81
|
|
|
75
|
-
|
|
82
|
+
> **From 3.5.0 and earlier:** `npx ts-reviewer` with no command now prints help, and Antigravity is no longer a target. A copy an older version put in a project (`.claude/`, `.agents/` or `.agent/skills/ts-reviewer/`) is not tracked: delete it by hand.
|
|
76
83
|
|
|
77
84
|
### Manual Install
|
|
78
85
|
|
|
79
|
-
You can still copy the `ts-reviewer/` folder directly into the skill directory for your AI agent.
|
|
86
|
+
You can still copy the `assets/skills/ts-reviewer/` folder directly into the skill directory for your AI agent.
|
|
80
87
|
|
|
81
88
|
## Usage
|
|
82
89
|
|
|
@@ -114,7 +121,7 @@ With Architecture active, the same directory also holds project discovery, Knip,
|
|
|
114
121
|
|
|
115
122
|
The analysis passes run in waves. `--agents N` sets how many run at once (default 3; `--agents 1` runs them one at a time in the main agent). Each pass writes its own findings file under `code-smells/passes/`, and the queue in `code-smells/passes/queue.md` tracks which passes are done, so an interrupted scan does not lose finished work.
|
|
116
123
|
|
|
117
|
-
The scan also runs a skill lint: a pinned ESLint + typescript-eslint config (`ts-reviewer/tools/eslint.config.mjs`), through `npx -y eslint@10 typescript-eslint@8 typescript@5.9`, after 1 approval. The checklist lines it decides by rule are marked `lint-owned` in the references, its findings land in the report like any pass, and the AI passes skip those lines. Declined or failed, the scan falls back to the passes for every line. Your project's own linter still runs as before.
|
|
124
|
+
The scan also runs a skill lint: a pinned ESLint + typescript-eslint config (`assets/skills/ts-reviewer/tools/eslint.config.mjs`), through `npx -y eslint@10 typescript-eslint@8 typescript@5.9`, after 1 approval. The checklist lines it decides by rule are marked `lint-owned` in the references, its findings land in the report like any pass, and the AI passes skip those lines. Declined or failed, the scan falls back to the passes for every line. Your project's own linter still runs as before.
|
|
118
125
|
|
|
119
126
|
The main agent writes its decisions, not the report: `tools/pass-prompts.mjs` fills the pass prompts from 1 plan, and `tools/build-report.mjs` applies the deduplication, merge, Recurring Pattern, and sorting steps to the pass files and renders the report, which `validate-report.mjs` then checks.
|
|
120
127
|
|
|
@@ -255,21 +262,21 @@ AGENTS.md # How to edit the review rules — read be
|
|
|
255
262
|
CLAUDE.md # Pointer to AGENTS.md, picked up automatically by Claude Code
|
|
256
263
|
|
|
257
264
|
src/ # npm/npx installer source
|
|
258
|
-
├── cli.ts # CLI entrypoint
|
|
259
|
-
├──
|
|
260
|
-
└──
|
|
265
|
+
├── cli.ts # CLI entrypoint: install, update, status, uninstall
|
|
266
|
+
├── tool.ts # Tool name and version, read from package.json
|
|
267
|
+
└── installer/ # Copied from ts-ai-tool-template: fix it there, then copy the folder over
|
|
261
268
|
|
|
262
269
|
cnlp/ # the CNL-P format the skill files are written in
|
|
263
270
|
├── cnlp-format.md # the standard: forms, line rules, lexicon
|
|
264
271
|
├── cnlp.js # the checker — Node builtins only, no dependencies
|
|
265
272
|
├── skill-format.test.js # the conformance test, run by `npm test`
|
|
266
273
|
└── profiles/ # what each kind of document may contain
|
|
267
|
-
├── skill.md # → ts-reviewer/SKILL.md
|
|
268
|
-
├── reference.md # → ts-reviewer/references/*.md
|
|
274
|
+
├── skill.md # → assets/skills/ts-reviewer/SKILL.md
|
|
275
|
+
├── reference.md # → assets/skills/ts-reviewer/references/*.md
|
|
269
276
|
├── guide.md # → AGENTS.md
|
|
270
277
|
└── profile.md # → the profiles themselves
|
|
271
278
|
|
|
272
|
-
ts-reviewer/
|
|
279
|
+
assets/skills/ts-reviewer/ # What the installer copies
|
|
273
280
|
├── SKILL.md # Main skill file — mode routing, workflow orchestration
|
|
274
281
|
├── tools/ # Mechanical steps of the scan — plain Node, no dependencies
|
|
275
282
|
│ ├── discover-projects.mjs # Finds the TypeScript projects and their source roots
|
package/dist/cli.js
CHANGED
|
@@ -1,52 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
].join(""));
|
|
17
|
-
}
|
|
18
|
-
async function main() {
|
|
19
|
-
const args = process.argv.slice(2);
|
|
20
|
-
if (args.includes("-h") || args.includes("--help")) {
|
|
21
|
-
printHelp();
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
const cwd = process.cwd();
|
|
25
|
-
const skillName = SKILL_NAME;
|
|
26
|
-
process.stdout.write([
|
|
27
|
-
"TypeScript Code Reviewer\n",
|
|
28
|
-
`Checks: type safety, security, async patterns, boundary validation, error handling, modernization, code quality, tsconfig, dependency hygiene\n`,
|
|
29
|
-
`Target stack: ${TARGET_STACK}\n`,
|
|
30
|
-
"\n",
|
|
31
|
-
].join(""));
|
|
32
|
-
const selected = await multiSelect("Install for which AI agents? (Space = toggle, Enter = confirm)", AI_PROVIDERS);
|
|
33
|
-
if (selected.length === 0) {
|
|
34
|
-
process.stdout.write("No agents selected. Exiting.\n");
|
|
35
|
-
return;
|
|
2
|
+
import { INSTALLER_COMMANDS, INSTALLER_HELP, runInstallerCommand } from "./installer/index.js";
|
|
3
|
+
import { COMMANDS, DESCRIPTION, TOOL } from "./tool.js";
|
|
4
|
+
const HELP = `${TOOL.name} ${TOOL.version}: ${DESCRIPTION}
|
|
5
|
+
|
|
6
|
+
Usage: ${TOOL.name} <command> [options]
|
|
7
|
+
Update: npx ${TOOL.packageName}@latest update
|
|
8
|
+
|
|
9
|
+
Commands:
|
|
10
|
+
${INSTALLER_HELP}${Object.entries(COMMANDS).map(([name, c]) => `\n ${name.padEnd(11)} ${c.summary}`).join("")}`;
|
|
11
|
+
async function main(argv) {
|
|
12
|
+
const [command, ...rest] = argv;
|
|
13
|
+
if (!command || command === "-h" || command === "--help" || command === "help") {
|
|
14
|
+
console.log(HELP);
|
|
15
|
+
return command ? 0 : 1;
|
|
36
16
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
process.stdout.write(`\n[${providerLabel}]\n`);
|
|
41
|
-
const result = await scaffoldTsReviewerSkill({ cwd, skillName, targetDir });
|
|
42
|
-
for (const e of result.entries) {
|
|
43
|
-
process.stdout.write(`${e.action}\t${e.relativePath}\n`);
|
|
44
|
-
}
|
|
17
|
+
if (command === "-v" || command === "--version") {
|
|
18
|
+
console.log(TOOL.version);
|
|
19
|
+
return 0;
|
|
45
20
|
}
|
|
21
|
+
if (INSTALLER_COMMANDS.includes(command))
|
|
22
|
+
return runInstallerCommand(command, rest, TOOL);
|
|
23
|
+
if (Object.hasOwn(COMMANDS, command))
|
|
24
|
+
return COMMANDS[command].run(rest);
|
|
25
|
+
console.error(`Unknown command "${command}".\n\n${HELP}`);
|
|
26
|
+
return 1;
|
|
46
27
|
}
|
|
47
|
-
main().
|
|
48
|
-
|
|
49
|
-
process.stderr.write("\n");
|
|
28
|
+
main(process.argv.slice(2)).then((code) => { process.exitCode = code; }, (e) => {
|
|
29
|
+
console.error(`${TOOL.name}: ${e.message}`);
|
|
50
30
|
process.exitCode = 1;
|
|
51
31
|
});
|
|
52
32
|
//# sourceMappingURL=cli.js.map
|
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":";AACA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC/F,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAExD,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,WAAW;;SAEhD,IAAI,CAAC,IAAI;cACJ,IAAI,CAAC,WAAW;;;EAG5B,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;AAEjH,KAAK,UAAU,IAAI,CAAC,IAAc;IAChC,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAChC,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QAC/E,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClB,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,kBAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,mBAAmB,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC1F,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;QAAE,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzE,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,SAAS,IAAI,EAAE,CAAC,CAAC;IAC1D,OAAO,CAAC,CAAC;AACX,CAAC;AAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC9B,CAAC,IAAI,EAAE,EAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,EACtC,CAAC,CAAQ,EAAE,EAAE;IACX,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5C,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CACF,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseFrontmatter } from "./frontmatter.js";
|
|
4
|
+
import { readText, walkFiles } from "./fsx.js";
|
|
5
|
+
/** File and folder names become paths on the user's disk: keep them plain. */
|
|
6
|
+
const SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
7
|
+
function safeName(name, where) {
|
|
8
|
+
if (!SAFE_NAME.test(name))
|
|
9
|
+
throw new Error(`invalid name "${name}" in ${where}`);
|
|
10
|
+
return name;
|
|
11
|
+
}
|
|
12
|
+
function readFiles(dir) {
|
|
13
|
+
return walkFiles(dir).map((rel) => {
|
|
14
|
+
rel.split("/").forEach((part) => safeName(part, dir));
|
|
15
|
+
return { rel, content: fs.readFileSync(path.join(dir, rel)) };
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function subdirs(dir) {
|
|
19
|
+
if (!fs.existsSync(dir))
|
|
20
|
+
return [];
|
|
21
|
+
return fs.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
22
|
+
}
|
|
23
|
+
export function loadAssets(assetsDir) {
|
|
24
|
+
const skillsDir = path.join(assetsDir, "skills");
|
|
25
|
+
const skills = subdirs(skillsDir).map((name) => ({
|
|
26
|
+
name: safeName(name, skillsDir),
|
|
27
|
+
files: readFiles(path.join(skillsDir, name)),
|
|
28
|
+
}));
|
|
29
|
+
const agentsDir = path.join(assetsDir, "agents");
|
|
30
|
+
const agents = walkFiles(agentsDir)
|
|
31
|
+
.filter((f) => !f.includes("/") && f.endsWith(".md"))
|
|
32
|
+
.map((file) => {
|
|
33
|
+
const source = fs.readFileSync(path.join(agentsDir, file), "utf8");
|
|
34
|
+
const { meta, body } = parseFrontmatter(source);
|
|
35
|
+
const name = safeName(meta.name ?? file.slice(0, -3), path.join(agentsDir, file));
|
|
36
|
+
if (!meta.description)
|
|
37
|
+
throw new Error(`agents/${file}: frontmatter needs a description`);
|
|
38
|
+
return { name, source, meta, body };
|
|
39
|
+
});
|
|
40
|
+
const hooksDir = path.join(assetsDir, "hooks");
|
|
41
|
+
const hooksText = readText(path.join(hooksDir, "hooks.json"));
|
|
42
|
+
const hooks = hooksText === undefined ? undefined : {
|
|
43
|
+
events: (JSON.parse(hooksText).hooks ?? {}),
|
|
44
|
+
files: readFiles(hooksDir).filter((f) => f.rel !== "hooks.json"),
|
|
45
|
+
};
|
|
46
|
+
const mcpText = readText(path.join(assetsDir, ".mcp.json"));
|
|
47
|
+
const mcpServers = mcpText === undefined ? undefined : JSON.parse(mcpText).mcpServers;
|
|
48
|
+
for (const name of Object.keys(mcpServers ?? {}))
|
|
49
|
+
safeName(name, ".mcp.json");
|
|
50
|
+
const instructions = readText(path.join(assetsDir, "instructions.md"))?.trim() || undefined;
|
|
51
|
+
return { skills, agents, hooks, mcpServers, instructions };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/installer/assets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flat YAML frontmatter: one `key: value` per line, optional quotes.
|
|
3
|
+
* ponytail: no nested maps or lists; if an agent needs them, swap in a YAML parser here.
|
|
4
|
+
*/
|
|
5
|
+
export function parseFrontmatter(text) {
|
|
6
|
+
const src = text.replace(/^/, "").replace(/\r\n/g, "\n");
|
|
7
|
+
const m = /^---\n([\s\S]*?)\n---\n?/.exec(src);
|
|
8
|
+
if (!m)
|
|
9
|
+
return { meta: {}, body: src };
|
|
10
|
+
const meta = {};
|
|
11
|
+
for (const line of m[1].split("\n")) {
|
|
12
|
+
const kv = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line);
|
|
13
|
+
if (!kv)
|
|
14
|
+
continue;
|
|
15
|
+
meta[kv[1]] = kv[2].trim().replace(/^(["'])(.*)\1$/, "$2");
|
|
16
|
+
}
|
|
17
|
+
return { meta, body: src.slice(m[0].length) };
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/installer/frontmatter.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,GAAG,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IACvC,MAAM,IAAI,GAA2B,EAAE,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,EAAE,GAAG,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnD,IAAI,CAAC,EAAE;YAAE,SAAS;QAClB,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export function atomicWriteFile(filePath, content) {
|
|
5
|
+
const dir = path.dirname(filePath);
|
|
6
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
7
|
+
const tmp = path.join(dir, `.${path.basename(filePath)}.tmp-${process.pid}-${Date.now()}`);
|
|
8
|
+
fs.writeFileSync(tmp, content);
|
|
9
|
+
// Windows cannot atomically replace an existing file; remove it first.
|
|
10
|
+
fs.rmSync(filePath, { force: true });
|
|
11
|
+
fs.renameSync(tmp, filePath);
|
|
12
|
+
}
|
|
13
|
+
export function sha256(content) {
|
|
14
|
+
return createHash("sha256").update(content).digest("hex");
|
|
15
|
+
}
|
|
16
|
+
export function readText(filePath) {
|
|
17
|
+
try {
|
|
18
|
+
return fs.readFileSync(filePath, "utf8");
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
if (e?.code === "ENOENT")
|
|
22
|
+
return undefined;
|
|
23
|
+
throw e;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function readBytes(filePath) {
|
|
27
|
+
try {
|
|
28
|
+
return fs.readFileSync(filePath);
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
if (e?.code === "ENOENT")
|
|
32
|
+
return undefined;
|
|
33
|
+
throw e;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** A file we cannot parse is never rewritten: the user's settings matter more than our entry. */
|
|
37
|
+
export function readJsonObject(filePath) {
|
|
38
|
+
const text = readText(filePath);
|
|
39
|
+
if (text === undefined || text.trim() === "")
|
|
40
|
+
return undefined;
|
|
41
|
+
let parsed;
|
|
42
|
+
try {
|
|
43
|
+
parsed = JSON.parse(text);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
throw new Error(`${filePath} is not valid JSON, refusing to modify it: ${e.message}`);
|
|
47
|
+
}
|
|
48
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
49
|
+
throw new Error(`${filePath} is not a JSON object, refusing to modify it`);
|
|
50
|
+
}
|
|
51
|
+
return parsed;
|
|
52
|
+
}
|
|
53
|
+
export function writeJson(filePath, value) {
|
|
54
|
+
atomicWriteFile(filePath, JSON.stringify(value, null, 2) + "\n");
|
|
55
|
+
}
|
|
56
|
+
/** Copy a foreign config file once, before the first time we touch it. */
|
|
57
|
+
export function backupOnce(filePath) {
|
|
58
|
+
const bak = `${filePath}.bak`;
|
|
59
|
+
if (fs.existsSync(filePath) && !fs.existsSync(bak))
|
|
60
|
+
fs.copyFileSync(filePath, bak);
|
|
61
|
+
}
|
|
62
|
+
/** All files under dir, as sorted "/"-separated relative paths. */
|
|
63
|
+
export function walkFiles(dir) {
|
|
64
|
+
if (!fs.existsSync(dir))
|
|
65
|
+
return [];
|
|
66
|
+
return fs
|
|
67
|
+
.readdirSync(dir, { recursive: true, withFileTypes: true })
|
|
68
|
+
.filter((e) => e.isFile())
|
|
69
|
+
.map((e) => path.relative(dir, path.join(e.parentPath, e.name)).replace(/\\/g, "/"))
|
|
70
|
+
.sort();
|
|
71
|
+
}
|
|
72
|
+
/** Remove empty directories from dir upwards, never at or above stopAt. */
|
|
73
|
+
export function pruneEmptyDirs(dir, stopAt) {
|
|
74
|
+
const stop = path.resolve(stopAt);
|
|
75
|
+
let current = path.resolve(dir);
|
|
76
|
+
while (current.startsWith(stop + path.sep) && current !== stop) {
|
|
77
|
+
try {
|
|
78
|
+
fs.rmdirSync(current);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return; // not empty or gone
|
|
82
|
+
}
|
|
83
|
+
current = path.dirname(current);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function blockMarkers(tool, style) {
|
|
87
|
+
return style === "md"
|
|
88
|
+
? [`<!-- ${tool}:start -->`, `<!-- ${tool}:end -->`]
|
|
89
|
+
: [`# >>> ${tool} >>>`, `# <<< ${tool} <<<`];
|
|
90
|
+
}
|
|
91
|
+
/** Replace the marked block in text, or append it. */
|
|
92
|
+
export function upsertBlock(text, markers, content) {
|
|
93
|
+
const block = `${markers[0]}\n${content.trim()}\n${markers[1]}`;
|
|
94
|
+
const range = findBlock(text, markers);
|
|
95
|
+
if (range)
|
|
96
|
+
return text.slice(0, range[0]) + block + text.slice(range[1]);
|
|
97
|
+
const sep = text === "" ? "" : text.endsWith("\n\n") ? "" : text.endsWith("\n") ? "\n" : "\n\n";
|
|
98
|
+
return `${text}${sep}${block}\n`;
|
|
99
|
+
}
|
|
100
|
+
export function removeBlock(text, markers) {
|
|
101
|
+
const range = findBlock(text, markers);
|
|
102
|
+
if (!range)
|
|
103
|
+
return text;
|
|
104
|
+
const before = text.slice(0, range[0]).replace(/\n+$/, "");
|
|
105
|
+
const after = text.slice(range[1]).replace(/^\n+/, "");
|
|
106
|
+
if (!before)
|
|
107
|
+
return after;
|
|
108
|
+
return after ? `${before}\n\n${after}` : `${before}\n`;
|
|
109
|
+
}
|
|
110
|
+
function findBlock(text, [start, end]) {
|
|
111
|
+
const from = text.indexOf(start);
|
|
112
|
+
if (from < 0)
|
|
113
|
+
return undefined;
|
|
114
|
+
const to = text.indexOf(end, from);
|
|
115
|
+
if (to < 0)
|
|
116
|
+
throw new Error(`found "${start}" without its closing "${end}"`);
|
|
117
|
+
return [from, to + end.length];
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=fsx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fsx.js","sourceRoot":"","sources":["../../src/installer/fsx.ts"],"names":[],"mappings":"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"}
|