@haus-tech/haus-workflow 6.2.0 → 6.4.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.
@@ -957,7 +957,7 @@ export function korAgent({ agent, modell, prompt, fil, maxTurer, ctx }) {
957
957
  let tidsgransMs = null
958
958
  let timer = null
959
959
  let samtidiga = null
960
- const avsluta = ({ exitCode, spawnfel = null }) => {
960
+ const avslutaAgent = ({ exitCode, spawnfel = null }) => {
961
961
  if (klar) return
962
962
  klar = true
963
963
  if (timer !== null) clearTimeout(timer)
@@ -991,9 +991,9 @@ export function korAgent({ agent, modell, prompt, fil, maxTurer, ctx }) {
991
991
  .then(
992
992
  (r) => {
993
993
  stdout = String(r?.stdout ?? '')
994
- avsluta({ exitCode: r?.exitCode ?? 0 })
994
+ avslutaAgent({ exitCode: r?.exitCode ?? 0 })
995
995
  },
996
- (e) => avsluta({ exitCode: null, spawnfel: e?.message ?? String(e) }),
996
+ (e) => avslutaAgent({ exitCode: null, spawnfel: e?.message ?? String(e) }),
997
997
  )
998
998
  return
999
999
  }
@@ -1004,7 +1004,7 @@ export function korAgent({ agent, modell, prompt, fil, maxTurer, ctx }) {
1004
1004
  stdio: ['pipe', 'pipe', 'pipe'],
1005
1005
  })
1006
1006
  } catch (e) {
1007
- avsluta({ exitCode: null, spawnfel: e.message })
1007
+ avslutaAgent({ exitCode: null, spawnfel: e.message })
1008
1008
  return
1009
1009
  }
1010
1010
  ctx.barn.add(barn)
@@ -1024,11 +1024,11 @@ export function korAgent({ agent, modell, prompt, fil, maxTurer, ctx }) {
1024
1024
  })
1025
1025
  barn.on('error', (e) => {
1026
1026
  ctx.barn.delete(barn)
1027
- avsluta({ exitCode: null, spawnfel: e.message })
1027
+ avslutaAgent({ exitCode: null, spawnfel: e.message })
1028
1028
  })
1029
1029
  barn.on('close', (code) => {
1030
1030
  ctx.barn.delete(barn)
1031
- avsluta({ exitCode: code })
1031
+ avslutaAgent({ exitCode: code })
1032
1032
  })
1033
1033
  barn.stdin.on('error', () => {})
1034
1034
  barn.stdin.end(prompt)
@@ -1415,6 +1415,9 @@ const forstaRubrikI = (text) => {
1415
1415
 
1416
1416
  const INTERVJU_RE = /^INTERVJU KRÄVS:\s*(.*)$/u
1417
1417
 
1418
+ /** Svarets första rad läst som `INTERVJU KRÄVS: <skäl>`, matchen eller null. */
1419
+ const lasIntervju = (svar) => INTERVJU_RE.exec(svar.trim().split('\n')[0].trim())
1420
+
1418
1421
  /** Designern (ADR-0156 beslut 1 och 6): ett agentsteg som skriver ett utkast
1419
1422
  * eller svarar `INTERVJU KRÄVS:`. Returnerar `{ status: null }` när ett
1420
1423
  * utkast bokfördes (eller steget inte gäller), `stopp` på INTERVJU KRÄVS
@@ -1449,8 +1452,28 @@ export async function korDesigner(del, ctx) {
1449
1452
  ctx,
1450
1453
  })
1451
1454
  if (!res.ok) return { status: 'ofärdig', orsak: `design: ${res.orsak}` }
1452
- const svar = utanYttreStaket(res.text)
1453
- const intervju = INTERVJU_RE.exec(svar.trim().split('\n')[0].trim())
1455
+ let svar = utanYttreStaket(res.text)
1456
+ let intervju = lasIntervju(svar)
1457
+ // #746: ett INTERVJU KRÄVS utan skäl är ett scriptbevisat brott (stopp
1458
+ // vägrar ett tomt --orsak). Det prövas före varje skrivning: EN
1459
+ // omdispatch (ADR-0092), sedan ofärdig, och inget kvitto i postens katalog.
1460
+ if (ctx.post && intervju && intervju[1].trim() === '') {
1461
+ const brist = '- INTERVJU KRÄVS: saknar skäl; skriv skälet efter kolonet på samma rad'
1462
+ logg(`BROTT ${delNamn(ctx, del.dir)} designer: INTERVJU KRÄVS utan skäl, omdispatch`)
1463
+ const om = await korAgent({
1464
+ agent: 'request-designer',
1465
+ modell: ctx.modell,
1466
+ prompt: omdispatchPrompt(prompt, [brist]),
1467
+ fil: `steg/1${del.nr}-designer-omdispatch.json`,
1468
+ maxTurer: MAX_TURER.designer,
1469
+ ctx,
1470
+ })
1471
+ if (!om.ok) return { status: 'ofärdig', orsak: `design: ${om.orsak}` }
1472
+ svar = utanYttreStaket(om.text)
1473
+ intervju = lasIntervju(svar)
1474
+ if (intervju && intervju[1].trim() === '')
1475
+ return { status: 'ofärdig', orsak: 'design: INTERVJU KRÄVS utan skäl efter omdispatchen' }
1476
+ }
1454
1477
  if (intervju) {
1455
1478
  const orsak = `intervju krävs: ${intervju[1].trim()}`
1456
1479
  logg(`STOPP ${delNamn(ctx, del.dir)} ${orsak}`)
@@ -2106,12 +2129,20 @@ function arkiveraDubbel(del, n, k1, k2, ctx) {
2106
2129
  const diff = jam.rader.map((r) => r.replace(/^- /, ''))
2107
2130
  skrivPris(del, huvud, vinnaren)
2108
2131
  skrivPris(del, run2, andra)
2109
- addRun(del.dir, scorerRad(vinnaren, huvud, { doubleRun, note: scorerNote(vinnaren) }), ctx)
2132
+ // #754: på en OLIKA står diffraderna på körning 1:s runs-rad, som i det
2133
+ // manuella flödet (SKILL.md bedöm steg 6). Arkiv skrivna före #754 bär dem
2134
+ // på körning 2:s rad; detaljer <post> state visar båda.
2135
+ const olikaDiff = (k) => (jam.verdikt === 'OLIKA' && k === k1 ? diff : [])
2136
+ addRun(
2137
+ del.dir,
2138
+ scorerRad(vinnaren, huvud, { doubleRun, note: scorerNote(vinnaren, olikaDiff(vinnaren)) }),
2139
+ ctx,
2140
+ )
2110
2141
  addRun(
2111
2142
  del.dir,
2112
2143
  scorerRad(andra, run2, {
2113
2144
  doubleRun,
2114
- note: scorerNote(andra, jam.verdikt === 'OLIKA' ? diff : []),
2145
+ note: scorerNote(andra, olikaDiff(andra)),
2115
2146
  // En OLIKA har ingen gällande fil att peka på förrän domen är fälld.
2116
2147
  ...(jam.verdikt === 'OLIKA' ? {} : { supersededBy: huvud }),
2117
2148
  }),
@@ -2230,7 +2261,7 @@ function postStatus(ctx, korStatus) {
2230
2261
  * en dödad korning.mjs, och korning.json skrivs aldrig, så både pengarna och
2231
2262
  * mätningen försvinner tyst. Kör aldrig återstoden av flödet: en avbruten
2232
2263
  * körning ÄR ofärdig. */
2233
- function avbryt(ctx, allaSteg, nyckel, signal) {
2264
+ export function avbryt(ctx, allaSteg, nyckel, signal, { exit = process.exit } = {}) {
2234
2265
  const antal = ctx.barn.size
2235
2266
  for (const barn of ctx.barn) barn.kill('SIGTERM')
2236
2267
  skrivKorningsjson(ctx, allaSteg)
@@ -2253,7 +2284,9 @@ function avbryt(ctx, allaSteg, nyckel, signal) {
2253
2284
  if (r.status !== 0) logg(`VARNING experiment --status ofärdig: ${felText(r)}`)
2254
2285
  }
2255
2286
  logg(`AVBRUTEN ${signal}: ${antal} barnprocess(er) dödade, korning.json skriven`)
2256
- process.exit(SIGNAL_EXIT)
2287
+ // #748: samma STATUS-rad som avslutet, så startaren läser mer än exitkoden.
2288
+ if (ctx.post) process.stdout.write('STATUS: ofärdig\n')
2289
+ exit(SIGNAL_EXIT)
2257
2290
  }
2258
2291
 
2259
2292
  /** Vaktens intervall: startaren prövas var 30:e sekund. */
@@ -623,17 +623,31 @@ export const avvikelseEtikett = (text) => {
623
623
  }
624
624
 
625
625
  /**
626
- * Fritexten efter etiketten på samma rad, utan inledande skiljetecken, eller
627
- * null när raden saknas eller bara bär etiketten. precheck skriver den som
628
- * `AVVIKELSE-TEXT:` så orkestratorn kan se en designkonflikt (texten börjar
629
- * med `design conflict:`, #596) utan att läsa filen själv.
626
+ * Fritexten efter etiketten, utan inledande skiljetecken eller streck
627
+ * (bindestreck, U+2012, U+2013, U+2014; #753), eller null när raden saknas
628
+ * eller ingen text följer. Bär etikettraden ingen text läses första
629
+ * icke-tomma raden efter den, utom när den raden är nästa fält (`**…:**`)
630
+ * eller en rubrik (#753). Etiketten själv läses lika strikt som förut.
631
+ * precheck skriver texten som `AVVIKELSE-TEXT:` så orkestratorn kan se en
632
+ * designkonflikt (texten börjar med `design conflict:`, #596) utan att läsa
633
+ * filen själv.
630
634
  */
631
635
  export const avvikelseText = (text) => {
632
636
  const rad = granskareRadRe('Avvikelse mot specen').source
633
- const m = new RegExp(`${rad}[ \\t]*\\p{L}+[ \\t.,;:!?]*(.*)$`, 'imu').exec(String(text))
637
+ const skilje = '[ \\t.,;:!?\\-\\u2012\\u2013\\u2014]*'
638
+ const str = String(text)
639
+ const m = new RegExp(`${rad}[ \\t]*\\p{L}+${skilje}(.*)$`, 'imu').exec(str)
634
640
  if (!m) return null
635
641
  const rest = m[1].trim()
636
- return rest === '' ? null : rest
642
+ if (rest !== '') return rest
643
+ const nasta = str
644
+ .slice(m.index + m[0].length)
645
+ .split(/\r?\n/)
646
+ .map((l) => l.trim())
647
+ .find((l) => l !== '')
648
+ if (nasta === undefined || /^\*\*[^*]+:\*\*|^#/u.test(nasta)) return null
649
+ const foljande = nasta.replace(new RegExp(`^${skilje}`, 'u'), '').trim()
650
+ return foljande === '' ? null : foljande
637
651
  }
638
652
 
639
653
  /**
@@ -504,11 +504,11 @@ export async function korVarv({ gid, modell, expDir, repoRot, konto, claude, pag
504
504
  )
505
505
  if (prov.status !== 0) varningar.push(`proveniens: ${felText(prov)}`)
506
506
 
507
- let usd = null
507
+ let belopp = null
508
508
  let modellfel = null
509
509
  try {
510
510
  const korning = JSON.parse(readFileSync(path.join(katalog, 'korning.json'), 'utf8'))
511
- if (typeof korning.total_cost_usd === 'number') usd = korning.total_cost_usd
511
+ if (typeof korning.total_cost_usd === 'number') belopp = korning.total_cost_usd
512
512
  if (exitCode === 0) {
513
513
  // korning.mjs har redan gjort verifieringen per steg (modellverifiering);
514
514
  // svepets egen verifieraModeller är reserven när det fältet saknas
@@ -543,9 +543,9 @@ export async function korVarv({ gid, modell, expDir, repoRot, konto, claude, pag
543
543
  }
544
544
  const orsak = avg.orsak ?? inre?.orsak ?? null
545
545
  ut(
546
- `KLAR ${gid} ${modell} ${avg.status} ${wall_ms}ms${usd !== null ? ` ${usd.toFixed(4)} USD` : ''}`,
546
+ `KLAR ${gid} ${modell} ${avg.status} ${wall_ms}ms${belopp !== null ? ` ${belopp.toFixed(4)} USD` : ''}`,
547
547
  )
548
- return { modell, status: avg.status, orsak, korning: nyckel, wall_ms, usd, varningar }
548
+ return { modell, status: avg.status, orsak, korning: nyckel, wall_ms, usd: belopp, varningar }
549
549
  }
550
550
 
551
551
  /** `väntar på dom` omvärderas: sparr exit 0 på alla löv betyder att domen är
@@ -0,0 +1,369 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/WeAreHausTech/haus-workflow/library/schemas/localdev.v2.schema.json",
4
+ "title": "haus localdev.yml (version 2)",
5
+ "description": "The CLI-parsed part of .haus-workflow/localdev.yml (ADR-0163). Only `version`, `instance` and the templates in `env` are validated by the CLI (src/workspace/env/manifest.ts, which is the enforcement); every other top-level key is agent-interpreted by the clone skill and left open here. Templates: {{slug}}, {{port.<name>}}, {{db.<name>}}, {{redis.<name>.db}}, {{search.<name>.prefix}}, {{site.<name>.url|host|root}}.",
6
+ "type": "object",
7
+ "properties": {
8
+ "version": {
9
+ "enum": [1, 2]
10
+ },
11
+ "instance": {
12
+ "type": "object",
13
+ "additionalProperties": false,
14
+ "description": "Per-worktree environment. A repo file may declare only ports and processes.",
15
+ "properties": {
16
+ "ports": {
17
+ "type": "object",
18
+ "propertyNames": {
19
+ "type": "string",
20
+ "pattern": "^[a-z0-9][a-z0-9_-]*$"
21
+ },
22
+ "additionalProperties": {
23
+ "type": "integer",
24
+ "minimum": 1,
25
+ "maximum": 65535
26
+ },
27
+ "maxProperties": 20,
28
+ "description": "name -> main-checkout port; a worktree gets lease base + declaration index"
29
+ },
30
+ "datastores": {
31
+ "type": "object",
32
+ "propertyNames": {
33
+ "type": "string",
34
+ "pattern": "^[a-z0-9][a-z0-9_-]*$"
35
+ },
36
+ "additionalProperties": {
37
+ "type": "object",
38
+ "additionalProperties": false,
39
+ "required": ["engine"],
40
+ "properties": {
41
+ "engine": {
42
+ "enum": [
43
+ "mariadb",
44
+ "mysql",
45
+ "postgres",
46
+ "redis",
47
+ "elasticsearch",
48
+ "opensearch",
49
+ "mssql"
50
+ ]
51
+ },
52
+ "compose": {
53
+ "type": "object",
54
+ "additionalProperties": false,
55
+ "required": ["project", "service"],
56
+ "properties": {
57
+ "project": {
58
+ "type": "string",
59
+ "minLength": 1
60
+ },
61
+ "service": {
62
+ "type": "string",
63
+ "minLength": 1
64
+ },
65
+ "file": {
66
+ "type": "string",
67
+ "minLength": 1,
68
+ "description": "Compose file, relative to the workspace root, passed as -f when env up has to create the engine (for a non-default name such as docker-compose.local.yml)."
69
+ }
70
+ }
71
+ },
72
+ "native": {
73
+ "type": "object",
74
+ "additionalProperties": false,
75
+ "required": ["host", "port"],
76
+ "description": "An engine on the host, outside Docker (mariadb, mysql, elasticsearch, opensearch). haus never starts it; SQL credentials come only from MYSQL_USER / MYSQL_PWD or ~/.my.cnf.",
77
+ "properties": {
78
+ "host": {
79
+ "type": "string",
80
+ "pattern": "^[A-Za-z0-9._:-]+$"
81
+ },
82
+ "port": {
83
+ "type": "integer",
84
+ "minimum": 1,
85
+ "maximum": 65535
86
+ },
87
+ "client": {
88
+ "type": "string",
89
+ "pattern": "^[A-Za-z0-9._/+-]+$",
90
+ "description": "mysql/mariadb client: a command on PATH or an absolute path (default: mysql or mariadb on PATH)"
91
+ }
92
+ }
93
+ },
94
+ "port": {
95
+ "type": "integer",
96
+ "minimum": 1,
97
+ "maximum": 65535,
98
+ "description": "The engine port. Optional with native: (defaults to native.port)."
99
+ },
100
+ "isolation": {
101
+ "enum": ["database", "db-index", "key-prefix", "prefix", "container"],
102
+ "description": "Default per engine: database (SQL, mssql included), db-index (redis), prefix (search). container has no driver yet."
103
+ },
104
+ "prefix": {
105
+ "type": "string",
106
+ "description": "The main checkout's index or key prefix (default empty)."
107
+ }
108
+ },
109
+ "oneOf": [
110
+ {
111
+ "required": ["compose", "port"]
112
+ },
113
+ {
114
+ "required": ["native"]
115
+ }
116
+ ]
117
+ }
118
+ },
119
+ "databases": {
120
+ "type": "object",
121
+ "propertyNames": {
122
+ "type": "string",
123
+ "pattern": "^[a-z0-9][a-z0-9_-]*$"
124
+ },
125
+ "additionalProperties": {
126
+ "type": "object",
127
+ "additionalProperties": false,
128
+ "required": ["on", "name"],
129
+ "properties": {
130
+ "on": {
131
+ "type": "string",
132
+ "description": "A datastore name"
133
+ },
134
+ "name": {
135
+ "type": "string",
136
+ "minLength": 1
137
+ },
138
+ "perWorktree": {
139
+ "enum": ["clone", "empty"],
140
+ "default": "clone"
141
+ }
142
+ }
143
+ }
144
+ },
145
+ "sites": {
146
+ "type": "object",
147
+ "propertyNames": {
148
+ "type": "string",
149
+ "pattern": "^[a-z0-9][a-z0-9_-]*$"
150
+ },
151
+ "additionalProperties": {
152
+ "type": "object",
153
+ "additionalProperties": false,
154
+ "required": ["kind", "root", "host"],
155
+ "properties": {
156
+ "kind": {
157
+ "const": "herd"
158
+ },
159
+ "mode": {
160
+ "enum": ["per-worktree", "shared-lock"],
161
+ "default": "per-worktree"
162
+ },
163
+ "root": {
164
+ "type": "string",
165
+ "minLength": 1,
166
+ "description": "Relative path inside the repo; no leading / and no .."
167
+ },
168
+ "host": {
169
+ "type": "string",
170
+ "minLength": 1,
171
+ "description": "May use templates, e.g. {{slug}}.shop.se"
172
+ },
173
+ "mainHost": {
174
+ "type": "string",
175
+ "description": "The main checkout's host (default: basename of root)"
176
+ },
177
+ "secure": {
178
+ "type": "boolean",
179
+ "default": false
180
+ },
181
+ "php": {
182
+ "type": ["string", "number"]
183
+ },
184
+ "copy": {
185
+ "enum": ["cow", "none"],
186
+ "description": "cow clones the main docroot; none uses the worktree folder as it is (the default, and required, for bedrock-env and laravel-env, whose root is a member repo)."
187
+ },
188
+ "share": {
189
+ "type": "array",
190
+ "items": {
191
+ "type": "string",
192
+ "minLength": 1
193
+ }
194
+ },
195
+ "plugins": {
196
+ "type": "object",
197
+ "additionalProperties": {
198
+ "type": "string",
199
+ "minLength": 1
200
+ }
201
+ },
202
+ "config": {
203
+ "type": "object",
204
+ "additionalProperties": false,
205
+ "required": ["kind"],
206
+ "properties": {
207
+ "kind": {
208
+ "enum": ["wp-config-override", "bedrock-env", "laravel-env"],
209
+ "description": "wp-config-override writes wp-config.haus-instance.php; bedrock-env and laravel-env write the defines into the site repo's .env (instance-owned keys)."
210
+ },
211
+ "defines": {
212
+ "type": "object",
213
+ "propertyNames": {
214
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
215
+ },
216
+ "additionalProperties": {
217
+ "type": "string"
218
+ }
219
+ }
220
+ }
221
+ },
222
+ "after": {
223
+ "type": "array",
224
+ "items": {
225
+ "type": "string",
226
+ "minLength": 1
227
+ }
228
+ }
229
+ }
230
+ }
231
+ },
232
+ "processes": {
233
+ "type": "object",
234
+ "propertyNames": {
235
+ "type": "string",
236
+ "pattern": "^[a-z0-9][a-z0-9_-]*$"
237
+ },
238
+ "additionalProperties": {
239
+ "type": "object",
240
+ "additionalProperties": false,
241
+ "required": ["start"],
242
+ "properties": {
243
+ "repo": {
244
+ "type": "string",
245
+ "description": "Required in the workspace file; defaults to the owning repo in a repo file. In a single-repo project (ADR-0167), the folder inside the repo the process runs in (`.` for the root)"
246
+ },
247
+ "start": {
248
+ "type": "string",
249
+ "minLength": 1
250
+ },
251
+ "port": {
252
+ "type": "string",
253
+ "description": "A name from instance.ports"
254
+ },
255
+ "health": {
256
+ "type": "string"
257
+ },
258
+ "healthTimeoutSec": {
259
+ "type": "number",
260
+ "exclusiveMinimum": 0,
261
+ "default": 120
262
+ },
263
+ "optional": {
264
+ "type": "boolean",
265
+ "default": false
266
+ }
267
+ }
268
+ }
269
+ },
270
+ "copyLocal": {
271
+ "type": "array",
272
+ "items": {
273
+ "type": "object",
274
+ "additionalProperties": false,
275
+ "required": ["repo", "path"],
276
+ "properties": {
277
+ "repo": {
278
+ "type": "string",
279
+ "minLength": 1
280
+ },
281
+ "path": {
282
+ "type": "string",
283
+ "minLength": 1,
284
+ "description": "Relative path inside the repo; no leading / and no .."
285
+ }
286
+ }
287
+ }
288
+ },
289
+ "teardown": {
290
+ "type": "array",
291
+ "items": {
292
+ "type": "string",
293
+ "minLength": 1
294
+ },
295
+ "description": "Shell commands (templated) env down --destroy and env gc --write run after stopping processes and before dropping databases; workspace file only, must be idempotent"
296
+ },
297
+ "after": {
298
+ "type": "array",
299
+ "items": {
300
+ "type": "string",
301
+ "minLength": 1
302
+ },
303
+ "description": "Shell commands (templated) env up runs once per environment in the worktree root, after the .env files and .haus-env.json are written (for example seeding a per-worktree bucket from the main one, ADR-0167); a failed one is retried by the next env up; workspace file only, must be idempotent"
304
+ }
305
+ }
306
+ },
307
+ "env": {
308
+ "type": "array",
309
+ "items": {
310
+ "type": "object",
311
+ "required": ["sinks"],
312
+ "properties": {
313
+ "value": {
314
+ "type": ["string", "number"]
315
+ },
316
+ "source": {
317
+ "type": "object",
318
+ "required": ["provides"],
319
+ "properties": {
320
+ "repo": {
321
+ "type": "string"
322
+ },
323
+ "provides": {
324
+ "type": "string"
325
+ }
326
+ }
327
+ },
328
+ "sinks": {
329
+ "type": "array",
330
+ "items": {
331
+ "type": "object",
332
+ "required": ["repo", "key"],
333
+ "properties": {
334
+ "repo": {
335
+ "type": "string",
336
+ "minLength": 1,
337
+ "description": "A member repo (id or folder) in a workspace; in a single-repo project (ADR-0167) a folder inside the repo in canonical form, `.` for the root or `apps/server` for a monorepo app, whose .env receives the key"
338
+ },
339
+ "key": {
340
+ "type": "string",
341
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
342
+ }
343
+ }
344
+ }
345
+ }
346
+ },
347
+ "anyOf": [
348
+ {
349
+ "required": ["value"]
350
+ },
351
+ {
352
+ "required": ["source"]
353
+ }
354
+ ]
355
+ }
356
+ }
357
+ },
358
+ "if": {
359
+ "required": ["instance"]
360
+ },
361
+ "then": {
362
+ "properties": {
363
+ "version": {
364
+ "const": 2
365
+ }
366
+ },
367
+ "required": ["version"]
368
+ }
369
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haus-tech/haus-workflow",
3
- "version": "6.2.0",
3
+ "version": "6.4.0",
4
4
  "description": "Haus AI workflow CLI for Claude Code.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,6 +16,7 @@
16
16
  "scripts/postinstall.mjs",
17
17
  "library/global",
18
18
  "library/catalog",
19
+ "library/schemas",
19
20
  "README.md",
20
21
  "CHANGELOG.md",
21
22
  "docs/before-you-start.md",
@@ -28,6 +29,7 @@
28
29
  "test:quick": "node scripts/test-fast.mjs --quick",
29
30
  "test:full": "node scripts/run-tests.mjs tests/**/*.test.js",
30
31
  "test:coverage": "c8 yarn test:full",
32
+ "test:integration": "HAUS_INTEGRATION=1 node scripts/run-tests.mjs tests/integration/*.int.test.js",
31
33
  "coverage:check": "c8 --check-coverage yarn test:full",
32
34
  "lint": "eslint src --cache --cache-location .eslintcache",
33
35
  "format:check": "prettier --check --cache --cache-location .prettiercache .",