@activate-core/test-forge 0.19.1 → 0.19.3
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/docs/discover.js +9 -2
- package/dist/core/docs/doc-guidance.js +81 -9
- package/dist/core/docs/reference.js +25 -25
- package/dist/core/generate/ai-generator.js +5 -2
- package/dist/core/generate/suite-planner.js +3 -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
|
|
|
@@ -92,11 +92,18 @@ export function buildDiscoverPrompt(srcDir, docsDir, journey) {
|
|
|
92
92
|
` and the key fields/enum values a test would need to`,
|
|
93
93
|
` seed or verify (use the data layer from the map)`,
|
|
94
94
|
``,
|
|
95
|
+
`Also add "Test setup" and "Questions for product (not asserted)" last,`,
|
|
96
|
+
`as described above.`,
|
|
97
|
+
``,
|
|
95
98
|
`Rules:`,
|
|
96
99
|
`- Base everything on real evidence in the code and the map. Do not`,
|
|
97
100
|
` invent routes, fields, or behavior with false confidence.`,
|
|
98
|
-
`-
|
|
99
|
-
`
|
|
101
|
+
`- With no external reference, the code is the source: behaviour the`,
|
|
102
|
+
` code clearly and deliberately implements is [spec]. Still add the`,
|
|
103
|
+
` full [baseline] scenarios for every field, control and action.`,
|
|
104
|
+
`- Never write a "# TODO" line: what the tests need goes in "Test`,
|
|
105
|
+
` setup", what only product can decide goes in "Questions for product`,
|
|
106
|
+
` (not asserted)".`,
|
|
100
107
|
`- For the Data section, never include secrets — refer to env var`,
|
|
101
108
|
` names only (as in the map).`,
|
|
102
109
|
`- Capture real risks you notice (e.g. a charge that could happen`,
|
|
@@ -5,11 +5,28 @@
|
|
|
5
5
|
* depth is the ceiling on test quality. This wording is shared by both
|
|
6
6
|
* discover prompts (code-based and reference-based) so a doc is deep and
|
|
7
7
|
* well-sectioned regardless of where it is drafted from.
|
|
8
|
+
*
|
|
9
|
+
* A source (a client spec, a ticket) describes a journey in a few lines;
|
|
10
|
+
* the doc must turn that into EVERY scenario a QA engineer would test. So
|
|
11
|
+
* each criterion carries a tier saying where it came from, and a failing
|
|
12
|
+
* test is reported under that tier:
|
|
13
|
+
* [spec] — the source states it. A failure is a defect against the spec.
|
|
14
|
+
* [baseline] — universal robustness every well-built app owes, added for
|
|
15
|
+
* every field, control and action even when the source is
|
|
16
|
+
* silent. Robustness only, never product policy.
|
|
17
|
+
* [observed] — the source is silent but the code fixes a concrete rule;
|
|
18
|
+
* tested for consistency across layers, not as the spec.
|
|
19
|
+
* Nothing becomes a "# TODO": what only product can decide is a question,
|
|
20
|
+
* what the test environment needs is test setup.
|
|
8
21
|
*/
|
|
9
22
|
export const DEEP_DOC_GUIDANCE = [
|
|
10
23
|
`Make each doc DEEP and exhaustive — the generated tests can only be as`,
|
|
11
|
-
`good as this doc, so do NOT stop at a thin outline.
|
|
12
|
-
`
|
|
24
|
+
`good as this doc, so do NOT stop at a thin outline. A source usually`,
|
|
25
|
+
`describes a journey in a few lines ("the user enters a username and a`,
|
|
26
|
+
`password"); YOUR job is to expand it into every scenario a senior QA`,
|
|
27
|
+
`engineer would test: the happy path, every validation of every field,`,
|
|
28
|
+
`every state of every control, every failure and edge case. For the`,
|
|
29
|
+
`journey, document, where they apply:`,
|
|
13
30
|
`- every distinct sub-flow and lifecycle STATE it can be in, and the`,
|
|
14
31
|
` transitions between them (not just the happy path);`,
|
|
15
32
|
`- each endpoint/screen involved, with real inputs, outputs, status`,
|
|
@@ -20,14 +37,69 @@ export const DEEP_DOC_GUIDANCE = [
|
|
|
20
37
|
` vague mentions;`,
|
|
21
38
|
`- concrete acceptance criteria per case (exactly what must hold).`,
|
|
22
39
|
`Aim for the depth of a spec a new engineer could implement from.`,
|
|
23
|
-
|
|
24
|
-
`
|
|
25
|
-
`
|
|
26
|
-
`
|
|
27
|
-
`
|
|
28
|
-
|
|
40
|
+
``,
|
|
41
|
+
`TIERS. Every acceptance criterion becomes an ORACLE: a test is generated`,
|
|
42
|
+
`from it, and a product that disagrees is reported. So start EVERY`,
|
|
43
|
+
`criterion with its tier, which says where it comes from and how a`,
|
|
44
|
+
`failure is reported:`,
|
|
45
|
+
`- "[spec]" — the source states this behaviour. A failure is a defect`,
|
|
46
|
+
` against the spec.`,
|
|
47
|
+
`- "[baseline]" — universal robustness that holds for ANY well-built app.`,
|
|
48
|
+
` Add these YOURSELF for every field, control and action the journey`,
|
|
49
|
+
` has, even when the source says nothing about them:`,
|
|
50
|
+
` * Inputs: empty and whitespace-only values; leading/trailing spaces;`,
|
|
51
|
+
` a very long value (5,000 characters); unicode and emoji; an HTML or`,
|
|
52
|
+
` script payload. The app never crashes, never answers with a server`,
|
|
53
|
+
` error (5xx), never executes the payload (it is shown as text), and`,
|
|
54
|
+
` either accepts the value or rejects it with a visible reason.`,
|
|
55
|
+
` * No silent failure: every rejected submit shows a visible reason; a`,
|
|
56
|
+
` server error, network failure or timeout shows a visible message`,
|
|
57
|
+
` and leaves the form usable.`,
|
|
58
|
+
` * Double submission: a double click or a repeated Enter sends at most`,
|
|
59
|
+
` one request with a side effect (one record, one charge, one`,
|
|
60
|
+
` message); the control is disabled or ignored while a request is`,
|
|
61
|
+
` pending.`,
|
|
62
|
+
` * Keyboard: the form can be filled and submitted with the keyboard`,
|
|
63
|
+
` alone; Enter submits.`,
|
|
64
|
+
` * Navigation: refresh, Back, and opening a mid-flow URL directly never`,
|
|
65
|
+
` crash or leave a blank, broken page.`,
|
|
66
|
+
` * Sensitive data: passwords are masked; passwords and tokens never`,
|
|
67
|
+
` appear in a URL or query string.`,
|
|
68
|
+
` * Access: a page or action that needs a login is not usable logged`,
|
|
69
|
+
` out; one user never sees another user's data.`,
|
|
70
|
+
` * Choice controls (dropdown, radio, checkbox, list): every option`,
|
|
71
|
+
` shown can be selected and submitted.`,
|
|
72
|
+
` [baseline] is ROBUSTNESS only. Never present product POLICY you`,
|
|
73
|
+
` invented (a maximum length, a password rule, an error wording, a`,
|
|
74
|
+
` limit) as [baseline].`,
|
|
75
|
+
`- "[observed]" — the source is silent, but the code fixes a concrete`,
|
|
76
|
+
` rule: a validation pattern, a length limit, allowed values, the`,
|
|
77
|
+
` options a list offers, a rate limit, a timeout. State the rule with`,
|
|
78
|
+
` its real value, use it to build valid data and boundary cases (just`,
|
|
79
|
+
` inside and just outside), and require every layer to enforce it the`,
|
|
80
|
+
` same way (the screen accepts exactly what the server accepts). A`,
|
|
81
|
+
` failure is an inconsistency, not a spec defect. Never use [observed]`,
|
|
82
|
+
` to override or soften a [spec] criterion.`,
|
|
83
|
+
``,
|
|
84
|
+
`TEST DATA. Tests fill data themselves. Never ask for an exact value:`,
|
|
85
|
+
`valid data comes from the stated or [observed] rules; invalid data`,
|
|
86
|
+
`breaks one rule at a time; a choice control is filled with an option`,
|
|
87
|
+
`the screen actually offers at run time, never a hard-coded one.`,
|
|
88
|
+
``,
|
|
89
|
+
`NO TODOs. Never write a "# TODO" line or leave a placeholder. Instead:`,
|
|
90
|
+
`- What the test environment must provide — accounts and records to`,
|
|
91
|
+
` seed, settings or flags that must be on, external services to stub`,
|
|
92
|
+
` (SMS/OTP, payments, OAuth, email) and the endpoint or SDK to stub,`,
|
|
93
|
+
` browser dialogs to handle — goes in a "## Test setup" section, written`,
|
|
94
|
+
` as instructions for the test generator.`,
|
|
95
|
+
`- What only product can decide, and neither the source nor the code`,
|
|
96
|
+
` fixes, goes in a "## Questions for product (not asserted)" section.`,
|
|
97
|
+
` Keep it short: a question is a last resort, after [spec], [baseline]`,
|
|
98
|
+
` and [observed] have been used.`,
|
|
99
|
+
``,
|
|
29
100
|
`Organise the body with numbered "## N. <Section>" headings, ONE`,
|
|
30
101
|
`concern per section, because each section later becomes its own`,
|
|
31
102
|
`focused test file — so the richer and better-separated the sections,`,
|
|
32
|
-
`the better the test suite
|
|
103
|
+
`the better the test suite. Put "## Test setup" and "## Questions for`,
|
|
104
|
+
`product (not asserted)" last, without numbers.`,
|
|
33
105
|
].join('\n');
|
|
@@ -115,10 +115,11 @@ export function buildReferenceDiscoverPrompt(srcDir, docsDir, reference, journey
|
|
|
115
115
|
`it describes. The comparison you need is already in "${DIFF_PATH}".`,
|
|
116
116
|
``,
|
|
117
117
|
`Use the project map at "${MAP_PATH}" and the source under "${srcDir}"`,
|
|
118
|
-
`
|
|
119
|
-
`
|
|
120
|
-
`
|
|
121
|
-
`
|
|
118
|
+
`for TECHNICAL detail the reference does not spell out (routes, field`,
|
|
119
|
+
`names, models/collections, enums, env var names), for the concrete`,
|
|
120
|
+
`rules behind [observed] criteria, and for what the Test setup needs.`,
|
|
121
|
+
`NEVER take a [spec] criterion from the code — [spec] comes only from`,
|
|
122
|
+
`the reference.`,
|
|
122
123
|
``,
|
|
123
124
|
`For EACH journey the reference describes, create one markdown file at`,
|
|
124
125
|
`"${docsDir}/<area>/<journey>.md" (area = folder such as api/web/ops,`,
|
|
@@ -137,10 +138,9 @@ export function buildReferenceDiscoverPrompt(srcDir, docsDir, reference, journey
|
|
|
137
138
|
``,
|
|
138
139
|
]
|
|
139
140
|
: [
|
|
140
|
-
`
|
|
141
|
-
`the
|
|
142
|
-
`
|
|
143
|
-
`at its top so it is clearly flagged.`,
|
|
141
|
+
`Create docs ONLY for journeys the reference describes. A journey`,
|
|
142
|
+
`that is in the map but not in the reference gets no file; name it`,
|
|
143
|
+
`in your final summary as "not covered by this reference".`,
|
|
144
144
|
``,
|
|
145
145
|
]),
|
|
146
146
|
`Each file starts with this header (required):`,
|
|
@@ -155,25 +155,25 @@ export function buildReferenceDiscoverPrompt(srcDir, docsDir, reference, journey
|
|
|
155
155
|
`- security (add more if relevant: performance, accessibility, ...)`,
|
|
156
156
|
``,
|
|
157
157
|
`Then whichever of these sections apply: Fields, Actions, Expected,`,
|
|
158
|
-
`Negative Cases, Race Conditions, Webhooks, Security Notes, Data
|
|
159
|
-
`
|
|
160
|
-
`
|
|
158
|
+
`Negative Cases, Race Conditions, Webhooks, Security Notes, Data, and`,
|
|
159
|
+
`last "Test setup" and "Questions for product (not asserted)". For the`,
|
|
160
|
+
`Data section use the map's data layer.`,
|
|
161
161
|
``,
|
|
162
162
|
`Rules:`,
|
|
163
|
-
`-
|
|
164
|
-
`
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
`
|
|
168
|
-
`
|
|
169
|
-
|
|
170
|
-
`
|
|
171
|
-
` the
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
`
|
|
175
|
-
`-
|
|
176
|
-
`
|
|
163
|
+
`- Tag every criterion [spec], [baseline] or [observed] as described`,
|
|
164
|
+
` above. [spec] only where the reference states the behaviour; never`,
|
|
165
|
+
` present your own idea of product policy as [spec].`,
|
|
166
|
+
`- Expand the reference: a field, control or action it mentions gets`,
|
|
167
|
+
` its full [baseline] scenarios and, where the code fixes a rule, its`,
|
|
168
|
+
` [observed] scenarios, even when the reference names it in passing.`,
|
|
169
|
+
`- When the code seems to behave differently from a [spec] criterion,`,
|
|
170
|
+
` keep the criterion exactly as the reference states it — the test`,
|
|
171
|
+
` will report the difference — and add a line "Likely defect: <what`,
|
|
172
|
+
` the code does>" under it. Never turn it into a question.`,
|
|
173
|
+
`- Base technical detail on real code; do not invent it with false`,
|
|
174
|
+
` confidence.`,
|
|
175
|
+
`- No "# TODO" lines and no "Suggested" section: use "Test setup" and`,
|
|
176
|
+
` "Questions for product (not asserted)" as described above.`,
|
|
177
177
|
`- Never include secrets — refer to env var names only.`,
|
|
178
178
|
`- Create the area subfolders as needed. Write NOTHING at the root of`,
|
|
179
179
|
` "${docsDir}" and no file that is not a journey doc — a shared note`,
|
|
@@ -130,8 +130,11 @@ export function buildPrompt(journey, map, testPath, focus, harnessPath, transpor
|
|
|
130
130
|
` than the prose. Only after checking those is it genuinely unstated.`,
|
|
131
131
|
` An assertion you cannot trace to a stated requirement is a false`,
|
|
132
132
|
` positive waiting to happen. Anything under a "Suggested (not a`,
|
|
133
|
-
` requirement…)"
|
|
134
|
-
` it; make it ${pending} if you
|
|
133
|
+
` requirement…)" or "Questions for product (not asserted)" heading is`,
|
|
134
|
+
` explicitly NOT an oracle — never assert it; make it ${pending} if you`,
|
|
135
|
+
` cover it at all. Criteria tagged [spec], [baseline] or [observed]`,
|
|
136
|
+
` ARE stated oracles. The "Test setup" section says how to establish`,
|
|
137
|
+
` preconditions (seeds, settings, stubs, dialogs) — follow it.`,
|
|
135
138
|
`(2) INFRASTRUCTURE IS NEVER A PRODUCT DEFECT. If a precondition cannot`,
|
|
136
139
|
` be established — a dependency is unreachable, a required env var or`,
|
|
137
140
|
` credential is absent, seed/fixture data is missing, a second`,
|
|
@@ -40,6 +40,9 @@ function buildPlanPrompt(journey, map) {
|
|
|
40
40
|
` refunds) may need many. Do not pad, do not over-split, do not force`,
|
|
41
41
|
` a fixed number.`,
|
|
42
42
|
`- Each file covers ONE concern and must be independently meaningful.`,
|
|
43
|
+
`- "## Test setup" is what every file needs to run, not a behaviour, and`,
|
|
44
|
+
` "## Questions for product (not asserted)" is not tested: plan no file`,
|
|
45
|
+
` for either.`,
|
|
43
46
|
``,
|
|
44
47
|
`Output ONLY a JSON array (no prose, no markdown fences) of objects:`,
|
|
45
48
|
` [{ "slug": "<short-kebab-name>", "title": "<describe title>",`,
|
|
@@ -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.3",
|
|
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": {
|