@activate-core/test-forge 0.19.1 → 0.19.2
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 +5 -0
- package/dist/core/project/config.js +31 -11
- package/dist/core/project/map.js +15 -3
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -330,6 +330,11 @@ The test runner produces its own pass/fail report.
|
|
|
330
330
|
added to them, never instead of them — so name only what is particular
|
|
331
331
|
to your product. Every domain has something: a shipping carrier, a CMS,
|
|
332
332
|
a ticketing or streaming backend, an ad network.
|
|
333
|
+
- **relatedDirs** — other repos this project depends on, read-only, e.g.
|
|
334
|
+
`["../backend"]` for a frontend whose API and database live next door.
|
|
335
|
+
Every AI phase may read them, so the map captures the real data layer and
|
|
336
|
+
tests can check what the API stores. A headless run cannot read outside
|
|
337
|
+
the project otherwise.
|
|
333
338
|
|
|
334
339
|
## Notes
|
|
335
340
|
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* into the output. See withTextOnlyTools / withEditTools.
|
|
27
27
|
*/
|
|
28
28
|
import { readFileSync, existsSync } from 'node:fs';
|
|
29
|
-
import { join } from 'node:path';
|
|
29
|
+
import { join, resolve } from 'node:path';
|
|
30
30
|
const CONFIG_PATH = 'testforge.config.json';
|
|
31
31
|
const DEFAULT_CONFIG = {
|
|
32
32
|
aiCommand: 'claude -p',
|
|
@@ -146,25 +146,42 @@ export function withTextOnlyTools(cmd) {
|
|
|
146
146
|
* assumptions: without source access the model leaves "endpoint shape
|
|
147
147
|
* unconfirmed" TODOs; with it, those become real assertions.
|
|
148
148
|
*/
|
|
149
|
-
export function withReadOnlyTools(cmd) {
|
|
149
|
+
export function withReadOnlyTools(cmd, relatedDirs = loadConfig().relatedDirs ?? []) {
|
|
150
150
|
if (!isClaude(cmd.program))
|
|
151
151
|
return cmd;
|
|
152
152
|
return {
|
|
153
153
|
program: cmd.program,
|
|
154
154
|
// Space-separated allowlist; Write/Edit/Bash deliberately excluded.
|
|
155
|
-
args: [...cmd.args, '--allowedTools', 'Read Grep Glob LS'],
|
|
155
|
+
args: [...cmd.args, '--allowedTools', 'Read Grep Glob LS', ...addDirArgs(relatedDirs)],
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Read-only tools a headless exploring phase needs pre-approved. The model
|
|
160
|
+
* reaches for shell `grep`/`ls` as often as for the Grep tool; under
|
|
161
|
+
* `acceptEdits` alone those are DENIED (no one is there to approve), and
|
|
162
|
+
* it gives up on what it was mapping. Only commands that read are listed.
|
|
163
|
+
*/
|
|
164
|
+
const EXPLORE_TOOLS = ['Read', 'Grep', 'Glob', 'LS', 'Bash(grep:*)', 'Bash(rg:*)', 'Bash(ls:*)', 'Bash(wc:*)'];
|
|
165
|
+
/** `--add-dir` per related repo: reads outside the project are otherwise denied. */
|
|
166
|
+
function addDirArgs(relatedDirs) {
|
|
167
|
+
return relatedDirs.flatMap((dir) => ['--add-dir', resolve(dir)]);
|
|
168
|
+
}
|
|
158
169
|
/**
|
|
159
170
|
* Args for a phase where the model must read/write files (init, map,
|
|
160
|
-
* discover). Mirrors withTextOnlyTools but grants edit permission
|
|
171
|
+
* discover). Mirrors withTextOnlyTools but grants edit permission, plus the
|
|
172
|
+
* read-only exploring tools and the related repos.
|
|
161
173
|
*/
|
|
162
|
-
export function withEditTools(cmd) {
|
|
174
|
+
export function withEditTools(cmd, relatedDirs = loadConfig().relatedDirs ?? []) {
|
|
163
175
|
if (!isClaude(cmd.program))
|
|
164
176
|
return cmd;
|
|
165
177
|
return {
|
|
166
178
|
program: cmd.program,
|
|
167
|
-
args: [
|
|
179
|
+
args: [
|
|
180
|
+
...cmd.args,
|
|
181
|
+
'--permission-mode', 'acceptEdits',
|
|
182
|
+
'--allowedTools', EXPLORE_TOOLS.join(' '),
|
|
183
|
+
...addDirArgs(relatedDirs),
|
|
184
|
+
],
|
|
168
185
|
};
|
|
169
186
|
}
|
|
170
187
|
/**
|
|
@@ -179,13 +196,11 @@ export function withEditTools(cmd) {
|
|
|
179
196
|
* `claude_ai_Notion`, and a CLI-added server conventionally named
|
|
180
197
|
* `notion`). Non-Claude CLIs get the command unchanged.
|
|
181
198
|
*/
|
|
182
|
-
export function withReferenceTools(cmd) {
|
|
199
|
+
export function withReferenceTools(cmd, relatedDirs = loadConfig().relatedDirs ?? []) {
|
|
183
200
|
if (!isClaude(cmd.program))
|
|
184
201
|
return cmd;
|
|
185
202
|
const allow = [
|
|
186
|
-
|
|
187
|
-
'Glob',
|
|
188
|
-
'Grep',
|
|
203
|
+
...EXPLORE_TOOLS,
|
|
189
204
|
'WebFetch',
|
|
190
205
|
'WebSearch',
|
|
191
206
|
'mcp__notion__*',
|
|
@@ -193,7 +208,12 @@ export function withReferenceTools(cmd) {
|
|
|
193
208
|
].join(' ');
|
|
194
209
|
return {
|
|
195
210
|
program: cmd.program,
|
|
196
|
-
args: [
|
|
211
|
+
args: [
|
|
212
|
+
...cmd.args,
|
|
213
|
+
'--permission-mode', 'acceptEdits',
|
|
214
|
+
'--allowedTools', allow,
|
|
215
|
+
...addDirArgs(relatedDirs),
|
|
216
|
+
],
|
|
197
217
|
};
|
|
198
218
|
}
|
|
199
219
|
/**
|
package/dist/core/project/map.js
CHANGED
|
@@ -20,12 +20,23 @@
|
|
|
20
20
|
import { execFileSync } from 'node:child_process';
|
|
21
21
|
import { loadConfig, splitCommand, withEditTools } from './config.js';
|
|
22
22
|
/** Build the prompt that asks the AI to produce the project map. */
|
|
23
|
-
export function buildMapPrompt(srcDir) {
|
|
23
|
+
export function buildMapPrompt(srcDir, relatedDirs = []) {
|
|
24
|
+
const related = relatedDirs.length
|
|
25
|
+
? [
|
|
26
|
+
`This project depends on related repos, which you may READ but must`,
|
|
27
|
+
`not change: ${relatedDirs.map((d) => `"${d}"`).join(', ')}. Journeys`,
|
|
28
|
+
`come only from "${srcDir}". When the data layer (database, models,`,
|
|
29
|
+
`enums) or the API this project calls lives in a related repo, map it`,
|
|
30
|
+
`from there, and name the repo it came from.`,
|
|
31
|
+
``,
|
|
32
|
+
]
|
|
33
|
+
: [];
|
|
24
34
|
return [
|
|
25
35
|
`You are a senior engineer mapping a codebase so a QA tool can`,
|
|
26
36
|
`plan tests and seed test data. Read the source code under`,
|
|
27
37
|
`"${srcDir}" and produce a SHORT, high-level map of the project.`,
|
|
28
38
|
``,
|
|
39
|
+
...related,
|
|
29
40
|
`The map has two parts: journeys and the data layer.`,
|
|
30
41
|
``,
|
|
31
42
|
`PART 1 — Journeys.`,
|
|
@@ -79,8 +90,9 @@ export function buildMapPrompt(srcDir) {
|
|
|
79
90
|
}
|
|
80
91
|
/** Run the map builder via the configured AI command. */
|
|
81
92
|
export function runMap(srcDir) {
|
|
82
|
-
const
|
|
83
|
-
const
|
|
93
|
+
const config = loadConfig();
|
|
94
|
+
const prompt = buildMapPrompt(srcDir, config.relatedDirs ?? []);
|
|
95
|
+
const { program, args } = withEditTools(splitCommand(config.aiCommand), config.relatedDirs ?? []);
|
|
84
96
|
const output = execFileSync(program, args, {
|
|
85
97
|
input: prompt,
|
|
86
98
|
encoding: 'utf-8',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@activate-core/test-forge",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.2",
|
|
4
4
|
"description": "AI-powered QA automation. Scans a project, drafts journey docs, and generates seeded Jest/Playwright tests. Works in any project, with any AI CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "rm -rf dist && tsc",
|
|
17
|
-
"dev": "tsc --watch"
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"test": "tsx --test test/*.test.ts"
|
|
18
19
|
},
|
|
19
20
|
"license": "MIT",
|
|
20
21
|
"publishConfig": {
|