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.
- package/LICENSE +21 -21
- package/README.md +11 -0
- package/dist/bin.js +1540 -491
- package/package.json +9 -7
- package/resources/harness/general/.harness-build.json +0 -5
- package/resources/harness/general/CONTEXT.md +0 -65
- package/resources/harness/general/README.md +0 -478
- package/resources/harness/general/agents/harness-evaluator.md +0 -96
- package/resources/harness/general/agents/harness-explorer.md +0 -72
- package/resources/harness/general/agents/harness-reviewer.md +0 -69
- package/resources/harness/general/harness-archive/SKILL.md +0 -215
- package/resources/harness/general/harness-archive/checklist.md +0 -52
- package/resources/harness/general/harness-archive/reference.md +0 -103
- package/resources/harness/general/harness-archive/scripts/gen-manifest.ps1 +0 -31
- package/resources/harness/general/harness-archive/templates/render-summary.mjs +0 -171
- package/resources/harness/general/harness-archive/templates/summary-data-template.json +0 -128
- package/resources/harness/general/harness-codebase-map/SKILL.md +0 -112
- package/resources/harness/general/harness-codebase-map/checklist.md +0 -126
- package/resources/harness/general/harness-codebase-map/reference.md +0 -364
- package/resources/harness/general/harness-codebase-map/templates/ARCHITECTURE.md +0 -57
- package/resources/harness/general/harness-codebase-map/templates/CONCERNS.md +0 -49
- package/resources/harness/general/harness-codebase-map/templates/CONVENTIONS.md +0 -57
- package/resources/harness/general/harness-codebase-map/templates/INTEGRATIONS.md +0 -52
- package/resources/harness/general/harness-codebase-map/templates/STACK.md +0 -55
- package/resources/harness/general/harness-codebase-map/templates/STRUCTURE.md +0 -69
- package/resources/harness/general/harness-codebase-map/templates/TESTING.md +0 -64
- package/resources/harness/general/harness-codebase-map/templates/map-manifest.schema.json +0 -73
- package/resources/harness/general/harness-codebase-map/templates/map-summary.md +0 -64
- package/resources/harness/general/harness-knowledge-ingest/SKILL.md +0 -246
- package/resources/harness/general/harness-knowledge-ingest/design.md +0 -842
- package/resources/harness/general/harness-knowledge-ingest/evaluations/harness_knowledge_evaluation.xml +0 -42
- package/resources/harness/general/harness-knowledge-ingest/mcp-config.example.json +0 -10
- package/resources/harness/general/harness-knowledge-ingest/reference.md +0 -309
- package/resources/harness/general/harness-knowledge-ingest/scripts/harness_knowledge.py +0 -4082
- package/resources/harness/general/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py +0 -273
- package/resources/harness/general/harness-knowledge-query/SKILL.md +0 -164
- package/resources/harness/general/harness-plan/SKILL.md +0 -127
- package/resources/harness/general/harness-plan/checklist.md +0 -243
- package/resources/harness/general/harness-plan/protocols.md +0 -124
- package/resources/harness/general/harness-plan/reference.md +0 -376
- package/resources/harness/general/harness-review/SKILL.md +0 -156
- package/resources/harness/general/harness-review/checklist.md +0 -124
- package/resources/harness/general/harness-review/protocols.md +0 -68
- package/resources/harness/general/harness-review/reference.md +0 -86
- package/resources/harness/general/harness-run/SKILL.md +0 -132
- package/resources/harness/general/harness-run/checklist.md +0 -259
- package/resources/harness/general/harness-run/protocols.md +0 -78
- package/resources/harness/general/harness-run/reference.md +0 -834
- package/resources/harness/general/harness-submit/SKILL.md +0 -159
- package/resources/harness/general/harness-submit/checklist.md +0 -407
- package/resources/harness/general/harness-submit/reference.md +0 -152
- package/resources/harness/general/harness-sync/SKILL.md +0 -82
- package/resources/harness/general/harness-sync/reference.md +0 -153
- package/resources/harness/general/harness-test/SKILL.md +0 -180
- package/resources/harness/general/harness-test/checklist.md +0 -319
- package/resources/harness/general/harness-test/pitfalls.md +0 -260
- package/resources/harness/general/harness-test/reference.md +0 -791
- package/resources/harness/general/protocols/archive-report-protocol.md +0 -175
- package/resources/harness/general/protocols/evidence-based-reporting-protocol.md +0 -134
- package/resources/harness/general/protocols/ledger-protocol.md +0 -270
- package/resources/harness/general/protocols/powershell-protocol.md +0 -181
- package/resources/harness/general/protocols/report-pipeline-protocol.md +0 -150
- package/resources/harness/general/protocols/sensitive-info-protocol.md +0 -92
- package/resources/harness/general/protocols/state-layout-protocol.md +0 -131
- package/resources/harness/general/protocols/submit-protocol.md +0 -78
- package/resources/harness/general/scripts/harness_archive.py +0 -2319
- package/resources/harness/general/scripts/harness_deploy.py +0 -613
- package/resources/harness/general/scripts/harness_events.py +0 -680
- package/resources/harness/general/scripts/harness_ledger.py +0 -671
- package/resources/harness/general/scripts/harness_preflight.py +0 -780
- package/resources/harness/general/scripts/harness_service.py +0 -1331
- package/resources/harness/java/.harness-build.json +0 -5
- package/resources/harness/java/CONTEXT.md +0 -65
- package/resources/harness/java/README.md +0 -478
- package/resources/harness/java/agents/harness-evaluator.md +0 -96
- package/resources/harness/java/agents/harness-explorer.md +0 -72
- package/resources/harness/java/agents/harness-reviewer.md +0 -69
- package/resources/harness/java/harness-apidoc/SKILL.md +0 -86
- package/resources/harness/java/harness-apidoc/checklist.md +0 -142
- package/resources/harness/java/harness-apidoc/reference.md +0 -233
- package/resources/harness/java/harness-archive/SKILL.md +0 -215
- package/resources/harness/java/harness-archive/checklist.md +0 -52
- package/resources/harness/java/harness-archive/reference.md +0 -103
- package/resources/harness/java/harness-archive/scripts/gen-manifest.ps1 +0 -31
- package/resources/harness/java/harness-archive/templates/render-summary.mjs +0 -171
- package/resources/harness/java/harness-archive/templates/summary-data-template.json +0 -128
- package/resources/harness/java/harness-codebase-map/SKILL.md +0 -112
- package/resources/harness/java/harness-codebase-map/checklist.md +0 -126
- package/resources/harness/java/harness-codebase-map/reference.md +0 -364
- package/resources/harness/java/harness-codebase-map/templates/ARCHITECTURE.md +0 -57
- package/resources/harness/java/harness-codebase-map/templates/CONCERNS.md +0 -49
- package/resources/harness/java/harness-codebase-map/templates/CONVENTIONS.md +0 -57
- package/resources/harness/java/harness-codebase-map/templates/INTEGRATIONS.md +0 -52
- package/resources/harness/java/harness-codebase-map/templates/STACK.md +0 -55
- package/resources/harness/java/harness-codebase-map/templates/STRUCTURE.md +0 -69
- package/resources/harness/java/harness-codebase-map/templates/TESTING.md +0 -64
- package/resources/harness/java/harness-codebase-map/templates/map-manifest.schema.json +0 -73
- package/resources/harness/java/harness-codebase-map/templates/map-summary.md +0 -64
- package/resources/harness/java/harness-knowledge-ingest/SKILL.md +0 -246
- package/resources/harness/java/harness-knowledge-ingest/design.md +0 -842
- package/resources/harness/java/harness-knowledge-ingest/evaluations/harness_knowledge_evaluation.xml +0 -42
- package/resources/harness/java/harness-knowledge-ingest/mcp-config.example.json +0 -10
- package/resources/harness/java/harness-knowledge-ingest/reference.md +0 -309
- package/resources/harness/java/harness-knowledge-ingest/scripts/harness_knowledge.py +0 -4082
- package/resources/harness/java/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py +0 -273
- package/resources/harness/java/harness-knowledge-query/SKILL.md +0 -164
- package/resources/harness/java/harness-package/SKILL.md +0 -87
- package/resources/harness/java/harness-package/checklist.md +0 -322
- package/resources/harness/java/harness-package/reference.md +0 -210
- package/resources/harness/java/harness-plan/SKILL.md +0 -127
- package/resources/harness/java/harness-plan/checklist.md +0 -243
- package/resources/harness/java/harness-plan/protocols.md +0 -124
- package/resources/harness/java/harness-plan/reference.md +0 -376
- package/resources/harness/java/harness-review/SKILL.md +0 -156
- package/resources/harness/java/harness-review/checklist.md +0 -124
- package/resources/harness/java/harness-review/protocols.md +0 -68
- package/resources/harness/java/harness-review/reference.md +0 -86
- package/resources/harness/java/harness-run/SKILL.md +0 -148
- package/resources/harness/java/harness-run/checklist.md +0 -250
- package/resources/harness/java/harness-run/protocols.md +0 -78
- package/resources/harness/java/harness-run/reference.md +0 -814
- package/resources/harness/java/harness-submit/SKILL.md +0 -166
- package/resources/harness/java/harness-submit/checklist.md +0 -407
- package/resources/harness/java/harness-submit/reference.md +0 -152
- package/resources/harness/java/harness-sync/SKILL.md +0 -82
- package/resources/harness/java/harness-sync/reference.md +0 -153
- package/resources/harness/java/harness-test/SKILL.md +0 -192
- package/resources/harness/java/harness-test/checklist.md +0 -317
- package/resources/harness/java/harness-test/pitfalls-java.md +0 -16
- package/resources/harness/java/harness-test/pitfalls.md +0 -260
- package/resources/harness/java/harness-test/reference.md +0 -788
- package/resources/harness/java/protocols/archive-report-protocol.md +0 -175
- package/resources/harness/java/protocols/evidence-based-reporting-protocol.md +0 -134
- package/resources/harness/java/protocols/ledger-protocol.md +0 -270
- package/resources/harness/java/protocols/powershell-protocol.md +0 -181
- package/resources/harness/java/protocols/report-pipeline-protocol.md +0 -150
- package/resources/harness/java/protocols/sensitive-info-protocol.md +0 -92
- package/resources/harness/java/protocols/state-layout-protocol.md +0 -131
- package/resources/harness/java/protocols/submit-protocol.md +0 -78
- package/resources/harness/java/scripts/harness_archive.py +0 -2319
- package/resources/harness/java/scripts/harness_deploy.py +0 -613
- package/resources/harness/java/scripts/harness_events.py +0 -680
- package/resources/harness/java/scripts/harness_ledger.py +0 -671
- package/resources/harness/java/scripts/harness_preflight.py +0 -780
- package/resources/harness/java/scripts/harness_service.py +0 -1331
- package/resources/harness/manifests/general.json +0 -276
- package/resources/harness/manifests/java.json +0 -304
- package/resources/harness/migrations/0.1.1/general.json +0 -358
- 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 {
|
|
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/
|
|
478
|
+
// ../contracts/dist/external-skill.js
|
|
484
479
|
import { z as z6 } from "zod";
|
|
485
|
-
var
|
|
486
|
-
var
|
|
487
|
-
|
|
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:
|
|
490
|
-
affectedPaths:
|
|
491
|
-
riskDelta:
|
|
492
|
-
message:
|
|
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:
|
|
495
|
-
explanation:
|
|
496
|
-
appliesTo:
|
|
497
|
-
generatedAt:
|
|
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 =
|
|
500
|
-
items:
|
|
501
|
-
mergedFiles:
|
|
502
|
-
summary:
|
|
503
|
-
autoCount:
|
|
504
|
-
confirmCount:
|
|
505
|
-
suggestCount:
|
|
506
|
-
changedFiles:
|
|
507
|
-
changedLines:
|
|
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
|
|
513
|
-
var knowledgeStatusSchema =
|
|
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 =
|
|
521
|
-
id:
|
|
522
|
-
type:
|
|
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:
|
|
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:
|
|
579
|
+
confidence: z8.enum(["verified", "inferred", "unverified", "low"]),
|
|
541
580
|
status: knowledgeStatusSchema,
|
|
542
|
-
domains:
|
|
543
|
-
modules:
|
|
544
|
-
related_paths:
|
|
545
|
-
source:
|
|
546
|
-
kind:
|
|
547
|
-
ref:
|
|
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
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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
|
|
559
|
-
var mcpToolContractSchema =
|
|
560
|
-
tool_name:
|
|
561
|
-
description:
|
|
562
|
-
input_schema:
|
|
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
|
|
567
|
-
var projectIdSchema =
|
|
568
|
-
var tokenEnvSchema =
|
|
569
|
-
var httpsUrlSchema =
|
|
570
|
-
var
|
|
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 =
|
|
578
|
-
|
|
579
|
-
profile:
|
|
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:
|
|
584
|
-
codegraph_check:
|
|
585
|
-
superpowers_check:
|
|
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 =
|
|
589
|
-
harness:
|
|
590
|
-
name:
|
|
591
|
-
schema_version:
|
|
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:
|
|
594
|
-
name:
|
|
595
|
-
root:
|
|
596
|
-
local_project_key:
|
|
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:
|
|
780
|
+
profiles: z10.array(z10.string().min(1)).min(1)
|
|
599
781
|
}).strict(),
|
|
600
|
-
server:
|
|
782
|
+
server: z10.object({
|
|
601
783
|
url: httpsUrlSchema.nullable(),
|
|
602
784
|
token_env: tokenEnvSchema
|
|
603
785
|
}).strict(),
|
|
604
|
-
adapters:
|
|
605
|
-
enabled:
|
|
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
|
|
611
|
-
var dashboardHealthStatusSchema =
|
|
612
|
-
var dashboardServiceStatusSchema =
|
|
613
|
-
var dashboardTrendPointSchema =
|
|
614
|
-
date:
|
|
615
|
-
submitted:
|
|
616
|
-
approved:
|
|
617
|
-
rejected:
|
|
618
|
-
pending:
|
|
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 =
|
|
621
|
-
key:
|
|
622
|
-
count:
|
|
807
|
+
var dashboardDistributionItemSchema = z11.object({
|
|
808
|
+
key: z11.string().min(1),
|
|
809
|
+
count: z11.number().int().nonnegative()
|
|
623
810
|
}).strict();
|
|
624
|
-
var dashboardHealthItemSchema =
|
|
625
|
-
key:
|
|
626
|
-
label:
|
|
811
|
+
var dashboardHealthItemSchema = z11.object({
|
|
812
|
+
key: z11.string().min(1),
|
|
813
|
+
label: z11.string().min(1),
|
|
627
814
|
status: dashboardHealthStatusSchema,
|
|
628
|
-
value:
|
|
629
|
-
detail:
|
|
815
|
+
value: z11.string().min(1),
|
|
816
|
+
detail: z11.string().min(1)
|
|
630
817
|
}).strict();
|
|
631
|
-
var dashboardServiceItemSchema =
|
|
632
|
-
key:
|
|
633
|
-
label:
|
|
818
|
+
var dashboardServiceItemSchema = z11.object({
|
|
819
|
+
key: z11.string().min(1),
|
|
820
|
+
label: z11.string().min(1),
|
|
634
821
|
status: dashboardServiceStatusSchema,
|
|
635
|
-
detail:
|
|
636
|
-
checked_at:
|
|
822
|
+
detail: z11.string().min(1),
|
|
823
|
+
checked_at: z11.iso.datetime()
|
|
637
824
|
}).strict();
|
|
638
|
-
var dashboardActivitySchema =
|
|
639
|
-
event_id:
|
|
640
|
-
action:
|
|
641
|
-
target_id:
|
|
642
|
-
project_id:
|
|
643
|
-
actor_id:
|
|
644
|
-
created_at:
|
|
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 =
|
|
647
|
-
generated_at:
|
|
648
|
-
window:
|
|
649
|
-
days:
|
|
650
|
-
starts_at:
|
|
651
|
-
ends_at:
|
|
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:
|
|
654
|
-
projects:
|
|
655
|
-
workflows:
|
|
656
|
-
skills:
|
|
657
|
-
published_skills:
|
|
658
|
-
pending_reviews:
|
|
659
|
-
approved_proposals:
|
|
660
|
-
rejected_proposals:
|
|
661
|
-
artifacts:
|
|
662
|
-
project_artifacts:
|
|
663
|
-
skill_artifacts:
|
|
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:
|
|
666
|
-
distributions:
|
|
667
|
-
skill_categories:
|
|
668
|
-
workflow_profiles:
|
|
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:
|
|
671
|
-
services:
|
|
672
|
-
activity:
|
|
857
|
+
health: z11.array(dashboardHealthItemSchema),
|
|
858
|
+
services: z11.array(dashboardServiceItemSchema),
|
|
859
|
+
activity: z11.array(dashboardActivitySchema)
|
|
673
860
|
}).strict();
|
|
674
861
|
|
|
675
|
-
// ../contracts/dist/
|
|
676
|
-
import { z as
|
|
677
|
-
var
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
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
|
-
|
|
683
|
-
|
|
684
|
-
|
|
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
|
|
702
|
-
artifact_id:
|
|
703
|
-
|
|
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:
|
|
707
|
-
|
|
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
|
|
710
|
-
|
|
946
|
+
var workflowFamilyVersionSchema = z13.object({
|
|
947
|
+
family_slug: registrySlugSchema,
|
|
711
948
|
version: registrySemverSchema,
|
|
712
|
-
|
|
713
|
-
artifacts:
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
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
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
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:
|
|
725
|
-
revision:
|
|
726
|
-
created_at:
|
|
727
|
-
updated_at:
|
|
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
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
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
|
|
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:
|
|
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) === "
|
|
1067
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
1516
|
-
"
|
|
1517
|
-
"
|
|
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/
|
|
1533
|
-
|
|
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
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
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
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
2220
|
+
const hex2 = bytes.toString("hex");
|
|
1701
2221
|
return [
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
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
|
|
1747
|
-
const
|
|
1748
|
-
const
|
|
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:
|
|
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
|
|
1772
|
-
|
|
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:
|
|
1786
|
-
project: {
|
|
1787
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
2442
|
+
schema_version: 3,
|
|
1808
2443
|
profile,
|
|
1809
|
-
|
|
1810
|
-
bundle_manifest_hash: bundleHash,
|
|
2444
|
+
adapters: enabledAgents,
|
|
1811
2445
|
installed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1812
|
-
|
|
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
|
-
}))
|
|
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:
|
|
2464
|
+
bundleHash: primaryBundleHash,
|
|
2465
|
+
registryVersion: primaryRegistryVersion
|
|
1829
2466
|
};
|
|
1830
2467
|
}
|
|
1831
2468
|
|
|
1832
2469
|
// ../core/dist/project/refresh.js
|
|
1833
|
-
import { createHash as
|
|
1834
|
-
import { readFile as readFile5, readdir as readdir3, rmdir
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
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:
|
|
2605
|
+
content: next
|
|
2008
2606
|
};
|
|
2009
2607
|
}
|
|
2010
|
-
async function
|
|
2011
|
-
if (previousProfile ===
|
|
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
|
-
|
|
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
|
|
2060
|
-
const
|
|
2061
|
-
|
|
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 =
|
|
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
|
|
2117
|
-
await
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
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:
|
|
2822
|
+
schema_version: 3,
|
|
2124
2823
|
profile,
|
|
2125
|
-
|
|
2126
|
-
bundle_manifest_hash: bundleManifestHash,
|
|
2824
|
+
adapters: agents,
|
|
2127
2825
|
installed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2128
|
-
|
|
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
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
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
|
|
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,
|
|
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
|
|
2523
|
-
".claude/rules",
|
|
3230
|
+
var SHARED_MANAGED_ROOTS = [
|
|
2524
3231
|
".harness/knowledge",
|
|
2525
3232
|
".harness/codebase"
|
|
2526
3233
|
];
|
|
2527
|
-
var
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
2564
|
-
|
|
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
|
|
3300
|
+
for (const path of SHARED_MANAGED_ROOTS) {
|
|
2569
3301
|
await walkFiles(root, join9(root, path), paths);
|
|
2570
3302
|
}
|
|
2571
|
-
const
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
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, {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3018
|
-
if (
|
|
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
|
|
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:
|
|
3775
|
+
operation: exists5 ? "modify" : "add",
|
|
3035
3776
|
path: snapshot.path,
|
|
3036
3777
|
content
|
|
3037
3778
|
});
|
|
3038
|
-
} else if (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3446
|
-
|
|
3447
|
-
|
|
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 ?? (
|
|
4293
|
+
fileConfig.profile ?? flags.profile ?? (prompts.profile === void 0 ? void 0 : await prompts.profile())
|
|
3451
4294
|
);
|
|
3452
4295
|
const candidate = {
|
|
3453
|
-
|
|
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
|
-
|
|
3578
|
-
|
|
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:
|
|
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
|
|
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 ?
|
|
3670
|
-
|
|
3671
|
-
|
|
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
|
|
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" : "
|
|
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
|
|
4887
|
+
import { stat as stat4 } from "node:fs/promises";
|
|
4010
4888
|
import { join as join15 } from "node:path";
|
|
4011
|
-
async function
|
|
4889
|
+
async function exists4(path) {
|
|
4012
4890
|
try {
|
|
4013
|
-
await
|
|
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
|
|
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 ??
|
|
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
|