comp-gate 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +353 -0
  2. package/dist/agents/agents-md.d.ts +2 -0
  3. package/dist/agents/agents-md.d.ts.map +1 -0
  4. package/dist/agents/agents-md.js +25 -0
  5. package/dist/agents/agents-md.js.map +1 -0
  6. package/dist/agents/claude.d.ts +2 -0
  7. package/dist/agents/claude.d.ts.map +1 -0
  8. package/dist/agents/claude.js +45 -0
  9. package/dist/agents/claude.js.map +1 -0
  10. package/dist/agents/cursor.d.ts +2 -0
  11. package/dist/agents/cursor.d.ts.map +1 -0
  12. package/dist/agents/cursor.js +28 -0
  13. package/dist/agents/cursor.js.map +1 -0
  14. package/dist/agents/detect.d.ts +4 -0
  15. package/dist/agents/detect.d.ts.map +1 -0
  16. package/dist/agents/detect.js +96 -0
  17. package/dist/agents/detect.js.map +1 -0
  18. package/dist/agents/shared.d.ts +9 -0
  19. package/dist/agents/shared.d.ts.map +1 -0
  20. package/dist/agents/shared.js +83 -0
  21. package/dist/agents/shared.js.map +1 -0
  22. package/dist/agents/types.d.ts +14 -0
  23. package/dist/agents/types.d.ts.map +1 -0
  24. package/dist/agents/types.js +8 -0
  25. package/dist/agents/types.js.map +1 -0
  26. package/dist/agents/windsurf.d.ts +2 -0
  27. package/dist/agents/windsurf.d.ts.map +1 -0
  28. package/dist/agents/windsurf.js +27 -0
  29. package/dist/agents/windsurf.js.map +1 -0
  30. package/dist/bootstrap-summary.d.ts +2 -0
  31. package/dist/bootstrap-summary.d.ts.map +1 -0
  32. package/dist/bootstrap-summary.js +262 -0
  33. package/dist/bootstrap-summary.js.map +1 -0
  34. package/dist/categories.d.ts +13 -0
  35. package/dist/categories.d.ts.map +1 -0
  36. package/dist/categories.js +382 -0
  37. package/dist/categories.js.map +1 -0
  38. package/dist/cli.d.ts +3 -0
  39. package/dist/cli.d.ts.map +1 -0
  40. package/dist/cli.js +361 -0
  41. package/dist/cli.js.map +1 -0
  42. package/dist/config-init.d.ts +16 -0
  43. package/dist/config-init.d.ts.map +1 -0
  44. package/dist/config-init.js +53 -0
  45. package/dist/config-init.js.map +1 -0
  46. package/dist/config.d.ts +11 -0
  47. package/dist/config.d.ts.map +1 -0
  48. package/dist/config.js +127 -0
  49. package/dist/config.js.map +1 -0
  50. package/dist/constants.d.ts +2 -0
  51. package/dist/constants.d.ts.map +1 -0
  52. package/dist/constants.js +2 -0
  53. package/dist/constants.js.map +1 -0
  54. package/dist/detect.d.ts +4 -0
  55. package/dist/detect.d.ts.map +1 -0
  56. package/dist/detect.js +244 -0
  57. package/dist/detect.js.map +1 -0
  58. package/dist/diff.d.ts +11 -0
  59. package/dist/diff.d.ts.map +1 -0
  60. package/dist/diff.js +198 -0
  61. package/dist/diff.js.map +1 -0
  62. package/dist/generate.d.ts +7 -0
  63. package/dist/generate.d.ts.map +1 -0
  64. package/dist/generate.js +71 -0
  65. package/dist/generate.js.map +1 -0
  66. package/dist/gitignore.d.ts +12 -0
  67. package/dist/gitignore.d.ts.map +1 -0
  68. package/dist/gitignore.js +106 -0
  69. package/dist/gitignore.js.map +1 -0
  70. package/dist/hooks.d.ts +22 -0
  71. package/dist/hooks.d.ts.map +1 -0
  72. package/dist/hooks.js +199 -0
  73. package/dist/hooks.js.map +1 -0
  74. package/dist/init.d.ts +24 -0
  75. package/dist/init.d.ts.map +1 -0
  76. package/dist/init.js +218 -0
  77. package/dist/init.js.map +1 -0
  78. package/dist/paths.d.ts +13 -0
  79. package/dist/paths.d.ts.map +1 -0
  80. package/dist/paths.js +18 -0
  81. package/dist/paths.js.map +1 -0
  82. package/dist/prompts.d.ts +9 -0
  83. package/dist/prompts.d.ts.map +1 -0
  84. package/dist/prompts.js +88 -0
  85. package/dist/prompts.js.map +1 -0
  86. package/dist/repo-stats.d.ts +10 -0
  87. package/dist/repo-stats.d.ts.map +1 -0
  88. package/dist/repo-stats.js +117 -0
  89. package/dist/repo-stats.js.map +1 -0
  90. package/dist/setup-wizard.d.ts +34 -0
  91. package/dist/setup-wizard.d.ts.map +1 -0
  92. package/dist/setup-wizard.js +251 -0
  93. package/dist/setup-wizard.js.map +1 -0
  94. package/dist/testing.d.ts +6 -0
  95. package/dist/testing.d.ts.map +1 -0
  96. package/dist/testing.js +24 -0
  97. package/dist/testing.js.map +1 -0
  98. package/dist/types.d.ts +156 -0
  99. package/dist/types.d.ts.map +1 -0
  100. package/dist/types.js +31 -0
  101. package/dist/types.js.map +1 -0
  102. package/dist/verify.d.ts +38 -0
  103. package/dist/verify.d.ts.map +1 -0
  104. package/dist/verify.js +254 -0
  105. package/dist/verify.js.map +1 -0
  106. package/package.json +52 -0
  107. package/src/templates/PROMPTS.md +6 -0
  108. package/src/templates/SKILL.md +253 -0
  109. package/src/templates/SUMMARY.md +29 -0
  110. package/src/templates/agents/agents-md/section.md +11 -0
  111. package/src/templates/agents/claude/SKILL.md +8 -0
  112. package/src/templates/agents/claude/settings.fragment.json +16 -0
  113. package/src/templates/agents/claude/testing-SKILL.md +9 -0
  114. package/src/templates/agents/cursor/SKILL.md +9 -0
  115. package/src/templates/agents/cursor/hooks.json +23 -0
  116. package/src/templates/agents/cursor/testing-SKILL.md +9 -0
  117. package/src/templates/agents/windsurf/SKILL.md +8 -0
  118. package/src/templates/agents/windsurf/hooks.json +10 -0
  119. package/src/templates/agents/windsurf/testing-SKILL.md +9 -0
  120. package/src/templates/hooks/after-push.sh +17 -0
  121. package/src/templates/hooks/block-commit.sh +29 -0
  122. package/src/templates/hooks/block-push.sh +29 -0
  123. package/src/templates/hooks/claude-pre-tool.sh +59 -0
  124. package/src/templates/hooks/git-pre-commit.sh +35 -0
  125. package/src/templates/hooks/git-pre-push.sh +53 -0
  126. package/src/templates/hooks/statusline.sh +3 -0
  127. package/src/templates/hooks/windsurf-pre-run.sh +39 -0
  128. package/src/templates/hooks.json +23 -0
  129. package/src/templates/testing-SKILL.md +33 -0
  130. package/src/templates/testme.config.json +32 -0
package/README.md ADDED
@@ -0,0 +1,353 @@
1
+ # testme
2
+
3
+ Comprehension gate for desktop coding agents. **testme** blocks `git commit` (when enabled) and `git push` to protected branches until you pass a comprehension test describing what changed.
4
+
5
+ Published on npm as [`comp-gate`](https://www.npmjs.com/package/comp-gate). The CLI command is `comp-gate` (alias: `testme`).
6
+
7
+ ## Quick start
8
+
9
+ ```bash
10
+ npx comp-gate init
11
+ ```
12
+
13
+ The setup wizard detects your desktop agent (Cursor, Claude Code, Windsurf), asks how many questions you want, how difficult they should be, whether to keep agent skills local-only in `.gitignore`, and whether to start with a blank `SUMMARY.md` or generate one from project metadata, then installs the integration.
14
+
15
+ Non-interactive example:
16
+
17
+ ```bash
18
+ npx comp-gate init --agent cursor --questions 3 --difficulty medium --local-only true --summary blank --yes
19
+ ```
20
+
21
+ ### Adopting an existing repository
22
+
23
+ During `init`, the wizard asks how to set up `SUMMARY.md`:
24
+
25
+ - **Blank (default)** — empty template; you fill in stack, layout, and conventions once. Best for large or monorepo codebases.
26
+ - **Generate** — drafts `SUMMARY.md` from README, manifests, and top-level folders only (not a full codebase scan).
27
+
28
+ On repos with **500+ tracked files**, the wizard recommends blank and warns that auto-generation is often shallow or misleading at that scale.
29
+
30
+ - Large repos: prefer blank — a hand-written map at package/layer granularity beats a shallow auto-draft.
31
+ - Staleness: generated summaries reflect init-time metadata and need manual upkeep.
32
+ - Token efficiency: comp-gate reads `SUMMARY.md` for architecture questions; keep it short and accurate.
33
+
34
+ See [adopting existing repos](https://github.com/ROrmand/comp-gate/blob/main/docs/adopting-existing-repos.md) for the full guide.
35
+
36
+ ```bash
37
+ npx comp-gate init --summary blank # default
38
+ npx comp-gate init --summary generate # draft from README + manifests
39
+ ```
40
+
41
+ ### What gets installed
42
+
43
+ **Committed (team-shared):**
44
+
45
+ - `SUMMARY.md` — stable project map (blank template or generated from project metadata at init)
46
+ - `PROMPTS.md` — session change log (resets after successful push to `main`)
47
+ - `testme.config.json` — question count, difficulty, protected branches
48
+ - `AGENTS.md` — portable workflow section (when selected)
49
+
50
+ **Local only (gitignored — each developer runs `npx comp-gate init` after clone):**
51
+
52
+ - `.testme/` — session state and hook scripts
53
+ - `.cursor/skills/testme/`, `.cursor/skills/testing/`, `.claude/skills/testme/`, `.claude/skills/testing/`, `.windsurf/skills/testme/`, `.windsurf/skills/testing/` — agent skills
54
+ - Agent hook configs (`.cursor/hooks.json`, `.claude/settings.json`, `.windsurf/hooks.json`) when testme-only
55
+
56
+ **Always installed per machine:**
57
+
58
+ - `.git/hooks/pre-commit` and `pre-push` — blocks commit/push in any terminal
59
+
60
+ ### Supported agents
61
+
62
+ | Agent | Integration |
63
+ |-------|-------------|
64
+ | Cursor | `.cursor/hooks.json` + `/testme` and `/testing` skills |
65
+ | Claude Code | `.claude/settings.json` PreToolUse hooks + skills |
66
+ | Windsurf | `.windsurf/hooks.json` pre_run_command hooks + skills |
67
+ | AGENTS.md | Portable workflow docs (fallback) |
68
+
69
+ Git hooks are the universal safety net when agent shell hooks are unavailable (e.g. some Claude Desktop sessions).
70
+
71
+ ## Toggle gating with `/testing`
72
+
73
+ Use `/testing` in chat (or `npx comp-gate testing`) to turn the comprehension gate on or off **locally** — state is stored in `.testme/config.json` (gitignored), so one developer can pause gating without changing team config.
74
+
75
+ | Skill / command | Purpose |
76
+ |-----------------|---------|
77
+ | `/testing` | Toggle gate on/off (default) |
78
+ | `npx comp-gate testing on` | Enable gating |
79
+ | `npx comp-gate testing off` | Disable gating (commit/push no longer require `/testme`) |
80
+ | `npx comp-gate testing status` | Show current state with status banner |
81
+ | `/testme` | Run comprehension workflow (only when gate is on) |
82
+
83
+ When the gate is **off**, hooks allow commit and push without verification. Re-enable before pushing to protected branches in team workflows.
84
+
85
+ ### Status line indicator
86
+
87
+ For a persistent on/off indicator above the Cursor prompt, add this to `~/.cursor/cli-config.json`:
88
+
89
+ ```json
90
+ {
91
+ "statusLine": {
92
+ "type": "command",
93
+ "command": "sh -c 'cd \"$PWD\" && .testme/hooks/statusline.sh'",
94
+ "padding": 2
95
+ }
96
+ }
97
+ ```
98
+
99
+ `npx comp-gate init` installs `.testme/hooks/statusline.sh`, which runs `npx comp-gate statusline`:
100
+
101
+ - Gate on: `🟢 testme`
102
+ - Gate off: `🔴 testme off`
103
+
104
+ Running `/testing` also prints an in-chat banner:
105
+
106
+ ```
107
+ ┌─────────────────────────┐
108
+ │ testme gate: ON 🟢 │
109
+ └─────────────────────────┘
110
+ ```
111
+
112
+ ## Workflow
113
+
114
+ ```mermaid
115
+ flowchart TD
116
+ work[Make changes] --> prompts[Update PROMPTS.md]
117
+ prompts --> gen[npx comp-gate generate]
118
+ gen --> answer[Answer questions in chat]
119
+ answer --> write[Agent writes .testme/answers.json]
120
+ write --> verify[npx comp-gate verify]
121
+ verify -->|PASS| push[git push origin main]
122
+ verify -->|FAIL| fix[Fix answers or PROMPTS.md]
123
+ push --> reset[PROMPTS.md resets via hook]
124
+ ```
125
+
126
+ ### 1. Document changes in PROMPTS.md
127
+
128
+ ```markdown
129
+ # Session Changes
130
+
131
+ ## src/auth.ts
132
+ - Added validateToken middleware; checks JWT expiry before route handlers
133
+ ```
134
+
135
+ Write bullets as you work — they become the answer rubric.
136
+
137
+ ### 2. Generate questions
138
+
139
+ ```bash
140
+ npx comp-gate generate
141
+ ```
142
+
143
+ Reads `SUMMARY.md`, `PROMPTS.md`, and git diff only. Produces 3–5 targeted questions in `.testme/session.json`.
144
+
145
+ ### 3. Answer questions (in chat)
146
+
147
+ Run `/testme` in Cursor. The agent asks each question one at a time in chat — reply in plain messages. The agent writes `.testme/answers.json` from your replies before verifying.
148
+
149
+ For each question, read only the files listed in that question's `files[]` array if you need to check your answer.
150
+
151
+ ### 4. Verify (semantic grading)
152
+
153
+ The agent generates reference answers, asks you questions in chat, then grades your replies with an **accuracy score (0–100)** and **alignment** (`low` / `medium` / `high`) per question. Default pass threshold is **70%** accuracy (`passThreshold` in config).
154
+
155
+ ```bash
156
+ npx comp-gate verify
157
+ ```
158
+
159
+ On pass, `.testme/pass.json` is written.
160
+
161
+ ### 5. Push
162
+
163
+ ```bash
164
+ git push origin main
165
+ ```
166
+
167
+ After a successful push, `PROMPTS.md` resets automatically.
168
+
169
+ ## Commands
170
+
171
+ | Command | Description |
172
+ |---------|-------------|
173
+ | `comp-gate init` | Interactive setup wizard: detect agent, configure questions/difficulty, install hooks and skills |
174
+ | `comp-gate init --agent all --yes` | Install all agent integrations without prompts |
175
+ | `comp-gate init --questions 5 --difficulty hard --force` | Override question count and difficulty |
176
+ | `comp-gate hook install-git` | Re-install git `pre-commit` / `pre-push` hooks only |
177
+ | `comp-gate generate` | Generate questions from diff + md files |
178
+ | `comp-gate generate --max-questions 3` | One-off question count override |
179
+ | `comp-gate generate --category runtime,testing` | One-off category filter |
180
+ | `comp-gate detect` | Show auto-detected domain categories |
181
+ | `comp-gate config init` | Create `testme.config.json` with detected defaults |
182
+ | `comp-gate config show` | Print merged config (repo + local overrides) |
183
+ | `comp-gate verify` | Score answers in `.testme/answers.json` |
184
+ | `comp-gate status` | Check if current pass is valid and whether gate is enabled |
185
+ | `comp-gate testing` | Toggle comprehension gate on/off (local) |
186
+ | `comp-gate testing on` / `off` / `status` | Explicitly enable, disable, or show gate state |
187
+ | `comp-gate statusline` | One-line gate status for Cursor status line |
188
+ | `comp-gate reset` | Clear `.testme/` session state |
189
+
190
+ ## Customization
191
+
192
+ ### Protected branches and commit gating
193
+
194
+ Configure in `testme.config.json`:
195
+
196
+ ```json
197
+ {
198
+ "protectedBranches": ["main"],
199
+ "gateCommits": true,
200
+ "autoProtectCurrentBranch": true
201
+ }
202
+ ```
203
+
204
+ - `protectedBranches` — always-protected branches (e.g. `main`)
205
+ - `autoProtectCurrentBranch` — also gate pushes to whatever branch you have checked out (default `true`)
206
+ - `gateCommits` — when `true`, `git commit` is blocked until `/testme` passes
207
+
208
+ Blocked push message: `You must run the /testme skill before pushing to 'main'.`
209
+
210
+ **Terminal blocking:** `npx comp-gate init` installs native git hooks into `.git/hooks/`. The Cursor integrated terminal does **not** run Cursor `beforeShellExecution` hooks — git hooks cover that gap.
211
+
212
+ ### Question count and difficulty
213
+
214
+ Set during `comp-gate init` or in `testme.config.json`:
215
+
216
+ | Difficulty | Pass threshold | Alignment required | Categories |
217
+ |------------|----------------|--------------------|------------|
218
+ | easy | 60% | low+ | changeRationale, symbols |
219
+ | medium | 70% | medium+ | balanced + auto-detect |
220
+ | hard | 85% | high only | + architecture, runtime, security |
221
+
222
+ ```json
223
+ {
224
+ "questions": { "min": 2, "max": 3 },
225
+ "difficulty": "medium",
226
+ "passThreshold": 70,
227
+ "minAlignment": "medium"
228
+ }
229
+ ```
230
+
231
+ ### Question count (generate)
232
+
233
+ Configure in `testme.config.json`:
234
+
235
+ ```json
236
+ {
237
+ "questions": { "min": 2, "max": 5 }
238
+ }
239
+ ```
240
+
241
+ Local override in `.testme/config.json` (gitignored via `.testme/`):
242
+
243
+ ```json
244
+ { "questions": { "max": 3 } }
245
+ ```
246
+
247
+ ### Category checkboxes
248
+
249
+ Each category is a boolean in `categories`. Core categories work everywhere; domain categories are auto-suggested when relevant.
250
+
251
+ | Category | Focus |
252
+ |----------|-------|
253
+ | `changeRationale` | What changed per file and why |
254
+ | `symbols` | What added/changed symbols do |
255
+ | `architecture` | Fit with SUMMARY.md structure |
256
+ | `runtime` | Run, test, deploy impact |
257
+ | `dataStructures` | Algorithms and data structures used |
258
+ | `dependencies` | Package/manifest changes |
259
+ | `testing` | Test coverage and assertions |
260
+ | `errorHandling` | Errors and edge cases |
261
+ | `apiContracts` | API/schema/interface changes |
262
+ | `security` | Auth, validation, secrets |
263
+ | `performance` | Caching, complexity, bottlenecks |
264
+ | `database` | Schema, queries, migrations |
265
+ | `machineLearning` | Training, inference, metrics |
266
+ | `cybersecurity` | Threat model, mitigations |
267
+ | `frontend` | UI components and state |
268
+ | `backend` | Routes, middleware, services |
269
+ | `devops` | CI/CD, containers, infra |
270
+ | `mobile` | Platform-specific behavior |
271
+ | `dataEngineering` | Pipelines, ETL, lineage |
272
+
273
+ ### Project-aware detection
274
+
275
+ ```bash
276
+ npx comp-gate detect
277
+ ```
278
+
279
+ Signals: `SUMMARY.md` `## Domain` section, `package.json`/`pyproject.toml` deps, diff file paths.
280
+
281
+ **ML example** — `comp-gate detect` enables `machineLearning`; disable `cybersecurity` in config.
282
+
283
+ **Security example** — enable `cybersecurity` and `security`; leave `machineLearning` false.
284
+
285
+ ```json
286
+ {
287
+ "autoDetect": true,
288
+ "categories": {
289
+ "machineLearning": true,
290
+ "dataStructures": true,
291
+ "cybersecurity": false
292
+ }
293
+ }
294
+ ```
295
+
296
+ Add to `SUMMARY.md`:
297
+
298
+ ```markdown
299
+ ## Domain
300
+
301
+ - Primary: machine learning
302
+ - Focus areas: model training, evaluation metrics, inference API
303
+ ```
304
+
305
+ ## Why PROMPTS.md matters
306
+
307
+ `PROMPTS.md` is the cheap answer key. Agents document changes as they work, so verification doesn't require re-reading full diffs or scanning the codebase. Better bullets → better rubrics → easier pass.
308
+
309
+ ## Token efficiency
310
+
311
+ - No full-repo scans — only git diff metadata + two md files
312
+ - Configurable question count and categories via `testme.config.json`
313
+ - Semantic grading by default (agent compares your answers to reference answers)
314
+ - Targeted file reads via `files[]` pointers in session
315
+
316
+ ## Troubleshooting
317
+
318
+ | Issue | Fix |
319
+ |-------|-----|
320
+ | Push blocked | Run `/testme` or `npx comp-gate generate` → answer → `verify` |
321
+ | Commit blocked | `gateCommits` is on — run `/testme` before committing |
322
+ | Session stale | Re-run `generate` after new edits |
323
+ | Missing terms | Add clearer bullets to `PROMPTS.md` |
324
+ | Pass invalid | `npx comp-gate status` — diff changed since verify |
325
+ | Gate disabled | Run `/testing` or `npx comp-gate testing on` to re-enable |
326
+
327
+ ## Development
328
+
329
+ ```bash
330
+ npm install
331
+ npm run build
332
+ npm test
333
+ ```
334
+
335
+ ### Publishing
336
+
337
+ ```bash
338
+ npm login
339
+ npm publish
340
+ ```
341
+
342
+ The package name on npm is `comp-gate` (the unscoped name `testme` is taken by an unrelated package). After publishing, users install with:
343
+
344
+ ```bash
345
+ npx comp-gate init
346
+ ```
347
+
348
+ ## Limitations (v1)
349
+
350
+ - Symbol extraction uses regex (TS/JS/Python/Go patterns) — not a full AST parser
351
+ - Protected branches configurable via `protectedBranches` in `testme.config.json` (`--branch` flag to override)
352
+ - Optional commit gating via `gateCommits: true` in `testme.config.json`
353
+ - Verification is semantic by default (`grading: "semantic"`); legacy keyword mode available
@@ -0,0 +1,2 @@
1
+ export declare function installAgentsMd(cwd: string, created: string[]): void;
2
+ //# sourceMappingURL=agents-md.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents-md.d.ts","sourceRoot":"","sources":["../../src/agents/agents-md.ts"],"names":[],"mappings":"AAOA,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAqBpE"}
@@ -0,0 +1,25 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { agentTemplatesDir } from "./shared.js";
4
+ const SECTION_START = "<!-- testme:start -->";
5
+ const SECTION_END = "<!-- testme:end -->";
6
+ export function installAgentsMd(cwd, created) {
7
+ const sectionPath = path.join(agentTemplatesDir("agents-md"), "section.md");
8
+ const section = readFileSync(sectionPath, "utf8").trim();
9
+ const wrapped = `${SECTION_START}\n${section}\n${SECTION_END}`;
10
+ const targetPath = path.join(cwd, "AGENTS.md");
11
+ const existing = existsSync(targetPath) ? readFileSync(targetPath, "utf8") : "";
12
+ if (existing.includes(SECTION_START) && existing.includes(SECTION_END)) {
13
+ const start = existing.indexOf(SECTION_START);
14
+ const end = existing.indexOf(SECTION_END) + SECTION_END.length;
15
+ const updated = `${existing.slice(0, start)}${wrapped}${existing.slice(end)}`;
16
+ writeFileSync(targetPath, updated.endsWith("\n") ? updated : `${updated}\n`, "utf8");
17
+ created.push("AGENTS.md (testme section updated)");
18
+ return;
19
+ }
20
+ const header = existing.length > 0 ? "\n\n" : "# Agent Instructions\n\n";
21
+ const content = existing.length > 0 ? `${existing.replace(/\s*$/, "")}${header}${wrapped}\n` : `${header}${wrapped}\n`;
22
+ writeFileSync(targetPath, content, "utf8");
23
+ created.push("AGENTS.md (testme section added)");
24
+ }
25
+ //# sourceMappingURL=agents-md.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents-md.js","sourceRoot":"","sources":["../../src/agents/agents-md.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,aAAa,GAAG,uBAAuB,CAAC;AAC9C,MAAM,WAAW,GAAG,qBAAqB,CAAC;AAE1C,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,OAAiB;IAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,YAAY,CAAC,CAAC;IAC5E,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACzD,MAAM,OAAO,GAAG,GAAG,aAAa,KAAK,OAAO,KAAK,WAAW,EAAE,CAAC;IAE/D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEhF,IAAI,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACvE,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC;QAC/D,MAAM,OAAO,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9E,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,EAAE,MAAM,CAAC,CAAC;QACrF,OAAO,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QACnD,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,0BAA0B,CAAC;IACzE,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,MAAM,GAAG,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,OAAO,IAAI,CAAC;IACvH,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC3C,OAAO,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function installClaude(cwd: string, created: string[]): void;
2
+ //# sourceMappingURL=claude.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":"AAqCA,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAoClE"}
@@ -0,0 +1,45 @@
1
+ import path from "node:path";
2
+ import { agentTemplatesDir, copySkill, readJson, writeJson } from "./shared.js";
3
+ function mergeHookCommands(existing, incoming) {
4
+ const merged = [...existing];
5
+ const seen = new Set(existing.map((hook) => hook.command));
6
+ for (const hook of incoming) {
7
+ if (!seen.has(hook.command)) {
8
+ merged.push(hook);
9
+ seen.add(hook.command);
10
+ }
11
+ }
12
+ return merged;
13
+ }
14
+ export function installClaude(cwd, created) {
15
+ const fragmentPath = path.join(agentTemplatesDir("claude"), "settings.fragment.json");
16
+ const targetPath = path.join(cwd, ".claude", "settings.json");
17
+ const fragment = readJson(fragmentPath);
18
+ if (!fragment?.hooks?.PreToolUse) {
19
+ throw new Error("Missing Claude settings template.");
20
+ }
21
+ const existing = readJson(targetPath) ?? { hooks: {} };
22
+ const preToolUse = [...(existing.hooks?.PreToolUse ?? [])];
23
+ for (const incoming of fragment.hooks.PreToolUse) {
24
+ const idx = preToolUse.findIndex((item) => item.matcher === incoming.matcher);
25
+ if (idx < 0) {
26
+ preToolUse.push(incoming);
27
+ continue;
28
+ }
29
+ preToolUse[idx] = {
30
+ ...preToolUse[idx],
31
+ hooks: mergeHookCommands(preToolUse[idx].hooks, incoming.hooks),
32
+ };
33
+ }
34
+ const merged = {
35
+ ...existing,
36
+ hooks: {
37
+ ...existing.hooks,
38
+ PreToolUse: preToolUse,
39
+ },
40
+ };
41
+ writeJson(targetPath, merged);
42
+ created.push(".claude/settings.json (merged)");
43
+ copySkill("claude", cwd, created);
44
+ }
45
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAmBhF,SAAS,iBAAiB,CACxB,QAA6B,EAC7B,QAA6B;IAE7B,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAE3D,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,OAAiB;IAC1D,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,wBAAwB,CAAC,CAAC;IACtF,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,QAAQ,CAAiB,YAAY,CAAC,CAAC;IAExD,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAiB,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACvE,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC;IAE3D,KAAK,MAAM,QAAQ,IAAI,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9E,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACZ,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC1B,SAAS;QACX,CAAC;QAED,UAAU,CAAC,GAAG,CAAC,GAAG;YAChB,GAAG,UAAU,CAAC,GAAG,CAAC;YAClB,KAAK,EAAE,iBAAiB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC;SAChE,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAmB;QAC7B,GAAG,QAAQ;QACX,KAAK,EAAE;YACL,GAAG,QAAQ,CAAC,KAAK;YACjB,UAAU,EAAE,UAAU;SACvB;KACF,CAAC;IAEF,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;IAC/C,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function installCursor(cwd: string, created: string[]): void;
2
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"AAcA,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAiClE"}
@@ -0,0 +1,28 @@
1
+ import path from "node:path";
2
+ import { agentTemplatesDir, copySkill, mergeJsonArraysByKey, readJson, writeJson, } from "./shared.js";
3
+ export function installCursor(cwd, created) {
4
+ const templatePath = path.join(agentTemplatesDir("cursor"), "hooks.json");
5
+ const targetPath = path.join(cwd, ".cursor", "hooks.json");
6
+ const template = readJson(templatePath);
7
+ if (!template) {
8
+ throw new Error("Missing Cursor hooks template.");
9
+ }
10
+ const existing = readJson(targetPath);
11
+ if (!existing) {
12
+ writeJson(targetPath, template);
13
+ created.push(".cursor/hooks.json");
14
+ }
15
+ else {
16
+ const merged = {
17
+ version: existing.version ?? template.version ?? 1,
18
+ hooks: { ...existing.hooks },
19
+ };
20
+ for (const [event, hooks] of Object.entries(template.hooks)) {
21
+ merged.hooks[event] = mergeJsonArraysByKey(merged.hooks[event] ?? [], hooks, "command");
22
+ }
23
+ writeJson(targetPath, merged);
24
+ created.push(".cursor/hooks.json (merged)");
25
+ }
26
+ copySkill("cursor", cwd, created);
27
+ }
28
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,oBAAoB,EACpB,QAAQ,EACR,SAAS,GACV,MAAM,aAAa,CAAC;AAOrB,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,OAAiB;IAC1D,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,CAAC;IAC1E,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,QAAQ,CAAoB,YAAY,CAAC,CAAC;IAE3D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAoB,UAAU,CAAC,CAAC;IAEzD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAChC,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACrC,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,GAAsB;YAChC,OAAO,EAAE,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,IAAI,CAAC;YAClD,KAAK,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE;SAC7B,CAAC;QAEF,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5D,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,oBAAoB,CACxC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EACzB,KAAK,EACL,SAAS,CACV,CAAC;QACJ,CAAC;QAED,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC9B,OAAO,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC9C,CAAC;IAED,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { AgentDetectionResult, AgentId } from "./types.js";
2
+ export declare function detectAgents(cwd: string, env?: NodeJS.ProcessEnv): AgentDetectionResult;
3
+ export declare function parseAgentList(value: string): AgentId[];
4
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/agents/detect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAe,MAAM,YAAY,CAAC;AA8B7E,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,oBAAoB,CAsEpG;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,CAavD"}
@@ -0,0 +1,96 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ function addSignal(signals, signal) {
4
+ const existing = signals.find((s) => s.id === signal.id);
5
+ if (!existing || (existing.confidence === "medium" && signal.confidence === "high")) {
6
+ if (existing) {
7
+ signals.splice(signals.indexOf(existing), 1);
8
+ }
9
+ signals.push(signal);
10
+ }
11
+ }
12
+ function hasRepoSignal(cwd, relativePath) {
13
+ return existsSync(path.join(cwd, relativePath));
14
+ }
15
+ function agentsMdMentionsTestme(cwd) {
16
+ const agentsPath = path.join(cwd, "AGENTS.md");
17
+ if (!existsSync(agentsPath)) {
18
+ return false;
19
+ }
20
+ const content = readFileSync(agentsPath, "utf8").toLowerCase();
21
+ return (content.includes("testme") ||
22
+ content.includes("comp-gate") ||
23
+ content.includes("npx testme") ||
24
+ content.includes("npx comp-gate"));
25
+ }
26
+ export function detectAgents(cwd, env = process.env) {
27
+ const signals = [];
28
+ if (hasRepoSignal(cwd, ".cursor/hooks.json") ||
29
+ hasRepoSignal(cwd, ".cursor/skills") ||
30
+ hasRepoSignal(cwd, ".cursor")) {
31
+ addSignal(signals, {
32
+ id: "cursor",
33
+ confidence: hasRepoSignal(cwd, ".cursor/hooks.json") ? "high" : "medium",
34
+ reason: ".cursor/ configuration found in repo",
35
+ });
36
+ }
37
+ if (hasRepoSignal(cwd, ".claude/settings.json") ||
38
+ hasRepoSignal(cwd, ".claude/skills") ||
39
+ hasRepoSignal(cwd, ".claude")) {
40
+ addSignal(signals, {
41
+ id: "claude",
42
+ confidence: hasRepoSignal(cwd, ".claude/settings.json") ? "high" : "medium",
43
+ reason: ".claude/ configuration found in repo",
44
+ });
45
+ }
46
+ if (hasRepoSignal(cwd, ".windsurf/hooks.json") || hasRepoSignal(cwd, ".windsurf")) {
47
+ addSignal(signals, {
48
+ id: "windsurf",
49
+ confidence: hasRepoSignal(cwd, ".windsurf/hooks.json") ? "high" : "medium",
50
+ reason: ".windsurf/ configuration found in repo",
51
+ });
52
+ }
53
+ if (agentsMdMentionsTestme(cwd)) {
54
+ addSignal(signals, {
55
+ id: "agents-md",
56
+ confidence: "high",
57
+ reason: "AGENTS.md already references testme",
58
+ });
59
+ }
60
+ if (env.CURSOR_TRACE_ID || env.CURSOR_SESSION_ID) {
61
+ addSignal(signals, {
62
+ id: "cursor",
63
+ confidence: "medium",
64
+ reason: "Cursor environment detected",
65
+ });
66
+ }
67
+ if (env.CLAUDE_CODE || env.CLAUDE_SESSION) {
68
+ addSignal(signals, {
69
+ id: "claude",
70
+ confidence: "medium",
71
+ reason: "Claude Code environment detected",
72
+ });
73
+ }
74
+ const suggested = [...new Set(signals.map((s) => s.id))];
75
+ const highConfidence = new Set(signals.filter((s) => s.confidence === "high").map((s) => s.id));
76
+ const ambiguous = suggested.length !== 1 && !(suggested.length === 0 && highConfidence.size === 0);
77
+ return {
78
+ signals,
79
+ suggested: suggested.length > 0 ? suggested : [],
80
+ ambiguous: suggested.length === 0 || suggested.length > 1,
81
+ };
82
+ }
83
+ export function parseAgentList(value) {
84
+ if (value === "all") {
85
+ return ["cursor", "claude", "windsurf", "agents-md"];
86
+ }
87
+ const ids = value.split(",").map((part) => part.trim());
88
+ const valid = ["cursor", "claude", "windsurf", "agents-md"];
89
+ for (const id of ids) {
90
+ if (!valid.includes(id)) {
91
+ throw new Error(`Unknown agent: ${id}. Use cursor, claude, windsurf, agents-md, or all.`);
92
+ }
93
+ }
94
+ return ids;
95
+ }
96
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/agents/detect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare function copyIfMissing(source: string, target: string): boolean;
2
+ export declare function copyAlways(source: string, target: string): void;
3
+ export declare function agentTemplatesDir(agent: string): string;
4
+ export declare function mergeJsonArraysByKey<T extends Record<string, unknown>>(existing: T[], incoming: T[], key: keyof T): T[];
5
+ export declare function writeJson(filePath: string, value: unknown): void;
6
+ export declare function readJson<T>(filePath: string): T | null;
7
+ export declare function copySkill(agent: string, cwd: string, created: string[]): void;
8
+ export declare function chmodScripts(dir: string): void;
9
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/agents/shared.ts"],"names":[],"mappings":"AAIA,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAQrE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAG/D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpE,QAAQ,EAAE,CAAC,EAAE,EACb,QAAQ,EAAE,CAAC,EAAE,EACb,GAAG,EAAE,MAAM,CAAC,GACX,CAAC,EAAE,CAaL;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAGhE;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAKtD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAoC7E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAU9C"}