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.
Files changed (56) hide show
  1. package/README.md +32 -25
  2. package/dist/cli.js +25 -45
  3. package/dist/cli.js.map +1 -1
  4. package/dist/installer/assets.js +53 -0
  5. package/dist/installer/assets.js.map +1 -0
  6. package/dist/installer/frontmatter.js +19 -0
  7. package/dist/installer/frontmatter.js.map +1 -0
  8. package/dist/installer/fsx.js +119 -0
  9. package/dist/installer/fsx.js.map +1 -0
  10. package/dist/installer/index.js +248 -0
  11. package/dist/installer/index.js.map +1 -0
  12. package/dist/installer/ops.js +213 -0
  13. package/dist/installer/ops.js.map +1 -0
  14. package/dist/installer/prompt.js +66 -0
  15. package/dist/installer/prompt.js.map +1 -0
  16. package/dist/installer/providers.js +130 -0
  17. package/dist/installer/providers.js.map +1 -0
  18. package/dist/installer/toml.js +37 -0
  19. package/dist/installer/toml.js.map +1 -0
  20. package/dist/tool.js +16 -0
  21. package/dist/tool.js.map +1 -0
  22. package/package.json +8 -9
  23. package/dist/index.js +0 -24
  24. package/dist/index.js.map +0 -1
  25. package/dist/io.js +0 -21
  26. package/dist/io.js.map +0 -1
  27. package/dist/paths.js +0 -50
  28. package/dist/paths.js.map +0 -1
  29. package/dist/prompt.js +0 -58
  30. package/dist/prompt.js.map +0 -1
  31. /package/{ts-reviewer → assets/skills/ts-reviewer}/SKILL.md +0 -0
  32. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/architecture.md +0 -0
  33. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/async-patterns.md +0 -0
  34. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/boundary-validation.md +0 -0
  35. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/code-quality.md +0 -0
  36. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/dependency-hygiene.md +0 -0
  37. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/error-handling.md +0 -0
  38. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/fix-design.md +0 -0
  39. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/fix-workflow.md +0 -0
  40. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/investigate.md +0 -0
  41. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/modernization.md +0 -0
  42. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/security.md +0 -0
  43. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/stack-cost.md +0 -0
  44. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/tsconfig.md +0 -0
  45. /package/{ts-reviewer → assets/skills/ts-reviewer}/references/type-safety.md +0 -0
  46. /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/build-report.mjs +0 -0
  47. /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/check-passes.mjs +0 -0
  48. /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/classify-run.mjs +0 -0
  49. /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/co-change.mjs +0 -0
  50. /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/discover-projects.mjs +0 -0
  51. /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/eslint.config.mjs +0 -0
  52. /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/lint-pass.mjs +0 -0
  53. /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/lint-rules.mjs +0 -0
  54. /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/pass-prompts.mjs +0 -0
  55. /package/{ts-reviewer → assets/skills/ts-reviewer}/tools/run-cruise.mjs +0 -0
  56. /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
- The installer prints a short summary before installation:
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
- ```text
58
- TypeScript Code Reviewer
59
- Checks: type safety, security, async patterns, boundary validation, error handling, modernization, code quality, tsconfig, dependency hygiene
60
- Target stack: TypeScript 5.9.x, ES2024, Node 24
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
- Then it asks which AI agents to install for. Use Up/Down arrows to move, Space to toggle, and Enter to confirm.
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
- Supported targets:
74
+ Without a terminal (CI, an AI agent, Git Bash under MinTTY) nothing is asked and flags decide:
66
75
 
67
- | AI agent | Install path |
68
- |---|---|
69
- | Claude Code | `.claude/skills/ts-reviewer/` |
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
- > **Note on Codex:** project-local skills belong in `.agents/` per the Codex docs; `~/.codex/` is the *global* per-user directory. Codex also reads a project-local `.codex/` implicitly, so installs from older versions of this installer keep working — but `.agents/` is the correct location going forward.
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
- In non-interactive terminals, the installer selects all supported targets.
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 and provider prompt
259
- ├── prompt.ts # raw-mode keyboard multi-select
260
- └── paths.ts # target directories and skill asset loading
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 { scaffoldTsReviewerSkill } from "./index.js";
3
- import { multiSelect } from "./prompt.js";
4
- import { AI_PROVIDERS, skillTargetDir } from "./paths.js";
5
- const SKILL_NAME = "ts-reviewer";
6
- const TARGET_STACK = "TypeScript 5.9.x, ES2024, Node 24";
7
- function printHelp() {
8
- process.stdout.write([
9
- "ts-reviewer: install TypeScript review skill\n",
10
- "\n",
11
- "Usage:\n",
12
- " npx ts-reviewer\n",
13
- "\n",
14
- "Options:\n",
15
- " -h, --help show help\n",
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
- for (const provider of selected) {
38
- const targetDir = skillTargetDir(cwd, provider, skillName);
39
- const providerLabel = AI_PROVIDERS.find((p) => p.value === provider).label;
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().catch((err) => {
48
- process.stderr.write(err?.stack ? String(err.stack) : String(err));
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":";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"}
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":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAsB/C,8EAA8E;AAC9E,MAAM,SAAS,GAAG,8BAA8B,CAAC;AAEjD,SAAS,QAAQ,CAAC,IAAY,EAAE,KAAa;IAC3C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,QAAQ,KAAK,EAAE,CAAC,CAAC;IACjF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAChC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;QACtD,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;IAChE,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,OAAO,CAAC,GAAW;IAC1B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,OAAO,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AAC/G,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC/C,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;QAC/B,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;KAC7C,CAAC,CAAC,CAAC;IAEJ,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC;SAChC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SACpD,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QACnE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;QAClF,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,mCAAmC,CAAC,CAAC;QAC1F,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IAEL,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC;IAC9D,MAAM,KAAK,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QAClD,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,IAAI,EAAE,CAAgC;QAC1E,KAAK,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,YAAY,CAAC;KACjE,CAAC;IAEF,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,UAAwC,CAAC;IACrH,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;QAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAE9E,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IAE5F,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;AAC7D,CAAC"}
@@ -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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"}