@cleocode/adapters 2026.10.3 → 2026.10.5-rc.ci.672

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.
Files changed (48) hide show
  1. package/dist/heavy-command-hook.d.ts +12 -0
  2. package/dist/heavy-command-hook.d.ts.map +1 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +2095 -1269
  6. package/dist/index.js.map +3 -3
  7. package/dist/providers/claude-code/adapter.d.ts.map +1 -1
  8. package/dist/providers/claude-code/hooks.d.ts +43 -17
  9. package/dist/providers/claude-code/hooks.d.ts.map +1 -1
  10. package/dist/providers/claude-code/install.d.ts +42 -14
  11. package/dist/providers/claude-code/install.d.ts.map +1 -1
  12. package/dist/providers/claude-code/paths.d.ts +77 -0
  13. package/dist/providers/claude-code/paths.d.ts.map +1 -1
  14. package/dist/providers/codex/install.d.ts.map +1 -1
  15. package/dist/providers/cursor/install.d.ts.map +1 -1
  16. package/dist/providers/gemini-cli/install.d.ts.map +1 -1
  17. package/dist/providers/kimi/install.d.ts.map +1 -1
  18. package/dist/providers/openai-sdk/install.d.ts.map +1 -1
  19. package/dist/providers/opencode/install.d.ts.map +1 -1
  20. package/dist/providers/pi/install.d.ts.map +1 -1
  21. package/dist/providers/shared/heavy-command-hook-delivery.d.ts +97 -0
  22. package/dist/providers/shared/heavy-command-hook-delivery.d.ts.map +1 -0
  23. package/dist/providers/shared/heavy-command-hook-install.d.ts +153 -7
  24. package/dist/providers/shared/heavy-command-hook-install.d.ts.map +1 -1
  25. package/dist/providers/shared/project-instruction-file.d.ts +30 -0
  26. package/dist/providers/shared/project-instruction-file.d.ts.map +1 -0
  27. package/package.json +9 -5
  28. package/src/__tests__/claude-code-adapter.test.ts +11 -6
  29. package/src/heavy-command-hook.ts +21 -0
  30. package/src/index.ts +12 -0
  31. package/src/providers/claude-code/__tests__/hooks-install.test.ts +20 -17
  32. package/src/providers/claude-code/__tests__/no-user-global-write.test.ts +293 -0
  33. package/src/providers/claude-code/adapter.ts +3 -1
  34. package/src/providers/claude-code/hooks.ts +138 -51
  35. package/src/providers/claude-code/install.ts +123 -52
  36. package/src/providers/claude-code/paths.ts +159 -1
  37. package/src/providers/codex/install.ts +7 -6
  38. package/src/providers/cursor/install.ts +26 -12
  39. package/src/providers/gemini-cli/install.ts +4 -2
  40. package/src/providers/kimi/install.ts +4 -2
  41. package/src/providers/openai-sdk/install.ts +5 -2
  42. package/src/providers/opencode/install.ts +4 -2
  43. package/src/providers/pi/install.ts +4 -2
  44. package/src/providers/shared/__tests__/heavy-command-hook-delivery.test.ts +580 -0
  45. package/src/providers/shared/__tests__/heavy-command-hook-install.test.ts +20 -0
  46. package/src/providers/shared/heavy-command-hook-delivery.ts +620 -0
  47. package/src/providers/shared/heavy-command-hook-install.ts +401 -32
  48. package/src/providers/shared/project-instruction-file.ts +47 -0
package/dist/index.js CHANGED
@@ -1617,154 +1617,11 @@ var init_identity = __esm({
1617
1617
  }
1618
1618
  });
1619
1619
 
1620
- // packages/contracts/src/docs-taxonomy.ts
1621
- var BUILTIN_DOC_KINDS, BUILTIN_DOC_KIND_VALUES;
1622
- var init_docs_taxonomy = __esm({
1623
- "packages/contracts/src/docs-taxonomy.ts"() {
1620
+ // packages/contracts/src/operations/docs-lifecycle.ts
1621
+ var DOCS_LIFECYCLE_STATUSES;
1622
+ var init_docs_lifecycle = __esm({
1623
+ "packages/contracts/src/operations/docs-lifecycle.ts"() {
1624
1624
  "use strict";
1625
- BUILTIN_DOC_KINDS = [
1626
- {
1627
- kind: "adr",
1628
- label: "ADR",
1629
- description: "Architectural decision record",
1630
- defaultOwnerKind: "task",
1631
- publishDir: "docs/adr",
1632
- requiresEntityId: true,
1633
- entityIdPattern: /^adr-\d{3,4}-[a-z0-9-]+$/,
1634
- requiredSections: ["Status", "Date", "Context", "Decision", "Consequences"]
1635
- },
1636
- {
1637
- kind: "spec",
1638
- label: "Spec",
1639
- description: "Technical specification",
1640
- defaultOwnerKind: "task",
1641
- publishDir: "docs/spec",
1642
- requiresEntityId: false,
1643
- requiredSections: ["Goal", "Non-Goals", "Requirements", "Out-of-Scope"]
1644
- },
1645
- {
1646
- kind: "research",
1647
- label: "Research",
1648
- description: "Investigation / research note",
1649
- defaultOwnerKind: "task",
1650
- publishDir: "docs/research",
1651
- requiresEntityId: false,
1652
- requiredSections: ["Question", "Findings", "Sources"]
1653
- },
1654
- {
1655
- kind: "handoff",
1656
- label: "Handoff",
1657
- description: "Session / agent handoff",
1658
- defaultOwnerKind: "session",
1659
- publishDir: "docs/handoff",
1660
- requiresEntityId: false,
1661
- requiredSections: ["Context", "State", "Next-Steps"]
1662
- },
1663
- {
1664
- kind: "note",
1665
- label: "Note",
1666
- description: "Agent observation / informal note",
1667
- defaultOwnerKind: "observation",
1668
- publishDir: "docs/note",
1669
- requiresEntityId: false,
1670
- requiredSections: []
1671
- },
1672
- {
1673
- kind: "llm-readme",
1674
- label: "LLM README",
1675
- description: "Machine-readable README (llms.txt)",
1676
- defaultOwnerKind: "project",
1677
- publishDir: ".",
1678
- requiresEntityId: false,
1679
- requiredSections: []
1680
- },
1681
- {
1682
- kind: "changeset",
1683
- label: "Changeset",
1684
- description: "Atomic change entry (release-note input)",
1685
- defaultOwnerKind: "task",
1686
- publishDir: ".changeset",
1687
- requiresEntityId: true,
1688
- entityIdPattern: /^t\d+-[a-z0-9-]+$/,
1689
- requiredSections: []
1690
- },
1691
- {
1692
- kind: "release-note",
1693
- label: "Release Note",
1694
- description: "Composed release notes",
1695
- defaultOwnerKind: "project",
1696
- publishDir: "docs/release",
1697
- requiresEntityId: true,
1698
- entityIdPattern: /^v\d{4}\.\d+\.\d+(-[a-z0-9-]+)?$/,
1699
- requiredSections: ["Changes", "Migration"]
1700
- },
1701
- {
1702
- kind: "plan",
1703
- label: "Plan",
1704
- description: "Epic / saga decomposition plan",
1705
- defaultOwnerKind: "task",
1706
- publishDir: "docs/plan",
1707
- requiresEntityId: false,
1708
- requiredSections: ["Goal", "Steps", "Owners"]
1709
- },
1710
- {
1711
- kind: "rcasd",
1712
- label: "RCASD",
1713
- description: "Root-cause analysis + scoped delivery",
1714
- defaultOwnerKind: "task",
1715
- publishDir: ".cleo/rcasd",
1716
- requiresEntityId: true,
1717
- entityIdPattern: /^t\d+(-.+)?$/,
1718
- requiredSections: ["Root-Cause", "Action", "Schedule", "Detection"]
1719
- }
1720
- ];
1721
- BUILTIN_DOC_KIND_VALUES = Object.freeze(
1722
- BUILTIN_DOC_KINDS.map((d) => d.kind)
1723
- );
1724
- }
1725
- });
1726
-
1727
- // packages/contracts/src/operations/docs.ts
1728
- import { z as z7 } from "zod";
1729
- var DOCS_PROJECTION_PROPOSAL_SCHEMA, DOCS_PROJECTION_RECEIPT_SCHEMA, DOCS_LIFECYCLE_STATUSES;
1730
- var init_docs = __esm({
1731
- "packages/contracts/src/operations/docs.ts"() {
1732
- "use strict";
1733
- init_docs_taxonomy();
1734
- DOCS_PROJECTION_PROPOSAL_SCHEMA = z7.object({
1735
- version: z7.literal(1),
1736
- operation: z7.literal("docs.projection"),
1737
- identity: z7.object({
1738
- projectId: z7.string().min(1),
1739
- projectRoot: z7.string().min(1),
1740
- actor: z7.string().min(1),
1741
- operation: z7.literal("docs.projection"),
1742
- idempotencyKey: z7.string().min(1)
1743
- }).strict(),
1744
- source: z7.object({
1745
- attachmentId: z7.string().min(1),
1746
- sha256: z7.string().regex(/^[a-f0-9]{64}$/),
1747
- ownerId: z7.string().min(1),
1748
- ownerType: z7.enum(["task", "session", "observation", "decision", "learning", "pattern"]),
1749
- label: z7.string().min(1)
1750
- }).strict(),
1751
- observation: z7.object({
1752
- kind: z7.literal("doc-attachment"),
1753
- attachmentId: z7.string().min(1),
1754
- ownerId: z7.string().min(1),
1755
- addedAt: z7.string().min(1),
1756
- slug: z7.string().optional(),
1757
- type: z7.string().optional()
1758
- }).strict()
1759
- }).strict();
1760
- DOCS_PROJECTION_RECEIPT_SCHEMA = z7.object({
1761
- version: z7.literal(1),
1762
- sourceHash: z7.string().regex(/^[a-f0-9]{64}$/),
1763
- graph: z7.enum(["completed", "disabled"]),
1764
- observationId: z7.string().min(1),
1765
- verifiedAt: z7.string().min(1),
1766
- actor: z7.string().min(1)
1767
- }).strict();
1768
1625
  DOCS_LIFECYCLE_STATUSES = [
1769
1626
  "draft",
1770
1627
  "proposed",
@@ -2914,7 +2771,7 @@ var requirementTaskParam, OPERATIONS;
2914
2771
  var init_operations_registry = __esm({
2915
2772
  "packages/contracts/src/dispatch/operations-registry.ts"() {
2916
2773
  "use strict";
2917
- init_docs();
2774
+ init_docs_lifecycle();
2918
2775
  init_output_contracts_data();
2919
2776
  init_tasks();
2920
2777
  requirementTaskParam = {
@@ -13382,7 +13239,7 @@ var init_operations_registry = __esm({
13382
13239
  });
13383
13240
 
13384
13241
  // packages/contracts/src/docs/provenance.ts
13385
- import { z as z8 } from "zod";
13242
+ import { z as z7 } from "zod";
13386
13243
  var PROVENANCE_NODE_KINDS, PROVENANCE_EDGE_RELATIONS, DOC_LIFECYCLE_STATUSES, provenanceNodeKindSchema, provenanceEdgeRelationSchema, docLifecycleStatusSchema, provenanceNodeBaseFields, provenanceDocNodeSchema, provenanceTaskNodeSchema, provenanceDecisionNodeSchema, provenanceSessionNodeSchema, provenanceMemoryNodeSchema, provenanceNodeSchema, provenanceEdgeSchema, docProvenanceResponseSchema;
13387
13244
  var init_provenance = __esm({
13388
13245
  "packages/contracts/src/docs/provenance.ts"() {
@@ -13409,108 +13266,215 @@ var init_provenance = __esm({
13409
13266
  "archived",
13410
13267
  "draft"
13411
13268
  ];
13412
- provenanceNodeKindSchema = z8.enum(PROVENANCE_NODE_KINDS);
13413
- provenanceEdgeRelationSchema = z8.enum(PROVENANCE_EDGE_RELATIONS);
13414
- docLifecycleStatusSchema = z8.enum(DOC_LIFECYCLE_STATUSES);
13269
+ provenanceNodeKindSchema = z7.enum(PROVENANCE_NODE_KINDS);
13270
+ provenanceEdgeRelationSchema = z7.enum(PROVENANCE_EDGE_RELATIONS);
13271
+ docLifecycleStatusSchema = z7.enum(DOC_LIFECYCLE_STATUSES);
13415
13272
  provenanceNodeBaseFields = {
13416
- id: z8.string().min(1),
13417
- title: z8.string().min(1),
13418
- metadata: z8.record(z8.string(), z8.unknown()).optional()
13273
+ id: z7.string().min(1),
13274
+ title: z7.string().min(1),
13275
+ metadata: z7.record(z7.string(), z7.unknown()).optional()
13419
13276
  };
13420
- provenanceDocNodeSchema = z8.object({
13277
+ provenanceDocNodeSchema = z7.object({
13421
13278
  ...provenanceNodeBaseFields,
13422
- kind: z8.literal("doc"),
13423
- slug: z8.string().min(1),
13424
- docKind: z8.string().min(1),
13279
+ kind: z7.literal("doc"),
13280
+ slug: z7.string().min(1),
13281
+ docKind: z7.string().min(1),
13425
13282
  lifecycleStatus: docLifecycleStatusSchema,
13426
- publishedAt: z8.string().min(1),
13427
- supersededAt: z8.string().min(1).optional(),
13428
- summary: z8.string().optional()
13283
+ publishedAt: z7.string().min(1),
13284
+ supersededAt: z7.string().min(1).optional(),
13285
+ summary: z7.string().optional()
13429
13286
  });
13430
- provenanceTaskNodeSchema = z8.object({
13287
+ provenanceTaskNodeSchema = z7.object({
13431
13288
  ...provenanceNodeBaseFields,
13432
- kind: z8.literal("task"),
13433
- taskType: z8.enum(["saga", "epic", "task", "subtask"]),
13434
- status: z8.enum(["pending", "in_progress", "done", "blocked", "cancelled", "archived"])
13289
+ kind: z7.literal("task"),
13290
+ taskType: z7.enum(["saga", "epic", "task", "subtask"]),
13291
+ status: z7.enum(["pending", "in_progress", "done", "blocked", "cancelled", "archived"])
13435
13292
  });
13436
- provenanceDecisionNodeSchema = z8.object({
13293
+ provenanceDecisionNodeSchema = z7.object({
13437
13294
  ...provenanceNodeBaseFields,
13438
- kind: z8.literal("decision"),
13439
- outcome: z8.enum(["proposed", "accepted", "rejected", "superseded"]),
13440
- decidedAt: z8.string().min(1)
13295
+ kind: z7.literal("decision"),
13296
+ outcome: z7.enum(["proposed", "accepted", "rejected", "superseded"]),
13297
+ decidedAt: z7.string().min(1)
13441
13298
  });
13442
- provenanceSessionNodeSchema = z8.object({
13299
+ provenanceSessionNodeSchema = z7.object({
13443
13300
  ...provenanceNodeBaseFields,
13444
- kind: z8.literal("session"),
13445
- startedAt: z8.string().min(1),
13446
- endedAt: z8.string().min(1).optional()
13301
+ kind: z7.literal("session"),
13302
+ startedAt: z7.string().min(1),
13303
+ endedAt: z7.string().min(1).optional()
13447
13304
  });
13448
- provenanceMemoryNodeSchema = z8.object({
13305
+ provenanceMemoryNodeSchema = z7.object({
13449
13306
  ...provenanceNodeBaseFields,
13450
- kind: z8.literal("memory"),
13451
- memoryType: z8.enum(["observation", "pattern", "decision", "diary"]),
13452
- recordedAt: z8.string().min(1)
13307
+ kind: z7.literal("memory"),
13308
+ memoryType: z7.enum(["observation", "pattern", "decision", "diary"]),
13309
+ recordedAt: z7.string().min(1)
13453
13310
  });
13454
- provenanceNodeSchema = z8.discriminatedUnion("kind", [
13311
+ provenanceNodeSchema = z7.discriminatedUnion("kind", [
13455
13312
  provenanceDocNodeSchema,
13456
13313
  provenanceTaskNodeSchema,
13457
13314
  provenanceDecisionNodeSchema,
13458
13315
  provenanceSessionNodeSchema,
13459
13316
  provenanceMemoryNodeSchema
13460
13317
  ]);
13461
- provenanceEdgeSchema = z8.object({
13318
+ provenanceEdgeSchema = z7.object({
13462
13319
  relation: provenanceEdgeRelationSchema,
13463
- from: z8.string().min(1),
13320
+ from: z7.string().min(1),
13464
13321
  fromKind: provenanceNodeKindSchema,
13465
- to: z8.string().min(1),
13322
+ to: z7.string().min(1),
13466
13323
  toKind: provenanceNodeKindSchema,
13467
- addedAt: z8.string().min(1),
13468
- summary: z8.string().optional()
13324
+ addedAt: z7.string().min(1),
13325
+ summary: z7.string().optional()
13469
13326
  });
13470
- docProvenanceResponseSchema = z8.object({
13471
- nodes: z8.array(provenanceNodeSchema).readonly(),
13472
- edges: z8.array(provenanceEdgeSchema).readonly(),
13473
- totalNodes: z8.number().int().nonnegative(),
13474
- totalEdges: z8.number().int().nonnegative()
13327
+ docProvenanceResponseSchema = z7.object({
13328
+ nodes: z7.array(provenanceNodeSchema).readonly(),
13329
+ edges: z7.array(provenanceEdgeSchema).readonly(),
13330
+ totalNodes: z7.number().int().nonnegative(),
13331
+ totalEdges: z7.number().int().nonnegative()
13475
13332
  });
13476
13333
  }
13477
13334
  });
13478
13335
 
13479
13336
  // packages/contracts/src/docs/read.ts
13480
- import { z as z9 } from "zod";
13337
+ import { z as z8 } from "zod";
13481
13338
  var docFrontmatterSchema, docBodySchema, docReadResponseSchema;
13482
13339
  var init_read = __esm({
13483
13340
  "packages/contracts/src/docs/read.ts"() {
13484
13341
  "use strict";
13485
- docFrontmatterSchema = z9.object({
13486
- slug: z9.string(),
13487
- kind: z9.string().nullable(),
13488
- title: z9.string().nullable(),
13489
- summary: z9.string().nullable(),
13490
- lifecycleStatus: z9.string(),
13491
- docVersion: z9.number().int(),
13492
- ownerVersion: z9.string().nullable(),
13493
- supersedes: z9.string().nullable(),
13494
- supersededBy: z9.string().nullable(),
13495
- topics: z9.array(z9.string()).readonly(),
13496
- relatedTasks: z9.array(z9.string()).readonly(),
13497
- sha256: z9.string(),
13498
- createdAt: z9.string()
13499
- });
13500
- docBodySchema = z9.object({
13501
- encoding: z9.enum(["utf-8", "base64"]),
13502
- text: z9.string().optional(),
13503
- base64: z9.string().optional(),
13504
- sizeBytes: z9.number().int().nonnegative(),
13505
- mimeType: z9.string().nullable()
13506
- });
13507
- docReadResponseSchema = z9.object({
13342
+ docFrontmatterSchema = z8.object({
13343
+ slug: z8.string(),
13344
+ kind: z8.string().nullable(),
13345
+ title: z8.string().nullable(),
13346
+ summary: z8.string().nullable(),
13347
+ lifecycleStatus: z8.string(),
13348
+ docVersion: z8.number().int(),
13349
+ ownerVersion: z8.string().nullable(),
13350
+ supersedes: z8.string().nullable(),
13351
+ supersededBy: z8.string().nullable(),
13352
+ topics: z8.array(z8.string()).readonly(),
13353
+ relatedTasks: z8.array(z8.string()).readonly(),
13354
+ sha256: z8.string(),
13355
+ createdAt: z8.string()
13356
+ });
13357
+ docBodySchema = z8.object({
13358
+ encoding: z8.enum(["utf-8", "base64"]),
13359
+ text: z8.string().optional(),
13360
+ base64: z8.string().optional(),
13361
+ sizeBytes: z8.number().int().nonnegative(),
13362
+ mimeType: z8.string().nullable()
13363
+ });
13364
+ docReadResponseSchema = z8.object({
13508
13365
  frontmatter: docFrontmatterSchema,
13509
13366
  body: docBodySchema
13510
13367
  });
13511
13368
  }
13512
13369
  });
13513
13370
 
13371
+ // packages/contracts/src/docs-taxonomy.ts
13372
+ var BUILTIN_DOC_KINDS, BUILTIN_DOC_KIND_VALUES;
13373
+ var init_docs_taxonomy = __esm({
13374
+ "packages/contracts/src/docs-taxonomy.ts"() {
13375
+ "use strict";
13376
+ BUILTIN_DOC_KINDS = [
13377
+ {
13378
+ kind: "adr",
13379
+ label: "ADR",
13380
+ description: "Architectural decision record",
13381
+ defaultOwnerKind: "task",
13382
+ publishDir: "docs/adr",
13383
+ requiresEntityId: true,
13384
+ entityIdPattern: /^adr-\d{3,4}-[a-z0-9-]+$/,
13385
+ requiredSections: ["Status", "Date", "Context", "Decision", "Consequences"]
13386
+ },
13387
+ {
13388
+ kind: "spec",
13389
+ label: "Spec",
13390
+ description: "Technical specification",
13391
+ defaultOwnerKind: "task",
13392
+ publishDir: "docs/spec",
13393
+ requiresEntityId: false,
13394
+ requiredSections: ["Goal", "Non-Goals", "Requirements", "Out-of-Scope"]
13395
+ },
13396
+ {
13397
+ kind: "research",
13398
+ label: "Research",
13399
+ description: "Investigation / research note",
13400
+ defaultOwnerKind: "task",
13401
+ publishDir: "docs/research",
13402
+ requiresEntityId: false,
13403
+ requiredSections: ["Question", "Findings", "Sources"]
13404
+ },
13405
+ {
13406
+ kind: "handoff",
13407
+ label: "Handoff",
13408
+ description: "Session / agent handoff",
13409
+ defaultOwnerKind: "session",
13410
+ publishDir: "docs/handoff",
13411
+ requiresEntityId: false,
13412
+ requiredSections: ["Context", "State", "Next-Steps"]
13413
+ },
13414
+ {
13415
+ kind: "note",
13416
+ label: "Note",
13417
+ description: "Agent observation / informal note",
13418
+ defaultOwnerKind: "observation",
13419
+ publishDir: "docs/note",
13420
+ requiresEntityId: false,
13421
+ requiredSections: []
13422
+ },
13423
+ {
13424
+ kind: "llm-readme",
13425
+ label: "LLM README",
13426
+ description: "Machine-readable README (llms.txt)",
13427
+ defaultOwnerKind: "project",
13428
+ publishDir: ".",
13429
+ requiresEntityId: false,
13430
+ requiredSections: []
13431
+ },
13432
+ {
13433
+ kind: "changeset",
13434
+ label: "Changeset",
13435
+ description: "Atomic change entry (release-note input)",
13436
+ defaultOwnerKind: "task",
13437
+ publishDir: ".changeset",
13438
+ requiresEntityId: true,
13439
+ entityIdPattern: /^t\d+-[a-z0-9-]+$/,
13440
+ requiredSections: []
13441
+ },
13442
+ {
13443
+ kind: "release-note",
13444
+ label: "Release Note",
13445
+ description: "Composed release notes",
13446
+ defaultOwnerKind: "project",
13447
+ publishDir: "docs/release",
13448
+ requiresEntityId: true,
13449
+ entityIdPattern: /^v\d{4}\.\d+\.\d+(-[a-z0-9-]+)?$/,
13450
+ requiredSections: ["Changes", "Migration"]
13451
+ },
13452
+ {
13453
+ kind: "plan",
13454
+ label: "Plan",
13455
+ description: "Epic / saga decomposition plan",
13456
+ defaultOwnerKind: "task",
13457
+ publishDir: "docs/plan",
13458
+ requiresEntityId: false,
13459
+ requiredSections: ["Goal", "Steps", "Owners"]
13460
+ },
13461
+ {
13462
+ kind: "rcasd",
13463
+ label: "RCASD",
13464
+ description: "Root-cause analysis + scoped delivery",
13465
+ defaultOwnerKind: "task",
13466
+ publishDir: ".cleo/rcasd",
13467
+ requiresEntityId: true,
13468
+ entityIdPattern: /^t\d+(-.+)?$/,
13469
+ requiredSections: ["Root-Cause", "Action", "Schedule", "Detection"]
13470
+ }
13471
+ ];
13472
+ BUILTIN_DOC_KIND_VALUES = Object.freeze(
13473
+ BUILTIN_DOC_KINDS.map((d) => d.kind)
13474
+ );
13475
+ }
13476
+ });
13477
+
13514
13478
  // packages/contracts/src/engine-result.ts
13515
13479
  var init_engine_result = __esm({
13516
13480
  "packages/contracts/src/engine-result.ts"() {
@@ -13564,7 +13528,7 @@ var init_errors = __esm({
13564
13528
  });
13565
13529
 
13566
13530
  // packages/contracts/src/evidence-atom-schema.ts
13567
- import { z as z10 } from "zod";
13531
+ import { z as z9 } from "zod";
13568
13532
  var EVIDENCE_ATOM_KINDS, PARSER_PREFIXES, KIND_BOUNDARY_REGEX, commitAtomSchema, filesAtomSchema, testRunAtomSchema, toolAtomSchema, urlAtomSchema, noteAtomSchema, decisionAtomSchema, prAtomSchema, ciAtomSchema, locDropAtomSchema, callsiteCoverageAtomSchema, AC_UUID_REGEX, AC_ALIAS_REGEX, SATISFIES_TASK_ID_REGEX, SATISFIES_VERSION_PIN_REGEX, satisfiesAtomSchema, EvidenceAtomSchema, GATE_EVIDENCE_REQUIREMENTS, ATOM_EXAMPLES;
13569
13533
  var init_evidence_atom_schema = __esm({
13570
13534
  "packages/contracts/src/evidence-atom-schema.ts"() {
@@ -13585,73 +13549,73 @@ var init_evidence_atom_schema = __esm({
13585
13549
  ];
13586
13550
  PARSER_PREFIXES = [...EVIDENCE_ATOM_KINDS, "state"];
13587
13551
  KIND_BOUNDARY_REGEX = new RegExp(`;(?=\\s*(?:${PARSER_PREFIXES.join("|")})\\s*:)`, "g");
13588
- commitAtomSchema = z10.object({
13589
- kind: z10.literal("commit"),
13590
- sha: z10.string().regex(/^[0-9a-f]{7,40}$/i, "commit sha must be 7-40 hex characters")
13591
- });
13592
- filesAtomSchema = z10.object({
13593
- kind: z10.literal("files"),
13594
- paths: z10.array(z10.string().min(1)).min(1, "files atom requires at least one path")
13595
- });
13596
- testRunAtomSchema = z10.object({
13597
- kind: z10.literal("test-run"),
13598
- path: z10.string().min(1, "test-run atom requires a non-empty path")
13599
- });
13600
- toolAtomSchema = z10.object({
13601
- kind: z10.literal("tool"),
13602
- tool: z10.string().min(1, "tool atom requires a non-empty tool name")
13603
- });
13604
- urlAtomSchema = z10.object({
13605
- kind: z10.literal("url"),
13606
- url: z10.string().min(1).regex(/^https?:\/\//, "url atom must start with http:// or https://")
13607
- });
13608
- noteAtomSchema = z10.object({
13609
- kind: z10.literal("note"),
13610
- note: z10.string().min(1, "note atom must be non-empty").max(512, "note atom is too long (max 512 chars)")
13611
- });
13612
- decisionAtomSchema = z10.object({
13613
- kind: z10.literal("decision"),
13614
- decisionId: z10.string().min(1, "decision atom requires a non-empty decision ID")
13615
- });
13616
- prAtomSchema = z10.object({
13617
- kind: z10.literal("pr"),
13618
- prNumber: z10.number().int().positive("pr atom requires a positive integer PR number"),
13619
- componentPrNumber: z10.number().int().positive().optional()
13620
- });
13621
- ciAtomSchema = z10.object({
13622
- kind: z10.literal("ci"),
13623
- prNumber: z10.number().int().positive("ci atom requires a positive integer PR number"),
13624
- componentPrNumber: z10.number().int().positive().optional()
13625
- });
13626
- locDropAtomSchema = z10.object({
13627
- kind: z10.literal("loc-drop"),
13628
- fromLines: z10.number().int().nonnegative("loc-drop fromLines must be \u2265 0"),
13629
- toLines: z10.number().int().nonnegative("loc-drop toLines must be \u2265 0")
13630
- });
13631
- callsiteCoverageAtomSchema = z10.object({
13632
- kind: z10.literal("callsite-coverage"),
13633
- symbolName: z10.string().min(1, "callsite-coverage atom requires a non-empty symbolName"),
13634
- relativeSourcePath: z10.string().min(1, "callsite-coverage atom requires a non-empty relativeSourcePath")
13552
+ commitAtomSchema = z9.object({
13553
+ kind: z9.literal("commit"),
13554
+ sha: z9.string().regex(/^[0-9a-f]{7,40}$/i, "commit sha must be 7-40 hex characters")
13555
+ });
13556
+ filesAtomSchema = z9.object({
13557
+ kind: z9.literal("files"),
13558
+ paths: z9.array(z9.string().min(1)).min(1, "files atom requires at least one path")
13559
+ });
13560
+ testRunAtomSchema = z9.object({
13561
+ kind: z9.literal("test-run"),
13562
+ path: z9.string().min(1, "test-run atom requires a non-empty path")
13563
+ });
13564
+ toolAtomSchema = z9.object({
13565
+ kind: z9.literal("tool"),
13566
+ tool: z9.string().min(1, "tool atom requires a non-empty tool name")
13567
+ });
13568
+ urlAtomSchema = z9.object({
13569
+ kind: z9.literal("url"),
13570
+ url: z9.string().min(1).regex(/^https?:\/\//, "url atom must start with http:// or https://")
13571
+ });
13572
+ noteAtomSchema = z9.object({
13573
+ kind: z9.literal("note"),
13574
+ note: z9.string().min(1, "note atom must be non-empty").max(512, "note atom is too long (max 512 chars)")
13575
+ });
13576
+ decisionAtomSchema = z9.object({
13577
+ kind: z9.literal("decision"),
13578
+ decisionId: z9.string().min(1, "decision atom requires a non-empty decision ID")
13579
+ });
13580
+ prAtomSchema = z9.object({
13581
+ kind: z9.literal("pr"),
13582
+ prNumber: z9.number().int().positive("pr atom requires a positive integer PR number"),
13583
+ componentPrNumber: z9.number().int().positive().optional()
13584
+ });
13585
+ ciAtomSchema = z9.object({
13586
+ kind: z9.literal("ci"),
13587
+ prNumber: z9.number().int().positive("ci atom requires a positive integer PR number"),
13588
+ componentPrNumber: z9.number().int().positive().optional()
13589
+ });
13590
+ locDropAtomSchema = z9.object({
13591
+ kind: z9.literal("loc-drop"),
13592
+ fromLines: z9.number().int().nonnegative("loc-drop fromLines must be \u2265 0"),
13593
+ toLines: z9.number().int().nonnegative("loc-drop toLines must be \u2265 0")
13594
+ });
13595
+ callsiteCoverageAtomSchema = z9.object({
13596
+ kind: z9.literal("callsite-coverage"),
13597
+ symbolName: z9.string().min(1, "callsite-coverage atom requires a non-empty symbolName"),
13598
+ relativeSourcePath: z9.string().min(1, "callsite-coverage atom requires a non-empty relativeSourcePath")
13635
13599
  });
13636
13600
  AC_UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[45][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
13637
13601
  AC_ALIAS_REGEX = /^AC[0-9]{1,4}$/;
13638
13602
  SATISFIES_TASK_ID_REGEX = /^T[0-9]{1,7}$/;
13639
13603
  SATISFIES_VERSION_PIN_REGEX = /^[0-9]{14}$/;
13640
- satisfiesAtomSchema = z10.object({
13641
- kind: z10.literal("satisfies"),
13604
+ satisfiesAtomSchema = z9.object({
13605
+ kind: z9.literal("satisfies"),
13642
13606
  /** Target task ID — `T<1-7 digits>` per ADR-079-r2 §2.1. */
13643
- targetTaskId: z10.string().regex(SATISFIES_TASK_ID_REGEX, "satisfies atom targetTaskId must match /^T[0-9]{1,7}$/"),
13607
+ targetTaskId: z9.string().regex(SATISFIES_TASK_ID_REGEX, "satisfies atom targetTaskId must match /^T[0-9]{1,7}$/"),
13644
13608
  /** Lowercase UUIDv4/v5 — populated for the canonical form; undefined for alias form. */
13645
- targetAcId: z10.string().regex(AC_UUID_REGEX, "satisfies atom targetAcId must be a lowercase UUIDv4/v5").optional(),
13609
+ targetAcId: z9.string().regex(AC_UUID_REGEX, "satisfies atom targetAcId must be a lowercase UUIDv4/v5").optional(),
13646
13610
  /** Positional alias `AC<1-4 digits>` — populated for alias form; undefined for UUID form. */
13647
- targetAcAlias: z10.string().regex(AC_ALIAS_REGEX, "satisfies atom targetAcAlias must match /^AC[0-9]{1,4}$/").optional(),
13611
+ targetAcAlias: z9.string().regex(AC_ALIAS_REGEX, "satisfies atom targetAcAlias must match /^AC[0-9]{1,4}$/").optional(),
13648
13612
  /** Optional `@<14-digit YYYYMMDDhhmmss>` pin captured at mint time. */
13649
- versionPin: z10.string().regex(
13613
+ versionPin: z9.string().regex(
13650
13614
  SATISFIES_VERSION_PIN_REGEX,
13651
13615
  "satisfies atom versionPin must be 14 digits (YYYYMMDDhhmmss)"
13652
13616
  ).optional()
13653
13617
  });
13654
- EvidenceAtomSchema = z10.discriminatedUnion("kind", [
13618
+ EvidenceAtomSchema = z9.discriminatedUnion("kind", [
13655
13619
  commitAtomSchema,
13656
13620
  filesAtomSchema,
13657
13621
  testRunAtomSchema,
@@ -13703,58 +13667,58 @@ var init_evidence_atom_schema = __esm({
13703
13667
  });
13704
13668
 
13705
13669
  // packages/contracts/src/evidence-record-schema.ts
13706
- import { z as z11 } from "zod";
13670
+ import { z as z10 } from "zod";
13707
13671
  var evidenceBaseSchema, implDiffRecordSchema, validateSpecCheckRecordSchema, testOutputRecordSchema, lintReportRecordSchema, commandOutputRecordSchema, evidenceRecordSchema;
13708
13672
  var init_evidence_record_schema = __esm({
13709
13673
  "packages/contracts/src/evidence-record-schema.ts"() {
13710
13674
  "use strict";
13711
- evidenceBaseSchema = z11.object({
13675
+ evidenceBaseSchema = z10.object({
13712
13676
  /** Identity string of the agent that produced this record. */
13713
- agentIdentity: z11.string().min(1),
13677
+ agentIdentity: z10.string().min(1),
13714
13678
  /** SHA-256 hex digest (64 chars) of the attached artifact. */
13715
- attachmentSha256: z11.string().length(64),
13679
+ attachmentSha256: z10.string().length(64),
13716
13680
  /** ISO 8601 timestamp at which the action ran. */
13717
- ranAt: z11.string().datetime(),
13681
+ ranAt: z10.string().datetime(),
13718
13682
  /** Wall-clock duration of the action in milliseconds. */
13719
- durationMs: z11.number().nonnegative()
13683
+ durationMs: z10.number().nonnegative()
13720
13684
  });
13721
13685
  implDiffRecordSchema = evidenceBaseSchema.extend({
13722
- kind: z11.literal("impl-diff"),
13723
- phase: z11.literal("implement"),
13724
- filesChanged: z11.array(z11.string().min(1)).min(1),
13725
- linesAdded: z11.number().int().nonnegative(),
13726
- linesRemoved: z11.number().int().nonnegative()
13686
+ kind: z10.literal("impl-diff"),
13687
+ phase: z10.literal("implement"),
13688
+ filesChanged: z10.array(z10.string().min(1)).min(1),
13689
+ linesAdded: z10.number().int().nonnegative(),
13690
+ linesRemoved: z10.number().int().nonnegative()
13727
13691
  });
13728
13692
  validateSpecCheckRecordSchema = evidenceBaseSchema.extend({
13729
- kind: z11.literal("validate-spec-check"),
13730
- phase: z11.literal("validate"),
13731
- reqIdsChecked: z11.array(z11.string().min(1)).min(1),
13732
- passed: z11.boolean(),
13733
- details: z11.string().min(1)
13693
+ kind: z10.literal("validate-spec-check"),
13694
+ phase: z10.literal("validate"),
13695
+ reqIdsChecked: z10.array(z10.string().min(1)).min(1),
13696
+ passed: z10.boolean(),
13697
+ details: z10.string().min(1)
13734
13698
  });
13735
13699
  testOutputRecordSchema = evidenceBaseSchema.extend({
13736
- kind: z11.literal("test-output"),
13737
- phase: z11.literal("test"),
13738
- command: z11.string().min(1),
13739
- exitCode: z11.number().int(),
13740
- testsPassed: z11.number().int().nonnegative(),
13741
- testsFailed: z11.number().int().nonnegative()
13700
+ kind: z10.literal("test-output"),
13701
+ phase: z10.literal("test"),
13702
+ command: z10.string().min(1),
13703
+ exitCode: z10.number().int(),
13704
+ testsPassed: z10.number().int().nonnegative(),
13705
+ testsFailed: z10.number().int().nonnegative()
13742
13706
  });
13743
13707
  lintReportRecordSchema = evidenceBaseSchema.extend({
13744
- kind: z11.literal("lint-report"),
13745
- phase: z11.enum(["implement", "test"]),
13746
- tool: z11.string().min(1),
13747
- passed: z11.boolean(),
13748
- warnings: z11.number().int().nonnegative(),
13749
- errors: z11.number().int().nonnegative()
13708
+ kind: z10.literal("lint-report"),
13709
+ phase: z10.enum(["implement", "test"]),
13710
+ tool: z10.string().min(1),
13711
+ passed: z10.boolean(),
13712
+ warnings: z10.number().int().nonnegative(),
13713
+ errors: z10.number().int().nonnegative()
13750
13714
  });
13751
13715
  commandOutputRecordSchema = evidenceBaseSchema.extend({
13752
- kind: z11.literal("command-output"),
13753
- phase: z11.enum(["implement", "validate", "test"]),
13754
- cmd: z11.string().min(1),
13755
- exitCode: z11.number().int()
13716
+ kind: z10.literal("command-output"),
13717
+ phase: z10.enum(["implement", "validate", "test"]),
13718
+ cmd: z10.string().min(1),
13719
+ exitCode: z10.number().int()
13756
13720
  });
13757
- evidenceRecordSchema = z11.discriminatedUnion("kind", [
13721
+ evidenceRecordSchema = z10.discriminatedUnion("kind", [
13758
13722
  implDiffRecordSchema,
13759
13723
  validateSpecCheckRecordSchema,
13760
13724
  testOutputRecordSchema,
@@ -14223,6 +14187,21 @@ function checkPending(task) {
14223
14187
  function rulePending(task) {
14224
14188
  return { task, reason: "the typed merge-rule registry does not define it yet (T12344 item)" };
14225
14189
  }
14190
+ function implemented(entry) {
14191
+ const { pending: _pending, ...rest } = entry;
14192
+ return { ...rest, runtimeGate: MERGE_ENGINE_GATE };
14193
+ }
14194
+ function implementedCheck(entry, functionName) {
14195
+ const { pending: _pending, ...rest } = entry;
14196
+ return {
14197
+ ...rest,
14198
+ check: {
14199
+ module: "packages/core/src/store/sync/apply/post-apply.ts",
14200
+ functionName,
14201
+ footprint: entry.check?.footprint ?? []
14202
+ }
14203
+ };
14204
+ }
14226
14205
  function trigger(id, tables, triggers, inventory, reason) {
14227
14206
  return { id, class: "trigger-covered", tables, sites: [], triggers, inventory, reason };
14228
14207
  }
@@ -14250,10 +14229,14 @@ function rule(id, task, table, columns, inventory, reason, tables = [table]) {
14250
14229
  reason
14251
14230
  };
14252
14231
  }
14253
- var SYNC_WRITE_INVARIANTS;
14232
+ var MERGE_ENGINE_GATE, SYNC_WRITE_INVARIANTS;
14254
14233
  var init_sync_write_invariants = __esm({
14255
14234
  "packages/contracts/src/invariants/sync-write-invariants.ts"() {
14256
14235
  "use strict";
14236
+ MERGE_ENGINE_GATE = {
14237
+ module: "packages/core/src/store/sync/merge/engine.ts",
14238
+ functionName: "applyOp"
14239
+ };
14257
14240
  SYNC_WRITE_INVARIANTS = Object.freeze([
14258
14241
  // §3.6.2: RAISE guards and UNIQUE indexes a migration creates on synced tables.
14259
14242
  trigger(
@@ -14334,13 +14317,16 @@ var init_sync_write_invariants = __esm({
14334
14317
  "at most one active account per provider (partial UNIQUE)"
14335
14318
  ),
14336
14319
  // §3.6.5: post-apply check families (T12344 items).
14337
- pac(
14338
- "task.tree.shape",
14339
- "PAC-01",
14340
- "T12922",
14341
- ["tasks_tasks"],
14342
- ["rows with a changed parent_id or type", "their direct children and ancestor chain"],
14343
- "saga\u2192epic\u2192task\u2192subtask matrix, non-saga has a parent, depth \u2264 3, no self-parent"
14320
+ implementedCheck(
14321
+ pac(
14322
+ "task.tree.shape",
14323
+ "PAC-01",
14324
+ "T12922",
14325
+ ["tasks_tasks"],
14326
+ ["rows with a changed parent_id or type", "their direct children and ancestor chain"],
14327
+ "saga\u2192epic\u2192task\u2192subtask matrix, non-saga has a parent, depth \u2264 3, no self-parent"
14328
+ ),
14329
+ "checkTaskTreeShape"
14344
14330
  ),
14345
14331
  pac(
14346
14332
  "task.terminal-parent.live-children",
@@ -14350,13 +14336,12 @@ var init_sync_write_invariants = __esm({
14350
14336
  ["rows whose status, stage or parent changed", "their parent and siblings"],
14351
14337
  "no terminal parent with a live child; epic stage \u2265 children; rollup re-evaluated"
14352
14338
  ),
14353
- pac(
14339
+ trigger(
14354
14340
  "task.dependency.graph",
14355
- "PAC-03",
14356
- "T12924",
14357
14341
  ["tasks_task_dependencies", "tasks_tasks"],
14358
- ["dependency edges inserted in the page", "reachability from each depends_on"],
14359
- "dependencies are acyclic, with no self-edge"
14342
+ ["tasks_task_dependencies_cycle_guard_insert", "tasks_task_dependencies_cycle_guard_update"],
14343
+ ["PAC-03"],
14344
+ "dependencies are acyclic, with no self-edge: enforced by the T12886 cycle guards on apply (a guard conflict)"
14360
14345
  ),
14361
14346
  pac(
14362
14347
  "task.done.evidence",
@@ -14446,38 +14431,47 @@ var init_sync_write_invariants = __esm({
14446
14431
  ["the owning rows"],
14447
14432
  "derived counters and cross-column facts agree with their rows"
14448
14433
  ),
14449
- pac(
14450
- "apply.preconditions",
14451
- "PAC-15",
14452
- "T12936",
14453
- ["tasks_tasks"],
14454
- ["the whole page"],
14455
- "no apply while the local twin collapse has failed; restore is a re-baseline"
14434
+ implementedCheck(
14435
+ pac(
14436
+ "apply.preconditions",
14437
+ "PAC-15",
14438
+ "T12936",
14439
+ ["tasks_tasks"],
14440
+ ["the whole page"],
14441
+ "no apply while the local twin collapse has failed; restore is a re-baseline"
14442
+ ),
14443
+ "checkApplyPreconditions"
14456
14444
  ),
14457
14445
  // §3.6.6: typed merge rules (T12344).
14458
- rule(
14459
- "task.status.absorbing",
14460
- "T12937",
14461
- "tasks_tasks",
14462
- ["status", "completed_at", "cancelled_at", "cancellation_reason", "pipeline_stage"],
14463
- ["V18", "V20", "C22", "P40", "P46", "P47"],
14464
- "done, cancelled and archived are absorbing; leaving them needs an explicit op"
14446
+ implemented(
14447
+ rule(
14448
+ "task.status.absorbing",
14449
+ "T12937",
14450
+ "tasks_tasks",
14451
+ ["status", "completed_at", "cancelled_at", "cancellation_reason", "pipeline_stage"],
14452
+ ["V18", "V20", "C22", "P40", "P46", "P47"],
14453
+ "done, cancelled and archived are absorbing; leaving them needs an explicit op"
14454
+ )
14465
14455
  ),
14466
- rule(
14467
- "task.pipeline-stage.max",
14468
- "T12938",
14469
- "tasks_tasks",
14470
- ["pipeline_stage"],
14471
- ["V21", "C20", "P01", "F3"],
14472
- "pipeline_stage is the maximum by STAGE_ORDER, except an explicit restore"
14456
+ implemented(
14457
+ rule(
14458
+ "task.pipeline-stage.max",
14459
+ "T12938",
14460
+ "tasks_tasks",
14461
+ ["pipeline_stage"],
14462
+ ["V21", "C20", "P01", "F3"],
14463
+ "pipeline_stage is the maximum by STAGE_ORDER, except an explicit restore"
14464
+ )
14473
14465
  ),
14474
- rule(
14475
- "task.verification.frozen-on-done",
14476
- "T12939",
14477
- "tasks_tasks",
14478
- ["verification_json"],
14479
- ["C01", "C02", "C03", "C17"],
14480
- "verification is frozen once done; otherwise merged per gate"
14466
+ implemented(
14467
+ rule(
14468
+ "task.verification.frozen-on-done",
14469
+ "T12939",
14470
+ "tasks_tasks",
14471
+ ["verification_json"],
14472
+ ["C01", "C02", "C03", "C17"],
14473
+ "verification is frozen once done; otherwise merged per gate"
14474
+ )
14481
14475
  ),
14482
14476
  rule(
14483
14477
  "session.status.terminal",
@@ -14613,111 +14607,111 @@ var init_lafs = __esm({
14613
14607
  });
14614
14608
 
14615
14609
  // packages/contracts/src/lease-ipc/messages.ts
14616
- import { z as z12 } from "zod";
14610
+ import { z as z11 } from "zod";
14617
14611
  var LeaseScopeSchema, LeaseLaneSchema, LeaseAcquireRequestSchema, LeaseReleaseRequestSchema, LeaseRenewRequestSchema, RateCheckRequestSchema, ToolGrantRequestSchema, QueuePriorityClassSchema, QueueAdmitRequestSchema, WorkerHeartbeatRequestSchema, ResourceAdmitRequestSchema, ResourceReleaseRequestSchema, LeaseIpcRequestSchema, LeaseGrantedResponseSchema, LeaseQueuedResponseSchema, LeaseDeniedResponseSchema, RateResultResponseSchema, ToolGrantedResponseSchema, LeaseRevokedResponseSchema, ChildKilledUnresponsiveResponseSchema, QueueAdmitDispositionSchema, QueueAdmitResultResponseSchema, HeartbeatAckResponseSchema, ResourceAdmitDispositionSchema, ResourceAdmitResultResponseSchema, ResourceReleaseResultResponseSchema, LeaseErrorResponseSchema, LeaseIpcResponseSchema, LeaseIpcRequestEnvelopeSchema, LeaseIpcResponseEnvelopeSchema, LeaseIpcEnvelopeSchema, LEASE_IPC_REQUEST_KINDS, LEASE_IPC_RESPONSE_KINDS, LEASE_IPC_MESSAGE_KINDS;
14618
14612
  var init_messages2 = __esm({
14619
14613
  "packages/contracts/src/lease-ipc/messages.ts"() {
14620
14614
  "use strict";
14621
- LeaseScopeSchema = z12.enum(["project", "global"]);
14622
- LeaseLaneSchema = z12.enum(["tasks", "brain", "bulk"]);
14623
- LeaseAcquireRequestSchema = z12.object({
14615
+ LeaseScopeSchema = z11.enum(["project", "global"]);
14616
+ LeaseLaneSchema = z11.enum(["tasks", "brain", "bulk"]);
14617
+ LeaseAcquireRequestSchema = z11.object({
14624
14618
  /** Tag discriminating this request variant. */
14625
- kind: z12.literal("lease_acquire"),
14619
+ kind: z11.literal("lease_acquire"),
14626
14620
  /** The cleo.db scope being arbitrated. */
14627
14621
  scope: LeaseScopeSchema,
14628
14622
  /** The write lane within the scope. */
14629
14623
  lane: LeaseLaneSchema,
14630
14624
  /** Process+lane holder identity. */
14631
- holder_id: z12.string().min(1),
14625
+ holder_id: z11.string().min(1),
14632
14626
  /** Advisory priority — lower acquires sooner. `0` = highest. */
14633
- priority: z12.number().int().min(0).max(255),
14627
+ priority: z11.number().int().min(0).max(255),
14634
14628
  /** Lease time-to-live in milliseconds. */
14635
- ttl_ms: z12.number().int().nonnegative(),
14629
+ ttl_ms: z11.number().int().nonnegative(),
14636
14630
  /** When true, a same-holder acquire re-enters (refcount++) rather than queuing. */
14637
- reentrant: z12.boolean()
14631
+ reentrant: z11.boolean()
14638
14632
  }).strict();
14639
- LeaseReleaseRequestSchema = z12.object({
14633
+ LeaseReleaseRequestSchema = z11.object({
14640
14634
  /** Tag discriminating this request variant. */
14641
- kind: z12.literal("lease_release"),
14635
+ kind: z11.literal("lease_release"),
14642
14636
  /** The cleo.db scope being arbitrated. */
14643
14637
  scope: LeaseScopeSchema,
14644
14638
  /** The write lane within the scope. */
14645
14639
  lane: LeaseLaneSchema,
14646
14640
  /** Process+lane holder identity. */
14647
- holder_id: z12.string().min(1),
14641
+ holder_id: z11.string().min(1),
14648
14642
  /** The epoch fence the holder acquired — a stale epoch no-ops. */
14649
- epoch: z12.number().int().nonnegative()
14643
+ epoch: z11.number().int().nonnegative()
14650
14644
  }).strict();
14651
- LeaseRenewRequestSchema = z12.object({
14645
+ LeaseRenewRequestSchema = z11.object({
14652
14646
  /** Tag discriminating this request variant. */
14653
- kind: z12.literal("lease_renew"),
14647
+ kind: z11.literal("lease_renew"),
14654
14648
  /** The cleo.db scope being arbitrated. */
14655
14649
  scope: LeaseScopeSchema,
14656
14650
  /** The write lane within the scope. */
14657
14651
  lane: LeaseLaneSchema,
14658
14652
  /** Process+lane holder identity. */
14659
- holder_id: z12.string().min(1),
14653
+ holder_id: z11.string().min(1),
14660
14654
  /** The epoch fence the holder acquired (epoch-guarded renew). */
14661
- epoch: z12.number().int().nonnegative()
14655
+ epoch: z11.number().int().nonnegative()
14662
14656
  }).strict();
14663
- RateCheckRequestSchema = z12.object({
14657
+ RateCheckRequestSchema = z11.object({
14664
14658
  /** Tag discriminating this request variant. */
14665
- kind: z12.literal("rate_check"),
14659
+ kind: z11.literal("rate_check"),
14666
14660
  /** The cleo.db scope being checked. */
14667
14661
  scope: LeaseScopeSchema,
14668
14662
  /** The write lane within the scope. */
14669
14663
  lane: LeaseLaneSchema,
14670
14664
  /** Estimated bytes the caller intends to write. */
14671
- est_bytes: z12.number().int().nonnegative()
14665
+ est_bytes: z11.number().int().nonnegative()
14672
14666
  }).strict();
14673
- ToolGrantRequestSchema = z12.object({
14667
+ ToolGrantRequestSchema = z11.object({
14674
14668
  /** Tag discriminating this request variant. */
14675
- kind: z12.literal("tool_grant"),
14669
+ kind: z11.literal("tool_grant"),
14676
14670
  /** The tool name being requested. */
14677
- tool: z12.string().min(1),
14671
+ tool: z11.string().min(1),
14678
14672
  /** The requesting holder identity. */
14679
- holder_id: z12.string().min(1)
14673
+ holder_id: z11.string().min(1)
14680
14674
  }).strict();
14681
- QueuePriorityClassSchema = z12.enum(["lead", "worker", "background"]);
14682
- QueueAdmitRequestSchema = z12.object({
14675
+ QueuePriorityClassSchema = z11.enum(["lead", "worker", "background"]);
14676
+ QueueAdmitRequestSchema = z11.object({
14683
14677
  /** Tag discriminating this request variant. */
14684
- kind: z12.literal("queue_admit"),
14678
+ kind: z11.literal("queue_admit"),
14685
14679
  /** The LLM provider id the call targets (rate budget is per-provider). */
14686
- provider: z12.string().min(1),
14680
+ provider: z11.string().min(1),
14687
14681
  /** The caller's priority class (lead > worker > background). */
14688
14682
  priority_class: QueuePriorityClassSchema,
14689
14683
  /** The caller's estimate of the request's token cost (debited on admit). */
14690
- est_tokens: z12.number().int().nonnegative(),
14684
+ est_tokens: z11.number().int().nonnegative(),
14691
14685
  /** The child the call belongs to (in-flight tracking — the watchdog seam). */
14692
- child_id: z12.string().min(1)
14686
+ child_id: z11.string().min(1)
14693
14687
  }).strict();
14694
- WorkerHeartbeatRequestSchema = z12.object({
14688
+ WorkerHeartbeatRequestSchema = z11.object({
14695
14689
  /** Tag discriminating this request variant. */
14696
- kind: z12.literal("worker_heartbeat"),
14690
+ kind: z11.literal("worker_heartbeat"),
14697
14691
  /** The logical id of the heartbeating child (matches the registry key). */
14698
- child_id: z12.string().min(1),
14692
+ child_id: z11.string().min(1),
14699
14693
  /** The worker's own view of whether it is currently inside an LLM call. */
14700
- in_flight_llm: z12.boolean()
14694
+ in_flight_llm: z11.boolean()
14701
14695
  }).strict();
14702
- ResourceAdmitRequestSchema = z12.object({
14696
+ ResourceAdmitRequestSchema = z11.object({
14703
14697
  /** Tag discriminating this request variant. */
14704
- kind: z12.literal("resource_admit"),
14698
+ kind: z11.literal("resource_admit"),
14705
14699
  /** The resource class (e.g. `"db-heavy"`, `"full-build"`, `"test-run"`). */
14706
- class: z12.string().min(1),
14700
+ class: z11.string().min(1),
14707
14701
  /** The caller holding the slot — process/worktree-unique; the release key. */
14708
- holder_id: z12.string().min(1),
14702
+ holder_id: z11.string().min(1),
14709
14703
  /** The client-computed budget: the max concurrent holders for this class. */
14710
- budget: z12.number().int().nonnegative()
14704
+ budget: z11.number().int().nonnegative()
14711
14705
  }).strict();
14712
- ResourceReleaseRequestSchema = z12.object({
14706
+ ResourceReleaseRequestSchema = z11.object({
14713
14707
  /** Tag discriminating this request variant. */
14714
- kind: z12.literal("resource_release"),
14708
+ kind: z11.literal("resource_release"),
14715
14709
  /** The resource class the slot belongs to. */
14716
- class: z12.string().min(1),
14710
+ class: z11.string().min(1),
14717
14711
  /** The holder that was admitted (matches the admit `holder_id`). */
14718
- holder_id: z12.string().min(1)
14712
+ holder_id: z11.string().min(1)
14719
14713
  }).strict();
14720
- LeaseIpcRequestSchema = z12.discriminatedUnion("kind", [
14714
+ LeaseIpcRequestSchema = z11.discriminatedUnion("kind", [
14721
14715
  LeaseAcquireRequestSchema,
14722
14716
  LeaseReleaseRequestSchema,
14723
14717
  LeaseRenewRequestSchema,
@@ -14728,133 +14722,133 @@ var init_messages2 = __esm({
14728
14722
  ResourceAdmitRequestSchema,
14729
14723
  ResourceReleaseRequestSchema
14730
14724
  ]);
14731
- LeaseGrantedResponseSchema = z12.object({
14725
+ LeaseGrantedResponseSchema = z11.object({
14732
14726
  /** Tag discriminating this response variant. */
14733
- kind: z12.literal("lease_granted"),
14727
+ kind: z11.literal("lease_granted"),
14734
14728
  /** The granted cleo.db scope. */
14735
14729
  scope: LeaseScopeSchema,
14736
14730
  /** The granted write lane. */
14737
14731
  lane: LeaseLaneSchema,
14738
14732
  /** The holder the lease was granted to. */
14739
- holder_id: z12.string().min(1),
14733
+ holder_id: z11.string().min(1),
14740
14734
  /** The monotonic epoch fence assigned to this grant. */
14741
- epoch: z12.number().int().nonnegative(),
14735
+ epoch: z11.number().int().nonnegative(),
14742
14736
  /** The lease TTL in milliseconds. */
14743
- ttl_ms: z12.number().int().nonnegative(),
14737
+ ttl_ms: z11.number().int().nonnegative(),
14744
14738
  /** Absolute expiry timestamp (epoch ms) for this grant. */
14745
- expires_at_ms: z12.number().int().nonnegative()
14739
+ expires_at_ms: z11.number().int().nonnegative()
14746
14740
  }).strict();
14747
- LeaseQueuedResponseSchema = z12.object({
14741
+ LeaseQueuedResponseSchema = z11.object({
14748
14742
  /** Tag discriminating this response variant. */
14749
- kind: z12.literal("lease_queued"),
14743
+ kind: z11.literal("lease_queued"),
14750
14744
  /** The queued cleo.db scope. */
14751
14745
  scope: LeaseScopeSchema,
14752
14746
  /** The queued write lane. */
14753
14747
  lane: LeaseLaneSchema,
14754
14748
  /** The monotonic ticket assigned for FIFO tiebreak. */
14755
- ticket: z12.number().int().nonnegative(),
14749
+ ticket: z11.number().int().nonnegative(),
14756
14750
  /** Number of waiters ahead of this one. */
14757
- ahead: z12.number().int().nonnegative()
14751
+ ahead: z11.number().int().nonnegative()
14758
14752
  }).strict();
14759
- LeaseDeniedResponseSchema = z12.object({
14753
+ LeaseDeniedResponseSchema = z11.object({
14760
14754
  /** Tag discriminating this response variant. */
14761
- kind: z12.literal("lease_denied"),
14755
+ kind: z11.literal("lease_denied"),
14762
14756
  /** The denied cleo.db scope. */
14763
14757
  scope: LeaseScopeSchema,
14764
14758
  /** Machine-readable denial code (e.g. `E_LEASE_UNAVAILABLE`). */
14765
- code: z12.string().min(1),
14759
+ code: z11.string().min(1),
14766
14760
  /** Human-readable denial message. */
14767
- message: z12.string()
14761
+ message: z11.string()
14768
14762
  }).strict();
14769
- RateResultResponseSchema = z12.object({
14763
+ RateResultResponseSchema = z11.object({
14770
14764
  /** Tag discriminating this response variant. */
14771
- kind: z12.literal("rate_result"),
14765
+ kind: z11.literal("rate_result"),
14772
14766
  /** The checked cleo.db scope. */
14773
14767
  scope: LeaseScopeSchema,
14774
14768
  /** Whether the write is within budget. */
14775
- ok: z12.boolean(),
14769
+ ok: z11.boolean(),
14776
14770
  /** Suggested back-off in milliseconds when `ok` is false. */
14777
- retry_after_ms: z12.number().int().nonnegative(),
14771
+ retry_after_ms: z11.number().int().nonnegative(),
14778
14772
  /** Remaining token budget for the scope. */
14779
- tokens_remaining: z12.number().int().nonnegative()
14773
+ tokens_remaining: z11.number().int().nonnegative()
14780
14774
  }).strict();
14781
- ToolGrantedResponseSchema = z12.object({
14775
+ ToolGrantedResponseSchema = z11.object({
14782
14776
  /** Tag discriminating this response variant. */
14783
- kind: z12.literal("tool_granted"),
14777
+ kind: z11.literal("tool_granted"),
14784
14778
  /** The granted tool name. */
14785
- tool: z12.string().min(1),
14779
+ tool: z11.string().min(1),
14786
14780
  /** The holder the tool grant was issued to. */
14787
- holder_id: z12.string().min(1)
14781
+ holder_id: z11.string().min(1)
14788
14782
  }).strict();
14789
- LeaseRevokedResponseSchema = z12.object({
14783
+ LeaseRevokedResponseSchema = z11.object({
14790
14784
  /** Tag discriminating this response variant. */
14791
- kind: z12.literal("lease_revoked"),
14785
+ kind: z11.literal("lease_revoked"),
14792
14786
  /** The revoked cleo.db scope. */
14793
14787
  scope: LeaseScopeSchema,
14794
14788
  /** The revoked write lane. */
14795
14789
  lane: LeaseLaneSchema,
14796
14790
  /** The holder whose lease was revoked. */
14797
- holder_id: z12.string().min(1),
14791
+ holder_id: z11.string().min(1),
14798
14792
  /** Human-readable reason for the revocation. */
14799
- reason: z12.string()
14793
+ reason: z11.string()
14800
14794
  }).strict();
14801
- ChildKilledUnresponsiveResponseSchema = z12.object({
14795
+ ChildKilledUnresponsiveResponseSchema = z11.object({
14802
14796
  /** Tag discriminating this response variant. */
14803
- kind: z12.literal("child_killed_unresponsive"),
14797
+ kind: z11.literal("child_killed_unresponsive"),
14804
14798
  /** Logical id of the killed child. */
14805
- child_id: z12.string().min(1),
14799
+ child_id: z11.string().min(1),
14806
14800
  /** The holder identity the killed child held the lease as. */
14807
- holder_id: z12.string().min(1),
14801
+ holder_id: z11.string().min(1),
14808
14802
  /** The cleo.db scope the killed child held the lease in. */
14809
14803
  scope: LeaseScopeSchema,
14810
14804
  /** Human-readable reason for the kill. */
14811
- reason: z12.string()
14805
+ reason: z11.string()
14812
14806
  }).strict();
14813
- QueueAdmitDispositionSchema = z12.enum(["admitted", "deferred"]);
14814
- QueueAdmitResultResponseSchema = z12.object({
14807
+ QueueAdmitDispositionSchema = z11.enum(["admitted", "deferred"]);
14808
+ QueueAdmitResultResponseSchema = z11.object({
14815
14809
  /** Tag discriminating this response variant. */
14816
- kind: z12.literal("queue_admit_result"),
14810
+ kind: z11.literal("queue_admit_result"),
14817
14811
  /** Whether the LLM call was admitted or deferred. */
14818
14812
  disposition: QueueAdmitDispositionSchema,
14819
14813
  /** Back-off in ms before re-requesting (0 when admitted). */
14820
- retry_after_ms: z12.number().int().nonnegative(),
14814
+ retry_after_ms: z11.number().int().nonnegative(),
14821
14815
  /** Remaining provider token budget after this decision. */
14822
- tokens_remaining: z12.number().int().nonnegative(),
14816
+ tokens_remaining: z11.number().int().nonnegative(),
14823
14817
  /** Number of higher/equal-priority waiters ahead (0 when admitted). */
14824
- queue_position: z12.number().int().nonnegative()
14818
+ queue_position: z11.number().int().nonnegative()
14825
14819
  }).strict();
14826
- HeartbeatAckResponseSchema = z12.object({
14820
+ HeartbeatAckResponseSchema = z11.object({
14827
14821
  /** Tag discriminating this response variant. */
14828
- kind: z12.literal("heartbeat_ack")
14822
+ kind: z11.literal("heartbeat_ack")
14829
14823
  }).strict();
14830
- ResourceAdmitDispositionSchema = z12.enum(["admitted", "deferred"]);
14831
- ResourceAdmitResultResponseSchema = z12.object({
14824
+ ResourceAdmitDispositionSchema = z11.enum(["admitted", "deferred"]);
14825
+ ResourceAdmitResultResponseSchema = z11.object({
14832
14826
  /** Tag discriminating this response variant. */
14833
- kind: z12.literal("resource_admit_result"),
14827
+ kind: z11.literal("resource_admit_result"),
14834
14828
  /** Whether the heavy op was admitted or deferred. */
14835
14829
  disposition: ResourceAdmitDispositionSchema,
14836
14830
  /** Back-off in ms before re-requesting (0 when admitted). */
14837
- retry_after_ms: z12.number().int().nonnegative(),
14831
+ retry_after_ms: z11.number().int().nonnegative(),
14838
14832
  /** Remaining slots for the class after this decision. */
14839
- slots_remaining: z12.number().int().nonnegative()
14833
+ slots_remaining: z11.number().int().nonnegative()
14840
14834
  }).strict();
14841
- ResourceReleaseResultResponseSchema = z12.object({
14835
+ ResourceReleaseResultResponseSchema = z11.object({
14842
14836
  /** Tag discriminating this response variant. */
14843
- kind: z12.literal("resource_release_result"),
14837
+ kind: z11.literal("resource_release_result"),
14844
14838
  /** Whether a held slot was actually released (false = no such holder). */
14845
- released: z12.boolean(),
14839
+ released: z11.boolean(),
14846
14840
  /** Remaining in-flight holders for the class after the release. */
14847
- slots_remaining: z12.number().int().nonnegative()
14841
+ slots_remaining: z11.number().int().nonnegative()
14848
14842
  }).strict();
14849
- LeaseErrorResponseSchema = z12.object({
14843
+ LeaseErrorResponseSchema = z11.object({
14850
14844
  /** Tag discriminating this response variant. */
14851
- kind: z12.literal("error"),
14845
+ kind: z11.literal("error"),
14852
14846
  /** Machine-readable error code (e.g. `E_LEASE_BAD_VERSION`, `E_LEASE_UNIMPLEMENTED`). */
14853
- code: z12.string().min(1),
14847
+ code: z11.string().min(1),
14854
14848
  /** Human-readable error message. */
14855
- message: z12.string()
14849
+ message: z11.string()
14856
14850
  }).strict();
14857
- LeaseIpcResponseSchema = z12.discriminatedUnion("kind", [
14851
+ LeaseIpcResponseSchema = z11.discriminatedUnion("kind", [
14858
14852
  LeaseGrantedResponseSchema,
14859
14853
  LeaseQueuedResponseSchema,
14860
14854
  LeaseDeniedResponseSchema,
@@ -14868,27 +14862,27 @@ var init_messages2 = __esm({
14868
14862
  ResourceReleaseResultResponseSchema,
14869
14863
  LeaseErrorResponseSchema
14870
14864
  ]);
14871
- LeaseIpcRequestEnvelopeSchema = z12.object({
14865
+ LeaseIpcRequestEnvelopeSchema = z11.object({
14872
14866
  /** Parallel protocol version. */
14873
- protocol_version: z12.string(),
14867
+ protocol_version: z11.string(),
14874
14868
  /** Correlation id echoed back on the matching response. */
14875
- id: z12.string().min(1),
14869
+ id: z11.string().min(1),
14876
14870
  /** Direction discriminator. */
14877
- direction: z12.literal("request"),
14871
+ direction: z11.literal("request"),
14878
14872
  /** The request body. */
14879
14873
  request: LeaseIpcRequestSchema
14880
14874
  }).strict();
14881
- LeaseIpcResponseEnvelopeSchema = z12.object({
14875
+ LeaseIpcResponseEnvelopeSchema = z11.object({
14882
14876
  /** Parallel protocol version. */
14883
- protocol_version: z12.string(),
14877
+ protocol_version: z11.string(),
14884
14878
  /** Correlation id echoed from the originating request (or a fresh id for events). */
14885
- id: z12.string().min(1),
14879
+ id: z11.string().min(1),
14886
14880
  /** Direction discriminator. */
14887
- direction: z12.literal("response"),
14881
+ direction: z11.literal("response"),
14888
14882
  /** The response body. */
14889
14883
  response: LeaseIpcResponseSchema
14890
14884
  }).strict();
14891
- LeaseIpcEnvelopeSchema = z12.discriminatedUnion("direction", [
14885
+ LeaseIpcEnvelopeSchema = z11.discriminatedUnion("direction", [
14892
14886
  LeaseIpcRequestEnvelopeSchema,
14893
14887
  LeaseIpcResponseEnvelopeSchema
14894
14888
  ]);
@@ -14941,7 +14935,7 @@ var init_lease_ipc = __esm({
14941
14935
  });
14942
14936
 
14943
14937
  // packages/contracts/src/llm/catalog-schema.ts
14944
- import { z as z13 } from "zod";
14938
+ import { z as z12 } from "zod";
14945
14939
  var CATALOG_MODEL_STATUSES, CATALOG_AUTH_TYPES, isoDate, semver, catalogModalitiesSchema, catalogCostSchema, catalogLimitSchema, catalogModelProviderSchema, catalogModelEntrySchema, catalogProviderSchema, curatedCatalogSchema, modelsCatalogRowInsertSchema, modelsCatalogRowSelectSchema;
14946
14940
  var init_catalog_schema = __esm({
14947
14941
  "packages/contracts/src/llm/catalog-schema.ts"() {
@@ -14954,78 +14948,78 @@ var init_catalog_schema = __esm({
14954
14948
  "retired"
14955
14949
  ];
14956
14950
  CATALOG_AUTH_TYPES = ["api_key", "oauth", "bedrock", "vertex"];
14957
- isoDate = z13.string().regex(/^\d{4}-\d{2}-\d{2}$/, "must be an ISO date (YYYY-MM-DD)");
14958
- semver = z13.string().regex(/^\d+\.\d+\.\d+$/, "must be semver (MAJOR.MINOR.PATCH)");
14959
- catalogModalitiesSchema = z13.object({
14960
- input: z13.array(z13.string()),
14961
- output: z13.array(z13.string())
14951
+ isoDate = z12.string().regex(/^\d{4}-\d{2}-\d{2}$/, "must be an ISO date (YYYY-MM-DD)");
14952
+ semver = z12.string().regex(/^\d+\.\d+\.\d+$/, "must be semver (MAJOR.MINOR.PATCH)");
14953
+ catalogModalitiesSchema = z12.object({
14954
+ input: z12.array(z12.string()),
14955
+ output: z12.array(z12.string())
14962
14956
  }).strict();
14963
- catalogCostSchema = z13.object({
14964
- input: z13.number().optional(),
14965
- output: z13.number().optional(),
14966
- cache_read: z13.number().optional(),
14967
- cache_write: z13.number().optional(),
14968
- context_over_200k: z13.number().optional()
14957
+ catalogCostSchema = z12.object({
14958
+ input: z12.number().optional(),
14959
+ output: z12.number().optional(),
14960
+ cache_read: z12.number().optional(),
14961
+ cache_write: z12.number().optional(),
14962
+ context_over_200k: z12.number().optional()
14969
14963
  }).strict();
14970
- catalogLimitSchema = z13.object({
14971
- context: z13.number(),
14972
- output: z13.number()
14964
+ catalogLimitSchema = z12.object({
14965
+ context: z12.number(),
14966
+ output: z12.number()
14973
14967
  }).strict();
14974
- catalogModelProviderSchema = z13.object({
14975
- npm: z13.string(),
14976
- api: z13.string()
14968
+ catalogModelProviderSchema = z12.object({
14969
+ npm: z12.string(),
14970
+ api: z12.string()
14977
14971
  }).strict();
14978
- catalogModelEntrySchema = z13.object({
14979
- id: z13.string(),
14980
- name: z13.string(),
14981
- family: z13.string(),
14982
- attachment: z13.boolean(),
14983
- reasoning: z13.boolean(),
14984
- temperature: z13.boolean(),
14985
- interleaved: z13.boolean(),
14986
- tool_call: z13.boolean(),
14972
+ catalogModelEntrySchema = z12.object({
14973
+ id: z12.string(),
14974
+ name: z12.string(),
14975
+ family: z12.string(),
14976
+ attachment: z12.boolean(),
14977
+ reasoning: z12.boolean(),
14978
+ temperature: z12.boolean(),
14979
+ interleaved: z12.boolean(),
14980
+ tool_call: z12.boolean(),
14987
14981
  modalities: catalogModalitiesSchema,
14988
14982
  cost: catalogCostSchema,
14989
14983
  limit: catalogLimitSchema,
14990
- status: z13.enum(CATALOG_MODEL_STATUSES),
14984
+ status: z12.enum(CATALOG_MODEL_STATUSES),
14991
14985
  release_date: isoDate,
14992
14986
  provider: catalogModelProviderSchema
14993
14987
  }).strict();
14994
- catalogProviderSchema = z13.object({
14995
- id: z13.string(),
14996
- endpoint: z13.string(),
14997
- authTypes: z13.array(z13.enum(CATALOG_AUTH_TYPES)).min(1),
14998
- npm: z13.string().optional()
14988
+ catalogProviderSchema = z12.object({
14989
+ id: z12.string(),
14990
+ endpoint: z12.string(),
14991
+ authTypes: z12.array(z12.enum(CATALOG_AUTH_TYPES)).min(1),
14992
+ npm: z12.string().optional()
14999
14993
  }).strict();
15000
- curatedCatalogSchema = z13.object({
15001
- $schema: z13.string().optional(),
14994
+ curatedCatalogSchema = z12.object({
14995
+ $schema: z12.string().optional(),
15002
14996
  version: semver,
15003
14997
  lastUpdated: isoDate,
15004
- providers: z13.record(z13.string(), catalogProviderSchema),
15005
- models: z13.record(z13.string(), z13.record(z13.string(), catalogModelEntrySchema))
14998
+ providers: z12.record(z12.string(), catalogProviderSchema),
14999
+ models: z12.record(z12.string(), z12.record(z12.string(), catalogModelEntrySchema))
15006
15000
  }).strict();
15007
- modelsCatalogRowInsertSchema = z13.object({
15008
- id: z13.string(),
15009
- providerId: z13.string(),
15010
- name: z13.string(),
15011
- family: z13.string(),
15012
- attachment: z13.boolean(),
15013
- reasoning: z13.boolean(),
15014
- temperature: z13.boolean(),
15015
- interleaved: z13.boolean(),
15016
- toolCall: z13.boolean(),
15017
- modalities: z13.string(),
15018
- cost: z13.string(),
15019
- contextLimit: z13.number().nullable().optional(),
15020
- outputLimit: z13.number().nullable().optional(),
15021
- status: z13.enum(CATALOG_MODEL_STATUSES),
15001
+ modelsCatalogRowInsertSchema = z12.object({
15002
+ id: z12.string(),
15003
+ providerId: z12.string(),
15004
+ name: z12.string(),
15005
+ family: z12.string(),
15006
+ attachment: z12.boolean(),
15007
+ reasoning: z12.boolean(),
15008
+ temperature: z12.boolean(),
15009
+ interleaved: z12.boolean(),
15010
+ toolCall: z12.boolean(),
15011
+ modalities: z12.string(),
15012
+ cost: z12.string(),
15013
+ contextLimit: z12.number().nullable().optional(),
15014
+ outputLimit: z12.number().nullable().optional(),
15015
+ status: z12.enum(CATALOG_MODEL_STATUSES),
15022
15016
  releaseDate: isoDate,
15023
- modelsDevId: z13.string(),
15024
- source: z13.string(),
15025
- seededAt: z13.string().optional()
15017
+ modelsDevId: z12.string(),
15018
+ source: z12.string(),
15019
+ seededAt: z12.string().optional()
15026
15020
  }).strict();
15027
15021
  modelsCatalogRowSelectSchema = modelsCatalogRowInsertSchema.extend({
15028
- seededAt: z13.string()
15022
+ seededAt: z12.string()
15029
15023
  });
15030
15024
  }
15031
15025
  });
@@ -15140,7 +15134,7 @@ var init_status_registry = __esm({
15140
15134
  });
15141
15135
 
15142
15136
  // packages/contracts/src/workgraph.ts
15143
- import { z as z14 } from "zod";
15137
+ import { z as z13 } from "zod";
15144
15138
  var E_WORKGRAPH_PARENT_TYPE_MATRIX, taskTypeSchema, taskPrioritySchema, taskStatusSchema, verificationGateSchema, workGraphRelationKindSchema, workGraphTraversalDirectionSchema, workGraphEdgeDirectionSchema, paginationParamsSchema, workGraphNodeSchema, workGraphEdgeSchema, workGraphHierarchyEdgeSchema, workGraphPageInfoSchema, workGraphRollupCountsSchema, workGraphSubtreePercentagesSchema, workGraphProjectionMismatchSchema, workGraphReadyFrontierTaskSchema, workGraphRelationEdgeSchema, workGraphDependencyEdgeSchema, workGraphOmissionReasonSchema, workGraphContextBudgetSchema, workGraphOmissionSchema, workGraphDirectEdgeSchema, workGraphContextPackParamsSchema, workGraphSliceParamsSchema, workGraphSliceSchema, workGraphReadinessParamsSchema, workGraphReadinessResultSchema, workGraphContextPackSchema, workGraphScaffoldValidateParamsSchema, workGraphScaffoldValidationIssueSchema, workGraphScaffoldValidateResultSchema, workGraphScaffoldApplyParamsSchema, workGraphScaffoldApplyResultSchema, workGraphPlanningDocParamsSchema, workGraphPlanningDocSchema, tasksTraverseParamsSchema, tasksTraverseResultSchema, tasksTreeParamsSchema, tasksTreeResultSchema, tasksRollupParamsSchema, tasksRollupResultSchema, tasksFrontierParamsSchema, tasksFrontierResultSchema, tasksWorkGraphAuditParamsSchema, tasksWorkGraphAuditResultSchema;
15145
15139
  var init_workgraph = __esm({
15146
15140
  "packages/contracts/src/workgraph.ts"() {
@@ -15148,10 +15142,10 @@ var init_workgraph = __esm({
15148
15142
  init_enums();
15149
15143
  init_status_registry();
15150
15144
  E_WORKGRAPH_PARENT_TYPE_MATRIX = "E_WORKGRAPH_PARENT_TYPE_MATRIX";
15151
- taskTypeSchema = z14.enum(["saga", "epic", "task", "subtask"]);
15152
- taskPrioritySchema = z14.enum(["critical", "high", "medium", "low"]);
15153
- taskStatusSchema = z14.enum(TASK_STATUSES);
15154
- verificationGateSchema = z14.enum([
15145
+ taskTypeSchema = z13.enum(["saga", "epic", "task", "subtask"]);
15146
+ taskPrioritySchema = z13.enum(["critical", "high", "medium", "low"]);
15147
+ taskStatusSchema = z13.enum(TASK_STATUSES);
15148
+ verificationGateSchema = z13.enum([
15155
15149
  "implemented",
15156
15150
  "testsPassed",
15157
15151
  "qaPassed",
@@ -15160,7 +15154,7 @@ var init_workgraph = __esm({
15160
15154
  "documented",
15161
15155
  "nexusImpact"
15162
15156
  ]);
15163
- workGraphRelationKindSchema = z14.enum([
15157
+ workGraphRelationKindSchema = z13.enum([
15164
15158
  "contains",
15165
15159
  "depends_on",
15166
15160
  "blocks",
@@ -15168,317 +15162,317 @@ var init_workgraph = __esm({
15168
15162
  "groups",
15169
15163
  "satisfies"
15170
15164
  ]);
15171
- workGraphTraversalDirectionSchema = z14.enum([
15165
+ workGraphTraversalDirectionSchema = z13.enum([
15172
15166
  "ancestors",
15173
15167
  "descendants",
15174
15168
  "upstream",
15175
15169
  "downstream"
15176
15170
  ]);
15177
- workGraphEdgeDirectionSchema = z14.enum(["out", "in", "both"]);
15178
- paginationParamsSchema = z14.object({
15179
- cursor: z14.string().min(1).optional(),
15180
- limit: z14.number().int().positive().max(500).optional()
15171
+ workGraphEdgeDirectionSchema = z13.enum(["out", "in", "both"]);
15172
+ paginationParamsSchema = z13.object({
15173
+ cursor: z13.string().min(1).optional(),
15174
+ limit: z13.number().int().positive().max(500).optional()
15181
15175
  });
15182
- workGraphNodeSchema = z14.object({
15183
- id: z14.string().min(1),
15176
+ workGraphNodeSchema = z13.object({
15177
+ id: z13.string().min(1),
15184
15178
  type: taskTypeSchema,
15185
- title: z14.string(),
15179
+ title: z13.string(),
15186
15180
  status: taskStatusSchema,
15187
15181
  priority: taskPrioritySchema,
15188
- parentId: z14.string().min(1).optional()
15182
+ parentId: z13.string().min(1).optional()
15189
15183
  });
15190
- workGraphEdgeSchema = z14.object({
15191
- fromId: z14.string().min(1),
15192
- toId: z14.string().min(1),
15184
+ workGraphEdgeSchema = z13.object({
15185
+ fromId: z13.string().min(1),
15186
+ toId: z13.string().min(1),
15193
15187
  kind: workGraphRelationKindSchema
15194
15188
  });
15195
- workGraphHierarchyEdgeSchema = z14.object({
15196
- fromId: z14.string().min(1),
15197
- toId: z14.string().min(1),
15198
- kind: z14.literal("contains")
15189
+ workGraphHierarchyEdgeSchema = z13.object({
15190
+ fromId: z13.string().min(1),
15191
+ toId: z13.string().min(1),
15192
+ kind: z13.literal("contains")
15199
15193
  }).strict();
15200
- workGraphPageInfoSchema = z14.object({
15201
- nextCursor: z14.string().min(1).optional(),
15202
- hasMore: z14.boolean()
15203
- });
15204
- workGraphRollupCountsSchema = z14.object({
15205
- total: z14.number().int().nonnegative(),
15206
- byStatus: z14.partialRecord(taskStatusSchema, z14.number().int().nonnegative()),
15207
- byType: z14.partialRecord(taskTypeSchema, z14.number().int().nonnegative())
15208
- });
15209
- workGraphSubtreePercentagesSchema = z14.object({
15210
- done: z14.number().nonnegative(),
15211
- active: z14.number().nonnegative(),
15212
- blocked: z14.number().nonnegative(),
15213
- pending: z14.number().nonnegative(),
15214
- cancelled: z14.number().nonnegative()
15215
- });
15216
- workGraphProjectionMismatchSchema = z14.object({
15217
- field: z14.string().min(1),
15218
- expected: z14.number().int().nonnegative(),
15219
- actual: z14.number().int().nonnegative()
15194
+ workGraphPageInfoSchema = z13.object({
15195
+ nextCursor: z13.string().min(1).optional(),
15196
+ hasMore: z13.boolean()
15197
+ });
15198
+ workGraphRollupCountsSchema = z13.object({
15199
+ total: z13.number().int().nonnegative(),
15200
+ byStatus: z13.partialRecord(taskStatusSchema, z13.number().int().nonnegative()),
15201
+ byType: z13.partialRecord(taskTypeSchema, z13.number().int().nonnegative())
15202
+ });
15203
+ workGraphSubtreePercentagesSchema = z13.object({
15204
+ done: z13.number().nonnegative(),
15205
+ active: z13.number().nonnegative(),
15206
+ blocked: z13.number().nonnegative(),
15207
+ pending: z13.number().nonnegative(),
15208
+ cancelled: z13.number().nonnegative()
15209
+ });
15210
+ workGraphProjectionMismatchSchema = z13.object({
15211
+ field: z13.string().min(1),
15212
+ expected: z13.number().int().nonnegative(),
15213
+ actual: z13.number().int().nonnegative()
15220
15214
  });
15221
15215
  workGraphReadyFrontierTaskSchema = workGraphNodeSchema.extend({
15222
- role: z14.string().min(1).optional(),
15223
- dependencyBlockers: z14.array(z14.object({ taskId: z14.string().min(1), status: taskStatusSchema })),
15224
- gateBlockers: z14.array(z14.object({ gate: verificationGateSchema }))
15216
+ role: z13.string().min(1).optional(),
15217
+ dependencyBlockers: z13.array(z13.object({ taskId: z13.string().min(1), status: taskStatusSchema })),
15218
+ gateBlockers: z13.array(z13.object({ gate: verificationGateSchema }))
15225
15219
  });
15226
15220
  workGraphRelationEdgeSchema = workGraphEdgeSchema.extend({
15227
- source: z14.literal("relation"),
15228
- relationType: z14.enum(TASK_RELATION_TYPES),
15229
- reason: z14.string().min(1).optional()
15221
+ source: z13.literal("relation"),
15222
+ relationType: z13.enum(TASK_RELATION_TYPES),
15223
+ reason: z13.string().min(1).optional()
15230
15224
  });
15231
15225
  workGraphDependencyEdgeSchema = workGraphEdgeSchema.extend({
15232
- source: z14.literal("dependency"),
15233
- kind: z14.literal("depends_on")
15226
+ source: z13.literal("dependency"),
15227
+ kind: z13.literal("depends_on")
15234
15228
  });
15235
- workGraphOmissionReasonSchema = z14.enum([
15229
+ workGraphOmissionReasonSchema = z13.enum([
15236
15230
  "budget_exceeded",
15237
15231
  "not_requested",
15238
15232
  "not_available",
15239
15233
  "redacted",
15240
15234
  "truncated"
15241
15235
  ]);
15242
- workGraphContextBudgetSchema = z14.object({
15243
- tokenBudget: z14.number().int().nonnegative(),
15244
- estimatedTokens: z14.number().int().nonnegative(),
15245
- remainingTokens: z14.number().int().nonnegative(),
15246
- truncated: z14.boolean()
15247
- });
15248
- workGraphOmissionSchema = z14.object({
15249
- path: z14.string().min(1),
15236
+ workGraphContextBudgetSchema = z13.object({
15237
+ tokenBudget: z13.number().int().nonnegative(),
15238
+ estimatedTokens: z13.number().int().nonnegative(),
15239
+ remainingTokens: z13.number().int().nonnegative(),
15240
+ truncated: z13.boolean()
15241
+ });
15242
+ workGraphOmissionSchema = z13.object({
15243
+ path: z13.string().min(1),
15250
15244
  reason: workGraphOmissionReasonSchema,
15251
- message: z14.string().min(1),
15252
- estimatedTokens: z14.number().int().nonnegative().optional()
15245
+ message: z13.string().min(1),
15246
+ estimatedTokens: z13.number().int().nonnegative().optional()
15253
15247
  });
15254
- workGraphDirectEdgeSchema = z14.discriminatedUnion("source", [
15248
+ workGraphDirectEdgeSchema = z13.discriminatedUnion("source", [
15255
15249
  workGraphRelationEdgeSchema,
15256
15250
  workGraphDependencyEdgeSchema
15257
15251
  ]);
15258
15252
  workGraphContextPackParamsSchema = paginationParamsSchema.extend({
15259
- rootId: z14.string().min(1),
15260
- tokenBudget: z14.number().int().positive().optional(),
15261
- includeRelations: z14.boolean().optional(),
15262
- includeReadiness: z14.boolean().optional(),
15263
- includeRollup: z14.boolean().optional()
15253
+ rootId: z13.string().min(1),
15254
+ tokenBudget: z13.number().int().positive().optional(),
15255
+ includeRelations: z13.boolean().optional(),
15256
+ includeReadiness: z13.boolean().optional(),
15257
+ includeRollup: z13.boolean().optional()
15264
15258
  });
15265
15259
  workGraphSliceParamsSchema = paginationParamsSchema.extend({
15266
- rootId: z14.string().min(1),
15260
+ rootId: z13.string().min(1),
15267
15261
  direction: workGraphTraversalDirectionSchema.optional(),
15268
- maxDepth: z14.number().int().nonnegative().optional(),
15269
- includeRelations: z14.boolean().optional()
15262
+ maxDepth: z13.number().int().nonnegative().optional(),
15263
+ includeRelations: z13.boolean().optional()
15270
15264
  });
15271
- workGraphSliceSchema = z14.object({
15272
- rootId: z14.string().min(1),
15265
+ workGraphSliceSchema = z13.object({
15266
+ rootId: z13.string().min(1),
15273
15267
  direction: workGraphTraversalDirectionSchema,
15274
- nodes: z14.array(workGraphNodeSchema),
15275
- edges: z14.array(workGraphEdgeSchema),
15268
+ nodes: z13.array(workGraphNodeSchema),
15269
+ edges: z13.array(workGraphEdgeSchema),
15276
15270
  pageInfo: workGraphPageInfoSchema,
15277
- omissions: z14.array(workGraphOmissionSchema).optional()
15278
- });
15279
- workGraphReadinessParamsSchema = z14.object({
15280
- rootId: z14.string().min(1),
15281
- role: z14.string().min(1).optional(),
15282
- includeGateBlockers: z14.boolean().optional()
15283
- });
15284
- workGraphReadinessResultSchema = z14.object({
15285
- rootId: z14.string().min(1),
15286
- role: z14.string().min(1).optional(),
15287
- ready: z14.boolean(),
15288
- warnings: z14.array(z14.string()),
15289
- groups: z14.object({
15290
- ready: z14.array(workGraphReadyFrontierTaskSchema),
15291
- blocked: z14.array(workGraphReadyFrontierTaskSchema),
15292
- blockedBy: z14.array(
15293
- z14.discriminatedUnion("kind", [
15294
- z14.object({
15295
- kind: z14.literal("dependency"),
15296
- blockerId: z14.string().min(1),
15297
- blocks: z14.array(z14.string().min(1))
15271
+ omissions: z13.array(workGraphOmissionSchema).optional()
15272
+ });
15273
+ workGraphReadinessParamsSchema = z13.object({
15274
+ rootId: z13.string().min(1),
15275
+ role: z13.string().min(1).optional(),
15276
+ includeGateBlockers: z13.boolean().optional()
15277
+ });
15278
+ workGraphReadinessResultSchema = z13.object({
15279
+ rootId: z13.string().min(1),
15280
+ role: z13.string().min(1).optional(),
15281
+ ready: z13.boolean(),
15282
+ warnings: z13.array(z13.string()),
15283
+ groups: z13.object({
15284
+ ready: z13.array(workGraphReadyFrontierTaskSchema),
15285
+ blocked: z13.array(workGraphReadyFrontierTaskSchema),
15286
+ blockedBy: z13.array(
15287
+ z13.discriminatedUnion("kind", [
15288
+ z13.object({
15289
+ kind: z13.literal("dependency"),
15290
+ blockerId: z13.string().min(1),
15291
+ blocks: z13.array(z13.string().min(1))
15298
15292
  }),
15299
- z14.object({
15300
- kind: z14.literal("gate"),
15293
+ z13.object({
15294
+ kind: z13.literal("gate"),
15301
15295
  gate: verificationGateSchema,
15302
- blocks: z14.array(z14.string().min(1))
15296
+ blocks: z13.array(z13.string().min(1))
15303
15297
  })
15304
15298
  ])
15305
15299
  )
15306
15300
  })
15307
15301
  });
15308
- workGraphContextPackSchema = z14.object({
15309
- rootId: z14.string().min(1),
15310
- generatedAt: z14.string().min(1),
15302
+ workGraphContextPackSchema = z13.object({
15303
+ rootId: z13.string().min(1),
15304
+ generatedAt: z13.string().min(1),
15311
15305
  budget: workGraphContextBudgetSchema,
15312
15306
  slice: workGraphSliceSchema,
15313
- relationEdges: z14.object({
15314
- rootId: z14.string().min(1),
15307
+ relationEdges: z13.object({
15308
+ rootId: z13.string().min(1),
15315
15309
  direction: workGraphEdgeDirectionSchema,
15316
- edges: z14.array(workGraphDirectEdgeSchema)
15310
+ edges: z13.array(workGraphDirectEdgeSchema)
15317
15311
  }).optional(),
15318
15312
  readiness: workGraphReadinessResultSchema.optional(),
15319
- rollup: z14.lazy(() => tasksRollupResultSchema).optional(),
15320
- omissions: z14.array(workGraphOmissionSchema)
15321
- });
15322
- workGraphScaffoldValidateParamsSchema = z14.object({
15323
- rootId: z14.string().min(1),
15324
- nodes: z14.array(
15325
- z14.object({
15326
- id: z14.string().min(1),
15313
+ rollup: z13.lazy(() => tasksRollupResultSchema).optional(),
15314
+ omissions: z13.array(workGraphOmissionSchema)
15315
+ });
15316
+ workGraphScaffoldValidateParamsSchema = z13.object({
15317
+ rootId: z13.string().min(1),
15318
+ nodes: z13.array(
15319
+ z13.object({
15320
+ id: z13.string().min(1),
15327
15321
  type: taskTypeSchema,
15328
- parentId: z14.string().min(1).nullable().optional()
15322
+ parentId: z13.string().min(1).nullable().optional()
15329
15323
  })
15330
15324
  ),
15331
- edges: z14.array(workGraphDirectEdgeSchema).optional(),
15332
- dryRun: z14.boolean().optional()
15333
- });
15334
- workGraphScaffoldValidationIssueSchema = z14.object({
15335
- code: z14.string().min(1),
15336
- message: z14.string().min(1),
15337
- taskId: z14.string().min(1).optional(),
15338
- severity: z14.enum(["error", "warning"])
15339
- });
15340
- workGraphScaffoldValidateResultSchema = z14.object({
15341
- rootId: z14.string().min(1),
15342
- valid: z14.boolean(),
15343
- dryRun: z14.boolean(),
15344
- issues: z14.array(workGraphScaffoldValidationIssueSchema),
15345
- hierarchy: z14.object({
15346
- valid: z14.boolean(),
15347
- violations: z14.array(
15348
- z14.object({
15349
- code: z14.literal(E_WORKGRAPH_PARENT_TYPE_MATRIX),
15350
- taskId: z14.string().min(1),
15325
+ edges: z13.array(workGraphDirectEdgeSchema).optional(),
15326
+ dryRun: z13.boolean().optional()
15327
+ });
15328
+ workGraphScaffoldValidationIssueSchema = z13.object({
15329
+ code: z13.string().min(1),
15330
+ message: z13.string().min(1),
15331
+ taskId: z13.string().min(1).optional(),
15332
+ severity: z13.enum(["error", "warning"])
15333
+ });
15334
+ workGraphScaffoldValidateResultSchema = z13.object({
15335
+ rootId: z13.string().min(1),
15336
+ valid: z13.boolean(),
15337
+ dryRun: z13.boolean(),
15338
+ issues: z13.array(workGraphScaffoldValidationIssueSchema),
15339
+ hierarchy: z13.object({
15340
+ valid: z13.boolean(),
15341
+ violations: z13.array(
15342
+ z13.object({
15343
+ code: z13.literal(E_WORKGRAPH_PARENT_TYPE_MATRIX),
15344
+ taskId: z13.string().min(1),
15351
15345
  taskType: taskTypeSchema,
15352
- parentId: z14.string().min(1).nullable(),
15346
+ parentId: z13.string().min(1).nullable(),
15353
15347
  parentType: taskTypeSchema.optional(),
15354
- message: z14.string().min(1)
15348
+ message: z13.string().min(1)
15355
15349
  })
15356
15350
  )
15357
15351
  })
15358
15352
  });
15359
15353
  workGraphScaffoldApplyParamsSchema = workGraphScaffoldValidateParamsSchema.extend({
15360
- apply: z14.boolean().optional()
15354
+ apply: z13.boolean().optional()
15361
15355
  });
15362
15356
  workGraphScaffoldApplyResultSchema = workGraphScaffoldValidateResultSchema.extend({
15363
- applied: z14.boolean(),
15364
- nodesChanged: z14.number().int().nonnegative(),
15365
- edgesChanged: z14.number().int().nonnegative()
15366
- });
15367
- workGraphPlanningDocParamsSchema = z14.object({
15368
- rootId: z14.string().min(1),
15369
- audience: z14.enum(["agent", "maintainer"]),
15370
- tokenBudget: z14.number().int().positive().optional(),
15371
- includeRelations: z14.boolean().optional(),
15372
- includeReadiness: z14.boolean().optional(),
15373
- includeRollup: z14.boolean().optional()
15374
- });
15375
- workGraphPlanningDocSchema = z14.object({
15376
- rootId: z14.string().min(1),
15377
- generatedAt: z14.string().min(1),
15378
- audience: z14.enum(["agent", "maintainer"]),
15379
- title: z14.string().min(1),
15380
- content: z14.string(),
15381
- sections: z14.array(z14.string().min(1)),
15382
- estimatedTokens: z14.number().int().nonnegative(),
15383
- budget: z14.object({
15384
- tokenBudget: z14.number().int().positive(),
15385
- truncated: z14.boolean()
15357
+ applied: z13.boolean(),
15358
+ nodesChanged: z13.number().int().nonnegative(),
15359
+ edgesChanged: z13.number().int().nonnegative()
15360
+ });
15361
+ workGraphPlanningDocParamsSchema = z13.object({
15362
+ rootId: z13.string().min(1),
15363
+ audience: z13.enum(["agent", "maintainer"]),
15364
+ tokenBudget: z13.number().int().positive().optional(),
15365
+ includeRelations: z13.boolean().optional(),
15366
+ includeReadiness: z13.boolean().optional(),
15367
+ includeRollup: z13.boolean().optional()
15368
+ });
15369
+ workGraphPlanningDocSchema = z13.object({
15370
+ rootId: z13.string().min(1),
15371
+ generatedAt: z13.string().min(1),
15372
+ audience: z13.enum(["agent", "maintainer"]),
15373
+ title: z13.string().min(1),
15374
+ content: z13.string(),
15375
+ sections: z13.array(z13.string().min(1)),
15376
+ estimatedTokens: z13.number().int().nonnegative(),
15377
+ budget: z13.object({
15378
+ tokenBudget: z13.number().int().positive(),
15379
+ truncated: z13.boolean()
15386
15380
  }).optional()
15387
15381
  });
15388
15382
  tasksTraverseParamsSchema = paginationParamsSchema.extend({
15389
- rootId: z14.string().min(1),
15383
+ rootId: z13.string().min(1),
15390
15384
  direction: workGraphTraversalDirectionSchema,
15391
- maxDepth: z14.number().int().nonnegative().optional(),
15392
- includeRelations: z14.boolean().optional()
15385
+ maxDepth: z13.number().int().nonnegative().optional(),
15386
+ includeRelations: z13.boolean().optional()
15393
15387
  });
15394
- tasksTraverseResultSchema = z14.object({
15395
- rootId: z14.string().min(1),
15388
+ tasksTraverseResultSchema = z13.object({
15389
+ rootId: z13.string().min(1),
15396
15390
  direction: workGraphTraversalDirectionSchema,
15397
- nodes: z14.array(workGraphNodeSchema),
15398
- edges: z14.array(workGraphEdgeSchema),
15391
+ nodes: z13.array(workGraphNodeSchema),
15392
+ edges: z13.array(workGraphEdgeSchema),
15399
15393
  pageInfo: workGraphPageInfoSchema
15400
15394
  });
15401
15395
  tasksTreeParamsSchema = paginationParamsSchema.extend({
15402
- rootId: z14.string().min(1),
15403
- maxDepth: z14.number().int().nonnegative().optional()
15396
+ rootId: z13.string().min(1),
15397
+ maxDepth: z13.number().int().nonnegative().optional()
15404
15398
  });
15405
- tasksTreeResultSchema = z14.object({
15406
- rootId: z14.string().min(1),
15407
- nodes: z14.array(workGraphNodeSchema.extend({ depth: z14.number().int().positive() })),
15408
- edges: z14.array(workGraphHierarchyEdgeSchema),
15399
+ tasksTreeResultSchema = z13.object({
15400
+ rootId: z13.string().min(1),
15401
+ nodes: z13.array(workGraphNodeSchema.extend({ depth: z13.number().int().positive() })),
15402
+ edges: z13.array(workGraphHierarchyEdgeSchema),
15409
15403
  pageInfo: workGraphPageInfoSchema
15410
15404
  });
15411
- tasksRollupParamsSchema = z14.object({
15412
- rootId: z14.string().min(1),
15405
+ tasksRollupParamsSchema = z13.object({
15406
+ rootId: z13.string().min(1),
15413
15407
  expectedDirectRollup: workGraphRollupCountsSchema.optional()
15414
15408
  });
15415
- tasksRollupResultSchema = z14.object({
15416
- rootId: z14.string().min(1),
15409
+ tasksRollupResultSchema = z13.object({
15410
+ rootId: z13.string().min(1),
15417
15411
  direct: workGraphRollupCountsSchema,
15418
15412
  subtree: workGraphRollupCountsSchema,
15419
- percentDenominator: z14.object({
15420
- basis: z14.literal("subtree-total"),
15421
- total: z14.number().int().nonnegative(),
15422
- description: z14.string().min(1)
15413
+ percentDenominator: z13.object({
15414
+ basis: z13.literal("subtree-total"),
15415
+ total: z13.number().int().nonnegative(),
15416
+ description: z13.string().min(1)
15423
15417
  }),
15424
15418
  percentages: workGraphSubtreePercentagesSchema,
15425
- staleProjection: z14.boolean(),
15426
- projectionMismatches: z14.array(workGraphProjectionMismatchSchema)
15427
- });
15428
- tasksFrontierParamsSchema = z14.object({
15429
- rootId: z14.string().min(1),
15430
- role: z14.string().min(1).optional()
15431
- });
15432
- tasksFrontierResultSchema = z14.object({
15433
- rootId: z14.string().min(1),
15434
- role: z14.string().min(1).optional(),
15435
- groups: z14.object({
15436
- ready: z14.array(workGraphReadyFrontierTaskSchema),
15437
- blocked: z14.array(workGraphReadyFrontierTaskSchema),
15438
- blockedBy: z14.array(
15439
- z14.discriminatedUnion("kind", [
15440
- z14.object({
15441
- kind: z14.literal("dependency"),
15442
- blockerId: z14.string().min(1),
15443
- blocks: z14.array(z14.string().min(1))
15419
+ staleProjection: z13.boolean(),
15420
+ projectionMismatches: z13.array(workGraphProjectionMismatchSchema)
15421
+ });
15422
+ tasksFrontierParamsSchema = z13.object({
15423
+ rootId: z13.string().min(1),
15424
+ role: z13.string().min(1).optional()
15425
+ });
15426
+ tasksFrontierResultSchema = z13.object({
15427
+ rootId: z13.string().min(1),
15428
+ role: z13.string().min(1).optional(),
15429
+ groups: z13.object({
15430
+ ready: z13.array(workGraphReadyFrontierTaskSchema),
15431
+ blocked: z13.array(workGraphReadyFrontierTaskSchema),
15432
+ blockedBy: z13.array(
15433
+ z13.discriminatedUnion("kind", [
15434
+ z13.object({
15435
+ kind: z13.literal("dependency"),
15436
+ blockerId: z13.string().min(1),
15437
+ blocks: z13.array(z13.string().min(1))
15444
15438
  }),
15445
- z14.object({
15446
- kind: z14.literal("gate"),
15439
+ z13.object({
15440
+ kind: z13.literal("gate"),
15447
15441
  gate: verificationGateSchema,
15448
- blocks: z14.array(z14.string().min(1))
15442
+ blocks: z13.array(z13.string().min(1))
15449
15443
  })
15450
15444
  ])
15451
15445
  )
15452
15446
  })
15453
15447
  });
15454
15448
  tasksWorkGraphAuditParamsSchema = paginationParamsSchema.extend({
15455
- rootId: z14.string().min(1),
15456
- maxDepth: z14.number().int().nonnegative().optional(),
15457
- includeRelations: z14.boolean().optional()
15458
- });
15459
- tasksWorkGraphAuditResultSchema = z14.object({
15460
- rootId: z14.string().min(1),
15461
- hierarchy: z14.object({
15462
- valid: z14.boolean(),
15463
- violations: z14.array(
15464
- z14.object({
15465
- code: z14.literal(E_WORKGRAPH_PARENT_TYPE_MATRIX),
15466
- taskId: z14.string().min(1),
15449
+ rootId: z13.string().min(1),
15450
+ maxDepth: z13.number().int().nonnegative().optional(),
15451
+ includeRelations: z13.boolean().optional()
15452
+ });
15453
+ tasksWorkGraphAuditResultSchema = z13.object({
15454
+ rootId: z13.string().min(1),
15455
+ hierarchy: z13.object({
15456
+ valid: z13.boolean(),
15457
+ violations: z13.array(
15458
+ z13.object({
15459
+ code: z13.literal(E_WORKGRAPH_PARENT_TYPE_MATRIX),
15460
+ taskId: z13.string().min(1),
15467
15461
  taskType: taskTypeSchema,
15468
- parentId: z14.string().min(1).nullable(),
15462
+ parentId: z13.string().min(1).nullable(),
15469
15463
  parentType: taskTypeSchema.optional(),
15470
- message: z14.string().min(1)
15464
+ message: z13.string().min(1)
15471
15465
  })
15472
15466
  )
15473
15467
  }),
15474
15468
  traversal: tasksTraverseResultSchema,
15475
15469
  frontier: tasksFrontierResultSchema,
15476
15470
  rollup: tasksRollupResultSchema,
15477
- relationEdges: z14.object({
15478
- rootId: z14.string().min(1),
15471
+ relationEdges: z13.object({
15472
+ rootId: z13.string().min(1),
15479
15473
  direction: workGraphEdgeDirectionSchema,
15480
- edges: z14.array(
15481
- z14.discriminatedUnion("source", [
15474
+ edges: z13.array(
15475
+ z13.discriminatedUnion("source", [
15482
15476
  workGraphRelationEdgeSchema,
15483
15477
  workGraphDependencyEdgeSchema
15484
15478
  ])
@@ -15497,6 +15491,51 @@ var init_operation_envelope_validation = __esm({
15497
15491
  }
15498
15492
  });
15499
15493
 
15494
+ // packages/contracts/src/operations/docs.ts
15495
+ import { z as z14 } from "zod";
15496
+ var DOCS_PROJECTION_PROPOSAL_SCHEMA, DOCS_PROJECTION_RECEIPT_SCHEMA;
15497
+ var init_docs = __esm({
15498
+ "packages/contracts/src/operations/docs.ts"() {
15499
+ "use strict";
15500
+ init_docs_taxonomy();
15501
+ init_docs_lifecycle();
15502
+ DOCS_PROJECTION_PROPOSAL_SCHEMA = z14.object({
15503
+ version: z14.literal(1),
15504
+ operation: z14.literal("docs.projection"),
15505
+ identity: z14.object({
15506
+ projectId: z14.string().min(1),
15507
+ projectRoot: z14.string().min(1),
15508
+ actor: z14.string().min(1),
15509
+ operation: z14.literal("docs.projection"),
15510
+ idempotencyKey: z14.string().min(1)
15511
+ }).strict(),
15512
+ source: z14.object({
15513
+ attachmentId: z14.string().min(1),
15514
+ sha256: z14.string().regex(/^[a-f0-9]{64}$/),
15515
+ ownerId: z14.string().min(1),
15516
+ ownerType: z14.enum(["task", "session", "observation", "decision", "learning", "pattern"]),
15517
+ label: z14.string().min(1)
15518
+ }).strict(),
15519
+ observation: z14.object({
15520
+ kind: z14.literal("doc-attachment"),
15521
+ attachmentId: z14.string().min(1),
15522
+ ownerId: z14.string().min(1),
15523
+ addedAt: z14.string().min(1),
15524
+ slug: z14.string().optional(),
15525
+ type: z14.string().optional()
15526
+ }).strict()
15527
+ }).strict();
15528
+ DOCS_PROJECTION_RECEIPT_SCHEMA = z14.object({
15529
+ version: z14.literal(1),
15530
+ sourceHash: z14.string().regex(/^[a-f0-9]{64}$/),
15531
+ graph: z14.enum(["completed", "disabled"]),
15532
+ observationId: z14.string().min(1),
15533
+ verifiedAt: z14.string().min(1),
15534
+ actor: z14.string().min(1)
15535
+ }).strict();
15536
+ }
15537
+ });
15538
+
15500
15539
  // packages/contracts/src/operations/ensures-schema-registry.ts
15501
15540
  import { z as z15 } from "zod";
15502
15541
  var taskTreeEntrySchema, taskTreeSchema, evidenceSchema, passthroughSchema, LEGACY_PASSTHROUGH_SCHEMA_NAMES, ENSURES_SCHEMA_REGISTRY;
@@ -15866,11 +15905,7 @@ var init_evidence_atoms = __esm({
15866
15905
  }).passthrough()
15867
15906
  ).optional().default([])
15868
15907
  }).passthrough();
15869
- PR_REQUIRED_WORKFLOWS = Object.freeze([
15870
- "CI",
15871
- "Lockfile Check",
15872
- "Contracts Dep Lint"
15873
- ]);
15908
+ PR_REQUIRED_WORKFLOWS = Object.freeze(["CI", "Contracts Dep Lint"]);
15874
15909
  }
15875
15910
  });
15876
15911
 
@@ -18088,8 +18123,15 @@ var init_nexus_cloud = __esm({
18088
18123
  nexusCloudProjectListItemSchema = z26.object({
18089
18124
  ...projectFields,
18090
18125
  role: z26.string(),
18126
+ /**
18127
+ * The project name sealed with the project data key (`Project.encryptedName`), base64;
18128
+ * only a holder of the account key can open it (T13102).
18129
+ */
18130
+ encryptedName: z26.string().nullable().optional(),
18091
18131
  streamId: z26.string().optional(),
18092
18132
  headSeq: z26.number().int().nonnegative().nullable().optional(),
18133
+ /** The stream's head snapshot; null when nothing was ever pushed (T13102: restorable or not). */
18134
+ headCheckpointId: z26.string().nullable().optional(),
18093
18135
  openConflicts: z26.number().int().nonnegative().optional(),
18094
18136
  replicas: z26.array(nexusCloudReplicaSchema).default([]),
18095
18137
  devices: nexusCloudDeviceCountsSchema.optional(),
@@ -18155,6 +18197,11 @@ var init_nexus_cloud = __esm({
18155
18197
  nexusCloudStatusSchema = z26.object({
18156
18198
  serverTime: isoTime.optional(),
18157
18199
  apiVersion: z26.string().optional(),
18200
+ /**
18201
+ * Optional server features (e.g. {@link NEXUS_FEATURE_PROJECT_INITIAL_KEY}). Absent from servers older
18202
+ * than the list, which therefore support none of them.
18203
+ */
18204
+ features: z26.array(z26.string()).optional(),
18158
18205
  user: nexusCloudUserSchema,
18159
18206
  credential: nexusCloudCredentialSchema,
18160
18207
  device: nexusCloudDeviceSchema.nullable(),
@@ -18235,6 +18282,13 @@ var init_nexus_cloud = __esm({
18235
18282
  }
18236
18283
  });
18237
18284
 
18285
+ // packages/contracts/src/nexus-first-run.ts
18286
+ var init_nexus_first_run = __esm({
18287
+ "packages/contracts/src/nexus-first-run.ts"() {
18288
+ "use strict";
18289
+ }
18290
+ });
18291
+
18238
18292
  // packages/contracts/src/nexus-vault.ts
18239
18293
  import { z as z27 } from "zod";
18240
18294
  var isoTime2, uuid, nexusUserKeysSchema, nexusProjectKeysSchema, nexusLeaseSchema, nexusLeaseAcquireSchema, nexusStreamHeadSchema, nexusActivityEventSchema, nexusActivityPageSchema;
@@ -18380,6 +18434,7 @@ var init_src = __esm({
18380
18434
  init_decide_sites();
18381
18435
  init_nexus_account();
18382
18436
  init_nexus_cloud();
18437
+ init_nexus_first_run();
18383
18438
  init_nexus_vault();
18384
18439
  init_sync_schema();
18385
18440
  init_task_id();
@@ -44442,18 +44497,6 @@ function appendHookEntry(hooks, event, entry) {
44442
44497
  }
44443
44498
  hooks[event] = [...existing ?? [], entry];
44444
44499
  }
44445
- function removeCleoHookEntries(hooks) {
44446
- let changed = false;
44447
- for (const [event, entries] of Object.entries(hooks)) {
44448
- if (!Array.isArray(entries)) continue;
44449
- const kept = entries.filter((e) => !isCleoHookEntry(e));
44450
- if (kept.length !== entries.length) {
44451
- hooks[event] = kept;
44452
- changed = true;
44453
- }
44454
- }
44455
- return changed;
44456
- }
44457
44500
  var CLEO_HOOK_MARKER;
44458
44501
  var init_hook_config = __esm({
44459
44502
  "packages/adapters/src/providers/shared/hook-config.ts"() {
@@ -44462,43 +44505,623 @@ var init_hook_config = __esm({
44462
44505
  }
44463
44506
  });
44464
44507
 
44508
+ // packages/adapters/src/providers/shared/heavy-command-hook-install.ts
44509
+ import { execFileSync } from "node:child_process";
44510
+ import {
44511
+ existsSync as existsSync3,
44512
+ mkdirSync,
44513
+ readdirSync as readdirSync2,
44514
+ readFileSync as readFileSync3,
44515
+ realpathSync,
44516
+ rmSync,
44517
+ statSync,
44518
+ writeFileSync
44519
+ } from "node:fs";
44520
+ import { homedir as homedir2, tmpdir } from "node:os";
44521
+ import { dirname, isAbsolute, join as join3, resolve as resolve2 } from "node:path";
44522
+ import { updateJsonConfigFile } from "@cleocode/caamp";
44523
+ function canonical(path) {
44524
+ try {
44525
+ return realpathSync.native(path);
44526
+ } catch {
44527
+ return resolve2(path);
44528
+ }
44529
+ }
44530
+ function nonDirectoryAncestor(dir) {
44531
+ let cur = resolve2(dir);
44532
+ for (; ; ) {
44533
+ try {
44534
+ return statSync(cur).isDirectory() ? null : cur;
44535
+ } catch (err) {
44536
+ const code = err.code;
44537
+ if (code !== "ENOENT" && code !== "ENOTDIR") return null;
44538
+ }
44539
+ const parent = dirname(cur);
44540
+ if (parent === cur) return null;
44541
+ cur = parent;
44542
+ }
44543
+ }
44544
+ function ensureParentDir(target) {
44545
+ const blocked = nonDirectoryAncestor(dirname(target));
44546
+ if (blocked !== null) throw new HeavyHookPathBlockedError(blocked, target);
44547
+ mkdirSync(dirname(target), { recursive: true });
44548
+ }
44549
+ function isUserHomeDir(projectDir, home = homedir2()) {
44550
+ return canonical(projectDir) === canonical(home);
44551
+ }
44552
+ function clearOlderCleoMarkers(dir = process.env.XDG_RUNTIME_DIR || tmpdir()) {
44553
+ let removed = 0;
44554
+ let names;
44555
+ try {
44556
+ names = readdirSync2(dir);
44557
+ } catch {
44558
+ return 0;
44559
+ }
44560
+ for (const name15 of names) {
44561
+ if (!name15.startsWith(OLDER_CLEO_MARKER_PREFIX)) continue;
44562
+ try {
44563
+ rmSync(join3(dir, name15), { force: true });
44564
+ removed++;
44565
+ } catch {
44566
+ }
44567
+ }
44568
+ return removed;
44569
+ }
44570
+ function shellWord(text) {
44571
+ return `'${text.replace(/'/g, `'\\''`)}'`;
44572
+ }
44573
+ function heavyCommandGuardScript(provider) {
44574
+ const words = HEAVY_TOOL_WORDS.join("|");
44575
+ const old = `printf '%s\\n' '${OLDER_CLEO_HOOK_ANSWER}'`;
44576
+ return [
44577
+ "command -v cleo >/dev/null 2>&1 || exit 0",
44578
+ 'in="$(cat)"',
44579
+ "set -f",
44580
+ "hit=",
44581
+ // `[\]` (not `\\`) matches a backslash: it survives fish's quoting under Codex.
44582
+ `for w in $(printf '%s' "$in" | sed 's/[\\][nrt]/ /g; s/[^A-Za-z0-9_./@+-]/ /g'); do case "\${w##*/}" in ${words}) hit=1; break;; esac; done`,
44583
+ '[ -n "$hit" ] || exit 0',
44584
+ 'p="$(command -v cleo)"',
44585
+ `d="\${XDG_RUNTIME_DIR:-\${TMPDIR:-/tmp}}"`,
44586
+ `k="$d/${OLDER_CLEO_MARKER_PREFIX}$(printf '%s|%s' "$p" "$PWD" | cksum | tr -c '0-9' '-')"`,
44587
+ `if [ -f "$k" ] && [ ! -L "$k" ] && [ -O "$k" ] && ! [ "$p" -nt "$k" ] && [ -n "$(find "$k" -mmin -60 2>/dev/null)" ]; then ${old}; exit 0; fi`,
44588
+ 'e="$(mktemp 2>/dev/null)" || e=',
44589
+ // Only the older-CLEO signature (exit 127, "Unknown command hook") writes the
44590
+ // marker; a crash or kill of a current CLEO fails open and asks again next time.
44591
+ `if out="$(printf '%s' "$in" | ${HEAVY_COMMAND_HOOK_ID} --provider ${provider} 2>"\${e:-/dev/null}")"; then case "$out" in "{"*) printf '%s\\n' "$out";; esac; else rc=$?; if [ "$rc" = 127 ] && [ -n "$e" ] && grep -q 'Unknown command' "$e" 2>/dev/null; then t="$(mktemp "$k.XXXXXX" 2>/dev/null)" && mv -f "$t" "$k"; ${old}; fi; fi`,
44592
+ '[ -z "$e" ] || rm -f "$e"',
44593
+ "exit 0"
44594
+ ].join("; ");
44595
+ }
44596
+ function heavyCommandHookCommand(provider) {
44597
+ const script = heavyCommandGuardScript(provider);
44598
+ const line = provider === "codex" ? `/bin/sh -c ${shellWord(script)}` : script;
44599
+ return `${line} ${CLEO_HOOK_MARKER}`;
44600
+ }
44601
+ function heavyCommandHookObject(provider) {
44602
+ return {
44603
+ type: "command",
44604
+ command: heavyCommandHookCommand(provider),
44605
+ timeout: HEAVY_COMMAND_HOOK_TIMEOUT_SEC
44606
+ };
44607
+ }
44608
+ function heavyCommandHookEntry(provider) {
44609
+ return { matcher: "Bash", hooks: [heavyCommandHookObject(provider)] };
44610
+ }
44611
+ function isHeavyHookObject(hook) {
44612
+ return isPlainObject2(hook) && typeof hook.command === "string" && hook.command.includes(CLEO_HOOK_MARKER) && hook.command.includes(HEAVY_COMMAND_HOOK_ID);
44613
+ }
44614
+ function placeHeavyHook(hooks, desired) {
44615
+ const groups = hooks.PreToolUse;
44616
+ if (groups === void 0) return { changed: false, found: false };
44617
+ if (!Array.isArray(groups)) {
44618
+ throw new Error('hooks "PreToolUse" is not an array; not modifying it');
44619
+ }
44620
+ let changed = false;
44621
+ let found = false;
44622
+ const next = [];
44623
+ for (const group of groups) {
44624
+ if (!isPlainObject2(group) || !Array.isArray(group.hooks)) {
44625
+ next.push(group);
44626
+ continue;
44627
+ }
44628
+ const kept = [];
44629
+ let groupChanged = false;
44630
+ for (const hook of group.hooks) {
44631
+ if (!isHeavyHookObject(hook)) {
44632
+ kept.push(hook);
44633
+ } else if (desired !== null && !found) {
44634
+ found = true;
44635
+ if (JSON.stringify(hook) !== JSON.stringify(desired)) groupChanged = true;
44636
+ kept.push(desired);
44637
+ } else {
44638
+ groupChanged = true;
44639
+ }
44640
+ }
44641
+ if (!groupChanged) {
44642
+ next.push(group);
44643
+ continue;
44644
+ }
44645
+ changed = true;
44646
+ if (kept.length > 0) next.push({ ...group, hooks: kept });
44647
+ }
44648
+ if (changed) hooks.PreToolUse = next;
44649
+ return { changed, found };
44650
+ }
44651
+ async function syncJsonHeavyCommandHook(configPath, provider, mode) {
44652
+ if (mode === "off") {
44653
+ if (!existsSync3(configPath)) return "unchanged";
44654
+ const removed = await updateJsonConfigFile(configPath, (config2) => {
44655
+ if (config2.hooks === void 0) return false;
44656
+ return placeHeavyHook(hookMap(config2), null).changed;
44657
+ });
44658
+ return removed ? "removed" : "unchanged";
44659
+ }
44660
+ ensureParentDir(configPath);
44661
+ clearOlderCleoMarkers();
44662
+ let result = "unchanged";
44663
+ await updateJsonConfigFile(configPath, (config2) => {
44664
+ const hooks = hookMap(config2);
44665
+ const placed = placeHeavyHook(hooks, heavyCommandHookObject(provider));
44666
+ if (placed.found) {
44667
+ if (placed.changed) result = "updated";
44668
+ return placed.changed;
44669
+ }
44670
+ const existing = hooks.PreToolUse;
44671
+ hooks.PreToolUse = [
44672
+ ...Array.isArray(existing) ? existing : [],
44673
+ heavyCommandHookEntry(provider)
44674
+ ];
44675
+ result = "installed";
44676
+ return true;
44677
+ });
44678
+ return result;
44679
+ }
44680
+ function gitOutput(projectDir, args) {
44681
+ try {
44682
+ return execFileSync("git", ["-C", projectDir, ...args], {
44683
+ encoding: "utf-8",
44684
+ stdio: ["ignore", "pipe", "ignore"],
44685
+ timeout: 5e3
44686
+ }).trim();
44687
+ } catch {
44688
+ return null;
44689
+ }
44690
+ }
44691
+ function excludeFile(projectDir) {
44692
+ if (gitOutput(projectDir, ["rev-parse", "--is-inside-work-tree"]) !== "true") return null;
44693
+ const path = gitOutput(projectDir, ["rev-parse", "--git-path", "info/exclude"]);
44694
+ if (path === null || path === "") return null;
44695
+ return isAbsolute(path) ? path : join3(projectDir, path);
44696
+ }
44697
+ function excludeLine(projectDir, relPath) {
44698
+ const prefix = gitOutput(projectDir, ["rev-parse", "--show-prefix"]) ?? "";
44699
+ return `/${prefix}${relPath}`;
44700
+ }
44701
+ function excludeHookFileFromGit(projectDir, relPath, marker16 = HOOK_FILE_EXCLUDE_MARKER) {
44702
+ const exclude = excludeFile(projectDir);
44703
+ if (exclude === null) return false;
44704
+ if (gitOutput(projectDir, ["check-ignore", relPath]) !== null) return false;
44705
+ if (gitOutput(projectDir, ["ls-files", "--error-unmatch", relPath]) !== null) return false;
44706
+ const line = excludeLine(projectDir, relPath);
44707
+ const text = existsSync3(exclude) ? readFileSync3(exclude, "utf-8") : "";
44708
+ if (text.split("\n").includes(line)) return false;
44709
+ const block = `${marker16}
44710
+ ${line}
44711
+ `;
44712
+ mkdirSync(dirname(exclude), { recursive: true });
44713
+ writeFileSync(exclude, text === "" || text.endsWith("\n") ? text + block : `${text}
44714
+ ${block}`);
44715
+ return true;
44716
+ }
44717
+ function unexcludeHookFileFromGit(projectDir, relPath) {
44718
+ const exclude = excludeFile(projectDir);
44719
+ if (exclude === null || !existsSync3(exclude)) return false;
44720
+ const lines = readFileSync3(exclude, "utf-8").split("\n");
44721
+ const at = lines.indexOf(excludeLine(projectDir, relPath));
44722
+ const above = lines[at - 1];
44723
+ if (at < 1 || above !== LOCAL_SETTINGS_EXCLUDE_MARKER && above !== HOOK_FILE_EXCLUDE_MARKER) {
44724
+ return false;
44725
+ }
44726
+ lines.splice(at - 1, 2);
44727
+ writeFileSync(exclude, lines.join("\n"));
44728
+ return true;
44729
+ }
44730
+ function hookFileVisibleToGit(projectDir, relPath) {
44731
+ if (gitOutput(projectDir, ["rev-parse", "--is-inside-work-tree"]) !== "true") return false;
44732
+ if (gitOutput(projectDir, ["check-ignore", relPath]) !== null) return false;
44733
+ return gitOutput(projectDir, ["ls-files", "--error-unmatch", relPath]) === null;
44734
+ }
44735
+ function excludeLocalSettingsFromGit(projectDir) {
44736
+ return excludeHookFileFromGit(projectDir, CLAUDE_LOCAL_SETTINGS, LOCAL_SETTINGS_EXCLUDE_MARKER);
44737
+ }
44738
+ function unexcludeLocalSettingsFromGit(projectDir) {
44739
+ return unexcludeHookFileFromGit(projectDir, CLAUDE_LOCAL_SETTINGS);
44740
+ }
44741
+ function cleoCreatedConfig(config2) {
44742
+ if (!isPlainObject2(config2) || Object.keys(config2).some((k) => k !== "hooks")) return false;
44743
+ if (!isPlainObject2(config2.hooks)) return false;
44744
+ let mine = 0;
44745
+ for (const groups of Object.values(config2.hooks)) {
44746
+ if (!Array.isArray(groups)) return false;
44747
+ for (const group of groups) {
44748
+ if (!isPlainObject2(group) || !Array.isArray(group.hooks)) return false;
44749
+ for (const hook of group.hooks) {
44750
+ if (!isPlainObject2(hook) || typeof hook.command !== "string" || !hook.command.includes(CLEO_HOOK_MARKER)) {
44751
+ return false;
44752
+ }
44753
+ mine++;
44754
+ }
44755
+ }
44756
+ }
44757
+ return mine > 0;
44758
+ }
44759
+ function codexHooksSharedReason(projectDir, checkGit = true) {
44760
+ const file2 = join3(projectDir, CODEX_HOOKS_FILE);
44761
+ if (checkGit && gitOutput(projectDir, ["ls-files", "--error-unmatch", CODEX_HOOKS_FILE]) !== null) {
44762
+ return "is tracked by git (a shared team config)";
44763
+ }
44764
+ if (!existsSync3(file2)) return null;
44765
+ let config2;
44766
+ try {
44767
+ config2 = JSON.parse(readFileSync3(file2, "utf-8"));
44768
+ } catch {
44769
+ return "is the project's own Codex hook config (empty or not valid JSON)";
44770
+ }
44771
+ return cleoCreatedConfig(config2) ? null : "is the project's own Codex hook config";
44772
+ }
44773
+ function committedCodexHookHoldsCleo(projectDir) {
44774
+ const committed = gitOutput(projectDir, ["show", `HEAD:./${CODEX_HOOKS_FILE}`]);
44775
+ if (committed === null || committed === "") return false;
44776
+ try {
44777
+ const config2 = JSON.parse(committed);
44778
+ if (!isPlainObject2(config2)) return false;
44779
+ const groups = isPlainObject2(config2.hooks) ? config2.hooks.PreToolUse : void 0;
44780
+ return Array.isArray(groups) && groups.some(
44781
+ (g) => isPlainObject2(g) && Array.isArray(g.hooks) && g.hooks.some(isHeavyHookObject)
44782
+ );
44783
+ } catch {
44784
+ return false;
44785
+ }
44786
+ }
44787
+ function jsonEqual(a, b) {
44788
+ if (Array.isArray(a) || Array.isArray(b)) {
44789
+ return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((v, i) => jsonEqual(v, b[i]));
44790
+ }
44791
+ if (isPlainObject2(a) && isPlainObject2(b)) {
44792
+ const keys = Object.keys(a);
44793
+ return keys.length === Object.keys(b).length && keys.every((k) => Object.hasOwn(b, k) && jsonEqual(a[k], b[k]));
44794
+ }
44795
+ return a === b;
44796
+ }
44797
+ function restoreCommittedCodexHooks(projectDir) {
44798
+ if (gitOutput(projectDir, ["ls-files", "--error-unmatch", CODEX_HOOKS_FILE]) === null)
44799
+ return false;
44800
+ const file2 = join3(projectDir, CODEX_HOOKS_FILE);
44801
+ let committed;
44802
+ let working;
44803
+ try {
44804
+ committed = execFileSync("git", ["-C", projectDir, "show", `HEAD:./${CODEX_HOOKS_FILE}`], {
44805
+ encoding: "utf-8",
44806
+ stdio: ["ignore", "pipe", "ignore"],
44807
+ timeout: 5e3
44808
+ });
44809
+ working = JSON.parse(readFileSync3(file2, "utf-8"));
44810
+ } catch {
44811
+ return false;
44812
+ }
44813
+ if (!isPlainObject2(working)) return false;
44814
+ const hooks = hookMap(working);
44815
+ if (!placeHeavyHook(hooks, null).changed) return false;
44816
+ try {
44817
+ if (!jsonEqual(working, JSON.parse(committed))) return false;
44818
+ } catch {
44819
+ return false;
44820
+ }
44821
+ writeFileSync(file2, committed);
44822
+ return true;
44823
+ }
44824
+ function excludedByCleo(projectDir, relPath) {
44825
+ const exclude = excludeFile(projectDir);
44826
+ if (exclude === null || !existsSync3(exclude)) return false;
44827
+ const lines = readFileSync3(exclude, "utf-8").split("\n");
44828
+ const at = lines.indexOf(excludeLine(projectDir, relPath));
44829
+ const above = lines[at - 1];
44830
+ return at >= 1 && (above === LOCAL_SETTINGS_EXCLUDE_MARKER || above === HOOK_FILE_EXCLUDE_MARKER);
44831
+ }
44832
+ function codexHookSnippet() {
44833
+ return JSON.stringify(heavyCommandHookEntry("codex"));
44834
+ }
44835
+ function trackedFileModified(projectDir, relPath) {
44836
+ if (gitOutput(projectDir, ["ls-files", "--error-unmatch", relPath]) === null) return false;
44837
+ return (gitOutput(projectDir, ["diff", "--name-only", "HEAD", "--", relPath]) ?? "") !== "";
44838
+ }
44839
+ async function syncCodexHeavyCommandHook(projectDir, mode) {
44840
+ const file2 = join3(projectDir, CODEX_HOOKS_FILE);
44841
+ if (mode !== "off") {
44842
+ const shared = codexHooksSharedReason(projectDir);
44843
+ if (shared !== null) {
44844
+ unexcludeHookFileFromGit(projectDir, CODEX_HOOKS_FILE);
44845
+ throw new HeavyHookSharedConfigError(file2, shared);
44846
+ }
44847
+ } else if (committedCodexHookHoldsCleo(projectDir)) {
44848
+ throw new HeavyHookSharedConfigError(
44849
+ file2,
44850
+ "holds CLEO's hook as the team committed it (removing it is a team change)"
44851
+ );
44852
+ } else if (restoreCommittedCodexHooks(projectDir)) {
44853
+ unexcludeHookFileFromGit(projectDir, CODEX_HOOKS_FILE);
44854
+ return "removed";
44855
+ }
44856
+ const result = await syncJsonHeavyCommandHook(file2, "codex", mode);
44857
+ if (mode === "off") unexcludeHookFileFromGit(projectDir, CODEX_HOOKS_FILE);
44858
+ else excludeHookFileFromGit(projectDir, CODEX_HOOKS_FILE);
44859
+ return result;
44860
+ }
44861
+ async function syncClaudeCodeHeavyCommandHook(projectDir, mode) {
44862
+ const result = await syncJsonHeavyCommandHook(
44863
+ join3(projectDir, CLAUDE_LOCAL_SETTINGS),
44864
+ "claude-code",
44865
+ mode
44866
+ );
44867
+ if (mode === "off") unexcludeLocalSettingsFromGit(projectDir);
44868
+ else excludeLocalSettingsFromGit(projectDir);
44869
+ try {
44870
+ await syncJsonHeavyCommandHook(
44871
+ join3(projectDir, ".claude", "settings.json"),
44872
+ "claude-code",
44873
+ "off"
44874
+ );
44875
+ } catch {
44876
+ }
44877
+ return result;
44878
+ }
44879
+ function opencodeHeavyCommandPluginSource() {
44880
+ return [
44881
+ "// CLEO heavy-command plugin for OpenCode (generated by @cleocode/adapters, T12983).",
44882
+ "// Routes heavy shell commands (tests, builds, typechecks, installs) through",
44883
+ "// `cleo run`, the machine-wide resource budget. Opt out with",
44884
+ '// CLEO_HEAVY_COMMAND_HOOK=off, or set resources.heavyCommandHook to "off" and',
44885
+ "// run `cleo upgrade` (which deletes this file). Fails open: on any error the",
44886
+ "// command runs unchanged.",
44887
+ "",
44888
+ "import { execFile } from 'node:child_process';",
44889
+ "",
44890
+ `const HEAVY = /(?:^|[^A-Za-z0-9_.@+-])(?:${HEAVY_TOOL_WORDS.join("|")})(?=$|[^A-Za-z0-9_.@+-])/;`,
44891
+ "const contexts = new Map();",
44892
+ "",
44893
+ "function ask(payload) {",
44894
+ " return new Promise((done) => {",
44895
+ " const child = execFile(",
44896
+ " 'cleo',",
44897
+ " ['hook', 'heavy-command', '--provider', 'opencode'],",
44898
+ ` { timeout: ${HEAVY_COMMAND_HOOK_TIMEOUT_SEC * 1e3} },`,
44899
+ " (err, stdout) => done(err ? '' : stdout),",
44900
+ " );",
44901
+ " child.on('error', () => done(''));",
44902
+ " child.stdin?.end(JSON.stringify(payload));",
44903
+ " });",
44904
+ "}",
44905
+ "",
44906
+ "export const CleoHeavyCommand = async ({ directory }) => ({",
44907
+ " 'tool.execute.before': async (input, output) => {",
44908
+ " if (input?.tool !== 'bash' || typeof output?.args?.command !== 'string') return;",
44909
+ " if (process.env.CLEO_HEAVY_COMMAND_HOOK === 'off') return;",
44910
+ " if (!HEAVY.test(output.args.command)) return;",
44911
+ " try {",
44912
+ " const text = await ask({ tool_name: 'bash', tool_input: output.args, cwd: directory });",
44913
+ " const answer = JSON.parse(text || '{}');",
44914
+ " if (typeof answer.command === 'string') output.args.command = answer.command;",
44915
+ " if (typeof answer.timeout === 'number') output.args.timeout = answer.timeout;",
44916
+ " if (typeof answer.context === 'string' && input.callID) contexts.set(input.callID, answer.context);",
44917
+ " } catch {",
44918
+ " // Fail open.",
44919
+ " }",
44920
+ " },",
44921
+ " 'tool.execute.after': async (input, output) => {",
44922
+ " const context = input?.callID ? contexts.get(input.callID) : undefined;",
44923
+ " if (context === undefined) return;",
44924
+ " contexts.delete(input.callID);",
44925
+ " if (typeof output?.output === 'string') output.output += '\\n\\n' + context;",
44926
+ " },",
44927
+ "});",
44928
+ ""
44929
+ ].join("\n");
44930
+ }
44931
+ function syncOpencodeHeavyCommandPlugin(projectDir, mode) {
44932
+ const pluginPath = join3(projectDir, OPENCODE_PLUGIN_FILE);
44933
+ const exists = existsSync3(pluginPath);
44934
+ if (mode === "off") {
44935
+ unexcludeHookFileFromGit(projectDir, OPENCODE_PLUGIN_FILE);
44936
+ if (!exists) return "unchanged";
44937
+ rmSync(pluginPath, { force: true });
44938
+ return "removed";
44939
+ }
44940
+ const source = opencodeHeavyCommandPluginSource();
44941
+ const current = exists && readFileSync3(pluginPath, "utf-8") === source;
44942
+ if (!current) {
44943
+ ensureParentDir(pluginPath);
44944
+ writeFileSync(pluginPath, source, "utf-8");
44945
+ }
44946
+ excludeHookFileFromGit(projectDir, OPENCODE_PLUGIN_FILE);
44947
+ return current ? "unchanged" : exists ? "updated" : "installed";
44948
+ }
44949
+ var HEAVY_COMMAND_HOOK_ID, HEAVY_COMMAND_HOOK_TIMEOUT_SEC, OPENCODE_HEAVY_COMMAND_PLUGIN, HEAVY_TOOL_WORDS, HeavyHookPathBlockedError, OLDER_CLEO_HOOK_ANSWER, OLDER_CLEO_MARKER_PREFIX, CLAUDE_LOCAL_SETTINGS, LOCAL_SETTINGS_EXCLUDE_MARKER, HOOK_FILE_EXCLUDE_MARKER, CODEX_HOOKS_FILE, OPENCODE_PLUGIN_FILE, HeavyHookSharedConfigError;
44950
+ var init_heavy_command_hook_install = __esm({
44951
+ "packages/adapters/src/providers/shared/heavy-command-hook-install.ts"() {
44952
+ "use strict";
44953
+ init_hook_config();
44954
+ HEAVY_COMMAND_HOOK_ID = "cleo hook heavy-command";
44955
+ HEAVY_COMMAND_HOOK_TIMEOUT_SEC = 20;
44956
+ OPENCODE_HEAVY_COMMAND_PLUGIN = "cleo-heavy-command.js";
44957
+ HEAVY_TOOL_WORDS = Object.freeze([
44958
+ "vitest",
44959
+ "jest",
44960
+ "mocha",
44961
+ "ava",
44962
+ "tap",
44963
+ "playwright",
44964
+ "pytest",
44965
+ "rspec",
44966
+ "phpunit",
44967
+ "tsc",
44968
+ "tsup",
44969
+ "turbo",
44970
+ "vite",
44971
+ "esbuild",
44972
+ "webpack",
44973
+ "rollup",
44974
+ "next",
44975
+ "nx",
44976
+ "biome",
44977
+ "eslint",
44978
+ "svelte-check",
44979
+ "npm",
44980
+ "pnpm",
44981
+ "yarn",
44982
+ "bun",
44983
+ "npx",
44984
+ "pnpx",
44985
+ "bunx",
44986
+ "cargo",
44987
+ "go"
44988
+ ]);
44989
+ HeavyHookPathBlockedError = class extends Error {
44990
+ /** The existing path that is not a directory. */
44991
+ path;
44992
+ /**
44993
+ * @param path - the existing non-directory path.
44994
+ * @param target - the config file that would have lived under it.
44995
+ */
44996
+ constructor(path, target) {
44997
+ super(`${path} exists but is not a directory, so ${target} cannot be created under it`);
44998
+ this.name = "HeavyHookPathBlockedError";
44999
+ this.path = path;
45000
+ }
45001
+ };
45002
+ OLDER_CLEO_HOOK_ANSWER = JSON.stringify({
45003
+ hookSpecificOutput: {
45004
+ hookEventName: "PreToolUse",
45005
+ additionalContext: "[cleo] This may be a heavy command, but the cleo on PATH predates cleo hook and cleo run, so it runs outside the machine-wide resource budget. Upgrade CLEO to govern it."
45006
+ }
45007
+ });
45008
+ OLDER_CLEO_MARKER_PREFIX = "cleo-hook-unsupported-";
45009
+ CLAUDE_LOCAL_SETTINGS = ".claude/settings.local.json";
45010
+ LOCAL_SETTINGS_EXCLUDE_MARKER = "# cleo-hook: keep the per-machine heavy-command hook settings out of git (T12983)";
45011
+ HOOK_FILE_EXCLUDE_MARKER = "# cleo-hook: keep a per-machine heavy-command hook file out of git (T13124)";
45012
+ CODEX_HOOKS_FILE = ".codex/hooks.json";
45013
+ OPENCODE_PLUGIN_FILE = `.opencode/plugins/${OPENCODE_HEAVY_COMMAND_PLUGIN}`;
45014
+ HeavyHookSharedConfigError = class extends Error {
45015
+ /** The project's own config file. */
45016
+ path;
45017
+ /**
45018
+ * @param path - the config file.
45019
+ * @param why - what makes it the project's own.
45020
+ */
45021
+ constructor(path, why) {
45022
+ super(`${path} ${why}, so CLEO does not write or hide it`);
45023
+ this.name = "HeavyHookSharedConfigError";
45024
+ this.path = path;
45025
+ }
45026
+ };
45027
+ }
45028
+ });
45029
+
44465
45030
  // packages/adapters/src/providers/claude-code/paths.ts
44466
- import { homedir as homedir2 } from "node:os";
44467
- import { join as join3 } from "node:path";
45031
+ import { realpathSync as realpathSync2 } from "node:fs";
45032
+ import { homedir as homedir3 } from "node:os";
45033
+ import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute2, join as join4, relative, resolve as resolve3, sep } from "node:path";
44468
45034
  function claudeSettingsPath() {
44469
- return new ClaudeCodePathProvider().getSettingsPath() ?? join3(homedir2(), ".claude", "settings.json");
45035
+ return new ClaudeCodePathProvider().getSettingsPath() ?? join4(homedir3(), ".claude", "settings.json");
44470
45036
  }
44471
- var ClaudeCodePathProvider;
45037
+ function canonicalPath(path) {
45038
+ const abs = resolve3(path);
45039
+ try {
45040
+ return realpathSync2.native(abs);
45041
+ } catch {
45042
+ const parent = dirname2(abs);
45043
+ return parent === abs ? abs : join4(canonicalPath(parent), basename2(abs));
45044
+ }
45045
+ }
45046
+ function comparable(path) {
45047
+ return CASE_INSENSITIVE_DEFAULT ? path.toLowerCase() : path;
45048
+ }
45049
+ function isAtOrInside(path, dir) {
45050
+ const rel = relative(comparable(dir), comparable(path));
45051
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute2(rel);
45052
+ }
45053
+ function assertProjectScoped(projectDir, target) {
45054
+ const abs = resolve3(target);
45055
+ if (isUserHomeDir(projectDir)) {
45056
+ throw new UserGlobalClaudeConfigError(abs, "the project is the home directory");
45057
+ }
45058
+ const canonical2 = canonicalPath(abs);
45059
+ if (comparable(canonical2) === comparable(canonicalPath(claudeSettingsPath()))) {
45060
+ throw new UserGlobalClaudeConfigError(abs, "it is the user-global Claude settings file");
45061
+ }
45062
+ if (isAtOrInside(canonical2, canonicalPath(new ClaudeCodePathProvider().getProviderDir()))) {
45063
+ throw new UserGlobalClaudeConfigError(abs, "it is inside the user-global Claude config dir");
45064
+ }
45065
+ return abs;
45066
+ }
45067
+ function projectClaudeSettingsPath(projectDir) {
45068
+ return assertProjectScoped(projectDir, join4(projectDir, CLAUDE_LOCAL_SETTINGS));
45069
+ }
45070
+ function projectClaudeHooksDir(projectDir) {
45071
+ return assertProjectScoped(projectDir, join4(projectDir, ".claude", "hooks"));
45072
+ }
45073
+ function assertProjectInstructionScope(projectDir) {
45074
+ assertProjectScoped(projectDir, join4(projectDir, "CLAUDE.md"));
45075
+ }
45076
+ function projectClaudeCommandsDir(projectDir) {
45077
+ return assertProjectScoped(projectDir, join4(projectDir, ".claude", "commands"));
45078
+ }
45079
+ var ClaudeCodePathProvider, CASE_INSENSITIVE_DEFAULT, UserGlobalClaudeConfigError;
44472
45080
  var init_paths = __esm({
44473
45081
  "packages/adapters/src/providers/claude-code/paths.ts"() {
44474
45082
  "use strict";
45083
+ init_heavy_command_hook_install();
44475
45084
  init_hook_config();
44476
45085
  ClaudeCodePathProvider = class {
44477
45086
  /** Get the provider's root configuration directory. */
44478
45087
  getProviderDir() {
44479
- return process.env["CLAUDE_HOME"] ?? join3(homedir2(), ".claude");
45088
+ return process.env["CLAUDE_HOME"] ?? join4(homedir3(), ".claude");
44480
45089
  }
44481
45090
  /** Get the path to the provider's settings file, or null if unavailable. */
44482
45091
  getSettingsPath() {
44483
- return process.env["CLAUDE_SETTINGS"] ?? join3(this.getProviderDir(), "settings.json");
45092
+ return process.env["CLAUDE_SETTINGS"] ?? join4(this.getProviderDir(), "settings.json");
44484
45093
  }
44485
45094
  /** Get the directory where agents are installed, or null if unsupported. */
44486
45095
  getAgentInstallDir() {
44487
- return join3(this.getProviderDir(), "agents");
45096
+ return join4(this.getProviderDir(), "agents");
44488
45097
  }
44489
45098
  /** Get the path to the provider's memory database, or null if unsupported. */
44490
45099
  getMemoryDbPath() {
44491
- return process.env["CLAUDE_MEM_DB"] ?? join3(homedir2(), ".claude-mem", "claude-mem.db");
45100
+ return process.env["CLAUDE_MEM_DB"] ?? join4(homedir3(), ".claude-mem", "claude-mem.db");
45101
+ }
45102
+ };
45103
+ CASE_INSENSITIVE_DEFAULT = process.platform === "darwin" || process.platform === "win32";
45104
+ UserGlobalClaudeConfigError = class extends Error {
45105
+ /** The path that was refused. */
45106
+ path;
45107
+ /**
45108
+ * @param path - the refused target.
45109
+ * @param why - which user-global location it hits.
45110
+ */
45111
+ constructor(path, why) {
45112
+ super(`refusing to write ${path}: ${why}; CLEO only writes project-level Claude Code config`);
45113
+ this.name = "UserGlobalClaudeConfigError";
45114
+ this.path = path;
44492
45115
  }
44493
45116
  };
44494
45117
  }
44495
45118
  });
44496
45119
 
44497
45120
  // packages/adapters/src/providers/claude-code/context-monitor.ts
44498
- import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync } from "node:fs";
45121
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
44499
45122
  import { mkdir } from "node:fs/promises";
44500
- import { homedir as homedir3 } from "node:os";
44501
- import { dirname, join as join4 } from "node:path";
45123
+ import { homedir as homedir4 } from "node:os";
45124
+ import { dirname as dirname3, join as join5 } from "node:path";
44502
45125
  import { resolveLegacyCleoDir } from "@cleocode/paths";
44503
45126
  function getContextStatusFromPercentage(percentage) {
44504
45127
  if (percentage >= THRESHOLDS.EMERGENCY) return "emergency";
@@ -44533,10 +45156,10 @@ var init_context_monitor = __esm({
44533
45156
  const totalTokens = inputTokens + outputTokens + cacheCreate;
44534
45157
  const percentage = Math.floor(totalTokens * 100 / contextSize);
44535
45158
  const status = getContextStatusFromPercentage(percentage);
44536
- const cleoDir = cwd ? join4(cwd, ".cleo") : ".cleo";
44537
- if (existsSync3(cleoDir)) {
44538
- const stateDir = join4(cleoDir, "context-states");
44539
- const statePath = join4(stateDir, ".context-state.json");
45159
+ const cleoDir = cwd ? join5(cwd, ".cleo") : ".cleo";
45160
+ if (existsSync4(cleoDir)) {
45161
+ const stateDir = join5(cleoDir, "context-states");
45162
+ const statePath = join5(stateDir, ".context-state.json");
44540
45163
  const state = {
44541
45164
  $schema: "https://cleo-dev.com/schemas/v1/context-state.schema.json",
44542
45165
  version: "1.0.0",
@@ -44563,8 +45186,8 @@ var init_context_monitor = __esm({
44563
45186
  cleoSessionId: ""
44564
45187
  };
44565
45188
  try {
44566
- await mkdir(dirname(statePath), { recursive: true });
44567
- writeFileSync(statePath, JSON.stringify(state, null, 2));
45189
+ await mkdir(dirname3(statePath), { recursive: true });
45190
+ writeFileSync2(statePath, JSON.stringify(state, null, 2));
44568
45191
  } catch {
44569
45192
  }
44570
45193
  }
@@ -44573,9 +45196,9 @@ var init_context_monitor = __esm({
44573
45196
  /** Check the current statusline integration status in Claude Code settings. */
44574
45197
  checkStatuslineIntegration() {
44575
45198
  const settingsPath = this.pathProvider.getSettingsPath();
44576
- if (!settingsPath || !existsSync3(settingsPath)) return "no_settings";
45199
+ if (!settingsPath || !existsSync4(settingsPath)) return "no_settings";
44577
45200
  try {
44578
- const settings = JSON.parse(readFileSync3(settingsPath, "utf-8"));
45201
+ const settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
44579
45202
  const statusLine = settings.statusLine;
44580
45203
  if (!statusLine?.type) return "not_configured";
44581
45204
  if (statusLine.type !== "command") return "custom_no_cleo";
@@ -44583,10 +45206,10 @@ var init_context_monitor = __esm({
44583
45206
  if (cmd.includes("context-monitor.sh") || cmd.includes("cleo-statusline") || cmd.includes(".context-state.json") || cmd.includes("context-states")) {
44584
45207
  return "configured";
44585
45208
  }
44586
- const scriptPath = cmd.startsWith("~") ? cmd.replace("~", homedir3()) : cmd;
44587
- if (existsSync3(scriptPath)) {
45209
+ const scriptPath = cmd.startsWith("~") ? cmd.replace("~", homedir4()) : cmd;
45210
+ if (existsSync4(scriptPath)) {
44588
45211
  try {
44589
- const content = readFileSync3(scriptPath, "utf-8");
45212
+ const content = readFileSync4(scriptPath, "utf-8");
44590
45213
  if (content.includes("context-state.json")) return "configured";
44591
45214
  } catch {
44592
45215
  }
@@ -44601,7 +45224,7 @@ var init_context_monitor = __esm({
44601
45224
  return {
44602
45225
  statusLine: {
44603
45226
  type: "command",
44604
- command: join4(resolveLegacyCleoDir(), "lib", "session", "context-monitor.sh")
45227
+ command: join5(resolveLegacyCleoDir(), "lib", "session", "context-monitor.sh")
44605
45228
  }
44606
45229
  };
44607
45230
  }
@@ -44622,15 +45245,56 @@ var init_context_monitor = __esm({
44622
45245
  });
44623
45246
 
44624
45247
  // packages/adapters/src/providers/claude-code/hooks.ts
44625
- import { existsSync as existsSync4 } from "node:fs";
45248
+ import { existsSync as existsSync5 } from "node:fs";
44626
45249
  import { readdir, readFile } from "node:fs/promises";
44627
- import { join as join5 } from "node:path";
44628
- import { updateJsonConfigFile } from "@cleocode/caamp";
44629
- var PROVIDER_ID, CLAUDE_CODE_EVENT_MAP, ClaudeCodeHookProvider;
45250
+ import { join as join6 } from "node:path";
45251
+ import { updateJsonConfigFile as updateJsonConfigFile2 } from "@cleocode/caamp";
45252
+ function isNativeHookObject(hook) {
45253
+ return isPlainObject2(hook) && typeof hook.command === "string" && NATIVE_HOOK_COMMANDS.has(hook.command);
45254
+ }
45255
+ function hasNativeHooks(hooks) {
45256
+ return Object.values(hooks).some(
45257
+ (entries) => Array.isArray(entries) && entries.some(
45258
+ (entry) => isPlainObject2(entry) && Array.isArray(entry.hooks) && entry.hooks.some(isNativeHookObject)
45259
+ )
45260
+ );
45261
+ }
45262
+ function removeNativeHooks(hooks) {
45263
+ let changed = false;
45264
+ for (const [event, entries] of Object.entries(hooks)) {
45265
+ if (!Array.isArray(entries)) continue;
45266
+ const kept = [];
45267
+ let eventChanged = false;
45268
+ for (const entry of entries) {
45269
+ if (!isPlainObject2(entry) || !Array.isArray(entry.hooks) || !entry.hooks.some(isNativeHookObject)) {
45270
+ kept.push(entry);
45271
+ continue;
45272
+ }
45273
+ eventChanged = true;
45274
+ const rest = entry.hooks.filter((hook) => !isNativeHookObject(hook));
45275
+ if (rest.length > 0) kept.push({ ...entry, hooks: rest });
45276
+ }
45277
+ if (!eventChanged) continue;
45278
+ changed = true;
45279
+ if (kept.length === 0) delete hooks[event];
45280
+ else hooks[event] = kept;
45281
+ }
45282
+ return changed;
45283
+ }
45284
+ var NATIVE_STOP_HOOK_COMMAND, NATIVE_OBSERVE_HOOK_COMMAND, NATIVE_NEXUS_HOOK_COMMAND, NATIVE_HOOK_COMMANDS, PROVIDER_ID, CLAUDE_CODE_EVENT_MAP, ClaudeCodeHookProvider;
44630
45285
  var init_hooks = __esm({
44631
45286
  "packages/adapters/src/providers/claude-code/hooks.ts"() {
44632
45287
  "use strict";
45288
+ init_heavy_command_hook_install();
44633
45289
  init_paths();
45290
+ NATIVE_STOP_HOOK_COMMAND = "cleo session end --quiet # cleo-hook";
45291
+ NATIVE_OBSERVE_HOOK_COMMAND = 'cleo observe "File modified via $TOOL_NAME" --title "tool-use" --quiet # cleo-hook';
45292
+ NATIVE_NEXUS_HOOK_COMMAND = 'cleo nexus analyze --incremental --json > /dev/null 2>&1 && cleo observe "NEXUS re-indexed after $TOOL_NAME on $TOOL_INPUT_file_path" --title "nexus-post-check" --quiet # cleo-hook';
45293
+ NATIVE_HOOK_COMMANDS = /* @__PURE__ */ new Set([
45294
+ NATIVE_STOP_HOOK_COMMAND,
45295
+ NATIVE_OBSERVE_HOOK_COMMAND,
45296
+ NATIVE_NEXUS_HOOK_COMMAND
45297
+ ]);
44634
45298
  PROVIDER_ID = "claude-code";
44635
45299
  CLAUDE_CODE_EVENT_MAP = {
44636
45300
  // CAAMP: toNative('SessionStart', 'claude-code') = 'SessionStart'
@@ -44685,95 +45349,111 @@ var init_hooks = __esm({
44685
45349
  /**
44686
45350
  * Register native hooks for a project.
44687
45351
  *
44688
- * Writes CLEO hook entries to `~/.claude/settings.json` so that Claude Code's
44689
- * native event system calls cleo CLI commands when events fire. This bridges
44690
- * Claude Code's event loop to CLEO's internal hook dispatch.
45352
+ * Writes CLEO hook entries to the project's per-machine
45353
+ * `<project>/.claude/settings.local.json` so that Claude Code's native event
45354
+ * system calls cleo CLI commands when events fire in this project. This
45355
+ * bridges Claude Code's event loop to CLEO's internal hook dispatch.
45356
+ * CLEO never writes the user-global `~/.claude/settings.json` (T13227): a
45357
+ * project that is the home directory, or whose settings file resolves into
45358
+ * the user-global Claude config, is refused and nothing is written. When
45359
+ * git does not ignore `settings.local.json` yet, a marked `info/exclude`
45360
+ * block keeps it out of git (the T12983 heavy-command rule).
44691
45361
  *
44692
- * Idempotent: skips writing if CLEO hooks already exist in settings.json.
45362
+ * Idempotent: skips writing if the native hooks are already present.
44693
45363
  *
44694
45364
  * Hook entries registered:
44695
45365
  * - `Stop` → `cleo session end --quiet` (triggers LLM extraction, reflector, consolidation)
44696
45366
  * - `PostToolUse` (Write|Edit) → brain observation for file modifications
44697
- * - `SubagentStop` → brain observation for agent completion
45367
+ * and the NEXUS post-check
44698
45368
  *
44699
- * @param projectDir - Project directory for context-scoped hook commands
44700
- * @task T164 @task T555
45369
+ * @param projectDir - Project whose Claude Code settings receive the hooks
45370
+ * @task T164 @task T555 @task T13227
44701
45371
  */
44702
45372
  async registerNativeHooks(projectDir) {
44703
45373
  this.projectDir = projectDir;
44704
45374
  this.registered = true;
44705
- await this.updateSettings((settings) => {
45375
+ const wrote = await this.updateSettings(projectDir, (settings) => {
44706
45376
  const hooks = hookMap(settings);
44707
- if (Object.values(hooks).some((entries) => hasCleoHook(entries))) {
44708
- return false;
44709
- }
45377
+ if (hasNativeHooks(hooks)) return false;
44710
45378
  appendHookEntry(hooks, "Stop", {
44711
45379
  matcher: "",
44712
- hooks: [{ type: "command", command: `cleo session end --quiet # cleo-hook` }]
45380
+ hooks: [{ type: "command", command: NATIVE_STOP_HOOK_COMMAND }]
44713
45381
  });
44714
45382
  appendHookEntry(hooks, "PostToolUse", {
44715
45383
  matcher: "Write|Edit",
44716
45384
  hooks: [
44717
- {
44718
- type: "command",
44719
- command: `cleo observe "File modified via $TOOL_NAME" --title "tool-use" --quiet # cleo-hook`
44720
- },
44721
- {
44722
- // NEXUS post-modification check: re-index changed files and flag regressions.
44723
- // $TOOL_INPUT_file_path is populated by Claude Code for Write/Edit events.
44724
- type: "command",
44725
- command: `cleo nexus analyze --incremental --json > /dev/null 2>&1 && cleo observe "NEXUS re-indexed after $TOOL_NAME on $TOOL_INPUT_file_path" --title "nexus-post-check" --quiet # cleo-hook`
44726
- }
45385
+ { type: "command", command: NATIVE_OBSERVE_HOOK_COMMAND },
45386
+ { type: "command", command: NATIVE_NEXUS_HOOK_COMMAND }
44727
45387
  ]
44728
45388
  });
44729
45389
  return true;
44730
45390
  });
45391
+ if (wrote) excludeLocalSettingsFromGit(projectDir);
44731
45392
  }
44732
45393
  /**
44733
45394
  * Unregister native hooks.
44734
45395
  *
44735
- * Removes CLEO hook entries from Claude Code's `settings.json` by filtering
44736
- * out entries containing the `# cleo-hook` marker. Same locking, atomicity
44737
- * and parse-error rules as {@link registerNativeHooks}.
45396
+ * Removes the hook objects {@link registerNativeHooks} wrote from the
45397
+ * project's `settings.local.json`, and nothing else: the heavy-command hook
45398
+ * and the user's own hooks stay, and an entry or event is dropped only when
45399
+ * left empty. Same locking, atomicity, parse-error and project-scope rules
45400
+ * as {@link registerNativeHooks}.
44738
45401
  *
44739
- * @task T164 @task T555
45402
+ * @task T164 @task T555 @task T13227
44740
45403
  */
44741
45404
  async unregisterNativeHooks() {
45405
+ const projectDir = this.projectDir;
44742
45406
  this.registered = false;
44743
45407
  this.projectDir = null;
44744
- if (!existsSync4(claudeSettingsPath())) return;
44745
- await this.updateSettings((settings) => {
45408
+ if (projectDir === null) return;
45409
+ let settingsPath;
45410
+ try {
45411
+ settingsPath = projectClaudeSettingsPath(projectDir);
45412
+ } catch {
45413
+ return;
45414
+ }
45415
+ if (!existsSync5(settingsPath)) return;
45416
+ await this.updateSettings(projectDir, (settings) => {
44746
45417
  if (settings.hooks === void 0) return false;
44747
- return removeCleoHookEntries(hookMap(settings));
45418
+ return removeNativeHooks(hookMap(settings));
44748
45419
  });
44749
45420
  }
44750
45421
  /** The last settings.json failure, or `null` when the last update succeeded. */
44751
45422
  settingsError = null;
44752
45423
  /**
44753
- * Why the last settings.json update did not happen, or `null`.
45424
+ * Why the last settings update did not happen, or `null`.
44754
45425
  *
44755
45426
  * @remarks
44756
45427
  * Hook registration stays non-fatal for adapter initialisation, so a
44757
- * refused write (malformed settings.json, lock timeout) is surfaced here and
44758
- * on stderr instead of being swallowed (T12385).
45428
+ * refused write (malformed settings file, lock timeout, a user-global
45429
+ * target) is surfaced here and on stderr instead of being swallowed
45430
+ * (T12385, T13227).
44759
45431
  *
44760
45432
  * @returns The failure message, or `null`
44761
45433
  */
44762
45434
  getSettingsError() {
44763
45435
  return this.settingsError;
44764
45436
  }
44765
- /** Apply `mutate` to settings.json via CAAMP's locked, atomic JSON writer. */
44766
- async updateSettings(mutate) {
44767
- const settingsPath = claudeSettingsPath();
45437
+ /**
45438
+ * Apply `mutate` to the project's `settings.local.json` via CAAMP's locked,
45439
+ * atomic JSON writer.
45440
+ *
45441
+ * @returns whether the file was written.
45442
+ */
45443
+ async updateSettings(projectDir, mutate) {
45444
+ let settingsPath = `${projectDir}/.claude/settings.local.json`;
44768
45445
  try {
44769
- await updateJsonConfigFile(settingsPath, mutate);
45446
+ settingsPath = projectClaudeSettingsPath(projectDir);
45447
+ const wrote = await updateJsonConfigFile2(settingsPath, mutate);
44770
45448
  this.settingsError = null;
45449
+ return wrote;
44771
45450
  } catch (err) {
44772
45451
  this.settingsError = err instanceof Error ? err.message : String(err);
44773
45452
  process.stderr.write(
44774
45453
  `[cleo:claude-code] hooks not written to ${settingsPath}: ${this.settingsError}
44775
45454
  `
44776
45455
  );
45456
+ return false;
44777
45457
  }
44778
45458
  }
44779
45459
  /**
@@ -44880,28 +45560,28 @@ var init_hooks = __esm({
44880
45560
  async getTranscript(sessionId, _projectDir) {
44881
45561
  try {
44882
45562
  const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "/root";
44883
- const projectsDir = join5(homeDir, ".claude", "projects");
45563
+ const projectsDir = join6(homeDir, ".claude", "projects");
44884
45564
  let rootFiles = [];
44885
45565
  const subagentFiles = [];
44886
45566
  try {
44887
45567
  const projectDirs = await readdir(projectsDir, { withFileTypes: true });
44888
45568
  for (const projectEntry of projectDirs) {
44889
45569
  if (!projectEntry.isDirectory()) continue;
44890
- const projectDir = join5(projectsDir, projectEntry.name);
45570
+ const projectDir = join6(projectsDir, projectEntry.name);
44891
45571
  try {
44892
45572
  const projectContents = await readdir(projectDir, { withFileTypes: true });
44893
45573
  for (const entry of projectContents) {
44894
45574
  if (entry.isFile() && entry.name.endsWith(".jsonl")) {
44895
- rootFiles.push({ path: join5(projectDir, entry.name) });
45575
+ rootFiles.push({ path: join6(projectDir, entry.name) });
44896
45576
  continue;
44897
45577
  }
44898
45578
  if (entry.isDirectory()) {
44899
- const subagentsDir = join5(projectDir, entry.name, "subagents");
45579
+ const subagentsDir = join6(projectDir, entry.name, "subagents");
44900
45580
  try {
44901
45581
  const subagentEntries = await readdir(subagentsDir);
44902
45582
  for (const sa of subagentEntries) {
44903
45583
  if (sa.startsWith("agent-") && sa.endsWith(".jsonl")) {
44904
- subagentFiles.push({ path: join5(subagentsDir, sa) });
45584
+ subagentFiles.push({ path: join6(subagentsDir, sa) });
44905
45585
  }
44906
45586
  }
44907
45587
  } catch {
@@ -44951,344 +45631,13 @@ var init_hooks = __esm({
44951
45631
  }
44952
45632
  });
44953
45633
 
44954
- // packages/adapters/src/providers/shared/heavy-command-hook-install.ts
44955
- import { execFileSync } from "node:child_process";
44956
- import {
44957
- existsSync as existsSync5,
44958
- mkdirSync,
44959
- readdirSync as readdirSync2,
44960
- readFileSync as readFileSync4,
44961
- realpathSync,
44962
- rmSync,
44963
- writeFileSync as writeFileSync2
44964
- } from "node:fs";
44965
- import { homedir as homedir4, tmpdir } from "node:os";
44966
- import { dirname as dirname2, isAbsolute, join as join6, resolve as resolve2 } from "node:path";
44967
- import { updateJsonConfigFile as updateJsonConfigFile2 } from "@cleocode/caamp";
44968
- function canonical(path) {
44969
- try {
44970
- return realpathSync(path);
44971
- } catch {
44972
- return resolve2(path);
44973
- }
44974
- }
44975
- function isUserHomeDir(projectDir, home = homedir4()) {
44976
- return canonical(projectDir) === canonical(home);
44977
- }
44978
- function clearOlderCleoMarkers(dir = process.env.XDG_RUNTIME_DIR || tmpdir()) {
44979
- let removed = 0;
44980
- let names;
44981
- try {
44982
- names = readdirSync2(dir);
44983
- } catch {
44984
- return 0;
44985
- }
44986
- for (const name15 of names) {
44987
- if (!name15.startsWith(OLDER_CLEO_MARKER_PREFIX)) continue;
44988
- try {
44989
- rmSync(join6(dir, name15), { force: true });
44990
- removed++;
44991
- } catch {
44992
- }
44993
- }
44994
- return removed;
44995
- }
44996
- function shellWord(text) {
44997
- return `'${text.replace(/'/g, `'\\''`)}'`;
44998
- }
44999
- function heavyCommandGuardScript(provider) {
45000
- const words = HEAVY_TOOL_WORDS.join("|");
45001
- const old = `printf '%s\\n' '${OLDER_CLEO_HOOK_ANSWER}'`;
45002
- return [
45003
- "command -v cleo >/dev/null 2>&1 || exit 0",
45004
- 'in="$(cat)"',
45005
- "set -f",
45006
- "hit=",
45007
- // `[\]` (not `\\`) matches a backslash: it survives fish's quoting under Codex.
45008
- `for w in $(printf '%s' "$in" | sed 's/[\\][nrt]/ /g; s/[^A-Za-z0-9_./@+-]/ /g'); do case "\${w##*/}" in ${words}) hit=1; break;; esac; done`,
45009
- '[ -n "$hit" ] || exit 0',
45010
- 'p="$(command -v cleo)"',
45011
- `d="\${XDG_RUNTIME_DIR:-\${TMPDIR:-/tmp}}"`,
45012
- `k="$d/${OLDER_CLEO_MARKER_PREFIX}$(printf '%s|%s' "$p" "$PWD" | cksum | tr -c '0-9' '-')"`,
45013
- `if [ -f "$k" ] && [ ! -L "$k" ] && [ -O "$k" ] && ! [ "$p" -nt "$k" ] && [ -n "$(find "$k" -mmin -60 2>/dev/null)" ]; then ${old}; exit 0; fi`,
45014
- 'e="$(mktemp 2>/dev/null)" || e=',
45015
- // Only the older-CLEO signature (exit 127, "Unknown command hook") writes the
45016
- // marker; a crash or kill of a current CLEO fails open and asks again next time.
45017
- `if out="$(printf '%s' "$in" | ${HEAVY_COMMAND_HOOK_ID} --provider ${provider} 2>"\${e:-/dev/null}")"; then case "$out" in "{"*) printf '%s\\n' "$out";; esac; else rc=$?; if [ "$rc" = 127 ] && [ -n "$e" ] && grep -q 'Unknown command' "$e" 2>/dev/null; then t="$(mktemp "$k.XXXXXX" 2>/dev/null)" && mv -f "$t" "$k"; ${old}; fi; fi`,
45018
- '[ -z "$e" ] || rm -f "$e"',
45019
- "exit 0"
45020
- ].join("; ");
45021
- }
45022
- function heavyCommandHookCommand(provider) {
45023
- const script = heavyCommandGuardScript(provider);
45024
- const line = provider === "codex" ? `/bin/sh -c ${shellWord(script)}` : script;
45025
- return `${line} ${CLEO_HOOK_MARKER}`;
45026
- }
45027
- function heavyCommandHookObject(provider) {
45028
- return {
45029
- type: "command",
45030
- command: heavyCommandHookCommand(provider),
45031
- timeout: HEAVY_COMMAND_HOOK_TIMEOUT_SEC
45032
- };
45033
- }
45034
- function heavyCommandHookEntry(provider) {
45035
- return { matcher: "Bash", hooks: [heavyCommandHookObject(provider)] };
45036
- }
45037
- function isHeavyHookObject(hook) {
45038
- return isPlainObject2(hook) && typeof hook.command === "string" && hook.command.includes(CLEO_HOOK_MARKER) && hook.command.includes(HEAVY_COMMAND_HOOK_ID);
45039
- }
45040
- function placeHeavyHook(hooks, desired) {
45041
- const groups = hooks.PreToolUse;
45042
- if (groups === void 0) return { changed: false, found: false };
45043
- if (!Array.isArray(groups)) {
45044
- throw new Error('hooks "PreToolUse" is not an array; not modifying it');
45045
- }
45046
- let changed = false;
45047
- let found = false;
45048
- const next = [];
45049
- for (const group of groups) {
45050
- if (!isPlainObject2(group) || !Array.isArray(group.hooks)) {
45051
- next.push(group);
45052
- continue;
45053
- }
45054
- const kept = [];
45055
- let groupChanged = false;
45056
- for (const hook of group.hooks) {
45057
- if (!isHeavyHookObject(hook)) {
45058
- kept.push(hook);
45059
- } else if (desired !== null && !found) {
45060
- found = true;
45061
- if (JSON.stringify(hook) !== JSON.stringify(desired)) groupChanged = true;
45062
- kept.push(desired);
45063
- } else {
45064
- groupChanged = true;
45065
- }
45066
- }
45067
- if (!groupChanged) {
45068
- next.push(group);
45069
- continue;
45070
- }
45071
- changed = true;
45072
- if (kept.length > 0) next.push({ ...group, hooks: kept });
45073
- }
45074
- if (changed) hooks.PreToolUse = next;
45075
- return { changed, found };
45076
- }
45077
- async function syncJsonHeavyCommandHook(configPath, provider, mode) {
45078
- if (mode === "off") {
45079
- if (!existsSync5(configPath)) return "unchanged";
45080
- const removed = await updateJsonConfigFile2(configPath, (config2) => {
45081
- if (config2.hooks === void 0) return false;
45082
- return placeHeavyHook(hookMap(config2), null).changed;
45083
- });
45084
- return removed ? "removed" : "unchanged";
45085
- }
45086
- mkdirSync(dirname2(configPath), { recursive: true });
45087
- clearOlderCleoMarkers();
45088
- let result = "unchanged";
45089
- await updateJsonConfigFile2(configPath, (config2) => {
45090
- const hooks = hookMap(config2);
45091
- const placed = placeHeavyHook(hooks, heavyCommandHookObject(provider));
45092
- if (placed.found) {
45093
- if (placed.changed) result = "updated";
45094
- return placed.changed;
45095
- }
45096
- const existing = hooks.PreToolUse;
45097
- hooks.PreToolUse = [
45098
- ...Array.isArray(existing) ? existing : [],
45099
- heavyCommandHookEntry(provider)
45100
- ];
45101
- result = "installed";
45102
- return true;
45103
- });
45104
- return result;
45105
- }
45106
- function gitOutput(projectDir, args) {
45107
- try {
45108
- return execFileSync("git", ["-C", projectDir, ...args], {
45109
- encoding: "utf-8",
45110
- stdio: ["ignore", "pipe", "ignore"],
45111
- timeout: 5e3
45112
- }).trim();
45113
- } catch {
45114
- return null;
45115
- }
45116
- }
45117
- function excludeFile(projectDir) {
45118
- if (gitOutput(projectDir, ["rev-parse", "--is-inside-work-tree"]) !== "true") return null;
45119
- const path = gitOutput(projectDir, ["rev-parse", "--git-path", "info/exclude"]);
45120
- if (path === null || path === "") return null;
45121
- return isAbsolute(path) ? path : join6(projectDir, path);
45122
- }
45123
- function excludeLine(projectDir) {
45124
- const prefix = gitOutput(projectDir, ["rev-parse", "--show-prefix"]) ?? "";
45125
- return `/${prefix}${CLAUDE_LOCAL_SETTINGS}`;
45126
- }
45127
- function excludeLocalSettingsFromGit(projectDir) {
45128
- const exclude = excludeFile(projectDir);
45129
- if (exclude === null) return false;
45130
- if (gitOutput(projectDir, ["check-ignore", CLAUDE_LOCAL_SETTINGS]) !== null) return false;
45131
- const line = excludeLine(projectDir);
45132
- const text = existsSync5(exclude) ? readFileSync4(exclude, "utf-8") : "";
45133
- if (text.split("\n").includes(line)) return false;
45134
- const block = `${LOCAL_SETTINGS_EXCLUDE_MARKER}
45135
- ${line}
45136
- `;
45137
- mkdirSync(dirname2(exclude), { recursive: true });
45138
- writeFileSync2(exclude, text === "" || text.endsWith("\n") ? text + block : `${text}
45139
- ${block}`);
45140
- return true;
45141
- }
45142
- function unexcludeLocalSettingsFromGit(projectDir) {
45143
- const exclude = excludeFile(projectDir);
45144
- if (exclude === null || !existsSync5(exclude)) return false;
45145
- const lines = readFileSync4(exclude, "utf-8").split("\n");
45146
- const at = lines.indexOf(excludeLine(projectDir));
45147
- if (at < 1 || lines[at - 1] !== LOCAL_SETTINGS_EXCLUDE_MARKER) return false;
45148
- lines.splice(at - 1, 2);
45149
- writeFileSync2(exclude, lines.join("\n"));
45150
- return true;
45151
- }
45152
- async function syncClaudeCodeHeavyCommandHook(projectDir, mode) {
45153
- const result = await syncJsonHeavyCommandHook(
45154
- join6(projectDir, CLAUDE_LOCAL_SETTINGS),
45155
- "claude-code",
45156
- mode
45157
- );
45158
- if (mode === "off") unexcludeLocalSettingsFromGit(projectDir);
45159
- else excludeLocalSettingsFromGit(projectDir);
45160
- await syncJsonHeavyCommandHook(
45161
- join6(projectDir, ".claude", "settings.json"),
45162
- "claude-code",
45163
- "off"
45164
- );
45165
- return result;
45166
- }
45167
- function opencodeHeavyCommandPluginSource() {
45168
- return [
45169
- "// CLEO heavy-command plugin for OpenCode (generated by @cleocode/adapters, T12983).",
45170
- "// Routes heavy shell commands (tests, builds, typechecks, installs) through",
45171
- "// `cleo run`, the machine-wide resource budget. Opt out with",
45172
- '// CLEO_HEAVY_COMMAND_HOOK=off, or set resources.heavyCommandHook to "off" and',
45173
- "// run `cleo upgrade` (which deletes this file). Fails open: on any error the",
45174
- "// command runs unchanged.",
45175
- "",
45176
- "import { execFile } from 'node:child_process';",
45177
- "",
45178
- `const HEAVY = /(?:^|[^A-Za-z0-9_.@+-])(?:${HEAVY_TOOL_WORDS.join("|")})(?=$|[^A-Za-z0-9_.@+-])/;`,
45179
- "const contexts = new Map();",
45180
- "",
45181
- "function ask(payload) {",
45182
- " return new Promise((done) => {",
45183
- " const child = execFile(",
45184
- " 'cleo',",
45185
- " ['hook', 'heavy-command', '--provider', 'opencode'],",
45186
- ` { timeout: ${HEAVY_COMMAND_HOOK_TIMEOUT_SEC * 1e3} },`,
45187
- " (err, stdout) => done(err ? '' : stdout),",
45188
- " );",
45189
- " child.on('error', () => done(''));",
45190
- " child.stdin?.end(JSON.stringify(payload));",
45191
- " });",
45192
- "}",
45193
- "",
45194
- "export const CleoHeavyCommand = async ({ directory }) => ({",
45195
- " 'tool.execute.before': async (input, output) => {",
45196
- " if (input?.tool !== 'bash' || typeof output?.args?.command !== 'string') return;",
45197
- " if (process.env.CLEO_HEAVY_COMMAND_HOOK === 'off') return;",
45198
- " if (!HEAVY.test(output.args.command)) return;",
45199
- " try {",
45200
- " const text = await ask({ tool_name: 'bash', tool_input: output.args, cwd: directory });",
45201
- " const answer = JSON.parse(text || '{}');",
45202
- " if (typeof answer.command === 'string') output.args.command = answer.command;",
45203
- " if (typeof answer.timeout === 'number') output.args.timeout = answer.timeout;",
45204
- " if (typeof answer.context === 'string' && input.callID) contexts.set(input.callID, answer.context);",
45205
- " } catch {",
45206
- " // Fail open.",
45207
- " }",
45208
- " },",
45209
- " 'tool.execute.after': async (input, output) => {",
45210
- " const context = input?.callID ? contexts.get(input.callID) : undefined;",
45211
- " if (context === undefined) return;",
45212
- " contexts.delete(input.callID);",
45213
- " if (typeof output?.output === 'string') output.output += '\\n\\n' + context;",
45214
- " },",
45215
- "});",
45216
- ""
45217
- ].join("\n");
45218
- }
45219
- function syncOpencodeHeavyCommandPlugin(projectDir, mode) {
45220
- const pluginPath = join6(projectDir, ".opencode", "plugins", OPENCODE_HEAVY_COMMAND_PLUGIN);
45221
- const exists = existsSync5(pluginPath);
45222
- if (mode === "off") {
45223
- if (!exists) return "unchanged";
45224
- rmSync(pluginPath, { force: true });
45225
- return "removed";
45226
- }
45227
- const source = opencodeHeavyCommandPluginSource();
45228
- if (exists && readFileSync4(pluginPath, "utf-8") === source) return "unchanged";
45229
- mkdirSync(dirname2(pluginPath), { recursive: true });
45230
- writeFileSync2(pluginPath, source, "utf-8");
45231
- return exists ? "updated" : "installed";
45232
- }
45233
- var HEAVY_COMMAND_HOOK_ID, HEAVY_COMMAND_HOOK_TIMEOUT_SEC, OPENCODE_HEAVY_COMMAND_PLUGIN, HEAVY_TOOL_WORDS, OLDER_CLEO_HOOK_ANSWER, OLDER_CLEO_MARKER_PREFIX, CLAUDE_LOCAL_SETTINGS, LOCAL_SETTINGS_EXCLUDE_MARKER;
45234
- var init_heavy_command_hook_install = __esm({
45235
- "packages/adapters/src/providers/shared/heavy-command-hook-install.ts"() {
45236
- "use strict";
45237
- init_hook_config();
45238
- HEAVY_COMMAND_HOOK_ID = "cleo hook heavy-command";
45239
- HEAVY_COMMAND_HOOK_TIMEOUT_SEC = 20;
45240
- OPENCODE_HEAVY_COMMAND_PLUGIN = "cleo-heavy-command.js";
45241
- HEAVY_TOOL_WORDS = Object.freeze([
45242
- "vitest",
45243
- "jest",
45244
- "mocha",
45245
- "ava",
45246
- "tap",
45247
- "playwright",
45248
- "pytest",
45249
- "rspec",
45250
- "phpunit",
45251
- "tsc",
45252
- "tsup",
45253
- "turbo",
45254
- "vite",
45255
- "esbuild",
45256
- "webpack",
45257
- "rollup",
45258
- "next",
45259
- "nx",
45260
- "biome",
45261
- "eslint",
45262
- "svelte-check",
45263
- "npm",
45264
- "pnpm",
45265
- "yarn",
45266
- "bun",
45267
- "npx",
45268
- "pnpx",
45269
- "bunx",
45270
- "cargo",
45271
- "go"
45272
- ]);
45273
- OLDER_CLEO_HOOK_ANSWER = JSON.stringify({
45274
- hookSpecificOutput: {
45275
- hookEventName: "PreToolUse",
45276
- additionalContext: "[cleo] This may be a heavy command, but the cleo on PATH predates cleo hook and cleo run, so it runs outside the machine-wide resource budget. Upgrade CLEO to govern it."
45277
- }
45278
- });
45279
- OLDER_CLEO_MARKER_PREFIX = "cleo-hook-unsupported-";
45280
- CLAUDE_LOCAL_SETTINGS = ".claude/settings.local.json";
45281
- LOCAL_SETTINGS_EXCLUDE_MARKER = "# cleo-hook: keep the per-machine heavy-command hook settings out of git (T12983)";
45282
- }
45283
- });
45284
-
45285
45634
  // packages/adapters/src/providers/shared/hook-template-installer.ts
45286
- import { copyFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2, statSync } from "node:fs";
45287
- import { dirname as dirname3, join as join7 } from "node:path";
45635
+ import { copyFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2, statSync as statSync2 } from "node:fs";
45636
+ import { dirname as dirname4, join as join7 } from "node:path";
45288
45637
  import { fileURLToPath } from "node:url";
45289
45638
  function resolveProviderCandidates() {
45290
- const thisDir = dirname3(fileURLToPath(import.meta.url));
45291
- const here = dirname3(thisDir);
45639
+ const thisDir = dirname4(fileURLToPath(import.meta.url));
45640
+ const here = dirname4(thisDir);
45292
45641
  const fromDist = join7(here, "..", "..", "src", "providers");
45293
45642
  return [here, fromDist];
45294
45643
  }
@@ -45311,13 +45660,13 @@ function copyTemplate(src, dest, force) {
45311
45660
  throw new Error(`CLEO hook template missing at source: ${src}`);
45312
45661
  }
45313
45662
  if (!force && existsSync6(dest)) {
45314
- const srcStat = statSync(src);
45315
- const destStat = statSync(dest);
45663
+ const srcStat = statSync2(src);
45664
+ const destStat = statSync2(dest);
45316
45665
  if (srcStat.size === destStat.size && srcStat.mtimeMs <= destStat.mtimeMs) {
45317
45666
  return { wrote: false };
45318
45667
  }
45319
45668
  }
45320
- mkdirSync2(dirname3(dest), { recursive: true });
45669
+ mkdirSync2(dirname4(dest), { recursive: true });
45321
45670
  copyFileSync(src, dest);
45322
45671
  return { wrote: true };
45323
45672
  }
@@ -45363,25 +45712,28 @@ var init_hook_template_installer = __esm({
45363
45712
 
45364
45713
  // packages/adapters/src/providers/claude-code/install.ts
45365
45714
  import { copyFileSync as copyFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readFileSync as readFileSync5 } from "node:fs";
45366
- import { homedir as homedir5 } from "node:os";
45367
- import { dirname as dirname4, join as join8, resolve as resolve3 } from "node:path";
45715
+ import { dirname as dirname5, join as join8, resolve as resolve4 } from "node:path";
45368
45716
  import { fileURLToPath as fileURLToPath2 } from "node:url";
45369
45717
  import { ensureProviderInstructionFile, updateJsonConfigFile as updateJsonConfigFile3 } from "@cleocode/caamp";
45370
- function getClaudeConfigDir() {
45371
- return process.env["CLAUDE_HOME"] ?? join8(homedir5(), ".claude");
45372
- }
45373
45718
  function getAdapterCommandsDir() {
45374
- const thisDir = dirname4(fileURLToPath2(import.meta.url));
45719
+ const thisDir = dirname5(fileURLToPath2(import.meta.url));
45375
45720
  return join8(thisDir, "commands");
45376
45721
  }
45377
- var ClaudeCodeInstallProvider;
45722
+ var CLEO_PLUGIN_KEY, PROJECT_HOOK_FILES, ClaudeCodeInstallProvider;
45378
45723
  var init_install = __esm({
45379
45724
  "packages/adapters/src/providers/claude-code/install.ts"() {
45380
45725
  "use strict";
45381
45726
  init_heavy_command_hook_install();
45382
45727
  init_hook_template_installer();
45383
45728
  init_paths();
45729
+ CLEO_PLUGIN_KEY = "cleo@cleocode";
45730
+ PROJECT_HOOK_FILES = [
45731
+ ".claude/hooks/precompact-safestop.sh",
45732
+ ".claude/hooks/cleo-precompact-core.sh"
45733
+ ];
45384
45734
  ClaudeCodeInstallProvider = class {
45735
+ /** The project the last {@link install} ran for; {@link isInstalled} reads its settings. */
45736
+ projectDir = null;
45385
45737
  /**
45386
45738
  * Install CLEO into a Claude Code project.
45387
45739
  *
@@ -45390,38 +45742,52 @@ var init_install = __esm({
45390
45742
  */
45391
45743
  async install(options) {
45392
45744
  const { projectDir } = options;
45745
+ this.projectDir = projectDir;
45393
45746
  const installedAt = (/* @__PURE__ */ new Date()).toISOString();
45394
45747
  let instructionFileUpdated = false;
45395
45748
  const details = {};
45396
- const instructionResult = await ensureProviderInstructionFile("claude-code", projectDir, {});
45397
- instructionFileUpdated = instructionResult.action !== "intact";
45398
- if (instructionFileUpdated) {
45399
- details.instructionFile = instructionResult.filePath;
45749
+ try {
45750
+ assertProjectInstructionScope(projectDir);
45751
+ const instructionResult = await ensureProviderInstructionFile("claude-code", projectDir, {});
45752
+ instructionFileUpdated = instructionResult.action !== "intact";
45753
+ if (instructionFileUpdated) {
45754
+ details.instructionFile = instructionResult.filePath;
45755
+ }
45756
+ } catch (err) {
45757
+ if (!(err instanceof UserGlobalClaudeConfigError)) throw err;
45758
+ details.instructionFile = "skipped";
45400
45759
  }
45401
- const commandsInstalled = this.installCommands(projectDir);
45402
- if (commandsInstalled.length > 0) {
45403
- details.commands = commandsInstalled;
45760
+ try {
45761
+ const commandsInstalled = this.installCommands(projectDir);
45762
+ if (commandsInstalled.length > 0) {
45763
+ details.commands = commandsInstalled;
45764
+ }
45765
+ } catch (err) {
45766
+ if (!(err instanceof UserGlobalClaudeConfigError)) throw err;
45767
+ details.commands = "skipped";
45404
45768
  }
45405
45769
  const settingsErrors = [];
45406
45770
  try {
45407
- const pluginResult = await this.registerPlugin();
45771
+ const pluginResult = await this.registerPlugin(projectDir);
45408
45772
  if (pluginResult) {
45409
45773
  details.plugin = pluginResult;
45410
45774
  }
45411
45775
  } catch (err) {
45412
- settingsErrors.push(err instanceof Error ? err.message : String(err));
45776
+ if (err instanceof UserGlobalClaudeConfigError) details.plugin = "skipped";
45777
+ else settingsErrors.push(err instanceof Error ? err.message : String(err));
45413
45778
  }
45414
45779
  try {
45415
- const hookResult = await this.installHookTemplates();
45780
+ const hookResult = await this.installHookTemplates(projectDir);
45416
45781
  if (hookResult) {
45417
45782
  details.hookTemplates = hookResult;
45418
45783
  }
45419
45784
  } catch (err) {
45420
- settingsErrors.push(err instanceof Error ? err.message : String(err));
45785
+ if (err instanceof UserGlobalClaudeConfigError) details.hookTemplates = "skipped";
45786
+ else settingsErrors.push(err instanceof Error ? err.message : String(err));
45421
45787
  }
45422
45788
  if (options.heavyCommandHook !== void 0) {
45423
45789
  const projectSettings = join8(projectDir, ".claude", "settings.json");
45424
- if (isUserHomeDir(projectDir) || resolve3(projectSettings) === resolve3(claudeSettingsPath())) {
45790
+ if (isUserHomeDir(projectDir) || resolve4(projectSettings) === resolve4(claudeSettingsPath())) {
45425
45791
  details.heavyCommandHook = "skipped";
45426
45792
  } else {
45427
45793
  try {
@@ -45454,16 +45820,25 @@ var init_install = __esm({
45454
45820
  /**
45455
45821
  * Check whether CLEO is installed in the current environment.
45456
45822
  *
45457
- * Checks for plugin enabled in ~/.claude/settings.json.
45823
+ * Checks for the plugin enabled in the project's
45824
+ * `.claude/settings.local.json` — the project of the last {@link install},
45825
+ * else the working directory (T13227).
45458
45826
  */
45459
45827
  async isInstalled() {
45460
- const settingsPath = claudeSettingsPath();
45828
+ let settingsPath;
45829
+ try {
45830
+ settingsPath = projectClaudeSettingsPath(this.projectDir ?? process.cwd());
45831
+ } catch {
45832
+ return false;
45833
+ }
45461
45834
  if (existsSync7(settingsPath)) {
45462
45835
  try {
45463
45836
  const settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
45464
- const plugins = settings.enabledPlugins;
45465
- if (plugins && plugins["cleo@cleocode"] === true) {
45466
- return true;
45837
+ if (isPlainObject2(settings)) {
45838
+ const plugins = settings.enabledPlugins;
45839
+ if (isPlainObject2(plugins) && plugins[CLEO_PLUGIN_KEY] === true) {
45840
+ return true;
45841
+ }
45467
45842
  }
45468
45843
  } catch {
45469
45844
  }
@@ -45477,8 +45852,11 @@ var init_install = __esm({
45477
45852
  * and default references from the provider registry (T1919).
45478
45853
  *
45479
45854
  * @param projectDir - Project root directory
45855
+ * @throws {@link UserGlobalClaudeConfigError} when the project root is inside
45856
+ * the user-global Claude config dir (T13227); nothing is written.
45480
45857
  */
45481
45858
  async ensureInstructionReferences(projectDir) {
45859
+ assertProjectInstructionScope(projectDir);
45482
45860
  await ensureProviderInstructionFile("claude-code", projectDir, {});
45483
45861
  }
45484
45862
  /**
@@ -45489,13 +45867,14 @@ var init_install = __esm({
45489
45867
  *
45490
45868
  * @param projectDir - Project root directory
45491
45869
  * @returns Array of installed command filenames
45870
+ * @throws {@link UserGlobalClaudeConfigError} when the target is user-global
45492
45871
  */
45493
45872
  installCommands(projectDir) {
45494
45873
  const adapterCommandsDir = getAdapterCommandsDir();
45495
45874
  if (!existsSync7(adapterCommandsDir)) {
45496
45875
  return [];
45497
45876
  }
45498
- const targetDir = join8(projectDir, ".claude", "commands");
45877
+ const targetDir = projectClaudeCommandsDir(projectDir);
45499
45878
  mkdirSync3(targetDir, { recursive: true });
45500
45879
  const installed = [];
45501
45880
  const files = readdirSync3(adapterCommandsDir).filter((f) => f.endsWith(".md"));
@@ -45508,13 +45887,17 @@ var init_install = __esm({
45508
45887
  return installed;
45509
45888
  }
45510
45889
  /**
45511
- * Register the CLEO brain plugin in ~/.claude/settings.json.
45890
+ * Register the CLEO brain plugin in the project's
45891
+ * `.claude/settings.local.json` (T13227: never the user-global settings).
45512
45892
  *
45893
+ * @param projectDir - Project root directory
45513
45894
  * @returns Description of what was registered, or null if no change needed
45895
+ * @throws {@link UserGlobalClaudeConfigError} when the target is user-global
45514
45896
  */
45515
- async registerPlugin() {
45516
- const pluginKey = "cleo@cleocode";
45517
- const wrote = await updateJsonConfigFile3(claudeSettingsPath(), (settings) => {
45897
+ async registerPlugin(projectDir) {
45898
+ const pluginKey = CLEO_PLUGIN_KEY;
45899
+ const settingsPath = projectClaudeSettingsPath(projectDir);
45900
+ const wrote = await updateJsonConfigFile3(settingsPath, (settings) => {
45518
45901
  const enabledPlugins = settings.enabledPlugins ?? {};
45519
45902
  if (!isPlainObject2(enabledPlugins)) {
45520
45903
  throw new Error('settings.json "enabledPlugins" is not an object; not modifying it');
@@ -45529,31 +45912,38 @@ var init_install = __esm({
45529
45912
  settings.enabledPlugins = enabledPlugins;
45530
45913
  return true;
45531
45914
  });
45532
- return wrote ? `Enabled ${pluginKey} in ~/.claude/settings.json` : null;
45915
+ if (!wrote) return null;
45916
+ excludeLocalSettingsFromGit(projectDir);
45917
+ return `Enabled ${pluginKey} in ${settingsPath}`;
45533
45918
  }
45534
45919
  /**
45535
45920
  * Install the CLEO PreCompact hook templates for Claude Code (T1013).
45536
45921
  *
45537
- * Writes two files to `~/.claude/hooks/`:
45922
+ * Writes two files to the project's `.claude/hooks/`:
45538
45923
  * 1. `cleo-precompact-core.sh` — universal CLEO safestop helper (shared
45539
45924
  * across all providers; sourced by the provider-specific shim).
45540
45925
  * 2. `precompact-safestop.sh` — Claude-Code-flavoured wrapper that invokes
45541
45926
  * `cleo memory precompact-flush` and `cleo safestop`.
45542
45927
  *
45543
- * Also registers a `PreCompact` entry in `~/.claude/settings.json` so Claude
45544
- * Code runs the hook when auto-compact fires (at 95% context).
45928
+ * Also registers a `PreCompact` entry in the project's
45929
+ * `.claude/settings.local.json` so Claude Code runs the hook when
45930
+ * auto-compact fires (at 95% context). Files git does not ignore yet are
45931
+ * kept out of it with a marked `info/exclude` block. Nothing is written to
45932
+ * the user-global Claude config (T13227).
45545
45933
  *
45546
45934
  * Idempotent: subsequent installs skip unchanged files and do not duplicate
45547
- * the settings.json hook entry.
45935
+ * the settings hook entry.
45548
45936
  *
45937
+ * @param projectDir - Project root directory
45549
45938
  * @returns Install summary (paths written + config change description), or
45550
45939
  * `null` when no change was required.
45940
+ * @throws {@link UserGlobalClaudeConfigError} when the target is user-global
45551
45941
  *
45552
- * @task T1013
45942
+ * @task T1013 @task T13227
45553
45943
  */
45554
- async installHookTemplates() {
45555
- const claudeDir = getClaudeConfigDir();
45556
- const hooksDir = join8(claudeDir, "hooks");
45944
+ async installHookTemplates(projectDir) {
45945
+ const hooksDir = projectClaudeHooksDir(projectDir);
45946
+ const settingsPath = projectClaudeSettingsPath(projectDir);
45557
45947
  let templates;
45558
45948
  try {
45559
45949
  templates = installProviderHookTemplates({
@@ -45563,30 +45953,35 @@ var init_install = __esm({
45563
45953
  } catch {
45564
45954
  return null;
45565
45955
  }
45956
+ for (const file2 of PROJECT_HOOK_FILES) excludeHookFileFromGit(projectDir, file2);
45566
45957
  const settingsEntryAdded = await this.registerPreCompactHook(
45958
+ settingsPath,
45567
45959
  join8(hooksDir, "precompact-safestop.sh")
45568
45960
  );
45961
+ if (settingsEntryAdded) excludeLocalSettingsFromGit(projectDir);
45569
45962
  if (templates.installedFiles.length === 0 && !settingsEntryAdded) {
45570
45963
  return null;
45571
45964
  }
45572
45965
  return { templates, settingsEntryAdded };
45573
45966
  }
45574
45967
  /**
45575
- * Register the PreCompact hook command in `~/.claude/settings.json`.
45968
+ * Register the PreCompact hook command in the project's
45969
+ * `.claude/settings.local.json`.
45576
45970
  *
45577
45971
  * The Claude Code native event name for the canonical `PreCompact` event is
45578
45972
  * `PreCompact` (identity mapping — see `hook-mappings.json`). The entry is
45579
45973
  * tagged with `# cleo-hook` so the uninstall flow can identify and remove
45580
45974
  * our additions without touching user-authored hooks.
45581
45975
  *
45976
+ * @param settingsPath - The project settings file (already scope-checked).
45582
45977
  * @param shimPath - Absolute path to the installed `precompact-safestop.sh`.
45583
45978
  * @returns `true` when a new hook entry was written, `false` when an
45584
45979
  * equivalent entry was already present.
45585
45980
  *
45586
45981
  * @task T1013
45587
45982
  */
45588
- async registerPreCompactHook(shimPath) {
45589
- return updateJsonConfigFile3(claudeSettingsPath(), (settings) => {
45983
+ async registerPreCompactHook(settingsPath, shimPath) {
45984
+ return updateJsonConfigFile3(settingsPath, (settings) => {
45590
45985
  const hooks = hookMap(settings);
45591
45986
  if (hasCleoHook(hooks.PreCompact, "precompact-safestop.sh")) {
45592
45987
  return false;
@@ -45709,7 +46104,7 @@ var init_agent_spawn_wrapper = __esm({
45709
46104
  // packages/adapters/src/providers/shared/prompt-file.ts
45710
46105
  import { mkdtemp, rm, writeFile } from "node:fs/promises";
45711
46106
  import { tmpdir as tmpdir2 } from "node:os";
45712
- import { dirname as dirname5, join as join9 } from "node:path";
46107
+ import { dirname as dirname6, join as join9 } from "node:path";
45713
46108
  async function writeSpawnPromptFile(prefix, prompt) {
45714
46109
  const dir = await mkdtemp(join9(tmpdir2(), `cleo-${prefix}-`));
45715
46110
  const file2 = join9(dir, "prompt.txt");
@@ -45717,7 +46112,7 @@ async function writeSpawnPromptFile(prefix, prompt) {
45717
46112
  return file2;
45718
46113
  }
45719
46114
  async function removeSpawnPromptFile(file2) {
45720
- await rm(dirname5(file2), { recursive: true, force: true }).catch(() => void 0);
46115
+ await rm(dirname6(file2), { recursive: true, force: true }).catch(() => void 0);
45721
46116
  }
45722
46117
  var init_prompt_file = __esm({
45723
46118
  "packages/adapters/src/providers/shared/prompt-file.ts"() {
@@ -45809,7 +46204,7 @@ function isEsrch(err) {
45809
46204
  return false;
45810
46205
  }
45811
46206
  function sleep(ms) {
45812
- return new Promise((resolve5) => setTimeout(resolve5, ms));
46207
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
45813
46208
  }
45814
46209
  var SIGTERM_GRACE_MS;
45815
46210
  var init_suite_reaper = __esm({
@@ -46103,7 +46498,7 @@ var init_transport = __esm({
46103
46498
 
46104
46499
  // packages/adapters/src/providers/claude-code/adapter.ts
46105
46500
  import { existsSync as existsSync8 } from "node:fs";
46106
- import { homedir as homedir6 } from "node:os";
46501
+ import { homedir as homedir5 } from "node:os";
46107
46502
  import { join as join11 } from "node:path";
46108
46503
  import { findOnPath as findOnPath2 } from "@cleocode/paths";
46109
46504
  var ClaudeCodeAdapter;
@@ -46257,7 +46652,7 @@ var init_adapter = __esm({
46257
46652
  cliAvailable = resolvedCli !== null;
46258
46653
  if (resolvedCli) details.cliPath = resolvedCli;
46259
46654
  else details.cliAvailable = false;
46260
- const claudeConfigDir = join11(homedir6(), ".claude");
46655
+ const claudeConfigDir = join11(homedir5(), ".claude");
46261
46656
  const configExists = existsSync8(claudeConfigDir);
46262
46657
  details.configDirExists = configExists;
46263
46658
  const entrypointSet = process.env.CLAUDE_CODE_ENTRYPOINT !== void 0;
@@ -46288,10 +46683,10 @@ var init_adapter = __esm({
46288
46683
 
46289
46684
  // packages/adapters/src/providers/claude-code/statusline.ts
46290
46685
  import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
46291
- import { homedir as homedir7 } from "node:os";
46686
+ import { homedir as homedir6 } from "node:os";
46292
46687
  import { join as join12 } from "node:path";
46293
46688
  function getClaudeSettingsPath() {
46294
- return process.env["CLAUDE_SETTINGS"] ?? join12(process.env["CLAUDE_HOME"] ?? join12(homedir7(), ".claude"), "settings.json");
46689
+ return process.env["CLAUDE_SETTINGS"] ?? join12(process.env["CLAUDE_HOME"] ?? join12(homedir6(), ".claude"), "settings.json");
46295
46690
  }
46296
46691
  function checkStatuslineIntegration() {
46297
46692
  const settingsPath = getClaudeSettingsPath();
@@ -46305,7 +46700,7 @@ function checkStatuslineIntegration() {
46305
46700
  if (cmd.includes("context-monitor.sh") || cmd.includes("cleo-statusline") || cmd.includes(".context-state.json") || cmd.includes("context-states")) {
46306
46701
  return "configured";
46307
46702
  }
46308
- const scriptPath = cmd.startsWith("~") ? cmd.replace("~", homedir7()) : cmd;
46703
+ const scriptPath = cmd.startsWith("~") ? cmd.replace("~", homedir6()) : cmd;
46309
46704
  if (existsSync9(scriptPath)) {
46310
46705
  try {
46311
46706
  const content = readFileSync7(scriptPath, "utf-8");
@@ -46387,6 +46782,25 @@ var init_paths2 = __esm({
46387
46782
  }
46388
46783
  });
46389
46784
 
46785
+ // packages/adapters/src/providers/shared/project-instruction-file.ts
46786
+ import {
46787
+ ensureProviderInstructionFile as ensureProviderInstructionFile2,
46788
+ HomeInstructionFileError
46789
+ } from "@cleocode/caamp";
46790
+ async function ensureProjectInstructionFile(providerId, projectDir, options) {
46791
+ try {
46792
+ return await ensureProviderInstructionFile2(providerId, projectDir, options);
46793
+ } catch (err) {
46794
+ if (err instanceof HomeInstructionFileError) return null;
46795
+ throw err;
46796
+ }
46797
+ }
46798
+ var init_project_instruction_file = __esm({
46799
+ "packages/adapters/src/providers/shared/project-instruction-file.ts"() {
46800
+ "use strict";
46801
+ }
46802
+ });
46803
+
46390
46804
  // packages/adapters/src/providers/shared/cli-spawn.ts
46391
46805
  import { spawn } from "node:child_process";
46392
46806
  import { resolveSpawnInvocation as resolveSpawnInvocation2 } from "@cleocode/paths";
@@ -46561,7 +46975,7 @@ var init_hooks2 = __esm({
46561
46975
  import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
46562
46976
  import { join as join18 } from "node:path";
46563
46977
  import {
46564
- ensureProviderInstructionFile as ensureProviderInstructionFile3,
46978
+ ensureProviderInstructionFile as ensureProviderInstructionFile4,
46565
46979
  ensureProviderRuleFile,
46566
46980
  updateJsonConfigFile as updateJsonConfigFile4
46567
46981
  } from "@cleocode/caamp";
@@ -46572,8 +46986,10 @@ var CLEO_MDC_FRONTMATTER, CursorInstallProvider;
46572
46986
  var init_install2 = __esm({
46573
46987
  "packages/adapters/src/providers/cursor/install.ts"() {
46574
46988
  "use strict";
46989
+ init_heavy_command_hook_install();
46575
46990
  init_hook_template_installer();
46576
46991
  init_paths2();
46992
+ init_project_instruction_file();
46577
46993
  CLEO_MDC_FRONTMATTER = [
46578
46994
  "---",
46579
46995
  "description: CLEO task management protocol references",
@@ -46594,21 +47010,29 @@ var init_install2 = __esm({
46594
47010
  const installedAt = (/* @__PURE__ */ new Date()).toISOString();
46595
47011
  let instructionFileUpdated = false;
46596
47012
  const details = {};
46597
- const instructionResult = await ensureProviderInstructionFile3("cursor", projectDir, {});
46598
- const cursorFilesUpdated = await this.updateInstructionFiles(projectDir);
46599
- instructionFileUpdated = instructionResult.action !== "intact" || cursorFilesUpdated;
46600
- if (instructionFileUpdated) {
46601
- details.instructionFiles = this.getUpdatedFileList(projectDir);
47013
+ const instructionResult = await ensureProjectInstructionFile("cursor", projectDir, {});
47014
+ if (instructionResult === null) {
47015
+ details.instructionFiles = "skipped";
47016
+ } else {
47017
+ const cursorFilesUpdated = await this.updateInstructionFiles(projectDir);
47018
+ instructionFileUpdated = instructionResult.action !== "intact" || cursorFilesUpdated;
47019
+ if (instructionFileUpdated) {
47020
+ details.instructionFiles = this.getUpdatedFileList(projectDir);
47021
+ }
46602
47022
  }
46603
47023
  let hooksError = null;
46604
- try {
46605
- const hookResult = await this.installHookTemplates(projectDir);
46606
- if (hookResult) {
46607
- details.hookTemplates = hookResult;
47024
+ if (isUserHomeDir(projectDir)) {
47025
+ details.hookTemplates = "skipped";
47026
+ } else {
47027
+ try {
47028
+ const hookResult = await this.installHookTemplates(projectDir);
47029
+ if (hookResult) {
47030
+ details.hookTemplates = hookResult;
47031
+ }
47032
+ } catch (err) {
47033
+ hooksError = err instanceof Error ? err.message : String(err);
47034
+ details.hooksError = hooksError;
46608
47035
  }
46609
- } catch (err) {
46610
- hooksError = err instanceof Error ? err.message : String(err);
46611
- details.hooksError = hooksError;
46612
47036
  }
46613
47037
  return {
46614
47038
  success: hooksError === null,
@@ -46656,7 +47080,7 @@ var init_install2 = __esm({
46656
47080
  * @param projectDir - Project root directory
46657
47081
  */
46658
47082
  async ensureInstructionReferences(projectDir) {
46659
- await ensureProviderInstructionFile3("cursor", projectDir, {});
47083
+ await ensureProviderInstructionFile4("cursor", projectDir, {});
46660
47084
  await this.updateInstructionFiles(projectDir);
46661
47085
  }
46662
47086
  /**
@@ -47088,7 +47512,7 @@ var init_hooks3 = __esm({
47088
47512
  // packages/adapters/src/providers/opencode/install.ts
47089
47513
  import { existsSync as existsSync18, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "node:fs";
47090
47514
  import { join as join25 } from "node:path";
47091
- import { ensureProviderInstructionFile as ensureProviderInstructionFile6 } from "@cleocode/caamp";
47515
+ import { ensureProviderInstructionFile as ensureProviderInstructionFile7 } from "@cleocode/caamp";
47092
47516
  var OpenCodeInstallProvider;
47093
47517
  var init_install3 = __esm({
47094
47518
  "packages/adapters/src/providers/opencode/install.ts"() {
@@ -47096,6 +47520,7 @@ var init_install3 = __esm({
47096
47520
  init_heavy_command_hook_install();
47097
47521
  init_hook_template_installer();
47098
47522
  init_paths2();
47523
+ init_project_instruction_file();
47099
47524
  OpenCodeInstallProvider = class {
47100
47525
  /**
47101
47526
  * Install CLEO into an OpenCode project.
@@ -47107,14 +47532,15 @@ var init_install3 = __esm({
47107
47532
  const { projectDir } = options;
47108
47533
  const installedAt = (/* @__PURE__ */ new Date()).toISOString();
47109
47534
  const details = {};
47110
- const instructResult = await ensureProviderInstructionFile6("opencode", projectDir, {
47535
+ const instructResult = await ensureProjectInstructionFile("opencode", projectDir, {
47111
47536
  scope: "project",
47112
47537
  references: [`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`, "@.cleo/memory-bridge.md"]
47113
47538
  });
47114
- const instructionFileUpdated = instructResult.action !== "intact";
47539
+ const instructionFileUpdated = instructResult !== null && instructResult.action !== "intact";
47115
47540
  if (instructionFileUpdated) {
47116
47541
  details.instructionFile = join25(projectDir, instructResult.instructFile);
47117
47542
  }
47543
+ if (instructResult === null) details.instructionFile = "skipped";
47118
47544
  const hookResult = this.installHookTemplates(projectDir);
47119
47545
  if (hookResult) {
47120
47546
  details.hookTemplates = hookResult;
@@ -47154,7 +47580,7 @@ var init_install3 = __esm({
47154
47580
  */
47155
47581
  async isInstalled() {
47156
47582
  try {
47157
- const result = await ensureProviderInstructionFile6("opencode", process.cwd(), {
47583
+ const result = await ensureProviderInstructionFile7("opencode", process.cwd(), {
47158
47584
  scope: "project",
47159
47585
  references: [
47160
47586
  `@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`,
@@ -47174,7 +47600,7 @@ var init_install3 = __esm({
47174
47600
  * @param projectDir - Project root directory
47175
47601
  */
47176
47602
  async ensureInstructionReferences(projectDir) {
47177
- await ensureProviderInstructionFile6("opencode", projectDir, {
47603
+ await ensureProviderInstructionFile7("opencode", projectDir, {
47178
47604
  scope: "project",
47179
47605
  references: [`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`, "@.cleo/memory-bridge.md"]
47180
47606
  });
@@ -47781,8 +48207,8 @@ var init_hooks4 = __esm({
47781
48207
  });
47782
48208
 
47783
48209
  // packages/adapters/src/providers/pi/install.ts
47784
- import { join as join28 } from "node:path";
47785
- import { ensureProviderInstructionFile as ensureProviderInstructionFile7 } from "@cleocode/caamp";
48210
+ import { join as join29 } from "node:path";
48211
+ import { ensureProviderInstructionFile as ensureProviderInstructionFile8 } from "@cleocode/caamp";
47786
48212
  function getCleoReferences() {
47787
48213
  return [`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`, "@.cleo/memory-bridge.md"];
47788
48214
  }
@@ -47791,6 +48217,7 @@ var init_install4 = __esm({
47791
48217
  "packages/adapters/src/providers/pi/install.ts"() {
47792
48218
  "use strict";
47793
48219
  init_paths2();
48220
+ init_project_instruction_file();
47794
48221
  PiInstallProvider = class {
47795
48222
  /**
47796
48223
  * Install CLEO into a Pi coding agent project.
@@ -47802,17 +48229,18 @@ var init_install4 = __esm({
47802
48229
  const { projectDir } = options;
47803
48230
  const installedAt = (/* @__PURE__ */ new Date()).toISOString();
47804
48231
  const details = {};
47805
- const projectResult = await ensureProviderInstructionFile7("pi", projectDir, {
48232
+ const projectResult = await ensureProjectInstructionFile("pi", projectDir, {
47806
48233
  scope: "project",
47807
48234
  references: getCleoReferences()
47808
48235
  });
47809
- const projectUpdated = projectResult.action !== "intact";
48236
+ const projectUpdated = projectResult !== null && projectResult.action !== "intact";
47810
48237
  if (projectUpdated) {
47811
- details.instructionFile = join28(projectDir, projectResult.instructFile);
48238
+ details.instructionFile = join29(projectDir, projectResult.instructFile);
47812
48239
  }
48240
+ if (projectResult === null) details.instructionFile = "skipped";
47813
48241
  let globalUpdated = false;
47814
48242
  try {
47815
- const globalResult = await ensureProviderInstructionFile7("pi", projectDir, {
48243
+ const globalResult = await ensureProviderInstructionFile8("pi", projectDir, {
47816
48244
  scope: "global",
47817
48245
  references: getCleoReferences()
47818
48246
  });
@@ -47844,7 +48272,7 @@ var init_install4 = __esm({
47844
48272
  */
47845
48273
  async isInstalled() {
47846
48274
  try {
47847
- const result = await ensureProviderInstructionFile7("pi", process.cwd(), {
48275
+ const result = await ensureProviderInstructionFile8("pi", process.cwd(), {
47848
48276
  scope: "project",
47849
48277
  references: getCleoReferences()
47850
48278
  });
@@ -47852,7 +48280,7 @@ var init_install4 = __esm({
47852
48280
  } catch {
47853
48281
  }
47854
48282
  try {
47855
- const result = await ensureProviderInstructionFile7("pi", process.cwd(), {
48283
+ const result = await ensureProviderInstructionFile8("pi", process.cwd(), {
47856
48284
  scope: "global",
47857
48285
  references: getCleoReferences()
47858
48286
  });
@@ -47869,7 +48297,7 @@ var init_install4 = __esm({
47869
48297
  * @param projectDir - Project root directory
47870
48298
  */
47871
48299
  async ensureInstructionReferences(projectDir) {
47872
- await ensureProviderInstructionFile7("pi", projectDir, {
48300
+ await ensureProviderInstructionFile8("pi", projectDir, {
47873
48301
  scope: "project",
47874
48302
  references: getCleoReferences()
47875
48303
  });
@@ -47879,7 +48307,7 @@ var init_install4 = __esm({
47879
48307
  });
47880
48308
 
47881
48309
  // packages/adapters/src/providers/pi/spawn.ts
47882
- import { findOnPath as findOnPath11 } from "@cleocode/paths";
48310
+ import { findOnPath as findOnPath12 } from "@cleocode/paths";
47883
48311
  function getPiCliPath() {
47884
48312
  return process.env["PI_CLI_PATH"] ?? "pi";
47885
48313
  }
@@ -47902,7 +48330,7 @@ var init_spawn4 = __esm({
47902
48330
  */
47903
48331
  async canSpawn() {
47904
48332
  const cliPath = getPiCliPath();
47905
- return findOnPath11(cliPath) !== null;
48333
+ return findOnPath12(cliPath) !== null;
47906
48334
  }
47907
48335
  /**
47908
48336
  * Spawn a subagent via Pi CLI.
@@ -48013,22 +48441,22 @@ var init_spawn4 = __esm({
48013
48441
  });
48014
48442
 
48015
48443
  // packages/adapters/src/providers/pi/adapter.ts
48016
- import { existsSync as existsSync20 } from "node:fs";
48444
+ import { existsSync as existsSync21 } from "node:fs";
48017
48445
  import { homedir as homedir13 } from "node:os";
48018
- import { join as join29 } from "node:path";
48019
- import { findOnPath as findOnPath12 } from "@cleocode/paths";
48446
+ import { join as join30 } from "node:path";
48447
+ import { findOnPath as findOnPath13 } from "@cleocode/paths";
48020
48448
  function getPiAgentDir() {
48021
48449
  const env = process.env["PI_CODING_AGENT_DIR"];
48022
48450
  if (env !== void 0 && env.length > 0) {
48023
48451
  if (env === "~") return homedir13();
48024
- if (env.startsWith("~/")) return join29(homedir13(), env.slice(2));
48452
+ if (env.startsWith("~/")) return join30(homedir13(), env.slice(2));
48025
48453
  return env;
48026
48454
  }
48027
48455
  const piHome = process.env["PI_HOME"];
48028
48456
  if (piHome !== void 0 && piHome.length > 0) {
48029
- return join29(piHome, "agent");
48457
+ return join30(piHome, "agent");
48030
48458
  }
48031
- return join29(homedir13(), ".pi", "agent");
48459
+ return join30(homedir13(), ".pi", "agent");
48032
48460
  }
48033
48461
  var PiAdapter;
48034
48462
  var init_adapter4 = __esm({
@@ -48131,17 +48559,17 @@ var init_adapter4 = __esm({
48131
48559
  }
48132
48560
  let cliAvailable = false;
48133
48561
  const cliPath = process.env["PI_CLI_PATH"] ?? "pi";
48134
- const resolvedCli = findOnPath12(cliPath);
48562
+ const resolvedCli = findOnPath13(cliPath);
48135
48563
  cliAvailable = resolvedCli !== null;
48136
48564
  if (resolvedCli) details.cliPath = cliPath === "pi" ? resolvedCli : cliPath;
48137
48565
  else details.cliAvailable = false;
48138
48566
  const agentDir = getPiAgentDir();
48139
- const agentDirExists = existsSync20(agentDir);
48567
+ const agentDirExists = existsSync21(agentDir);
48140
48568
  details.agentDirExists = agentDirExists;
48141
48569
  details.agentDir = agentDir;
48142
48570
  if (this.projectDir) {
48143
- const projectPiDir = join29(this.projectDir, ".pi");
48144
- details.projectPiDirExists = existsSync20(projectPiDir);
48571
+ const projectPiDir = join30(this.projectDir, ".pi");
48572
+ details.projectPiDirExists = existsSync21(projectPiDir);
48145
48573
  }
48146
48574
  details.piCodingAgentDirSet = process.env["PI_CODING_AGENT_DIR"] !== void 0;
48147
48575
  details.piHomeSet = process.env["PI_HOME"] !== void 0;
@@ -48353,12 +48781,12 @@ function createAdapter2() {
48353
48781
 
48354
48782
  // packages/adapters/src/providers/codex/adapter.ts
48355
48783
  import { existsSync as existsSync11 } from "node:fs";
48356
- import { homedir as homedir9 } from "node:os";
48784
+ import { homedir as homedir8 } from "node:os";
48357
48785
  import { join as join17 } from "node:path";
48358
48786
  import { findOnPath as findOnPath3 } from "@cleocode/paths";
48359
48787
 
48360
48788
  // packages/adapters/src/providers/codex/hooks.ts
48361
- import { homedir as homedir8 } from "node:os";
48789
+ import { homedir as homedir7 } from "node:os";
48362
48790
  import { join as join15 } from "node:path";
48363
48791
 
48364
48792
  // packages/adapters/src/providers/shared/transcript-reader.ts
@@ -48478,15 +48906,16 @@ var CodexHookProvider = class {
48478
48906
  * @task T162 @epic T134
48479
48907
  */
48480
48908
  async getTranscript(_sessionId, _projectDir) {
48481
- return readLatestTranscript(join15(homedir8(), ".codex"));
48909
+ return readLatestTranscript(join15(homedir7(), ".codex"));
48482
48910
  }
48483
48911
  };
48484
48912
 
48485
48913
  // packages/adapters/src/providers/codex/install.ts
48486
48914
  init_heavy_command_hook_install();
48487
48915
  init_paths2();
48916
+ init_project_instruction_file();
48488
48917
  import { join as join16 } from "node:path";
48489
- import { ensureProviderInstructionFile as ensureProviderInstructionFile2 } from "@cleocode/caamp";
48918
+ import { ensureProviderInstructionFile as ensureProviderInstructionFile3 } from "@cleocode/caamp";
48490
48919
  var CodexInstallProvider = class {
48491
48920
  /**
48492
48921
  * Install CLEO into a Codex CLI environment.
@@ -48499,22 +48928,22 @@ var CodexInstallProvider = class {
48499
48928
  const { projectDir } = options;
48500
48929
  const installedAt = (/* @__PURE__ */ new Date()).toISOString();
48501
48930
  const details = {};
48502
- const result = await ensureProviderInstructionFile2("codex", projectDir, {
48931
+ const result = await ensureProjectInstructionFile("codex", projectDir, {
48503
48932
  scope: "project",
48504
48933
  references: [`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`, "@.cleo/memory-bridge.md"]
48505
48934
  });
48506
- const instructionFileUpdated = result.action !== "intact";
48935
+ const instructionFileUpdated = result !== null && result.action !== "intact";
48507
48936
  if (instructionFileUpdated) {
48508
48937
  details.instructionFile = join16(projectDir, result.instructFile);
48509
48938
  }
48939
+ if (result === null) details.instructionFile = "skipped";
48510
48940
  let success2 = true;
48511
48941
  if (options.heavyCommandHook !== void 0 && isUserHomeDir(projectDir)) {
48512
48942
  details.heavyCommandHook = "skipped";
48513
48943
  } else if (options.heavyCommandHook !== void 0) {
48514
48944
  try {
48515
- details.heavyCommandHook = await syncJsonHeavyCommandHook(
48516
- join16(projectDir, ".codex", "hooks.json"),
48517
- "codex",
48945
+ details.heavyCommandHook = await syncCodexHeavyCommandHook(
48946
+ projectDir,
48518
48947
  options.heavyCommandHook
48519
48948
  );
48520
48949
  } catch (err) {
@@ -48545,7 +48974,7 @@ var CodexInstallProvider = class {
48545
48974
  */
48546
48975
  async isInstalled() {
48547
48976
  try {
48548
- const result = await ensureProviderInstructionFile2("codex", process.cwd(), {
48977
+ const result = await ensureProviderInstructionFile3("codex", process.cwd(), {
48549
48978
  scope: "project",
48550
48979
  references: [
48551
48980
  `@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`,
@@ -48566,7 +48995,7 @@ var CodexInstallProvider = class {
48566
48995
  * @task T162
48567
48996
  */
48568
48997
  async ensureInstructionReferences(projectDir) {
48569
- await ensureProviderInstructionFile2("codex", projectDir, {
48998
+ await ensureProviderInstructionFile3("codex", projectDir, {
48570
48999
  scope: "project",
48571
49000
  references: [`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`, "@.cleo/memory-bridge.md"]
48572
49001
  });
@@ -48654,7 +49083,7 @@ var CodexAdapter = class {
48654
49083
  cliAvailable = resolvedCli !== null;
48655
49084
  if (resolvedCli) details.cliPath = resolvedCli;
48656
49085
  else details.cliAvailable = false;
48657
- const codexConfigDir = join17(homedir9(), ".codex");
49086
+ const codexConfigDir = join17(homedir8(), ".codex");
48658
49087
  const configExists = existsSync11(codexConfigDir);
48659
49088
  details.configDirExists = configExists;
48660
49089
  const healthy = cliAvailable;
@@ -48697,12 +49126,12 @@ init_cursor();
48697
49126
 
48698
49127
  // packages/adapters/src/providers/gemini-cli/adapter.ts
48699
49128
  import { existsSync as existsSync15 } from "node:fs";
48700
- import { homedir as homedir11 } from "node:os";
49129
+ import { homedir as homedir10 } from "node:os";
48701
49130
  import { join as join22 } from "node:path";
48702
49131
  import { findOnPath as findOnPath5 } from "@cleocode/paths";
48703
49132
 
48704
49133
  // packages/adapters/src/providers/gemini-cli/hooks.ts
48705
- import { homedir as homedir10 } from "node:os";
49134
+ import { homedir as homedir9 } from "node:os";
48706
49135
  import { join as join20 } from "node:path";
48707
49136
  var GEMINI_CLI_EVENT_MAP = {
48708
49137
  SessionStart: "SessionStart",
@@ -48781,15 +49210,16 @@ var GeminiCliHookProvider = class {
48781
49210
  * @task T161 @epic T134
48782
49211
  */
48783
49212
  async getTranscript(_sessionId, _projectDir) {
48784
- return readLatestTranscript(join20(homedir10(), ".gemini"));
49213
+ return readLatestTranscript(join20(homedir9(), ".gemini"));
48785
49214
  }
48786
49215
  };
48787
49216
 
48788
49217
  // packages/adapters/src/providers/gemini-cli/install.ts
48789
49218
  init_paths2();
49219
+ init_project_instruction_file();
48790
49220
  import { existsSync as existsSync14, readFileSync as readFileSync9 } from "node:fs";
48791
49221
  import { join as join21 } from "node:path";
48792
- import { ensureProviderInstructionFile as ensureProviderInstructionFile4 } from "@cleocode/caamp";
49222
+ import { ensureProviderInstructionFile as ensureProviderInstructionFile5 } from "@cleocode/caamp";
48793
49223
  var GeminiCliInstallProvider = class {
48794
49224
  /**
48795
49225
  * Install CLEO into a Gemini CLI environment.
@@ -48802,14 +49232,15 @@ var GeminiCliInstallProvider = class {
48802
49232
  const { projectDir } = options;
48803
49233
  const installedAt = (/* @__PURE__ */ new Date()).toISOString();
48804
49234
  const details = {};
48805
- const result = await ensureProviderInstructionFile4("gemini-cli", projectDir, {
49235
+ const result = await ensureProjectInstructionFile("gemini-cli", projectDir, {
48806
49236
  references: [`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`, "@.cleo/memory-bridge.md"],
48807
49237
  scope: "project"
48808
49238
  });
48809
- const instructionFileUpdated = result.action !== "intact";
49239
+ const instructionFileUpdated = result !== null && result.action !== "intact";
48810
49240
  if (instructionFileUpdated) {
48811
49241
  details.instructionFile = result.filePath;
48812
49242
  }
49243
+ if (result === null) details.instructionFile = "skipped";
48813
49244
  return {
48814
49245
  success: true,
48815
49246
  installedAt,
@@ -48854,7 +49285,7 @@ var GeminiCliInstallProvider = class {
48854
49285
  * @task T161
48855
49286
  */
48856
49287
  async ensureInstructionReferences(projectDir) {
48857
- await ensureProviderInstructionFile4("gemini-cli", projectDir, {
49288
+ await ensureProviderInstructionFile5("gemini-cli", projectDir, {
48858
49289
  references: [`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`, "@.cleo/memory-bridge.md"],
48859
49290
  scope: "project"
48860
49291
  });
@@ -48953,7 +49384,7 @@ var GeminiCliAdapter = class {
48953
49384
  cliAvailable = resolvedCli !== null;
48954
49385
  if (resolvedCli) details.cliPath = resolvedCli;
48955
49386
  else details.cliAvailable = false;
48956
- const geminiConfigDir = join22(homedir11(), ".gemini");
49387
+ const geminiConfigDir = join22(homedir10(), ".gemini");
48957
49388
  const configExists = existsSync15(geminiConfigDir);
48958
49389
  details.configDirExists = configExists;
48959
49390
  const healthy = cliAvailable;
@@ -48992,7 +49423,7 @@ function createAdapter5() {
48992
49423
 
48993
49424
  // packages/adapters/src/providers/kimi/adapter.ts
48994
49425
  import { existsSync as existsSync17 } from "node:fs";
48995
- import { homedir as homedir12 } from "node:os";
49426
+ import { homedir as homedir11 } from "node:os";
48996
49427
  import { join as join24 } from "node:path";
48997
49428
  import { findOnPath as findOnPath7 } from "@cleocode/paths";
48998
49429
 
@@ -49053,9 +49484,10 @@ var KimiHookProvider = class {
49053
49484
 
49054
49485
  // packages/adapters/src/providers/kimi/install.ts
49055
49486
  init_paths2();
49487
+ init_project_instruction_file();
49056
49488
  import { existsSync as existsSync16, readFileSync as readFileSync10 } from "node:fs";
49057
49489
  import { join as join23 } from "node:path";
49058
- import { ensureProviderInstructionFile as ensureProviderInstructionFile5 } from "@cleocode/caamp";
49490
+ import { ensureProviderInstructionFile as ensureProviderInstructionFile6 } from "@cleocode/caamp";
49059
49491
  var KimiInstallProvider = class {
49060
49492
  /**
49061
49493
  * Install CLEO into a Kimi environment.
@@ -49068,14 +49500,15 @@ var KimiInstallProvider = class {
49068
49500
  const { projectDir } = options;
49069
49501
  const installedAt = (/* @__PURE__ */ new Date()).toISOString();
49070
49502
  const details = {};
49071
- const result = await ensureProviderInstructionFile5("kimi", projectDir, {
49503
+ const result = await ensureProjectInstructionFile("kimi", projectDir, {
49072
49504
  references: [`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`, "@.cleo/memory-bridge.md"],
49073
49505
  scope: "project"
49074
49506
  });
49075
- const instructionFileUpdated = result.action !== "intact";
49507
+ const instructionFileUpdated = result !== null && result.action !== "intact";
49076
49508
  if (instructionFileUpdated) {
49077
49509
  details.instructionFile = result.filePath;
49078
49510
  }
49511
+ if (result === null) details.instructionFile = "skipped";
49079
49512
  return {
49080
49513
  success: true,
49081
49514
  installedAt,
@@ -49120,7 +49553,7 @@ var KimiInstallProvider = class {
49120
49553
  * @task T163
49121
49554
  */
49122
49555
  async ensureInstructionReferences(projectDir) {
49123
- await ensureProviderInstructionFile5("kimi", projectDir, {
49556
+ await ensureProviderInstructionFile6("kimi", projectDir, {
49124
49557
  references: [`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`, "@.cleo/memory-bridge.md"],
49125
49558
  scope: "project"
49126
49559
  });
@@ -49206,7 +49639,7 @@ var KimiAdapter = class {
49206
49639
  cliAvailable = resolvedCli !== null;
49207
49640
  if (resolvedCli) details.cliPath = resolvedCli;
49208
49641
  else details.cliAvailable = false;
49209
- const kimiConfigDir = join24(homedir12(), ".kimi");
49642
+ const kimiConfigDir = join24(homedir11(), ".kimi");
49210
49643
  const configExists = existsSync17(kimiConfigDir);
49211
49644
  details.configDirExists = configExists;
49212
49645
  const healthy = cliAvailable;
@@ -49244,20 +49677,405 @@ function createAdapter6() {
49244
49677
 
49245
49678
  // packages/adapters/src/index.ts
49246
49679
  init_opencode();
49680
+
49681
+ // packages/adapters/src/providers/shared/heavy-command-hook-delivery.ts
49682
+ init_heavy_command_hook_install();
49683
+ init_hook_config();
49684
+ import { execFileSync as execFileSync2, spawnSync as spawnSync3 } from "node:child_process";
49685
+ import { existsSync as existsSync20, lstatSync, readFileSync as readFileSync12, statSync as statSync3 } from "node:fs";
49686
+ import { homedir as homedir12 } from "node:os";
49687
+ import { dirname as dirname7, join as join28 } from "node:path";
49688
+ import { JsonConfigParseError } from "@cleocode/caamp";
49689
+ import { findOnPath as findOnPath11 } from "@cleocode/paths";
49690
+ var HEAVY_HOOK_DELIVERY_PROVIDERS = Object.freeze([
49691
+ "claude-code",
49692
+ "codex",
49693
+ "opencode",
49694
+ "kimi"
49695
+ ]);
49696
+ var HEAVY_HOOK_FIX_COMMAND = "cleo doctor heavy-command-hook --fix";
49697
+ var CODEX_SHARED_REMEDY = "if the team wants Codex governed here, add CLEO's PreToolUse entry (the snippet) to .codex/hooks.json yourself and commit it deliberately; CLEO never writes or hides a project's own hook config";
49698
+ var KIMI_REASON = "Kimi reads hooks only from its global ~/.kimi/config.toml, and CLEO never writes user-global configs";
49699
+ var KIMI_REMEDY = "add a [[hooks]] entry for PreToolUse that runs `cleo hook heavy-command --provider kimi` to ~/.kimi/config.toml yourself";
49700
+ function homeOf(env) {
49701
+ return env.HOME || homedir12();
49702
+ }
49703
+ function heavyHookTarget(provider, projectDir, env = process.env) {
49704
+ switch (provider) {
49705
+ case "claude-code":
49706
+ return join28(projectDir, CLAUDE_LOCAL_SETTINGS);
49707
+ case "codex":
49708
+ return join28(projectDir, CODEX_HOOKS_FILE);
49709
+ case "opencode":
49710
+ return join28(projectDir, OPENCODE_PLUGIN_FILE);
49711
+ case "kimi":
49712
+ return join28(homeOf(env), ".kimi", "config.toml");
49713
+ }
49714
+ }
49715
+ function detectHeavyHookProvider(provider, projectDir, env = process.env) {
49716
+ const home = homeOf(env);
49717
+ const signals = provider === "claude-code" ? [
49718
+ [
49719
+ env.CLAUDECODE === "1" || Boolean(env.CLAUDE_CODE_ENTRYPOINT),
49720
+ "running inside Claude Code"
49721
+ ],
49722
+ [() => existsSync20(join28(projectDir, ".claude")), "the project has .claude/"],
49723
+ [() => findOnPath11("claude", { env }) !== null, "`claude` is on PATH"],
49724
+ [() => existsSync20(env.CLAUDE_CONFIG_DIR || join28(home, ".claude")), "~/.claude exists"]
49725
+ ] : provider === "codex" ? [
49726
+ [() => existsSync20(join28(projectDir, ".codex")), "the project has .codex"],
49727
+ [() => findOnPath11("codex", { env }) !== null, "`codex` is on PATH"],
49728
+ [() => existsSync20(env.CODEX_HOME || join28(home, ".codex")), "~/.codex exists"]
49729
+ ] : provider === "opencode" ? [
49730
+ [() => existsSync20(join28(projectDir, ".opencode")), "the project has .opencode/"],
49731
+ [
49732
+ () => existsSync20(join28(projectDir, "opencode.json")) || existsSync20(join28(projectDir, "opencode.jsonc")),
49733
+ "the project has opencode.json"
49734
+ ],
49735
+ [() => findOnPath11("opencode", { env }) !== null, "`opencode` is on PATH"],
49736
+ [
49737
+ () => existsSync20(join28(env.XDG_CONFIG_HOME || join28(home, ".config"), "opencode")),
49738
+ "~/.config/opencode exists"
49739
+ ]
49740
+ ] : [
49741
+ [() => findOnPath11("kimi", { env }) !== null, "`kimi` is on PATH"],
49742
+ [() => existsSync20(join28(home, ".kimi")), "~/.kimi exists"]
49743
+ ];
49744
+ for (const [test, why] of signals) {
49745
+ if (typeof test === "boolean" ? test : test()) return { detected: true, why };
49746
+ }
49747
+ if (provider !== "kimi" && existsSync20(heavyHookTarget(provider, projectDir, env))) {
49748
+ return { detected: true, why: "its hook config exists" };
49749
+ }
49750
+ return {
49751
+ detected: false,
49752
+ why: `${provider} is not in use here (no config directory or CLI found)`
49753
+ };
49754
+ }
49755
+ function errorText(err) {
49756
+ return err instanceof Error ? err.message : String(err);
49757
+ }
49758
+ async function syncProvider(provider, projectDir, mode, env) {
49759
+ const target = heavyHookTarget(provider, projectDir, env);
49760
+ if (provider === "kimi") {
49761
+ return mode === "off" ? { provider, status: "unchanged", target } : { provider, status: "unsupported", target, reason: KIMI_REASON, remedy: KIMI_REMEDY };
49762
+ }
49763
+ try {
49764
+ const result = provider === "claude-code" ? await syncClaudeCodeHeavyCommandHook(projectDir, mode) : provider === "codex" ? await syncCodexHeavyCommandHook(projectDir, mode) : syncOpencodeHeavyCommandPlugin(projectDir, mode);
49765
+ return { provider, status: result, target };
49766
+ } catch (err) {
49767
+ if (err instanceof HeavyHookSharedConfigError) {
49768
+ return {
49769
+ provider,
49770
+ status: "needs-consent",
49771
+ target,
49772
+ reason: err.message,
49773
+ ...mode === "off" ? {
49774
+ remedy: "the team committed CLEO's hook in .codex/hooks.json; remove it there in a team change if the team agrees"
49775
+ } : { remedy: CODEX_SHARED_REMEDY, snippet: codexHookSnippet() }
49776
+ };
49777
+ }
49778
+ if (err instanceof HeavyHookPathBlockedError) {
49779
+ return {
49780
+ provider,
49781
+ status: "blocked",
49782
+ target,
49783
+ reason: err.message,
49784
+ remedy: `remove or rename ${err.path} (it is not a directory), then run: ${HEAVY_HOOK_FIX_COMMAND}`
49785
+ };
49786
+ }
49787
+ return {
49788
+ provider,
49789
+ status: "failed",
49790
+ target,
49791
+ reason: errorText(err),
49792
+ remedy: err instanceof JsonConfigParseError ? `fix the JSON in ${err.filePath} (it was left untouched), then run: ${HEAVY_HOOK_FIX_COMMAND}` : `run: ${HEAVY_HOOK_FIX_COMMAND}`
49793
+ };
49794
+ }
49795
+ }
49796
+ async function syncProjectHeavyCommandHooks(projectDir, mode, options = {}) {
49797
+ const env = options.env ?? process.env;
49798
+ const providers = options.providers ?? HEAVY_HOOK_DELIVERY_PROVIDERS;
49799
+ const home = isUserHomeDir(projectDir, homeOf(env));
49800
+ const outcomes = [];
49801
+ for (const provider of providers) {
49802
+ const target = heavyHookTarget(provider, projectDir, env);
49803
+ if (home) {
49804
+ outcomes.push({
49805
+ provider,
49806
+ status: "skipped",
49807
+ target,
49808
+ reason: "the project is your home directory, whose provider configs are user-global"
49809
+ });
49810
+ continue;
49811
+ }
49812
+ const detection = detectHeavyHookProvider(provider, projectDir, env);
49813
+ if (mode !== "off" && !detection.detected) {
49814
+ outcomes.push({ provider, status: "skipped", target, reason: detection.why });
49815
+ continue;
49816
+ }
49817
+ outcomes.push(await syncProvider(provider, projectDir, mode, env));
49818
+ }
49819
+ return outcomes;
49820
+ }
49821
+ function jsonHookPresence(file2, provider) {
49822
+ let raw;
49823
+ try {
49824
+ raw = readFileSync12(file2, "utf-8");
49825
+ } catch (err) {
49826
+ const code = err.code;
49827
+ return code === "ENOENT" || code === "ENOTDIR" ? { kind: "absent" } : { kind: "unreadable", error: errorText(err) };
49828
+ }
49829
+ if (raw.trim() === "") return { kind: "absent" };
49830
+ let config2;
49831
+ try {
49832
+ config2 = JSON.parse(raw);
49833
+ } catch (err) {
49834
+ return { kind: "unreadable", error: errorText(err) };
49835
+ }
49836
+ if (!isPlainObject2(config2))
49837
+ return { kind: "unreadable", error: "top-level value is not an object" };
49838
+ const hooks = config2.hooks;
49839
+ const groups = isPlainObject2(hooks) ? hooks.PreToolUse : void 0;
49840
+ if (!Array.isArray(groups)) return { kind: "absent" };
49841
+ const expected = JSON.stringify(heavyCommandHookObject(provider));
49842
+ let found = { kind: "absent" };
49843
+ for (const group of groups) {
49844
+ if (!isPlainObject2(group) || !Array.isArray(group.hooks)) continue;
49845
+ for (const hook of group.hooks) {
49846
+ if (!isHeavyHookObject(hook)) continue;
49847
+ if (JSON.stringify(hook) === expected) return { kind: "present", current: true };
49848
+ found = { kind: "present", current: false };
49849
+ }
49850
+ }
49851
+ return found;
49852
+ }
49853
+ function presenceOf(provider, projectDir, target) {
49854
+ if (provider !== "opencode") {
49855
+ const local = jsonHookPresence(target, provider);
49856
+ if (provider !== "claude-code" || local.kind !== "absent") return local;
49857
+ const legacy = jsonHookPresence(join28(projectDir, ".claude", "settings.json"), provider);
49858
+ return legacy.kind === "present" ? { kind: "present", current: false } : { kind: "absent" };
49859
+ }
49860
+ if (!existsSync20(target)) return { kind: "absent" };
49861
+ try {
49862
+ return {
49863
+ kind: "present",
49864
+ current: readFileSync12(target, "utf-8") === opencodeHeavyCommandPluginSource()
49865
+ };
49866
+ } catch (err) {
49867
+ return { kind: "unreadable", error: errorText(err) };
49868
+ }
49869
+ }
49870
+ var HOOK_FILES = {
49871
+ "claude-code": CLAUDE_LOCAL_SETTINGS,
49872
+ codex: CODEX_HOOKS_FILE,
49873
+ opencode: OPENCODE_PLUGIN_FILE
49874
+ };
49875
+ function inspectProvider(provider, projectDir, mode, env, gitChecks) {
49876
+ const target = heavyHookTarget(provider, projectDir, env);
49877
+ const { detected, why } = detectHeavyHookProvider(provider, projectDir, env);
49878
+ const base = { provider, detected, target };
49879
+ if (provider === "kimi") {
49880
+ if (!detected || mode === "off") return { ...base, state: "not-detected", detail: why };
49881
+ let manual = false;
49882
+ try {
49883
+ manual = readFileSync12(target, "utf-8").includes(HEAVY_COMMAND_HOOK_ID);
49884
+ } catch {
49885
+ }
49886
+ return manual ? { ...base, state: "installed", detail: `a manual entry in ${target} runs the hook` } : { ...base, state: "unsupported", detail: `${why}; ${KIMI_REASON}`, remedy: KIMI_REMEDY };
49887
+ }
49888
+ const presence = presenceOf(provider, projectDir, target);
49889
+ if (presence.kind === "unreadable") {
49890
+ return {
49891
+ ...base,
49892
+ state: "unreadable",
49893
+ detail: `${target} cannot be read or parsed (${presence.error})`,
49894
+ remedy: `fix ${target}, then run: ${HEAVY_HOOK_FIX_COMMAND}`
49895
+ };
49896
+ }
49897
+ if (mode === "off") {
49898
+ return presence.kind === "present" ? {
49899
+ ...base,
49900
+ state: "outdated",
49901
+ detail: "resources.heavyCommandHook is off, but the hook is still installed",
49902
+ remedy: `run: ${HEAVY_HOOK_FIX_COMMAND} (removes it)`
49903
+ } : { ...base, state: "disabled", detail: "resources.heavyCommandHook is off" };
49904
+ }
49905
+ if (presence.kind === "present") {
49906
+ if (!presence.current) {
49907
+ return {
49908
+ ...base,
49909
+ state: "outdated",
49910
+ detail: `the hook in ${target} (or a legacy location) was written by another CLEO build`,
49911
+ remedy: `run: ${HEAVY_HOOK_FIX_COMMAND}`
49912
+ };
49913
+ }
49914
+ if (!gitChecks) return { ...base, state: "installed", detail: `installed in ${target}` };
49915
+ const shared2 = provider === "codex" ? codexHooksSharedReason(projectDir, true) : null;
49916
+ if (shared2 !== null && excludedByCleo(projectDir, CODEX_HOOKS_FILE)) {
49917
+ return {
49918
+ ...base,
49919
+ state: "outdated",
49920
+ detail: `CLEO's info/exclude line hides ${target}, which ${shared2}`,
49921
+ remedy: `run: ${HEAVY_HOOK_FIX_COMMAND} (removes CLEO's exclude line)`
49922
+ };
49923
+ }
49924
+ if (shared2 !== null) {
49925
+ const modified = trackedFileModified(projectDir, CODEX_HOOKS_FILE);
49926
+ if (modified || hookFileVisibleToGit(projectDir, CODEX_HOOKS_FILE)) {
49927
+ return {
49928
+ ...base,
49929
+ state: "outdated",
49930
+ detail: `CLEO's per-machine hook sits in ${target}, which ${shared2}, as ${modified ? "an uncommitted change" : "an untracked file"}: one \`git commit -a\` ships it to the team, and it widens evidence scope`,
49931
+ remedy: "remove CLEO's entry from .codex/hooks.json, or commit it deliberately if the team wants Codex governed"
49932
+ };
49933
+ }
49934
+ return { ...base, state: "installed", detail: `installed in ${target}, which ${shared2}` };
49935
+ }
49936
+ if (hookFileVisibleToGit(projectDir, HOOK_FILES[provider])) {
49937
+ return {
49938
+ ...base,
49939
+ state: "outdated",
49940
+ detail: `${target} is installed but git sees it as an untracked file (it widens evidence scope)`,
49941
+ remedy: `run: ${HEAVY_HOOK_FIX_COMMAND} (adds it to the repository's info/exclude)`
49942
+ };
49943
+ }
49944
+ return { ...base, state: "installed", detail: `installed in ${target}` };
49945
+ }
49946
+ if (!detected) return { ...base, state: "not-detected", detail: why };
49947
+ const shared = provider === "codex" ? codexHooksSharedReason(projectDir, gitChecks) : null;
49948
+ if (shared !== null && gitChecks && excludedByCleo(projectDir, CODEX_HOOKS_FILE)) {
49949
+ return {
49950
+ ...base,
49951
+ state: "outdated",
49952
+ detail: `CLEO's info/exclude line hides ${target}, which ${shared}`,
49953
+ remedy: `run: ${HEAVY_HOOK_FIX_COMMAND} (removes CLEO's exclude line)`
49954
+ };
49955
+ }
49956
+ if (shared !== null) {
49957
+ return {
49958
+ ...base,
49959
+ state: "needs-consent",
49960
+ detail: `${target} ${shared}, so CLEO does not write or hide it; Codex runs ungoverned here until the team adds the entry (${why})`,
49961
+ remedy: CODEX_SHARED_REMEDY,
49962
+ snippet: codexHookSnippet()
49963
+ };
49964
+ }
49965
+ const blocked = nonDirectoryAncestor(dirname7(target));
49966
+ if (blocked !== null) {
49967
+ return {
49968
+ ...base,
49969
+ state: "blocked",
49970
+ detail: `${blocked} exists but is not a directory, so the hook cannot be installed (${why})`,
49971
+ remedy: `remove or rename ${blocked}, then run: ${HEAVY_HOOK_FIX_COMMAND}`
49972
+ };
49973
+ }
49974
+ return {
49975
+ ...base,
49976
+ state: "missing",
49977
+ detail: `${why}, but ${target} has no heavy-command hook: agent-run tests and builds bypass the machine-wide budget`,
49978
+ remedy: `run: ${HEAVY_HOOK_FIX_COMMAND}`
49979
+ };
49980
+ }
49981
+ function inspectProjectHeavyCommandHooks(projectDir, mode, options = {}) {
49982
+ const env = options.env ?? process.env;
49983
+ const providers = options.providers ?? HEAVY_HOOK_DELIVERY_PROVIDERS;
49984
+ if (isUserHomeDir(projectDir, homeOf(env))) {
49985
+ return providers.map((provider) => ({
49986
+ provider,
49987
+ detected: false,
49988
+ state: "not-detected",
49989
+ target: heavyHookTarget(provider, projectDir, env),
49990
+ detail: "the project is your home directory; CLEO installs this hook per project only"
49991
+ }));
49992
+ }
49993
+ const gitChecks = options.gitChecks ?? true;
49994
+ return providers.map((provider) => inspectProvider(provider, projectDir, mode, env, gitChecks));
49995
+ }
49996
+ var OLDER_CLEO_MARKER_TTL_MS = 60 * 60 * 1e3;
49997
+ function olderCleoMarkerPath(cleoPath, dir, env) {
49998
+ try {
49999
+ const key = execFileSync2(
50000
+ "/bin/sh",
50001
+ ["-c", `printf '%s|%s' "$1" "$2" | cksum | tr -c '0-9' '-'`, "sh", cleoPath, dir],
50002
+ { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 5e3 }
50003
+ );
50004
+ const base = env.XDG_RUNTIME_DIR || env.TMPDIR || "/tmp";
50005
+ return join28(base, `${OLDER_CLEO_MARKER_PREFIX}${key}`);
50006
+ } catch {
50007
+ return null;
50008
+ }
50009
+ }
50010
+ function hasOlderCleoMarker(cleoPath, dir, env) {
50011
+ const marker16 = olderCleoMarkerPath(cleoPath, dir, env);
50012
+ if (marker16 === null) return false;
50013
+ try {
50014
+ const st = lstatSync(marker16);
50015
+ if (!st.isFile()) return false;
50016
+ if (Date.now() - st.mtimeMs > OLDER_CLEO_MARKER_TTL_MS) return false;
50017
+ return statSync3(cleoPath).mtimeMs <= st.mtimeMs;
50018
+ } catch {
50019
+ return false;
50020
+ }
50021
+ }
50022
+ function probeHeavyHookCli(projectDir, options = {}) {
50023
+ const env = options.env ?? process.env;
50024
+ const path = findOnPath11("cleo", { env });
50025
+ if (path === null) {
50026
+ return {
50027
+ state: "missing",
50028
+ path: null,
50029
+ detail: "no `cleo` on PATH, so the hook lets every heavy command run ungoverned",
50030
+ remedy: "install CLEO so `cleo` is on PATH: npm i -g @cleocode/cleo"
50031
+ };
50032
+ }
50033
+ const older = {
50034
+ state: "older",
50035
+ path,
50036
+ detail: `${path} predates \`cleo hook\` and \`cleo run\`, so the hook lets every heavy command run ungoverned`,
50037
+ remedy: `upgrade the cleo at ${path} (npm i -g @cleocode/cleo, or your version manager), then run: ${HEAVY_HOOK_FIX_COMMAND}`
50038
+ };
50039
+ if (hasOlderCleoMarker(path, projectDir, env)) return older;
50040
+ const probe = spawnSync3(path, ["hook", "heavy-command", "--provider", "claude-code"], {
50041
+ cwd: projectDir,
50042
+ env: { ...env },
50043
+ input: "{}",
50044
+ encoding: "utf-8",
50045
+ timeout: 2e4
50046
+ });
50047
+ if (probe.status === 0) {
50048
+ return { state: "current", path, detail: `${path} answers \`cleo hook heavy-command\`` };
50049
+ }
50050
+ if (probe.status === 127 && /Unknown command/.test(probe.stderr ?? "")) return older;
50051
+ return {
50052
+ state: "unknown",
50053
+ path,
50054
+ detail: `${path} did not answer \`cleo hook heavy-command\` (${probe.error?.message ?? `exit ${probe.status ?? "none"}`}); the hook fails open, so heavy commands run ungoverned while it does not`,
50055
+ remedy: `run \`${path} hook heavy-command < /dev/null\` in ${projectDir} to see why it fails (a broken version-manager shim, say)`
50056
+ };
50057
+ }
50058
+ var heavyHookDeliveryApi = Object.freeze({
50059
+ syncProjectHeavyCommandHooks,
50060
+ inspectProjectHeavyCommandHooks,
50061
+ probeHeavyHookCli
50062
+ });
50063
+
50064
+ // packages/adapters/src/index.ts
49247
50065
  init_hook_template_installer();
49248
50066
 
49249
50067
  // packages/adapters/src/registry.ts
49250
- import { readFileSync as readFileSync12 } from "node:fs";
49251
- import { dirname as dirname6, join as join30, resolve as resolve4 } from "node:path";
50068
+ import { readFileSync as readFileSync13 } from "node:fs";
50069
+ import { dirname as dirname8, join as join31, resolve as resolve5 } from "node:path";
49252
50070
  import { fileURLToPath as fileURLToPath3 } from "node:url";
49253
50071
  var PROVIDER_IDS = ["claude-code", "opencode", "cursor", "pi"];
49254
50072
  function getProviderManifests() {
49255
50073
  const manifests = [];
49256
- const baseDir = resolve4(dirname6(fileURLToPath3(import.meta.url)), "providers");
50074
+ const baseDir = resolve5(dirname8(fileURLToPath3(import.meta.url)), "providers");
49257
50075
  for (const providerId of PROVIDER_IDS) {
49258
50076
  try {
49259
- const manifestPath = join30(baseDir, providerId, "manifest.json");
49260
- const raw = readFileSync12(manifestPath, "utf-8");
50077
+ const manifestPath = join31(baseDir, providerId, "manifest.json");
50078
+ const raw = readFileSync13(manifestPath, "utf-8");
49261
50079
  manifests.push(JSON.parse(raw));
49262
50080
  } catch {
49263
50081
  }
@@ -49305,6 +50123,8 @@ export {
49305
50123
  GeminiCliAdapter,
49306
50124
  GeminiCliHookProvider,
49307
50125
  GeminiCliInstallProvider,
50126
+ HEAVY_HOOK_DELIVERY_PROVIDERS,
50127
+ HEAVY_HOOK_FIX_COMMAND,
49308
50128
  KimiAdapter,
49309
50129
  KimiHookProvider,
49310
50130
  KimiInstallProvider,
@@ -49325,6 +50145,7 @@ export {
49325
50145
  createAdapter5 as createGeminiCliAdapter,
49326
50146
  createAdapter6 as createKimiAdapter,
49327
50147
  createAdapter7 as createOpenCodeAdapter,
50148
+ detectHeavyHookProvider,
49328
50149
  discoverProviders,
49329
50150
  extractSymbolsFromText,
49330
50151
  getProviderHookTemplatePath,
@@ -49333,8 +50154,13 @@ export {
49333
50154
  getSetupInstructions,
49334
50155
  getSharedHookCorePath,
49335
50156
  getStatuslineConfig,
50157
+ heavyHookDeliveryApi,
50158
+ heavyHookTarget,
50159
+ inspectProjectHeavyCommandHooks,
49336
50160
  installProviderHookTemplates,
50161
+ probeHeavyHookCli,
49337
50162
  resolveTools,
49338
- runNexusPostModificationCheck
50163
+ runNexusPostModificationCheck,
50164
+ syncProjectHeavyCommandHooks
49339
50165
  };
49340
50166
  //# sourceMappingURL=index.js.map