owlrunkit 0.21.0 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +66 -11
  3. package/integrations/codex/skills/owlcoda-runkit/SKILL.md +60 -18
  4. package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
  5. package/package.json +1 -1
  6. package/packages/attest/distribution-manifest-v1.json +15 -0
  7. package/packages/attest/src/quick.mjs +15 -0
  8. package/registry-release-policy-v2.json +5 -5
  9. package/sbom.spdx.json +4 -4
  10. package/scripts/runkit-contract/core-contract.mjs +6 -1
  11. package/scripts/runkit-contract/fleet-skill-plan.mjs +14 -66
  12. package/scripts/runkit-contract/formal-workflow.mjs +1 -1
  13. package/scripts/runkit-contract/hook-recovery.mjs +496 -0
  14. package/scripts/runkit-contract/inspect-presentation.mjs +131 -0
  15. package/scripts/runkit-contract/install-codex-skill.mjs +2097 -196
  16. package/scripts/runkit-contract/onboarding-doctor.mjs +179 -4
  17. package/scripts/runkit-contract/project-cli-resolver.mjs +12 -0
  18. package/scripts/runkit-contract/registry-adoption.mjs +4 -4
  19. package/scripts/runkit-contract/runkit-bootstrap.mjs +1 -0
  20. package/scripts/runkit-contract/runkit-cli.mjs +62 -30
  21. package/scripts/runkit-contract/schemas/fleet-skill-upgrade-plan-v1.schema.json +16 -3
  22. package/scripts/runkit-contract/schemas/hook-recovery-v1.schema.json +592 -0
  23. package/scripts/runkit-contract/schemas/inspect-compact-v2.schema.json +122 -0
  24. package/scripts/runkit-contract/schemas/team-delivery-status-v3.schema.json +113 -0
  25. package/scripts/runkit-contract/schemas/team-recommendation-v2.schema.json +120 -0
  26. package/scripts/runkit-contract/sync-release-identity.mjs +1 -1
  27. package/scripts/runkit-contract/team-delivery.mjs +75 -0
  28. package/scripts/runkit-contract/team-project.mjs +62 -2
@@ -563,6 +563,75 @@ function ignoreFileCoversRuntime(root, ignoreFile) {
563
563
  return ignored;
564
564
  }
565
565
 
566
+ const ESLINT_FLAT_CONFIG_FILES = Object.freeze([
567
+ "eslint.config.js",
568
+ "eslint.config.mjs",
569
+ "eslint.config.cjs",
570
+ "eslint.config.ts",
571
+ "eslint.config.mts",
572
+ "eslint.config.cts",
573
+ ]);
574
+
575
+ function eslintIgnoreListCoversRuntime(listText) {
576
+ let ignored = false;
577
+ for (const match of listText.matchAll(/(["'])([^"']*)\1/gu)) {
578
+ const negated = match[2].startsWith("!");
579
+ const pattern = (negated ? match[2].slice(1) : match[2])
580
+ .replace(/^\.\//u, "")
581
+ .replace(/^\*\*\//u, "");
582
+ if (new Set([
583
+ ".owlcoda/",
584
+ ".owlcoda/**",
585
+ ".owlcoda/runkit/",
586
+ ".owlcoda/runkit/**",
587
+ ]).has(pattern)) {
588
+ ignored = !negated;
589
+ } else if (negated && pattern.startsWith(".owlcoda/runkit/")) {
590
+ ignored = false;
591
+ }
592
+ }
593
+ return ignored;
594
+ }
595
+
596
+ function eslintFlatConfigCoversRuntime(text) {
597
+ const importsGlobalHelper = /(?:import\s*\{[^}]*\bglobalIgnores\b[^}]*\}\s*from|(?:const|let|var)\s*\{[^}]*\bglobalIgnores\b[^}]*\}\s*=\s*require\s*\()\s*["'](?:eslint\/config|@eslint\/config-helpers)["']/su.test(text);
598
+ if (importsGlobalHelper) {
599
+ for (const match of text.matchAll(/\bglobalIgnores\s*\(\s*\[([^\]]*)\]/gsu)) {
600
+ if (eslintIgnoreListCoversRuntime(match[1])) return true;
601
+ }
602
+ }
603
+ for (const match of text.matchAll(/\{([^{}]*)\}/gsu)) {
604
+ const body = match[1];
605
+ const withoutOptionalName = body.replace(
606
+ /(?:^|,)\s*name\s*:\s*(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')\s*(?=,|$)/su,
607
+ "",
608
+ );
609
+ const ignores = /^\s*,?\s*ignores\s*:\s*\[([^\]]*)\]\s*,?\s*$/su.exec(
610
+ withoutOptionalName,
611
+ );
612
+ if (ignores === null) continue;
613
+ if (eslintIgnoreListCoversRuntime(ignores[1])) return true;
614
+ }
615
+ return false;
616
+ }
617
+
618
+ function eslintFlatConfig(root) {
619
+ for (const relative of ESLINT_FLAT_CONFIG_FILES) {
620
+ if (!existsSync(path.join(root, relative))) continue;
621
+ let text;
622
+ try {
623
+ text = readWorkspaceFileBounded(root, relative);
624
+ } catch {
625
+ return { relative, coversRuntime: false };
626
+ }
627
+ return {
628
+ relative,
629
+ coversRuntime: eslintFlatConfigCoversRuntime(text),
630
+ };
631
+ }
632
+ return null;
633
+ }
634
+
566
635
  const BROAD_COLLECTION_TOOLS = Object.freeze([
567
636
  {
568
637
  tool: "biome",
@@ -629,14 +698,18 @@ function runtimeIsolationCheck(root) {
629
698
  if (typeof value !== "string" || !hasBroadDotTarget(value)) continue;
630
699
  for (const descriptor of BROAD_COLLECTION_TOOLS) {
631
700
  if (!descriptor.command.test(value)) continue;
701
+ const flatConfig = descriptor.tool === "eslint" ? eslintFlatConfig(root) : null;
702
+ if (flatConfig?.coversRuntime) continue;
632
703
  if (
704
+ flatConfig === null
705
+ &&
633
706
  descriptor.ignoreFileSupported
634
707
  && ignoreFileCoversRuntime(root, descriptor.ignoreFile)
635
708
  ) {
636
709
  continue;
637
710
  }
638
711
  affectedScripts.push({
639
- ignoreFile: descriptor.ignoreFile,
712
+ ignoreFile: flatConfig?.relative ?? descriptor.ignoreFile,
640
713
  script,
641
714
  tool: descriptor.tool,
642
715
  });
@@ -653,6 +726,21 @@ export function inspectRuntimeIsolationV1({ workspaceRoot } = {}) {
653
726
  return runtimeIsolationCheck(resolveBoundedWorkspaceRoot(workspaceRoot));
654
727
  }
655
728
 
729
+ export function inspectLocalMaintenanceV1({
730
+ workspaceRoot,
731
+ packageRoot,
732
+ sharedSkillRoot,
733
+ } = {}) {
734
+ const root = resolveBoundedWorkspaceRoot(workspaceRoot);
735
+ return {
736
+ schemaVersion: "OwlCodaRunKitLocalMaintenanceV1",
737
+ profileLaunchers: readProfileLauncherReadiness({ workspaceRoot: root }),
738
+ runtimeIsolation: runtimeIsolationCheck(root),
739
+ skill: inspectDoctorSkillStateV1({ packageRoot, sharedSkillRoot }),
740
+ authorizationGranted: false,
741
+ };
742
+ }
743
+
656
744
  function configCheck(root) {
657
745
  try {
658
746
  const config = readWorkspaceJsonBounded(
@@ -850,8 +938,12 @@ async function buildDoctorReport({
850
938
  issues: [error instanceof Error ? error.message : String(error)],
851
939
  };
852
940
  }
853
- let activationBlockedReason = null;
854
- if (inspectedSkill.status !== "valid") {
941
+ let activationBlockedReason = new Set(["unmanaged", "drifted"]).has(
942
+ inspectedSkill.status,
943
+ )
944
+ ? `shared_skill_integrity_invalid:${inspectedSkill.status}`
945
+ : null;
946
+ if (inspectedSkill.status !== "valid" && activationBlockedReason === null) {
855
947
  try {
856
948
  await skillFleetInspector();
857
949
  } catch (error) {
@@ -1000,6 +1092,77 @@ export async function runDoctorV2(options = {}) {
1000
1092
  return buildDoctorReport({ ...options, includeSharedSkill: true });
1001
1093
  }
1002
1094
 
1095
+ function shellQuote(value) {
1096
+ return `'${String(value).replaceAll("'", `'"'"'`)}'`;
1097
+ }
1098
+
1099
+ function doctorRuntimeIsolationGuidance(runtimeIsolation) {
1100
+ return runtimeIsolation.affectedScripts.map((affected) => ({
1101
+ configurationFile: affected.ignoreFile,
1102
+ configurationStyle: affected.tool === "eslint"
1103
+ && affected.ignoreFile.startsWith("eslint.config.")
1104
+ ? "flat_config_global_ignores"
1105
+ : "ignore_file",
1106
+ recommendedPattern: affected.tool === "eslint"
1107
+ && affected.ignoreFile.startsWith("eslint.config.")
1108
+ ? ".owlcoda/runkit/**"
1109
+ : ".owlcoda/runkit/",
1110
+ script: affected.script,
1111
+ tool: affected.tool,
1112
+ }));
1113
+ }
1114
+
1115
+ export async function runDoctorV3(options = {}) {
1116
+ const report = await buildDoctorReport({ ...options, includeSharedSkill: true });
1117
+ const skillRoot = options.skillRoot
1118
+ ?? path.join(userInfo().homedir, ".agents", "skills", "owlcoda-runkit");
1119
+ const archiveRoot = path.join(
1120
+ userInfo().homedir,
1121
+ ".agents",
1122
+ "skill-archives",
1123
+ "owlcoda-runkit",
1124
+ );
1125
+ const sharedSkill = report.checks.sharedSkill;
1126
+ const activation = sharedSkill.status === "valid"
1127
+ ? {
1128
+ status: "not_evaluated",
1129
+ reason: "shared_skill_already_current",
1130
+ }
1131
+ : sharedSkill.activationBlockedReason
1132
+ ? {
1133
+ status: "blocked",
1134
+ reason: sharedSkill.activationBlockedReason,
1135
+ }
1136
+ : {
1137
+ status: "eligible",
1138
+ reason: "fleet_check_passed",
1139
+ };
1140
+ return {
1141
+ ...report,
1142
+ schemaVersion: "OwlCodaRunKitDoctorReportV3",
1143
+ checks: {
1144
+ ...report.checks,
1145
+ runtimeIsolation: {
1146
+ ...report.checks.runtimeIsolation,
1147
+ guidance: doctorRuntimeIsolationGuidance(report.checks.runtimeIsolation),
1148
+ },
1149
+ sharedSkill: {
1150
+ ...sharedSkill,
1151
+ activation: {
1152
+ ...activation,
1153
+ checkCommand: `npx --no-install owlrunkit fleet skill-plan --skill-root ${shellQuote(skillRoot)} --archive-root ${shellQuote(archiveRoot)}`,
1154
+ },
1155
+ },
1156
+ },
1157
+ profileLauncherWarnings: {
1158
+ ...report.profileLauncherWarnings,
1159
+ diagnosticCommand: report.profileLauncherWarnings.count > 0
1160
+ ? "npx --no-install owlrunkit profiles reconcile --workspace . --dry-run"
1161
+ : null,
1162
+ },
1163
+ };
1164
+ }
1165
+
1003
1166
  export function formatDoctorHuman(report) {
1004
1167
  const otherWarnings = report.warningCodes.filter(
1005
1168
  (code) => code !== "profile_launcher_warnings",
@@ -1023,12 +1186,24 @@ export function formatDoctorHuman(report) {
1023
1186
  ? [`Shared Skill: ${report.checks.sharedSkill.status} (${report.checks.sharedSkill.sharedSkillVersion ?? "missing"}; project Core ${report.checks.sharedSkill.projectCoreVersion})`]
1024
1187
  : []),
1025
1188
  ...(report.checks.sharedSkill?.activationBlockedReason
1189
+ && !report.checks.sharedSkill?.activation
1026
1190
  ? [`Shared Skill activation: ${report.checks.sharedSkill.activationBlockedReason}`]
1027
1191
  : []),
1192
+ ...(report.checks.sharedSkill?.activation
1193
+ ? [
1194
+ `Shared Skill activation: ${report.checks.sharedSkill.activation.status} (${report.checks.sharedSkill.activation.reason})`,
1195
+ `Fleet-safe check: ${report.checks.sharedSkill.activation.checkCommand}`,
1196
+ ]
1197
+ : []),
1028
1198
  `Lifecycle: ${report.checks.lifecycle.status}`,
1029
1199
  ...(report.issueCodes.length > 0 ? [`Issues: ${report.issueCodes.join(", ")}`] : []),
1030
1200
  ...(report.profileLauncherWarnings.count > 0
1031
- ? [`Profile launcher warnings: ${report.profileLauncherWarnings.count} (non-blocking)`]
1201
+ ? [
1202
+ `Profile launcher warnings: ${report.profileLauncherWarnings.count} (non-blocking)`,
1203
+ ...(report.profileLauncherWarnings.diagnosticCommand
1204
+ ? [`Profile launcher diagnostic: ${report.profileLauncherWarnings.diagnosticCommand}`]
1205
+ : []),
1206
+ ]
1032
1207
  : []),
1033
1208
  ...(otherWarnings.length > 0 ? [`Warnings: ${otherWarnings.join(", ")}`] : []),
1034
1209
  `Next: ${report.nextAllowedAction === "adopt_current_release"
@@ -20,10 +20,22 @@ const SELF_NORMALIZED_QUICK_CORE_VERSIONS = new Set([
20
20
  "0.20.0",
21
21
  "0.20.1",
22
22
  "0.21.0",
23
+ "0.21.1",
24
+ "0.22.0",
25
+ "0.22.1",
23
26
  ]);
24
27
  const EXACT_VERSION = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/;
25
28
  const SHA256_REF = /^sha256:[a-f0-9]{64}$/;
26
29
  const TRUSTED_PRIOR_CORE_IDENTITIES = new Map([
30
+ ["0.22.0", new Set([
31
+ "sha256:e79d2123d88ada0e2e31f62fabee83b1d661ca02e93a383691a502f721ae1e7e",
32
+ ])],
33
+ ["0.21.0", new Set([
34
+ "sha256:f85b9bf8c32705d5f5dfebdb653e83ac8d1ac0468214c17f230cbd2de04246fd",
35
+ ])],
36
+ ["0.20.1", new Set([
37
+ "sha256:8af2b9792731efa37f596255a7677dcec6e2fd3cc079dfdb0e6ff29e8017e784",
38
+ ])],
27
39
  ["0.20.0", new Set([
28
40
  "sha256:30098aa9344c17c1ed0b5ed45ef1c590b3214b824402f584029c1ecb38ca7a70",
29
41
  ])],
@@ -47,10 +47,10 @@ const POLICY = Object.freeze({
47
47
  ],
48
48
  rollbackRelease: {
49
49
  packageName: PACKAGE_NAME,
50
- version: "0.20.0",
51
- shasum: "b123f5ea33a4f500961a0df3226ed0ca409d111d",
52
- integrity: "sha512-VcXUh+q8DwR6RoO9JDoUMEzFrpFRBpSvpqgHvIY15Z+Oo+A31Ij2kFO5wCFki80nd6Tl/YT70t7UV2aG0TZ0GQ==",
53
- tarballUrl: "https://registry.npmjs.org/owlrunkit/-/owlrunkit-0.20.0.tgz",
50
+ version: "0.21.0",
51
+ shasum: "f9ecfa41ad2ba3140258234b49d98798675651f2",
52
+ integrity: "sha512-hopLyvN5vp0y3xu0H0jQbMI6DxaDSWcGYcn7tAbCa0m2OqW2G8n5kJvnr217jqPYlArQwJ2TDPRLNmp4vPEhVQ==",
53
+ tarballUrl: "https://registry.npmjs.org/owlrunkit/-/owlrunkit-0.21.0.tgz",
54
54
  },
55
55
  registryEvidenceMustBeExternal: true,
56
56
  registryEvidenceSchemaVersion: "OwlCodaRunKitRegistryReleaseEvidenceV1",
@@ -15,6 +15,7 @@ const CURRENT_PACKAGE_COMMANDS = new Set([
15
15
  "bootstrap",
16
16
  "core-successor",
17
17
  "fleet",
18
+ "hook",
18
19
  "init",
19
20
  ]);
20
21
 
@@ -87,10 +87,11 @@ import {
87
87
  verifySourceCandidateV2,
88
88
  } from "./source-candidate.mjs";
89
89
  import {
90
- buildInspectCompactV1,
90
+ buildInspectCompactV2,
91
91
  buildInspectSummary,
92
92
  formatInspectHuman,
93
93
  } from "./inspect-presentation.mjs";
94
+ import { buildHookRecoveryV1 } from "./hook-recovery.mjs";
94
95
  import { attestQuickReceiptDetails } from "./quick-attest.mjs";
95
96
  import { readLocalQuickMetrics } from "./quick-metrics.mjs";
96
97
  import { runQuickEvidenceReuse } from "./quick-evidence-reuse.mjs";
@@ -163,9 +164,10 @@ import {
163
164
  } from "./core-successor.mjs";
164
165
  import {
165
166
  formatDoctorHuman,
167
+ inspectLocalMaintenanceV1,
166
168
  readAdoptionReadiness,
167
169
  readProfileLauncherReadiness,
168
- runDoctorV2,
170
+ runDoctorV3,
169
171
  } from "./onboarding-doctor.mjs";
170
172
  import {
171
173
  applyOnboardingBootstrapV1,
@@ -180,7 +182,7 @@ import { OFFICIAL_NPM_REGISTRY } from "./registry-adoption-gate.mjs";
180
182
  import {
181
183
  appendTeamProjectEventV1,
182
184
  assignTeamProjectV1,
183
- buildTeamProjectTakeoverV3,
185
+ buildTeamProjectTakeoverV4,
184
186
  checkpointTeamProjectV1,
185
187
  checkpointTeamProjectFromQuickReceiptV1,
186
188
  closeTeamProjectVerificationV1,
@@ -212,13 +214,11 @@ import {
212
214
  importExternalDeliveryV1,
213
215
  } from "./project-external-delivery.mjs";
214
216
  import {
215
- formatTeamDeliveryRecommendationHumanV1,
216
- formatTeamDeliveryStatusHumanV1,
217
- formatTeamDeliveryStatusHumanV2,
217
+ formatTeamDeliveryRecommendationHumanV2,
218
+ formatTeamDeliveryStatusHumanV3,
218
219
  formatTeamPacketValidationHumanV1,
219
- readTeamDeliveryStatusV1,
220
- readTeamDeliveryStatusV2,
221
- recommendTeamDeliveryV1,
220
+ readTeamDeliveryStatusV3,
221
+ recommendTeamDeliveryV2,
222
222
  validateTeamPacketFileV1,
223
223
  } from "./team-delivery.mjs";
224
224
 
@@ -256,12 +256,12 @@ Fleet and status
256
256
  npx --no-install owlrunkit fleet rollback-registry --receipt <file> --rollback-receipt <file>
257
257
  npx --no-install owlrunkit fleet skill-plan --skill-root <directory> --archive-root <directory>
258
258
  npx --no-install owlrunkit fleet discover; npx --no-install owlrunkit status --workspace .
259
+ npx --no-install owlrunkit hook recovery --workspace . --json
259
260
  Agent team projects
260
261
  npx --no-install owlrunkit project <init|assign|checkpoint|handoff|reject-and-return|reopen-upstream|transfer|work-item revise-scope|work-item require-evidence|reconcile|delivery record|decision|verification|capture|target-snapshot|import-delivery|status|takeover|successor> [options]
261
262
  npx --no-install owlrunkit team <recommend|status|packet validate> [options]
262
263
  Durable project truth plus read-only Team recommendation and packet validation. No Agent dispatch.
263
- Core successor
264
- npx --no-install owlrunkit core-successor <plan|apply|resume> --workspace . [...]
264
+ Core successor: npx --no-install owlrunkit core-successor <plan|apply|resume> --workspace . [...]
265
265
  Other onboarding commands:
266
266
  npx --no-install owlrunkit profiles validate --workspace .; npx --no-install owlrunkit profiles impact --workspace . --changed <path>
267
267
  npx --no-install owlrunkit inspect --workspace . --json
@@ -295,9 +295,31 @@ grants no Git, release, deployment, production, or business authority.
295
295
  npx --no-install owlrunkit inspect --workspace <directory> --run-id <id> [--verbose] [--json]
296
296
 
297
297
  --compact returns only current project truth, progress, dominant gap, owner,
298
- next action, warning summary, execution counts, and the authority boundary.
298
+ separate lifecycle/project/maintenance actions, actionable warning groups,
299
+ advisories, execution counts, and the authority boundary.
299
300
  It never grants Git, release, deployment, production, or business authority.
300
301
  `,
302
+ hook: `Usage:
303
+ npx --no-install owlrunkit hook recovery --workspace <directory> --json
304
+
305
+ Returns a deterministic, read-only, strictly bounded recovery projection for
306
+ SessionStart(source=compact). Project text remains escaped untrusted data.
307
+ The consumer must compare its current Goal/Done-when with projectId and the
308
+ objective digest/preview before considering nextAction. This command never
309
+ changes project, execution, lease, assurance, Agent, Git, release, deployment,
310
+ production, or business state.
311
+ `,
312
+ "hook recovery": `Usage:
313
+ npx --no-install owlrunkit hook recovery --workspace <directory> --json
314
+
315
+ Returns a deterministic, read-only, strictly bounded recovery projection for
316
+ SessionStart(source=compact). Project text remains escaped untrusted data.
317
+ The consumer must compare its current Goal/Done-when with projectId and the
318
+ objective digest/preview before considering nextAction. This command never
319
+ changes project, execution, lease, assurance, Agent, Git, release, deployment,
320
+ production, or business state.
321
+ `,
322
+
301
323
  doctor: `Usage:
302
324
  npx --no-install owlrunkit doctor --workspace <directory>
303
325
  [--registry <url>] [--timeout-ms <milliseconds>]
@@ -313,16 +335,6 @@ non-blocking; it never changes project authority.
313
335
 
314
336
  Verifies the exact registry release against the project-local install and writes
315
337
  project-owned adoption evidence. It grants no Git or release authority.
316
- `,
317
- doctor: `Usage:
318
- npx --no-install owlrunkit doctor --workspace <directory>
319
- [--skill-root <shared-skill-directory>] [--registry <url>]
320
- [--timeout-ms <milliseconds>] [--json]
321
-
322
- Diagnoses the project package, Core, registry, profiles, lifecycle, and the
323
- package-bound versus shared Skill version. A shared Skill mismatch is a visible
324
- non-blocking warning because the CLI cannot prove which Skill the active Agent
325
- session already loaded.
326
338
  `,
327
339
  assurance: `Usage:
328
340
  npx --no-install owlrunkit assurance route --workspace <directory> --request <risk-facts.json>
@@ -2159,6 +2171,13 @@ function inspect(options) {
2159
2171
  const projectDriver = pathEntryExists(projectDefinitionPath)
2160
2172
  ? readTeamProjectStatusV3({ workspaceRoot: root })
2161
2173
  : null;
2174
+ const localMaintenance = options.compact === true
2175
+ ? inspectLocalMaintenanceV1({
2176
+ workspaceRoot: root,
2177
+ packageRoot: path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."),
2178
+ sharedSkillRoot: process.env.OWLCODA_RUNKIT_PROCESS_SKILL_ROOT,
2179
+ })
2180
+ : null;
2162
2181
  const summary = buildInspectSummary(result, {
2163
2182
  maintenanceNextAction: onboarding.maintenanceNextAction ?? null,
2164
2183
  projectDriver,
@@ -2178,9 +2197,10 @@ function inspect(options) {
2178
2197
  summary,
2179
2198
  view,
2180
2199
  onboarding,
2200
+ ...(localMaintenance === null ? {} : { localMaintenance }),
2181
2201
  ...(projectDriver === null ? {} : { projectDriver }),
2182
2202
  };
2183
- return options.compact === true ? buildInspectCompactV1(inspected) : inspected;
2203
+ return options.compact === true ? buildInspectCompactV2(inspected) : inspected;
2184
2204
  }
2185
2205
 
2186
2206
  function closeout(options) {
@@ -2444,6 +2464,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
2444
2464
  "efficiency",
2445
2465
  "fleet",
2446
2466
  "formal",
2467
+ "hook",
2447
2468
  "lease",
2448
2469
  "mode",
2449
2470
  "profiles",
@@ -2555,6 +2576,8 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
2555
2576
  ? ["json"]
2556
2577
  : command === "team"
2557
2578
  ? ["json"]
2579
+ : command === "hook"
2580
+ ? ["json"]
2558
2581
  : command === "inspect"
2559
2582
  ? ["json", "compact", "verbose", "history"]
2560
2583
  : command === "init"
@@ -2637,7 +2660,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
2637
2660
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 25 || timeoutMs > 10_000) {
2638
2661
  throw new Error("--timeout-ms must be an integer from 25 to 10000.");
2639
2662
  }
2640
- const report = await runDoctorV2({
2663
+ const report = await runDoctorV3({
2641
2664
  workspaceRoot: requireOption(options, "workspace"),
2642
2665
  sharedSkillRoot: options["skill-root"],
2643
2666
  discoverSharedSkill: true,
@@ -2772,20 +2795,20 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
2772
2795
  const root = workspace(options);
2773
2796
  if (action === "recommend") {
2774
2797
  assertOnlyOptions(options, ["workspace", "json"]);
2775
- const recommendation = recommendTeamDeliveryV1({ workspaceRoot: root });
2798
+ const recommendation = recommendTeamDeliveryV2({ workspaceRoot: root });
2776
2799
  return options.json === true
2777
2800
  ? recommendation
2778
2801
  : withHumanOutput(
2779
2802
  recommendation,
2780
- formatTeamDeliveryRecommendationHumanV1(recommendation),
2803
+ formatTeamDeliveryRecommendationHumanV2(recommendation),
2781
2804
  );
2782
2805
  }
2783
2806
  if (action === "status") {
2784
2807
  assertOnlyOptions(options, ["workspace", "json"]);
2785
- const status = readTeamDeliveryStatusV2({ workspaceRoot: root });
2808
+ const status = readTeamDeliveryStatusV3({ workspaceRoot: root });
2786
2809
  return options.json === true
2787
2810
  ? status
2788
- : withHumanOutput(status, formatTeamDeliveryStatusHumanV2(status));
2811
+ : withHumanOutput(status, formatTeamDeliveryStatusHumanV3(status));
2789
2812
  }
2790
2813
  if (action === "packet validate") {
2791
2814
  assertOnlyOptions(options, ["workspace", "packet", "json"]);
@@ -3598,6 +3621,15 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
3598
3621
  const root = workspace(options);
3599
3622
  return withControlTransaction(root, () => plan(options));
3600
3623
  }
3624
+ if (command === "hook") {
3625
+ if (action !== "recovery") {
3626
+ throw new Error("Usage: npx --no-install owlrunkit hook recovery --workspace <directory> --json");
3627
+ }
3628
+ assertOnlyOptions(options, ["workspace", "json"]);
3629
+ return buildHookRecoveryV1({
3630
+ workspaceRoot: requireOption(options, "workspace"),
3631
+ });
3632
+ }
3601
3633
  if (command === "inspect") return inspect(options);
3602
3634
  if (command === "status") {
3603
3635
  const inspected = inspect({ ...options, json: true });
@@ -4335,7 +4367,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
4335
4367
  : withHumanOutput(status, formatTeamProjectStatusHumanV2(status));
4336
4368
  }
4337
4369
  if (action === "takeover") {
4338
- const takeover = buildTeamProjectTakeoverV3({
4370
+ const takeover = buildTeamProjectTakeoverV4({
4339
4371
  workspaceRoot: root,
4340
4372
  agentId: requireOption(options, "agent"),
4341
4373
  });
@@ -4600,7 +4632,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
4600
4632
  return withControlTransaction(root, () => closeout(options));
4601
4633
  }
4602
4634
  if (command === "finish") return finishExecution(options);
4603
- throw new Error("Usage: npx --no-install owlrunkit <bootstrap|init|doctor|adopt|mode|team|assurance|fleet|profiles|project|status|formal|candidate|deployment|core-successor|start|verify|repair|offline-export|offline-import|quick-verify|quick-reuse|quick-runtime-verify|quick-attest|quick-metrics|efficiency|finish|plan|inspect|lease|delivery|coverage-adopt|resume|verify-plan|resource-preflight|snapshot|visual-smoke|finalize|ready-for-commit|closeout> [options]");
4635
+ throw new Error("Usage: npx --no-install owlrunkit <bootstrap|init|doctor|adopt|mode|team|hook|assurance|fleet|profiles|project|status|formal|candidate|deployment|core-successor|start|verify|repair|offline-export|offline-import|quick-verify|quick-reuse|quick-runtime-verify|quick-attest|quick-metrics|efficiency|finish|plan|inspect|lease|delivery|coverage-adopt|resume|verify-plan|resource-preflight|snapshot|visual-smoke|finalize|ready-for-commit|closeout> [options]");
4604
4636
  } catch (error) {
4605
4637
  return {
4606
4638
  status: "invalid_input",
@@ -42,7 +42,20 @@
42
42
  "source": { "type": "string", "minLength": 1 },
43
43
  "complete": { "type": "boolean" },
44
44
  "workspaceCount": { "type": "integer", "minimum": 0 },
45
- "unreachableRoots": { "$ref": "#/$defs/strings" }
45
+ "unreachableRoots": { "$ref": "#/$defs/strings" },
46
+ "issues": {
47
+ "type": "array",
48
+ "items": {
49
+ "type": "object",
50
+ "additionalProperties": false,
51
+ "required": ["code", "path", "coverageRoot"],
52
+ "properties": {
53
+ "code": { "type": "string", "minLength": 1 },
54
+ "path": { "type": "string", "minLength": 1 },
55
+ "coverageRoot": { "type": "string", "minLength": 1 }
56
+ }
57
+ }
58
+ }
46
59
  }
47
60
  }
48
61
  ]
@@ -102,7 +115,7 @@
102
115
  "status": "blocked",
103
116
  "expectedCore": {
104
117
  "contractVersion": "0.2",
105
- "coreVersion": "0.21.0",
118
+ "coreVersion": "0.22.1",
106
119
  "coreManifestSha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
107
120
  "coreSourceRef": "artifact:sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
108
121
  },
@@ -113,7 +126,7 @@
113
126
  "installedConfigCoreVersion": "0.20.0",
114
127
  "issues": ["installed_skill_core_version_mismatch"]
115
128
  },
116
- "discovery": { "source": "fleet_registry", "complete": true, "workspaceCount": 1, "unreachableRoots": [] },
129
+ "discovery": { "source": "fleet_registry", "complete": true, "workspaceCount": 1, "unreachableRoots": [], "issues": [] },
117
130
  "affectedProjects": [
118
131
  {
119
132
  "workspaceRoot": "/projects/example",