hunter-harness 0.1.3 → 0.2.1

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 (149) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +11 -0
  3. package/dist/bin.js +1540 -491
  4. package/package.json +9 -7
  5. package/resources/harness/general/.harness-build.json +0 -5
  6. package/resources/harness/general/CONTEXT.md +0 -65
  7. package/resources/harness/general/README.md +0 -478
  8. package/resources/harness/general/agents/harness-evaluator.md +0 -96
  9. package/resources/harness/general/agents/harness-explorer.md +0 -72
  10. package/resources/harness/general/agents/harness-reviewer.md +0 -69
  11. package/resources/harness/general/harness-archive/SKILL.md +0 -215
  12. package/resources/harness/general/harness-archive/checklist.md +0 -52
  13. package/resources/harness/general/harness-archive/reference.md +0 -103
  14. package/resources/harness/general/harness-archive/scripts/gen-manifest.ps1 +0 -31
  15. package/resources/harness/general/harness-archive/templates/render-summary.mjs +0 -171
  16. package/resources/harness/general/harness-archive/templates/summary-data-template.json +0 -128
  17. package/resources/harness/general/harness-codebase-map/SKILL.md +0 -112
  18. package/resources/harness/general/harness-codebase-map/checklist.md +0 -126
  19. package/resources/harness/general/harness-codebase-map/reference.md +0 -364
  20. package/resources/harness/general/harness-codebase-map/templates/ARCHITECTURE.md +0 -57
  21. package/resources/harness/general/harness-codebase-map/templates/CONCERNS.md +0 -49
  22. package/resources/harness/general/harness-codebase-map/templates/CONVENTIONS.md +0 -57
  23. package/resources/harness/general/harness-codebase-map/templates/INTEGRATIONS.md +0 -52
  24. package/resources/harness/general/harness-codebase-map/templates/STACK.md +0 -55
  25. package/resources/harness/general/harness-codebase-map/templates/STRUCTURE.md +0 -69
  26. package/resources/harness/general/harness-codebase-map/templates/TESTING.md +0 -64
  27. package/resources/harness/general/harness-codebase-map/templates/map-manifest.schema.json +0 -73
  28. package/resources/harness/general/harness-codebase-map/templates/map-summary.md +0 -64
  29. package/resources/harness/general/harness-knowledge-ingest/SKILL.md +0 -246
  30. package/resources/harness/general/harness-knowledge-ingest/design.md +0 -842
  31. package/resources/harness/general/harness-knowledge-ingest/evaluations/harness_knowledge_evaluation.xml +0 -42
  32. package/resources/harness/general/harness-knowledge-ingest/mcp-config.example.json +0 -10
  33. package/resources/harness/general/harness-knowledge-ingest/reference.md +0 -309
  34. package/resources/harness/general/harness-knowledge-ingest/scripts/harness_knowledge.py +0 -4082
  35. package/resources/harness/general/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py +0 -273
  36. package/resources/harness/general/harness-knowledge-query/SKILL.md +0 -164
  37. package/resources/harness/general/harness-plan/SKILL.md +0 -127
  38. package/resources/harness/general/harness-plan/checklist.md +0 -243
  39. package/resources/harness/general/harness-plan/protocols.md +0 -124
  40. package/resources/harness/general/harness-plan/reference.md +0 -376
  41. package/resources/harness/general/harness-review/SKILL.md +0 -156
  42. package/resources/harness/general/harness-review/checklist.md +0 -124
  43. package/resources/harness/general/harness-review/protocols.md +0 -68
  44. package/resources/harness/general/harness-review/reference.md +0 -86
  45. package/resources/harness/general/harness-run/SKILL.md +0 -132
  46. package/resources/harness/general/harness-run/checklist.md +0 -259
  47. package/resources/harness/general/harness-run/protocols.md +0 -78
  48. package/resources/harness/general/harness-run/reference.md +0 -834
  49. package/resources/harness/general/harness-submit/SKILL.md +0 -159
  50. package/resources/harness/general/harness-submit/checklist.md +0 -407
  51. package/resources/harness/general/harness-submit/reference.md +0 -152
  52. package/resources/harness/general/harness-sync/SKILL.md +0 -82
  53. package/resources/harness/general/harness-sync/reference.md +0 -153
  54. package/resources/harness/general/harness-test/SKILL.md +0 -180
  55. package/resources/harness/general/harness-test/checklist.md +0 -319
  56. package/resources/harness/general/harness-test/pitfalls.md +0 -260
  57. package/resources/harness/general/harness-test/reference.md +0 -791
  58. package/resources/harness/general/protocols/archive-report-protocol.md +0 -175
  59. package/resources/harness/general/protocols/evidence-based-reporting-protocol.md +0 -134
  60. package/resources/harness/general/protocols/ledger-protocol.md +0 -270
  61. package/resources/harness/general/protocols/powershell-protocol.md +0 -181
  62. package/resources/harness/general/protocols/report-pipeline-protocol.md +0 -150
  63. package/resources/harness/general/protocols/sensitive-info-protocol.md +0 -92
  64. package/resources/harness/general/protocols/state-layout-protocol.md +0 -131
  65. package/resources/harness/general/protocols/submit-protocol.md +0 -78
  66. package/resources/harness/general/scripts/harness_archive.py +0 -2319
  67. package/resources/harness/general/scripts/harness_deploy.py +0 -613
  68. package/resources/harness/general/scripts/harness_events.py +0 -680
  69. package/resources/harness/general/scripts/harness_ledger.py +0 -671
  70. package/resources/harness/general/scripts/harness_preflight.py +0 -780
  71. package/resources/harness/general/scripts/harness_service.py +0 -1331
  72. package/resources/harness/java/.harness-build.json +0 -5
  73. package/resources/harness/java/CONTEXT.md +0 -65
  74. package/resources/harness/java/README.md +0 -478
  75. package/resources/harness/java/agents/harness-evaluator.md +0 -96
  76. package/resources/harness/java/agents/harness-explorer.md +0 -72
  77. package/resources/harness/java/agents/harness-reviewer.md +0 -69
  78. package/resources/harness/java/harness-apidoc/SKILL.md +0 -86
  79. package/resources/harness/java/harness-apidoc/checklist.md +0 -142
  80. package/resources/harness/java/harness-apidoc/reference.md +0 -233
  81. package/resources/harness/java/harness-archive/SKILL.md +0 -215
  82. package/resources/harness/java/harness-archive/checklist.md +0 -52
  83. package/resources/harness/java/harness-archive/reference.md +0 -103
  84. package/resources/harness/java/harness-archive/scripts/gen-manifest.ps1 +0 -31
  85. package/resources/harness/java/harness-archive/templates/render-summary.mjs +0 -171
  86. package/resources/harness/java/harness-archive/templates/summary-data-template.json +0 -128
  87. package/resources/harness/java/harness-codebase-map/SKILL.md +0 -112
  88. package/resources/harness/java/harness-codebase-map/checklist.md +0 -126
  89. package/resources/harness/java/harness-codebase-map/reference.md +0 -364
  90. package/resources/harness/java/harness-codebase-map/templates/ARCHITECTURE.md +0 -57
  91. package/resources/harness/java/harness-codebase-map/templates/CONCERNS.md +0 -49
  92. package/resources/harness/java/harness-codebase-map/templates/CONVENTIONS.md +0 -57
  93. package/resources/harness/java/harness-codebase-map/templates/INTEGRATIONS.md +0 -52
  94. package/resources/harness/java/harness-codebase-map/templates/STACK.md +0 -55
  95. package/resources/harness/java/harness-codebase-map/templates/STRUCTURE.md +0 -69
  96. package/resources/harness/java/harness-codebase-map/templates/TESTING.md +0 -64
  97. package/resources/harness/java/harness-codebase-map/templates/map-manifest.schema.json +0 -73
  98. package/resources/harness/java/harness-codebase-map/templates/map-summary.md +0 -64
  99. package/resources/harness/java/harness-knowledge-ingest/SKILL.md +0 -246
  100. package/resources/harness/java/harness-knowledge-ingest/design.md +0 -842
  101. package/resources/harness/java/harness-knowledge-ingest/evaluations/harness_knowledge_evaluation.xml +0 -42
  102. package/resources/harness/java/harness-knowledge-ingest/mcp-config.example.json +0 -10
  103. package/resources/harness/java/harness-knowledge-ingest/reference.md +0 -309
  104. package/resources/harness/java/harness-knowledge-ingest/scripts/harness_knowledge.py +0 -4082
  105. package/resources/harness/java/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py +0 -273
  106. package/resources/harness/java/harness-knowledge-query/SKILL.md +0 -164
  107. package/resources/harness/java/harness-package/SKILL.md +0 -87
  108. package/resources/harness/java/harness-package/checklist.md +0 -322
  109. package/resources/harness/java/harness-package/reference.md +0 -210
  110. package/resources/harness/java/harness-plan/SKILL.md +0 -127
  111. package/resources/harness/java/harness-plan/checklist.md +0 -243
  112. package/resources/harness/java/harness-plan/protocols.md +0 -124
  113. package/resources/harness/java/harness-plan/reference.md +0 -376
  114. package/resources/harness/java/harness-review/SKILL.md +0 -156
  115. package/resources/harness/java/harness-review/checklist.md +0 -124
  116. package/resources/harness/java/harness-review/protocols.md +0 -68
  117. package/resources/harness/java/harness-review/reference.md +0 -86
  118. package/resources/harness/java/harness-run/SKILL.md +0 -148
  119. package/resources/harness/java/harness-run/checklist.md +0 -250
  120. package/resources/harness/java/harness-run/protocols.md +0 -78
  121. package/resources/harness/java/harness-run/reference.md +0 -814
  122. package/resources/harness/java/harness-submit/SKILL.md +0 -166
  123. package/resources/harness/java/harness-submit/checklist.md +0 -407
  124. package/resources/harness/java/harness-submit/reference.md +0 -152
  125. package/resources/harness/java/harness-sync/SKILL.md +0 -82
  126. package/resources/harness/java/harness-sync/reference.md +0 -153
  127. package/resources/harness/java/harness-test/SKILL.md +0 -192
  128. package/resources/harness/java/harness-test/checklist.md +0 -317
  129. package/resources/harness/java/harness-test/pitfalls-java.md +0 -16
  130. package/resources/harness/java/harness-test/pitfalls.md +0 -260
  131. package/resources/harness/java/harness-test/reference.md +0 -788
  132. package/resources/harness/java/protocols/archive-report-protocol.md +0 -175
  133. package/resources/harness/java/protocols/evidence-based-reporting-protocol.md +0 -134
  134. package/resources/harness/java/protocols/ledger-protocol.md +0 -270
  135. package/resources/harness/java/protocols/powershell-protocol.md +0 -181
  136. package/resources/harness/java/protocols/report-pipeline-protocol.md +0 -150
  137. package/resources/harness/java/protocols/sensitive-info-protocol.md +0 -92
  138. package/resources/harness/java/protocols/state-layout-protocol.md +0 -131
  139. package/resources/harness/java/protocols/submit-protocol.md +0 -78
  140. package/resources/harness/java/scripts/harness_archive.py +0 -2319
  141. package/resources/harness/java/scripts/harness_deploy.py +0 -613
  142. package/resources/harness/java/scripts/harness_events.py +0 -680
  143. package/resources/harness/java/scripts/harness_ledger.py +0 -671
  144. package/resources/harness/java/scripts/harness_preflight.py +0 -780
  145. package/resources/harness/java/scripts/harness_service.py +0 -1331
  146. package/resources/harness/manifests/general.json +0 -276
  147. package/resources/harness/manifests/java.json +0 -304
  148. package/resources/harness/migrations/0.1.1/general.json +0 -358
  149. package/resources/harness/migrations/0.1.1/java.json +0 -398
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/bin.ts
4
- import { fileURLToPath, pathToFileURL } from "node:url";
4
+ import { pathToFileURL } from "node:url";
5
5
  import { createInterface } from "node:readline/promises";
6
6
  import { Command, CommanderError } from "commander";
7
7
 
@@ -133,8 +133,14 @@ var baselineManifestSchema = z2.object({
133
133
  project_id: z2.string().regex(/^prj_/).nullable(),
134
134
  complete_project_version: z2.string().regex(/^pv_/).nullable(),
135
135
  artifact_manifest_hash: sha256Schema.nullable(),
136
+ latest_artifact_id: z2.string().regex(/^art_/).nullable().optional(),
136
137
  files: z2.record(z2.string(), baselineFileSchema)
137
138
  }).strict();
139
+ var finalizeProposalSchema = z2.object({
140
+ schema_version: z2.literal(1),
141
+ manifest_sha256: sha256Schema,
142
+ base_artifact_id: z2.string().regex(/^art_/).nullable()
143
+ }).strict();
138
144
  var requestMetadataSchema = z2.object({
139
145
  request_id: z2.uuid(),
140
146
  idempotency_key: z2.uuid(),
@@ -286,7 +292,21 @@ var registrySkillSummarySchema = z3.object({
286
292
  agents: z3.array(agentSkillConfigSchema),
287
293
  revision: z3.number().int().positive(),
288
294
  created_at: z3.iso.datetime(),
289
- updated_at: z3.iso.datetime()
295
+ updated_at: z3.iso.datetime(),
296
+ // npm 发布记录(registry snapshot 可选字段,旧快照缺省为 [])
297
+ npmReleases: z3.array(z3.lazy(() => npmReleaseRecordSchema)).optional().default([])
298
+ }).strict();
299
+ var npmReleaseStatusSchema = z3.enum(["published", "failed", "conflict"]);
300
+ var npmReleaseRecordSchema = z3.object({
301
+ version: registrySemverSchema,
302
+ packageName: z3.string().min(1),
303
+ status: npmReleaseStatusSchema,
304
+ publishedAt: z3.iso.datetime(),
305
+ error: z3.string().nullable().optional().default(null)
306
+ }).strict();
307
+ var npmReleaseResponseSchema = z3.object({
308
+ slug: registrySlugSchema,
309
+ release: npmReleaseRecordSchema
290
310
  }).strict();
291
311
  var registrySkillDetailSchema = registrySkillSummarySchema.extend({
292
312
  ir: z3.unknown().optional(),
@@ -313,34 +333,6 @@ var registryTagSchema = z3.object({
313
333
  created_at: z3.iso.datetime(),
314
334
  updated_at: z3.iso.datetime()
315
335
  }).strict();
316
- var registryWorkflowSchema = z3.object({
317
- workflow_id: z3.string().regex(/^wf_/),
318
- key: registrySlugSchema,
319
- name: z3.string().min(1).max(120),
320
- description: z3.string().min(1).max(1e3),
321
- profile: registrySlugSchema,
322
- default_agent: registryAgentSchema,
323
- enabled: z3.boolean(),
324
- skill_slugs: z3.array(registrySlugSchema),
325
- revision: z3.number().int().positive(),
326
- created_at: z3.iso.datetime(),
327
- updated_at: z3.iso.datetime()
328
- }).strict();
329
- var registryWorkflowMutationSchema = z3.object({
330
- key: registrySlugSchema,
331
- name: z3.string().min(1).max(120),
332
- description: z3.string().min(1).max(1e3),
333
- profile: registrySlugSchema,
334
- default_agent: registryAgentSchema,
335
- enabled: z3.boolean(),
336
- skill_slugs: z3.array(registrySlugSchema)
337
- }).strict();
338
- var registryProjectWorkflowBindingSchema = z3.object({
339
- project_id: z3.string().regex(/^prj_/),
340
- workflow_id: z3.string().regex(/^wf_/),
341
- revision: z3.number().int().positive(),
342
- updated_at: z3.iso.datetime()
343
- }).strict();
344
336
 
345
337
  // ../contracts/dist/ai-config.js
346
338
  var aiProviderApiFormatSchema = z4.enum(["openai", "anthropic", "custom"]);
@@ -458,7 +450,10 @@ var apiErrorCodeSchema = z5.enum([
458
450
  "DRAFT_NOT_FOUND",
459
451
  "REVISION_CONFLICT",
460
452
  "ADAPTER_NOT_INSTALLABLE",
461
- "WORKFLOW_PACKAGE_REDIRECT"
453
+ "WORKFLOW_PACKAGE_REDIRECT",
454
+ "NPM_PUBLISH_NOT_CONFIGURED",
455
+ "NPM_PUBLISH_NOT_PUBLISHED",
456
+ "NPM_PUBLISH_CONFLICT"
462
457
  ]);
463
458
  var apiErrorEnvelopeSchema = z5.object({
464
459
  error: z5.object({
@@ -480,46 +475,90 @@ var cliExitCodeSchema = z5.union([
480
475
  z5.literal(8)
481
476
  ]);
482
477
 
483
- // ../contracts/dist/fix.js
478
+ // ../contracts/dist/external-skill.js
484
479
  import { z as z6 } from "zod";
485
- var fixActionSchema = z6.enum(["auto", "confirm", "suggest"]);
486
- var fixPlanItemSchema = z6.object({
487
- checkId: z6.string(),
480
+ var externalSkillSourceTypeSchema = z6.enum(["npm", "github"]);
481
+ var externalSkillSourceSchema = z6.object({
482
+ type: externalSkillSourceTypeSchema,
483
+ /** npm 包名,或规范化后的 `owner/repo` */
484
+ ref: z6.string().min(1)
485
+ }).strict();
486
+ var externalSkillSnapshotSchema = z6.object({
487
+ name: z6.string(),
488
+ description: z6.string(),
489
+ version: z6.string().nullable(),
490
+ readme: z6.string().nullable(),
491
+ installCommand: z6.string(),
492
+ license: z6.string().nullable(),
493
+ homepage: z6.string().nullable(),
494
+ releaseUrl: z6.string().nullable(),
495
+ fetchedAt: z6.string()
496
+ }).strict();
497
+ var externalSkillSchema = z6.object({
498
+ id: z6.string().regex(/^ext_/),
499
+ source: externalSkillSourceSchema,
500
+ snapshot: externalSkillSnapshotSchema,
501
+ curationNote: z6.string(),
502
+ tags: z6.array(z6.string()),
503
+ updateAvailable: z6.boolean(),
504
+ lastCheckedAt: z6.string(),
505
+ revision: z6.number().int().positive(),
506
+ created_at: z6.string(),
507
+ updated_at: z6.string()
508
+ }).strict();
509
+ var createExternalSkillRequestSchema = z6.object({
510
+ source: externalSkillSourceSchema,
511
+ curationNote: z6.string().default(""),
512
+ tags: z6.array(z6.string()).default([])
513
+ }).strict();
514
+ var patchExternalSkillRequestSchema = z6.object({
515
+ curationNote: z6.string().optional(),
516
+ tags: z6.array(z6.string()).optional(),
517
+ /** 清除「有更新」徽章(不改变 curationNote / snapshot) */
518
+ acknowledgeUpdate: z6.boolean().optional(),
519
+ revision: z6.number().int().positive()
520
+ }).strict();
521
+
522
+ // ../contracts/dist/fix.js
523
+ import { z as z7 } from "zod";
524
+ var fixActionSchema = z7.enum(["auto", "confirm", "suggest"]);
525
+ var fixPlanItemSchema = z7.object({
526
+ checkId: z7.string(),
488
527
  action: fixActionSchema,
489
- label: z6.string(),
490
- affectedPaths: z6.array(z6.string()),
491
- riskDelta: z6.string().nullable(),
492
- message: z6.string(),
528
+ label: z7.string(),
529
+ affectedPaths: z7.array(z7.string()),
530
+ riskDelta: z7.string().nullable(),
531
+ message: z7.string(),
493
532
  // AI 内容生成切片:fix-suggestions 端点填充(fixer.buildFixPatch 填的 item 不带,向后兼容)
494
- suggestedContent: z6.string().nullable().optional(),
495
- explanation: z6.string().nullable().optional(),
496
- appliesTo: z6.enum(["examples", "allowed_capabilities", "instructions", "description", "tags"]).nullable().optional(),
497
- generatedAt: z6.string().nullable().optional()
533
+ suggestedContent: z7.string().nullable().optional(),
534
+ explanation: z7.string().nullable().optional(),
535
+ appliesTo: z7.enum(["examples", "allowed_capabilities", "instructions", "description", "tags"]).nullable().optional(),
536
+ generatedAt: z7.string().nullable().optional()
498
537
  }).strict();
499
- var fixPlanSchema = z6.object({
500
- items: z6.array(fixPlanItemSchema),
501
- mergedFiles: z6.array(skillDiffFileSchema),
502
- summary: z6.object({
503
- autoCount: z6.number().int(),
504
- confirmCount: z6.number().int(),
505
- suggestCount: z6.number().int(),
506
- changedFiles: z6.number().int(),
507
- changedLines: z6.number().int()
538
+ var fixPlanSchema = z7.object({
539
+ items: z7.array(fixPlanItemSchema),
540
+ mergedFiles: z7.array(skillDiffFileSchema),
541
+ summary: z7.object({
542
+ autoCount: z7.number().int(),
543
+ confirmCount: z7.number().int(),
544
+ suggestCount: z7.number().int(),
545
+ changedFiles: z7.number().int(),
546
+ changedLines: z7.number().int()
508
547
  })
509
548
  }).strict();
510
549
 
511
550
  // ../contracts/dist/knowledge.js
512
- import { z as z7 } from "zod";
513
- var knowledgeStatusSchema = z7.enum([
551
+ import { z as z8 } from "zod";
552
+ var knowledgeStatusSchema = z8.enum([
514
553
  "candidate",
515
554
  "active",
516
555
  "stale",
517
556
  "deprecated",
518
557
  "superseded"
519
558
  ]);
520
- var knowledgeFrontmatterSchema = z7.object({
521
- id: z7.string().regex(/^knowledge\.[a-z0-9.-]+$/),
522
- type: z7.enum([
559
+ var knowledgeFrontmatterSchema = z8.object({
560
+ id: z8.string().regex(/^knowledge\.[a-z0-9.-]+$/),
561
+ type: z8.enum([
523
562
  "business",
524
563
  "architecture",
525
564
  "decision",
@@ -528,7 +567,7 @@ var knowledgeFrontmatterSchema = z7.object({
528
567
  "glossary",
529
568
  "project-local"
530
569
  ]),
531
- scope: z7.enum([
570
+ scope: z8.enum([
532
571
  "global",
533
572
  "project",
534
573
  "module",
@@ -537,207 +576,435 @@ var knowledgeFrontmatterSchema = z7.object({
537
576
  "business-domain",
538
577
  "local"
539
578
  ]),
540
- confidence: z7.enum(["verified", "inferred", "unverified", "low"]),
579
+ confidence: z8.enum(["verified", "inferred", "unverified", "low"]),
541
580
  status: knowledgeStatusSchema,
542
- domains: z7.array(z7.string()),
543
- modules: z7.array(z7.string()),
544
- related_paths: z7.array(z7.string()),
545
- source: z7.object({
546
- kind: z7.enum(["manual", "archive", "review", "imported", "test", "design"]),
547
- ref: z7.string().min(1)
581
+ domains: z8.array(z8.string()),
582
+ modules: z8.array(z8.string()),
583
+ related_paths: z8.array(z8.string()),
584
+ source: z8.object({
585
+ kind: z8.enum(["manual", "archive", "review", "imported", "test", "design"]),
586
+ ref: z8.string().min(1)
587
+ }).strict(),
588
+ created_at: z8.iso.datetime(),
589
+ updated_at: z8.iso.datetime(),
590
+ last_verified_at: z8.iso.datetime().nullable(),
591
+ expires_at: z8.iso.datetime().nullable(),
592
+ supersedes: z8.array(z8.string()),
593
+ superseded_by: z8.array(z8.string())
594
+ }).strict();
595
+ var knowledgeMarkdownIndexSchema = z8.object({
596
+ schema_version: z8.literal(1),
597
+ generated_at: z8.iso.datetime(),
598
+ entries: z8.array(z8.object({
599
+ id: z8.string(),
600
+ type: z8.string(),
601
+ scope: z8.string(),
602
+ confidence: z8.string(),
603
+ status: knowledgeStatusSchema,
604
+ domains: z8.array(z8.string()),
605
+ modules: z8.array(z8.string()),
606
+ related_paths: z8.array(z8.string()),
607
+ source: knowledgeFrontmatterSchema.shape.source,
608
+ created_at: z8.iso.datetime(),
609
+ updated_at: z8.iso.datetime(),
610
+ last_verified_at: z8.iso.datetime().nullable(),
611
+ expires_at: z8.iso.datetime().nullable(),
612
+ supersedes: z8.array(z8.string()),
613
+ superseded_by: z8.array(z8.string()),
614
+ path: z8.string(),
615
+ content_sha256: z8.string(),
616
+ summary: z8.string(),
617
+ local: z8.boolean()
618
+ }).strict())
619
+ }).strict();
620
+ var knowledgeIngestTimestampSchema = z8.string().min(1);
621
+ var knowledgeIngestEntryTypeSchema = z8.enum([
622
+ "requirement",
623
+ "decision",
624
+ "implementation",
625
+ "risk",
626
+ "test-evidence",
627
+ "pitfall",
628
+ "api-contract"
629
+ ]);
630
+ var knowledgeIngestEntryStatusSchema = z8.enum([
631
+ "candidate",
632
+ "active",
633
+ "stale",
634
+ "superseded",
635
+ "deprecated",
636
+ "conflicted"
637
+ ]);
638
+ var knowledgeConfidenceScoreSchema = z8.object({
639
+ score: z8.number(),
640
+ level: z8.string(),
641
+ signals: z8.array(z8.string()),
642
+ lastCalculatedAt: knowledgeIngestTimestampSchema
643
+ }).strict();
644
+ var knowledgeIngestEntryLifecycleSchema = z8.object({
645
+ createdAt: knowledgeIngestTimestampSchema,
646
+ verifiedAt: knowledgeIngestTimestampSchema,
647
+ lastCheckedAt: knowledgeIngestTimestampSchema,
648
+ confidence: z8.string(),
649
+ supersedes: z8.array(z8.string()),
650
+ supersededBy: z8.string().nullable(),
651
+ conflictsWith: z8.array(z8.string()),
652
+ staleReasons: z8.array(z8.string()),
653
+ promotedAt: knowledgeIngestTimestampSchema.optional(),
654
+ promotionNote: z8.string().optional(),
655
+ demotedAt: knowledgeIngestTimestampSchema.optional(),
656
+ validation: z8.object({
657
+ validatedAt: knowledgeIngestTimestampSchema,
658
+ status: z8.enum(["passed", "failed", "skipped"]),
659
+ results: z8.array(z8.record(z8.string(), z8.unknown()))
660
+ }).strict().optional()
661
+ }).strict();
662
+ var knowledgeIngestEntrySourceSchema = z8.object({
663
+ archive: z8.string(),
664
+ summaryData: z8.string(),
665
+ summarySha256: z8.string(),
666
+ sourceCommit: z8.string(),
667
+ baseCommit: z8.string(),
668
+ changeName: z8.string(),
669
+ finalStatus: z8.string()
670
+ }).strict();
671
+ var knowledgeIngestEntrySchema = z8.object({
672
+ schemaVersion: z8.literal(1),
673
+ id: z8.string().min(1),
674
+ projectId: z8.string().min(1),
675
+ type: knowledgeIngestEntryTypeSchema,
676
+ status: knowledgeIngestEntryStatusSchema,
677
+ title: z8.string(),
678
+ summary: z8.string(),
679
+ body: z8.string(),
680
+ keywords: z8.array(z8.string()),
681
+ source: knowledgeIngestEntrySourceSchema,
682
+ scope: z8.object({
683
+ sourceFiles: z8.array(z8.string()),
684
+ staleIfPathsChanged: z8.array(z8.string()).optional()
548
685
  }).strict(),
549
- created_at: z7.iso.datetime(),
550
- updated_at: z7.iso.datetime(),
551
- last_verified_at: z7.iso.datetime().nullable(),
552
- expires_at: z7.iso.datetime().nullable(),
553
- supersedes: z7.array(z7.string()),
554
- superseded_by: z7.array(z7.string())
686
+ lifecycle: knowledgeIngestEntryLifecycleSchema,
687
+ confidence: knowledgeConfidenceScoreSchema.optional(),
688
+ validators: z8.array(z8.record(z8.string(), z8.unknown())).optional(),
689
+ reviewReasons: z8.array(z8.string()).optional()
690
+ }).strict();
691
+ var knowledgeIngestIndexEntryRefSchema = z8.object({
692
+ id: z8.string(),
693
+ type: knowledgeIngestEntryTypeSchema,
694
+ status: knowledgeIngestEntryStatusSchema,
695
+ title: z8.string(),
696
+ sourceArchive: z8.string(),
697
+ sourceCommit: z8.string(),
698
+ sourceFiles: z8.array(z8.string()),
699
+ confidence: z8.union([knowledgeConfidenceScoreSchema, z8.object({}).strict()])
700
+ }).strict();
701
+ var knowledgeIngestIndexSchema = z8.object({
702
+ schemaVersion: z8.literal(1),
703
+ generatedAt: knowledgeIngestTimestampSchema,
704
+ projectId: z8.string(),
705
+ projectRoot: z8.string(),
706
+ headCommit: z8.string().nullable().optional(),
707
+ archives: z8.object({
708
+ scanned: z8.number().int().nonnegative(),
709
+ indexed: z8.number().int().nonnegative(),
710
+ failed: z8.number().int().nonnegative(),
711
+ items: z8.array(z8.object({
712
+ archive: z8.string(),
713
+ summaryData: z8.string(),
714
+ summarySha256: z8.string(),
715
+ mtime: knowledgeIngestTimestampSchema
716
+ }).strict())
717
+ }).strict(),
718
+ stats: z8.record(z8.string(), z8.number().int().nonnegative()),
719
+ byType: z8.record(z8.string(), z8.number().int().nonnegative()),
720
+ duplicatesSkipped: z8.number().int().nonnegative(),
721
+ ingestMode: z8.record(z8.string(), z8.unknown()),
722
+ failures: z8.array(z8.record(z8.string(), z8.string())),
723
+ entries: z8.array(knowledgeIngestIndexEntryRefSchema)
555
724
  }).strict();
556
725
 
557
726
  // ../contracts/dist/mcp-contract.js
558
- import { z as z8 } from "zod";
559
- var mcpToolContractSchema = z8.object({
560
- tool_name: z8.string(),
561
- description: z8.string(),
562
- input_schema: z8.record(z8.string(), z8.unknown())
727
+ import { z as z9 } from "zod";
728
+ var mcpToolContractSchema = z9.object({
729
+ tool_name: z9.string(),
730
+ description: z9.string(),
731
+ input_schema: z9.record(z9.string(), z9.unknown())
563
732
  }).strict();
564
733
 
565
734
  // ../contracts/dist/project.js
566
- import { z as z9 } from "zod";
567
- var projectIdSchema = z9.string().regex(/^prj_[A-Za-z0-9_-]+$/);
568
- var tokenEnvSchema = z9.string().regex(/^[A-Z_][A-Z0-9_]*$/);
569
- var httpsUrlSchema = z9.url().refine((value) => new URL(value).protocol === "https:", "server URL must use HTTPS");
570
- var adapterNameSchema = z9.enum([
735
+ import { z as z10 } from "zod";
736
+ var projectIdSchema = z10.string().regex(/^prj_[A-Za-z0-9_-]+$/);
737
+ var tokenEnvSchema = z10.string().regex(/^[A-Z_][A-Z0-9_]*$/);
738
+ var httpsUrlSchema = z10.url().refine((value) => new URL(value).protocol === "https:", "server URL must use HTTPS");
739
+ var HARNESS_AGENT_ORDER = [
571
740
  "claude-code",
572
741
  "codex",
573
742
  "cursor",
743
+ "codebuddy"
744
+ ];
745
+ var harnessAgentSchema = z10.enum(HARNESS_AGENT_ORDER);
746
+ var codebuddySurfaceSchema = z10.enum(["both", "ide", "cli"]);
747
+ function sortHarnessAgents(agents) {
748
+ return HARNESS_AGENT_ORDER.filter((agent) => agents.includes(agent));
749
+ }
750
+ var adapterNameSchema = z10.enum([
751
+ "claude-code",
752
+ "codex",
753
+ "cursor",
754
+ "codebuddy",
574
755
  "generic",
575
756
  "mcp"
576
757
  ]);
577
- var initConfigSchema = z9.object({
578
- adapter: adapterNameSchema,
579
- profile: z9.enum(["general", "java"]),
758
+ var initConfigSchema = z10.object({
759
+ agents: z10.array(harnessAgentSchema).min(1),
760
+ profile: z10.enum(["general", "java"]),
761
+ codebuddy_surface: codebuddySurfaceSchema.default("both"),
580
762
  server_url: httpsUrlSchema.nullable().optional(),
581
763
  token_env: tokenEnvSchema.nullable().optional(),
582
764
  project_id: projectIdSchema.nullable().optional(),
583
- features: z9.object({
584
- codegraph_check: z9.boolean().default(true),
585
- superpowers_check: z9.boolean().default(true)
765
+ features: z10.object({
766
+ codegraph_check: z10.boolean().default(true),
767
+ superpowers_check: z10.boolean().default(true)
586
768
  }).strict().optional()
587
769
  }).strict();
588
- var projectConfigSchema = z9.object({
589
- harness: z9.object({
590
- name: z9.literal("hunter-harness"),
591
- schema_version: z9.literal(1)
770
+ var projectConfigSchema = z10.object({
771
+ harness: z10.object({
772
+ name: z10.literal("hunter-harness"),
773
+ schema_version: z10.literal(1)
592
774
  }).strict(),
593
- project: z9.object({
594
- name: z9.string().min(1),
595
- root: z9.literal("."),
596
- local_project_key: z9.uuid(),
775
+ project: z10.object({
776
+ name: z10.string().min(1),
777
+ root: z10.literal("."),
778
+ local_project_key: z10.uuid(),
597
779
  project_id: projectIdSchema.nullable(),
598
- profiles: z9.array(z9.string().min(1)).min(1)
780
+ profiles: z10.array(z10.string().min(1)).min(1)
599
781
  }).strict(),
600
- server: z9.object({
782
+ server: z10.object({
601
783
  url: httpsUrlSchema.nullable(),
602
784
  token_env: tokenEnvSchema
603
785
  }).strict(),
604
- adapters: z9.object({
605
- enabled: z9.array(adapterNameSchema).min(1)
606
- }).strict()
786
+ adapters: z10.object({
787
+ enabled: z10.array(adapterNameSchema).min(1)
788
+ }).strict(),
789
+ adapter_options: z10.object({
790
+ codebuddy: z10.object({
791
+ surface: codebuddySurfaceSchema
792
+ }).strict()
793
+ }).strict().optional()
607
794
  }).strict();
608
795
 
609
796
  // ../contracts/dist/dashboard.js
610
- import { z as z10 } from "zod";
611
- var dashboardHealthStatusSchema = z10.enum(["healthy", "attention", "unavailable"]);
612
- var dashboardServiceStatusSchema = z10.enum(["operational", "degraded", "unavailable"]);
613
- var dashboardTrendPointSchema = z10.object({
614
- date: z10.string().regex(/^\d{4}-\d{2}-\d{2}$/),
615
- submitted: z10.number().int().nonnegative(),
616
- approved: z10.number().int().nonnegative(),
617
- rejected: z10.number().int().nonnegative(),
618
- pending: z10.number().int().nonnegative()
797
+ import { z as z11 } from "zod";
798
+ var dashboardHealthStatusSchema = z11.enum(["healthy", "attention", "unavailable"]);
799
+ var dashboardServiceStatusSchema = z11.enum(["operational", "degraded", "unavailable"]);
800
+ var dashboardTrendPointSchema = z11.object({
801
+ date: z11.string().regex(/^\d{4}-\d{2}-\d{2}$/),
802
+ submitted: z11.number().int().nonnegative(),
803
+ approved: z11.number().int().nonnegative(),
804
+ rejected: z11.number().int().nonnegative(),
805
+ pending: z11.number().int().nonnegative()
619
806
  }).strict();
620
- var dashboardDistributionItemSchema = z10.object({
621
- key: z10.string().min(1),
622
- count: z10.number().int().nonnegative()
807
+ var dashboardDistributionItemSchema = z11.object({
808
+ key: z11.string().min(1),
809
+ count: z11.number().int().nonnegative()
623
810
  }).strict();
624
- var dashboardHealthItemSchema = z10.object({
625
- key: z10.string().min(1),
626
- label: z10.string().min(1),
811
+ var dashboardHealthItemSchema = z11.object({
812
+ key: z11.string().min(1),
813
+ label: z11.string().min(1),
627
814
  status: dashboardHealthStatusSchema,
628
- value: z10.string().min(1),
629
- detail: z10.string().min(1)
815
+ value: z11.string().min(1),
816
+ detail: z11.string().min(1)
630
817
  }).strict();
631
- var dashboardServiceItemSchema = z10.object({
632
- key: z10.string().min(1),
633
- label: z10.string().min(1),
818
+ var dashboardServiceItemSchema = z11.object({
819
+ key: z11.string().min(1),
820
+ label: z11.string().min(1),
634
821
  status: dashboardServiceStatusSchema,
635
- detail: z10.string().min(1),
636
- checked_at: z10.iso.datetime()
822
+ detail: z11.string().min(1),
823
+ checked_at: z11.iso.datetime()
637
824
  }).strict();
638
- var dashboardActivitySchema = z10.object({
639
- event_id: z10.string().min(1),
640
- action: z10.string().min(1),
641
- target_id: z10.string().min(1),
642
- project_id: z10.string().nullable(),
643
- actor_id: z10.string().min(1),
644
- created_at: z10.iso.datetime()
825
+ var dashboardActivitySchema = z11.object({
826
+ event_id: z11.string().min(1),
827
+ action: z11.string().min(1),
828
+ target_id: z11.string().min(1),
829
+ project_id: z11.string().nullable(),
830
+ actor_id: z11.string().min(1),
831
+ created_at: z11.iso.datetime()
645
832
  }).strict();
646
- var dashboardOverviewSchema = z10.object({
647
- generated_at: z10.iso.datetime(),
648
- window: z10.object({
649
- days: z10.number().int().min(7).max(30),
650
- starts_at: z10.iso.datetime(),
651
- ends_at: z10.iso.datetime()
833
+ var dashboardOverviewSchema = z11.object({
834
+ generated_at: z11.iso.datetime(),
835
+ window: z11.object({
836
+ days: z11.number().int().min(7).max(30),
837
+ starts_at: z11.iso.datetime(),
838
+ ends_at: z11.iso.datetime()
652
839
  }).strict(),
653
- metrics: z10.object({
654
- projects: z10.number().int().nonnegative(),
655
- workflows: z10.number().int().nonnegative(),
656
- skills: z10.number().int().nonnegative(),
657
- published_skills: z10.number().int().nonnegative(),
658
- pending_reviews: z10.number().int().nonnegative(),
659
- approved_proposals: z10.number().int().nonnegative(),
660
- rejected_proposals: z10.number().int().nonnegative(),
661
- artifacts: z10.number().int().nonnegative(),
662
- project_artifacts: z10.number().int().nonnegative(),
663
- skill_artifacts: z10.number().int().nonnegative()
840
+ metrics: z11.object({
841
+ projects: z11.number().int().nonnegative(),
842
+ workflows: z11.number().int().nonnegative(),
843
+ skills: z11.number().int().nonnegative(),
844
+ published_skills: z11.number().int().nonnegative(),
845
+ pending_reviews: z11.number().int().nonnegative(),
846
+ approved_proposals: z11.number().int().nonnegative(),
847
+ rejected_proposals: z11.number().int().nonnegative(),
848
+ artifacts: z11.number().int().nonnegative(),
849
+ project_artifacts: z11.number().int().nonnegative(),
850
+ skill_artifacts: z11.number().int().nonnegative()
664
851
  }).strict(),
665
- trend: z10.array(dashboardTrendPointSchema),
666
- distributions: z10.object({
667
- skill_categories: z10.array(dashboardDistributionItemSchema),
668
- workflow_profiles: z10.array(dashboardDistributionItemSchema)
852
+ trend: z11.array(dashboardTrendPointSchema),
853
+ distributions: z11.object({
854
+ skill_categories: z11.array(dashboardDistributionItemSchema),
855
+ workflow_profiles: z11.array(dashboardDistributionItemSchema)
669
856
  }).strict(),
670
- health: z10.array(dashboardHealthItemSchema),
671
- services: z10.array(dashboardServiceItemSchema),
672
- activity: z10.array(dashboardActivitySchema)
857
+ health: z11.array(dashboardHealthItemSchema),
858
+ services: z11.array(dashboardServiceItemSchema),
859
+ activity: z11.array(dashboardActivitySchema)
673
860
  }).strict();
674
861
 
675
- // ../contracts/dist/workflow-package.js
676
- import { z as z11 } from "zod";
677
- var workflowPackageManifestSchema = z11.object({
678
- key: registrySlugSchema,
679
- name: z11.string().min(1).max(120),
680
- description: z11.string().min(1).max(1e3),
862
+ // ../contracts/dist/semantic.js
863
+ import { z as z12 } from "zod";
864
+ var semanticDocumentKindSchema = z12.enum([
865
+ "knowledge_entry",
866
+ "knowledge_markdown",
867
+ "rule",
868
+ "archive_record",
869
+ "agent_instruction"
870
+ ]);
871
+ var semanticEdgeKindSchema = z12.enum([
872
+ "references_path",
873
+ "supersedes",
874
+ "related_archive",
875
+ "tag_cooccurrence"
876
+ ]);
877
+ var semanticDocumentSchema = z12.object({
878
+ document_id: z12.string(),
879
+ project_id: z12.string(),
880
+ artifact_id: z12.string(),
881
+ kind: semanticDocumentKindSchema,
882
+ source_path: z12.string(),
883
+ title: z12.string(),
884
+ body: z12.string(),
885
+ metadata: z12.record(z12.string(), z12.unknown()),
886
+ content_sha256: z12.string()
887
+ }).strict();
888
+ var semanticEdgeSchema = z12.object({
889
+ edge_id: z12.string(),
890
+ project_id: z12.string(),
891
+ artifact_id: z12.string(),
892
+ from_document_id: z12.string(),
893
+ to_document_id: z12.string(),
894
+ kind: semanticEdgeKindSchema,
895
+ metadata: z12.record(z12.string(), z12.unknown())
896
+ }).strict();
897
+ var semanticIndexBuildSchema = z12.object({
898
+ project_id: z12.string(),
899
+ artifact_id: z12.string(),
900
+ documents: z12.array(semanticDocumentSchema),
901
+ edges: z12.array(semanticEdgeSchema)
902
+ }).strict();
903
+ var semanticOverviewSchema = z12.object({
904
+ project_id: z12.string(),
905
+ artifact_id: z12.string().nullable(),
906
+ counts: z12.object({
907
+ documents: z12.number().int().nonnegative(),
908
+ knowledge: z12.number().int().nonnegative(),
909
+ rules: z12.number().int().nonnegative(),
910
+ changes: z12.number().int().nonnegative(),
911
+ agent_instructions: z12.number().int().nonnegative(),
912
+ edges: z12.number().int().nonnegative()
913
+ }).strict()
914
+ }).strict();
915
+ var semanticSearchHitSchema = z12.object({
916
+ document: semanticDocumentSchema,
917
+ project_id: z12.string()
918
+ }).strict();
919
+
920
+ // ../contracts/dist/workflow-family.js
921
+ import { z as z13 } from "zod";
922
+ var workflowBundleManifestSchema = z13.object({
923
+ schema_version: z13.literal(1),
681
924
  profile: registrySlugSchema,
682
- skills: z11.array(z11.object({
683
- slug: registrySlugSchema,
684
- ref: z11.string()
685
- }).strict()),
686
- agents: z11.array(z11.object({
687
- path: z11.string(),
688
- ref: z11.string()
689
- }).strict()),
690
- protocols: z11.array(z11.object({
691
- path: z11.string(),
692
- ref: z11.string()
693
- }).strict()),
694
- templates: z11.array(z11.object({
695
- path: z11.string(),
696
- ref: z11.string()
697
- }).strict()),
698
- execution_order: z11.array(z11.string()),
699
- strategy: z11.enum(["sequential", "parallel", "manual"])
925
+ files: z13.array(z13.object({
926
+ path: z13.string().min(1),
927
+ sha256: sha256Schema
928
+ }).strict()).min(1)
700
929
  }).strict();
701
- var workflowPackageArtifactSchema = z11.object({
702
- artifact_id: z11.string().regex(/^wfa_/),
703
- package_key: registrySlugSchema,
930
+ var workflowFamilyBundleArtifactSchema = z13.object({
931
+ artifact_id: z13.string().regex(/^wfb_/),
932
+ family_slug: registrySlugSchema,
933
+ profile: registrySlugSchema,
704
934
  version: registrySemverSchema,
705
935
  content_sha256: sha256Schema,
706
- size_bytes: z11.number().int().nonnegative(),
707
- created_at: z11.iso.datetime()
936
+ size_bytes: z13.number().int().nonnegative(),
937
+ bundle_manifest: workflowBundleManifestSchema,
938
+ created_at: z13.iso.datetime()
939
+ }).strict();
940
+ var workflowFamilyVersionProfileSchema = z13.object({
941
+ profile: registrySlugSchema,
942
+ bundle_manifest: workflowBundleManifestSchema,
943
+ artifact_id: z13.string().regex(/^wfb_/),
944
+ sourceFiles: z13.array(sourceFileSchema).default([])
708
945
  }).strict();
709
- var workflowPackageVersionSchema = z11.object({
710
- package_key: registrySlugSchema,
946
+ var workflowFamilyVersionSchema = z13.object({
947
+ family_slug: registrySlugSchema,
711
948
  version: registrySemverSchema,
712
- manifest: workflowPackageManifestSchema,
713
- artifacts: z11.array(workflowPackageArtifactSchema),
714
- sourceFiles: z11.array(sourceFileSchema).default([]),
715
- changeNote: z11.string().nullable(),
716
- created_at: z11.iso.datetime()
949
+ profiles: z13.array(workflowFamilyVersionProfileSchema).min(1),
950
+ artifacts: z13.array(workflowFamilyBundleArtifactSchema),
951
+ changeNote: z13.string().nullable(),
952
+ created_at: z13.iso.datetime()
953
+ }).strict();
954
+ var workflowFamilyDraftProfileSchema = z13.object({
955
+ profile: registrySlugSchema,
956
+ sourceFiles: z13.array(sourceFileSchema),
957
+ bundle_manifest: workflowBundleManifestSchema
717
958
  }).strict();
718
- var workflowPackageDraftStateSchema = z11.object({
719
- key: registrySlugSchema,
720
- manifest: workflowPackageManifestSchema,
721
- sourceFiles: z11.array(sourceFileSchema),
959
+ var workflowFamilyDraftStateSchema = z13.object({
960
+ family_slug: registrySlugSchema,
961
+ profiles: z13.array(workflowFamilyDraftProfileSchema),
962
+ required_profiles: z13.array(registrySlugSchema),
722
963
  draftVersion: registrySemverSchema.nullable(),
723
964
  checks: skillCheckResultSchema.nullable(),
724
- releaseNote: z11.string().nullable(),
725
- revision: z11.number().int(),
726
- created_at: z11.string(),
727
- updated_at: z11.string()
965
+ releaseNote: z13.string().nullable(),
966
+ revision: z13.number().int(),
967
+ created_at: z13.string(),
968
+ updated_at: z13.string()
728
969
  }).strict();
729
- var workflowPackageSchema = z11.object({
730
- package_id: z11.string().regex(/^wfp_/),
731
- key: registrySlugSchema,
732
- manifest: workflowPackageManifestSchema,
733
- latestVersion: registrySemverSchema.nullable(),
734
- revision: z11.number().int().positive(),
735
- created_at: z11.iso.datetime(),
736
- updated_at: z11.iso.datetime()
970
+ var workflowFamilySchema = z13.object({
971
+ family_id: z13.string().regex(/^wff_/),
972
+ slug: registrySlugSchema,
973
+ displayName: z13.string().min(1).max(120),
974
+ description: z13.string().min(1).max(1e3),
975
+ tags: z13.array(registrySlugSchema).default([]),
976
+ latest_version: registrySemverSchema.nullable(),
977
+ required_profiles: z13.array(registrySlugSchema).min(1),
978
+ revision: z13.number().int().positive(),
979
+ npmReleases: z13.array(npmReleaseRecordSchema).optional().default([]),
980
+ created_at: z13.iso.datetime(),
981
+ updated_at: z13.iso.datetime()
737
982
  }).strict();
738
- var publishWorkflowPackageRequestSchema = z11.object({
983
+ var workflowFamilyMutationSchema = z13.object({
984
+ slug: registrySlugSchema,
985
+ displayName: z13.string().min(1).max(120),
986
+ description: z13.string().min(1).max(1e3),
987
+ tags: z13.array(registrySlugSchema).default([]),
988
+ required_profiles: z13.array(registrySlugSchema).min(1)
989
+ }).strict();
990
+ var publishWorkflowFamilyRequestSchema = z13.object({
739
991
  version: registrySemverSchema,
740
- releaseNote: z11.string().optional()
992
+ releaseNote: z13.string().optional()
993
+ }).strict();
994
+ var registryProjectWorkflowBindingSchema = z13.object({
995
+ project_id: z13.string().regex(/^prj_/),
996
+ family_slug: registrySlugSchema,
997
+ profile: registrySlugSchema,
998
+ version: registrySemverSchema.nullable().optional(),
999
+ revision: z13.number().int().positive(),
1000
+ updated_at: z13.iso.datetime()
1001
+ }).strict();
1002
+ var bindProjectWorkflowFamilyRequestSchema = z13.object({
1003
+ schema_version: z13.literal(1),
1004
+ family_slug: registrySlugSchema,
1005
+ profile: registrySlugSchema,
1006
+ version: registrySemverSchema.nullable().optional(),
1007
+ revision: z13.number().int().nullable()
741
1008
  }).strict();
742
1009
 
743
1010
  // ../core/dist/fs/hash.js
@@ -1063,12 +1330,12 @@ function renderBlock(content, newline) {
1063
1330
  return MANAGED_BLOCK_START + newline + normalized + newline + MANAGED_BLOCK_END;
1064
1331
  }
1065
1332
  function extractManagedBlock(content) {
1066
- if (validateMarkers(content) === "absent") {
1067
- return null;
1333
+ if (validateMarkers(content) === "present") {
1334
+ const start = content.indexOf(MANAGED_BLOCK_START) + MANAGED_BLOCK_START.length;
1335
+ const end = content.indexOf(MANAGED_BLOCK_END);
1336
+ return content.slice(start, end).replace(/^\r?\n/, "").replace(/\r?\n$/, "");
1068
1337
  }
1069
- const start = content.indexOf(MANAGED_BLOCK_START) + MANAGED_BLOCK_START.length;
1070
- const end = content.indexOf(MANAGED_BLOCK_END);
1071
- return content.slice(start, end).replace(/^\r?\n/, "").replace(/\r?\n$/, "");
1338
+ return extractSingleManagedBlockById(content)?.content ?? null;
1072
1339
  }
1073
1340
  function upsertManagedBlock(original, content) {
1074
1341
  const newline = original.includes("\r\n") ? "\r\n" : "\n";
@@ -1094,25 +1361,34 @@ function removeManagedBlock(original) {
1094
1361
  const after = original.slice(end).replace(/^(?:\r?\n){1,2}/, "");
1095
1362
  return before + after;
1096
1363
  }
1097
- function refreshManagedBlock(original, blockContent) {
1098
- const starts = markerCount(original, MANAGED_BLOCK_START);
1099
- const ends = markerCount(original, MANAGED_BLOCK_END);
1100
- const absent = starts === 0 && ends === 0;
1101
- const malformed = !absent && (starts !== 1 || ends !== 1 || original.indexOf(MANAGED_BLOCK_START) > original.indexOf(MANAGED_BLOCK_END));
1102
- if (malformed) {
1103
- return { content: original, action: "preserved_conflict", conflict: true };
1104
- }
1105
- const action = absent ? "appended" : "refreshed";
1106
- return { content: upsertManagedBlock(original, blockContent), action, conflict: false };
1107
- }
1108
1364
  function escapeRe(value) {
1109
1365
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1110
1366
  }
1111
1367
  var startById = (id) => `<!-- hunter-harness:start id=${id} -->`;
1112
1368
  var endById = (id) => `<!-- hunter-harness:end id=${id} -->`;
1369
+ function extractSingleManagedBlockById(content) {
1370
+ const matches = [...content.matchAll(/<!-- hunter-harness:start id=([A-Za-z0-9_-]+) -->/g)];
1371
+ if (matches.length !== 1)
1372
+ return null;
1373
+ const id = matches[0]?.[1];
1374
+ if (id === void 0)
1375
+ return null;
1376
+ const start = startById(id);
1377
+ const end = endById(id);
1378
+ if (markerCount(content, start) !== 1 || markerCount(content, end) !== 1 || content.indexOf(start) > content.indexOf(end)) {
1379
+ throw new Error("managed block markers are malformed or duplicated");
1380
+ }
1381
+ const bodyStart = content.indexOf(start) + start.length;
1382
+ const bodyEnd = content.indexOf(end);
1383
+ return {
1384
+ id,
1385
+ content: content.slice(bodyStart, bodyEnd).replace(/^\r?\n/, "").replace(/\r?\n$/, "")
1386
+ };
1387
+ }
1113
1388
  function upsertManagedBlockById(original, id, content) {
1114
1389
  const newline = original.includes("\r\n") ? "\r\n" : "\n";
1115
- const block = startById(id) + newline + content + newline + endById(id);
1390
+ const normalized = content.replace(/\r\n/g, "\n").replace(/\n/g, newline);
1391
+ const block = startById(id) + newline + normalized + newline + endById(id);
1116
1392
  const re = new RegExp(escapeRe(startById(id)) + "[\\s\\S]*?" + escapeRe(endById(id)));
1117
1393
  if (re.test(original)) {
1118
1394
  return original.replace(re, block);
@@ -1120,6 +1396,68 @@ function upsertManagedBlockById(original, id, content) {
1120
1396
  const separator = original.length === 0 ? "" : original.endsWith(newline) ? newline : newline + newline;
1121
1397
  return original + separator + block + newline;
1122
1398
  }
1399
+ function refreshManagedBlockById(original, id, blockContent, options = {}) {
1400
+ const idStart = startById(id);
1401
+ const idEnd = endById(id);
1402
+ const idStarts = markerCount(original, idStart);
1403
+ const idEnds = markerCount(original, idEnd);
1404
+ if (idStarts > 0 || idEnds > 0) {
1405
+ if (idStarts !== 1 || idEnds !== 1 || original.indexOf(idStart) > original.indexOf(idEnd)) {
1406
+ return { content: original, action: "preserved_conflict", conflict: true };
1407
+ }
1408
+ return {
1409
+ content: upsertManagedBlockById(original, id, blockContent),
1410
+ action: "refreshed",
1411
+ conflict: false
1412
+ };
1413
+ }
1414
+ const legacyStarts = markerCount(original, MANAGED_BLOCK_START);
1415
+ const legacyEnds = markerCount(original, MANAGED_BLOCK_END);
1416
+ const legacyAbsent = legacyStarts === 0 && legacyEnds === 0;
1417
+ const legacyMalformed = !legacyAbsent && (legacyStarts !== 1 || legacyEnds !== 1 || original.indexOf(MANAGED_BLOCK_START) > original.indexOf(MANAGED_BLOCK_END));
1418
+ if (legacyMalformed) {
1419
+ return { content: original, action: "preserved_conflict", conflict: true };
1420
+ }
1421
+ if (!legacyAbsent && options.upgradeLegacy === true) {
1422
+ const newline = original.includes("\r\n") ? "\r\n" : "\n";
1423
+ const normalized = blockContent.replace(/\r\n/g, "\n").replace(/\n/g, newline);
1424
+ const block = idStart + newline + normalized + newline + idEnd;
1425
+ const start = original.indexOf(MANAGED_BLOCK_START);
1426
+ const end = original.indexOf(MANAGED_BLOCK_END) + MANAGED_BLOCK_END.length;
1427
+ return {
1428
+ content: original.slice(0, start) + block + original.slice(end),
1429
+ action: "refreshed",
1430
+ conflict: false
1431
+ };
1432
+ }
1433
+ if (!legacyAbsent) {
1434
+ return {
1435
+ content: upsertManagedBlockById(original, id, blockContent),
1436
+ action: "appended",
1437
+ conflict: false
1438
+ };
1439
+ }
1440
+ return {
1441
+ content: upsertManagedBlockById(original, id, blockContent),
1442
+ action: "appended",
1443
+ conflict: false
1444
+ };
1445
+ }
1446
+ function removeManagedBlockById(original, id) {
1447
+ const idStart = startById(id);
1448
+ const idEnd = endById(id);
1449
+ if (markerCount(original, idStart) === 0 && markerCount(original, idEnd) === 0) {
1450
+ return original;
1451
+ }
1452
+ if (markerCount(original, idStart) !== 1 || markerCount(original, idEnd) !== 1 || original.indexOf(idStart) > original.indexOf(idEnd)) {
1453
+ throw new Error("managed block markers are malformed or duplicated");
1454
+ }
1455
+ const start = original.indexOf(idStart);
1456
+ const end = original.indexOf(idEnd) + idEnd.length;
1457
+ const before = original.slice(0, start).replace(/(?:\r?\n){2}$/, "\n");
1458
+ const after = original.slice(end).replace(/^(?:\r?\n){1,2}/, "");
1459
+ return before + after;
1460
+ }
1123
1461
 
1124
1462
  // ../core/dist/policy/file-policy.js
1125
1463
  var USER_MANAGED_BLOCK = {
@@ -1136,13 +1474,6 @@ var USER_DIFF = {
1136
1474
  update_policy: "skip-if-local-dirty",
1137
1475
  conflict_policy: "skip-and-report"
1138
1476
  };
1139
- var USER_FULL = {
1140
- file_kind: "user_editable",
1141
- edit_policy: "allow",
1142
- push_policy: "full-diff-proposal",
1143
- update_policy: "skip-if-local-dirty",
1144
- conflict_policy: "skip-and-report"
1145
- };
1146
1477
  var PROJECT_LOCAL = {
1147
1478
  file_kind: "user_editable",
1148
1479
  edit_policy: "allow",
@@ -1197,10 +1528,10 @@ function under(path, prefix) {
1197
1528
  }
1198
1529
  function classifyFile(input) {
1199
1530
  const path = normalizeManagedPath(input);
1200
- if (path === "CLAUDE.md" || path === "AGENTS.md") {
1531
+ if (path === "CLAUDE.md" || path === "AGENTS.md" || path === "CODEBUDDY.md") {
1201
1532
  return USER_MANAGED_BLOCK;
1202
1533
  }
1203
- if (under(path, ".claude/rules/") || under(path, ".claude/skills/harness-")) {
1534
+ if (under(path, ".claude/rules/") || under(path, ".claude/skills/harness-") || under(path, ".agents/skills/harness-") || under(path, ".cursor/skills/harness-") || under(path, ".codebuddy/skills/harness-") || under(path, ".codebuddy/agents/harness-")) {
1204
1535
  return USER_DIFF;
1205
1536
  }
1206
1537
  if (under(path, ".cursor/rules/") || under(path, ".agent-skills/")) {
@@ -1209,8 +1540,14 @@ function classifyFile(input) {
1209
1540
  if (under(path, ".harness/knowledge/project-local/")) {
1210
1541
  return PROJECT_LOCAL;
1211
1542
  }
1543
+ if (path === ".harness/knowledge/index.sqlite" || under(path, ".harness/knowledge/cache/") || under(path, ".harness/knowledge/views/") || under(path, ".harness/knowledge/context-packs/")) {
1544
+ return GENERATED_CACHE;
1545
+ }
1546
+ if (under(path, ".harness/knowledge/reports/")) {
1547
+ return REPORT_CACHE;
1548
+ }
1212
1549
  if (under(path, ".harness/knowledge/")) {
1213
- return USER_FULL;
1550
+ return USER_DIFF;
1214
1551
  }
1215
1552
  if (under(path, ".harness/codebase/map/") || path === ".harness/codebase/map-summary.md" || path === ".harness/codebase/map-manifest.json") {
1216
1553
  return GENERATED_REVIEWABLE;
@@ -1255,6 +1592,7 @@ function decideUpdate(policy, dirty) {
1255
1592
  }
1256
1593
 
1257
1594
  // ../core/dist/project/initialize.js
1595
+ import { createHash as createHash4 } from "node:crypto";
1258
1596
  import { readFile as readFile4, stat as stat2 } from "node:fs/promises";
1259
1597
  import { basename, join as join5, resolve as resolve3 } from "node:path";
1260
1598
  import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
@@ -1508,13 +1846,20 @@ async function runTransaction(projectRoot, rawOperations, options = {}) {
1508
1846
  return { transactionId, status: "committed" };
1509
1847
  }
1510
1848
 
1849
+ // ../core/dist/project/agent-adapters.js
1850
+ import { createHash as createHash2 } from "node:crypto";
1851
+
1511
1852
  // ../core/dist/project/managed-content.js
1853
+ var AGENTS_CORE_BLOCK_ID = "hunter-harness-core";
1854
+ var CLAUDE_BLOCK_ID = "hunter-harness-claude-code";
1855
+ var CODEBUDDY_BLOCK_ID = "hunter-harness-codebuddy";
1512
1856
  var AGENTS_MANAGED_BLOCK_CONTENT = [
1513
1857
  "# Hunter Harness",
1514
1858
  "",
1515
- "Use .harness/context-index.json to route rules, Knowledge, and codebase maps.",
1516
- "Treat .claude/skills/harness-* as editable adapter working copies.",
1517
- "Do not modify .harness/state or .harness/cache directly."
1859
+ "Use `.harness/context-index.json` to locate the instructions, skills, knowledge,",
1860
+ "and codebase map for the active agent.",
1861
+ "Treat installed `harness-*` skills as editable adapter working copies.",
1862
+ "Do not modify `.harness/state` or `.harness/cache` directly."
1518
1863
  ].join("\n");
1519
1864
  var CLAUDE_MANAGED_BLOCK_CONTENT = [
1520
1865
  "@AGENTS.md",
@@ -1526,54 +1871,61 @@ var CLAUDE_MANAGED_BLOCK_CONTENT = [
1526
1871
  "- Knowledge: .harness/knowledge/",
1527
1872
  "- Codebase map: .harness/codebase/map/"
1528
1873
  ].join("\n");
1874
+ var CODEBUDDY_MANAGED_BLOCK_CONTENT = [
1875
+ "# Hunter Harness",
1876
+ "",
1877
+ "Use `.harness/context-index.json` to locate the instructions, skills, knowledge,",
1878
+ "and codebase map for the active agent.",
1879
+ "Treat installed `harness-*` skills as editable adapter working copies.",
1880
+ "Do not modify `.harness/state` or `.harness/cache` directly.",
1881
+ "",
1882
+ "- Skills: .codebuddy/skills/harness-*/",
1883
+ "- Agents: .codebuddy/agents/harness-*.md",
1884
+ "- Knowledge: .harness/knowledge/",
1885
+ "- Codebase map: .harness/codebase/map/"
1886
+ ].join("\n");
1529
1887
  var HARNESS_GENERAL_RULES_CONTENT = "# Hunter Harness Rules\n\n- Report evidence honestly.\n- Do not execute destructive actions without confirmation.\n";
1530
1888
  var HARNESS_JAVA_RULES_CONTENT = "# Java Profile\n\n- Verify builds and tests with the project build tool.\n";
1889
+ var CURSOR_GENERAL_RULES_CONTENT = "---\ndescription: Hunter Harness project-wide safety and evidence rules\nglobs:\nalwaysApply: true\n---\n\n# Hunter Harness Rules\n\n- Report evidence honestly.\n- Do not execute destructive actions without confirmation.\n";
1890
+ var CURSOR_JAVA_RULES_CONTENT = "---\ndescription: Hunter Harness Java profile rules\nglobs:\nalwaysApply: true\n---\n\n# Java Profile\n\n- Verify builds and tests with the project build tool.\n";
1531
1891
 
1532
- // ../core/dist/project/profile-bundle.js
1533
- import { createHash as createHash2 } from "node:crypto";
1534
- import { readFile as readFile3, readdir as readdir2 } from "node:fs/promises";
1535
- import { join as join4 } from "node:path";
1536
- function isProfile(value) {
1537
- return value === "general" || value === "java";
1538
- }
1892
+ // ../core/dist/project/agent-adapters.js
1893
+ var AGENT_SOURCE_PATH = /^agents\/([^/]+\.md)$/;
1539
1894
  function validateRelativeBundlePath(path) {
1540
1895
  if (typeof path !== "string" || path.length === 0 || path.includes("\0") || path.startsWith("/") || path.startsWith("\\") || /^[A-Za-z]:/.test(path) || path.split("/").some((part) => part === "" || part === "." || part === "..")) {
1541
1896
  throw new Error("invalid Harness Bundle path");
1542
1897
  }
1543
1898
  }
1544
- async function loadProfileBundle(resourcesRoot, profile) {
1545
- const raw = JSON.parse(await readFile3(join4(resourcesRoot, "harness", "manifests", `${profile}.json`), "utf8"));
1546
- if (raw.schema_version !== 1 || raw.profile !== profile || typeof raw.bundle_version !== "string" || raw.generator !== "harness_deploy.py" || !Array.isArray(raw.files)) {
1547
- throw new Error(`invalid ${profile} Harness Bundle manifest`);
1548
- }
1549
- const files = /* @__PURE__ */ new Map();
1550
- for (const item2 of raw.files) {
1551
- validateRelativeBundlePath(item2.path);
1552
- if (typeof item2.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(item2.sha256) || files.has(item2.path)) {
1553
- throw new Error(`invalid ${profile} Harness Bundle manifest entry`);
1554
- }
1555
- const bytes = await readFile3(join4(resourcesRoot, "harness", profile, item2.path));
1556
- if (createHash2("sha256").update(bytes).digest("hex") !== item2.sha256) {
1557
- throw new Error(`Harness Bundle hash mismatch: ${item2.path}`);
1558
- }
1559
- files.set(item2.path, bytes);
1560
- }
1561
- return { manifest: raw, files };
1562
- }
1563
- async function managedBundleTargets(resourcesRoot, profile) {
1564
- const bundle = await loadProfileBundle(resourcesRoot, profile);
1565
- const targets = new Set(bundleTargetContents(bundle).keys());
1566
- if (profile === "java")
1567
- targets.add(".claude/rules/harness-profile-java.md");
1568
- return targets;
1899
+ function ruleTarget(sourcePath, targetPath, content) {
1900
+ return {
1901
+ source_path: sourcePath,
1902
+ target_path: normalizeManagedPath(targetPath),
1903
+ sha256: createHash2("sha256").update(content).digest("hex"),
1904
+ bytes: new TextEncoder().encode(content)
1905
+ };
1569
1906
  }
1570
- var AGENT_SOURCE_PATH = /^agents\/([^/]+\.md)$/;
1571
- function projectBundle(bundle) {
1907
+ function projectBundleFiles(bundle, skillsRoot, agentsRoot) {
1572
1908
  const records = [];
1573
1909
  for (const [sourcePath, bytes] of bundle.files) {
1574
1910
  validateRelativeBundlePath(sourcePath);
1575
1911
  const agent = AGENT_SOURCE_PATH.exec(sourcePath);
1576
- const projectedTarget = agent?.[1] !== void 0 ? `.claude/agents/${agent[1]}` : `.claude/skills/${sourcePath}`;
1912
+ if (agent?.[1] !== void 0) {
1913
+ if (agentsRoot === null)
1914
+ continue;
1915
+ const projectedTarget2 = `${agentsRoot}/${agent[1]}`;
1916
+ const manifestEntry2 = bundle.manifest.files.find((entry) => entry.path === sourcePath);
1917
+ if (manifestEntry2 === void 0) {
1918
+ throw new Error(`Harness Bundle missing manifest entry: ${sourcePath}`);
1919
+ }
1920
+ records.push({
1921
+ source_path: sourcePath,
1922
+ target_path: normalizeManagedPath(projectedTarget2),
1923
+ sha256: manifestEntry2.sha256,
1924
+ bytes
1925
+ });
1926
+ continue;
1927
+ }
1928
+ const projectedTarget = `${skillsRoot}/${sourcePath}`;
1577
1929
  const manifestEntry = bundle.manifest.files.find((entry) => entry.path === sourcePath);
1578
1930
  if (manifestEntry === void 0) {
1579
1931
  throw new Error(`Harness Bundle missing manifest entry: ${sourcePath}`);
@@ -1588,32 +1940,195 @@ function projectBundle(bundle) {
1588
1940
  assertNoCaseCollisions(records.map((record) => record.target_path));
1589
1941
  return records.sort((left, right) => left.target_path.localeCompare(right.target_path));
1590
1942
  }
1591
- function bundleTargetContents(bundle) {
1592
- return new Map(projectBundle(bundle).map((record) => [record.target_path, record.bytes]));
1593
- }
1594
- function ruleTarget(sourcePath, targetPath, content) {
1943
+ function makeAdapter(spec) {
1595
1944
  return {
1596
- source_path: sourcePath,
1597
- target_path: targetPath,
1598
- sha256: createHash2("sha256").update(content).digest("hex"),
1599
- bytes: new TextEncoder().encode(content)
1945
+ name: spec.name,
1946
+ skillsRoot: spec.skillsRoot,
1947
+ rulesRoot: spec.rulesRoot,
1948
+ agentsRoot: spec.agentsRoot,
1949
+ commandsRoot: spec.commandsRoot,
1950
+ supportsExecutableHooks: false,
1951
+ projectInstructionTargets() {
1952
+ return ["AGENTS.md", ...spec.extraInstructionFiles];
1953
+ },
1954
+ projectBundle(bundle) {
1955
+ return projectBundleFiles(bundle, spec.skillsRoot, spec.agentsRoot);
1956
+ },
1957
+ contextIndex(context) {
1958
+ const rules = [];
1959
+ if (spec.rulesRoot !== null && spec.ruleExt !== null) {
1960
+ rules.push(`${spec.rulesRoot}/harness-general${spec.ruleExt}`);
1961
+ if (context.profile === "java") {
1962
+ rules.push(`${spec.rulesRoot}/harness-profile-java${spec.ruleExt}`);
1963
+ }
1964
+ }
1965
+ return {
1966
+ instructions: spec.instructions,
1967
+ skills_root: spec.skillsRoot,
1968
+ rules
1969
+ };
1970
+ },
1971
+ pruneBoundaries() {
1972
+ const boundaries = [spec.skillsRoot];
1973
+ if (spec.agentsRoot !== null)
1974
+ boundaries.push(spec.agentsRoot);
1975
+ if (spec.rulesRoot !== null)
1976
+ boundaries.push(spec.rulesRoot);
1977
+ const top = spec.skillsRoot.split("/")[0];
1978
+ if (top !== void 0)
1979
+ boundaries.push(top);
1980
+ return boundaries;
1981
+ }
1600
1982
  };
1601
1983
  }
1602
- async function managedTargets(resourcesRoot, profile) {
1603
- const bundle = await loadProfileBundle(resourcesRoot, profile);
1604
- const records = projectBundle(bundle).map((record) => ({
1605
- source_path: record.source_path,
1606
- target_path: record.target_path,
1607
- sha256: record.sha256,
1608
- bytes: record.bytes
1609
- }));
1610
- records.push(ruleTarget("rules/harness-general.md", ".claude/rules/harness-general.md", HARNESS_GENERAL_RULES_CONTENT));
1611
- if (profile === "java") {
1612
- records.push(ruleTarget("rules/harness-profile-java.md", ".claude/rules/harness-profile-java.md", HARNESS_JAVA_RULES_CONTENT));
1984
+ var ADAPTERS = {
1985
+ "claude-code": makeAdapter({
1986
+ name: "claude-code",
1987
+ skillsRoot: ".claude/skills",
1988
+ rulesRoot: ".claude/rules",
1989
+ agentsRoot: ".claude/agents",
1990
+ commandsRoot: null,
1991
+ instructions: "CLAUDE.md",
1992
+ extraInstructionFiles: ["CLAUDE.md"],
1993
+ ruleExt: ".md"
1994
+ }),
1995
+ codex: makeAdapter({
1996
+ name: "codex",
1997
+ skillsRoot: ".agents/skills",
1998
+ rulesRoot: null,
1999
+ agentsRoot: null,
2000
+ commandsRoot: null,
2001
+ instructions: "AGENTS.md",
2002
+ extraInstructionFiles: [],
2003
+ ruleExt: null
2004
+ }),
2005
+ cursor: makeAdapter({
2006
+ name: "cursor",
2007
+ skillsRoot: ".cursor/skills",
2008
+ rulesRoot: ".cursor/rules",
2009
+ agentsRoot: null,
2010
+ commandsRoot: ".cursor/commands",
2011
+ instructions: "AGENTS.md",
2012
+ extraInstructionFiles: [],
2013
+ ruleExt: ".mdc"
2014
+ }),
2015
+ codebuddy: makeAdapter({
2016
+ name: "codebuddy",
2017
+ skillsRoot: ".codebuddy/skills",
2018
+ rulesRoot: null,
2019
+ agentsRoot: ".codebuddy/agents",
2020
+ commandsRoot: ".codebuddy/commands",
2021
+ instructions: "CODEBUDDY.md",
2022
+ extraInstructionFiles: ["CODEBUDDY.md"],
2023
+ ruleExt: null
2024
+ })
2025
+ };
2026
+ function getAdapter(name) {
2027
+ return ADAPTERS[name];
2028
+ }
2029
+ function getAdapters(names) {
2030
+ return HARNESS_AGENT_ORDER.filter((n) => names.includes(n)).map(getAdapter);
2031
+ }
2032
+ function managedTargetsFor(adapter, bundle, context) {
2033
+ const records = [...adapter.projectBundle(bundle, context)];
2034
+ if (adapter.name === "claude-code") {
2035
+ records.push(ruleTarget("rules/harness-general.md", ".claude/rules/harness-general.md", HARNESS_GENERAL_RULES_CONTENT));
2036
+ if (context.profile === "java") {
2037
+ records.push(ruleTarget("rules/harness-profile-java.md", ".claude/rules/harness-profile-java.md", HARNESS_JAVA_RULES_CONTENT));
2038
+ }
2039
+ } else if (adapter.name === "cursor") {
2040
+ records.push(ruleTarget("rules/harness-general.mdc", ".cursor/rules/harness-general.mdc", CURSOR_GENERAL_RULES_CONTENT));
2041
+ if (context.profile === "java") {
2042
+ records.push(ruleTarget("rules/harness-profile-java.mdc", ".cursor/rules/harness-profile-java.mdc", CURSOR_JAVA_RULES_CONTENT));
2043
+ }
1613
2044
  }
1614
2045
  assertNoCaseCollisions(records.map((record) => record.target_path));
1615
2046
  return records.sort((left, right) => left.target_path.localeCompare(right.target_path));
1616
2047
  }
2048
+
2049
+ // ../core/dist/project/profile-bundle.js
2050
+ import { createHash as createHash3 } from "node:crypto";
2051
+ import { readFile as readFile3, readdir as readdir2 } from "node:fs/promises";
2052
+ import { join as join4 } from "node:path";
2053
+ var AdapterBundleError = class extends Error {
2054
+ code;
2055
+ exitCode = 7;
2056
+ constructor(code, message) {
2057
+ super(message);
2058
+ this.code = code;
2059
+ this.name = "AdapterBundleError";
2060
+ }
2061
+ };
2062
+ function isEnoent(error) {
2063
+ return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
2064
+ }
2065
+ function isProfile(value) {
2066
+ return value === "general" || value === "java";
2067
+ }
2068
+ function isAgent(value) {
2069
+ return value === "claude-code" || value === "codex" || value === "cursor" || value === "codebuddy";
2070
+ }
2071
+ function validateRelativeBundlePath2(path) {
2072
+ if (typeof path !== "string" || path.length === 0 || path.includes("\0") || path.startsWith("/") || path.startsWith("\\") || /^[A-Za-z]:/.test(path) || path.split("/").some((part) => part === "" || part === "." || part === "..")) {
2073
+ throw new Error("invalid Harness Bundle path");
2074
+ }
2075
+ }
2076
+ async function loadAgentBundle(resourcesRoot, profile, agent) {
2077
+ const manifestPath = join4(resourcesRoot, "harness", "manifests", profile, `${agent}.json`);
2078
+ let manifestText;
2079
+ try {
2080
+ manifestText = await readFile3(manifestPath, "utf8");
2081
+ } catch (error) {
2082
+ if (isEnoent(error)) {
2083
+ throw new AdapterBundleError("ADAPTER_BUNDLE_MISSING", `offline Harness Bundle manifest missing: ${profile}/${agent}`);
2084
+ }
2085
+ throw error;
2086
+ }
2087
+ let raw;
2088
+ try {
2089
+ raw = JSON.parse(manifestText);
2090
+ } catch {
2091
+ throw new AdapterBundleError("ADAPTER_BUNDLE_INVALID", `unparseable ${profile}/${agent} Harness Bundle manifest`);
2092
+ }
2093
+ if (raw.schema_version !== 2 || raw.profile !== profile || raw.adapter !== agent || typeof raw.bundle_version !== "string" || raw.generator !== "harness_deploy.py" || !Array.isArray(raw.files)) {
2094
+ throw new AdapterBundleError("ADAPTER_BUNDLE_INVALID", `invalid ${profile}/${agent} Harness Bundle manifest`);
2095
+ }
2096
+ const files = /* @__PURE__ */ new Map();
2097
+ for (const item2 of raw.files) {
2098
+ try {
2099
+ validateRelativeBundlePath2(item2.path);
2100
+ } catch {
2101
+ throw new AdapterBundleError("ADAPTER_BUNDLE_INVALID", `invalid ${profile}/${agent} Harness Bundle path`);
2102
+ }
2103
+ if (typeof item2.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(item2.sha256) || files.has(item2.path)) {
2104
+ throw new AdapterBundleError("ADAPTER_BUNDLE_INVALID", `invalid ${profile}/${agent} Harness Bundle manifest entry`);
2105
+ }
2106
+ let bytes;
2107
+ try {
2108
+ bytes = await readFile3(join4(resourcesRoot, "harness", "bundles", profile, agent, item2.path));
2109
+ } catch (error) {
2110
+ if (isEnoent(error)) {
2111
+ throw new AdapterBundleError("ADAPTER_BUNDLE_MISSING", `offline Harness Bundle file missing: ${profile}/${agent}/${item2.path}`);
2112
+ }
2113
+ throw error;
2114
+ }
2115
+ if (createHash3("sha256").update(bytes).digest("hex") !== item2.sha256) {
2116
+ throw new AdapterBundleError("ADAPTER_BUNDLE_INVALID", `Harness Bundle hash mismatch: ${profile}/${agent}/${item2.path}`);
2117
+ }
2118
+ files.set(item2.path, bytes);
2119
+ }
2120
+ return { manifest: raw, files };
2121
+ }
2122
+ async function managedBundleTargets(resourcesRoot, profile, agent = "claude-code") {
2123
+ const bundle = await loadAgentBundle(resourcesRoot, profile, agent);
2124
+ const adapter = getAdapter(agent);
2125
+ const context = { profile, codebuddySurface: "both" };
2126
+ const targets = new Set(adapter.projectBundle(bundle, context).map((t) => t.target_path));
2127
+ if (profile === "java" && agent === "claude-code") {
2128
+ targets.add(".claude/rules/harness-profile-java.md");
2129
+ }
2130
+ return targets;
2131
+ }
1617
2132
  function parseHarnessProfile(value) {
1618
2133
  return isProfile(value) ? value : null;
1619
2134
  }
@@ -1622,21 +2137,25 @@ function parseMigrationManifest(raw) {
1622
2137
  throw new Error("invalid Harness migration manifest");
1623
2138
  }
1624
2139
  const record = raw;
1625
- if (record.schema_version !== 1 || !isProfile(record.profile) || typeof record.bundle_version !== "string" || !/^sha256:[a-f0-9]{64}$/.test(String(record.bundle_manifest_hash)) || !Array.isArray(record.projection)) {
2140
+ const schemaVersion = record.schema_version;
2141
+ if (schemaVersion !== 1 && schemaVersion !== 2 || !isProfile(record.profile) || typeof record.bundle_version !== "string" || !/^sha256:[a-f0-9]{64}$/.test(String(record.bundle_manifest_hash)) || !Array.isArray(record.projection)) {
1626
2142
  throw new Error("invalid Harness migration manifest");
1627
2143
  }
2144
+ const adapter = schemaVersion === 1 ? "claude-code" : isAgent(record.adapter) ? record.adapter : (() => {
2145
+ throw new Error("invalid Harness migration manifest adapter");
2146
+ })();
1628
2147
  const projection = [];
1629
2148
  for (const entry of record.projection) {
1630
2149
  if (entry === null || typeof entry !== "object") {
1631
2150
  throw new Error("invalid Harness migration manifest entry");
1632
2151
  }
1633
2152
  const item2 = entry;
1634
- validateRelativeBundlePath(item2.source_path);
2153
+ validateRelativeBundlePath2(item2.source_path);
1635
2154
  if (typeof item2.target_path !== "string" || typeof item2.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(item2.sha256)) {
1636
2155
  throw new Error("invalid Harness migration manifest entry");
1637
2156
  }
1638
2157
  const targetPath = normalizeManagedPath(item2.target_path);
1639
- if (!targetPath.startsWith(".claude/")) {
2158
+ if (schemaVersion === 1 && !targetPath.startsWith(".claude/")) {
1640
2159
  throw new Error("invalid Harness migration manifest target");
1641
2160
  }
1642
2161
  projection.push({
@@ -1647,8 +2166,9 @@ function parseMigrationManifest(raw) {
1647
2166
  }
1648
2167
  assertNoCaseCollisions(projection.map((item2) => item2.target_path));
1649
2168
  return {
1650
- schema_version: 1,
2169
+ schema_version: schemaVersion,
1651
2170
  profile: record.profile,
2171
+ adapter,
1652
2172
  bundle_version: record.bundle_version,
1653
2173
  bundle_manifest_hash: record.bundle_manifest_hash,
1654
2174
  projection
@@ -1697,17 +2217,37 @@ function uuidV7(now = Date.now()) {
1697
2217
  const byte8 = bytes[8] ?? 0;
1698
2218
  bytes[6] = byte6 & 15 | 112;
1699
2219
  bytes[8] = byte8 & 63 | 128;
1700
- const hex = bytes.toString("hex");
2220
+ const hex2 = bytes.toString("hex");
1701
2221
  return [
1702
- hex.slice(0, 8),
1703
- hex.slice(8, 12),
1704
- hex.slice(12, 16),
1705
- hex.slice(16, 20),
1706
- hex.slice(20)
2222
+ hex2.slice(0, 8),
2223
+ hex2.slice(8, 12),
2224
+ hex2.slice(12, 16),
2225
+ hex2.slice(16, 20),
2226
+ hex2.slice(20)
1707
2227
  ].join("-");
1708
2228
  }
1709
2229
 
1710
2230
  // ../core/dist/project/initialize.js
2231
+ var TargetCollisionError = class extends Error {
2232
+ code = "TARGET_COLLISION";
2233
+ exitCode = 7;
2234
+ constructor(targetPath) {
2235
+ super(`TARGET_COLLISION: conflicting bytes for ${targetPath}`);
2236
+ this.name = "TargetCollisionError";
2237
+ }
2238
+ };
2239
+ function hex(bytes) {
2240
+ return createHash4("sha256").update(bytes).digest("hex");
2241
+ }
2242
+ function bytesEqual(left, right) {
2243
+ if (left.byteLength !== right.byteLength)
2244
+ return false;
2245
+ for (let i = 0; i < left.byteLength; i += 1) {
2246
+ if (left[i] !== right[i])
2247
+ return false;
2248
+ }
2249
+ return true;
2250
+ }
1711
2251
  async function readOptional(path) {
1712
2252
  try {
1713
2253
  return await readFile4(path, "utf8");
@@ -1738,14 +2278,76 @@ async function operationFor(root, path, content) {
1738
2278
  return await exists2(join5(root, path)) ? { operation: "modify", path, content } : { operation: "add", path, content };
1739
2279
  }
1740
2280
  var INSTALLED_BUNDLE_PATH = ".harness/state/local/installed-harness-bundle.json";
2281
+ function mergeOwnedTargets(owned) {
2282
+ const byTarget = /* @__PURE__ */ new Map();
2283
+ for (const item2 of owned) {
2284
+ const list = byTarget.get(item2.target_path) ?? [];
2285
+ list.push(item2);
2286
+ byTarget.set(item2.target_path, list);
2287
+ }
2288
+ const merged = [];
2289
+ for (const [targetPath, items] of [...byTarget.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
2290
+ const first = items[0];
2291
+ if (first === void 0) {
2292
+ throw new Error(`missing projected targets for ${targetPath}`);
2293
+ }
2294
+ for (const item2 of items.slice(1)) {
2295
+ if (!bytesEqual(first.bytes, item2.bytes) || first.sha256 !== item2.sha256) {
2296
+ throw new TargetCollisionError(targetPath);
2297
+ }
2298
+ }
2299
+ const owners = new Set(items.map((item2) => item2.owner));
2300
+ merged.push({
2301
+ owner: owners.size === 1 ? first.owner : "shared",
2302
+ source_path: first.source_path,
2303
+ target_path: targetPath,
2304
+ sha256: first.sha256,
2305
+ bytes: first.bytes
2306
+ });
2307
+ }
2308
+ return merged.sort((left, right) => {
2309
+ const byTarget2 = left.target_path.localeCompare(right.target_path);
2310
+ return byTarget2 !== 0 ? byTarget2 : left.source_path.localeCompare(right.source_path);
2311
+ });
2312
+ }
1741
2313
  async function initializeProject(options) {
1742
2314
  const root = resolve3(options.projectRoot);
1743
2315
  const config = initConfigSchema.parse(options.config);
1744
2316
  const existing = await existingProjectConfig(root);
1745
2317
  const profile = config.profile;
1746
- const bundle = await loadProfileBundle(options.resourcesRoot, profile);
1747
- const managed = await managedTargets(options.resourcesRoot, profile);
1748
- const bundleHash = sha256Bytes(canonicalJson(bundle.manifest.files));
2318
+ const enabledAgents = sortHarnessAgents(config.agents);
2319
+ const surface = config.codebuddy_surface;
2320
+ const adapterContext = { profile, codebuddySurface: surface };
2321
+ const owned = [];
2322
+ const manifests = [];
2323
+ const adaptersIndex = {};
2324
+ const skillBundles = {};
2325
+ let primaryBundleHash = "";
2326
+ let primaryRegistryVersion = "";
2327
+ for (const agent of enabledAgents) {
2328
+ const bundle = await loadAgentBundle(options.resourcesRoot, profile, agent);
2329
+ const adapter = getAdapter(agent);
2330
+ const targets = managedTargetsFor(adapter, bundle, adapterContext);
2331
+ const bundleHash = sha256Bytes(canonicalJson(bundle.manifest.files));
2332
+ if (primaryBundleHash === "") {
2333
+ primaryBundleHash = bundleHash;
2334
+ primaryRegistryVersion = bundle.manifest.bundle_version;
2335
+ }
2336
+ manifests.push({
2337
+ adapter: agent,
2338
+ bundle_version: bundle.manifest.bundle_version,
2339
+ bundle_manifest_hash: bundleHash
2340
+ });
2341
+ skillBundles[agent] = {
2342
+ registry_version: bundle.manifest.bundle_version,
2343
+ bundle_hash: bundleHash
2344
+ };
2345
+ adaptersIndex[agent] = adapter.contextIndex(adapterContext);
2346
+ for (const target of targets) {
2347
+ owned.push({ ...target, owner: agent });
2348
+ }
2349
+ }
2350
+ const mergedTargets = mergeOwnedTargets(owned);
1749
2351
  const projectConfig = projectConfigSchema.parse({
1750
2352
  harness: { name: "hunter-harness", schema_version: 1 },
1751
2353
  project: {
@@ -1759,17 +2361,26 @@ async function initializeProject(options) {
1759
2361
  url: config.server_url ?? existing?.server.url ?? null,
1760
2362
  token_env: config.token_env ?? existing?.server.token_env ?? "HUNTER_HARNESS_TOKEN"
1761
2363
  },
1762
- adapters: { enabled: [config.adapter] }
2364
+ adapters: { enabled: enabledAgents },
2365
+ ...enabledAgents.includes("codebuddy") ? { adapter_options: { codebuddy: { surface: config.codebuddy_surface } } } : {}
1763
2366
  });
1764
2367
  const baseline = baselineManifestSchema.parse({
1765
2368
  schema_version: 1,
1766
2369
  project_id: projectConfig.project.project_id,
1767
2370
  complete_project_version: null,
1768
2371
  artifact_manifest_hash: null,
2372
+ latest_artifact_id: null,
1769
2373
  files: {}
1770
2374
  });
1771
- const agentsContent = upsertManagedBlock(await readOptional(join5(root, "AGENTS.md")), AGENTS_MANAGED_BLOCK_CONTENT);
1772
- const claudeContent = upsertManagedBlock(await readOptional(join5(root, "CLAUDE.md")), CLAUDE_MANAGED_BLOCK_CONTENT);
2375
+ const managedBlocks = [];
2376
+ let agentsContent = await readOptional(join5(root, "AGENTS.md"));
2377
+ agentsContent = upsertManagedBlockById(agentsContent, AGENTS_CORE_BLOCK_ID, AGENTS_MANAGED_BLOCK_CONTENT);
2378
+ managedBlocks.push({
2379
+ owner: "shared",
2380
+ target_path: "AGENTS.md",
2381
+ block_id: AGENTS_CORE_BLOCK_ID,
2382
+ content_sha256: hex(AGENTS_MANAGED_BLOCK_CONTENT)
2383
+ });
1773
2384
  const files = /* @__PURE__ */ new Map([
1774
2385
  [
1775
2386
  ".harness/project.yaml",
@@ -1782,38 +2393,64 @@ async function initializeProject(options) {
1782
2393
  [
1783
2394
  ".harness/context-index.json",
1784
2395
  JSON.stringify({
1785
- schema_version: 1,
1786
- project: { claude_md: "CLAUDE.md", agents_md: "AGENTS.md" },
1787
- rules: [".claude/rules/harness-general.md"],
2396
+ schema_version: 2,
2397
+ project: {
2398
+ shared_instructions: "AGENTS.md",
2399
+ adapters: adaptersIndex
2400
+ },
1788
2401
  knowledge: { index: ".harness/knowledge/index.json" },
1789
2402
  codebase: { map: ".harness/codebase/map", status: "missing" },
1790
- skill_bundle: {
1791
- registry_version: bundle.manifest.bundle_version,
1792
- bundle_hash: bundleHash
1793
- }
2403
+ skill_bundles: skillBundles
1794
2404
  }, null, 2) + "\n"
1795
2405
  ],
1796
2406
  [
1797
2407
  ".harness/knowledge/index.json",
1798
2408
  JSON.stringify({ schema_version: 1, generated_at: null, entries: [] }, null, 2) + "\n"
1799
2409
  ],
1800
- ["AGENTS.md", agentsContent],
1801
- ["CLAUDE.md", claudeContent]
2410
+ ["AGENTS.md", agentsContent]
1802
2411
  ]);
1803
- for (const target of managed) {
2412
+ if (enabledAgents.includes("claude-code")) {
2413
+ let claudeContent = await readOptional(join5(root, "CLAUDE.md"));
2414
+ claudeContent = upsertManagedBlockById(claudeContent, CLAUDE_BLOCK_ID, CLAUDE_MANAGED_BLOCK_CONTENT);
2415
+ files.set("CLAUDE.md", claudeContent);
2416
+ managedBlocks.push({
2417
+ owner: "claude-code",
2418
+ target_path: "CLAUDE.md",
2419
+ block_id: CLAUDE_BLOCK_ID,
2420
+ content_sha256: hex(CLAUDE_MANAGED_BLOCK_CONTENT)
2421
+ });
2422
+ }
2423
+ if (enabledAgents.includes("codebuddy")) {
2424
+ let codebuddyContent = await readOptional(join5(root, "CODEBUDDY.md"));
2425
+ codebuddyContent = upsertManagedBlockById(codebuddyContent, CODEBUDDY_BLOCK_ID, CODEBUDDY_MANAGED_BLOCK_CONTENT);
2426
+ files.set("CODEBUDDY.md", codebuddyContent);
2427
+ managedBlocks.push({
2428
+ owner: "codebuddy",
2429
+ target_path: "CODEBUDDY.md",
2430
+ block_id: CODEBUDDY_BLOCK_ID,
2431
+ content_sha256: hex(CODEBUDDY_MANAGED_BLOCK_CONTENT)
2432
+ });
2433
+ }
2434
+ for (const target of mergedTargets) {
1804
2435
  files.set(target.target_path, target.bytes);
1805
2436
  }
2437
+ managedBlocks.sort((left, right) => {
2438
+ const byTarget = left.target_path.localeCompare(right.target_path);
2439
+ return byTarget !== 0 ? byTarget : left.block_id.localeCompare(right.block_id);
2440
+ });
1806
2441
  const installedState = {
1807
- schema_version: 2,
2442
+ schema_version: 3,
1808
2443
  profile,
1809
- bundle_version: bundle.manifest.bundle_version,
1810
- bundle_manifest_hash: bundleHash,
2444
+ adapters: enabledAgents,
1811
2445
  installed_at: (/* @__PURE__ */ new Date()).toISOString(),
1812
- files: managed.map((target) => ({
2446
+ manifests,
2447
+ files: mergedTargets.map((target) => ({
2448
+ owner: target.owner,
1813
2449
  source_path: target.source_path,
1814
2450
  target_path: target.target_path,
1815
2451
  sha256: target.sha256
1816
- })).sort((left, right) => left.target_path.localeCompare(right.target_path))
2452
+ })),
2453
+ managed_blocks: managedBlocks
1817
2454
  };
1818
2455
  files.set(INSTALLED_BUNDLE_PATH, JSON.stringify(installedState, null, 2) + "\n");
1819
2456
  const paths = [...files.keys()].sort((left, right) => left.localeCompare(right));
@@ -1824,32 +2461,21 @@ async function initializeProject(options) {
1824
2461
  return {
1825
2462
  projectConfig,
1826
2463
  paths,
1827
- bundleHash,
1828
- registryVersion: bundle.manifest.bundle_version
2464
+ bundleHash: primaryBundleHash,
2465
+ registryVersion: primaryRegistryVersion
1829
2466
  };
1830
2467
  }
1831
2468
 
1832
2469
  // ../core/dist/project/refresh.js
1833
- import { createHash as createHash3 } from "node:crypto";
1834
- import { readFile as readFile5, readdir as readdir3, rmdir, stat as stat3 } from "node:fs/promises";
2470
+ import { createHash as createHash5 } from "node:crypto";
2471
+ import { readFile as readFile5, readdir as readdir3, rmdir } from "node:fs/promises";
1835
2472
  import { dirname as dirname3, join as join6, resolve as resolve4 } from "node:path";
1836
2473
  import { parse as parseYaml3, stringify as stringifyYaml2 } from "yaml";
1837
2474
  var INSTALLED_STATE_PATH = ".harness/state/local/installed-harness-bundle.json";
1838
2475
  var CONTEXT_INDEX_PATH = ".harness/context-index.json";
1839
- async function exists3(path) {
1840
- try {
1841
- await stat3(path);
1842
- return true;
1843
- } catch (error) {
1844
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
1845
- return false;
1846
- }
1847
- throw error;
1848
- }
1849
- }
1850
2476
  async function fileHex(path) {
1851
2477
  try {
1852
- return createHash3("sha256").update(await readFile5(path)).digest("hex");
2478
+ return createHash5("sha256").update(await readFile5(path)).digest("hex");
1853
2479
  } catch (error) {
1854
2480
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
1855
2481
  return null;
@@ -1870,17 +2496,17 @@ async function readOptionalText(path) {
1870
2496
  async function readInstalledState(root) {
1871
2497
  const content = await readOptionalText(join6(root, INSTALLED_STATE_PATH));
1872
2498
  if (content === "") {
1873
- return { profile: null, schemaVersion: null, trusted: /* @__PURE__ */ new Map() };
2499
+ return { profile: null, schemaVersion: null, adapters: [], trusted: /* @__PURE__ */ new Map() };
1874
2500
  }
1875
2501
  let parsed;
1876
2502
  try {
1877
2503
  parsed = JSON.parse(content);
1878
2504
  } catch {
1879
- return { profile: null, schemaVersion: null, trusted: /* @__PURE__ */ new Map() };
2505
+ return { profile: null, schemaVersion: null, adapters: [], trusted: /* @__PURE__ */ new Map() };
1880
2506
  }
1881
2507
  const profile = parseHarnessProfile(parsed.profile);
1882
2508
  const trusted = /* @__PURE__ */ new Map();
1883
- if (parsed.schema_version === 2 && Array.isArray(parsed.files)) {
2509
+ if ((parsed.schema_version === 2 || parsed.schema_version === 3) && Array.isArray(parsed.files)) {
1884
2510
  for (const entry of parsed.files) {
1885
2511
  if (entry !== null && typeof entry === "object" && "target_path" in entry && "sha256" in entry) {
1886
2512
  const target = entry.target_path;
@@ -1891,7 +2517,9 @@ async function readInstalledState(root) {
1891
2517
  }
1892
2518
  }
1893
2519
  }
1894
- return { profile, schemaVersion: typeof parsed.schema_version === "number" ? parsed.schema_version : null, trusted };
2520
+ const schemaVersion = typeof parsed.schema_version === "number" ? parsed.schema_version : null;
2521
+ const adapters = schemaVersion === 3 && Array.isArray(parsed.adapters) ? sortHarnessAgents(parsed.adapters.filter((value) => value === "claude-code" || value === "codex" || value === "cursor" || value === "codebuddy")) : schemaVersion === 1 || schemaVersion === 2 ? ["claude-code"] : [];
2522
+ return { profile, schemaVersion, adapters, trusted };
1895
2523
  }
1896
2524
  async function readContextIndexBundleHash(root) {
1897
2525
  const content = await readOptionalText(join6(root, CONTEXT_INDEX_PATH));
@@ -1905,16 +2533,11 @@ async function readContextIndexBundleHash(root) {
1905
2533
  return null;
1906
2534
  }
1907
2535
  }
1908
- async function pruneEmptyParentDirs(root, deletedPaths) {
1909
- const claudeRoot = join6(root, ".claude");
1910
- const boundaries = /* @__PURE__ */ new Set([
1911
- claudeRoot,
1912
- join6(claudeRoot, "skills"),
1913
- join6(claudeRoot, "agents")
1914
- ]);
2536
+ async function pruneEmptyParentDirs(root, deletedPaths, boundaryPaths) {
2537
+ const boundaries = new Set(boundaryPaths.map((path) => join6(root, path)));
1915
2538
  for (const deleted of deletedPaths) {
1916
2539
  let dir = dirname3(join6(root, deleted));
1917
- while (dir.startsWith(claudeRoot) && !boundaries.has(dir)) {
2540
+ while (dir.startsWith(root) && !boundaries.has(dir)) {
1918
2541
  let entries;
1919
2542
  try {
1920
2543
  entries = await readdir3(dir);
@@ -1954,82 +2577,130 @@ function conflict(target, reason, oldSha, incomingSha) {
1954
2577
  function sortByTarget(items) {
1955
2578
  return [...items].sort((left, right) => left.target_path.localeCompare(right.target_path));
1956
2579
  }
1957
- async function refreshMarkdownBlock(root, fileName, blockContent, ops, conflicts, preserved) {
1958
- const original = await readOptionalText(join6(root, fileName));
1959
- const current = original === "" ? null : createHash3("sha256").update(original).digest("hex");
1960
- const refresh = refreshManagedBlock(original, blockContent);
1961
- const synthetic = {
1962
- source_path: fileName,
1963
- target_path: fileName,
1964
- sha256: createHash3("sha256").update(blockContent).digest("hex"),
1965
- bytes: new TextEncoder().encode(blockContent)
1966
- };
1967
- if (refresh.conflict) {
1968
- preserved.push(item(synthetic, "preserve", "MALFORMED_MANAGED_BLOCK", current, synthetic.sha256));
1969
- conflicts.push(conflict(synthetic, "MALFORMED_MANAGED_BLOCK", current, synthetic.sha256));
1970
- return;
1971
- }
1972
- if (refresh.content === original) {
1973
- return;
1974
- }
1975
- ops.push({
1976
- operation: original === "" ? "add" : "modify",
1977
- path: fileName,
1978
- content: refresh.content
1979
- });
1980
- }
1981
- async function reconcileContextIndex(root, bundleVersion, bundleManifestHash) {
2580
+ async function reconcileContextIndex(root, profile, agents, manifests, codebuddySurface2) {
1982
2581
  const existing = await readOptionalText(join6(root, CONTEXT_INDEX_PATH));
1983
- let record = {};
1984
- if (existing !== "") {
1985
- try {
1986
- record = JSON.parse(existing);
1987
- } catch {
1988
- record = {};
1989
- }
1990
- }
1991
- if (Object.keys(record).length === 0) {
1992
- record = {
1993
- schema_version: 1,
1994
- project: { claude_md: "CLAUDE.md", agents_md: "AGENTS.md" },
1995
- rules: [".claude/rules/harness-general.md"],
1996
- knowledge: { index: ".harness/knowledge/index.json" },
1997
- codebase: { map: ".harness/codebase/map", status: "missing" }
1998
- };
1999
- }
2000
- record.skill_bundle = {
2001
- registry_version: bundleVersion,
2002
- bundle_hash: bundleManifestHash
2582
+ const context = { profile, codebuddySurface: codebuddySurface2 };
2583
+ const record = {
2584
+ schema_version: 2,
2585
+ project: {
2586
+ shared_instructions: "AGENTS.md",
2587
+ adapters: Object.fromEntries(agents.map((agent) => [
2588
+ agent,
2589
+ getAdapter(agent).contextIndex(context)
2590
+ ]))
2591
+ },
2592
+ knowledge: { index: ".harness/knowledge/index.json" },
2593
+ codebase: { map: ".harness/codebase/map", status: "missing" },
2594
+ skill_bundles: Object.fromEntries(manifests.map((manifest) => [
2595
+ manifest.adapter,
2596
+ { registry_version: manifest.bundle_version, bundle_hash: manifest.bundle_manifest_hash }
2597
+ ]))
2003
2598
  };
2599
+ const next = JSON.stringify(record, null, 2) + "\n";
2600
+ if (existing === next)
2601
+ return null;
2004
2602
  return {
2005
2603
  operation: existing === "" ? "add" : "modify",
2006
2604
  path: CONTEXT_INDEX_PATH,
2007
- content: JSON.stringify(record, null, 2) + "\n"
2605
+ content: next
2008
2606
  };
2009
2607
  }
2010
- async function profileTransitionOperation(root, previousProfile, profile) {
2011
- if (previousProfile === null || previousProfile === profile)
2608
+ async function projectTransitionOperation(root, previousProfile, profile, oldAgents, agents, codebuddySurface2) {
2609
+ if (previousProfile === profile && oldAgents.length === agents.length && oldAgents.every((agent, index) => agent === agents[index]))
2012
2610
  return null;
2013
2611
  const path = ".harness/project.yaml";
2014
2612
  const content = await readOptionalText(join6(root, path));
2015
- const project = projectConfigSchema.parse(parseYaml3(content));
2613
+ if (content === "")
2614
+ return null;
2615
+ const project = parseYaml3(content);
2016
2616
  return {
2017
2617
  operation: "modify",
2018
2618
  path,
2019
2619
  content: stringifyYaml2({
2020
2620
  ...project,
2021
- project: { ...project.project, profiles: [profile] }
2621
+ project: { ...project.project, profiles: [profile] },
2622
+ adapters: { enabled: agents },
2623
+ ...agents.includes("codebuddy") ? { adapter_options: { codebuddy: { surface: codebuddySurface2 } } } : { adapter_options: void 0 }
2022
2624
  }, { sortMapEntries: true })
2023
2625
  };
2024
2626
  }
2627
+ function mergeTargets(targets) {
2628
+ const grouped = /* @__PURE__ */ new Map();
2629
+ for (const target of targets) {
2630
+ grouped.set(target.target_path, [...grouped.get(target.target_path) ?? [], target]);
2631
+ }
2632
+ return [...grouped.entries()].map(([path, values]) => {
2633
+ const first = values[0];
2634
+ if (first === void 0)
2635
+ throw new TargetCollisionError(path);
2636
+ if (values.some((value) => value.sha256 !== first.sha256))
2637
+ throw new TargetCollisionError(path);
2638
+ const owner = new Set(values.map((value) => value.owner)).size === 1 ? first.owner : "shared";
2639
+ return { ...first, owner };
2640
+ }).sort((left, right) => left.target_path.localeCompare(right.target_path));
2641
+ }
2642
+ async function reconcileMarkdownBlock(root, fileName, blockId, content, remove, ops, conflicts, preserved) {
2643
+ const original = await readOptionalText(join6(root, fileName));
2644
+ const synthetic = {
2645
+ source_path: fileName,
2646
+ target_path: fileName,
2647
+ sha256: createHash5("sha256").update(content).digest("hex"),
2648
+ bytes: new TextEncoder().encode(content)
2649
+ };
2650
+ let next;
2651
+ try {
2652
+ if (remove) {
2653
+ const hasId = original.includes(`<!-- hunter-harness:start id=${blockId} -->`);
2654
+ next = hasId ? removeManagedBlockById(original, blockId) : removeManagedBlock(original);
2655
+ } else {
2656
+ const refreshed = refreshManagedBlockById(original, blockId, content, { upgradeLegacy: true });
2657
+ if (refreshed.conflict)
2658
+ throw new Error("managed block conflict");
2659
+ next = refreshed.content;
2660
+ }
2661
+ } catch {
2662
+ const current = original === "" ? null : createHash5("sha256").update(original).digest("hex");
2663
+ preserved.push(item(synthetic, "preserve", "MALFORMED_MANAGED_BLOCK", current, synthetic.sha256));
2664
+ conflicts.push(conflict(synthetic, "MALFORMED_MANAGED_BLOCK", current, synthetic.sha256));
2665
+ return;
2666
+ }
2667
+ if (next !== original)
2668
+ ops.push({
2669
+ operation: original === "" ? "add" : "modify",
2670
+ path: fileName,
2671
+ content: next
2672
+ });
2673
+ }
2674
+ function stateWithoutInstalledAt(value) {
2675
+ if (value === null || typeof value !== "object" || Array.isArray(value))
2676
+ return value;
2677
+ const copy = { ...value };
2678
+ delete copy.installed_at;
2679
+ return copy;
2680
+ }
2025
2681
  async function refreshProject(options) {
2026
2682
  const root = resolve4(options.projectRoot);
2027
2683
  const profile = options.profile;
2028
- const newManaged = await managedTargets(options.resourcesRoot, profile);
2029
- const newBundle = await loadProfileBundle(options.resourcesRoot, profile);
2030
- const bundleManifestHash = sha256Bytes(canonicalJson(newBundle.manifest.files));
2031
2684
  const installed = await readInstalledState(root);
2032
2685
  const previousProfile = installed.profile;
2686
+ const agents = sortHarnessAgents(options.agents);
2687
+ const oldAgents = installed.adapters.length > 0 ? installed.adapters : ["claude-code"];
2688
+ const codebuddySurface2 = options.codebuddySurface ?? "both";
2689
+ const context = { profile, codebuddySurface: codebuddySurface2 };
2690
+ const owned = [];
2691
+ const manifests = [];
2692
+ for (const agent of agents) {
2693
+ const bundle = await loadAgentBundle(options.resourcesRoot, profile, agent);
2694
+ manifests.push({
2695
+ adapter: agent,
2696
+ bundle_version: bundle.manifest.bundle_version,
2697
+ bundle_manifest_hash: sha256Bytes(canonicalJson(bundle.manifest.files))
2698
+ });
2699
+ for (const target of managedTargetsFor(getAdapter(agent), bundle, context)) {
2700
+ owned.push({ ...target, owner: agent });
2701
+ }
2702
+ }
2703
+ const newManaged = mergeTargets(owned);
2033
2704
  let trusted = installed.trusted;
2034
2705
  let migrationOldPaths = null;
2035
2706
  if (installed.schemaVersion === 1) {
@@ -2056,9 +2727,14 @@ async function refreshProject(options) {
2056
2727
  });
2057
2728
  }
2058
2729
  }
2059
- } else if (previousProfile !== null && previousProfile !== profile) {
2060
- const oldManaged = await managedTargets(options.resourcesRoot, previousProfile);
2061
- oldOnly = oldManaged.filter((target) => !newTargetSet.has(target.target_path));
2730
+ } else if (previousProfile !== null) {
2731
+ const oldContext = { profile: previousProfile, codebuddySurface: codebuddySurface2 };
2732
+ const oldTargets = [];
2733
+ for (const agent of oldAgents) {
2734
+ const bundle = await loadAgentBundle(options.resourcesRoot, previousProfile, agent);
2735
+ oldTargets.push(...managedTargetsFor(getAdapter(agent), bundle, oldContext));
2736
+ }
2737
+ oldOnly = oldTargets.filter((target) => !newTargetSet.has(target.target_path));
2062
2738
  }
2063
2739
  const applied = [];
2064
2740
  const removed = [];
@@ -2073,12 +2749,12 @@ async function refreshProject(options) {
2073
2749
  if (current === null) {
2074
2750
  applied.push(item(target, "add", "MISSING_TARGET", null, incoming));
2075
2751
  ops.push({ operation: "add", path: target.target_path, content: target.bytes });
2076
- newStateFiles.push({ source_path: target.source_path, target_path: target.target_path, sha256: incoming });
2752
+ newStateFiles.push({ owner: target.owner, source_path: target.source_path, target_path: target.target_path, sha256: incoming });
2077
2753
  continue;
2078
2754
  }
2079
2755
  if (current === incoming) {
2080
2756
  unchanged.push(item(target, "unchanged", "ALREADY_CURRENT", current, incoming));
2081
- newStateFiles.push({ source_path: target.source_path, target_path: target.target_path, sha256: incoming });
2757
+ newStateFiles.push({ owner: target.owner, source_path: target.source_path, target_path: target.target_path, sha256: incoming });
2082
2758
  continue;
2083
2759
  }
2084
2760
  const trustedHash = trusted.get(target.target_path);
@@ -2086,13 +2762,13 @@ async function refreshProject(options) {
2086
2762
  const reason = options.forceManaged ? "FORCE_MANAGED" : "BASELINE_CLEAN";
2087
2763
  applied.push(item(target, "replace", reason, current, incoming));
2088
2764
  ops.push({ operation: "modify", path: target.target_path, content: target.bytes });
2089
- newStateFiles.push({ source_path: target.source_path, target_path: target.target_path, sha256: incoming });
2765
+ newStateFiles.push({ owner: target.owner, source_path: target.source_path, target_path: target.target_path, sha256: incoming });
2090
2766
  } else {
2091
2767
  const reason = trustedHash === void 0 ? "LEGACY_BASELINE_UNKNOWN" : "LOCAL_MODIFICATION";
2092
2768
  preserved.push(item(target, "preserve", reason, current, incoming));
2093
2769
  conflicts.push(conflict(target, reason, current, incoming));
2094
2770
  if (trustedHash !== void 0) {
2095
- newStateFiles.push({ source_path: target.source_path, target_path: target.target_path, sha256: trustedHash });
2771
+ newStateFiles.push({ owner: target.owner, source_path: target.source_path, target_path: target.target_path, sha256: trustedHash });
2096
2772
  }
2097
2773
  }
2098
2774
  }
@@ -2101,7 +2777,7 @@ async function refreshProject(options) {
2101
2777
  if (current === null) {
2102
2778
  continue;
2103
2779
  }
2104
- const trustedHash = trusted.get(target.target_path);
2780
+ const trustedHash = target.sha256 !== "" ? target.sha256 : void 0;
2105
2781
  const clean = trustedHash !== void 0 && current === trustedHash;
2106
2782
  if (clean || options.forceManaged) {
2107
2783
  const reason = clean ? "BASELINE_CLEAN" : "FORCE_MANAGED";
@@ -2113,28 +2789,60 @@ async function refreshProject(options) {
2113
2789
  conflicts.push(conflict(target, reason, current, null));
2114
2790
  }
2115
2791
  }
2116
- await refreshMarkdownBlock(root, "AGENTS.md", AGENTS_MANAGED_BLOCK_CONTENT, ops, conflicts, preserved);
2117
- await refreshMarkdownBlock(root, "CLAUDE.md", CLAUDE_MANAGED_BLOCK_CONTENT, ops, conflicts, preserved);
2118
- const profileOperation = await profileTransitionOperation(root, previousProfile, profile);
2119
- if (profileOperation !== null)
2120
- ops.push(profileOperation);
2121
- ops.push(await reconcileContextIndex(root, newBundle.manifest.bundle_version, bundleManifestHash));
2792
+ await reconcileMarkdownBlock(root, "AGENTS.md", AGENTS_CORE_BLOCK_ID, AGENTS_MANAGED_BLOCK_CONTENT, false, ops, conflicts, preserved);
2793
+ await reconcileMarkdownBlock(root, "CLAUDE.md", CLAUDE_BLOCK_ID, CLAUDE_MANAGED_BLOCK_CONTENT, !agents.includes("claude-code"), ops, conflicts, preserved);
2794
+ await reconcileMarkdownBlock(root, "CODEBUDDY.md", CODEBUDDY_BLOCK_ID, CODEBUDDY_MANAGED_BLOCK_CONTENT, !agents.includes("codebuddy"), ops, conflicts, preserved);
2795
+ const projectOperation = await projectTransitionOperation(root, previousProfile, profile, oldAgents, agents, codebuddySurface2);
2796
+ if (projectOperation !== null)
2797
+ ops.push(projectOperation);
2798
+ const contextOperation = await reconcileContextIndex(root, profile, agents, manifests, codebuddySurface2);
2799
+ if (contextOperation !== null)
2800
+ ops.push(contextOperation);
2801
+ const managedBlocks = [
2802
+ {
2803
+ owner: "shared",
2804
+ target_path: "AGENTS.md",
2805
+ block_id: AGENTS_CORE_BLOCK_ID,
2806
+ content_sha256: createHash5("sha256").update(AGENTS_MANAGED_BLOCK_CONTENT).digest("hex")
2807
+ },
2808
+ ...agents.includes("claude-code") ? [{
2809
+ owner: "claude-code",
2810
+ target_path: "CLAUDE.md",
2811
+ block_id: CLAUDE_BLOCK_ID,
2812
+ content_sha256: createHash5("sha256").update(CLAUDE_MANAGED_BLOCK_CONTENT).digest("hex")
2813
+ }] : [],
2814
+ ...agents.includes("codebuddy") ? [{
2815
+ owner: "codebuddy",
2816
+ target_path: "CODEBUDDY.md",
2817
+ block_id: CODEBUDDY_BLOCK_ID,
2818
+ content_sha256: createHash5("sha256").update(CODEBUDDY_MANAGED_BLOCK_CONTENT).digest("hex")
2819
+ }] : []
2820
+ ].sort((left, right) => left.target_path.localeCompare(right.target_path) || left.block_id.localeCompare(right.block_id));
2122
2821
  const installedState = {
2123
- schema_version: 2,
2822
+ schema_version: 3,
2124
2823
  profile,
2125
- bundle_version: newBundle.manifest.bundle_version,
2126
- bundle_manifest_hash: bundleManifestHash,
2824
+ adapters: agents,
2127
2825
  installed_at: (/* @__PURE__ */ new Date()).toISOString(),
2128
- files: newStateFiles.sort((left, right) => left.target_path.localeCompare(right.target_path))
2826
+ manifests,
2827
+ files: newStateFiles.sort((left, right) => left.target_path.localeCompare(right.target_path) || left.source_path.localeCompare(right.source_path)),
2828
+ managed_blocks: managedBlocks
2129
2829
  };
2130
- ops.push({
2131
- operation: await exists3(join6(root, INSTALLED_STATE_PATH)) ? "modify" : "add",
2132
- path: INSTALLED_STATE_PATH,
2133
- content: JSON.stringify(installedState, null, 2) + "\n"
2134
- });
2830
+ const existingState = await readOptionalText(join6(root, INSTALLED_STATE_PATH));
2831
+ let existingParsed = null;
2832
+ try {
2833
+ existingParsed = existingState === "" ? null : JSON.parse(existingState);
2834
+ } catch {
2835
+ }
2836
+ if (JSON.stringify(stateWithoutInstalledAt(existingParsed)) !== JSON.stringify(stateWithoutInstalledAt(installedState))) {
2837
+ ops.push({
2838
+ operation: existingState === "" ? "add" : "modify",
2839
+ path: INSTALLED_STATE_PATH,
2840
+ content: JSON.stringify(installedState, null, 2) + "\n"
2841
+ });
2842
+ }
2135
2843
  if (!options.dryRun) {
2136
2844
  await runTransaction(root, ops, { kind: "refresh" });
2137
- await pruneEmptyParentDirs(root, removed.map((item2) => item2.target_path));
2845
+ await pruneEmptyParentDirs(root, removed.map((item2) => item2.target_path), getAdapters([.../* @__PURE__ */ new Set([...agents, ...oldAgents])]).flatMap((adapter) => adapter.pruneBoundaries(context)));
2138
2846
  }
2139
2847
  return {
2140
2848
  profile,
@@ -2350,11 +3058,11 @@ function rawFindings(content) {
2350
3058
  for (const rule of RULES) {
2351
3059
  for (const match of content.matchAll(rule.pattern)) {
2352
3060
  const value = match[rule.valueGroup ?? 0] ?? match[0];
2353
- const relative2 = match[0].indexOf(value);
3061
+ const relative3 = match[0].indexOf(value);
2354
3062
  findings.push({
2355
3063
  ruleId: rule.id,
2356
3064
  severity: rule.severity,
2357
- offset: match.index + Math.max(0, relative2),
3065
+ offset: match.index + Math.max(0, relative3),
2358
3066
  value
2359
3067
  });
2360
3068
  }
@@ -2519,18 +3227,16 @@ var PushWorkflowError = class extends Error {
2519
3227
  this.code = code;
2520
3228
  }
2521
3229
  };
2522
- var MANAGED_ROOTS = [
2523
- ".claude/rules",
3230
+ var SHARED_MANAGED_ROOTS = [
2524
3231
  ".harness/knowledge",
2525
3232
  ".harness/codebase"
2526
3233
  ];
2527
- var MANAGED_FILES = [
2528
- "CLAUDE.md",
3234
+ var SHARED_MANAGED_FILES = [
2529
3235
  "AGENTS.md",
2530
3236
  ".harness/project.yaml",
2531
3237
  ".harness/context-index.json"
2532
3238
  ];
2533
- async function exists4(path) {
3239
+ async function exists3(path) {
2534
3240
  try {
2535
3241
  await lstat2(path);
2536
3242
  return true;
@@ -2542,7 +3248,7 @@ async function exists4(path) {
2542
3248
  }
2543
3249
  }
2544
3250
  async function walkFiles(root, current, output) {
2545
- if (!await exists4(current)) {
3251
+ if (!await exists3(current)) {
2546
3252
  return;
2547
3253
  }
2548
3254
  for (const item2 of await readdir4(current, { withFileTypes: true })) {
@@ -2557,23 +3263,50 @@ async function walkFiles(root, current, output) {
2557
3263
  }
2558
3264
  }
2559
3265
  }
2560
- async function managedFiles(projectRoot) {
3266
+ function enabledHarnessAgents(project) {
3267
+ return project.adapters.enabled.flatMap((agent) => {
3268
+ const parsed = harnessAgentSchema.safeParse(agent);
3269
+ return parsed.success ? [parsed.data] : [];
3270
+ });
3271
+ }
3272
+ async function walkHarnessEntries(root, directory, output) {
3273
+ if (!await exists3(directory))
3274
+ return;
3275
+ for (const item2 of await readdir4(directory, { withFileTypes: true })) {
3276
+ if (item2.name.startsWith("harness-")) {
3277
+ const path = join9(directory, item2.name);
3278
+ if (item2.isDirectory()) {
3279
+ await walkFiles(root, path, output);
3280
+ } else if (item2.isFile()) {
3281
+ output.push(normalizeManagedPath(relative(root, path).replaceAll("\\", "/")));
3282
+ }
3283
+ }
3284
+ }
3285
+ }
3286
+ async function managedFiles(projectRoot, project) {
2561
3287
  const root = resolve5(projectRoot);
2562
3288
  const paths = [];
2563
- for (const path of MANAGED_FILES) {
2564
- if (await exists4(join9(root, path))) {
3289
+ const adapters = getAdapters(enabledHarnessAgents(project));
3290
+ const managedFiles2 = [
3291
+ ...SHARED_MANAGED_FILES,
3292
+ ...adapters.some((adapter) => adapter.name === "claude-code") ? ["CLAUDE.md"] : [],
3293
+ ...adapters.some((adapter) => adapter.name === "codebuddy") ? ["CODEBUDDY.md"] : []
3294
+ ];
3295
+ for (const path of managedFiles2) {
3296
+ if (await exists3(join9(root, path))) {
2565
3297
  paths.push(path);
2566
3298
  }
2567
3299
  }
2568
- for (const path of MANAGED_ROOTS) {
3300
+ for (const path of SHARED_MANAGED_ROOTS) {
2569
3301
  await walkFiles(root, join9(root, path), paths);
2570
3302
  }
2571
- const skillsRoot = join9(root, ".claude", "skills");
2572
- if (await exists4(skillsRoot)) {
2573
- for (const item2 of await readdir4(skillsRoot, { withFileTypes: true })) {
2574
- if (item2.isDirectory() && item2.name.startsWith("harness-")) {
2575
- await walkFiles(root, join9(skillsRoot, item2.name), paths);
2576
- }
3303
+ for (const adapter of adapters) {
3304
+ if (adapter.rulesRoot !== null) {
3305
+ await walkFiles(root, join9(root, adapter.rulesRoot), paths);
3306
+ }
3307
+ await walkHarnessEntries(root, join9(root, adapter.skillsRoot), paths);
3308
+ if (adapter.agentsRoot !== null) {
3309
+ await walkHarnessEntries(root, join9(root, adapter.agentsRoot), paths);
2577
3310
  }
2578
3311
  }
2579
3312
  const result = {};
@@ -2693,8 +3426,8 @@ async function pushProject(options) {
2693
3426
  let project = await readProject(root);
2694
3427
  let baseline = await readBaseline(root);
2695
3428
  const profile = parseHarnessProfile(project.project.profiles[0]);
2696
- const installedPaths = profile === null ? /* @__PURE__ */ new Set() : await managedBundleTargets(options.resourcesRoot, profile);
2697
- let preview = makePreview(baseline, await managedFiles(root), options.confirmedProjectLocal ?? [], installedPaths);
3429
+ const installedPaths = profile === null ? /* @__PURE__ */ new Set() : new Set(await Promise.all(enabledHarnessAgents(project).map((agent) => managedBundleTargets(options.resourcesRoot, profile, agent))).then((targets) => targets.flatMap((target) => [...target])));
3430
+ let preview = makePreview(baseline, await managedFiles(root, project), options.confirmedProjectLocal ?? [], installedPaths);
2698
3431
  if (preview.blocked) {
2699
3432
  throw new PushWorkflowError("sensitive information scan blocked the proposal", 6, "SENSITIVE_CONTENT_BLOCKED");
2700
3433
  }
@@ -2734,7 +3467,7 @@ async function pushProject(options) {
2734
3467
  let workflow = priorWorkflow?.local_project_key === project.project.local_project_key ? priorWorkflow : newWorkflowState(project, clientId);
2735
3468
  workflow.client_id = clientId;
2736
3469
  await atomicWriteJson(workflowPath, workflow);
2737
- preview = makePreview(baseline, await managedFiles(root), options.confirmedProjectLocal ?? [], installedPaths, workflow.created_at);
3470
+ preview = makePreview(baseline, await managedFiles(root, project), options.confirmedProjectLocal ?? [], installedPaths, workflow.created_at);
2738
3471
  const requestId = workflow.request_id;
2739
3472
  const client = new HunterHarnessApiClient({
2740
3473
  serverUrl: parsedServerUrl.toString(),
@@ -2752,7 +3485,7 @@ async function pushProject(options) {
2752
3485
  ({ project, baseline } = await bindProject(root, project, baseline, resolved.project_id));
2753
3486
  workflow.project_id = resolved.project_id;
2754
3487
  await atomicWriteJson(workflowPath, workflow);
2755
- preview = makePreview(baseline, await managedFiles(root), options.confirmedProjectLocal ?? [], installedPaths, workflow.created_at);
3488
+ preview = makePreview(baseline, await managedFiles(root, project), options.confirmedProjectLocal ?? [], installedPaths, workflow.created_at);
2756
3489
  if (preview.blocked) {
2757
3490
  throw new PushWorkflowError("sensitive information scan blocked the proposal", 6, "SENSITIVE_CONTENT_BLOCKED");
2758
3491
  }
@@ -2815,23 +3548,31 @@ async function pushProject(options) {
2815
3548
  });
2816
3549
  }
2817
3550
  }
2818
- const finalized = await client.finalizeProposal(session.session_id, { schema_version: 1, manifest_sha256: proposalManifestHash }, requestId, workflow.finalize_idempotency_key);
3551
+ const finalized = await client.finalizeProposal(session.session_id, {
3552
+ schema_version: 1,
3553
+ manifest_sha256: proposalManifestHash,
3554
+ base_artifact_id: baseline.latest_artifact_id ?? null
3555
+ }, requestId, workflow.finalize_idempotency_key);
2819
3556
  await atomicWriteJson(join9(root, ".harness", "state", "local", "push-results", finalized.proposal_id + ".json"), {
2820
3557
  schema_version: 1,
2821
3558
  request_id: requestId,
2822
3559
  project_id: projectId,
2823
3560
  proposal_id: finalized.proposal_id,
2824
3561
  status: finalized.status,
3562
+ artifact_id: finalized.artifact_id,
2825
3563
  operation_count: preview.operations.length,
2826
3564
  recorded_at: (/* @__PURE__ */ new Date()).toISOString()
2827
3565
  });
2828
3566
  await rm4(workflowPath, { force: true });
2829
- return { preview, proposalId: finalized.proposal_id, projectId };
3567
+ return { preview, proposalId: finalized.proposal_id, projectId, artifactId: finalized.artifact_id };
2830
3568
  } catch (error) {
2831
3569
  if (error instanceof PushWorkflowError) {
2832
3570
  throw error;
2833
3571
  }
2834
3572
  if (error instanceof ApiError) {
3573
+ if (error.code === "STALE_PUSH") {
3574
+ throw new PushWorkflowError("\u670D\u52A1\u7AEF\u5DF2\u6709\u66F4\u65B0\u7684\u63A8\u9001\uFF0C\u8BF7\u5148 git pull \u540E\u6267\u884C npx hunter-harness update \u518D\u63A8", 5, "STALE_PUSH");
3575
+ }
2835
3576
  const exitCode = error.status === 401 ? 8 : error.status === 409 ? 5 : 4;
2836
3577
  throw new PushWorkflowError(error.message, exitCode, error.code);
2837
3578
  }
@@ -2906,7 +3647,7 @@ async function cleanupProject(options) {
2906
3647
 
2907
3648
  // ../core/dist/skill/frontmatter.js
2908
3649
  import { parse as parseYaml5 } from "yaml";
2909
- import { z as z12 } from "zod";
3650
+ import { z as z14 } from "zod";
2910
3651
 
2911
3652
  // ../core/dist/skill/fixer.js
2912
3653
  import { parse as parseYaml6, stringify as stringifyYaml4 } from "yaml";
@@ -2948,7 +3689,7 @@ var AGENT_DESCRIPTORS = {
2948
3689
  var INSTALLABLE_AGENTS = Object.keys(AGENT_DESCRIPTORS).filter((agent) => AGENT_DESCRIPTORS[agent]?.installable === true);
2949
3690
 
2950
3691
  // ../core/dist/transaction/recovery.js
2951
- import { readFile as readFile10, readdir as readdir6, rm as rm6, stat as stat4 } from "node:fs/promises";
3692
+ import { readFile as readFile10, readdir as readdir6, rm as rm6, stat as stat3 } from "node:fs/promises";
2952
3693
  import { join as join11 } from "node:path";
2953
3694
  async function recoverTransaction(projectRoot, transactionId) {
2954
3695
  const journal = JSON.parse(await readFile10(join11(stateLayout(projectRoot).transactions, transactionId, "journal.json"), "utf8"));
@@ -2995,7 +3736,7 @@ async function pendingTransactions(projectRoot) {
2995
3736
  }
2996
3737
  async function pathExists(path) {
2997
3738
  try {
2998
- await stat4(path);
3739
+ await stat3(path);
2999
3740
  return true;
3000
3741
  } catch (error) {
3001
3742
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
@@ -3014,8 +3755,8 @@ async function rollbackLatestCommittedUpdate(projectRoot) {
3014
3755
  const after = JSON.parse(await readFile10(join11(transactionRoot, "after", "manifest.json"), "utf8"));
3015
3756
  for (const entry of after) {
3016
3757
  const target = join11(projectRoot, entry.path);
3017
- const exists6 = await pathExists(target);
3018
- if (exists6 !== entry.exists || exists6 && await sha256File(target) !== entry.hash) {
3758
+ const exists5 = await pathExists(target);
3759
+ if (exists5 !== entry.exists || exists5 && await sha256File(target) !== entry.hash) {
3019
3760
  throw new Error("cannot rollback dirty path: " + entry.path);
3020
3761
  }
3021
3762
  }
@@ -3027,15 +3768,15 @@ async function rollbackLatestCommittedUpdate(projectRoot) {
3027
3768
  }
3028
3769
  seen.add(snapshot.path);
3029
3770
  const target = join11(projectRoot, snapshot.path);
3030
- const exists6 = await pathExists(target);
3771
+ const exists5 = await pathExists(target);
3031
3772
  if (snapshot.existed && snapshot.snapshot_name !== null) {
3032
3773
  const content = await readFile10(join11(transactionRoot, "before", snapshot.snapshot_name));
3033
3774
  operations.push({
3034
- operation: exists6 ? "modify" : "add",
3775
+ operation: exists5 ? "modify" : "add",
3035
3776
  path: snapshot.path,
3036
3777
  content
3037
3778
  });
3038
- } else if (exists6) {
3779
+ } else if (exists5) {
3039
3780
  operations.push({ operation: "delete", path: snapshot.path });
3040
3781
  }
3041
3782
  }
@@ -3308,7 +4049,8 @@ async function updateProject(options) {
3308
4049
  equivalent = finalContent === sourceContent;
3309
4050
  } else if (incoming !== null) {
3310
4051
  const incomingBlock = extractManagedBlock(incoming) ?? incoming.trim();
3311
- const blockId = operation.operation === "add" || operation.operation === "modify" ? operation.block_id : void 0;
4052
+ const incomingId = extractSingleManagedBlockById(incoming)?.id;
4053
+ const blockId = operation.operation === "add" || operation.operation === "modify" ? operation.block_id ?? incomingId : void 0;
3312
4054
  finalContent = blockId !== void 0 ? upsertManagedBlockById(targetContent ?? "", blockId, incomingBlock) : upsertManagedBlock(targetContent ?? "", incomingBlock);
3313
4055
  equivalent = finalContent === targetContent;
3314
4056
  }
@@ -3351,6 +4093,7 @@ async function updateProject(options) {
3351
4093
  if (skipped.length === 0) {
3352
4094
  nextBaseline.complete_project_version = manifest.project_version;
3353
4095
  nextBaseline.artifact_manifest_hash = manifest.manifest_sha256;
4096
+ nextBaseline.latest_artifact_id = manifest.artifact_id;
3354
4097
  }
3355
4098
  const transactionId = "tx_update_" + Date.now() + "_" + uuidV7();
3356
4099
  const reportPath = ".harness/reports/update-" + requestId + ".json";
@@ -3416,19 +4159,82 @@ import { isAbsolute as isAbsolute2, join as join13 } from "node:path";
3416
4159
  import { readFile as readFile12 } from "node:fs/promises";
3417
4160
  var InitConfigurationError = class extends Error {
3418
4161
  exitCode;
3419
- constructor(message, exitCode = 3, options) {
4162
+ code;
4163
+ constructor(message, exitCode = 3, code = "INIT_CONFIG_INVALID", options) {
3420
4164
  super(message, options);
3421
4165
  this.name = "InitConfigurationError";
3422
4166
  this.exitCode = exitCode;
4167
+ this.code = code;
3423
4168
  }
3424
4169
  };
4170
+ function harnessErrorInfo(error) {
4171
+ const info = {};
4172
+ if (error === null || typeof error !== "object") return info;
4173
+ const record = error;
4174
+ if (typeof record.code === "string") info.code = record.code;
4175
+ if (typeof record.exitCode === "number") {
4176
+ info.exitCode = record.exitCode;
4177
+ }
4178
+ return info;
4179
+ }
4180
+ var AGENT_BY_INDEX = {
4181
+ "1": "claude-code",
4182
+ "2": "codex",
4183
+ "3": "cursor",
4184
+ "4": "codebuddy"
4185
+ };
3425
4186
  function normalizeProfile(value) {
3426
4187
  if (value === void 0) return void 0;
3427
4188
  if (value === "" || value === "1" || value === "general") return "general";
3428
4189
  if (value === "2" || value === "java") return "java";
3429
4190
  throw new InitConfigurationError("\u914D\u7F6E\u7C7B\u578B\u5FC5\u987B\u4E3A general \u6216 java");
3430
4191
  }
3431
- async function resolveInitConfig(cwd, flags, promptMissing) {
4192
+ function parseAgentsInput(raw) {
4193
+ const trimmed = raw.trim();
4194
+ if (trimmed === "") return ["claude-code"];
4195
+ if (trimmed === "all") return [...HARNESS_AGENT_ORDER];
4196
+ const agents = [];
4197
+ for (const token of trimmed.split(",")) {
4198
+ const value = token.trim();
4199
+ const byIndex = AGENT_BY_INDEX[value];
4200
+ if (byIndex !== void 0) {
4201
+ agents.push(byIndex);
4202
+ continue;
4203
+ }
4204
+ const byName = harnessAgentSchema.safeParse(value);
4205
+ if (byName.success) {
4206
+ agents.push(byName.data);
4207
+ continue;
4208
+ }
4209
+ throw new InitConfigurationError(`\u672A\u77E5 Agent\uFF1A${value}`, 3, "AGENT_UNSUPPORTED");
4210
+ }
4211
+ if (agents.length === 0) {
4212
+ throw new InitConfigurationError("Agent \u5217\u8868\u4E3A\u7A7A", 3, "AGENTS_REQUIRED");
4213
+ }
4214
+ return sortHarnessAgents(agents);
4215
+ }
4216
+ function parseAgentsFromConfig(value) {
4217
+ if (!Array.isArray(value) || value.length === 0) {
4218
+ throw new InitConfigurationError("Agent \u5217\u8868\u4E3A\u7A7A", 3, "AGENTS_REQUIRED");
4219
+ }
4220
+ const agents = [];
4221
+ for (const item2 of value) {
4222
+ const parsed = harnessAgentSchema.safeParse(item2);
4223
+ if (!parsed.success) {
4224
+ throw new InitConfigurationError(
4225
+ `\u672A\u77E5 Agent\uFF1A${String(item2)}`,
4226
+ 3,
4227
+ "AGENT_UNSUPPORTED"
4228
+ );
4229
+ }
4230
+ agents.push(parsed.data);
4231
+ }
4232
+ return sortHarnessAgents(agents);
4233
+ }
4234
+ function hasOwn(record, key) {
4235
+ return Object.prototype.hasOwnProperty.call(record, key);
4236
+ }
4237
+ async function resolveInitConfig(cwd, flags, prompts = {}, warnings = []) {
3432
4238
  let fileConfig = {};
3433
4239
  if (flags.config !== void 0) {
3434
4240
  const path = isAbsolute2(flags.config) ? flags.config : join13(cwd, flags.config);
@@ -3438,20 +4244,58 @@ async function resolveInitConfig(cwd, flags, promptMissing) {
3438
4244
  throw new InitConfigurationError(
3439
4245
  "unable to read init config: " + (error instanceof Error ? error.message : String(error)),
3440
4246
  3,
4247
+ "INIT_CONFIG_INVALID",
3441
4248
  { cause: error }
3442
4249
  );
3443
4250
  }
3444
4251
  }
3445
- const configuredAdapter = fileConfig.adapter ?? flags.adapter ?? "claude-code";
3446
- if (configuredAdapter !== "claude-code") {
3447
- throw new InitConfigurationError("only claude-code is supported");
4252
+ const hasConfigAgents = hasOwn(fileConfig, "agents");
4253
+ const hasConfigAdapter = hasOwn(fileConfig, "adapter");
4254
+ if (hasConfigAgents && hasConfigAdapter) {
4255
+ throw new InitConfigurationError(
4256
+ "\u914D\u7F6E\u4E0D\u80FD\u540C\u65F6\u5305\u542B agents \u4E0E adapter",
4257
+ 3,
4258
+ "AGENT_OPTIONS_CONFLICT"
4259
+ );
4260
+ }
4261
+ let agents;
4262
+ if (hasConfigAgents) {
4263
+ agents = parseAgentsFromConfig(fileConfig.agents);
4264
+ } else if (hasConfigAdapter) {
4265
+ if (fileConfig.adapter !== "claude-code") {
4266
+ throw new InitConfigurationError(
4267
+ `\u672A\u77E5 Agent\uFF1A${String(fileConfig.adapter)}`,
4268
+ 3,
4269
+ "AGENT_UNSUPPORTED"
4270
+ );
4271
+ }
4272
+ agents = ["claude-code"];
4273
+ warnings.push(
4274
+ 'DEPRECATION: init config field "adapter" is deprecated; use "agents" instead'
4275
+ );
4276
+ } else if (flags.agents !== void 0) {
4277
+ agents = parseAgentsInput(flags.agents);
4278
+ } else if (prompts.agents !== void 0) {
4279
+ agents = parseAgentsInput(await prompts.agents());
4280
+ } else {
4281
+ agents = ["claude-code"];
4282
+ }
4283
+ const surfaceFromConfig = hasOwn(fileConfig, "codebuddy_surface");
4284
+ const surfaceFromFlags = flags.codebuddySurface !== void 0;
4285
+ if ((surfaceFromConfig || surfaceFromFlags) && !agents.includes("codebuddy")) {
4286
+ throw new InitConfigurationError(
4287
+ "\u672A\u9009\u62E9 CodeBuddy \u65F6\u4E0D\u80FD\u6307\u5B9A codebuddy_surface",
4288
+ 3,
4289
+ "CODEBUDDY_SURFACE_UNUSED"
4290
+ );
3448
4291
  }
3449
4292
  const profile = normalizeProfile(
3450
- fileConfig.profile ?? flags.profile ?? (promptMissing === void 0 ? void 0 : await promptMissing())
4293
+ fileConfig.profile ?? flags.profile ?? (prompts.profile === void 0 ? void 0 : await prompts.profile())
3451
4294
  );
3452
4295
  const candidate = {
3453
- adapter: "claude-code",
4296
+ agents,
3454
4297
  profile,
4298
+ codebuddy_surface: fileConfig.codebuddy_surface ?? flags.codebuddySurface ?? "both",
3455
4299
  server_url: fileConfig.server_url ?? flags.serverUrl ?? null,
3456
4300
  token_env: fileConfig.token_env ?? flags.tokenEnv ?? "HUNTER_HARNESS_TOKEN",
3457
4301
  project_id: fileConfig.project_id ?? null,
@@ -3464,7 +4308,8 @@ async function resolveInitConfig(cwd, flags, promptMissing) {
3464
4308
  if (!parsed.success) {
3465
4309
  throw new InitConfigurationError(
3466
4310
  "init config schema validation failed: " + parsed.error.message,
3467
- 7
4311
+ 7,
4312
+ "INIT_CONFIG_INVALID"
3468
4313
  );
3469
4314
  }
3470
4315
  return parsed.data;
@@ -3503,6 +4348,16 @@ function parseProfile(value, current) {
3503
4348
  if (value === "2" || value === "java") return "java";
3504
4349
  throw new Error("\u914D\u7F6E\u7C7B\u578B\u5FC5\u987B\u4E3A general \u6216 java");
3505
4350
  }
4351
+ function refreshAgents(config) {
4352
+ const agents = sortHarnessAgents(config.adapters.enabled.flatMap((agent) => {
4353
+ const parsed = harnessAgentSchema.safeParse(agent);
4354
+ return parsed.success ? [parsed.data] : [];
4355
+ }));
4356
+ return agents.length > 0 ? agents : ["claude-code"];
4357
+ }
4358
+ function codebuddySurface(config) {
4359
+ return config.adapter_options?.codebuddy?.surface ?? "both";
4360
+ }
3506
4361
  function summarize(result) {
3507
4362
  const items = [
3508
4363
  ...result.applied.map((item2) => ({ ...item2, status: result.dry_run ? "planned" : "applied" })),
@@ -3570,20 +4425,25 @@ async function runRefresh(options, dependencies) {
3570
4425
  }
3571
4426
  const currentProfile = detection.config.project.profiles[0] ?? "general";
3572
4427
  let targetProfile;
4428
+ let targetAgents;
3573
4429
  try {
3574
4430
  targetProfile = parseProfile(options.profile, currentProfile);
4431
+ targetAgents = options.agents === void 0 ? refreshAgents(detection.config) : parseAgentsInput(options.agents);
3575
4432
  } catch (error) {
3576
4433
  const message = error instanceof Error ? error.message : String(error);
3577
- dependencies.stderr(message + "\n");
3578
- return 3;
4434
+ const code = error instanceof InitConfigurationError ? error.code : void 0;
4435
+ dependencies.stderr((code === void 0 ? "" : code + ": ") + message + "\n");
4436
+ return error instanceof InitConfigurationError ? error.exitCode : 3;
3579
4437
  }
3580
4438
  const dryRun = options.dryRun === true;
3581
- if (targetProfile !== currentProfile && !dryRun) {
4439
+ if ((targetProfile !== currentProfile || targetAgents.some((agent, index) => agent !== refreshAgents(detection.config)[index]) || targetAgents.length !== refreshAgents(detection.config).length) && !dryRun) {
3582
4440
  try {
3583
4441
  const preview = await refreshProject({
3584
4442
  projectRoot: dependencies.cwd,
3585
4443
  resourcesRoot: dependencies.resourcesRoot,
3586
4444
  profile: targetProfile,
4445
+ agents: targetAgents,
4446
+ codebuddySurface: codebuddySurface(detection.config),
3587
4447
  dryRun: true,
3588
4448
  forceManaged: options.forceManaged === true
3589
4449
  });
@@ -3618,6 +4478,8 @@ async function runRefresh(options, dependencies) {
3618
4478
  projectRoot: dependencies.cwd,
3619
4479
  resourcesRoot: dependencies.resourcesRoot,
3620
4480
  profile: targetProfile,
4481
+ agents: targetAgents,
4482
+ codebuddySurface: codebuddySurface(detection.config),
3621
4483
  dryRun,
3622
4484
  forceManaged: options.forceManaged === true
3623
4485
  });
@@ -3635,8 +4497,11 @@ async function runRefresh(options, dependencies) {
3635
4497
  }
3636
4498
  return output.exit_code;
3637
4499
  } catch (error) {
4500
+ const info = harnessErrorInfo(error);
4501
+ const exitCode = info.exitCode ?? 1;
4502
+ const code = info.code;
3638
4503
  const message = error instanceof Error ? error.message : String(error);
3639
- dependencies.stderr(message + "\n");
4504
+ dependencies.stderr((code !== void 0 ? code + ": " : "") + message + "\n");
3640
4505
  if (options.json === true) {
3641
4506
  dependencies.stdout(serializeCliResult({
3642
4507
  schema_version: 1,
@@ -3644,15 +4509,15 @@ async function runRefresh(options, dependencies) {
3644
4509
  request_id: requestId,
3645
4510
  dry_run: dryRun,
3646
4511
  ok: false,
3647
- exit_code: 1,
4512
+ exit_code: exitCode,
3648
4513
  project_id: null,
3649
4514
  summary: { applied: 0, removed: 0, preserved: 0, unchanged: 0, conflicts: 0 },
3650
4515
  items: [],
3651
4516
  warnings: [],
3652
- errors: [{ message }]
4517
+ errors: [{ ...code === void 0 ? {} : { code }, message }]
3653
4518
  }));
3654
4519
  }
3655
- return 1;
4520
+ return exitCode;
3656
4521
  }
3657
4522
  }
3658
4523
 
@@ -3663,13 +4528,23 @@ function otherProfile(current) {
3663
4528
  async function runFirstInstall(options, dependencies) {
3664
4529
  const requestId = uuidV7();
3665
4530
  try {
4531
+ const warnings = [];
3666
4532
  const config = await resolveInitConfig(
3667
4533
  dependencies.cwd,
3668
4534
  options,
3669
- options.nonInteractive === true ? void 0 : () => dependencies.prompt(
3670
- "\u8BF7\u9009\u62E9 Harness \u7C7B\u578B\uFF1A\n1. \u901A\u7528\uFF08\u9ED8\u8BA4\uFF09\n2. Java\n\u8BF7\u8F93\u5165 1 \u6216 2 [1]: "
3671
- ).then((answer) => answer.trim())
4535
+ options.nonInteractive === true ? {} : {
4536
+ agents: () => dependencies.prompt(
4537
+ "\u8BF7\u9009\u62E9\u76EE\u6807 Agent\uFF08\u53EF\u591A\u9009\uFF0C\u4F7F\u7528\u9017\u53F7\u5206\u9694\uFF09\n 1. Claude Code\n 2. Codex\n 3. Cursor\n 4. CodeBuddy\n\u8BF7\u8F93\u5165\u7F16\u53F7 [1]: "
4538
+ ).then((answer) => answer.trim()),
4539
+ profile: () => dependencies.prompt(
4540
+ "\u8BF7\u9009\u62E9 Harness \u7C7B\u578B\uFF1A\n1. \u901A\u7528\uFF08\u9ED8\u8BA4\uFF09\n2. Java\n\u8BF7\u8F93\u5165 1 \u6216 2 [1]: "
4541
+ ).then((answer) => answer.trim())
4542
+ },
4543
+ warnings
3672
4544
  );
4545
+ for (const warning of warnings) {
4546
+ dependencies.stderr(warning + "\n");
4547
+ }
3673
4548
  if (options.nonInteractive === true && options.yes !== true && options.dryRun !== true) {
3674
4549
  dependencies.stderr("\u975E\u4EA4\u4E92\u6A21\u5F0F\u6267\u884C\u5199\u5165\u64CD\u4F5C\u9700\u8981 --yes\n");
3675
4550
  return 2;
@@ -3696,9 +4571,11 @@ async function runFirstInstall(options, dependencies) {
3696
4571
  dependencies.stdout(options.json === true ? serializeCliResult(output) : "Hunter Harness \u521D\u59CB\u5316\u5B8C\u6210\uFF0C\u5171\u5904\u7406 " + result.paths.length + " \u4E2A\u6587\u4EF6\u3002\n");
3697
4572
  return 0;
3698
4573
  } catch (error) {
3699
- const exitCode = error instanceof InitConfigurationError ? error.exitCode : 1;
4574
+ const info = harnessErrorInfo(error);
4575
+ const exitCode = info.exitCode ?? 1;
4576
+ const code = info.code;
3700
4577
  const message = error instanceof Error ? error.message : String(error);
3701
- dependencies.stderr(message + "\n");
4578
+ dependencies.stderr((code !== void 0 ? code + ": " : "") + message + "\n");
3702
4579
  if (options.json === true) {
3703
4580
  dependencies.stdout(serializeCliResult({
3704
4581
  schema_version: 1,
@@ -3711,7 +4588,7 @@ async function runFirstInstall(options, dependencies) {
3711
4588
  summary: { planned: 0, applied: 0 },
3712
4589
  items: [],
3713
4590
  warnings: [],
3714
- errors: [{ message }]
4591
+ errors: [{ ...code === void 0 ? {} : { code }, message }]
3715
4592
  }));
3716
4593
  }
3717
4594
  return exitCode;
@@ -3719,6 +4596,7 @@ async function runFirstInstall(options, dependencies) {
3719
4596
  }
3720
4597
  async function runExistingProject(options, dependencies, currentProfile) {
3721
4598
  const refreshOptions = {};
4599
+ if (options.agents !== void 0) refreshOptions.agents = options.agents;
3722
4600
  if (options.profile !== void 0) refreshOptions.profile = options.profile;
3723
4601
  if (options.nonInteractive !== void 0) refreshOptions.nonInteractive = options.nonInteractive;
3724
4602
  if (options.yes !== void 0) refreshOptions.yes = options.yes;
@@ -3886,7 +4764,7 @@ async function runPush(options, dependencies) {
3886
4764
  warnings: result.preview.skipped,
3887
4765
  errors: []
3888
4766
  };
3889
- dependencies.stdout(options.json === true ? serializeCliResult(output) : options.dryRun === true ? "Push preview contains " + items.length + " operations.\n" : "Proposal " + result.proposalId + " created.\n");
4767
+ dependencies.stdout(options.json === true ? serializeCliResult(output) : options.dryRun === true ? "Push preview contains " + items.length + " operations.\n" : "Pushed artifact " + result.artifactId + " (proposal " + result.proposalId + ").\n");
3890
4768
  return 0;
3891
4769
  } catch (error) {
3892
4770
  const exitCode = error instanceof PushWorkflowError ? error.exitCode : 1;
@@ -4006,18 +4884,18 @@ async function runUpdate(options, dependencies) {
4006
4884
  }
4007
4885
 
4008
4886
  // src/commands/recovery.ts
4009
- import { stat as stat5 } from "node:fs/promises";
4887
+ import { stat as stat4 } from "node:fs/promises";
4010
4888
  import { join as join15 } from "node:path";
4011
- async function exists5(path) {
4889
+ async function exists4(path) {
4012
4890
  try {
4013
- await stat5(path);
4891
+ await stat4(path);
4014
4892
  return true;
4015
4893
  } catch {
4016
4894
  return false;
4017
4895
  }
4018
4896
  }
4019
4897
  function explicitConfigure(options) {
4020
- return options.adapter !== void 0 || options.profile !== void 0 || options.config !== void 0 || options.serverUrl !== void 0 || options.tokenEnv !== void 0;
4898
+ return options.agents !== void 0 || options.adapter !== void 0 || options.profile !== void 0 || options.config !== void 0 || options.serverUrl !== void 0 || options.tokenEnv !== void 0;
4021
4899
  }
4022
4900
  async function runRecoveryMenuIfApplicable(options, dependencies) {
4023
4901
  const pending = await pendingTransactions(dependencies.cwd);
@@ -4067,7 +4945,7 @@ async function runRecoveryMenuIfApplicable(options, dependencies) {
4067
4945
  return 5;
4068
4946
  }
4069
4947
  }
4070
- const initialized = await exists5(join15(dependencies.cwd, ".harness", "project.yaml"));
4948
+ const initialized = await exists4(join15(dependencies.cwd, ".harness", "project.yaml"));
4071
4949
  if (!initialized || options.nonInteractive === true || explicitConfigure(options)) {
4072
4950
  return null;
4073
4951
  }
@@ -4109,13 +4987,167 @@ async function runRecoveryMenuIfApplicable(options, dependencies) {
4109
4987
  }
4110
4988
  }
4111
4989
 
4990
+ // src/workflow-data/resolve.ts
4991
+ import { cp, mkdir as mkdir4, readdir as readdir7, readFile as readFile14, stat as stat5 } from "node:fs/promises";
4992
+ import { dirname as dirname4, join as join16, relative as relative2 } from "node:path";
4993
+ import { fileURLToPath } from "node:url";
4994
+ var WorkflowDataResolutionError = class extends Error {
4995
+ code;
4996
+ exitCode;
4997
+ constructor(message, code = "WORKFLOW_DATA_UNAVAILABLE", exitCode = 4) {
4998
+ super(message);
4999
+ this.name = "WorkflowDataResolutionError";
5000
+ this.code = code;
5001
+ this.exitCode = exitCode;
5002
+ }
5003
+ };
5004
+ async function pathExists3(path) {
5005
+ try {
5006
+ await stat5(path);
5007
+ return true;
5008
+ } catch {
5009
+ return false;
5010
+ }
5011
+ }
5012
+ function readOption(argv, name) {
5013
+ const flag = `--${name}`;
5014
+ const index = argv.indexOf(flag);
5015
+ if (index >= 0) return argv[index + 1];
5016
+ const prefixed = argv.find((value) => value.startsWith(`${flag}=`));
5017
+ if (prefixed !== void 0) return prefixed.slice(flag.length + 1);
5018
+ return void 0;
5019
+ }
5020
+ function workflowPackageName(family, env) {
5021
+ const scope = (env.HUNTER_HARNESS_NPM_SCOPE ?? "@hunter-harness").replace(/\/$/, "");
5022
+ if (family === "harness") return `${scope}/workflow-harness`;
5023
+ return `${scope}/workflow-${family}`;
5024
+ }
5025
+ async function listFilesRecursive(root, base = root) {
5026
+ const entries = await readdir7(root, { withFileTypes: true });
5027
+ const files = [];
5028
+ for (const entry of entries) {
5029
+ const absolute = join16(root, entry.name);
5030
+ if (entry.isDirectory()) {
5031
+ files.push(...await listFilesRecursive(absolute, base));
5032
+ continue;
5033
+ }
5034
+ if (!entry.isFile()) continue;
5035
+ const rel = relative2(base, absolute).replaceAll("\\", "/");
5036
+ files.push({ path: rel, content: await readFile14(absolute, "utf8") });
5037
+ }
5038
+ return files;
5039
+ }
5040
+ function sha256Canonical(files) {
5041
+ return sha256Bytes(canonicalJson(
5042
+ [...files].sort((left, right) => left.path.localeCompare(right.path)).map((file) => ({ path: file.path, content: file.content }))
5043
+ ));
5044
+ }
5045
+ async function verifyWorkflowPackageIntegrity(resourcesRoot) {
5046
+ const manifest = await readWorkflowFamilyManifest(resourcesRoot);
5047
+ const expected = manifest.content_sha256;
5048
+ if (typeof expected !== "string" || expected.length === 0) return;
5049
+ const harnessRoot = join16(resourcesRoot, "harness");
5050
+ if (!await pathExists3(harnessRoot)) {
5051
+ throw new WorkflowDataResolutionError(
5052
+ "\u5DE5\u4F5C\u6D41\u6570\u636E\u5305\u7F3A\u5C11 harness/\uFF0C\u65E0\u6CD5\u6821\u9A8C SHA-256",
5053
+ "WORKFLOW_DATA_INTEGRITY",
5054
+ 7
5055
+ );
5056
+ }
5057
+ const files = await listFilesRecursive(harnessRoot, resourcesRoot);
5058
+ const actual = sha256Canonical(files);
5059
+ if (actual !== expected) {
5060
+ throw new WorkflowDataResolutionError(
5061
+ `\u5DE5\u4F5C\u6D41\u6570\u636E\u5305 SHA-256 \u6821\u9A8C\u5931\u8D25\uFF08\u671F\u671B ${expected}\uFF0C\u5B9E\u9645 ${actual}\uFF09`,
5062
+ "WORKFLOW_DATA_INTEGRITY",
5063
+ 7
5064
+ );
5065
+ }
5066
+ }
5067
+ async function monorepoResourcesRoot() {
5068
+ const candidates = [
5069
+ fileURLToPath(new URL("../../../resources", import.meta.url)),
5070
+ fileURLToPath(new URL("../../../workflow-data-harness", import.meta.url)),
5071
+ fileURLToPath(new URL("../../../../resources", import.meta.url))
5072
+ ];
5073
+ for (const candidate of candidates) {
5074
+ if (await pathExists3(join16(candidate, "harness", "manifests"))) return candidate;
5075
+ }
5076
+ return null;
5077
+ }
5078
+ async function siblingWorkflowPackage(cwd) {
5079
+ const candidates = [
5080
+ join16(cwd, "node_modules", "@hunter-harness", "workflow-harness"),
5081
+ join16(dirname4(fileURLToPath(import.meta.url)), "..", "..", "workflow-data-harness")
5082
+ ];
5083
+ for (const candidate of candidates) {
5084
+ if (await pathExists3(join16(candidate, "harness", "manifests"))) return candidate;
5085
+ }
5086
+ return null;
5087
+ }
5088
+ async function resolveWorkflowResourcesRoot(options, argv = []) {
5089
+ if (options.override !== void 0) return options.override;
5090
+ const envRoot = options.env.HUNTER_HARNESS_RESOURCES_ROOT?.trim();
5091
+ if (envRoot !== void 0 && envRoot.length > 0) return envRoot;
5092
+ const sibling = await siblingWorkflowPackage(options.cwd);
5093
+ if (sibling !== null) {
5094
+ await verifyWorkflowPackageIntegrity(sibling);
5095
+ return sibling;
5096
+ }
5097
+ const monorepo = await monorepoResourcesRoot();
5098
+ if (monorepo !== null) {
5099
+ await verifyWorkflowPackageIntegrity(monorepo);
5100
+ return monorepo;
5101
+ }
5102
+ const family = options.workflowFamily ?? readOption(argv, "workflow-family") ?? options.env.HUNTER_HARNESS_WORKFLOW_FAMILY ?? "harness";
5103
+ const version = options.workflowVersion ?? readOption(argv, "workflow-version") ?? options.env.HUNTER_HARNESS_WORKFLOW_VERSION ?? "latest";
5104
+ const packageName = workflowPackageName(family, options.env);
5105
+ const packageSpec = version === "latest" ? packageName : `${packageName}@${version}`;
5106
+ const cacheKey = packageSpec.replace("/", "+");
5107
+ const cacheRoot = join16(options.cwd, ".harness", "cache", "workflow-packages", cacheKey);
5108
+ if (await pathExists3(join16(cacheRoot, "harness", "manifests"))) {
5109
+ await verifyWorkflowPackageIntegrity(cacheRoot);
5110
+ return cacheRoot;
5111
+ }
5112
+ try {
5113
+ await mkdir4(cacheRoot, { recursive: true });
5114
+ const extract = options.pacoteExtract ?? (async (spec, destination) => {
5115
+ const pacote = await import("pacote");
5116
+ await pacote.default.extract(spec, destination);
5117
+ });
5118
+ const staging = join16(cacheRoot, ".extract");
5119
+ await mkdir4(staging, { recursive: true });
5120
+ await extract(packageSpec, staging);
5121
+ const packageDir = await pathExists3(join16(staging, "harness", "manifests")) ? staging : join16(staging, "package");
5122
+ if (!await pathExists3(join16(packageDir, "harness", "manifests"))) {
5123
+ throw new WorkflowDataResolutionError(
5124
+ "\u5DE5\u4F5C\u6D41\u6570\u636E\u5305\u5185\u5BB9\u65E0\u6548\uFF1A\u7F3A\u5C11 harness/manifests",
5125
+ "WORKFLOW_DATA_INVALID",
5126
+ 7
5127
+ );
5128
+ }
5129
+ await cp(packageDir, cacheRoot, { recursive: true });
5130
+ await verifyWorkflowPackageIntegrity(cacheRoot);
5131
+ return cacheRoot;
5132
+ } catch (error) {
5133
+ if (error instanceof WorkflowDataResolutionError) throw error;
5134
+ throw new WorkflowDataResolutionError(
5135
+ "\u65E0\u6CD5\u83B7\u53D6\u5DE5\u4F5C\u6D41\u6570\u636E\u5305\uFF1A\u65E0\u7F51\u7EDC\u4E14\u672C\u5730\u7F13\u5B58\u4E0D\u5B58\u5728\u3002\u8BF7\u5148\u5728\u6709\u7F51\u7EDC\u7684\u73AF\u5883\u8FD0\u884C hunter-harness\uFF0C\u6216\u8BBE\u7F6E HUNTER_HARNESS_RESOURCES_ROOT \u6307\u5411\u542B harness/ \u7684\u76EE\u5F55\u3002",
5136
+ "WORKFLOW_DATA_UNAVAILABLE"
5137
+ );
5138
+ }
5139
+ }
5140
+ async function readWorkflowFamilyManifest(resourcesRoot) {
5141
+ const manifestPath = join16(resourcesRoot, "hunter-workflow-family.json");
5142
+ if (!await pathExists3(manifestPath)) return {};
5143
+ return JSON.parse(await readFile14(manifestPath, "utf8"));
5144
+ }
5145
+
4112
5146
  // src/bin.ts
4113
5147
  function defaultDependencies(overrides) {
4114
5148
  return {
4115
5149
  cwd: overrides.cwd ?? process.cwd(),
4116
- resourcesRoot: overrides.resourcesRoot ?? fileURLToPath(
4117
- new URL("../resources", import.meta.url)
4118
- ),
5150
+ resourcesRoot: overrides.resourcesRoot ?? "",
4119
5151
  stdout: overrides.stdout ?? ((value) => process.stdout.write(value)),
4120
5152
  stderr: overrides.stderr ?? ((value) => process.stderr.write(value)),
4121
5153
  prompt: overrides.prompt ?? (async (question) => {
@@ -4131,10 +5163,27 @@ function defaultDependencies(overrides) {
4131
5163
  };
4132
5164
  }
4133
5165
  function addCommonOptions(command) {
4134
- return command.option("--dry-run").option("--yes").option("--json").option("--server-url <url>").option("--token-env <ENV_NAME>").option("--non-interactive");
5166
+ return command.option("--dry-run").option("--yes").option("--json").option("--server-url <url>").option("--token-env <ENV_NAME>").option("--non-interactive").option("--agents <csv>").option("--codebuddy-surface <surface>").option("--workflow-family <slug>").option("--workflow-version <version>");
4135
5167
  }
4136
5168
  async function runCli(argv, overrides = {}) {
4137
5169
  const dependencies = defaultDependencies(overrides);
5170
+ try {
5171
+ const resolveOptions = {
5172
+ cwd: dependencies.cwd,
5173
+ env: dependencies.env,
5174
+ override: overrides.resourcesRoot
5175
+ };
5176
+ if (overrides.pacoteExtract !== void 0) {
5177
+ resolveOptions.pacoteExtract = overrides.pacoteExtract;
5178
+ }
5179
+ dependencies.resourcesRoot = await resolveWorkflowResourcesRoot(resolveOptions, argv);
5180
+ } catch (error) {
5181
+ if (error instanceof WorkflowDataResolutionError) {
5182
+ dependencies.stderr(error.message + "\n");
5183
+ return error.exitCode;
5184
+ }
5185
+ throw error;
5186
+ }
4138
5187
  const program = addCommonOptions(new Command()).name("hunter-harness").description("Local-first, server-governed agent harness").option("--profile <name>").option("--config <file>").option("--force-managed").showHelpAfterError().exitOverride().configureOutput({
4139
5188
  writeOut: dependencies.stdout,
4140
5189
  writeErr: dependencies.stderr