create-healix 1.1.6 → 1.3.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 ADDED
@@ -0,0 +1,34 @@
1
+ # create-healix
2
+
3
+ Scaffold a [Healix](https://www.npmjs.com/package/wdio-healix-service) test-automation project in one command — a ready-to-run WebdriverIO setup wired for AI-assisted authoring and **self-healing, standalone** test suites.
4
+
5
+ ```bash
6
+ npx create-healix my-app
7
+ cd my-app
8
+ ```
9
+
10
+ ## What you get
11
+ - **WebdriverIO + Healix service** preconfigured (`wdio.web.conf.ts`).
12
+ - **`.mcp.json`** registering the [`healix-mcp`](https://www.npmjs.com/package/healix-mcp) server, so an AI agent (Claude Code, Cursor, Windsurf, …) can drive the live browser, observe the real DOM, and author specs/page objects for you.
13
+ - **Two-file page-object model** + an example spec/POM to copy.
14
+ - **Allure reporting** and a `reports/screenshots/` directory wired out of the box.
15
+ - **`CLAUDE.md`** + a SessionStart hook so the agent always loads the framework guide.
16
+
17
+ ## Options
18
+ | Flag | Effect |
19
+ |------|--------|
20
+ | `--yes`, `-y` | Accept all defaults |
21
+ | `--platforms=web,android,ios` | Target platforms (default `web`) |
22
+ | `--app-url=<url>` | Default app URL for the example spec |
23
+ | `--mcp=cursor\|claude\|windsurf` | Which MCP client to configure |
24
+ | `--skip-install` | Don't run `npm install` |
25
+
26
+ ## Run it
27
+ ```bash
28
+ npm run session:web # opens a live browser + the Healix API for the agent to drive
29
+ npm test # runs the suite standalone — no AI, no MCP
30
+ ```
31
+
32
+ The deliverable is a normal WDIO suite: it runs in CI with `npx wdio run` and **no AI in the loop**.
33
+
34
+ See [`wdio-healix-service`](https://www.npmjs.com/package/wdio-healix-service) (the framework) and [`healix-mcp`](https://www.npmjs.com/package/healix-mcp) (the agent server).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-healix",
3
- "version": "1.1.6",
3
+ "version": "1.3.0",
4
4
  "description": "Scaffold a healix WDIO project with MCP integration ready out of the box",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "build": "echo 'No build step required — source is plain ESM'",
15
- "test": "node test/seed-parity.test.mjs && node bin/create-healix.js --help",
15
+ "test": "node test/seed-parity.test.mjs && node test/rehydrate-hooks.test.mjs && node bin/create-healix.js --help",
16
16
  "test:parity": "node test/seed-parity.test.mjs"
17
17
  },
18
18
  "dependencies": {
package/src/scaffold.js CHANGED
@@ -4,7 +4,7 @@ import { execSync } from 'node:child_process';
4
4
  import {
5
5
  tsconfigJson, packageJson, wdioWebConf, mcpJson, healixAgentJson,
6
6
  exampleSpec, examplePage, exampleLocators, readme,
7
- claudeSettings, claudeMd, claudeRehydrateScript,
7
+ claudeSettings, claudeMd, claudeRehydrateScript, cursorHooks,
8
8
  } from './templates.js';
9
9
 
10
10
  function write(filePath, content) {
@@ -54,9 +54,12 @@ export function scaffold(projectName, targetDir, options = {}) {
54
54
  write(path.join(projectDir, '.mcp.json'), mcpJson(apiPort));
55
55
  write(path.join(projectDir, 'healix.agent.json'), healixAgentJson());
56
56
  write(path.join(projectDir, 'README.md'), readme(projectName));
57
- // Durable knowledge delivery: compact-hook backstop + thin project-scope pointer.
57
+ // Durable knowledge delivery: re-hydration hook (fires at start + after compaction)
58
+ // wired for EVERY host — Spryv + Claude Code via .claude/settings.json, Cursor via
59
+ // .cursor/hooks.json — all running ONE shared script. Plus a thin project-scope pointer.
58
60
  write(path.join(projectDir, '.claude', 'settings.json'), claudeSettings());
59
61
  write(path.join(projectDir, '.claude', 'healix-rehydrate.mjs'), claudeRehydrateScript());
62
+ write(path.join(projectDir, '.cursor', 'hooks.json'), cursorHooks());
60
63
  write(path.join(projectDir, 'CLAUDE.md'), claudeMd(projectName));
61
64
 
62
65
  write(path.join(projectDir, 'test', 'specs', 'example.spec.ts'), exampleSpec());
package/src/templates.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Template generators for create-healix scaffold output.
2
2
  // Each function returns the file content as a string with all known fixes applied.
3
3
 
4
- const HEALIX_SERVICE_VERSION = '^0.2.0';
4
+ const HEALIX_SERVICE_VERSION = '^0.3.0';
5
5
 
6
6
  export function tsconfigJson() {
7
7
  return JSON.stringify(
@@ -55,6 +55,7 @@ export function packageJson(projectName) {
55
55
  '@wdio/local-runner': '^9',
56
56
  '@wdio/mocha-framework': '^9',
57
57
  '@wdio/spec-reporter': '^9',
58
+ '@wdio/allure-reporter': '^9',
58
59
  'cross-env': '^7',
59
60
  'ts-node': '^10',
60
61
  typescript: '^5',
@@ -77,10 +78,14 @@ export function wdioWebConf(apiPort = 4000) {
77
78
  framework: 'mocha',
78
79
  mochaOpts: {
79
80
  ui: 'bdd',
80
- timeout: 300000,
81
+ // Interactive/agent sessions are held open inside ONE long-lived mocha test; WDIO
82
+ // (@wdio/utils) would otherwise kill it at this timeout (default 5 min) and force a
83
+ // mid-run restart. Lift it for agent mode with a large FINITE value — NEVER 0, which
84
+ // in WDIO yields a negative (instant-firing) timeout, the opposite of mocha's "disable".
85
+ timeout: (process.env.HEALIX_AGENT === '1' || process.env.HEALIX_INTERACTIVE === 'true') ? 86400000 : 300000,
81
86
  },
82
87
 
83
- reporters: ['spec'],
88
+ reporters: ['spec', ['allure', { outputDir: 'allure-results' }]],
84
89
 
85
90
  suites: {
86
91
  smoke: ['./test/specs/smoke/**/*.spec.ts'],
@@ -116,7 +121,7 @@ export function wdioWebConf(apiPort = 4000) {
116
121
  },
117
122
  }]],
118
123
 
119
- waitforTimeout: 10000,
124
+ waitforTimeout: 5000,
120
125
  connectionRetryTimeout: 120000,
121
126
  connectionRetryCount: 3,
122
127
 
@@ -149,11 +154,66 @@ const REHYDRATE_REMINDER =
149
154
  'the scaffold tools. If the Healix guide is not currently in your context, run ' +
150
155
  'healix_guide before any scaffold/heal/run step — do not operate from memory of it.';
151
156
 
152
- // Tiny script the hook runs. Living in a FILE (not an inline `node -e`) avoids
153
- // cross-shell quoting hell — the hook command is just `node <path>`.
157
+ // The ONE script every host runs to re-anchor the agent. Living in a FILE (not an
158
+ // inline `node -e`) avoids cross-shell quoting hell — the hook command is just
159
+ // `node <path>`. It is host-agnostic: the SAME file is referenced by both
160
+ // • .claude/settings.json (Spryv + Claude Code SessionStart) → wants PLAIN TEXT stdout
161
+ // • .cursor/hooks.json (Cursor sessionStart) → wants JSON {additional_context}
162
+ // We detect Cursor from its stdin payload (it sends composer_mode / is_background_agent)
163
+ // and emit JSON for it; everyone else gets plain text, which both Spryv (captures hook
164
+ // stdout) and Claude Code (adds SessionStart stdout to context) inject verbatim. Cursor
165
+ // has no post-compaction inject event, so its sessionStart additional_context is seeded
166
+ // into the persistent system context — where it survives compaction.
154
167
  export function claudeRehydrateScript() {
155
- return `// Printed into the agent's context by the SessionStart hook (startup + after compaction).\n` +
156
- `console.log(${JSON.stringify(REHYDRATE_REMINDER)});\n`;
168
+ return `// Re-anchors the agent on the Healix workflow at session start and after a context
169
+ // compaction. Host-agnostic — referenced by .claude/settings.json (Spryv + Claude Code)
170
+ // AND .cursor/hooks.json (Cursor). Cursor wants JSON; the others want plain text.
171
+ const REMINDER = ${JSON.stringify(REHYDRATE_REMINDER)};
172
+
173
+ let buf = '';
174
+ let settled = false;
175
+ function settle() {
176
+ if (settled) return;
177
+ settled = true;
178
+ let host = null;
179
+ try { host = buf.trim() ? JSON.parse(buf) : null; } catch { host = null; }
180
+ // composer_mode / is_background_agent are Cursor-only stdin keys.
181
+ const isCursor = !!host && ('composer_mode' in host || 'is_background_agent' in host);
182
+ if (isCursor) process.stdout.write(JSON.stringify({ additional_context: REMINDER }));
183
+ else process.stdout.write(REMINDER + '\\n');
184
+ }
185
+ try {
186
+ process.stdin.setEncoding('utf8');
187
+ process.stdin.on('data', (c) => { buf += c; });
188
+ process.stdin.on('end', settle);
189
+ process.stdin.on('error', settle);
190
+ } catch { /* no stdin — the timer below settles it */ }
191
+ // Hosts that ignore stdin (Spryv pipes /dev/null) may not emit 'end' reliably; a short
192
+ // unref'd timer guarantees we print exactly once without keeping the process alive.
193
+ const t = setTimeout(settle, 60);
194
+ if (t && t.unref) t.unref();
195
+ `;
196
+ }
197
+
198
+ // Cursor 1.7+ hook config — Cursor reads .cursor/hooks.json (NOT .claude/settings.json)
199
+ // and its hooks speak a stdin-JSON → stdout-JSON contract. `sessionStart` returns
200
+ // { additional_context } which Cursor injects into the agent's system context. (Cursor's
201
+ // only compaction event, `preCompact`, is observational and cannot inject — so seeding at
202
+ // sessionStart, which persists through compaction, is the correct re-hydration vehicle.)
203
+ // The command runs the SAME shared script as the Claude/Spryv hook — one source of truth.
204
+ export function cursorHooks() {
205
+ return JSON.stringify(
206
+ {
207
+ version: 1,
208
+ hooks: {
209
+ sessionStart: [
210
+ { command: 'node .claude/healix-rehydrate.mjs', type: 'command' },
211
+ ],
212
+ },
213
+ },
214
+ null,
215
+ 2,
216
+ );
157
217
  }
158
218
 
159
219
  // Claude Code-format SessionStart hook (host-agnostic: read by any host that