hunter-harness 0.2.19 → 0.2.21
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/dist/bin.js +424 -128
- package/package.json +36 -36
package/dist/bin.js
CHANGED
|
@@ -164,7 +164,15 @@ var requestMetadataSchema = z2.object({
|
|
|
164
164
|
}).strict();
|
|
165
165
|
|
|
166
166
|
// ../contracts/dist/registry.js
|
|
167
|
-
var
|
|
167
|
+
var skillTargetAgentSchema = z3.enum(["claude-code", "codex", "cursor", "codebuddy"]);
|
|
168
|
+
var registryAgentSchema = z3.enum([
|
|
169
|
+
"claude-code",
|
|
170
|
+
"codex",
|
|
171
|
+
"cursor",
|
|
172
|
+
"codebuddy",
|
|
173
|
+
"generic",
|
|
174
|
+
"mcp"
|
|
175
|
+
]);
|
|
168
176
|
var registrySkillStatusSchema = z3.enum([
|
|
169
177
|
"draft",
|
|
170
178
|
"pending_review",
|
|
@@ -180,6 +188,7 @@ var registrySkillProposalStatusSchema = z3.enum([
|
|
|
180
188
|
var registrySemverSchema = z3.string().regex(/^\d+\.\d+\.\d+$/);
|
|
181
189
|
var registrySlugSchema = z3.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/);
|
|
182
190
|
var checkStatusSchema = z3.enum(["green", "yellow", "red"]);
|
|
191
|
+
var skillVariantStatusSchema = z3.enum(["ready", "degraded", "unsupported"]);
|
|
183
192
|
var sourceFileSchema = z3.object({
|
|
184
193
|
path: z3.string(),
|
|
185
194
|
content: z3.string()
|
|
@@ -204,6 +213,39 @@ var skillUsageExampleSchema = z3.object({
|
|
|
204
213
|
result: z3.string(),
|
|
205
214
|
files: z3.array(z3.string()).default([])
|
|
206
215
|
}).strict();
|
|
216
|
+
var sensitiveFindingViewSchema = z3.object({
|
|
217
|
+
rule_id: z3.string().min(1),
|
|
218
|
+
severity: z3.enum(["high", "medium", "low"]),
|
|
219
|
+
path: z3.string().min(1),
|
|
220
|
+
line: z3.number().int().positive(),
|
|
221
|
+
column: z3.number().int().positive(),
|
|
222
|
+
fingerprint: sha256Schema,
|
|
223
|
+
redacted_preview: z3.string(),
|
|
224
|
+
overridable: z3.boolean()
|
|
225
|
+
}).strict();
|
|
226
|
+
var sensitiveReviewSubmissionSchema = z3.object({
|
|
227
|
+
scanner_version: z3.string().min(1),
|
|
228
|
+
finding_fingerprints: z3.array(sha256Schema).min(1),
|
|
229
|
+
reason: z3.string().trim().min(3).max(500)
|
|
230
|
+
}).strict();
|
|
231
|
+
var sensitiveReviewEvidenceSchema = sensitiveReviewSubmissionSchema.extend({
|
|
232
|
+
actor: z3.string().min(1),
|
|
233
|
+
accepted_at: z3.iso.datetime()
|
|
234
|
+
}).strict();
|
|
235
|
+
var skillVariantSchema = z3.object({
|
|
236
|
+
agent: skillTargetAgentSchema,
|
|
237
|
+
status: skillVariantStatusSchema,
|
|
238
|
+
buildHash: sha256Schema.nullable(),
|
|
239
|
+
adapterVersion: z3.string().min(1),
|
|
240
|
+
components: z3.array(z3.string().min(1))
|
|
241
|
+
}).strict();
|
|
242
|
+
var skillReleaseSchema = z3.object({
|
|
243
|
+
version: registrySemverSchema,
|
|
244
|
+
packageName: z3.string().min(1),
|
|
245
|
+
packageDigest: z3.string().min(1),
|
|
246
|
+
variants: z3.partialRecord(skillTargetAgentSchema, skillVariantSchema),
|
|
247
|
+
created_at: z3.iso.datetime()
|
|
248
|
+
}).strict();
|
|
207
249
|
var agentSkillConfigSchema = z3.object({
|
|
208
250
|
agent: registryAgentSchema,
|
|
209
251
|
enabled: z3.boolean(),
|
|
@@ -250,6 +292,24 @@ var publishSkillRequestSchema = z3.object({
|
|
|
250
292
|
version: registrySemverSchema,
|
|
251
293
|
releaseNote: z3.string().optional()
|
|
252
294
|
}).strict();
|
|
295
|
+
var publishUnifiedSkillRequestSchema = z3.object({
|
|
296
|
+
version: registrySemverSchema,
|
|
297
|
+
sourceAgent: skillTargetAgentSchema,
|
|
298
|
+
draftRevision: z3.number().int().positive(),
|
|
299
|
+
releaseNote: z3.string().trim().min(1).max(2e3).optional()
|
|
300
|
+
}).strict();
|
|
301
|
+
var publishSkillResponseSchema = z3.object({
|
|
302
|
+
release: z3.object({
|
|
303
|
+
slug: registrySlugSchema,
|
|
304
|
+
version: registrySemverSchema
|
|
305
|
+
}).strict(),
|
|
306
|
+
npmRelease: z3.object({
|
|
307
|
+
status: z3.enum(["published", "idempotent"]),
|
|
308
|
+
packageName: z3.string().min(1),
|
|
309
|
+
version: registrySemverSchema,
|
|
310
|
+
tarballHash: z3.string().min(1)
|
|
311
|
+
}).strict()
|
|
312
|
+
}).strict();
|
|
253
313
|
var setDefaultAgentRequestSchema = z3.object({
|
|
254
314
|
defaultAgent: registryAgentSchema,
|
|
255
315
|
revision: z3.number().int().positive()
|
|
@@ -931,94 +991,148 @@ var semanticSearchHitSchema = z12.object({
|
|
|
931
991
|
project_id: z12.string()
|
|
932
992
|
}).strict();
|
|
933
993
|
|
|
934
|
-
// ../contracts/dist/
|
|
994
|
+
// ../contracts/dist/skill-package.js
|
|
935
995
|
import { z as z13 } from "zod";
|
|
936
|
-
var
|
|
937
|
-
|
|
996
|
+
var skillComponentRoleSchema = z13.enum(["skill", "subagent"]);
|
|
997
|
+
var skillBundlePathSchema = z13.string().min(1).refine((value) => {
|
|
998
|
+
if (value === ".")
|
|
999
|
+
return true;
|
|
1000
|
+
return !value.includes("\0") && !value.includes("\\") && !value.startsWith("/") && !/^[A-Za-z]:/.test(value) && value.split("/").every((part) => part !== "" && part !== "." && part !== "..");
|
|
1001
|
+
}, "path must be a normalized relative POSIX path");
|
|
1002
|
+
var skillBundleComponentSchema = z13.object({
|
|
1003
|
+
role: skillComponentRoleSchema,
|
|
1004
|
+
source: skillBundlePathSchema,
|
|
1005
|
+
name: registrySlugSchema.optional(),
|
|
1006
|
+
variants: z13.partialRecord(skillTargetAgentSchema, skillBundlePathSchema).optional()
|
|
1007
|
+
}).strict().superRefine((component, context) => {
|
|
1008
|
+
if (component.role === "skill" && component.variants !== void 0) {
|
|
1009
|
+
context.addIssue({ code: "custom", message: "skill components cannot define agent variants" });
|
|
1010
|
+
}
|
|
1011
|
+
if (component.role === "subagent" && (component.name === void 0 || component.variants === void 0 || Object.keys(component.variants).length === 0)) {
|
|
1012
|
+
context.addIssue({ code: "custom", message: "subagent components require a name and variants" });
|
|
1013
|
+
}
|
|
1014
|
+
}).superRefine((component, context) => {
|
|
1015
|
+
if (component.role === "subagent" && component.source !== ".") {
|
|
1016
|
+
context.addIssue({ code: "custom", message: "subagent source must be '.'; variants select native files" });
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
var authorSkillBundleManifestSchema = z13.object({
|
|
1020
|
+
apiVersion: z13.literal("hunter-harness/v1"),
|
|
1021
|
+
kind: z13.literal("SkillBundle"),
|
|
1022
|
+
components: z13.array(skillBundleComponentSchema).min(1)
|
|
1023
|
+
}).strict().superRefine((manifest, context) => {
|
|
1024
|
+
if (!manifest.components.some((component) => component.role === "skill")) {
|
|
1025
|
+
context.addIssue({ code: "custom", message: "bundle requires a skill component" });
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
var skillPackageFileSchema = z13.object({
|
|
1029
|
+
path: skillBundlePathSchema,
|
|
1030
|
+
sha256: sha256Schema,
|
|
1031
|
+
size: z13.number().int().nonnegative()
|
|
1032
|
+
}).strict();
|
|
1033
|
+
var skillPackageVariantSchema = z13.object({
|
|
1034
|
+
status: skillVariantStatusSchema,
|
|
1035
|
+
adapterVersion: z13.string().min(1),
|
|
1036
|
+
buildHash: sha256Schema.nullable(),
|
|
1037
|
+
components: z13.array(z13.string().min(1))
|
|
1038
|
+
}).strict();
|
|
1039
|
+
var skillPackageManifestV3Schema = z13.object({
|
|
1040
|
+
schema_version: z13.literal(3),
|
|
1041
|
+
slug: registrySlugSchema,
|
|
1042
|
+
version: registrySemverSchema,
|
|
1043
|
+
files: z13.array(skillPackageFileSchema),
|
|
1044
|
+
components: z13.array(skillBundleComponentSchema).min(1),
|
|
1045
|
+
variants: z13.record(skillTargetAgentSchema, skillPackageVariantSchema)
|
|
1046
|
+
}).strict();
|
|
1047
|
+
|
|
1048
|
+
// ../contracts/dist/workflow-family.js
|
|
1049
|
+
import { z as z14 } from "zod";
|
|
1050
|
+
var workflowBundleManifestSchema = z14.object({
|
|
1051
|
+
schema_version: z14.literal(1),
|
|
938
1052
|
profile: registrySlugSchema,
|
|
939
|
-
files:
|
|
940
|
-
path:
|
|
1053
|
+
files: z14.array(z14.object({
|
|
1054
|
+
path: z14.string().min(1),
|
|
941
1055
|
sha256: sha256Schema
|
|
942
1056
|
}).strict()).min(1)
|
|
943
1057
|
}).strict();
|
|
944
|
-
var workflowFamilyBundleArtifactSchema =
|
|
945
|
-
artifact_id:
|
|
1058
|
+
var workflowFamilyBundleArtifactSchema = z14.object({
|
|
1059
|
+
artifact_id: z14.string().regex(/^wfb_/),
|
|
946
1060
|
family_slug: registrySlugSchema,
|
|
947
1061
|
profile: registrySlugSchema,
|
|
948
1062
|
version: registrySemverSchema,
|
|
949
1063
|
content_sha256: sha256Schema,
|
|
950
|
-
size_bytes:
|
|
1064
|
+
size_bytes: z14.number().int().nonnegative(),
|
|
951
1065
|
bundle_manifest: workflowBundleManifestSchema,
|
|
952
|
-
created_at:
|
|
1066
|
+
created_at: z14.iso.datetime()
|
|
953
1067
|
}).strict();
|
|
954
|
-
var workflowFamilyVersionProfileSchema =
|
|
1068
|
+
var workflowFamilyVersionProfileSchema = z14.object({
|
|
955
1069
|
profile: registrySlugSchema,
|
|
956
1070
|
bundle_manifest: workflowBundleManifestSchema,
|
|
957
|
-
artifact_id:
|
|
958
|
-
sourceFiles:
|
|
1071
|
+
artifact_id: z14.string().regex(/^wfb_/),
|
|
1072
|
+
sourceFiles: z14.array(sourceFileSchema).default([])
|
|
959
1073
|
}).strict();
|
|
960
|
-
var workflowFamilyVersionSchema =
|
|
1074
|
+
var workflowFamilyVersionSchema = z14.object({
|
|
961
1075
|
family_slug: registrySlugSchema,
|
|
962
1076
|
version: registrySemverSchema,
|
|
963
|
-
profiles:
|
|
964
|
-
artifacts:
|
|
965
|
-
changeNote:
|
|
966
|
-
created_at:
|
|
1077
|
+
profiles: z14.array(workflowFamilyVersionProfileSchema).min(1),
|
|
1078
|
+
artifacts: z14.array(workflowFamilyBundleArtifactSchema),
|
|
1079
|
+
changeNote: z14.string().nullable(),
|
|
1080
|
+
created_at: z14.iso.datetime()
|
|
967
1081
|
}).strict();
|
|
968
|
-
var workflowFamilyDraftProfileSchema =
|
|
1082
|
+
var workflowFamilyDraftProfileSchema = z14.object({
|
|
969
1083
|
profile: registrySlugSchema,
|
|
970
|
-
sourceFiles:
|
|
1084
|
+
sourceFiles: z14.array(sourceFileSchema),
|
|
971
1085
|
bundle_manifest: workflowBundleManifestSchema
|
|
972
1086
|
}).strict();
|
|
973
|
-
var workflowFamilyDraftStateSchema =
|
|
1087
|
+
var workflowFamilyDraftStateSchema = z14.object({
|
|
974
1088
|
family_slug: registrySlugSchema,
|
|
975
|
-
profiles:
|
|
976
|
-
required_profiles:
|
|
1089
|
+
profiles: z14.array(workflowFamilyDraftProfileSchema),
|
|
1090
|
+
required_profiles: z14.array(registrySlugSchema),
|
|
977
1091
|
draftVersion: registrySemverSchema.nullable(),
|
|
978
1092
|
checks: skillCheckResultSchema.nullable(),
|
|
979
|
-
releaseNote:
|
|
980
|
-
revision:
|
|
981
|
-
created_at:
|
|
982
|
-
updated_at:
|
|
1093
|
+
releaseNote: z14.string().nullable(),
|
|
1094
|
+
revision: z14.number().int(),
|
|
1095
|
+
created_at: z14.string(),
|
|
1096
|
+
updated_at: z14.string()
|
|
983
1097
|
}).strict();
|
|
984
|
-
var workflowFamilySchema =
|
|
985
|
-
family_id:
|
|
1098
|
+
var workflowFamilySchema = z14.object({
|
|
1099
|
+
family_id: z14.string().regex(/^wff_/),
|
|
986
1100
|
slug: registrySlugSchema,
|
|
987
|
-
displayName:
|
|
988
|
-
description:
|
|
989
|
-
tags:
|
|
1101
|
+
displayName: z14.string().min(1).max(120),
|
|
1102
|
+
description: z14.string().min(1).max(1e3),
|
|
1103
|
+
tags: z14.array(registrySlugSchema).default([]),
|
|
990
1104
|
latest_version: registrySemverSchema.nullable(),
|
|
991
|
-
required_profiles:
|
|
992
|
-
revision:
|
|
993
|
-
npmReleases:
|
|
994
|
-
created_at:
|
|
995
|
-
updated_at:
|
|
1105
|
+
required_profiles: z14.array(registrySlugSchema).min(1),
|
|
1106
|
+
revision: z14.number().int().positive(),
|
|
1107
|
+
npmReleases: z14.array(npmReleaseRecordSchema).optional().default([]),
|
|
1108
|
+
created_at: z14.iso.datetime(),
|
|
1109
|
+
updated_at: z14.iso.datetime()
|
|
996
1110
|
}).strict();
|
|
997
|
-
var workflowFamilyMutationSchema =
|
|
1111
|
+
var workflowFamilyMutationSchema = z14.object({
|
|
998
1112
|
slug: registrySlugSchema,
|
|
999
|
-
displayName:
|
|
1000
|
-
description:
|
|
1001
|
-
tags:
|
|
1002
|
-
required_profiles:
|
|
1113
|
+
displayName: z14.string().min(1).max(120),
|
|
1114
|
+
description: z14.string().min(1).max(1e3),
|
|
1115
|
+
tags: z14.array(registrySlugSchema).default([]),
|
|
1116
|
+
required_profiles: z14.array(registrySlugSchema).min(1)
|
|
1003
1117
|
}).strict();
|
|
1004
|
-
var publishWorkflowFamilyRequestSchema =
|
|
1118
|
+
var publishWorkflowFamilyRequestSchema = z14.object({
|
|
1005
1119
|
version: registrySemverSchema,
|
|
1006
|
-
releaseNote:
|
|
1120
|
+
releaseNote: z14.string().optional()
|
|
1007
1121
|
}).strict();
|
|
1008
|
-
var registryProjectWorkflowBindingSchema =
|
|
1009
|
-
project_id:
|
|
1122
|
+
var registryProjectWorkflowBindingSchema = z14.object({
|
|
1123
|
+
project_id: z14.string().regex(/^prj_/),
|
|
1010
1124
|
family_slug: registrySlugSchema,
|
|
1011
1125
|
profile: registrySlugSchema,
|
|
1012
1126
|
version: registrySemverSchema.nullable().optional(),
|
|
1013
|
-
revision:
|
|
1014
|
-
updated_at:
|
|
1127
|
+
revision: z14.number().int().positive(),
|
|
1128
|
+
updated_at: z14.iso.datetime()
|
|
1015
1129
|
}).strict();
|
|
1016
|
-
var bindProjectWorkflowFamilyRequestSchema =
|
|
1017
|
-
schema_version:
|
|
1130
|
+
var bindProjectWorkflowFamilyRequestSchema = z14.object({
|
|
1131
|
+
schema_version: z14.literal(1),
|
|
1018
1132
|
family_slug: registrySlugSchema,
|
|
1019
1133
|
profile: registrySlugSchema,
|
|
1020
1134
|
version: registrySemverSchema.nullable().optional(),
|
|
1021
|
-
revision:
|
|
1135
|
+
revision: z14.number().int().nullable()
|
|
1022
1136
|
}).strict();
|
|
1023
1137
|
|
|
1024
1138
|
// ../core/dist/fs/hash.js
|
|
@@ -1030,6 +1144,10 @@ function sha256Bytes(content) {
|
|
|
1030
1144
|
async function sha256File(path) {
|
|
1031
1145
|
return sha256Bytes(await readFile(path));
|
|
1032
1146
|
}
|
|
1147
|
+
function aggregateInstalledContentHash(files) {
|
|
1148
|
+
const lines = [...files].sort((a, b) => a.relpath.localeCompare(b.relpath)).map((entry) => `${entry.relpath}:${entry.sha256}`);
|
|
1149
|
+
return createHash("sha256").update(lines.join("\n"), "utf8").digest("hex");
|
|
1150
|
+
}
|
|
1033
1151
|
|
|
1034
1152
|
// ../core/dist/fs/path-safety.js
|
|
1035
1153
|
import { lstat } from "node:fs/promises";
|
|
@@ -1115,7 +1233,7 @@ async function assertNoSymlinks(root, relativePath) {
|
|
|
1115
1233
|
async function withRetry(action, options) {
|
|
1116
1234
|
const attempts = options.attempts ?? 3;
|
|
1117
1235
|
const sleep = options.sleep ?? (async (milliseconds) => {
|
|
1118
|
-
await new Promise((
|
|
1236
|
+
await new Promise((resolve9) => setTimeout(resolve9, milliseconds));
|
|
1119
1237
|
});
|
|
1120
1238
|
let lastError;
|
|
1121
1239
|
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
@@ -1613,8 +1731,8 @@ function decideUpdate(policy, dirty) {
|
|
|
1613
1731
|
|
|
1614
1732
|
// ../core/dist/project/initialize.js
|
|
1615
1733
|
import { createHash as createHash4 } from "node:crypto";
|
|
1616
|
-
import { readFile as readFile4, stat as stat2 } from "node:fs/promises";
|
|
1617
|
-
import { basename, join as join5, resolve as
|
|
1734
|
+
import { readFile as readFile4, stat as stat2, writeFile as writeFile3 } from "node:fs/promises";
|
|
1735
|
+
import { basename, join as join5, resolve as resolve4 } from "node:path";
|
|
1618
1736
|
import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
|
|
1619
1737
|
|
|
1620
1738
|
// ../core/dist/transaction/transaction.js
|
|
@@ -2132,7 +2250,7 @@ function managedTargetsFor(adapter, bundle, context) {
|
|
|
2132
2250
|
// ../core/dist/project/profile-bundle.js
|
|
2133
2251
|
import { createHash as createHash3 } from "node:crypto";
|
|
2134
2252
|
import { readFile as readFile3, readdir as readdir2 } from "node:fs/promises";
|
|
2135
|
-
import { join as join4 } from "node:path";
|
|
2253
|
+
import { join as join4, resolve as resolve3 } from "node:path";
|
|
2136
2254
|
var AdapterBundleError = class extends Error {
|
|
2137
2255
|
code;
|
|
2138
2256
|
exitCode = 7;
|
|
@@ -2156,7 +2274,13 @@ function validateRelativeBundlePath2(path) {
|
|
|
2156
2274
|
throw new Error("invalid Harness Bundle path");
|
|
2157
2275
|
}
|
|
2158
2276
|
}
|
|
2277
|
+
var bundleCache = /* @__PURE__ */ new Map();
|
|
2159
2278
|
async function loadAgentBundle(resourcesRoot, profile, agent) {
|
|
2279
|
+
const cacheKey = `${resolve3(resourcesRoot)}:${profile}:${agent}`;
|
|
2280
|
+
const cached = bundleCache.get(cacheKey);
|
|
2281
|
+
if (cached) {
|
|
2282
|
+
return { manifest: cached.manifest, files: new Map(cached.files) };
|
|
2283
|
+
}
|
|
2160
2284
|
const manifestPath = join4(resourcesRoot, "harness", "manifests", profile, `${agent}.json`);
|
|
2161
2285
|
let manifestText;
|
|
2162
2286
|
try {
|
|
@@ -2200,7 +2324,9 @@ async function loadAgentBundle(resourcesRoot, profile, agent) {
|
|
|
2200
2324
|
}
|
|
2201
2325
|
files.set(item2.path, bytes);
|
|
2202
2326
|
}
|
|
2203
|
-
|
|
2327
|
+
const result = { manifest: raw, files };
|
|
2328
|
+
bundleCache.set(cacheKey, result);
|
|
2329
|
+
return { manifest: result.manifest, files: new Map(result.files) };
|
|
2204
2330
|
}
|
|
2205
2331
|
async function managedBundleTargets(resourcesRoot, profile, agent = "claude-code") {
|
|
2206
2332
|
const bundle = await loadAgentBundle(resourcesRoot, profile, agent);
|
|
@@ -2311,6 +2437,27 @@ function uuidV7(now = Date.now()) {
|
|
|
2311
2437
|
}
|
|
2312
2438
|
|
|
2313
2439
|
// ../core/dist/project/initialize.js
|
|
2440
|
+
var CONTEXT_INDEX_PATH = ".harness/context-index.json";
|
|
2441
|
+
async function fileHex(path) {
|
|
2442
|
+
try {
|
|
2443
|
+
const data = await readFile4(path);
|
|
2444
|
+
return createHash4("sha256").update(data).digest("hex");
|
|
2445
|
+
} catch {
|
|
2446
|
+
return null;
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
async function readExistingSkillBundles(root) {
|
|
2450
|
+
const text = await readOptional(join5(root, CONTEXT_INDEX_PATH));
|
|
2451
|
+
if (text === "")
|
|
2452
|
+
return /* @__PURE__ */ new Map();
|
|
2453
|
+
try {
|
|
2454
|
+
const parsed = JSON.parse(text);
|
|
2455
|
+
const bundles = parsed.skill_bundles ?? {};
|
|
2456
|
+
return new Map(Object.entries(bundles));
|
|
2457
|
+
} catch {
|
|
2458
|
+
return /* @__PURE__ */ new Map();
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2314
2461
|
var TargetCollisionError = class extends Error {
|
|
2315
2462
|
code = "TARGET_COLLISION";
|
|
2316
2463
|
exitCode = 7;
|
|
@@ -2394,7 +2541,7 @@ function mergeOwnedTargets(owned) {
|
|
|
2394
2541
|
});
|
|
2395
2542
|
}
|
|
2396
2543
|
async function initializeProject(options) {
|
|
2397
|
-
const root =
|
|
2544
|
+
const root = resolve4(options.projectRoot);
|
|
2398
2545
|
const config = initConfigSchema.parse(options.config);
|
|
2399
2546
|
const existing = await existingProjectConfig(root);
|
|
2400
2547
|
const profile = config.profile;
|
|
@@ -2405,6 +2552,7 @@ async function initializeProject(options) {
|
|
|
2405
2552
|
const manifests = [];
|
|
2406
2553
|
const adaptersIndex = {};
|
|
2407
2554
|
const skillBundles = {};
|
|
2555
|
+
const existingContextBundles = await readExistingSkillBundles(root);
|
|
2408
2556
|
let primaryBundleHash = "";
|
|
2409
2557
|
let primaryRegistryVersion = "";
|
|
2410
2558
|
for (const agent of enabledAgents) {
|
|
@@ -2424,7 +2572,8 @@ async function initializeProject(options) {
|
|
|
2424
2572
|
});
|
|
2425
2573
|
skillBundles[agent] = {
|
|
2426
2574
|
registry_version: bundle.manifest.bundle_version,
|
|
2427
|
-
bundle_hash: bundleHash
|
|
2575
|
+
bundle_hash: bundleHash,
|
|
2576
|
+
...existingContextBundles.get(agent) ?? {}
|
|
2428
2577
|
};
|
|
2429
2578
|
adaptersIndex[agent] = adapter.contextIndex(adapterContext);
|
|
2430
2579
|
for (const target of targets) {
|
|
@@ -2541,6 +2690,7 @@ async function initializeProject(options) {
|
|
|
2541
2690
|
if (!options.dryRun) {
|
|
2542
2691
|
const writeOperations = await Promise.all([...files.entries()].map(async ([path, content]) => operationFor(root, path, content)));
|
|
2543
2692
|
await runTransaction(root, writeOperations, { kind: "init" });
|
|
2693
|
+
await enrichContextIndexWithVerification(root, enabledAgents, profile, options.resourcesRoot, adapterContext);
|
|
2544
2694
|
}
|
|
2545
2695
|
return {
|
|
2546
2696
|
projectConfig,
|
|
@@ -2549,15 +2699,61 @@ async function initializeProject(options) {
|
|
|
2549
2699
|
registryVersion: primaryRegistryVersion
|
|
2550
2700
|
};
|
|
2551
2701
|
}
|
|
2702
|
+
async function enrichContextIndexWithVerification(root, agents, profile, resourcesRoot, adapterContext) {
|
|
2703
|
+
const contextPath = join5(root, CONTEXT_INDEX_PATH);
|
|
2704
|
+
const existing = await readOptional(join5(contextPath));
|
|
2705
|
+
if (existing === "")
|
|
2706
|
+
return;
|
|
2707
|
+
let parsed;
|
|
2708
|
+
try {
|
|
2709
|
+
parsed = JSON.parse(existing);
|
|
2710
|
+
} catch {
|
|
2711
|
+
return;
|
|
2712
|
+
}
|
|
2713
|
+
const bundles = parsed.skill_bundles ?? {};
|
|
2714
|
+
for (const agent of agents) {
|
|
2715
|
+
const entry = bundles[agent];
|
|
2716
|
+
if (!entry)
|
|
2717
|
+
continue;
|
|
2718
|
+
let loadedBundle;
|
|
2719
|
+
try {
|
|
2720
|
+
loadedBundle = await loadAgentBundle(resourcesRoot, profile, agent);
|
|
2721
|
+
} catch {
|
|
2722
|
+
continue;
|
|
2723
|
+
}
|
|
2724
|
+
const targets = managedTargetsFor(getAdapter(agent), loadedBundle, adapterContext);
|
|
2725
|
+
const mismatches = [];
|
|
2726
|
+
const entries = [];
|
|
2727
|
+
for (const target of targets) {
|
|
2728
|
+
const rel = target.target_path.replace(/\\/g, "/");
|
|
2729
|
+
const actual = await fileHex(join5(root, target.target_path));
|
|
2730
|
+
entries.push({ relpath: rel, sha256: actual ?? "" });
|
|
2731
|
+
if (actual === null) {
|
|
2732
|
+
mismatches.push({ relpath: rel, expected: target.sha256, actual: "<missing>" });
|
|
2733
|
+
} else if (actual !== target.sha256) {
|
|
2734
|
+
mismatches.push({ relpath: rel, expected: target.sha256, actual });
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
entry.installedContentHash = aggregateInstalledContentHash(entries);
|
|
2738
|
+
const newStatus = mismatches.length === 0 ? "verified" : "degraded";
|
|
2739
|
+
if (newStatus === "verified" && typeof entry.verifiedAt === "string" && entry.verifiedAt !== "") {
|
|
2740
|
+
} else {
|
|
2741
|
+
entry.verifiedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2742
|
+
}
|
|
2743
|
+
entry.verificationStatus = newStatus;
|
|
2744
|
+
entry.mismatchDetails = mismatches;
|
|
2745
|
+
}
|
|
2746
|
+
await writeFile3(contextPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
|
|
2747
|
+
}
|
|
2552
2748
|
|
|
2553
2749
|
// ../core/dist/project/refresh.js
|
|
2554
2750
|
import { createHash as createHash5 } from "node:crypto";
|
|
2555
2751
|
import { readFile as readFile5, readdir as readdir3, rmdir } from "node:fs/promises";
|
|
2556
|
-
import { dirname as dirname3, join as join6, resolve as
|
|
2752
|
+
import { dirname as dirname3, join as join6, resolve as resolve5 } from "node:path";
|
|
2557
2753
|
import { parse as parseYaml3, stringify as stringifyYaml2 } from "yaml";
|
|
2558
2754
|
var INSTALLED_STATE_PATH = ".harness/state/local/installed-harness-bundle.json";
|
|
2559
|
-
var
|
|
2560
|
-
async function
|
|
2755
|
+
var CONTEXT_INDEX_PATH2 = ".harness/context-index.json";
|
|
2756
|
+
async function fileHex2(path) {
|
|
2561
2757
|
try {
|
|
2562
2758
|
return createHash5("sha256").update(await readFile5(path)).digest("hex");
|
|
2563
2759
|
} catch (error) {
|
|
@@ -2648,7 +2844,7 @@ async function readInstalledState(root) {
|
|
|
2648
2844
|
};
|
|
2649
2845
|
}
|
|
2650
2846
|
async function readInstalledAgentConfiguration(projectRoot) {
|
|
2651
|
-
const installed = await readInstalledState(
|
|
2847
|
+
const installed = await readInstalledState(resolve5(projectRoot));
|
|
2652
2848
|
return {
|
|
2653
2849
|
agents: installed.adapters,
|
|
2654
2850
|
profiles: Object.fromEntries(installed.adapters.map((agent) => [
|
|
@@ -2658,7 +2854,7 @@ async function readInstalledAgentConfiguration(projectRoot) {
|
|
|
2658
2854
|
};
|
|
2659
2855
|
}
|
|
2660
2856
|
async function readContextIndexBundleHash(root) {
|
|
2661
|
-
const content = await readOptionalText(join6(root,
|
|
2857
|
+
const content = await readOptionalText(join6(root, CONTEXT_INDEX_PATH2));
|
|
2662
2858
|
if (content === "")
|
|
2663
2859
|
return null;
|
|
2664
2860
|
try {
|
|
@@ -2713,8 +2909,8 @@ function conflict(target, reason, oldSha, incomingSha) {
|
|
|
2713
2909
|
function sortByTarget(items) {
|
|
2714
2910
|
return [...items].sort((left, right) => left.target_path.localeCompare(right.target_path));
|
|
2715
2911
|
}
|
|
2716
|
-
async function reconcileContextIndex(root, agents, profiles, manifests, codebuddySurface2) {
|
|
2717
|
-
const existing = await readOptionalText(join6(root,
|
|
2912
|
+
async function reconcileContextIndex(root, agents, profiles, manifests, codebuddySurface2, verifications) {
|
|
2913
|
+
const existing = await readOptionalText(join6(root, CONTEXT_INDEX_PATH2));
|
|
2718
2914
|
let codebase = {
|
|
2719
2915
|
map: ".harness/codebase/map",
|
|
2720
2916
|
status: "missing"
|
|
@@ -2741,17 +2937,27 @@ async function reconcileContextIndex(root, agents, profiles, manifests, codebudd
|
|
|
2741
2937
|
},
|
|
2742
2938
|
knowledge: { index: ".harness/knowledge/index.json" },
|
|
2743
2939
|
codebase,
|
|
2744
|
-
skill_bundles: Object.fromEntries(manifests.map((manifest) =>
|
|
2745
|
-
manifest.adapter
|
|
2746
|
-
|
|
2747
|
-
|
|
2940
|
+
skill_bundles: Object.fromEntries(manifests.map((manifest) => {
|
|
2941
|
+
const ver = verifications.get(manifest.adapter);
|
|
2942
|
+
return [
|
|
2943
|
+
manifest.adapter,
|
|
2944
|
+
{
|
|
2945
|
+
registry_version: manifest.bundle_version,
|
|
2946
|
+
bundle_hash: manifest.bundle_manifest_hash,
|
|
2947
|
+
installedContentHash: ver?.installedContentHash ?? null,
|
|
2948
|
+
verifiedAt: ver?.verifiedAt ?? null,
|
|
2949
|
+
verificationStatus: ver?.verificationStatus ?? "unknown",
|
|
2950
|
+
mismatchDetails: ver?.mismatchDetails ?? []
|
|
2951
|
+
}
|
|
2952
|
+
];
|
|
2953
|
+
}))
|
|
2748
2954
|
};
|
|
2749
2955
|
const next = JSON.stringify(record, null, 2) + "\n";
|
|
2750
2956
|
if (existing === next)
|
|
2751
2957
|
return null;
|
|
2752
2958
|
return {
|
|
2753
2959
|
operation: existing === "" ? "add" : "modify",
|
|
2754
|
-
path:
|
|
2960
|
+
path: CONTEXT_INDEX_PATH2,
|
|
2755
2961
|
content: next
|
|
2756
2962
|
};
|
|
2757
2963
|
}
|
|
@@ -2831,7 +3037,7 @@ function stateWithoutInstalledAt(value) {
|
|
|
2831
3037
|
return copy;
|
|
2832
3038
|
}
|
|
2833
3039
|
async function refreshProject(options) {
|
|
2834
|
-
const root =
|
|
3040
|
+
const root = resolve5(options.projectRoot);
|
|
2835
3041
|
const installed = await readInstalledState(root);
|
|
2836
3042
|
const oldAgents = installed.adapters.length > 0 ? installed.adapters : ["claude-code"];
|
|
2837
3043
|
const selectedAgents = sortHarnessAgents(options.agents);
|
|
@@ -2915,7 +3121,7 @@ async function refreshProject(options) {
|
|
|
2915
3121
|
const newStateFiles = installed.files.filter((entry) => entry.owner === "shared" || !selectedSet.has(entry.owner));
|
|
2916
3122
|
for (const target of newManaged) {
|
|
2917
3123
|
const incoming = target.sha256;
|
|
2918
|
-
const current = await
|
|
3124
|
+
const current = await fileHex2(join6(root, target.target_path));
|
|
2919
3125
|
if (current === null) {
|
|
2920
3126
|
applied.push(item(target, "add", "MISSING_TARGET", null, incoming));
|
|
2921
3127
|
ops.push({ operation: "add", path: target.target_path, content: target.bytes });
|
|
@@ -2943,7 +3149,7 @@ async function refreshProject(options) {
|
|
|
2943
3149
|
}
|
|
2944
3150
|
}
|
|
2945
3151
|
for (const target of oldOnly) {
|
|
2946
|
-
const current = await
|
|
3152
|
+
const current = await fileHex2(join6(root, target.target_path));
|
|
2947
3153
|
if (current === null) {
|
|
2948
3154
|
continue;
|
|
2949
3155
|
}
|
|
@@ -2969,7 +3175,48 @@ async function refreshProject(options) {
|
|
|
2969
3175
|
const projectOperation = await projectTransitionOperation(root, agents, profiles, codebuddySurface2);
|
|
2970
3176
|
if (projectOperation !== null)
|
|
2971
3177
|
ops.push(projectOperation);
|
|
2972
|
-
const
|
|
3178
|
+
const verifications = /* @__PURE__ */ new Map();
|
|
3179
|
+
for (const agent of selectedAgents) {
|
|
3180
|
+
const agentProfile = profiles.get(agent) ?? profile;
|
|
3181
|
+
let bundleForVerify;
|
|
3182
|
+
try {
|
|
3183
|
+
bundleForVerify = await loadAgentBundle(options.resourcesRoot, agentProfile, agent);
|
|
3184
|
+
} catch {
|
|
3185
|
+
continue;
|
|
3186
|
+
}
|
|
3187
|
+
const verifyTargets = managedTargetsFor(getAdapter(agent), bundleForVerify, {
|
|
3188
|
+
profile: agentProfile,
|
|
3189
|
+
codebuddySurface: codebuddySurface2
|
|
3190
|
+
});
|
|
3191
|
+
const verifyMismatches = [];
|
|
3192
|
+
const verifyEntries = [];
|
|
3193
|
+
for (const target of verifyTargets) {
|
|
3194
|
+
const rel = target.target_path.replace(/\\/g, "/");
|
|
3195
|
+
const actual = await fileHex2(join6(root, target.target_path));
|
|
3196
|
+
verifyEntries.push({ relpath: rel, sha256: actual ?? "" });
|
|
3197
|
+
if (actual === null) {
|
|
3198
|
+
verifyMismatches.push({ relpath: rel, expected: target.sha256, actual: "<missing>" });
|
|
3199
|
+
} else if (actual !== target.sha256) {
|
|
3200
|
+
verifyMismatches.push({ relpath: rel, expected: target.sha256, actual });
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
verifications.set(agent, {
|
|
3204
|
+
adapter: agent,
|
|
3205
|
+
bundleVersion: bundleForVerify.manifest.bundle_version,
|
|
3206
|
+
installedBundleVersion: null,
|
|
3207
|
+
manifestHash: null,
|
|
3208
|
+
installedManifestHash: null,
|
|
3209
|
+
coreHash: null,
|
|
3210
|
+
installedCoreHash: null,
|
|
3211
|
+
adapterHash: null,
|
|
3212
|
+
installedAdapterHash: null,
|
|
3213
|
+
installedContentHash: aggregateInstalledContentHash(verifyEntries),
|
|
3214
|
+
verifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3215
|
+
verificationStatus: verifyMismatches.length === 0 ? "verified" : "degraded",
|
|
3216
|
+
mismatchDetails: verifyMismatches
|
|
3217
|
+
});
|
|
3218
|
+
}
|
|
3219
|
+
const contextOperation = await reconcileContextIndex(root, agents, profiles, manifests, codebuddySurface2, verifications);
|
|
2973
3220
|
if (contextOperation !== null)
|
|
2974
3221
|
ops.push(contextOperation);
|
|
2975
3222
|
const managedBlocks = [
|
|
@@ -3040,6 +3287,14 @@ async function refreshProject(options) {
|
|
|
3040
3287
|
function freshnessEntry(agent, profile, status, identity) {
|
|
3041
3288
|
return { agent, profile, status, identity, driftedFiles: [], missingFiles: [] };
|
|
3042
3289
|
}
|
|
3290
|
+
function emptyVerification() {
|
|
3291
|
+
return {
|
|
3292
|
+
installedContentHash: null,
|
|
3293
|
+
verifiedAt: null,
|
|
3294
|
+
verificationStatus: "unknown",
|
|
3295
|
+
mismatchDetails: []
|
|
3296
|
+
};
|
|
3297
|
+
}
|
|
3043
3298
|
var BUILD_MARKER_BUNDLE_PATH = ".harness-build.json";
|
|
3044
3299
|
function buildMarkerCoreHash(text) {
|
|
3045
3300
|
if (text === null || text === "")
|
|
@@ -3052,7 +3307,7 @@ function buildMarkerCoreHash(text) {
|
|
|
3052
3307
|
}
|
|
3053
3308
|
}
|
|
3054
3309
|
async function collectFreshness(options) {
|
|
3055
|
-
const root =
|
|
3310
|
+
const root = resolve5(options.projectRoot);
|
|
3056
3311
|
const installed = await readInstalledState(root);
|
|
3057
3312
|
const codebuddySurface2 = options.codebuddySurface ?? "both";
|
|
3058
3313
|
const agents = [];
|
|
@@ -3069,7 +3324,8 @@ async function collectFreshness(options) {
|
|
|
3069
3324
|
coreHash: null,
|
|
3070
3325
|
installedCoreHash: null,
|
|
3071
3326
|
adapterHash: null,
|
|
3072
|
-
installedAdapterHash: null
|
|
3327
|
+
installedAdapterHash: null,
|
|
3328
|
+
...emptyVerification()
|
|
3073
3329
|
};
|
|
3074
3330
|
let officialHash = null;
|
|
3075
3331
|
let officialVersion = null;
|
|
@@ -3096,13 +3352,29 @@ async function collectFreshness(options) {
|
|
|
3096
3352
|
identity.adapterHash = sha256Bytes(canonicalJson(targets.map((target) => ({ path: target.target_path.replace(/\\/g, "/"), sha256: target.sha256 })).sort((a, b) => a.path.localeCompare(b.path))));
|
|
3097
3353
|
const installedProjection = await Promise.all(targets.map(async (target) => ({
|
|
3098
3354
|
path: target.target_path.replace(/\\/g, "/"),
|
|
3099
|
-
sha256: await
|
|
3355
|
+
sha256: await fileHex2(join6(root, target.target_path))
|
|
3100
3356
|
})));
|
|
3101
3357
|
identity.installedAdapterHash = sha256Bytes(canonicalJson(installedProjection.sort((a, b) => a.path.localeCompare(b.path))));
|
|
3102
3358
|
const markerTarget = targets.find((target) => target.target_path.replace(/\\/g, "/").endsWith(`/${BUILD_MARKER_BUNDLE_PATH}`) || target.target_path === BUILD_MARKER_BUNDLE_PATH);
|
|
3103
3359
|
if (markerTarget !== void 0) {
|
|
3104
3360
|
identity.installedCoreHash = buildMarkerCoreHash(await readOptionalText(join6(root, markerTarget.target_path)));
|
|
3105
3361
|
}
|
|
3362
|
+
const mismatchDetails = [];
|
|
3363
|
+
const contentEntries = [];
|
|
3364
|
+
for (const target of targets) {
|
|
3365
|
+
const rel = target.target_path.replace(/\\/g, "/");
|
|
3366
|
+
const actual = await fileHex2(join6(root, target.target_path));
|
|
3367
|
+
contentEntries.push({ relpath: rel, sha256: actual ?? "" });
|
|
3368
|
+
if (actual === null) {
|
|
3369
|
+
mismatchDetails.push({ relpath: rel, expected: target.sha256, actual: "<missing>" });
|
|
3370
|
+
} else if (actual !== target.sha256) {
|
|
3371
|
+
mismatchDetails.push({ relpath: rel, expected: target.sha256, actual });
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
identity.installedContentHash = aggregateInstalledContentHash(contentEntries);
|
|
3375
|
+
identity.verifiedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3376
|
+
identity.verificationStatus = mismatchDetails.length === 0 ? "verified" : "degraded";
|
|
3377
|
+
identity.mismatchDetails = mismatchDetails;
|
|
3106
3378
|
if (requestedProfile !== installedProfile) {
|
|
3107
3379
|
agents.push(freshnessEntry(agent, installedProfile, "PROFILE_MISMATCH", identity));
|
|
3108
3380
|
continue;
|
|
@@ -3114,7 +3386,7 @@ async function collectFreshness(options) {
|
|
|
3114
3386
|
const drifted = [];
|
|
3115
3387
|
const missing = [];
|
|
3116
3388
|
for (const target of targets) {
|
|
3117
|
-
const current = await
|
|
3389
|
+
const current = await fileHex2(join6(root, target.target_path));
|
|
3118
3390
|
if (current === null) {
|
|
3119
3391
|
missing.push(target.target_path);
|
|
3120
3392
|
} else if (current !== target.sha256) {
|
|
@@ -3304,7 +3576,7 @@ function highEntropyCandidates(content) {
|
|
|
3304
3576
|
}
|
|
3305
3577
|
|
|
3306
3578
|
// ../core/dist/security/scanner.js
|
|
3307
|
-
var SENSITIVE_SCANNER_VERSION = "1.
|
|
3579
|
+
var SENSITIVE_SCANNER_VERSION = "1.1.0";
|
|
3308
3580
|
var RULES = [
|
|
3309
3581
|
{
|
|
3310
3582
|
id: "HH_PRIVATE_KEY",
|
|
@@ -3359,6 +3631,9 @@ function rawFindings(content) {
|
|
|
3359
3631
|
for (const rule of RULES) {
|
|
3360
3632
|
for (const match of content.matchAll(rule.pattern)) {
|
|
3361
3633
|
const value = match[rule.valueGroup ?? 0] ?? match[0];
|
|
3634
|
+
if (rule.id === "HH_PASSWORD_VALUE" && isObviousPlaceholder(value)) {
|
|
3635
|
+
continue;
|
|
3636
|
+
}
|
|
3362
3637
|
const relative3 = match[0].indexOf(value);
|
|
3363
3638
|
findings.push({
|
|
3364
3639
|
ruleId: rule.id,
|
|
@@ -3369,6 +3644,8 @@ function rawFindings(content) {
|
|
|
3369
3644
|
}
|
|
3370
3645
|
}
|
|
3371
3646
|
for (const candidate of highEntropyCandidates(content)) {
|
|
3647
|
+
if (!hasSensitiveLexicalContext(content, candidate.offset))
|
|
3648
|
+
continue;
|
|
3372
3649
|
findings.push({
|
|
3373
3650
|
ruleId: "HH_HIGH_ENTROPY",
|
|
3374
3651
|
severity: "high",
|
|
@@ -3378,6 +3655,17 @@ function rawFindings(content) {
|
|
|
3378
3655
|
}
|
|
3379
3656
|
return findings;
|
|
3380
3657
|
}
|
|
3658
|
+
function isObviousPlaceholder(value) {
|
|
3659
|
+
const trimmed = value.trim();
|
|
3660
|
+
return /^\$\([^)]+\)$/.test(trimmed) || /^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/.test(trimmed) || /^\{\{[^{}]+\}\}$/.test(trimmed) || /^<[^<>]+>$/.test(trimmed) || /^(?:example|placeholder|your[-_][a-z0-9_-]+)$/i.test(trimmed);
|
|
3661
|
+
}
|
|
3662
|
+
function hasSensitiveLexicalContext(content, offset) {
|
|
3663
|
+
const lineStart = content.lastIndexOf("\n", Math.max(0, offset - 1)) + 1;
|
|
3664
|
+
const nextLineBreak = content.indexOf("\n", offset);
|
|
3665
|
+
const lineEnd = nextLineBreak === -1 ? content.length : nextLineBreak;
|
|
3666
|
+
const line = content.slice(lineStart, lineEnd);
|
|
3667
|
+
return /(?:secret|token|password|passwd|pwd|api[_-]?key|credential|authorization|bearer)\s*[:=]\s*["']?[A-Za-z0-9._~+/-]{8,}/i.test(line);
|
|
3668
|
+
}
|
|
3381
3669
|
function scanSensitiveFiles(files, options = {}) {
|
|
3382
3670
|
const findings = [];
|
|
3383
3671
|
const evidence = [];
|
|
@@ -3436,6 +3724,8 @@ function scanSensitiveFiles(files, options = {}) {
|
|
|
3436
3724
|
return {
|
|
3437
3725
|
scanner_version: SENSITIVE_SCANNER_VERSION,
|
|
3438
3726
|
blocked: findings.some((finding) => finding.disposition === "blocked"),
|
|
3727
|
+
hard_blocked: findings.some((finding) => finding.disposition === "blocked" && finding.severity === "high"),
|
|
3728
|
+
review_required: findings.some((finding) => finding.disposition === "blocked" && finding.severity !== "high"),
|
|
3439
3729
|
findings,
|
|
3440
3730
|
override_evidence: Object.freeze(evidence)
|
|
3441
3731
|
};
|
|
@@ -3460,7 +3750,7 @@ function generateProposalPreview(input, scanOptions = {}) {
|
|
|
3460
3750
|
}
|
|
3461
3751
|
|
|
3462
3752
|
// ../core/dist/push/credentials.js
|
|
3463
|
-
import { readFile as readFile6, writeFile as
|
|
3753
|
+
import { readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
3464
3754
|
import { join as join7 } from "node:path";
|
|
3465
3755
|
import { parse as parseYaml4, stringify as stringifyYaml3 } from "yaml";
|
|
3466
3756
|
var CREDENTIALS_LOCAL_RELATIVE = ".harness/credentials.local.yaml";
|
|
@@ -3542,7 +3832,7 @@ async function readLocalCredentials(projectRoot) {
|
|
|
3542
3832
|
}
|
|
3543
3833
|
async function writeLocalCredentials(projectRoot, credentials) {
|
|
3544
3834
|
const normalized = validateLocalCredentials(credentials);
|
|
3545
|
-
await
|
|
3835
|
+
await writeFile4(join7(projectRoot, CREDENTIALS_LOCAL_RELATIVE), stringifyYaml3(normalized, { sortMapEntries: true }) + "\n", "utf8");
|
|
3546
3836
|
}
|
|
3547
3837
|
async function ensureCredentialsGitignore(projectRoot) {
|
|
3548
3838
|
const gitignorePath = join7(projectRoot, ".gitignore");
|
|
@@ -3571,7 +3861,7 @@ async function ensureCredentialsGitignore(projectRoot) {
|
|
|
3571
3861
|
return;
|
|
3572
3862
|
}
|
|
3573
3863
|
const suffix = content.length === 0 || content.endsWith("\n") ? "" : "\n";
|
|
3574
|
-
await
|
|
3864
|
+
await writeFile4(gitignorePath, content + suffix + missing.join("\n") + "\n", "utf8");
|
|
3575
3865
|
}
|
|
3576
3866
|
function resolvePushAuth(input) {
|
|
3577
3867
|
const tokenEnv = input.tokenEnv ?? input.projectTokenEnv;
|
|
@@ -3600,7 +3890,7 @@ function resolvePushAuth(input) {
|
|
|
3600
3890
|
|
|
3601
3891
|
// ../core/dist/push/push.js
|
|
3602
3892
|
import { lstat as lstat3, readFile as readFile10, readdir as readdir4, rm as rm5 } from "node:fs/promises";
|
|
3603
|
-
import { join as join11, relative, resolve as
|
|
3893
|
+
import { join as join11, relative, resolve as resolve7 } from "node:path";
|
|
3604
3894
|
import { parse as parseYaml5, stringify as stringifyYaml4 } from "yaml";
|
|
3605
3895
|
|
|
3606
3896
|
// ../core/dist/state/baseline.js
|
|
@@ -3613,7 +3903,7 @@ async function readBaseline(projectRoot) {
|
|
|
3613
3903
|
|
|
3614
3904
|
// ../core/dist/state/locks.js
|
|
3615
3905
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
3616
|
-
import { readFile as readFile8, rename as rename3, rm as rm3, writeFile as
|
|
3906
|
+
import { readFile as readFile8, rename as rename3, rm as rm3, writeFile as writeFile5 } from "node:fs/promises";
|
|
3617
3907
|
import { join as join9 } from "node:path";
|
|
3618
3908
|
async function readLock(path) {
|
|
3619
3909
|
return JSON.parse(await readFile8(path, "utf8"));
|
|
@@ -3633,7 +3923,7 @@ async function acquireProtocolLock(projectRoot, operation, options = {}) {
|
|
|
3633
3923
|
};
|
|
3634
3924
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
3635
3925
|
try {
|
|
3636
|
-
await
|
|
3926
|
+
await writeFile5(lockPath, JSON.stringify(record, null, 2), { flag: "wx" });
|
|
3637
3927
|
return {
|
|
3638
3928
|
path: lockPath,
|
|
3639
3929
|
operation,
|
|
@@ -3663,7 +3953,7 @@ async function acquireProtocolLock(projectRoot, operation, options = {}) {
|
|
|
3663
3953
|
|
|
3664
3954
|
// ../core/dist/sync/synchronize.js
|
|
3665
3955
|
import { lstat as lstat2, readFile as readFile9, rm as rm4 } from "node:fs/promises";
|
|
3666
|
-
import { join as join10, resolve as
|
|
3956
|
+
import { join as join10, resolve as resolve6 } from "node:path";
|
|
3667
3957
|
|
|
3668
3958
|
// ../core/dist/update/conflicts.js
|
|
3669
3959
|
function operationTargetPath(operation) {
|
|
@@ -4077,7 +4367,7 @@ async function saveConflictReport(root, requestId, manifest, plan) {
|
|
|
4077
4367
|
});
|
|
4078
4368
|
}
|
|
4079
4369
|
async function synchronizeArtifacts(options, initialBaseline) {
|
|
4080
|
-
const root =
|
|
4370
|
+
const root = resolve6(options.projectRoot);
|
|
4081
4371
|
const projectId = options.project.project.project_id;
|
|
4082
4372
|
if (projectId === null) {
|
|
4083
4373
|
throw new Error("PROJECT_NOT_BOUND");
|
|
@@ -4254,7 +4544,7 @@ function baselineEntryFromOperation(manifest, operation, localContent) {
|
|
|
4254
4544
|
};
|
|
4255
4545
|
}
|
|
4256
4546
|
async function advanceBaselineFromArtifact(options, baseline) {
|
|
4257
|
-
const root =
|
|
4547
|
+
const root = resolve6(options.projectRoot);
|
|
4258
4548
|
if (options.manifest.project_version === null) {
|
|
4259
4549
|
throw new Error("artifact manifest missing project_version");
|
|
4260
4550
|
}
|
|
@@ -4416,7 +4706,7 @@ async function walkHarnessEntries(root, directory, output) {
|
|
|
4416
4706
|
}
|
|
4417
4707
|
}
|
|
4418
4708
|
async function managedFiles(projectRoot, project) {
|
|
4419
|
-
const root =
|
|
4709
|
+
const root = resolve7(projectRoot);
|
|
4420
4710
|
const paths = [];
|
|
4421
4711
|
const adapters = getAdapters(enabledHarnessAgents(project));
|
|
4422
4712
|
const managedFiles2 = [
|
|
@@ -4668,7 +4958,7 @@ async function bindProject(root, project, baseline, projectId) {
|
|
|
4668
4958
|
return { project: nextProject, baseline: nextBaseline };
|
|
4669
4959
|
}
|
|
4670
4960
|
async function pushProject(options) {
|
|
4671
|
-
const root =
|
|
4961
|
+
const root = resolve7(options.projectRoot);
|
|
4672
4962
|
let project = await readProject(root);
|
|
4673
4963
|
let baseline = await readBaseline(root);
|
|
4674
4964
|
const profile = parseHarnessProfile(project.project.profiles[0]);
|
|
@@ -4998,46 +5288,52 @@ async function cleanupProject(options) {
|
|
|
4998
5288
|
|
|
4999
5289
|
// ../core/dist/skill/frontmatter.js
|
|
5000
5290
|
import { parse as parseYaml6 } from "yaml";
|
|
5001
|
-
import { z as
|
|
5291
|
+
import { z as z15 } from "zod";
|
|
5002
5292
|
|
|
5003
5293
|
// ../core/dist/skill/fixer.js
|
|
5004
5294
|
import { parse as parseYaml7, stringify as stringifyYaml5 } from "yaml";
|
|
5005
5295
|
|
|
5006
|
-
// ../core/dist/skill/
|
|
5007
|
-
var
|
|
5008
|
-
"claude-code": {
|
|
5296
|
+
// ../core/dist/skill/agent-surfaces.js
|
|
5297
|
+
var SURFACES = Object.freeze({
|
|
5298
|
+
"claude-code": Object.freeze({
|
|
5009
5299
|
agent: "claude-code",
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5300
|
+
skillRoots: Object.freeze({ project: ".claude/skills", user: ".claude/skills" }),
|
|
5301
|
+
subagentRoots: Object.freeze({ project: ".claude/agents", user: ".claude/agents" }),
|
|
5302
|
+
subagentFormat: "markdown",
|
|
5303
|
+
subagentExtension: ".md",
|
|
5304
|
+
nativeSkillAliases: Object.freeze([])
|
|
5305
|
+
}),
|
|
5306
|
+
codex: Object.freeze({
|
|
5015
5307
|
agent: "codex",
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5308
|
+
skillRoots: Object.freeze({ project: ".agents/skills", user: ".agents/skills" }),
|
|
5309
|
+
subagentRoots: Object.freeze({ project: ".codex/agents", user: ".codex/agents" }),
|
|
5310
|
+
subagentFormat: "toml",
|
|
5311
|
+
subagentExtension: ".toml",
|
|
5312
|
+
nativeSkillAliases: Object.freeze([])
|
|
5313
|
+
}),
|
|
5314
|
+
cursor: Object.freeze({
|
|
5022
5315
|
agent: "cursor",
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5316
|
+
skillRoots: Object.freeze({ project: ".cursor/skills", user: ".cursor/skills" }),
|
|
5317
|
+
subagentRoots: Object.freeze({ project: ".cursor/agents", user: ".cursor/agents" }),
|
|
5318
|
+
subagentFormat: "markdown",
|
|
5319
|
+
subagentExtension: ".md",
|
|
5320
|
+
nativeSkillAliases: Object.freeze([".agents/skills"])
|
|
5321
|
+
}),
|
|
5322
|
+
codebuddy: Object.freeze({
|
|
5323
|
+
agent: "codebuddy",
|
|
5324
|
+
skillRoots: Object.freeze({ project: ".codebuddy/skills", user: ".codebuddy/skills" }),
|
|
5325
|
+
subagentRoots: Object.freeze({ project: ".codebuddy/agents", user: ".codebuddy/agents" }),
|
|
5326
|
+
subagentFormat: "markdown",
|
|
5327
|
+
subagentExtension: ".md",
|
|
5328
|
+
nativeSkillAliases: Object.freeze([])
|
|
5329
|
+
})
|
|
5330
|
+
});
|
|
5331
|
+
var SKILL_TARGET_AGENTS = Object.freeze([
|
|
5332
|
+
"claude-code",
|
|
5333
|
+
"codex",
|
|
5334
|
+
"cursor",
|
|
5335
|
+
"codebuddy"
|
|
5336
|
+
]);
|
|
5041
5337
|
|
|
5042
5338
|
// ../core/dist/transaction/recovery.js
|
|
5043
5339
|
import { readFile as readFile12, readdir as readdir6, rm as rm7, stat as stat3 } from "node:fs/promises";
|
|
@@ -5161,7 +5457,7 @@ async function cleanupOldTransactions(projectRoot, now = /* @__PURE__ */ new Dat
|
|
|
5161
5457
|
|
|
5162
5458
|
// ../core/dist/update/update.js
|
|
5163
5459
|
import { readFile as readFile13 } from "node:fs/promises";
|
|
5164
|
-
import { join as join14, resolve as
|
|
5460
|
+
import { join as join14, resolve as resolve8 } from "node:path";
|
|
5165
5461
|
import { parse as parseYaml8 } from "yaml";
|
|
5166
5462
|
var UpdateWorkflowError = class extends Error {
|
|
5167
5463
|
exitCode;
|
|
@@ -5174,7 +5470,7 @@ var UpdateWorkflowError = class extends Error {
|
|
|
5174
5470
|
}
|
|
5175
5471
|
};
|
|
5176
5472
|
async function updateProject(options) {
|
|
5177
|
-
const root =
|
|
5473
|
+
const root = resolve8(options.projectRoot);
|
|
5178
5474
|
let project;
|
|
5179
5475
|
try {
|
|
5180
5476
|
project = projectConfigSchema.parse(parseYaml8(await readFile13(join14(root, ".harness", "project.yaml"), "utf8")));
|
|
@@ -5420,7 +5716,7 @@ function serializeCliResult(result) {
|
|
|
5420
5716
|
}
|
|
5421
5717
|
|
|
5422
5718
|
// src/config/codebuddy-setup.ts
|
|
5423
|
-
import { mkdir as mkdir4, readFile as readFile15, readdir as readdir7, stat as stat4, writeFile as
|
|
5719
|
+
import { mkdir as mkdir4, readFile as readFile15, readdir as readdir7, stat as stat4, writeFile as writeFile6 } from "node:fs/promises";
|
|
5424
5720
|
import { basename as basename2, dirname as dirname4, extname, join as join16 } from "node:path";
|
|
5425
5721
|
var MANAGED_RULE_NAMES = /* @__PURE__ */ new Set([
|
|
5426
5722
|
"harness-general.md",
|
|
@@ -5494,7 +5790,7 @@ async function applyCodeBuddySetup(options) {
|
|
|
5494
5790
|
continue;
|
|
5495
5791
|
}
|
|
5496
5792
|
await mkdir4(dirname4(target), { recursive: true });
|
|
5497
|
-
await
|
|
5793
|
+
await writeFile6(target, content, { encoding: "utf8", flag: "wx" });
|
|
5498
5794
|
result.copied.push(target);
|
|
5499
5795
|
}
|
|
5500
5796
|
}
|
|
@@ -5515,7 +5811,7 @@ async function applyCodeBuddySetup(options) {
|
|
|
5515
5811
|
codegraph: { command: "codegraph", args: ["serve", "--mcp"] }
|
|
5516
5812
|
}
|
|
5517
5813
|
};
|
|
5518
|
-
await
|
|
5814
|
+
await writeFile6(path, JSON.stringify(next, null, 2) + "\n", "utf8");
|
|
5519
5815
|
result.mcpUpdated = true;
|
|
5520
5816
|
}
|
|
5521
5817
|
}
|
package/package.json
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "hunter-harness",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Local-first, server-governed agent harness",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"engines": {
|
|
8
|
-
"node": ">=22.12.0"
|
|
9
|
-
},
|
|
10
|
-
"bin": {
|
|
11
|
-
"hunter-harness": "dist/bin.js"
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"dist/bin.js"
|
|
15
|
-
],
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json && npm run bundle",
|
|
18
|
-
"bundle": "node ../../scripts/bundle-cli.mjs",
|
|
19
|
-
"typecheck": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json --noEmit",
|
|
20
|
-
"prepack": "npm run build"
|
|
21
|
-
},
|
|
22
|
-
"dependencies": {
|
|
23
|
-
"commander": "15.0.0",
|
|
24
|
-
"yaml": "2.9.0",
|
|
25
|
-
"zod": "4.4.3"
|
|
26
|
-
},
|
|
27
|
-
"optionalDependencies": {
|
|
28
|
-
"pacote": "21.4.0"
|
|
29
|
-
},
|
|
30
|
-
"devDependencies": {
|
|
31
|
-
"@hunter-harness/contracts": "*",
|
|
32
|
-
"@hunter-harness/core": "*",
|
|
33
|
-
"@types/pacote": "^11.1.8",
|
|
34
|
-
"esbuild": "^0.25.12"
|
|
35
|
-
}
|
|
36
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "hunter-harness",
|
|
3
|
+
"version": "0.2.21",
|
|
4
|
+
"description": "Local-first, server-governed agent harness",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.12.0"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"hunter-harness": "dist/bin.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist/bin.js"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json && npm run bundle",
|
|
18
|
+
"bundle": "node ../../scripts/bundle-cli.mjs",
|
|
19
|
+
"typecheck": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json --noEmit",
|
|
20
|
+
"prepack": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"commander": "15.0.0",
|
|
24
|
+
"yaml": "2.9.0",
|
|
25
|
+
"zod": "4.4.3"
|
|
26
|
+
},
|
|
27
|
+
"optionalDependencies": {
|
|
28
|
+
"pacote": "21.4.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@hunter-harness/contracts": "*",
|
|
32
|
+
"@hunter-harness/core": "*",
|
|
33
|
+
"@types/pacote": "^11.1.8",
|
|
34
|
+
"esbuild": "^0.25.12"
|
|
35
|
+
}
|
|
36
|
+
}
|