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.
- package/README.md +353 -0
- package/dist/agents/agents-md.d.ts +2 -0
- package/dist/agents/agents-md.d.ts.map +1 -0
- package/dist/agents/agents-md.js +25 -0
- package/dist/agents/agents-md.js.map +1 -0
- package/dist/agents/claude.d.ts +2 -0
- package/dist/agents/claude.d.ts.map +1 -0
- package/dist/agents/claude.js +45 -0
- package/dist/agents/claude.js.map +1 -0
- package/dist/agents/cursor.d.ts +2 -0
- package/dist/agents/cursor.d.ts.map +1 -0
- package/dist/agents/cursor.js +28 -0
- package/dist/agents/cursor.js.map +1 -0
- package/dist/agents/detect.d.ts +4 -0
- package/dist/agents/detect.d.ts.map +1 -0
- package/dist/agents/detect.js +96 -0
- package/dist/agents/detect.js.map +1 -0
- package/dist/agents/shared.d.ts +9 -0
- package/dist/agents/shared.d.ts.map +1 -0
- package/dist/agents/shared.js +83 -0
- package/dist/agents/shared.js.map +1 -0
- package/dist/agents/types.d.ts +14 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +8 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/agents/windsurf.d.ts +2 -0
- package/dist/agents/windsurf.d.ts.map +1 -0
- package/dist/agents/windsurf.js +27 -0
- package/dist/agents/windsurf.js.map +1 -0
- package/dist/bootstrap-summary.d.ts +2 -0
- package/dist/bootstrap-summary.d.ts.map +1 -0
- package/dist/bootstrap-summary.js +262 -0
- package/dist/bootstrap-summary.js.map +1 -0
- package/dist/categories.d.ts +13 -0
- package/dist/categories.d.ts.map +1 -0
- package/dist/categories.js +382 -0
- package/dist/categories.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +361 -0
- package/dist/cli.js.map +1 -0
- package/dist/config-init.d.ts +16 -0
- package/dist/config-init.d.ts.map +1 -0
- package/dist/config-init.js +53 -0
- package/dist/config-init.js.map +1 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +127 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -0
- package/dist/detect.d.ts +4 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +244 -0
- package/dist/detect.js.map +1 -0
- package/dist/diff.d.ts +11 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +198 -0
- package/dist/diff.js.map +1 -0
- package/dist/generate.d.ts +7 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +71 -0
- package/dist/generate.js.map +1 -0
- package/dist/gitignore.d.ts +12 -0
- package/dist/gitignore.d.ts.map +1 -0
- package/dist/gitignore.js +106 -0
- package/dist/gitignore.js.map +1 -0
- package/dist/hooks.d.ts +22 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +199 -0
- package/dist/hooks.js.map +1 -0
- package/dist/init.d.ts +24 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +218 -0
- package/dist/init.js.map +1 -0
- package/dist/paths.d.ts +13 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +18 -0
- package/dist/paths.js.map +1 -0
- package/dist/prompts.d.ts +9 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +88 -0
- package/dist/prompts.js.map +1 -0
- package/dist/repo-stats.d.ts +10 -0
- package/dist/repo-stats.d.ts.map +1 -0
- package/dist/repo-stats.js +117 -0
- package/dist/repo-stats.js.map +1 -0
- package/dist/setup-wizard.d.ts +34 -0
- package/dist/setup-wizard.d.ts.map +1 -0
- package/dist/setup-wizard.js +251 -0
- package/dist/setup-wizard.js.map +1 -0
- package/dist/testing.d.ts +6 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +24 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +156 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +31 -0
- package/dist/types.js.map +1 -0
- package/dist/verify.d.ts +38 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +254 -0
- package/dist/verify.js.map +1 -0
- package/package.json +52 -0
- package/src/templates/PROMPTS.md +6 -0
- package/src/templates/SKILL.md +253 -0
- package/src/templates/SUMMARY.md +29 -0
- package/src/templates/agents/agents-md/section.md +11 -0
- package/src/templates/agents/claude/SKILL.md +8 -0
- package/src/templates/agents/claude/settings.fragment.json +16 -0
- package/src/templates/agents/claude/testing-SKILL.md +9 -0
- package/src/templates/agents/cursor/SKILL.md +9 -0
- package/src/templates/agents/cursor/hooks.json +23 -0
- package/src/templates/agents/cursor/testing-SKILL.md +9 -0
- package/src/templates/agents/windsurf/SKILL.md +8 -0
- package/src/templates/agents/windsurf/hooks.json +10 -0
- package/src/templates/agents/windsurf/testing-SKILL.md +9 -0
- package/src/templates/hooks/after-push.sh +17 -0
- package/src/templates/hooks/block-commit.sh +29 -0
- package/src/templates/hooks/block-push.sh +29 -0
- package/src/templates/hooks/claude-pre-tool.sh +59 -0
- package/src/templates/hooks/git-pre-commit.sh +35 -0
- package/src/templates/hooks/git-pre-push.sh +53 -0
- package/src/templates/hooks/statusline.sh +3 -0
- package/src/templates/hooks/windsurf-pre-run.sh +39 -0
- package/src/templates/hooks.json +23 -0
- package/src/templates/testing-SKILL.md +33 -0
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|