akm-cli 0.9.0 → 0.9.1-beta.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 (109) hide show
  1. package/CHANGELOG.md +707 -0
  2. package/README.md +28 -63
  3. package/STABILITY.md +4 -2
  4. package/dist/commands/agent/contribute-cli.js +1 -1
  5. package/dist/commands/feedback-cli.js +7 -1
  6. package/dist/commands/health/llm-usage.js +2 -1
  7. package/dist/commands/health/surfaces.js +4 -77
  8. package/dist/commands/health.js +65 -11
  9. package/dist/commands/improve/distill/quality-gate.js +6 -1
  10. package/dist/commands/improve/eligibility.js +7 -1
  11. package/dist/commands/improve/improve.js +126 -10
  12. package/dist/commands/improve/locks.js +7 -0
  13. package/dist/commands/improve/memory/memory-improve.js +8 -0
  14. package/dist/commands/improve/run-context.js +5 -0
  15. package/dist/commands/improve/session-asset.js +4 -0
  16. package/dist/commands/lint/base-linter.js +31 -7
  17. package/dist/commands/lint/index.js +200 -50
  18. package/dist/commands/lint/types.js +22 -1
  19. package/dist/commands/proposal/repository.js +17 -1
  20. package/dist/commands/sources/info.js +12 -2
  21. package/dist/commands/sources/installed-stashes.js +6 -1
  22. package/dist/commands/tasks/tasks.js +8 -2
  23. package/dist/commands/workflow-cli.js +17 -11
  24. package/dist/core/abort-deadline.js +28 -0
  25. package/dist/core/adapter/adapters/agent-skills-adapter.js +67 -5
  26. package/dist/core/adapter/adapters/akm-adapter.js +13 -10
  27. package/dist/core/adapter/adapters/akm-lint.js +72 -20
  28. package/dist/core/adapter/adapters/akm-task-adapter.js +40 -19
  29. package/dist/core/adapter/adapters/tool-dir-shared.js +5 -3
  30. package/dist/core/asset/frontmatter.js +4 -0
  31. package/dist/core/common.js +66 -6
  32. package/dist/core/concurrent.js +32 -0
  33. package/dist/core/config/schema/workflow.js +11 -0
  34. package/dist/core/errors.js +25 -0
  35. package/dist/core/events.js +30 -24
  36. package/dist/core/file-lock.js +7 -1
  37. package/dist/core/improve-result.js +5 -0
  38. package/dist/core/json-schema.js +327 -9
  39. package/dist/core/loopback.js +89 -0
  40. package/dist/core/migration-operation.js +17 -2
  41. package/dist/core/path-access.js +107 -0
  42. package/dist/core/redaction.js +64 -17
  43. package/dist/core/spawn-env.js +234 -0
  44. package/dist/core/state-db-scope.js +134 -0
  45. package/dist/core/subprocess.js +181 -37
  46. package/dist/core/write-provenance.js +85 -0
  47. package/dist/core/write-source.js +7 -0
  48. package/dist/indexer/db/graph-db.js +17 -6
  49. package/dist/indexer/ensure-index.js +10 -3
  50. package/dist/indexer/index-written-assets.js +17 -2
  51. package/dist/indexer/indexer.js +55 -15
  52. package/dist/indexer/passes/memory-inference.js +4 -0
  53. package/dist/indexer/search/db-search.js +8 -15
  54. package/dist/integrations/agent/engine-resolution.js +24 -11
  55. package/dist/integrations/agent/model-aliases.js +1 -1
  56. package/dist/integrations/agent/profiles.js +9 -1
  57. package/dist/integrations/agent/spawn.js +15 -87
  58. package/dist/integrations/lockfile.js +45 -2
  59. package/dist/output/text/lint-format.js +17 -4
  60. package/dist/scripts/akm-migrate-node.js +1435 -753
  61. package/dist/scripts/akm-migrate.js +1403 -721
  62. package/dist/sources/providers/git-stash.js +19 -0
  63. package/dist/sources/providers/git.js +1 -1
  64. package/dist/sources/snapshot-fetchers/website-ingest.js +126 -20
  65. package/dist/storage/database.js +6 -1
  66. package/dist/storage/managed-db.js +19 -0
  67. package/dist/storage/repositories/index-connection.js +28 -3
  68. package/dist/storage/repositories/index-entries-repository.js +6 -1
  69. package/dist/storage/repositories/workflow-runs-repository.js +66 -13
  70. package/dist/tasks/log-redaction.js +156 -0
  71. package/dist/tasks/parser.js +82 -5
  72. package/dist/tasks/runner.js +139 -11
  73. package/dist/tasks/schema.js +65 -0
  74. package/dist/workflows/concurrency-policy.js +95 -1
  75. package/dist/workflows/exec/dispatch-redaction.js +114 -0
  76. package/dist/workflows/exec/exec-unit.js +542 -0
  77. package/dist/workflows/exec/frozen-judge.js +114 -42
  78. package/dist/workflows/exec/native-executor.js +457 -238
  79. package/dist/workflows/exec/param-secrets.js +4 -3
  80. package/dist/workflows/exec/run-workflow.js +424 -219
  81. package/dist/workflows/exec/step-work.js +496 -165
  82. package/dist/workflows/exec/unit-dispatch.js +31 -1
  83. package/dist/workflows/exec/unit-writer.js +53 -13
  84. package/dist/workflows/exec/worktree.js +454 -41
  85. package/dist/workflows/ir/compile.js +26 -2
  86. package/dist/workflows/ir/freeze.js +82 -15
  87. package/dist/workflows/ir/schema.js +105 -20
  88. package/dist/workflows/parser.js +216 -18
  89. package/dist/workflows/program/schema.js +24 -0
  90. package/dist/workflows/renderer.js +32 -4
  91. package/dist/workflows/resource-limits.js +182 -0
  92. package/dist/workflows/runtime/runs.js +146 -6
  93. package/dist/workflows/validate-summary.js +17 -2
  94. package/docs/README.md +74 -32
  95. package/docs/migration/release-notes/0.9.0.md +2 -1
  96. package/docs/migration/v0.7-to-v0.8.md +2 -1
  97. package/docs/migration/v0.8-to-v0.9.md +3 -1
  98. package/docs/reference/README.md +11 -4
  99. package/docs/reference/bundle-types.md +19 -0
  100. package/docs/reference/cli.md +105 -16
  101. package/docs/reference/configuration.md +15 -2
  102. package/docs/reference/data-and-telemetry.md +30 -10
  103. package/docs/reference/supported-formats.md +50 -0
  104. package/docs/reference/workflow-schema.md +1014 -0
  105. package/docs/reference/workflows.md +37 -633
  106. package/package.json +13 -6
  107. package/schemas/akm-config.json +8 -0
  108. package/schemas/akm-task.json +27 -5
  109. package/schemas/akm-workflow.json +85 -10
@@ -6977,12 +6977,21 @@ function rethrowIfTestIsolationError(err) {
6977
6977
  throw err;
6978
6978
  }
6979
6979
  }
6980
+ function isDataDirUnreadableError(err) {
6981
+ return err instanceof ConfigError && err.code === "DATA_DIR_UNREADABLE";
6982
+ }
6983
+ function rethrowIfDataDirUnreadable(err) {
6984
+ if (isDataDirUnreadableError(err)) {
6985
+ throw err;
6986
+ }
6987
+ }
6980
6988
  var CONFIG_HINTS, USAGE_HINTS, NOT_FOUND_HINTS, AkmError, ConfigError, UsageError, NotFoundError;
6981
6989
  var init_errors = __esm(() => {
6982
6990
  CONFIG_HINTS = {
6983
6991
  STASH_DIR_NOT_FOUND: "Run `akm setup` to create and configure your bundle, or configure a defaultBundle path.",
6984
6992
  STASH_DIR_NOT_A_DIRECTORY: "The configured default bundle path exists but isn't a directory. Update it to point at a folder.",
6985
6993
  STASH_DIR_UNREADABLE: "Check the path exists and your user has read permission, or update the default bundle path.",
6994
+ DATA_DIR_UNREADABLE: "The data directory is not readable by the user running akm. Check its owner and mode, or point AKM_DATA_DIR / XDG_DATA_HOME somewhere this user owns.",
6986
6995
  EMBEDDING_NOT_CONFIGURED: 'Run `akm config set embedding \'{"endpoint":"...","model":"..."}\'` to enable embeddings.',
6987
6996
  LLM_NOT_CONFIGURED: 'Run `akm setup` or configure an `engines` entry with `kind: "llm"`, then select it with `defaults.llmEngine`.',
6988
6997
  TEST_ISOLATION_MISSING: "Under bun test, when AKM_BUNDLE_DIR is set you MUST also set XDG_DATA_HOME (or AKM_DATA_DIR) and XDG_STATE_HOME (or AKM_STATE_DIR) to temp directories so the test does not touch the developer's real ~/.local/share/akm or ~/.local/state/akm.",
@@ -7411,13 +7420,23 @@ function hasErrnoCode(error, code) {
7411
7420
  return false;
7412
7421
  return error.code === code;
7413
7422
  }
7423
+ function isContainedRelativePath(value) {
7424
+ if (value === "" || value.startsWith("/") || value.startsWith("\\") || value.startsWith("~"))
7425
+ return false;
7426
+ if (/^[A-Za-z]:/.test(value))
7427
+ return false;
7428
+ return !value.split(/[/\\]+/).includes("..");
7429
+ }
7414
7430
  function isWithin(candidate, root) {
7415
- const resolvedRoot = safeRealpath(root);
7416
- const resolvedCandidate = safeRealpath(candidate);
7417
- const normalizedRoot = normalizeFsPathForComparison(resolvedRoot);
7418
- const normalizedCandidate = normalizeFsPathForComparison(resolvedCandidate);
7419
- const rel = path2.relative(normalizedRoot, normalizedCandidate);
7420
- return rel === "" || !rel.startsWith("..") && !path2.isAbsolute(rel);
7431
+ return isContainedResolvedPath(safeRealpath(candidate), safeRealpath(root));
7432
+ }
7433
+ function isContainedResolvedPath(resolvedCandidate, resolvedRoot) {
7434
+ const rel = path2.relative(normalizeFsPathForComparison(resolvedRoot), normalizeFsPathForComparison(resolvedCandidate));
7435
+ if (rel === "")
7436
+ return true;
7437
+ if (path2.isAbsolute(rel))
7438
+ return false;
7439
+ return rel.split(/[/\\]+/)[0] !== "..";
7421
7440
  }
7422
7441
  function safeRealpath(p) {
7423
7442
  const resolved = path2.resolve(p);
@@ -8124,31 +8143,83 @@ var require_main = __commonJS((exports, module) => {
8124
8143
  module.exports = DotenvModule;
8125
8144
  });
8126
8145
 
8146
+ // src/core/path-access.ts
8147
+ import fs5 from "fs";
8148
+ function errnoOf(error) {
8149
+ const code = error?.code;
8150
+ return typeof code === "string" ? code : undefined;
8151
+ }
8152
+ function classifyPathAccess(target) {
8153
+ try {
8154
+ fs5.statSync(target);
8155
+ } catch (error) {
8156
+ const code = errnoOf(error);
8157
+ if (code === "ENOENT" || code === "ENOTDIR")
8158
+ return { access: "absent" };
8159
+ return { access: "inaccessible", ...code ? { code } : {} };
8160
+ }
8161
+ try {
8162
+ fs5.accessSync(target, fs5.constants.R_OK);
8163
+ } catch (error) {
8164
+ const code = errnoOf(error);
8165
+ return { access: "inaccessible", ...code ? { code } : {} };
8166
+ }
8167
+ return { access: "present" };
8168
+ }
8169
+ function isPathAbsent(target) {
8170
+ return classifyPathAccess(target).access === "absent";
8171
+ }
8172
+ function describeInaccessiblePath(target, code) {
8173
+ const parts = [target];
8174
+ if (code)
8175
+ parts.push(`(${code})`);
8176
+ try {
8177
+ const stat = fs5.statSync(target);
8178
+ parts.push(`mode ${(stat.mode & 511).toString(8).padStart(3, "0")}, owner uid ${stat.uid}`);
8179
+ } catch {
8180
+ const parent = target.slice(0, Math.max(0, target.lastIndexOf("/")));
8181
+ if (parent) {
8182
+ try {
8183
+ const stat = fs5.statSync(parent);
8184
+ parts.push(`parent ${parent} is mode ${(stat.mode & 511).toString(8).padStart(3, "0")}, owner uid ${stat.uid}`);
8185
+ } catch {}
8186
+ }
8187
+ }
8188
+ const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
8189
+ if (uid !== undefined)
8190
+ parts.push(`running as uid ${uid}`);
8191
+ return parts.join("; ");
8192
+ }
8193
+ var init_path_access = () => {};
8194
+
8127
8195
  // src/core/migration-operation.ts
8128
8196
  import crypto2 from "crypto";
8129
- import fs5 from "fs";
8130
- import path8 from "path";
8197
+ import path9 from "path";
8131
8198
  function installationId() {
8132
- return crypto2.createHash("sha256").update(path8.resolve(path8.dirname(getConfigPath()))).update("\x00").update(path8.resolve(getDataDir())).digest("hex").slice(0, 24);
8199
+ return crypto2.createHash("sha256").update(path9.resolve(path9.dirname(getConfigPath()))).update("\x00").update(path9.resolve(getDataDir())).digest("hex").slice(0, 24);
8133
8200
  }
8134
8201
  function getMigrationOperationRoot() {
8135
- return path8.join(getDataDir(), "backups", "migrations", installationId());
8202
+ return path9.join(getDataDir(), "backups", "migrations", installationId());
8136
8203
  }
8137
8204
  function getMigrationRestoreJournalPath() {
8138
- return path8.join(getMigrationOperationRoot(), "restore-active.json");
8205
+ return path9.join(getMigrationOperationRoot(), "restore-active.json");
8139
8206
  }
8140
8207
  function getMigrationApplyJournalPath() {
8141
- return path8.join(getMigrationOperationRoot(), "apply-active.json");
8208
+ return path9.join(getMigrationOperationRoot(), "apply-active.json");
8142
8209
  }
8143
8210
  function getMigrationGeneratedConfigPath() {
8144
- return path8.join(getMigrationOperationRoot(), "generated-config.json");
8211
+ return path9.join(getMigrationOperationRoot(), "generated-config.json");
8145
8212
  }
8146
8213
  function assertNoPendingMigrationOperation() {
8147
8214
  for (const [kind, journalPath] of [
8148
8215
  ["restore", getMigrationRestoreJournalPath()],
8149
8216
  ["migration apply", getMigrationApplyJournalPath()]
8150
8217
  ]) {
8151
- if (fs5.existsSync(journalPath)) {
8218
+ const { access, code } = classifyPathAccess(journalPath);
8219
+ if (access === "inaccessible") {
8220
+ throw new ConfigError(`Cannot determine whether an AKM ${kind} recovery is pending: ${describeInaccessiblePath(journalPath, code)}. ` + "Refusing canonical config/database access \u2014 proceeding could write on top of a half-applied migration.", "DATA_DIR_UNREADABLE");
8221
+ }
8222
+ if (access === "present") {
8152
8223
  throw new ConfigError(`AKM ${kind} recovery is pending at ${journalPath}; refusing canonical config/database access until recovery completes.`, "INVALID_CONFIG_FILE");
8153
8224
  }
8154
8225
  }
@@ -8159,6 +8230,7 @@ function assertNoPendingMigrationOperation() {
8159
8230
  var afterPendingCheckHook;
8160
8231
  var init_migration_operation = __esm(() => {
8161
8232
  init_errors();
8233
+ init_path_access();
8162
8234
  init_paths();
8163
8235
  });
8164
8236
 
@@ -8171,11 +8243,11 @@ function selectProvider() {
8171
8243
  }
8172
8244
  return provider;
8173
8245
  }
8174
- function openDatabase(path9, opts) {
8175
- return selectProvider().open(path9, opts);
8246
+ function openDatabase(path10, opts) {
8247
+ return selectProvider().open(path10, opts);
8176
8248
  }
8177
- function openDatabaseFinalizing(path9, opts) {
8178
- const db = selectProvider().open(path9, opts);
8249
+ function openDatabaseFinalizing(path10, opts) {
8250
+ const db = selectProvider().open(path10, opts);
8179
8251
  const tracked = new Set;
8180
8252
  const origPrepare = db.prepare.bind(db);
8181
8253
  const origClose = db.close.bind(db);
@@ -8195,9 +8267,9 @@ function openDatabaseFinalizing(path9, opts) {
8195
8267
  };
8196
8268
  return db;
8197
8269
  }
8198
- function openBunDatabase(path9, opts) {
8270
+ function openBunDatabase(path10, opts) {
8199
8271
  const { Database: BunDatabase } = loadBunSqlite();
8200
- const db = opts ? new BunDatabase(path9, bunOptions(opts)) : new BunDatabase(path9);
8272
+ const db = opts ? new BunDatabase(path10, bunOptions(opts)) : new BunDatabase(path10);
8201
8273
  return db;
8202
8274
  }
8203
8275
  function bunOptions(opts) {
@@ -8223,7 +8295,11 @@ function loadBetterSqlite3() {
8223
8295
  mod = nodeRequire2("better-sqlite3");
8224
8296
  } catch (err) {
8225
8297
  throw new Error(`akm could not load 'better-sqlite3', the SQLite driver it needs on Node.js.
8226
- ` + ` \u2022 Reinstall akm with a working C/C++ build toolchain so its optional
8298
+ ` + ` \u2022 If the error below says the module was compiled against a DIFFERENT Node.js
8299
+ ` + ` version, you upgraded Node after installing akm. Reinstall akm (or run
8300
+ ` + " `npm rebuild better-sqlite3` in its install directory) so the binding is\n" + ` rebuilt for the Node you are now running. This is the common case after a
8301
+ ` + ` Node major upgrade, and it is NOT a broken install.
8302
+ ` + ` \u2022 Otherwise, reinstall akm with a working C/C++ build toolchain so its optional
8227
8303
  ` + " 'better-sqlite3' native binding rebuilds (a global `npm i -g better-sqlite3`\n" + ` will NOT be resolved \u2014 Node loads it from akm's own node_modules).
8228
8304
  ` + ` \u2022 Or run akm under Bun, which has a built-in SQLite driver and needs no native build.
8229
8305
  ` + ` Underlying load error: ${err instanceof Error ? err.message : String(err)}`);
@@ -8232,14 +8308,14 @@ function loadBetterSqlite3() {
8232
8308
  }
8233
8309
  return betterSqlite3Ctor;
8234
8310
  }
8235
- function openNodeDatabase(path9, opts) {
8311
+ function openNodeDatabase(path10, opts) {
8236
8312
  const BetterSqlite3 = loadBetterSqlite3();
8237
8313
  const options = {};
8238
8314
  if (opts?.readonly !== undefined)
8239
8315
  options.readonly = opts.readonly;
8240
8316
  if (opts?.create === false)
8241
8317
  options.fileMustExist = true;
8242
- const db = opts ? new BetterSqlite3(path9, options) : new BetterSqlite3(path9);
8318
+ const db = opts ? new BetterSqlite3(path10, options) : new BetterSqlite3(path10);
8243
8319
  return {
8244
8320
  prepare: db.prepare.bind(db),
8245
8321
  exec: db.exec.bind(db),
@@ -8271,7 +8347,7 @@ var init_database = __esm(() => {
8271
8347
  // src/core/file-lock.ts
8272
8348
  import { randomUUID } from "crypto";
8273
8349
  import fs6 from "fs";
8274
- import path9 from "path";
8350
+ import path10 from "path";
8275
8351
  function readLockSnapshot(lockPath) {
8276
8352
  let fd;
8277
8353
  try {
@@ -8296,7 +8372,7 @@ function sameIdentity(left, right) {
8296
8372
  return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs;
8297
8373
  }
8298
8374
  function operationMutexPath(lockPath) {
8299
- return path9.join(path9.dirname(lockPath), `.${path9.basename(lockPath)}.operations.sensitive`);
8375
+ return path10.join(path10.dirname(lockPath), `.${path10.basename(lockPath)}.operations.sensitive`);
8300
8376
  }
8301
8377
  function withLockOperationMutex(lockPath, run) {
8302
8378
  const db = openDatabase(operationMutexPath(lockPath));
@@ -8362,7 +8438,10 @@ function probeLock(lockPath, opts) {
8362
8438
  let snapshot;
8363
8439
  try {
8364
8440
  snapshot = readLockSnapshot(lockPath);
8365
- } catch {
8441
+ } catch (error) {
8442
+ const code = error?.code;
8443
+ if (code === "EACCES" || code === "EPERM")
8444
+ return { state: "inaccessible", code };
8366
8445
  return { state: "stale", reason: "unreadable" };
8367
8446
  }
8368
8447
  if (!snapshot)
@@ -8529,10 +8608,10 @@ var init_warn = () => {};
8529
8608
  import { AsyncLocalStorage } from "async_hooks";
8530
8609
  import { randomUUID as randomUUID2 } from "crypto";
8531
8610
  import fs10 from "fs";
8532
- import path11 from "path";
8611
+ import path12 from "path";
8533
8612
  function tryAcquireMaintenanceBarrier() {
8534
8613
  const lockPath = getMaintenanceBarrierPath();
8535
- fs10.mkdirSync(path11.dirname(lockPath), { recursive: true });
8614
+ fs10.mkdirSync(path12.dirname(lockPath), { recursive: true });
8536
8615
  for (let attempt = 0;attempt < 2; attempt += 1) {
8537
8616
  const ownership = tryAcquireLockSync(lockPath, createLockPayload({ purpose: "maintenance-start" }));
8538
8617
  if (ownership) {
@@ -8583,9 +8662,9 @@ function withMaintenanceStartBarrierSyncWait(run) {
8583
8662
  }
8584
8663
  function acquireMaintenanceActivitySync(name) {
8585
8664
  return withMaintenanceStartBarrierSyncWait(() => {
8586
- const directory = path11.join(path11.dirname(getMaintenanceBarrierPath()), "maintenance-activities");
8665
+ const directory = path12.join(path12.dirname(getMaintenanceBarrierPath()), "maintenance-activities");
8587
8666
  fs10.mkdirSync(directory, { recursive: true, mode: 448 });
8588
- const lockPath = path11.join(directory, `${name}-${process.pid}-${randomUUID2()}.lock`);
8667
+ const lockPath = path12.join(directory, `${name}-${process.pid}-${randomUUID2()}.lock`);
8589
8668
  const ownership = tryAcquireLockSync(lockPath, createLockPayload({ purpose: name }));
8590
8669
  if (!ownership) {
8591
8670
  throw new ConfigError(`Could not register AKM maintenance activity at ${lockPath}.`, "INVALID_CONFIG_FILE");
@@ -15636,33 +15715,33 @@ var require_URL = __commonJS((exports, module) => {
15636
15715
  else
15637
15716
  return basepath.substring(0, lastslash + 1) + refpath;
15638
15717
  }
15639
- function remove_dot_segments(path42) {
15640
- if (!path42)
15641
- return path42;
15718
+ function remove_dot_segments(path43) {
15719
+ if (!path43)
15720
+ return path43;
15642
15721
  var output = "";
15643
- while (path42.length > 0) {
15644
- if (path42 === "." || path42 === "..") {
15645
- path42 = "";
15722
+ while (path43.length > 0) {
15723
+ if (path43 === "." || path43 === "..") {
15724
+ path43 = "";
15646
15725
  break;
15647
15726
  }
15648
- var twochars = path42.substring(0, 2);
15649
- var threechars = path42.substring(0, 3);
15650
- var fourchars = path42.substring(0, 4);
15727
+ var twochars = path43.substring(0, 2);
15728
+ var threechars = path43.substring(0, 3);
15729
+ var fourchars = path43.substring(0, 4);
15651
15730
  if (threechars === "../") {
15652
- path42 = path42.substring(3);
15731
+ path43 = path43.substring(3);
15653
15732
  } else if (twochars === "./") {
15654
- path42 = path42.substring(2);
15733
+ path43 = path43.substring(2);
15655
15734
  } else if (threechars === "/./") {
15656
- path42 = "/" + path42.substring(3);
15657
- } else if (twochars === "/." && path42.length === 2) {
15658
- path42 = "/";
15659
- } else if (fourchars === "/../" || threechars === "/.." && path42.length === 3) {
15660
- path42 = "/" + path42.substring(4);
15735
+ path43 = "/" + path43.substring(3);
15736
+ } else if (twochars === "/." && path43.length === 2) {
15737
+ path43 = "/";
15738
+ } else if (fourchars === "/../" || threechars === "/.." && path43.length === 3) {
15739
+ path43 = "/" + path43.substring(4);
15661
15740
  output = output.replace(/\/?[^\/]*$/, "");
15662
15741
  } else {
15663
- var segment = path42.match(/(\/?([^\/]*))/)[0];
15742
+ var segment = path43.match(/(\/?([^\/]*))/)[0];
15664
15743
  output += segment;
15665
- path42 = path42.substring(segment.length);
15744
+ path43 = path43.substring(segment.length);
15666
15745
  }
15667
15746
  }
15668
15747
  return output;
@@ -26972,9 +27051,9 @@ var init_sqlite_pragmas = __esm(() => {
26972
27051
 
26973
27052
  // src/storage/managed-db.ts
26974
27053
  import fs44 from "fs";
26975
- import path49 from "path";
27054
+ import path50 from "path";
26976
27055
  function openManagedDatabase(spec) {
26977
- const dir = path49.dirname(spec.path);
27056
+ const dir = path50.dirname(spec.path);
26978
27057
  if (spec.create !== false && !fs44.existsSync(dir)) {
26979
27058
  fs44.mkdirSync(dir, { recursive: true });
26980
27059
  }
@@ -27010,14 +27089,14 @@ __export(exports_state_db, {
27010
27089
  getStateDbPath: () => getStateDbPath
27011
27090
  });
27012
27091
  import fs45 from "fs";
27013
- import path50 from "path";
27092
+ import path51 from "path";
27014
27093
  function getStateDbPath() {
27015
- return path50.join(getDataDir(), "state.db");
27094
+ return path51.join(getDataDir(), "state.db");
27016
27095
  }
27017
27096
  function openStateDatabase(dbPath) {
27018
27097
  const canonicalPath = getStateDbPath();
27019
27098
  const resolvedPath = dbPath ?? canonicalPath;
27020
- const isCanonical = path50.resolve(resolvedPath) === path50.resolve(canonicalPath);
27099
+ const isCanonical = path51.resolve(resolvedPath) === path51.resolve(canonicalPath);
27021
27100
  if (isCanonical)
27022
27101
  assertNoPendingMigrationOperation();
27023
27102
  const releaseActivity = isCanonical ? acquireMaintenanceActivitySync("state-db") : undefined;
@@ -27907,9 +27986,9 @@ __export(exports_index_connection, {
27907
27986
  openReadonlyExistingDatabase: () => openReadonlyExistingDatabase,
27908
27987
  openIndexDatabase: () => openIndexDatabase,
27909
27988
  openExistingDatabase: () => openExistingDatabase,
27910
- closeDatabase: () => closeDatabase
27989
+ closeDatabase: () => closeDatabase,
27990
+ assertIndexPathReadable: () => assertIndexPathReadable
27911
27991
  });
27912
- import fs46 from "fs";
27913
27992
  import { createRequire as createRequire4 } from "module";
27914
27993
  function openIndexDatabase(dbPath, options) {
27915
27994
  return openManagedDatabase({
@@ -27937,14 +28016,22 @@ function resolveConfiguredEmbeddingDim() {
27937
28016
  }
27938
28017
  function openExistingDatabase(dbPath) {
27939
28018
  const resolvedPath = dbPath ?? getDbPath();
27940
- if (!fs46.existsSync(resolvedPath)) {
28019
+ assertIndexPathReadable(resolvedPath);
28020
+ if (classifyPathAccess(resolvedPath).access === "absent") {
27941
28021
  throw new Error(`Index database not found at ${resolvedPath}. Run 'akm index' to build it.`);
27942
28022
  }
27943
28023
  return openManagedDatabase({ path: resolvedPath, init: loadVecExtension, create: false });
27944
28024
  }
28025
+ function assertIndexPathReadable(resolvedPath) {
28026
+ const { access, code } = classifyPathAccess(resolvedPath);
28027
+ if (access !== "inaccessible")
28028
+ return;
28029
+ throw new ConfigError(`Index database exists but is not readable: ${describeInaccessiblePath(resolvedPath, code)}.`, "DATA_DIR_UNREADABLE");
28030
+ }
27945
28031
  function openReadonlyExistingDatabase(dbPath) {
27946
28032
  const resolvedPath = dbPath ?? getDbPath();
27947
- if (!fs46.existsSync(resolvedPath))
28033
+ assertIndexPathReadable(resolvedPath);
28034
+ if (classifyPathAccess(resolvedPath).access === "absent")
27948
28035
  return;
27949
28036
  return openDatabase(resolvedPath, { readonly: true, create: false });
27950
28037
  }
@@ -27952,6 +28039,8 @@ function closeDatabase(db) {
27952
28039
  db.close();
27953
28040
  }
27954
28041
  var init_index_connection = __esm(() => {
28042
+ init_errors();
28043
+ init_path_access();
27955
28044
  init_paths();
27956
28045
  init_database();
27957
28046
  init_managed_db();
@@ -27971,12 +28060,11 @@ __export(exports_graph_db, {
27971
28060
  drainExtractionQueue: () => drainExtractionQueue,
27972
28061
  deleteStoredGraph: () => deleteStoredGraph
27973
28062
  });
27974
- import fs47 from "fs";
27975
28063
  function withReadableGraphDb(db, fn) {
27976
28064
  if (db)
27977
28065
  return fn(db);
27978
28066
  const dbPath = getDbPath();
27979
- if (!fs47.existsSync(dbPath))
28067
+ if (isPathAbsent(dbPath))
27980
28068
  throw new Error("GRAPH_DB_MISSING");
27981
28069
  const opened = openExistingDatabase(dbPath);
27982
28070
  try {
@@ -28150,6 +28238,7 @@ function loadGraphFilesOnly(stashPath, db) {
28150
28238
  });
28151
28239
  } catch (err) {
28152
28240
  rethrowIfTestIsolationError(err);
28241
+ rethrowIfDataDirUnreadable(err);
28153
28242
  return [];
28154
28243
  }
28155
28244
  }
@@ -28212,6 +28301,7 @@ function loadStoredGraphMeta(stashPath, db) {
28212
28301
  });
28213
28302
  } catch (err) {
28214
28303
  rethrowIfTestIsolationError(err);
28304
+ rethrowIfDataDirUnreadable(err);
28215
28305
  return null;
28216
28306
  }
28217
28307
  }
@@ -28296,11 +28386,13 @@ function loadStoredGraphSnapshot(stashPath, db) {
28296
28386
  });
28297
28387
  } catch (err) {
28298
28388
  rethrowIfTestIsolationError(err);
28389
+ rethrowIfDataDirUnreadable(err);
28299
28390
  return null;
28300
28391
  }
28301
28392
  }
28302
28393
  var init_graph_db = __esm(() => {
28303
28394
  init_errors();
28395
+ init_path_access();
28304
28396
  init_paths();
28305
28397
  init_index_connection();
28306
28398
  });
@@ -28312,9 +28404,9 @@ __export(exports_migrate_storage, {
28312
28404
  main: () => main,
28313
28405
  MIGRATIONS: () => MIGRATIONS
28314
28406
  });
28315
- import fs48 from "fs";
28407
+ import fs46 from "fs";
28316
28408
  import os7 from "os";
28317
- import path51 from "path";
28409
+ import path52 from "path";
28318
28410
  import readline from "readline";
28319
28411
  function parseFromArg(args) {
28320
28412
  const idx = args.indexOf("--from");
@@ -28328,14 +28420,14 @@ function parseFromArg(args) {
28328
28420
  return val;
28329
28421
  }
28330
28422
  function ensureDir(dir) {
28331
- if (!fs48.existsSync(dir)) {
28332
- fs48.mkdirSync(dir, { recursive: true });
28423
+ if (!fs46.existsSync(dir)) {
28424
+ fs46.mkdirSync(dir, { recursive: true });
28333
28425
  }
28334
28426
  }
28335
28427
  function copyAndVerify(src, dest) {
28336
- fs48.copyFileSync(src, dest);
28337
- const srcStat = fs48.statSync(src);
28338
- const destStat = fs48.statSync(dest);
28428
+ fs46.copyFileSync(src, dest);
28429
+ const srcStat = fs46.statSync(src);
28430
+ const destStat = fs46.statSync(dest);
28339
28431
  return destStat.size === srcStat.size;
28340
28432
  }
28341
28433
  function copyTree(src, dest, opts) {
@@ -28343,9 +28435,9 @@ function copyTree(src, dest, opts) {
28343
28435
  let skipped = 0;
28344
28436
  let failed = 0;
28345
28437
  ensureDir(dest);
28346
- for (const entry of fs48.readdirSync(src, { withFileTypes: true })) {
28347
- const srcEntry = path51.join(src, entry.name);
28348
- const destEntry = path51.join(dest, entry.name);
28438
+ for (const entry of fs46.readdirSync(src, { withFileTypes: true })) {
28439
+ const srcEntry = path52.join(src, entry.name);
28440
+ const destEntry = path52.join(dest, entry.name);
28349
28441
  if (entry.isDirectory()) {
28350
28442
  const sub = copyTree(srcEntry, destEntry, opts);
28351
28443
  copied += sub.copied;
@@ -28353,7 +28445,7 @@ function copyTree(src, dest, opts) {
28353
28445
  failed += sub.failed;
28354
28446
  continue;
28355
28447
  }
28356
- if (!opts.clobber && fs48.existsSync(destEntry)) {
28448
+ if (!opts.clobber && fs46.existsSync(destEntry)) {
28357
28449
  skipped++;
28358
28450
  continue;
28359
28451
  }
@@ -28371,9 +28463,9 @@ function copyTree(src, dest, opts) {
28371
28463
  }
28372
28464
  function countFilesRecursive(dir) {
28373
28465
  let count = 0;
28374
- for (const entry of fs48.readdirSync(dir, { withFileTypes: true })) {
28466
+ for (const entry of fs46.readdirSync(dir, { withFileTypes: true })) {
28375
28467
  if (entry.isDirectory()) {
28376
- count += countFilesRecursive(path51.join(dir, entry.name));
28468
+ count += countFilesRecursive(path52.join(dir, entry.name));
28377
28469
  } else {
28378
28470
  count++;
28379
28471
  }
@@ -28386,12 +28478,12 @@ async function runSteps(steps, ctx) {
28386
28478
  }
28387
28479
  }
28388
28480
  function migrateDb(ctx, filename) {
28389
- const src = path51.join(ctx.paths.cacheDir, filename);
28390
- const dest = path51.join(ctx.paths.dataDir, filename);
28391
- if (!fs48.existsSync(src)) {
28481
+ const src = path52.join(ctx.paths.cacheDir, filename);
28482
+ const dest = path52.join(ctx.paths.dataDir, filename);
28483
+ if (!fs46.existsSync(src)) {
28392
28484
  return { name: filename, status: "skipped", detail: "source not found" };
28393
28485
  }
28394
- if (fs48.existsSync(dest)) {
28486
+ if (fs46.existsSync(dest)) {
28395
28487
  return { name: filename, status: "skipped", detail: "destination already exists" };
28396
28488
  }
28397
28489
  if (ctx.dryRun) {
@@ -28409,8 +28501,8 @@ function migrateDb(ctx, filename) {
28409
28501
  return { name: filename, status: "failed", detail: `size mismatch after copy: ${src} \u2192 ${dest}` };
28410
28502
  }
28411
28503
  async function migrateEventsJsonl(ctx) {
28412
- const src = path51.join(ctx.paths.cacheDir, "events.jsonl");
28413
- if (!fs48.existsSync(src)) {
28504
+ const src = path52.join(ctx.paths.cacheDir, "events.jsonl");
28505
+ if (!fs46.existsSync(src)) {
28414
28506
  return { name: "events.jsonl \u2192 state.db", status: "skipped", detail: "source not found" };
28415
28507
  }
28416
28508
  if (ctx.dryRun) {
@@ -28455,13 +28547,13 @@ async function migrateEventsJsonl(ctx) {
28455
28547
  }
28456
28548
  }
28457
28549
  function migrateTaskHistory(ctx) {
28458
- const src = path51.join(ctx.paths.cacheDir, "tasks", "history");
28459
- const dest = path51.join(ctx.paths.stateDir, "tasks", "history");
28460
- if (!fs48.existsSync(src)) {
28550
+ const src = path52.join(ctx.paths.cacheDir, "tasks", "history");
28551
+ const dest = path52.join(ctx.paths.stateDir, "tasks", "history");
28552
+ if (!fs46.existsSync(src)) {
28461
28553
  return { name: "tasks/history/", status: "skipped", detail: "source directory not found" };
28462
28554
  }
28463
28555
  try {
28464
- const files = fs48.readdirSync(src).filter((f) => f.endsWith(".jsonl"));
28556
+ const files = fs46.readdirSync(src).filter((f) => f.endsWith(".jsonl"));
28465
28557
  if (files.length === 0) {
28466
28558
  return { name: "tasks/history/", status: "skipped", detail: "no *.jsonl files found in source directory" };
28467
28559
  }
@@ -28476,8 +28568,8 @@ function migrateTaskHistory(ctx) {
28476
28568
  let copied = 0;
28477
28569
  let failed = 0;
28478
28570
  for (const file of files) {
28479
- const srcFile = path51.join(src, file);
28480
- const destFile = path51.join(dest, file);
28571
+ const srcFile = path52.join(src, file);
28572
+ const destFile = path52.join(dest, file);
28481
28573
  try {
28482
28574
  if (copyAndVerify(srcFile, destFile)) {
28483
28575
  copied++;
@@ -28506,12 +28598,12 @@ function migrateTaskHistory(ctx) {
28506
28598
  }
28507
28599
  }
28508
28600
  function migrateLockfile(ctx) {
28509
- const src = path51.join(ctx.paths.configDir, "akm.lock");
28510
- const dest = path51.join(ctx.paths.dataDir, "akm.lock");
28511
- if (!fs48.existsSync(src)) {
28601
+ const src = path52.join(ctx.paths.configDir, "akm.lock");
28602
+ const dest = path52.join(ctx.paths.dataDir, "akm.lock");
28603
+ if (!fs46.existsSync(src)) {
28512
28604
  return { name: "akm.lock", status: "skipped", detail: "source not found" };
28513
28605
  }
28514
- if (fs48.existsSync(dest)) {
28606
+ if (fs46.existsSync(dest)) {
28515
28607
  return { name: "akm.lock", status: "skipped", detail: "destination already exists" };
28516
28608
  }
28517
28609
  if (ctx.dryRun) {
@@ -28531,12 +28623,12 @@ function migrateLockfile(ctx) {
28531
28623
  return { name: "akm.lock", status: "failed", detail: `size mismatch after copy: ${src} \u2192 ${dest}` };
28532
28624
  }
28533
28625
  function migrateConfigBackups(ctx) {
28534
- const src = path51.join(ctx.paths.cacheDir, "config-backups");
28535
- const dest = path51.join(ctx.paths.dataDir, "config-backups");
28536
- if (!fs48.existsSync(src)) {
28626
+ const src = path52.join(ctx.paths.cacheDir, "config-backups");
28627
+ const dest = path52.join(ctx.paths.dataDir, "config-backups");
28628
+ if (!fs46.existsSync(src)) {
28537
28629
  return { name: "config-backups/", status: "skipped", detail: "source directory not found" };
28538
28630
  }
28539
- if (fs48.existsSync(dest)) {
28631
+ if (fs46.existsSync(dest)) {
28540
28632
  return { name: "config-backups/", status: "skipped", detail: "destination already exists" };
28541
28633
  }
28542
28634
  if (ctx.dryRun) {
@@ -28553,7 +28645,7 @@ function migrateConfigBackups(ctx) {
28553
28645
  try {
28554
28646
  const srcCount = countFilesRecursive(src);
28555
28647
  const { copied, failed } = copyTree(src, dest, { clobber: true });
28556
- const destCount = fs48.existsSync(dest) ? countFilesRecursive(dest) : 0;
28648
+ const destCount = fs46.existsSync(dest) ? countFilesRecursive(dest) : 0;
28557
28649
  if (failed === 0 && destCount === srcCount) {
28558
28650
  return {
28559
28651
  name: "config-backups/",
@@ -28572,11 +28664,11 @@ function migrateConfigBackups(ctx) {
28572
28664
  }
28573
28665
  }
28574
28666
  async function migrateTaskHistoryToDb(ctx) {
28575
- const src = path51.join(ctx.paths.cacheDir, "tasks", "history");
28576
- if (!fs48.existsSync(src)) {
28667
+ const src = path52.join(ctx.paths.cacheDir, "tasks", "history");
28668
+ if (!fs46.existsSync(src)) {
28577
28669
  return { name: "tasks/history/ \u2192 state.db", status: "skipped", detail: "source directory not found" };
28578
28670
  }
28579
- const files = fs48.readdirSync(src).filter((f) => f.endsWith(".jsonl"));
28671
+ const files = fs46.readdirSync(src).filter((f) => f.endsWith(".jsonl"));
28580
28672
  if (files.length === 0) {
28581
28673
  return {
28582
28674
  name: "tasks/history/ \u2192 state.db",
@@ -28600,8 +28692,8 @@ async function migrateTaskHistoryToDb(ctx) {
28600
28692
  let failed = 0;
28601
28693
  try {
28602
28694
  for (const file of files) {
28603
- const filePath = path51.join(src, file);
28604
- const text2 = fs48.readFileSync(filePath, "utf8");
28695
+ const filePath = path52.join(src, file);
28696
+ const text2 = fs46.readFileSync(filePath, "utf8");
28605
28697
  const lines = text2.split(`
28606
28698
  `).filter((l) => l.trim().length > 0);
28607
28699
  db.exec("BEGIN");
@@ -28660,15 +28752,15 @@ async function migrateTaskHistoryToDb(ctx) {
28660
28752
  }
28661
28753
  }
28662
28754
  function noteRegistryIndexCache(ctx) {
28663
- const src = path51.join(ctx.paths.cacheDir, "registry-index");
28664
- if (!fs48.existsSync(src)) {
28755
+ const src = path52.join(ctx.paths.cacheDir, "registry-index");
28756
+ if (!fs46.existsSync(src)) {
28665
28757
  return {
28666
28758
  name: "registry-index/ (note)",
28667
28759
  status: "skipped",
28668
28760
  detail: "no old $CACHE/registry-index/ directory found"
28669
28761
  };
28670
28762
  }
28671
- const legacyFiles = fs48.readdirSync(src).filter((f) => f.endsWith(".json") && !f.startsWith("website-"));
28763
+ const legacyFiles = fs46.readdirSync(src).filter((f) => f.endsWith(".json") && !f.startsWith("website-"));
28672
28764
  if (legacyFiles.length === 0) {
28673
28765
  return { name: "registry-index/ (note)", status: "skipped", detail: "no old *.json cache files found" };
28674
28766
  }
@@ -28678,7 +28770,7 @@ function noteRegistryIndexCache(ctx) {
28678
28770
  These are ignored in v0.9 \u2014 data is now stored in the registry_index_cache` + `
28679
28771
  table in $DATA/index.db and will be rebuilt on next 'akm search --from registry'.
28680
28772
  You may safely delete these files after migration:
28681
- ` + legacyFiles.map((f) => ` ${path51.join(src, f)}`).join(`
28773
+ ` + legacyFiles.map((f) => ` ${path52.join(src, f)}`).join(`
28682
28774
  `));
28683
28775
  }
28684
28776
  return {
@@ -28689,20 +28781,20 @@ function noteRegistryIndexCache(ctx) {
28689
28781
  }
28690
28782
  function legacyGraphCandidatePaths(ctx) {
28691
28783
  return [
28692
- path51.join(ctx.paths.cacheDir, "graph-snapshot.json"),
28693
- path51.join(ctx.paths.dataDir, "graph-snapshot.json"),
28694
- path51.join(ctx.paths.dataDir, "graph-export.json")
28784
+ path52.join(ctx.paths.cacheDir, "graph-snapshot.json"),
28785
+ path52.join(ctx.paths.dataDir, "graph-snapshot.json"),
28786
+ path52.join(ctx.paths.dataDir, "graph-export.json")
28695
28787
  ];
28696
28788
  }
28697
28789
  function findLegacyGraphFile(ctx) {
28698
28790
  for (const candidate of legacyGraphCandidatePaths(ctx)) {
28699
- if (fs48.existsSync(candidate) && fs48.statSync(candidate).isFile())
28791
+ if (fs46.existsSync(candidate) && fs46.statSync(candidate).isFile())
28700
28792
  return candidate;
28701
28793
  }
28702
- const graphDir = path51.join(ctx.paths.cacheDir, "graph");
28703
- if (fs48.existsSync(graphDir) && fs48.statSync(graphDir).isDirectory()) {
28794
+ const graphDir = path52.join(ctx.paths.cacheDir, "graph");
28795
+ if (fs46.existsSync(graphDir) && fs46.statSync(graphDir).isDirectory()) {
28704
28796
  try {
28705
- const json = fs48.readdirSync(graphDir).filter((f) => f.endsWith(".json")).map((f) => path51.join(graphDir, f));
28797
+ const json = fs46.readdirSync(graphDir).filter((f) => f.endsWith(".json")).map((f) => path52.join(graphDir, f));
28706
28798
  const [first] = json;
28707
28799
  if (first)
28708
28800
  return first;
@@ -28739,7 +28831,7 @@ async function migrateGraphFileToDb(ctx) {
28739
28831
  if (!legacyFile) {
28740
28832
  return { name, status: "skipped", detail: "no legacy graph file found" };
28741
28833
  }
28742
- if (!fs48.existsSync(ctx.paths.indexDbPath)) {
28834
+ if (!fs46.existsSync(ctx.paths.indexDbPath)) {
28743
28835
  return {
28744
28836
  name,
28745
28837
  status: "skipped",
@@ -28754,7 +28846,7 @@ async function migrateGraphFileToDb(ctx) {
28754
28846
  };
28755
28847
  }
28756
28848
  try {
28757
- const raw = fs48.readFileSync(legacyFile, "utf8");
28849
+ const raw = fs46.readFileSync(legacyFile, "utf8");
28758
28850
  let parsed;
28759
28851
  try {
28760
28852
  parsed = JSON.parse(raw);
@@ -28832,7 +28924,7 @@ async function migrateGraphFileToDb(ctx) {
28832
28924
  }
28833
28925
  const renamed = `${legacyFile}.migrated`;
28834
28926
  try {
28835
- fs48.renameSync(legacyFile, renamed);
28927
+ fs46.renameSync(legacyFile, renamed);
28836
28928
  } catch (err) {
28837
28929
  const msg = err instanceof Error ? err.message : String(err);
28838
28930
  return {
@@ -28856,9 +28948,9 @@ async function migrateGraphFileToDb(ctx) {
28856
28948
  }
28857
28949
  function resolvePrimaryStashDir(configDirPath) {
28858
28950
  try {
28859
- const cfgPath = path51.join(configDirPath, "config.json");
28860
- if (fs48.existsSync(cfgPath)) {
28861
- const cfg = JSON.parse(fs48.readFileSync(cfgPath, "utf8"));
28951
+ const cfgPath = path52.join(configDirPath, "config.json");
28952
+ if (fs46.existsSync(cfgPath)) {
28953
+ const cfg = JSON.parse(fs46.readFileSync(cfgPath, "utf8"));
28862
28954
  if (typeof cfg.stashDir === "string" && cfg.stashDir.length > 0)
28863
28955
  return cfg.stashDir;
28864
28956
  }
@@ -28867,8 +28959,8 @@ function resolvePrimaryStashDir(configDirPath) {
28867
28959
  }
28868
28960
  function countEnvFilesRecursive(dir) {
28869
28961
  let count = 0;
28870
- for (const entry of fs48.readdirSync(dir, { withFileTypes: true })) {
28871
- const full = path51.join(dir, entry.name);
28962
+ for (const entry of fs46.readdirSync(dir, { withFileTypes: true })) {
28963
+ const full = path52.join(dir, entry.name);
28872
28964
  if (entry.isDirectory()) {
28873
28965
  count += countEnvFilesRecursive(full);
28874
28966
  } else if (entry.name === ".env" || entry.name.endsWith(".env")) {
@@ -28880,13 +28972,13 @@ function countEnvFilesRecursive(dir) {
28880
28972
  function chmodTreeSecure(dir) {
28881
28973
  const isWin = process.platform === "win32";
28882
28974
  try {
28883
- fs48.chmodSync(dir, 448);
28975
+ fs46.chmodSync(dir, 448);
28884
28976
  } catch (err) {
28885
28977
  if (!isWin)
28886
28978
  return { ok: false, detail: `chmod 0700 ${dir} failed: ${err instanceof Error ? err.message : err}` };
28887
28979
  }
28888
- for (const entry of fs48.readdirSync(dir, { withFileTypes: true })) {
28889
- const full = path51.join(dir, entry.name);
28980
+ for (const entry of fs46.readdirSync(dir, { withFileTypes: true })) {
28981
+ const full = path52.join(dir, entry.name);
28890
28982
  if (entry.isDirectory()) {
28891
28983
  const sub = chmodTreeSecure(full);
28892
28984
  if (!sub.ok)
@@ -28894,8 +28986,8 @@ function chmodTreeSecure(dir) {
28894
28986
  continue;
28895
28987
  }
28896
28988
  try {
28897
- fs48.chmodSync(full, 384);
28898
- if (!isWin && (fs48.statSync(full).mode & 511) !== 384) {
28989
+ fs46.chmodSync(full, 384);
28990
+ if (!isWin && (fs46.statSync(full).mode & 511) !== 384) {
28899
28991
  return { ok: false, detail: `mode verification failed for ${full}` };
28900
28992
  }
28901
28993
  } catch (err) {
@@ -28908,13 +29000,13 @@ function chmodTreeSecure(dir) {
28908
29000
  async function migrateVaultsToEnv(ctx) {
28909
29001
  const name = "vaults/ \u2192 env/";
28910
29002
  const stashDir = resolvePrimaryStashDir(ctx.paths.configDir);
28911
- const vaultsDir = path51.join(stashDir, "vaults");
28912
- const envDir = path51.join(stashDir, "env");
28913
- const marker = path51.join(vaultsDir, MIGRATED_MARKER);
28914
- if (!fs48.existsSync(vaultsDir)) {
29003
+ const vaultsDir = path52.join(stashDir, "vaults");
29004
+ const envDir = path52.join(stashDir, "env");
29005
+ const marker = path52.join(vaultsDir, MIGRATED_MARKER);
29006
+ if (!fs46.existsSync(vaultsDir)) {
28915
29007
  return { name, status: "skipped", detail: `no vaults/ directory under ${stashDir}` };
28916
29008
  }
28917
- if (fs48.existsSync(marker)) {
29009
+ if (fs46.existsSync(marker)) {
28918
29010
  return { name, status: "skipped", detail: "already migrated (.migrated marker present)" };
28919
29011
  }
28920
29012
  const vaultEnvCount = countEnvFilesRecursive(vaultsDir);
@@ -28945,7 +29037,7 @@ async function migrateVaultsToEnv(ctx) {
28945
29037
  detail: `post-copy count ${envEnvCount} < source ${vaultEnvCount}; no marker written`
28946
29038
  };
28947
29039
  }
28948
- fs48.writeFileSync(marker, `migrated vaults/ -> env/ at ${new Date().toISOString()}
29040
+ fs46.writeFileSync(marker, `migrated vaults/ -> env/ at ${new Date().toISOString()}
28949
29041
  `, { mode: 384 });
28950
29042
  return {
28951
29043
  name,
@@ -29039,12 +29131,12 @@ task_history: task_id PK).
29039
29131
  Migration complete. No errors.`);
29040
29132
  console.log(`
29041
29133
  Old files at the original locations are safe to delete manually after verifying akm works:
29042
- ${path51.join(cacheDir, "index.db")}
29043
- ${path51.join(cacheDir, "workflow.db")}
29044
- ${path51.join(cacheDir, "events.jsonl")}
29045
- ${path51.join(cacheDir, "tasks", "history")}
29046
- ${path51.join(cacheDir, "config-backups")}
29047
- ${path51.join(configDir, "akm.lock")}
29134
+ ${path52.join(cacheDir, "index.db")}
29135
+ ${path52.join(cacheDir, "workflow.db")}
29136
+ ${path52.join(cacheDir, "events.jsonl")}
29137
+ ${path52.join(cacheDir, "tasks", "history")}
29138
+ ${path52.join(cacheDir, "config-backups")}
29139
+ ${path52.join(configDir, "akm.lock")}
29048
29140
 
29049
29141
  Next step \u2014 repopulate graph data (if migrating from 0.7):
29050
29142
  The 0.8.0 graph schema redesign (DB_VERSION 12 \u2192 13) rebuilds the graph
@@ -29138,12 +29230,12 @@ async function main(args = process.argv.slice(2)) {
29138
29230
  var MIGRATED_MARKER = ".migrated", dataDir, stateDir, cacheDir, configDir, stateDbPath, indexDbPath, PATHS, versionReports, v07To08Migration, v08To09Migration, MIGRATIONS;
29139
29231
  var init_migrate_storage = __esm(() => {
29140
29232
  init_paths();
29141
- dataDir = process.env.AKM_DATA_DIR?.trim() ?? (process.env.XDG_DATA_HOME?.trim() ? path51.join(process.env.XDG_DATA_HOME.trim(), "akm") : path51.join(os7.homedir(), ".local", "share", "akm"));
29142
- stateDir = process.env.AKM_STATE_DIR?.trim() ?? (process.env.XDG_STATE_HOME?.trim() ? path51.join(process.env.XDG_STATE_HOME.trim(), "akm") : path51.join(os7.homedir(), ".local", "state", "akm"));
29233
+ dataDir = process.env.AKM_DATA_DIR?.trim() ?? (process.env.XDG_DATA_HOME?.trim() ? path52.join(process.env.XDG_DATA_HOME.trim(), "akm") : path52.join(os7.homedir(), ".local", "share", "akm"));
29234
+ stateDir = process.env.AKM_STATE_DIR?.trim() ?? (process.env.XDG_STATE_HOME?.trim() ? path52.join(process.env.XDG_STATE_HOME.trim(), "akm") : path52.join(os7.homedir(), ".local", "state", "akm"));
29143
29235
  cacheDir = getCacheDir();
29144
29236
  configDir = getConfigDir();
29145
- stateDbPath = path51.join(dataDir, "state.db");
29146
- indexDbPath = path51.join(dataDir, "index.db");
29237
+ stateDbPath = path52.join(dataDir, "state.db");
29238
+ indexDbPath = path52.join(dataDir, "index.db");
29147
29239
  PATHS = {
29148
29240
  cacheDir,
29149
29241
  configDir,
@@ -29158,15 +29250,15 @@ var init_migrate_storage = __esm(() => {
29158
29250
  sourceVersion: "0.7",
29159
29251
  isNeeded: (paths) => {
29160
29252
  const candidates = [
29161
- path51.join(paths.cacheDir, "index.db"),
29162
- path51.join(paths.cacheDir, "workflow.db"),
29163
- path51.join(paths.cacheDir, "events.jsonl"),
29164
- path51.join(paths.cacheDir, "tasks", "history"),
29165
- path51.join(paths.configDir, "akm.lock"),
29166
- path51.join(paths.cacheDir, "config-backups"),
29167
- path51.join(paths.cacheDir, "registry-index")
29253
+ path52.join(paths.cacheDir, "index.db"),
29254
+ path52.join(paths.cacheDir, "workflow.db"),
29255
+ path52.join(paths.cacheDir, "events.jsonl"),
29256
+ path52.join(paths.cacheDir, "tasks", "history"),
29257
+ path52.join(paths.configDir, "akm.lock"),
29258
+ path52.join(paths.cacheDir, "config-backups"),
29259
+ path52.join(paths.cacheDir, "registry-index")
29168
29260
  ];
29169
- return candidates.some((p) => fs48.existsSync(p));
29261
+ return candidates.some((p) => fs46.existsSync(p));
29170
29262
  },
29171
29263
  steps: [
29172
29264
  { id: "index-db", title: "index.db", run: (ctx) => migrateDb(ctx, "index.db") },
@@ -30641,9 +30733,12 @@ function glyphFor(fixed) {
30641
30733
  return { glyph: "\u2713", severityRank: 2 };
30642
30734
  return { glyph: "\u26A0", severityRank: 1 };
30643
30735
  }
30736
+ function locationOf(issue) {
30737
+ return typeof issue.line === "number" ? `${issue.file}:${issue.line}` : issue.file;
30738
+ }
30644
30739
  function issueEntry(issue) {
30645
30740
  const { glyph, severityRank } = glyphFor(issue.fixed);
30646
- return { severityRank, glyph, headline: `${issue.file} [${issue.issue}] ${issue.detail}` };
30741
+ return { severityRank, glyph, headline: `${locationOf(issue)} [${issue.issue}] ${issue.detail}` };
30647
30742
  }
30648
30743
  function renderIssueSection(title, issues) {
30649
30744
  if (issues.length === 0)
@@ -30655,12 +30750,14 @@ function formatLintPlain(r) {
30655
30750
  return null;
30656
30751
  const fixed = Array.isArray(r.fixed) ? r.fixed : [];
30657
30752
  const flagged = Array.isArray(r.flagged) ? r.flagged : [];
30753
+ const warnings = Array.isArray(r.warnings) ? r.warnings : [];
30658
30754
  const summary = r.summary;
30659
30755
  const lines = [];
30660
30756
  if (typeof r.ok === "boolean")
30661
30757
  lines.push(`ok: ${r.ok}`);
30662
- lines.push(`summary: fixed=${summary?.fixed ?? fixed.length} flagged=${summary?.flagged ?? flagged.length}`);
30758
+ lines.push(`summary: fixed=${summary?.fixed ?? fixed.length} flagged=${summary?.flagged ?? flagged.length}` + ` warnings=${summary?.warnings ?? warnings.length}`);
30663
30759
  lines.push("", ...renderIssueSection("flagged", flagged));
30760
+ lines.push("", ...renderIssueSection("warnings", warnings));
30664
30761
  lines.push("", ...renderIssueSection("fixed", fixed));
30665
30762
  return lines.join(`
30666
30763
  `).trim();
@@ -31758,10 +31855,10 @@ init_errors();
31758
31855
  import { randomUUID as randomUUID5 } from "crypto";
31759
31856
  import fs43 from "fs";
31760
31857
  import os6 from "os";
31761
- import path48 from "path";
31858
+ import path49 from "path";
31762
31859
 
31763
31860
  // src/core/adapter/adapters/akm-lint.ts
31764
- import path7 from "path";
31861
+ import path8 from "path";
31765
31862
 
31766
31863
  // src/commands/env/env.ts
31767
31864
  import fs3 from "fs";
@@ -31854,8 +31951,48 @@ function isDangerousEnvKey(key) {
31854
31951
  return false;
31855
31952
  }
31856
31953
 
31954
+ // src/workflows/resource-limits.ts
31955
+ var WORKFLOW_MAX_PLAN_BYTES = 2 * 1024 * 1024;
31956
+ var WORKFLOW_MAX_SOURCE_BYTES = 1024 * 1024;
31957
+ var WORKFLOW_MAX_STEPS = 256;
31958
+ var WORKFLOW_MAX_PARAMS = 128;
31959
+ var WORKFLOW_MAX_ROUTE_BRANCHES = 256;
31960
+ var WORKFLOW_MAX_INSTRUCTION_BYTES = 256 * 1024;
31961
+ var WORKFLOW_MAX_SCHEMA_BYTES = 256 * 1024;
31962
+ var WORKFLOW_MAX_EXTRA_PARAMS_BYTES = 64 * 1024;
31963
+ var WORKFLOW_MAX_MAP_EXPANSION = 1e4;
31964
+ var WORKFLOW_MAX_INPUTS = 64;
31965
+ var WORKFLOW_MAX_CONCURRENCY = 64;
31966
+ var WORKFLOW_MAX_GATE_LOOPS = 100;
31967
+ var WORKFLOW_MAX_RETRIES = 100;
31968
+ var WORKFLOW_MAX_TIMEOUT_MS = 2 ** 31 - 1;
31969
+ var WORKFLOW_ENGINE_NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
31970
+ var WORKFLOW_MAX_ENGINE_NAME_LENGTH = 63;
31971
+ var WORKFLOW_MAX_EXEC_ARGV = 64;
31972
+ var WORKFLOW_MAX_EXEC_ARG_BYTES = 4096;
31973
+ var WORKFLOW_MAX_EXEC_CWD_LENGTH = 1024;
31974
+ var WORKFLOW_MAX_EXEC_PASS_ENV = 32;
31975
+ var WORKFLOW_ENV_VAR_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
31976
+ var WORKFLOW_MAX_EXEC_OUTPUT_BYTES = 8 * 1024 * 1024;
31977
+ var EXEC_CONTEXT_LIMITS_POSIX = {
31978
+ perVarBytes: 96 * 1024,
31979
+ totalBytes: 128 * 1024,
31980
+ source: "Linux caps one argv/environ string at MAX_ARG_STRLEN (32 pages = 131 072 bytes) and macOS caps argv+environ at ARG_MAX (256 KiB)"
31981
+ };
31982
+ var WORKFLOW_MAX_EVIDENCE_JSON_BYTES = 1024 * 1024;
31983
+ function utf8Bytes(value) {
31984
+ return Buffer.byteLength(value, "utf8");
31985
+ }
31986
+ function jsonBytes(value) {
31987
+ return utf8Bytes(JSON.stringify(value));
31988
+ }
31989
+
31857
31990
  // src/tasks/schema.ts
31858
31991
  var TASK_SCHEMA_VERSION = 2;
31992
+ var TASK_EXTENSION = ".yml";
31993
+ var TASK_NEAR_MISS_EXTENSION = ".yaml";
31994
+ var TASK_MAX_TIMEOUT_MS = WORKFLOW_MAX_TIMEOUT_MS;
31995
+ var TASK_MAX_REDACT_NAMES = WORKFLOW_MAX_EXEC_PASS_ENV;
31859
31996
  function taskFieldProblems(data) {
31860
31997
  const problems = [];
31861
31998
  if (data.version !== TASK_SCHEMA_VERSION)
@@ -31866,6 +32003,25 @@ function taskFieldProblems(data) {
31866
32003
  problems.push("enabled (must be a boolean when present)");
31867
32004
  return problems;
31868
32005
  }
32006
+ function parseTaskYaml(raw) {
32007
+ try {
32008
+ const doc = $parse(raw);
32009
+ if (doc && typeof doc === "object" && !Array.isArray(doc))
32010
+ return { ok: true, data: doc };
32011
+ return { ok: true, data: {} };
32012
+ } catch (e) {
32013
+ const message = (e instanceof Error ? e.message : String(e)).split(`
32014
+ `)[0]?.trim() ?? "unknown parse error";
32015
+ return { ok: false, data: {}, error: message };
32016
+ }
32017
+ }
32018
+ function taskYamlParseDetail(error) {
32019
+ return `task YAML does not parse: ${error}`;
32020
+ }
32021
+ function taskExtensionDetail(relPath) {
32022
+ const base = relPath.replace(/\.yaml$/i, "");
32023
+ return `task file uses the ${TASK_NEAR_MISS_EXTENSION} extension; akm recognizes tasks only as ` + `${TASK_EXTENSION}, so this file is never indexed or scheduled \u2014 rename it to ${base}${TASK_EXTENSION}.`;
32024
+ }
31869
32025
 
31870
32026
  // src/workflows/program/expressions.ts
31871
32027
  var GRAMMAR_HINT = "allowed forms: params.<name>, steps.<id>.output(.<ident>|[<int>])*";
@@ -31970,6 +32126,36 @@ function formatReference(expr) {
31970
32126
  }
31971
32127
  }
31972
32128
 
32129
+ // src/workflows/program/schema.ts
32130
+ var PROGRAM_REDUCERS = ["collect", "vote"];
32131
+ var PROGRAM_ON_ERROR = ["fail", "continue"];
32132
+ var PROGRAM_ISOLATION_KINDS = ["none", "worktree"];
32133
+ var RETRY_REASON_SET = {
32134
+ timeout: true,
32135
+ spawn_failed: true,
32136
+ non_zero_exit: true,
32137
+ parse_error: true,
32138
+ cooldown: true,
32139
+ llm_rate_limit: true,
32140
+ llm_content_filter: true,
32141
+ llm_invalid_json: true,
32142
+ content_policy_reject: true,
32143
+ unsupported_type: true,
32144
+ no_change: true,
32145
+ aborted: true
32146
+ };
32147
+ var PROGRAM_RETRY_REASONS = Object.keys(RETRY_REASON_SET);
32148
+ var PROGRAM_STEP_ID_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]*$/;
32149
+ var PROGRAM_PARAM_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
32150
+ function projectExecCore(exec) {
32151
+ return {
32152
+ command: [...exec.command],
32153
+ ...exec.cwd ? { cwd: exec.cwd } : {},
32154
+ ...exec.passEnv && exec.passEnv.length > 0 ? { passEnv: [...exec.passEnv] } : {},
32155
+ ...exec.inheritEnv ? { inheritEnv: true } : {}
32156
+ };
32157
+ }
32158
+
31973
32159
  // src/workflows/ir/compile.ts
31974
32160
  function compileWorkflowPlan(document, title) {
31975
32161
  const errors2 = [];
@@ -32069,6 +32255,7 @@ function compileUnit(unit, id, instructions, defaults, inputs, source) {
32069
32255
  instructions,
32070
32256
  templating: "verbatim",
32071
32257
  ...inputs && inputs.length > 0 ? { inputs: [...inputs] } : {},
32258
+ ...unit?.exec ? { exec: projectExecCore(unit.exec) } : {},
32072
32259
  ...unit?.output !== undefined ? { schema: unit.output } : {},
32073
32260
  ...unit?.retry ? { retry: { max: unit.retry.max, on: [...unit.retry.on] } } : {},
32074
32261
  onError: unit?.onError ?? defaults?.onError ?? "fail",
@@ -32116,6 +32303,14 @@ function collectWorkflowWarnings(document) {
32116
32303
  const warnings = [];
32117
32304
  const declaredParams = document.params ? new Set(Object.keys(document.params)) : undefined;
32118
32305
  for (const step of document.steps) {
32306
+ const maxLoops = step.gate?.maxLoops ?? 1;
32307
+ const execUnit = step.map ? step.map.unit?.exec : step.unit?.exec;
32308
+ if (maxLoops > 1 && execUnit && step.gateRubric?.text.trim()) {
32309
+ warnings.push({
32310
+ line: step.source.start,
32311
+ message: `Step "${step.id}" declares \`gate.max_loops: ${maxLoops}\` on an \`exec\` step \u2014 it runs its command ` + `ONCE. A gate loop re-executes the step so it can address the judge's feedback, and a frozen argv cannot ` + `read that feedback; looping would only repeat the command's side effects. The gate still evaluates and ` + `can still fail the step.`
32312
+ });
32313
+ }
32119
32314
  if ((step.map || step.route === undefined) && step.output === undefined) {
32120
32315
  warnings.push({
32121
32316
  line: step.source.start,
@@ -32147,6 +32342,22 @@ function collectWorkflowWarnings(document) {
32147
32342
  // src/core/asset/frontmatter.ts
32148
32343
  import fs4 from "fs";
32149
32344
 
32345
+ // src/core/write-provenance.ts
32346
+ import path7 from "path";
32347
+ var activeJournals = new Set;
32348
+ function recordWrittenPath(filePath) {
32349
+ if (activeJournals.size === 0 || !filePath)
32350
+ return;
32351
+ let absolute;
32352
+ try {
32353
+ absolute = path7.resolve(filePath);
32354
+ } catch {
32355
+ return;
32356
+ }
32357
+ for (const journal of activeJournals)
32358
+ journal.touched.add(absolute);
32359
+ }
32360
+
32150
32361
  // src/core/asset/asset-serialize.ts
32151
32362
  function serializeFrontmatter(frontmatter) {
32152
32363
  return $stringify(frontmatter).trimEnd();
@@ -32249,6 +32460,7 @@ ${serializeFrontmatter(nextFrontmatter)}
32249
32460
  ---
32250
32461
  ${parsed.content}` : assembleAsset(nextFrontmatter, parsed.content);
32251
32462
  fs4.writeFileSync(filePath, next, "utf8");
32463
+ recordWrittenPath(filePath);
32252
32464
  return true;
32253
32465
  }
32254
32466
  function parseFrontmatterBlock(raw) {
@@ -32300,6 +32512,9 @@ function parseMarkdownToc(content) {
32300
32512
  return { headings, totalLines: lines.length };
32301
32513
  }
32302
32514
 
32515
+ // src/workflows/parser.ts
32516
+ init_common();
32517
+
32303
32518
  // src/core/extra-params.ts
32304
32519
  var EXTRA_PARAMS_PROTECTED_TOP_LEVEL_KEYS = [
32305
32520
  "model",
@@ -32332,10 +32547,10 @@ function validateExtraParams(value) {
32332
32547
  return [{ path: [], message: "must be an object" }];
32333
32548
  }
32334
32549
  const issues = [];
32335
- const visit2 = (entry, path7) => {
32550
+ const visit2 = (entry, path8) => {
32336
32551
  if (Array.isArray(entry)) {
32337
32552
  entry.forEach((child, index) => {
32338
- visit2(child, [...path7, index]);
32553
+ visit2(child, [...path8, index]);
32339
32554
  });
32340
32555
  return;
32341
32556
  }
@@ -32343,13 +32558,13 @@ function validateExtraParams(value) {
32343
32558
  return;
32344
32559
  for (const [key, child] of Object.entries(entry)) {
32345
32560
  const normalized = normalizeExtraParamKey(key);
32346
- if (path7.length === 0 && PROTECTED_TOP_LEVEL_KEYS.has(normalized)) {
32561
+ if (path8.length === 0 && PROTECTED_TOP_LEVEL_KEYS.has(normalized)) {
32347
32562
  issues.push({ path: [key], message: `${key} is protected by AKM` });
32348
32563
  }
32349
32564
  if (CREDENTIAL_KEYS.has(normalized)) {
32350
- issues.push({ path: [...path7, key], message: `${key} cannot carry credentials` });
32565
+ issues.push({ path: [...path8, key], message: `${key} cannot carry credentials` });
32351
32566
  }
32352
- visit2(child, [...path7, key]);
32567
+ visit2(child, [...path8, key]);
32353
32568
  }
32354
32569
  };
32355
32570
  visit2(value, []);
@@ -32360,44 +32575,179 @@ function formatExtraParamsIssue(label, issue) {
32360
32575
  return `${label}${suffix} ${issue.message}`;
32361
32576
  }
32362
32577
 
32363
- // src/workflows/program/schema.ts
32364
- var PROGRAM_REDUCERS = ["collect", "vote"];
32365
- var PROGRAM_ON_ERROR = ["fail", "continue"];
32366
- var PROGRAM_ISOLATION_KINDS = ["none", "worktree"];
32367
- var RETRY_REASON_SET = {
32368
- timeout: true,
32369
- spawn_failed: true,
32370
- non_zero_exit: true,
32371
- parse_error: true,
32372
- cooldown: true,
32373
- llm_rate_limit: true,
32374
- llm_content_filter: true,
32375
- llm_invalid_json: true,
32376
- content_policy_reject: true,
32377
- unsupported_type: true,
32378
- no_change: true,
32379
- aborted: true
32380
- };
32381
- var PROGRAM_RETRY_REASONS = Object.keys(RETRY_REASON_SET);
32382
- var PROGRAM_STEP_ID_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]*$/;
32383
- var PROGRAM_PARAM_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
32384
-
32385
- // src/workflows/resource-limits.ts
32386
- var WORKFLOW_MAX_PLAN_BYTES = 2 * 1024 * 1024;
32387
- var WORKFLOW_MAX_SOURCE_BYTES = 1024 * 1024;
32388
- var WORKFLOW_MAX_STEPS = 256;
32389
- var WORKFLOW_MAX_PARAMS = 128;
32390
- var WORKFLOW_MAX_ROUTE_BRANCHES = 256;
32391
- var WORKFLOW_MAX_INSTRUCTION_BYTES = 256 * 1024;
32392
- var WORKFLOW_MAX_SCHEMA_BYTES = 256 * 1024;
32393
- var WORKFLOW_MAX_EXTRA_PARAMS_BYTES = 64 * 1024;
32394
- var WORKFLOW_MAX_MAP_EXPANSION = 1e4;
32395
- var WORKFLOW_MAX_INPUTS = 64;
32396
- function utf8Bytes(value) {
32397
- return Buffer.byteLength(value, "utf8");
32578
+ // src/core/json-schema.ts
32579
+ var MAX_DEFINITION_DEPTH = 64;
32580
+ var JSON_SCHEMA_SUBSET_SUPPORTED_KEYWORDS = "type, enum, properties, required, items, additionalProperties: false, minItems, maxItems, " + "minLength, maxLength, minimum, maximum, allOf, anyOf, oneOf, not";
32581
+ var KNOWN_TYPE_NAMES = new Set(["string", "number", "integer", "boolean", "object", "array", "null"]);
32582
+ var UNSUPPORTED_KEYWORDS = new Set([
32583
+ "$ref",
32584
+ "$defs",
32585
+ "definitions",
32586
+ "$anchor",
32587
+ "$dynamicRef",
32588
+ "$dynamicAnchor",
32589
+ "if",
32590
+ "then",
32591
+ "else",
32592
+ "const",
32593
+ "pattern",
32594
+ "format",
32595
+ "patternProperties",
32596
+ "propertyNames",
32597
+ "additionalItems",
32598
+ "prefixItems",
32599
+ "contains",
32600
+ "minContains",
32601
+ "maxContains",
32602
+ "uniqueItems",
32603
+ "multipleOf",
32604
+ "exclusiveMinimum",
32605
+ "exclusiveMaximum",
32606
+ "minProperties",
32607
+ "maxProperties",
32608
+ "dependencies",
32609
+ "dependentRequired",
32610
+ "dependentSchemas",
32611
+ "unevaluatedItems",
32612
+ "unevaluatedProperties",
32613
+ "contentEncoding",
32614
+ "contentMediaType",
32615
+ "contentSchema"
32616
+ ]);
32617
+ var UNSUPPORTED_KEYWORD_HINTS = new Map([
32618
+ ["$ref", `inline the referenced schema (the subset resolves no references, so it cannot follow "$ref")`],
32619
+ ["$defs", `inline the definitions at their use sites \u2014 "$ref" is not resolved, so "$defs" can never be reached`],
32620
+ [
32621
+ "definitions",
32622
+ `inline the definitions at their use sites \u2014 "$ref" is not resolved, so "definitions" can never be reached`
32623
+ ],
32624
+ ["const", `use a single-value "enum" (e.g. enum: [pass])`],
32625
+ [
32626
+ "pattern",
32627
+ `use "enum" when the allowed strings can be listed, or "minLength"/"maxLength" for a size bound \u2014 ` + `a regular-expression constraint is not expressible in the subset; check the shape in the step's gate rubric instead`
32628
+ ],
32629
+ [
32630
+ "format",
32631
+ `"format" is annotation-only in JSON Schema 2020-12 and the subset enforces no string-shape keyword \u2014 ` + `use "enum" when the allowed values can be listed, otherwise check the shape in the step's gate rubric`
32632
+ ],
32633
+ ["patternProperties", `declare the properties explicitly under "properties", or drop the constraint`],
32634
+ ["if", `use "anyOf"/"oneOf" to express the alternatives directly`],
32635
+ ["then", `use "anyOf"/"oneOf" to express the alternatives directly`],
32636
+ ["else", `use "anyOf"/"oneOf" to express the alternatives directly`],
32637
+ ["uniqueItems", `drop the constraint, or validate uniqueness in the step's gate rubric`]
32638
+ ]);
32639
+ function checkJsonSchemaDefinition(schema) {
32640
+ const issues = [];
32641
+ checkDefinitionNode(schema, [], issues, 0);
32642
+ return issues;
32398
32643
  }
32399
- function jsonBytes(value) {
32400
- return utf8Bytes(JSON.stringify(value));
32644
+ function pointerFor(path8) {
32645
+ return path8.length === 0 ? "$" : `$.${path8.map(String).join(".")}`;
32646
+ }
32647
+ function pushIssue(issues, path8, keyword, kind, message) {
32648
+ issues.push({ path: [...path8], pointer: pointerFor(path8), keyword, kind, message });
32649
+ }
32650
+ function isPlainObject(value) {
32651
+ return typeof value === "object" && value !== null && !Array.isArray(value);
32652
+ }
32653
+ function checkDefinitionNode(schema, path8, issues, depth) {
32654
+ if (depth > MAX_DEFINITION_DEPTH) {
32655
+ pushIssue(issues, path8, "(depth)", "malformed", `schema nesting exceeds the depth limit of ${MAX_DEFINITION_DEPTH}`);
32656
+ return;
32657
+ }
32658
+ for (const keyword of Object.keys(schema)) {
32659
+ if (UNSUPPORTED_KEYWORDS.has(keyword)) {
32660
+ const hint = UNSUPPORTED_KEYWORD_HINTS.get(keyword);
32661
+ pushIssue(issues, [...path8, keyword], keyword, "unsupported", `keyword "${keyword}" is not enforced by the workflow schema subset \u2014 the schema would silently not ` + `constrain what it looks like it constrains${hint ? `; ${hint}` : ""}`);
32662
+ }
32663
+ }
32664
+ const declared = schema.type;
32665
+ if (declared !== undefined) {
32666
+ const names = Array.isArray(declared) ? declared : [declared];
32667
+ if (names.length === 0) {
32668
+ pushIssue(issues, [...path8, "type"], "type", "malformed", `"type" must name at least one type`);
32669
+ }
32670
+ for (const [index, name] of names.entries()) {
32671
+ const namePath = Array.isArray(declared) ? [...path8, "type", index] : [...path8, "type"];
32672
+ if (typeof name !== "string") {
32673
+ pushIssue(issues, namePath, "type", "malformed", `"type" must be a string or an array of strings`);
32674
+ } else if (!KNOWN_TYPE_NAMES.has(name)) {
32675
+ pushIssue(issues, namePath, "type", "malformed", `unknown type ${JSON.stringify(name)} (valid types: ${[...KNOWN_TYPE_NAMES].join(", ")})`);
32676
+ }
32677
+ }
32678
+ }
32679
+ if (schema.enum !== undefined && (!Array.isArray(schema.enum) || schema.enum.length === 0)) {
32680
+ pushIssue(issues, [...path8, "enum"], "enum", "malformed", `"enum" must be a non-empty array of allowed values`);
32681
+ }
32682
+ for (const keyword of ["allOf", "anyOf", "oneOf"]) {
32683
+ const branches = schema[keyword];
32684
+ if (branches === undefined)
32685
+ continue;
32686
+ if (!Array.isArray(branches) || branches.length === 0) {
32687
+ pushIssue(issues, [...path8, keyword], keyword, "malformed", `"${keyword}" must be a non-empty array of schema objects`);
32688
+ continue;
32689
+ }
32690
+ branches.forEach((branch, index) => {
32691
+ if (isPlainObject(branch)) {
32692
+ checkDefinitionNode(branch, [...path8, keyword, index], issues, depth + 1);
32693
+ } else {
32694
+ pushIssue(issues, [...path8, keyword, index], keyword, "malformed", `"${keyword}[${index}]" must be a schema object`);
32695
+ }
32696
+ });
32697
+ }
32698
+ if (schema.not !== undefined) {
32699
+ if (isPlainObject(schema.not)) {
32700
+ checkDefinitionNode(schema.not, [...path8, "not"], issues, depth + 1);
32701
+ } else {
32702
+ pushIssue(issues, [...path8, "not"], "not", "malformed", `"not" must be a schema object`);
32703
+ }
32704
+ }
32705
+ if (schema.required !== undefined) {
32706
+ if (!Array.isArray(schema.required) || !schema.required.every((key) => typeof key === "string")) {
32707
+ pushIssue(issues, [...path8, "required"], "required", "malformed", `"required" must be an array of property-name strings`);
32708
+ }
32709
+ }
32710
+ if (schema.properties !== undefined) {
32711
+ if (!isPlainObject(schema.properties)) {
32712
+ pushIssue(issues, [...path8, "properties"], "properties", "malformed", `"properties" must be an object mapping property names to schemas`);
32713
+ } else {
32714
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
32715
+ if (isPlainObject(propSchema)) {
32716
+ checkDefinitionNode(propSchema, [...path8, "properties", key], issues, depth + 1);
32717
+ } else {
32718
+ pushIssue(issues, [...path8, "properties", key], "properties", "malformed", `property ${JSON.stringify(key)} must be a schema object`);
32719
+ }
32720
+ }
32721
+ }
32722
+ }
32723
+ if (schema.items !== undefined) {
32724
+ if (isPlainObject(schema.items)) {
32725
+ checkDefinitionNode(schema.items, [...path8, "items"], issues, depth + 1);
32726
+ } else if (Array.isArray(schema.items)) {
32727
+ pushIssue(issues, [...path8, "items"], "items", "unsupported", `tuple-form "items" (an array of schemas) is not enforced by the workflow schema subset \u2014 use a single schema object`);
32728
+ } else {
32729
+ pushIssue(issues, [...path8, "items"], "items", "malformed", `"items" must be a schema object`);
32730
+ }
32731
+ }
32732
+ if (schema.additionalProperties !== undefined && typeof schema.additionalProperties !== "boolean") {
32733
+ if (isPlainObject(schema.additionalProperties)) {
32734
+ pushIssue(issues, [...path8, "additionalProperties"], "additionalProperties", "unsupported", `schema-form "additionalProperties" is not enforced by the workflow schema subset \u2014 only "additionalProperties: false" is`);
32735
+ } else {
32736
+ pushIssue(issues, [...path8, "additionalProperties"], "additionalProperties", "malformed", `"additionalProperties" must be a boolean (only "false" is enforced)`);
32737
+ }
32738
+ }
32739
+ for (const keyword of ["minItems", "maxItems", "minLength", "maxLength"]) {
32740
+ const value = schema[keyword];
32741
+ if (value !== undefined && (typeof value !== "number" || !Number.isInteger(value) || value < 0)) {
32742
+ pushIssue(issues, [...path8, keyword], keyword, "malformed", `"${keyword}" must be a non-negative integer`);
32743
+ }
32744
+ }
32745
+ for (const keyword of ["minimum", "maximum"]) {
32746
+ const value = schema[keyword];
32747
+ if (value !== undefined && (typeof value !== "number" || !Number.isFinite(value))) {
32748
+ pushIssue(issues, [...path8, keyword], keyword, "malformed", `"${keyword}" must be a finite number`);
32749
+ }
32750
+ }
32401
32751
  }
32402
32752
 
32403
32753
  // src/workflows/schema.ts
@@ -32471,7 +32821,9 @@ var TOP_LEVEL_KEYS = [...ENVELOPE_KEYS, ...WORKFLOW_KEYS];
32471
32821
  var DEFAULTS_KEYS = ["engine", "model", "timeout", "on_error", "llm"];
32472
32822
  var BUDGET_KEYS = ["max_tokens", "max_units"];
32473
32823
  var STEP_KEYS = ["id", "unit", "map", "route", "inputs", "output", "gate"];
32474
- var UNIT_KEYS = ["engine", "model", "llm", "timeout", "retry", "on_error", "output", "env", "isolation"];
32824
+ var UNIT_KEYS = ["exec", "engine", "model", "llm", "timeout", "retry", "on_error", "output", "env", "isolation"];
32825
+ var EXEC_KEYS = ["command", "cwd", "pass_env", "inherit_env"];
32826
+ var UNIT_ENGINE_KEYS = ["engine", "model", "llm"];
32475
32827
  var MAP_KEYS = ["over", "concurrency", "reducer", "unit"];
32476
32828
  var ROUTE_KEYS = ["input", "when", "default"];
32477
32829
  var RETRY_KEYS = ["max", "on"];
@@ -32489,7 +32841,7 @@ function parseWorkflow(markdown, source) {
32489
32841
  };
32490
32842
  }
32491
32843
  const errors2 = [];
32492
- const path7 = source.path;
32844
+ const path8 = source.path;
32493
32845
  const lines = markdown.split(/\r?\n/);
32494
32846
  const totalLines = lines.length;
32495
32847
  const fmBlock = parseFrontmatterBlock(markdown);
@@ -32541,7 +32893,7 @@ function parseWorkflow(markdown, source) {
32541
32893
  return 2;
32542
32894
  };
32543
32895
  const ctx = {
32544
- filePath: path7,
32896
+ filePath: path8,
32545
32897
  errors: errors2,
32546
32898
  lineAt,
32547
32899
  lineAtOffset: (offset) => Math.max(1, lineCounter2.linePos(offset).line + lineOffset),
@@ -32553,10 +32905,10 @@ function parseWorkflow(markdown, source) {
32553
32905
  if (range) {
32554
32906
  const start = Math.max(1, lineCounter2.linePos(range[0]).line + lineOffset);
32555
32907
  const end = Math.max(start, lineCounter2.linePos(Math.max(range[0], range[1] - 1)).line + lineOffset);
32556
- return { path: path7, start, end };
32908
+ return { path: path8, start, end };
32557
32909
  }
32558
32910
  }
32559
- return { path: path7, start: frontmatterEndLine, end: frontmatterEndLine };
32911
+ return { path: path8, start: frontmatterEndLine, end: frontmatterEndLine };
32560
32912
  },
32561
32913
  err: (p, message) => errors2.push({ line: lineAt(p), message }),
32562
32914
  errAtLine: (line, message) => errors2.push({ line, message })
@@ -32579,7 +32931,7 @@ function parseWorkflow(markdown, source) {
32579
32931
  const parsedSteps = parseSteps(ctx, root.steps);
32580
32932
  const toc = parseMarkdownToc(markdown);
32581
32933
  const declaredIds = new Set(parsedSteps.map((s) => s.id));
32582
- const { sections, preamble } = bindStepSections(toc.headings, lines, fmBlock.bodyStartLine, totalLines, path7, declaredIds, errors2);
32934
+ const { sections, preamble } = bindStepSections(toc.headings, lines, fmBlock.bodyStartLine, totalLines, path8, declaredIds, errors2);
32583
32935
  const steps = parsedSteps.map((step, index) => {
32584
32936
  const section = sections.get(step.id);
32585
32937
  if (!section) {
@@ -32622,14 +32974,14 @@ function parseWorkflow(markdown, source) {
32622
32974
  ...budget ? { budget } : {},
32623
32975
  steps,
32624
32976
  ...preamble ? { preamble } : {},
32625
- source: { path: path7, lineCount: totalLines }
32977
+ source: { path: path8, lineCount: totalLines }
32626
32978
  };
32627
32979
  runSemanticChecks(draft, root, frontmatterEndLine, errors2);
32628
32980
  if (errors2.length > 0)
32629
32981
  return { ok: false, errors: sortErrors(errors2) };
32630
32982
  return { ok: true, document: draft };
32631
32983
  }
32632
- function bindStepSections(headings, lines, bodyStartLine, totalLines, path7, declaredIds, errors2) {
32984
+ function bindStepSections(headings, lines, bodyStartLine, totalLines, path8, declaredIds, errors2) {
32633
32985
  const sections = new Map;
32634
32986
  const h2s = headings.filter((h) => h.level === 2);
32635
32987
  const firstH2Line = h2s[0]?.line;
@@ -32653,24 +33005,24 @@ function bindStepSections(headings, lines, bodyStartLine, totalLines, path7, dec
32653
33005
  continue;
32654
33006
  }
32655
33007
  const sectionEnd = findNextHeadingAtOrAboveLevel(headings, i, 2, totalLines);
32656
- const gate = findGateSubsection(headings, i, sectionEnd, path7, h.text, errors2);
33008
+ const gate = findGateSubsection(headings, i, sectionEnd, path8, h.text, errors2);
32657
33009
  const instructionsEnd = gate ? gate.headingLine - 1 : sectionEnd;
32658
33010
  const instructionsText = sliceProseLines(lines, h.line + 1, instructionsEnd);
32659
33011
  const section = { headingLine: h.line };
32660
33012
  if (instructionsText) {
32661
- section.instructions = { text: instructionsText, source: { path: path7, start: h.line + 1, end: instructionsEnd } };
33013
+ section.instructions = { text: instructionsText, source: { path: path8, start: h.line + 1, end: instructionsEnd } };
32662
33014
  }
32663
33015
  if (gate) {
32664
33016
  const gateText = sliceProseLines(lines, gate.bodyStart, gate.bodyEnd);
32665
33017
  if (gateText) {
32666
- section.gateRubric = { text: gateText, source: { path: path7, start: gate.bodyStart, end: gate.bodyEnd } };
33018
+ section.gateRubric = { text: gateText, source: { path: path8, start: gate.bodyStart, end: gate.bodyEnd } };
32667
33019
  }
32668
33020
  }
32669
33021
  sections.set(h.text, section);
32670
33022
  }
32671
33023
  return { sections, preamble: preambleRaw || undefined };
32672
33024
  }
32673
- function findGateSubsection(headings, stepHeadingIndex, sectionEnd, path7, stepId, errors2) {
33025
+ function findGateSubsection(headings, stepHeadingIndex, sectionEnd, path8, stepId, errors2) {
32674
33026
  let found;
32675
33027
  for (let j = stepHeadingIndex + 1;j < headings.length; j++) {
32676
33028
  const h = headings[j];
@@ -32752,19 +33104,19 @@ function checkEnvelopeFields(ctx, root, fmEndLine) {
32752
33104
  ctx.err(["stale_after"], `Workflow frontmatter "stale_after" must be a string.`);
32753
33105
  }
32754
33106
  }
32755
- function checkActorStamp(ctx, value, path7, label) {
33107
+ function checkActorStamp(ctx, value, path8, label) {
32756
33108
  if (value === undefined)
32757
33109
  return;
32758
33110
  if (!isPlainRecord(value)) {
32759
- ctx.err(path7, `Workflow frontmatter ${label} must be a mapping with a non-empty "by".`);
33111
+ ctx.err(path8, `Workflow frontmatter ${label} must be a mapping with a non-empty "by".`);
32760
33112
  return;
32761
33113
  }
32762
- checkUnknownKeys(ctx, value, path7, ACTOR_STAMP_KEYS, `${label} actor stamp`);
33114
+ checkUnknownKeys(ctx, value, path8, ACTOR_STAMP_KEYS, `${label} actor stamp`);
32763
33115
  if (typeof value.by !== "string" || value.by.length === 0) {
32764
- ctx.err([...path7, "by"], `Workflow frontmatter ${label} must be a mapping with a non-empty "by".`);
33116
+ ctx.err([...path8, "by"], `Workflow frontmatter ${label} must be a mapping with a non-empty "by".`);
32765
33117
  }
32766
33118
  if (value.at !== undefined && typeof value.at !== "string") {
32767
- ctx.err([...path7, "at"], `Workflow frontmatter ${label} actor stamp "at" must be a string.`);
33119
+ ctx.err([...path8, "at"], `Workflow frontmatter ${label} actor stamp "at" must be a string.`);
32768
33120
  }
32769
33121
  }
32770
33122
  function readTags(ctx, value, fmEndLine) {
@@ -32803,6 +33155,10 @@ function parseParams(ctx, raw) {
32803
33155
  ctx.err(["params", paramName], `Param "${paramName}" must be a JSON Schema object (e.g. { type: string }).`);
32804
33156
  continue;
32805
33157
  }
33158
+ if (jsonBytes(value) > WORKFLOW_MAX_SCHEMA_BYTES) {
33159
+ ctx.err(["params", paramName], `Param "${paramName}" schema exceeds the 256 KiB resource limit.`);
33160
+ }
33161
+ checkSchemaDefinition(ctx, value, ["params", paramName], `Param "${paramName}" schema`);
32806
33162
  params[paramName] = value;
32807
33163
  }
32808
33164
  return Object.keys(params).length > 0 ? params : undefined;
@@ -32810,32 +33166,31 @@ function parseParams(ctx, raw) {
32810
33166
  function parseDefaults(ctx, raw) {
32811
33167
  if (raw === undefined)
32812
33168
  return;
32813
- const path7 = ["defaults"];
33169
+ const path8 = ["defaults"];
32814
33170
  if (!isPlainRecord(raw)) {
32815
- ctx.err(path7, `"defaults" must be a mapping with any of: ${DEFAULTS_KEYS.join(", ")}.`);
33171
+ ctx.err(path8, `"defaults" must be a mapping with any of: ${DEFAULTS_KEYS.join(", ")}.`);
32816
33172
  return;
32817
33173
  }
32818
- checkUnknownKeys(ctx, raw, path7, DEFAULTS_KEYS, `"defaults"`);
33174
+ checkUnknownKeys(ctx, raw, path8, DEFAULTS_KEYS, `"defaults"`);
32819
33175
  const defaults = {};
32820
33176
  if (raw.engine !== undefined) {
32821
- if (typeof raw.engine === "string" && raw.engine.trim() !== "")
32822
- defaults.engine = raw.engine.trim();
32823
- else
32824
- ctx.err([...path7, "engine"], `"defaults.engine" must be a non-empty engine name.`);
33177
+ const engine = parseEngineName(ctx, raw.engine, [...path8, "engine"], `"defaults.engine"`);
33178
+ if (engine !== undefined)
33179
+ defaults.engine = engine;
32825
33180
  }
32826
33181
  if (raw.model !== undefined) {
32827
33182
  if (typeof raw.model === "string" && raw.model.trim() !== "")
32828
33183
  defaults.model = raw.model.trim();
32829
33184
  else
32830
- ctx.err([...path7, "model"], `"defaults.model" must be a non-empty string (a model alias or exact id).`);
33185
+ ctx.err([...path8, "model"], `"defaults.model" must be a non-empty string (a model alias or exact id).`);
32831
33186
  }
32832
- const timeoutMs = parseTimeoutField(ctx, raw.timeout, [...path7, "timeout"], `"defaults.timeout"`);
33187
+ const timeoutMs = parseTimeoutField(ctx, raw.timeout, [...path8, "timeout"], `"defaults.timeout"`);
32833
33188
  if (timeoutMs !== undefined)
32834
33189
  defaults.timeoutMs = timeoutMs;
32835
- const onError = parseEnumField(ctx, raw.on_error, [...path7, "on_error"], `"defaults.on_error"`, PROGRAM_ON_ERROR);
33190
+ const onError = parseEnumField(ctx, raw.on_error, [...path8, "on_error"], `"defaults.on_error"`, PROGRAM_ON_ERROR);
32836
33191
  if (onError !== undefined)
32837
33192
  defaults.onError = onError;
32838
- const llm = parseLlmOverrides(ctx, raw.llm, [...path7, "llm"], `"defaults.llm"`);
33193
+ const llm = parseLlmOverrides(ctx, raw.llm, [...path8, "llm"], `"defaults.llm"`);
32839
33194
  if (llm !== undefined)
32840
33195
  defaults.llm = llm;
32841
33196
  return Object.keys(defaults).length > 0 ? defaults : undefined;
@@ -32843,25 +33198,25 @@ function parseDefaults(ctx, raw) {
32843
33198
  function parseBudget(ctx, raw) {
32844
33199
  if (raw === undefined)
32845
33200
  return;
32846
- const path7 = ["budget"];
33201
+ const path8 = ["budget"];
32847
33202
  if (!isPlainRecord(raw)) {
32848
- ctx.err(path7, `"budget" must be a mapping with any of: ${BUDGET_KEYS.join(", ")}.`);
33203
+ ctx.err(path8, `"budget" must be a mapping with any of: ${BUDGET_KEYS.join(", ")}.`);
32849
33204
  return;
32850
33205
  }
32851
- checkUnknownKeys(ctx, raw, path7, BUDGET_KEYS, `"budget"`);
33206
+ checkUnknownKeys(ctx, raw, path8, BUDGET_KEYS, `"budget"`);
32852
33207
  const budget = {};
32853
33208
  if (raw.max_tokens !== undefined) {
32854
33209
  if (typeof raw.max_tokens === "number" && Number.isInteger(raw.max_tokens) && raw.max_tokens >= 1) {
32855
33210
  budget.maxTokens = raw.max_tokens;
32856
33211
  } else {
32857
- ctx.err([...path7, "max_tokens"], `"budget.max_tokens" must be an integer >= 1.`);
33212
+ ctx.err([...path8, "max_tokens"], `"budget.max_tokens" must be an integer >= 1.`);
32858
33213
  }
32859
33214
  }
32860
33215
  if (raw.max_units !== undefined) {
32861
33216
  if (typeof raw.max_units === "number" && Number.isInteger(raw.max_units) && raw.max_units >= 1 && raw.max_units <= WORKFLOW_MAX_MAP_EXPANSION) {
32862
33217
  budget.maxUnits = raw.max_units;
32863
33218
  } else {
32864
- ctx.err([...path7, "max_units"], `"budget.max_units" must be an integer from 1 through ${WORKFLOW_MAX_MAP_EXPANSION}.`);
33219
+ ctx.err([...path8, "max_units"], `"budget.max_units" must be an integer from 1 through ${WORKFLOW_MAX_MAP_EXPANSION}.`);
32865
33220
  }
32866
33221
  }
32867
33222
  return Object.keys(budget).length > 0 ? budget : undefined;
@@ -32884,49 +33239,49 @@ function parseSteps(ctx, raw) {
32884
33239
  const seenIds = new Map;
32885
33240
  const routeChecks = [];
32886
33241
  raw.forEach((rawStep, index) => {
32887
- const path7 = ["steps", index];
33242
+ const path8 = ["steps", index];
32888
33243
  if (!isPlainRecord(rawStep)) {
32889
- ctx.err(path7, `Step ${index + 1} must be a mapping with an "id".`);
33244
+ ctx.err(path8, `Step ${index + 1} must be a mapping with an "id".`);
32890
33245
  return;
32891
33246
  }
32892
33247
  const label = typeof rawStep.id === "string" && rawStep.id !== "" ? `Step "${rawStep.id}"` : `Step ${index + 1}`;
32893
- checkUnknownKeys(ctx, rawStep, path7, STEP_KEYS, label);
33248
+ checkUnknownKeys(ctx, rawStep, path8, STEP_KEYS, label);
32894
33249
  let id = "";
32895
33250
  if (typeof rawStep.id !== "string" || rawStep.id === "") {
32896
- ctx.err([...path7, "id"], `${label} requires a non-empty string "id".`);
33251
+ ctx.err([...path8, "id"], `${label} requires a non-empty string "id".`);
32897
33252
  } else if (!PROGRAM_STEP_ID_PATTERN.test(rawStep.id)) {
32898
- ctx.err([...path7, "id"], `${label} has an invalid id "${rawStep.id}". A step id cannot be referenced from steps.${rawStep.id}.output ` + `unless it matches [A-Za-z_][A-Za-z0-9_-]* (a letter or underscore first, then letters, digits, ` + `underscores, or dashes; no dots, no leading digit).`);
33253
+ ctx.err([...path8, "id"], `${label} has an invalid id "${rawStep.id}". A step id cannot be referenced from steps.${rawStep.id}.output ` + `unless it matches [A-Za-z_][A-Za-z0-9_-]* (a letter or underscore first, then letters, digits, ` + `underscores, or dashes; no dots, no leading digit).`);
32899
33254
  } else {
32900
33255
  id = rawStep.id;
32901
33256
  const firstIndex = seenIds.get(id);
32902
33257
  if (firstIndex !== undefined) {
32903
- ctx.err([...path7, "id"], `Duplicate step id "${id}" (first used by step ${firstIndex + 1}). Step ids must be unique.`);
33258
+ ctx.err([...path8, "id"], `Duplicate step id "${id}" (first used by step ${firstIndex + 1}). Step ids must be unique.`);
32904
33259
  } else {
32905
33260
  seenIds.set(id, index);
32906
33261
  }
32907
33262
  }
32908
33263
  const declaredKinds = ["map", "route"].filter((kind) => rawStep[kind] !== undefined);
32909
33264
  if (declaredKinds.length > 1) {
32910
- ctx.err(path7, `${label} must declare at most one of "map" or "route" (found ${declaredKinds.join(" + ")}).`);
33265
+ ctx.err(path8, `${label} must declare at most one of "map" or "route" (found ${declaredKinds.join(" + ")}).`);
32911
33266
  }
32912
33267
  const isRoute = rawStep.route !== undefined;
32913
33268
  const isMapStep = rawStep.map !== undefined;
32914
33269
  if (isRoute && rawStep.unit !== undefined) {
32915
- ctx.err(path7, `${label} is a route step and cannot also declare "unit" (route steps dispatch no unit).`);
33270
+ ctx.err(path8, `${label} is a route step and cannot also declare "unit" (route steps dispatch no unit).`);
32916
33271
  }
32917
33272
  if (isMapStep && rawStep.unit !== undefined) {
32918
- ctx.err(path7, `${label} is a map step; the per-item dispatch-override bag belongs at "map.unit", not top-level "unit".`);
33273
+ ctx.err(path8, `${label} is a map step; the per-item dispatch-override bag belongs at "map.unit", not top-level "unit".`);
32919
33274
  }
32920
33275
  if (isRoute && rawStep.inputs !== undefined) {
32921
- ctx.err(path7, `${label} is a route step and cannot declare "inputs" (route steps dispatch no unit).`);
32922
- }
32923
- const unit = rawStep.unit !== undefined && !isRoute && !isMapStep ? parseUnit(ctx, rawStep.unit, [...path7, "unit"], label) : undefined;
32924
- const map = isMapStep ? parseMap(ctx, rawStep.map, [...path7, "map"], label) : undefined;
32925
- const route = isRoute ? parseRoute(ctx, rawStep.route, [...path7, "route"], label, index, routeChecks) : undefined;
32926
- const inputs = !isRoute ? parseInputs(ctx, rawStep.inputs, [...path7, "inputs"], label) : undefined;
32927
- const output = parseSchemaObject(ctx, rawStep.output, [...path7, "output"], `${label} "output"`);
32928
- const gate = rawStep.gate !== undefined ? parseGate(ctx, rawStep.gate, [...path7, "gate"], label) : undefined;
32929
- const step = { id, source: ctx.refAt(path7) };
33276
+ ctx.err(path8, `${label} is a route step and cannot declare "inputs" (route steps dispatch no unit).`);
33277
+ }
33278
+ const unit = rawStep.unit !== undefined && !isRoute && !isMapStep ? parseUnit(ctx, rawStep.unit, [...path8, "unit"], label) : undefined;
33279
+ const map = isMapStep ? parseMap(ctx, rawStep.map, [...path8, "map"], label) : undefined;
33280
+ const route = isRoute ? parseRoute(ctx, rawStep.route, [...path8, "route"], label, index, routeChecks) : undefined;
33281
+ const inputs = !isRoute ? parseInputs(ctx, rawStep.inputs, [...path8, "inputs"], label) : undefined;
33282
+ const output = parseSchemaObject(ctx, rawStep.output, [...path8, "output"], `${label} "output"`);
33283
+ const gate = rawStep.gate !== undefined ? parseGate(ctx, rawStep.gate, [...path8, "gate"], label) : undefined;
33284
+ const step = { id, source: ctx.refAt(path8) };
32930
33285
  if (unit)
32931
33286
  step.unit = unit;
32932
33287
  if (map)
@@ -32958,75 +33313,175 @@ function parseSteps(ctx, raw) {
32958
33313
  }
32959
33314
  return steps;
32960
33315
  }
32961
- function parseUnit(ctx, raw, path7, stepLabel) {
33316
+ function parseUnit(ctx, raw, path8, stepLabel) {
32962
33317
  if (!isPlainRecord(raw)) {
32963
- ctx.err(path7, `${stepLabel} "unit" must be a mapping (a dispatch-override bag).`);
33318
+ ctx.err(path8, `${stepLabel} "unit" must be a mapping (a dispatch-override bag).`);
32964
33319
  return;
32965
33320
  }
32966
- checkUnknownKeys(ctx, raw, path7, UNIT_KEYS, `${stepLabel} "unit"`);
32967
- const unit = { source: ctx.refAt(path7) };
33321
+ checkUnknownKeys(ctx, raw, path8, UNIT_KEYS, `${stepLabel} "unit"`);
33322
+ const unit = { source: ctx.refAt(path8) };
33323
+ if (raw.exec !== undefined) {
33324
+ const exec = parseExec(ctx, raw.exec, [...path8, "exec"], stepLabel);
33325
+ if (exec !== undefined)
33326
+ unit.exec = exec;
33327
+ for (const key of UNIT_ENGINE_KEYS) {
33328
+ if (raw[key] === undefined)
33329
+ continue;
33330
+ ctx.err([...path8, key], `${stepLabel} "unit" declares both "exec" and "${key}". An exec unit runs a shell command and never ` + `reaches an engine, so "${key}" would have no effect \u2014 remove one of the two.`);
33331
+ }
33332
+ }
32968
33333
  if (raw.engine !== undefined) {
32969
- if (typeof raw.engine === "string" && raw.engine.trim() !== "")
32970
- unit.engine = raw.engine.trim();
32971
- else
32972
- ctx.err([...path7, "engine"], `${stepLabel} "engine" must be a non-empty engine name.`);
33334
+ const engine = parseEngineName(ctx, raw.engine, [...path8, "engine"], `${stepLabel} "engine"`);
33335
+ if (engine !== undefined)
33336
+ unit.engine = engine;
32973
33337
  }
32974
33338
  if (raw.model !== undefined) {
32975
33339
  if (typeof raw.model === "string" && raw.model.trim() !== "")
32976
33340
  unit.model = raw.model.trim();
32977
33341
  else
32978
- ctx.err([...path7, "model"], `${stepLabel} "model" must be a non-empty string (a model alias or exact id).`);
33342
+ ctx.err([...path8, "model"], `${stepLabel} "model" must be a non-empty string (a model alias or exact id).`);
32979
33343
  }
32980
- const llm = parseLlmOverrides(ctx, raw.llm, [...path7, "llm"], `${stepLabel} "llm"`);
33344
+ const llm = parseLlmOverrides(ctx, raw.llm, [...path8, "llm"], `${stepLabel} "llm"`);
32981
33345
  if (llm !== undefined)
32982
33346
  unit.llm = llm;
32983
- const timeoutMs = parseTimeoutField(ctx, raw.timeout, [...path7, "timeout"], `${stepLabel} "timeout"`);
33347
+ const timeoutMs = parseTimeoutField(ctx, raw.timeout, [...path8, "timeout"], `${stepLabel} "timeout"`);
32984
33348
  if (timeoutMs !== undefined)
32985
33349
  unit.timeoutMs = timeoutMs;
32986
- const retry = parseRetry(ctx, raw.retry, [...path7, "retry"], stepLabel);
33350
+ const retry = parseRetry(ctx, raw.retry, [...path8, "retry"], stepLabel);
32987
33351
  if (retry !== undefined)
32988
33352
  unit.retry = retry;
32989
- const onError = parseEnumField(ctx, raw.on_error, [...path7, "on_error"], `${stepLabel} "on_error"`, PROGRAM_ON_ERROR);
33353
+ const onError = parseEnumField(ctx, raw.on_error, [...path8, "on_error"], `${stepLabel} "on_error"`, PROGRAM_ON_ERROR);
32990
33354
  if (onError !== undefined)
32991
33355
  unit.onError = onError;
32992
- const output = parseSchemaObject(ctx, raw.output, [...path7, "output"], `${stepLabel} unit "output"`);
33356
+ const output = parseSchemaObject(ctx, raw.output, [...path8, "output"], `${stepLabel} unit "output"`);
32993
33357
  if (output !== undefined)
32994
33358
  unit.output = output;
32995
33359
  if (raw.env !== undefined) {
32996
33360
  if (Array.isArray(raw.env) && raw.env.every((entry) => typeof entry === "string" && entry.trim() !== "")) {
32997
33361
  unit.env = raw.env.map((entry) => entry.trim());
32998
33362
  } else {
32999
- ctx.err([...path7, "env"], `${stepLabel} "env" must be a list of non-empty env asset refs.`);
33363
+ ctx.err([...path8, "env"], `${stepLabel} "env" must be a list of non-empty env asset refs.`);
33000
33364
  }
33001
33365
  }
33002
- const isolation = parseEnumField(ctx, raw.isolation, [...path7, "isolation"], `${stepLabel} "isolation"`, PROGRAM_ISOLATION_KINDS);
33366
+ const isolation = parseEnumField(ctx, raw.isolation, [...path8, "isolation"], `${stepLabel} "isolation"`, PROGRAM_ISOLATION_KINDS);
33003
33367
  if (isolation !== undefined)
33004
33368
  unit.isolation = isolation;
33005
33369
  return unit;
33006
33370
  }
33007
- function parseMap(ctx, raw, path7, stepLabel) {
33371
+ function parseExec(ctx, raw, path8, stepLabel) {
33008
33372
  if (!isPlainRecord(raw)) {
33009
- ctx.err(path7, `${stepLabel} "map" must be a mapping with an "over" key.`);
33373
+ ctx.err(path8, `${stepLabel} "exec" must be a mapping with a "command" argv list.`);
33374
+ return;
33375
+ }
33376
+ checkUnknownKeys(ctx, raw, path8, EXEC_KEYS, `${stepLabel} "exec"`);
33377
+ const command = parseExecCommand(ctx, raw.command, [...path8, "command"], stepLabel);
33378
+ if (command === undefined)
33379
+ return;
33380
+ const exec = { command };
33381
+ const cwd = parseExecCwd(ctx, raw.cwd, [...path8, "cwd"], stepLabel);
33382
+ if (cwd !== undefined)
33383
+ exec.cwd = cwd;
33384
+ const passEnv = parseExecPassEnv(ctx, raw.pass_env, [...path8, "pass_env"], stepLabel);
33385
+ if (passEnv !== undefined)
33386
+ exec.passEnv = passEnv;
33387
+ if (raw.inherit_env !== undefined) {
33388
+ if (typeof raw.inherit_env !== "boolean") {
33389
+ ctx.err([...path8, "inherit_env"], `${stepLabel} "exec.inherit_env" must be true or false. true gives the command akm's whole environment ` + `instead of the default allowlist; omit it (or write false) to keep the allowlist.`);
33390
+ } else if (raw.inherit_env) {
33391
+ exec.inheritEnv = true;
33392
+ }
33393
+ }
33394
+ return exec;
33395
+ }
33396
+ function parseExecPassEnv(ctx, raw, path8, stepLabel) {
33397
+ if (raw === undefined)
33398
+ return;
33399
+ if (!Array.isArray(raw) || raw.length === 0) {
33400
+ ctx.err(path8, `${stepLabel} "exec.pass_env" must be a non-empty list of environment variable NAMES to copy through from ` + `akm's own environment, e.g. pass_env: [CARGO_HOME]. Values never appear here \u2014 use "env:" bindings for those.`);
33401
+ return;
33402
+ }
33403
+ if (raw.length > WORKFLOW_MAX_EXEC_PASS_ENV) {
33404
+ ctx.err(path8, `${stepLabel} "exec.pass_env" must have at most ${WORKFLOW_MAX_EXEC_PASS_ENV} entries. A command needing ` + `more than that wants "inherit_env: true", which says so explicitly.`);
33405
+ return;
33406
+ }
33407
+ const names = [];
33408
+ for (const [index, entry] of raw.entries()) {
33409
+ if (typeof entry !== "string" || !WORKFLOW_ENV_VAR_NAME_PATTERN.test(entry)) {
33410
+ ctx.err(path8, `${stepLabel} "exec.pass_env[${index}]" must be an environment variable name matching ` + `${WORKFLOW_ENV_VAR_NAME_PATTERN.source}.`);
33411
+ return;
33412
+ }
33413
+ if (names.includes(entry)) {
33414
+ ctx.err(path8, `${stepLabel} "exec.pass_env" lists "${entry}" more than once.`);
33415
+ return;
33416
+ }
33417
+ names.push(entry);
33418
+ }
33419
+ return names;
33420
+ }
33421
+ function parseExecCommand(ctx, raw, path8, stepLabel) {
33422
+ if (!Array.isArray(raw) || raw.length === 0) {
33423
+ ctx.err(path8, `${stepLabel} "exec" requires "command": a non-empty argv list, e.g. command: ["bun", "run", "test:unit"]. ` + `A single shell string is not accepted \u2014 the command is spawned directly, never through a shell.`);
33010
33424
  return;
33011
33425
  }
33012
- checkUnknownKeys(ctx, raw, path7, MAP_KEYS, `${stepLabel} "map"`);
33426
+ if (raw.length > WORKFLOW_MAX_EXEC_ARGV) {
33427
+ ctx.err(path8, `${stepLabel} "exec.command" must have at most ${WORKFLOW_MAX_EXEC_ARGV} entries.`);
33428
+ return;
33429
+ }
33430
+ const argv = [];
33431
+ for (const [index, entry] of raw.entries()) {
33432
+ if (typeof entry !== "string" || entry === "") {
33433
+ ctx.err(path8, `${stepLabel} "exec.command[${index}]" must be a non-empty string.`);
33434
+ return;
33435
+ }
33436
+ if (utf8Bytes(entry) > WORKFLOW_MAX_EXEC_ARG_BYTES) {
33437
+ ctx.err(path8, `${stepLabel} "exec.command[${index}]" exceeds ${WORKFLOW_MAX_EXEC_ARG_BYTES} bytes.`);
33438
+ return;
33439
+ }
33440
+ argv.push(entry);
33441
+ }
33442
+ return argv;
33443
+ }
33444
+ function parseExecCwd(ctx, raw, path8, stepLabel) {
33445
+ if (raw === undefined)
33446
+ return;
33447
+ if (typeof raw !== "string" || raw.trim() === "") {
33448
+ ctx.err(path8, `${stepLabel} "exec.cwd" must be a non-empty relative path inside the unit's working directory.`);
33449
+ return;
33450
+ }
33451
+ const value = raw.trim();
33452
+ if (value.length > WORKFLOW_MAX_EXEC_CWD_LENGTH) {
33453
+ ctx.err(path8, `${stepLabel} "exec.cwd" exceeds ${WORKFLOW_MAX_EXEC_CWD_LENGTH} characters.`);
33454
+ return;
33455
+ }
33456
+ if (!isContainedRelativePath(value)) {
33457
+ ctx.err(path8, `${stepLabel} "exec.cwd" (${JSON.stringify(value)}) must be a RELATIVE path inside the unit's working ` + `directory \u2014 absolute paths, Windows drive letters, "~", and ".." segments are rejected.`);
33458
+ return;
33459
+ }
33460
+ return value;
33461
+ }
33462
+ function parseMap(ctx, raw, path8, stepLabel) {
33463
+ if (!isPlainRecord(raw)) {
33464
+ ctx.err(path8, `${stepLabel} "map" must be a mapping with an "over" key.`);
33465
+ return;
33466
+ }
33467
+ checkUnknownKeys(ctx, raw, path8, MAP_KEYS, `${stepLabel} "map"`);
33013
33468
  let over = "";
33014
33469
  if (typeof raw.over === "string" && raw.over.trim() !== "") {
33015
33470
  over = raw.over.trim();
33016
- checkReferenceSyntax(ctx, over, [...path7, "over"], `${stepLabel} "over"`);
33471
+ checkReferenceSyntax(ctx, over, [...path8, "over"], `${stepLabel} "over"`);
33017
33472
  } else {
33018
- ctx.err([...path7, "over"], `${stepLabel} "map" requires "over": a reference naming the item list (e.g. steps.discover.output.files).`);
33473
+ ctx.err([...path8, "over"], `${stepLabel} "map" requires "over": a reference naming the item list (e.g. steps.discover.output.files).`);
33019
33474
  }
33020
33475
  let concurrency;
33021
33476
  if (raw.concurrency !== undefined) {
33022
- if (typeof raw.concurrency === "number" && Number.isInteger(raw.concurrency) && raw.concurrency > 0) {
33477
+ if (typeof raw.concurrency === "number" && Number.isInteger(raw.concurrency) && raw.concurrency > 0 && raw.concurrency <= WORKFLOW_MAX_CONCURRENCY) {
33023
33478
  concurrency = raw.concurrency;
33024
33479
  } else {
33025
- ctx.err([...path7, "concurrency"], `${stepLabel} "concurrency" must be a positive integer.`);
33480
+ ctx.err([...path8, "concurrency"], `${stepLabel} "concurrency" must be an integer from 1 through ${WORKFLOW_MAX_CONCURRENCY}.`);
33026
33481
  }
33027
33482
  }
33028
- const reducer = parseEnumField(ctx, raw.reducer, [...path7, "reducer"], `${stepLabel} "reducer"`, PROGRAM_REDUCERS);
33029
- const unit = raw.unit !== undefined ? parseUnit(ctx, raw.unit, [...path7, "unit"], stepLabel) : undefined;
33483
+ const reducer = parseEnumField(ctx, raw.reducer, [...path8, "reducer"], `${stepLabel} "reducer"`, PROGRAM_REDUCERS);
33484
+ const unit = raw.unit !== undefined ? parseUnit(ctx, raw.unit, [...path8, "unit"], stepLabel) : undefined;
33030
33485
  const map = { over };
33031
33486
  if (concurrency !== undefined)
33032
33487
  map.concurrency = concurrency;
@@ -33036,21 +33491,21 @@ function parseMap(ctx, raw, path7, stepLabel) {
33036
33491
  map.unit = unit;
33037
33492
  return map;
33038
33493
  }
33039
- function parseRoute(ctx, raw, path7, stepLabel, stepIndex, routeChecks) {
33494
+ function parseRoute(ctx, raw, path8, stepLabel, stepIndex, routeChecks) {
33040
33495
  if (!isPlainRecord(raw)) {
33041
- ctx.err(path7, `${stepLabel} "route" must be a mapping with "input" and "when" keys.`);
33496
+ ctx.err(path8, `${stepLabel} "route" must be a mapping with "input" and "when" keys.`);
33042
33497
  return;
33043
33498
  }
33044
- checkUnknownKeys(ctx, raw, path7, ROUTE_KEYS, `${stepLabel} "route"`);
33499
+ checkUnknownKeys(ctx, raw, path8, ROUTE_KEYS, `${stepLabel} "route"`);
33045
33500
  let input = "";
33046
33501
  if (typeof raw.input === "string" && raw.input.trim() !== "") {
33047
33502
  input = raw.input.trim();
33048
- checkReferenceSyntax(ctx, input, [...path7, "input"], `${stepLabel} "route.input"`);
33503
+ checkReferenceSyntax(ctx, input, [...path8, "input"], `${stepLabel} "route.input"`);
33049
33504
  } else {
33050
- ctx.err([...path7, "input"], `${stepLabel} "route" requires "input": a reference naming the value to route on.`);
33505
+ ctx.err([...path8, "input"], `${stepLabel} "route" requires "input": a reference naming the value to route on.`);
33051
33506
  }
33052
33507
  const check = { stepIndex, stepLabel, branches: [] };
33053
- const whenPath = [...path7, "when"];
33508
+ const whenPath = [...path8, "when"];
33054
33509
  if (!Array.isArray(raw.when) || raw.when.length === 0) {
33055
33510
  ctx.err(whenPath, `${stepLabel} "route" requires "when": a non-empty list of { match, step } branches (e.g. when: [{ match: pass, step: ship }]).`);
33056
33511
  } else {
@@ -33090,9 +33545,9 @@ function parseRoute(ctx, raw, path7, stepLabel, stepIndex, routeChecks) {
33090
33545
  if (raw.default !== undefined) {
33091
33546
  if (typeof raw.default === "string" && raw.default.trim() !== "") {
33092
33547
  defaultStepId = raw.default.trim();
33093
- check.defaultTarget = { stepId: defaultStepId, line: ctx.lineAt([...path7, "default"]) };
33548
+ check.defaultTarget = { stepId: defaultStepId, line: ctx.lineAt([...path8, "default"]) };
33094
33549
  } else {
33095
- ctx.err([...path7, "default"], `${stepLabel} "route.default" must be a step id string.`);
33550
+ ctx.err([...path8, "default"], `${stepLabel} "route.default" must be a step id string.`);
33096
33551
  }
33097
33552
  }
33098
33553
  routeChecks.push(check);
@@ -33101,55 +33556,67 @@ function parseRoute(ctx, raw, path7, stepLabel, stepIndex, routeChecks) {
33101
33556
  route.defaultStepId = defaultStepId;
33102
33557
  return route;
33103
33558
  }
33104
- function parseInputs(ctx, raw, path7, stepLabel) {
33559
+ function parseInputs(ctx, raw, path8, stepLabel) {
33105
33560
  if (raw === undefined)
33106
33561
  return;
33107
33562
  if (!Array.isArray(raw) || raw.length === 0) {
33108
- ctx.err(path7, `${stepLabel} "inputs" must be a non-empty list of reference strings.`);
33563
+ ctx.err(path8, `${stepLabel} "inputs" must be a non-empty list of reference strings.`);
33109
33564
  return;
33110
33565
  }
33111
33566
  if (raw.length > WORKFLOW_MAX_INPUTS) {
33112
- ctx.err(path7, `${stepLabel} "inputs" must contain at most ${WORKFLOW_MAX_INPUTS} entries.`);
33567
+ ctx.err(path8, `${stepLabel} "inputs" must contain at most ${WORKFLOW_MAX_INPUTS} entries.`);
33113
33568
  }
33114
33569
  const out = [];
33115
33570
  raw.forEach((entry, i) => {
33116
33571
  if (typeof entry !== "string" || entry.trim() === "") {
33117
- ctx.err([...path7, i], `${stepLabel} "inputs[${i}]" must be a non-empty reference string.`);
33572
+ ctx.err([...path8, i], `${stepLabel} "inputs[${i}]" must be a non-empty reference string.`);
33118
33573
  return;
33119
33574
  }
33120
33575
  const value = entry.trim();
33121
- checkReferenceSyntax(ctx, value, [...path7, i], `${stepLabel} "inputs[${i}]"`);
33576
+ checkReferenceSyntax(ctx, value, [...path8, i], `${stepLabel} "inputs[${i}]"`);
33122
33577
  out.push(value);
33123
33578
  });
33124
33579
  return out.length > 0 ? out : undefined;
33125
33580
  }
33126
- function parseGate(ctx, raw, path7, stepLabel) {
33581
+ function parseGate(ctx, raw, path8, stepLabel) {
33127
33582
  if (!isPlainRecord(raw)) {
33128
- ctx.err(path7, `${stepLabel} "gate" must be a mapping with any of: ${GATE_KEYS.join(", ")}.`);
33583
+ ctx.err(path8, `${stepLabel} "gate" must be a mapping with any of: ${GATE_KEYS.join(", ")}.`);
33129
33584
  return;
33130
33585
  }
33131
- checkUnknownKeys(ctx, raw, path7, GATE_KEYS, `${stepLabel} "gate"`);
33586
+ checkUnknownKeys(ctx, raw, path8, GATE_KEYS, `${stepLabel} "gate"`);
33132
33587
  const gate = {};
33133
33588
  if (raw.max_loops !== undefined) {
33134
- if (typeof raw.max_loops === "number" && Number.isInteger(raw.max_loops) && raw.max_loops >= 1) {
33589
+ if (typeof raw.max_loops === "number" && Number.isInteger(raw.max_loops) && raw.max_loops >= 1 && raw.max_loops <= WORKFLOW_MAX_GATE_LOOPS) {
33135
33590
  gate.maxLoops = raw.max_loops;
33136
33591
  } else {
33137
- ctx.err([...path7, "max_loops"], `${stepLabel} "gate.max_loops" must be an integer >= 1.`);
33592
+ ctx.err([...path8, "max_loops"], `${stepLabel} "gate.max_loops" must be an integer from 1 through ${WORKFLOW_MAX_GATE_LOOPS}.`);
33138
33593
  }
33139
33594
  }
33140
33595
  return gate;
33141
33596
  }
33142
- function parseRetry(ctx, raw, path7, stepLabel) {
33597
+ function parseEngineName(ctx, raw, path8, label) {
33598
+ if (typeof raw !== "string" || raw.trim() === "") {
33599
+ ctx.err(path8, `${label} must be a non-empty engine name.`);
33600
+ return;
33601
+ }
33602
+ const name = raw.trim();
33603
+ if (!WORKFLOW_ENGINE_NAME_PATTERN.test(name) || name.length > WORKFLOW_MAX_ENGINE_NAME_LENGTH) {
33604
+ ctx.err(path8, `${label} has an invalid engine name ${JSON.stringify(name)}. Engine names are lowercase words of letters ` + `and digits separated by single dashes, starting with a letter (e.g. "code-review-llm"), at most ` + `${WORKFLOW_MAX_ENGINE_NAME_LENGTH} characters.`);
33605
+ return;
33606
+ }
33607
+ return name;
33608
+ }
33609
+ function parseRetry(ctx, raw, path8, stepLabel) {
33143
33610
  if (raw === undefined)
33144
33611
  return;
33145
33612
  if (!isPlainRecord(raw)) {
33146
- ctx.err(path7, `${stepLabel} "retry" must be a mapping: { max: <n>, on: [<failure_reason>, \u2026] }.`);
33613
+ ctx.err(path8, `${stepLabel} "retry" must be a mapping: { max: <n>, on: [<failure_reason>, \u2026] }.`);
33147
33614
  return;
33148
33615
  }
33149
- checkUnknownKeys(ctx, raw, path7, RETRY_KEYS, `${stepLabel} "retry"`);
33616
+ checkUnknownKeys(ctx, raw, path8, RETRY_KEYS, `${stepLabel} "retry"`);
33150
33617
  let ok = true;
33151
- if (!(typeof raw.max === "number" && Number.isInteger(raw.max) && raw.max >= 0)) {
33152
- ctx.err([...path7, "max"], `${stepLabel} "retry.max" is required and must be a non-negative integer.`);
33618
+ if (!(typeof raw.max === "number" && Number.isInteger(raw.max) && raw.max >= 0 && raw.max <= WORKFLOW_MAX_RETRIES)) {
33619
+ ctx.err([...path8, "max"], `${stepLabel} "retry.max" is required and must be an integer from 0 through ${WORKFLOW_MAX_RETRIES}.`);
33153
33620
  ok = false;
33154
33621
  }
33155
33622
  const on = [];
@@ -33158,27 +33625,27 @@ function parseRetry(ctx, raw, path7, stepLabel) {
33158
33625
  if (typeof reason === "string" && PROGRAM_RETRY_REASONS.includes(reason)) {
33159
33626
  on.push(reason);
33160
33627
  } else {
33161
- ctx.err([...path7, "on", i], `${stepLabel} "retry.on" has unknown failure reason ${JSON.stringify(reason)}. Valid reasons: ${PROGRAM_RETRY_REASONS.join(", ")}.`);
33628
+ ctx.err([...path8, "on", i], `${stepLabel} "retry.on" has unknown failure reason ${JSON.stringify(reason)}. Valid reasons: ${PROGRAM_RETRY_REASONS.join(", ")}.`);
33162
33629
  ok = false;
33163
33630
  }
33164
33631
  });
33165
33632
  } else {
33166
- ctx.err([...path7, "on"], `${stepLabel} "retry.on" is required and must be a non-empty list of failure reasons (${PROGRAM_RETRY_REASONS.join(", ")}).`);
33633
+ ctx.err([...path8, "on"], `${stepLabel} "retry.on" is required and must be a non-empty list of failure reasons (${PROGRAM_RETRY_REASONS.join(", ")}).`);
33167
33634
  ok = false;
33168
33635
  }
33169
33636
  return ok ? { max: raw.max, on } : undefined;
33170
33637
  }
33171
- function parseTimeoutField(ctx, raw, path7, label) {
33638
+ function parseTimeoutField(ctx, raw, path8, label) {
33172
33639
  if (raw === undefined)
33173
33640
  return;
33174
33641
  if (typeof raw === "number") {
33175
33642
  if (Number.isInteger(raw) && raw > 0)
33176
- return raw;
33177
- ctx.err(path7, `${label} has a non-positive timeout ${JSON.stringify(raw)}. ${TIMEOUT_HINT}.`);
33643
+ return checkTimeoutCeiling(ctx, raw, path8, label, String(raw));
33644
+ ctx.err(path8, `${label} has a non-positive timeout ${JSON.stringify(raw)}. ${TIMEOUT_HINT}.`);
33178
33645
  return;
33179
33646
  }
33180
33647
  if (typeof raw !== "string") {
33181
- ctx.err(path7, `${label} must be a duration string. ${TIMEOUT_HINT}.`);
33648
+ ctx.err(path8, `${label} must be a duration string. ${TIMEOUT_HINT}.`);
33182
33649
  return;
33183
33650
  }
33184
33651
  const value = raw.trim().toLowerCase();
@@ -33186,31 +33653,37 @@ function parseTimeoutField(ctx, raw, path7, label) {
33186
33653
  return null;
33187
33654
  const match = value.match(TIMEOUT_VALUE);
33188
33655
  if (!match) {
33189
- ctx.err(path7, `${label} has an invalid timeout "${raw}". ${TIMEOUT_HINT}.`);
33656
+ ctx.err(path8, `${label} has an invalid timeout "${raw}". ${TIMEOUT_HINT}.`);
33190
33657
  return;
33191
33658
  }
33192
33659
  const n = Number.parseInt(match[1], 10);
33193
33660
  const unit = match[2] ?? "ms";
33194
33661
  const timeoutMs = unit === "m" ? n * 60000 : unit === "s" ? n * 1000 : n;
33195
33662
  if (timeoutMs <= 0) {
33196
- ctx.err(path7, `${label} has a non-positive timeout "${raw}". Use a positive duration or "none".`);
33663
+ ctx.err(path8, `${label} has a non-positive timeout "${raw}". Use a positive duration or "none".`);
33197
33664
  return;
33198
33665
  }
33199
- return timeoutMs;
33666
+ return checkTimeoutCeiling(ctx, timeoutMs, path8, label, raw);
33667
+ }
33668
+ function checkTimeoutCeiling(ctx, timeoutMs, path8, label, raw) {
33669
+ if (timeoutMs <= WORKFLOW_MAX_TIMEOUT_MS)
33670
+ return timeoutMs;
33671
+ ctx.err(path8, `${label} has a timeout "${raw}" above the maximum of ${WORKFLOW_MAX_TIMEOUT_MS} ms (about 24.8 days). ` + `Use a shorter duration or "none" for no timeout.`);
33672
+ return;
33200
33673
  }
33201
- function parseEnumField(ctx, raw, path7, label, allowed) {
33674
+ function parseEnumField(ctx, raw, path8, label, allowed) {
33202
33675
  if (raw === undefined)
33203
33676
  return;
33204
33677
  if (typeof raw === "string" && allowed.includes(raw))
33205
33678
  return raw;
33206
- ctx.err(path7, `${label} must be one of: ${allowed.join(" | ")} (got ${JSON.stringify(raw)}).`);
33679
+ ctx.err(path8, `${label} must be one of: ${allowed.join(" | ")} (got ${JSON.stringify(raw)}).`);
33207
33680
  return;
33208
33681
  }
33209
- function parseLlmOverrides(ctx, raw, path7, label) {
33682
+ function parseLlmOverrides(ctx, raw, path8, label) {
33210
33683
  if (raw === undefined)
33211
33684
  return;
33212
33685
  if (!isPlainRecord(raw)) {
33213
- ctx.err(path7, `${label} must be a mapping of LLM invocation overrides.`);
33686
+ ctx.err(path8, `${label} must be a mapping of LLM invocation overrides.`);
33214
33687
  return;
33215
33688
  }
33216
33689
  const keys = [
@@ -33221,36 +33694,36 @@ function parseLlmOverrides(ctx, raw, path7, label) {
33221
33694
  "context_length",
33222
33695
  "enable_thinking"
33223
33696
  ];
33224
- checkUnknownKeys(ctx, raw, path7, keys, label);
33697
+ checkUnknownKeys(ctx, raw, path8, keys, label);
33225
33698
  const result = {};
33226
33699
  if (raw.temperature !== undefined) {
33227
33700
  if (typeof raw.temperature === "number" && Number.isFinite(raw.temperature))
33228
33701
  result.temperature = raw.temperature;
33229
33702
  else
33230
- ctx.err([...path7, "temperature"], `${label}.temperature must be a finite number.`);
33703
+ ctx.err([...path8, "temperature"], `${label}.temperature must be a finite number.`);
33231
33704
  }
33232
33705
  if (raw.max_tokens !== undefined) {
33233
33706
  if (typeof raw.max_tokens === "number" && Number.isInteger(raw.max_tokens) && raw.max_tokens > 0) {
33234
33707
  result.maxTokens = raw.max_tokens;
33235
33708
  } else
33236
- ctx.err([...path7, "max_tokens"], `${label}.max_tokens must be a positive integer.`);
33709
+ ctx.err([...path8, "max_tokens"], `${label}.max_tokens must be a positive integer.`);
33237
33710
  }
33238
33711
  if (raw.supports_json_schema !== undefined) {
33239
33712
  if (typeof raw.supports_json_schema === "boolean")
33240
33713
  result.supportsJsonSchema = raw.supports_json_schema;
33241
33714
  else
33242
- ctx.err([...path7, "supports_json_schema"], `${label}.supports_json_schema must be a boolean.`);
33715
+ ctx.err([...path8, "supports_json_schema"], `${label}.supports_json_schema must be a boolean.`);
33243
33716
  }
33244
33717
  if (raw.extra_params !== undefined) {
33245
33718
  if (!isPlainRecord(raw.extra_params)) {
33246
- ctx.err([...path7, "extra_params"], `${label}.extra_params must be a JSON object.`);
33719
+ ctx.err([...path8, "extra_params"], `${label}.extra_params must be a JSON object.`);
33247
33720
  } else {
33248
33721
  const issues = validateExtraParams(raw.extra_params);
33249
33722
  for (const issue of issues) {
33250
- ctx.err([...path7, "extra_params", ...issue.path], `${formatExtraParamsIssue(`${label}.extra_params`, issue)}.`);
33723
+ ctx.err([...path8, "extra_params", ...issue.path], `${formatExtraParamsIssue(`${label}.extra_params`, issue)}.`);
33251
33724
  }
33252
33725
  if (jsonBytes(raw.extra_params) > WORKFLOW_MAX_EXTRA_PARAMS_BYTES) {
33253
- ctx.err([...path7, "extra_params"], `${label}.extra_params exceeds the 64 KiB resource limit.`);
33726
+ ctx.err([...path8, "extra_params"], `${label}.extra_params exceeds the 64 KiB resource limit.`);
33254
33727
  }
33255
33728
  if (issues.length === 0 && jsonBytes(raw.extra_params) <= WORKFLOW_MAX_EXTRA_PARAMS_BYTES) {
33256
33729
  result.extraParams = raw.extra_params;
@@ -33261,37 +33734,48 @@ function parseLlmOverrides(ctx, raw, path7, label) {
33261
33734
  if (typeof raw.context_length === "number" && Number.isInteger(raw.context_length) && raw.context_length > 0) {
33262
33735
  result.contextLength = raw.context_length;
33263
33736
  } else
33264
- ctx.err([...path7, "context_length"], `${label}.context_length must be a positive integer.`);
33737
+ ctx.err([...path8, "context_length"], `${label}.context_length must be a positive integer.`);
33265
33738
  }
33266
33739
  if (raw.enable_thinking !== undefined) {
33267
33740
  if (typeof raw.enable_thinking === "boolean")
33268
33741
  result.enableThinking = raw.enable_thinking;
33269
33742
  else
33270
- ctx.err([...path7, "enable_thinking"], `${label}.enable_thinking must be a boolean.`);
33743
+ ctx.err([...path8, "enable_thinking"], `${label}.enable_thinking must be a boolean.`);
33271
33744
  }
33272
33745
  return Object.keys(result).length > 0 ? result : undefined;
33273
33746
  }
33274
- function parseSchemaObject(ctx, raw, path7, label) {
33747
+ function parseSchemaObject(ctx, raw, path8, label) {
33275
33748
  if (raw === undefined)
33276
33749
  return;
33277
33750
  if (!isPlainRecord(raw)) {
33278
- ctx.err(path7, `${label} must be a JSON Schema object (e.g. { type: object, properties: { \u2026 } }).`);
33751
+ ctx.err(path8, `${label} must be a JSON Schema object (e.g. { type: object, properties: { \u2026 } }).`);
33279
33752
  return;
33280
33753
  }
33281
33754
  if (jsonBytes(raw) > WORKFLOW_MAX_SCHEMA_BYTES) {
33282
- ctx.err(path7, `${label} exceeds the 256 KiB resource limit.`);
33755
+ ctx.err(path8, `${label} exceeds the 256 KiB resource limit.`);
33283
33756
  }
33757
+ checkSchemaDefinition(ctx, raw, path8, label);
33284
33758
  return raw;
33285
33759
  }
33286
- function checkReferenceSyntax(ctx, text, path7, label) {
33760
+ function checkSchemaDefinition(ctx, schema, path8, label) {
33761
+ for (const issue of checkJsonSchemaDefinition(schema)) {
33762
+ const issuePath = [...path8, ...issue.path];
33763
+ if (issue.kind === "unsupported") {
33764
+ ctx.err(issuePath, `${label} (at ${issue.pointer}): ${issue.message}. Supported JSON Schema keywords: ` + `${JSON_SCHEMA_SUBSET_SUPPORTED_KEYWORDS}.`);
33765
+ } else {
33766
+ ctx.err(issuePath, `${label} is not a valid JSON Schema (at ${issue.pointer}): ${issue.message}.`);
33767
+ }
33768
+ }
33769
+ }
33770
+ function checkReferenceSyntax(ctx, text, path8, label) {
33287
33771
  const result = parseReference(text);
33288
33772
  if (!result.ok)
33289
- ctx.err(path7, `${label}: ${result.message}`);
33773
+ ctx.err(path8, `${label}: ${result.message}`);
33290
33774
  }
33291
- function checkUnknownKeys(ctx, obj, path7, allowed, label) {
33775
+ function checkUnknownKeys(ctx, obj, path8, allowed, label) {
33292
33776
  for (const key of Object.keys(obj)) {
33293
33777
  if (!allowed.includes(key)) {
33294
- ctx.err([...path7, key], `Unknown ${label} key "${key}". Allowed keys: ${allowed.join(", ")}.`);
33778
+ ctx.err([...path8, key], `Unknown ${label} key "${key}". Allowed keys: ${allowed.join(", ")}.`);
33295
33779
  }
33296
33780
  }
33297
33781
  }
@@ -33335,7 +33819,7 @@ function checkInvalidTypeValue(data, allowedTypes) {
33335
33819
  return `type field has invalid value '${value}'; expected one of: ${allowedTypes.join(", ")}`;
33336
33820
  }
33337
33821
  function suggestSlug(filePath) {
33338
- return path7.basename(filePath, ".md").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
33822
+ return path8.basename(filePath, ".md").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
33339
33823
  }
33340
33824
  function nameOrTypeDiagnostics(relPath, data, frontmatter, allowedTypes) {
33341
33825
  const missingFieldDetail = checkMissingNameOrType(data, frontmatter);
@@ -33391,7 +33875,7 @@ function collectSuppressedKeys(raw) {
33391
33875
  function dangerousEnvKeyDiagnostics(type, relPath, raw) {
33392
33876
  if (type !== "env" && type !== "secret")
33393
33877
  return [];
33394
- const baseNameWithExt = path7.basename(relPath);
33878
+ const baseNameWithExt = path8.basename(relPath);
33395
33879
  if (!baseNameWithExt.endsWith(".env"))
33396
33880
  return [];
33397
33881
  const ref = conceptIdForStashFile(type, ".", relPath);
@@ -33412,9 +33896,10 @@ function dangerousEnvKeyDiagnostics(type, relPath, raw) {
33412
33896
  }
33413
33897
  return diagnostics;
33414
33898
  }
33415
- async function skillDirectoryDiagnostics(relPath, seen, ctx) {
33899
+ var AKM_SKILL_DIRS = new Set(["skills"]);
33900
+ async function skillDirectoryDiagnostics(relPath, seen, ctx, skillDirs = AKM_SKILL_DIRS) {
33416
33901
  const segments = relPath.replace(/\\/g, "/").split("/").filter((s) => s.length > 0);
33417
- if (segments.length < 3 || segments[0] !== "skills")
33902
+ if (segments.length < 3 || !skillDirs.has(segments[0]))
33418
33903
  return [];
33419
33904
  const skillDir = `${segments[0]}/${segments[1]}`;
33420
33905
  if (seen.has(skillDir))
@@ -33494,42 +33979,62 @@ function matchWorkflowPlaceholder(body) {
33494
33979
  return PLACEHOLDER_STRINGS.find((p) => body.includes(p)) ?? null;
33495
33980
  }
33496
33981
  function workflowStructureDiagnostics(relPath, raw, parsePath2) {
33982
+ return workflowFrontendDiagnostics(relPath, raw, parsePath2).errors;
33983
+ }
33984
+ function lineOf(err) {
33985
+ return typeof err.line === "number" && Number.isFinite(err.line) && err.line > 0 ? { line: err.line } : {};
33986
+ }
33987
+ function workflowFrontendDiagnostics(relPath, raw, parsePath2) {
33988
+ const none = { errors: [], warnings: [] };
33497
33989
  if (parsePath2.includes("/.cache/") || parsePath2.includes("/registry/"))
33498
- return [];
33499
- const diagnostics = [];
33990
+ return none;
33991
+ const errors2 = [];
33992
+ const warnings = [];
33500
33993
  try {
33501
33994
  const result = parseWorkflow(raw, { path: parsePath2 });
33502
33995
  if (!result.ok) {
33503
33996
  for (const err of result.errors ?? []) {
33504
- diagnostics.push({
33997
+ errors2.push({
33505
33998
  file: relPath,
33506
33999
  issue: "invalid-workflow-structure",
33507
34000
  detail: err.message ?? String(err),
33508
- fixed: false
34001
+ fixed: false,
34002
+ ...lineOf(err)
33509
34003
  });
33510
34004
  }
33511
- return diagnostics;
34005
+ return { errors: errors2, warnings };
33512
34006
  }
33513
- const compiled = compileWorkflowPlan(result.document, path7.basename(parsePath2, path7.extname(parsePath2)));
34007
+ const compiled = compileWorkflowPlan(result.document, path8.basename(parsePath2, path8.extname(parsePath2)));
33514
34008
  if (!compiled.ok) {
33515
34009
  for (const err of compiled.errors) {
33516
- diagnostics.push({
34010
+ errors2.push({
33517
34011
  file: relPath,
33518
34012
  issue: "invalid-workflow-structure",
33519
34013
  detail: err.message,
33520
- fixed: false
34014
+ fixed: false,
34015
+ ...lineOf(err)
33521
34016
  });
33522
34017
  }
34018
+ return { errors: errors2, warnings };
34019
+ }
34020
+ for (const warning of compiled.warnings) {
34021
+ warnings.push({
34022
+ file: relPath,
34023
+ issue: "workflow-warning",
34024
+ detail: warning.message,
34025
+ fixed: false,
34026
+ ...lineOf(warning)
34027
+ });
33523
34028
  }
33524
34029
  } catch (e) {
33525
- diagnostics.push({
34030
+ errors2.push({
33526
34031
  file: relPath,
33527
34032
  issue: "invalid-workflow-structure",
33528
34033
  detail: `workflow parser error: ${e instanceof Error ? e.message : String(e)}`,
33529
34034
  fixed: false
33530
34035
  });
33531
34036
  }
33532
- return diagnostics;
34037
+ return { errors: errors2, warnings };
33533
34038
  }
33534
34039
  function workflowDiagnostics(relPath, raw, body) {
33535
34040
  const diagnostics = [];
@@ -33574,7 +34079,7 @@ init_errors();
33574
34079
  init_file_lock();
33575
34080
  init_paths();
33576
34081
  import fs7 from "fs";
33577
- import path10 from "path";
34082
+ import path11 from "path";
33578
34083
  function readConfigText(configPath) {
33579
34084
  try {
33580
34085
  return readTextFileWithLimit(configPath, MAX_CONFIG_FILE_BYTES, "Config file");
@@ -33613,7 +34118,7 @@ function describeJsonRoot(value) {
33613
34118
  return typeof value;
33614
34119
  }
33615
34120
  function getConfigLockPath() {
33616
- return path10.join(getConfigDir(), "config.json.lck");
34121
+ return path11.join(getConfigDir(), "config.json.lck");
33617
34122
  }
33618
34123
  var CONFIG_LOCK_MAX_RETRIES = 40;
33619
34124
  var CONFIG_LOCK_RETRY_DELAY_MS = 50;
@@ -33623,7 +34128,7 @@ function sleepSyncMs(ms) {
33623
34128
  function acquireConfigLock() {
33624
34129
  const lockPath = getConfigLockPath();
33625
34130
  try {
33626
- fs7.mkdirSync(path10.dirname(lockPath), { recursive: true });
34131
+ fs7.mkdirSync(path11.dirname(lockPath), { recursive: true });
33627
34132
  } catch {}
33628
34133
  for (let attempt = 0;attempt < CONFIG_LOCK_MAX_RETRIES; attempt++) {
33629
34134
  try {
@@ -34170,8 +34675,8 @@ function getErrorMap() {
34170
34675
  }
34171
34676
  // node_modules/zod/v3/helpers/parseUtil.js
34172
34677
  var makeIssue = (params) => {
34173
- const { data, path: path11, errorMaps, issueData } = params;
34174
- const fullPath = [...path11, ...issueData.path || []];
34678
+ const { data, path: path12, errorMaps, issueData } = params;
34679
+ const fullPath = [...path12, ...issueData.path || []];
34175
34680
  const fullIssue = {
34176
34681
  ...issueData,
34177
34682
  path: fullPath
@@ -34283,11 +34788,11 @@ var errorUtil;
34283
34788
 
34284
34789
  // node_modules/zod/v3/types.js
34285
34790
  class ParseInputLazyPath {
34286
- constructor(parent, value, path11, key) {
34791
+ constructor(parent, value, path12, key) {
34287
34792
  this._cachedPath = [];
34288
34793
  this.parent = parent;
34289
34794
  this.data = value;
34290
- this._path = path11;
34795
+ this._path = path12;
34291
34796
  this._key = key;
34292
34797
  }
34293
34798
  get path() {
@@ -38337,6 +38842,7 @@ var BundlesConfigSchema = exports_external.record(exports_external.string().min(
38337
38842
  // src/core/config/schema/workflow.ts
38338
38843
  var WorkflowConfigSchema = exports_external.object({
38339
38844
  maxConcurrency: positiveInt.optional(),
38845
+ defaultMapConcurrency: positiveInt.optional(),
38340
38846
  judgeEngine: engineName.optional()
38341
38847
  }).passthrough();
38342
38848
 
@@ -38633,7 +39139,7 @@ function normalizeInstalledGitRef(source, ref) {
38633
39139
 
38634
39140
  // src/core/config/deep-merge.ts
38635
39141
  var UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
38636
- function isPlainObject(value) {
39142
+ function isPlainObject2(value) {
38637
39143
  if (value === null || typeof value !== "object" || Array.isArray(value))
38638
39144
  return false;
38639
39145
  const prototype = Object.getPrototypeOf(value);
@@ -38642,7 +39148,7 @@ function isPlainObject(value) {
38642
39148
  function copyConfigValue(value) {
38643
39149
  if (Array.isArray(value))
38644
39150
  return value.map(copyConfigValue);
38645
- if (!isPlainObject(value))
39151
+ if (!isPlainObject2(value))
38646
39152
  return value;
38647
39153
  const copy = {};
38648
39154
  for (const key of Object.keys(value)) {
@@ -38661,7 +39167,7 @@ function deepMergeConfig(base, override) {
38661
39167
  if (next === undefined)
38662
39168
  continue;
38663
39169
  const current = result[key];
38664
- result[key] = isPlainObject(current) && isPlainObject(next) ? deepMergeConfig(current, next) : copyConfigValue(next);
39170
+ result[key] = isPlainObject2(current) && isPlainObject2(next) ? deepMergeConfig(current, next) : copyConfigValue(next);
38665
39171
  }
38666
39172
  return result;
38667
39173
  }
@@ -38803,9 +39309,10 @@ init_common();
38803
39309
  init_errors();
38804
39310
  init_file_lock();
38805
39311
  init_maintenance_barrier();
39312
+ init_path_access();
38806
39313
  init_paths();
38807
39314
  import fs11 from "fs";
38808
- import path12 from "path";
39315
+ import path13 from "path";
38809
39316
  function readLockfile() {
38810
39317
  const lockfilePath = getLockfilePath();
38811
39318
  try {
@@ -38818,6 +39325,12 @@ function readLockfile() {
38818
39325
  return [];
38819
39326
  }
38820
39327
  }
39328
+ function assertLockfilePathReadable(target) {
39329
+ const { access, code } = classifyPathAccess(target);
39330
+ if (access !== "inaccessible")
39331
+ return;
39332
+ throw new ConfigError(`Refusing to modify the lockfile: ${describeInaccessiblePath(target, code)}. akm cannot read the existing lock ` + "records, and writing over them would destroy every bundle they track. Fix the ownership or mode of that " + "path (or point AKM_DATA_DIR / XDG_DATA_HOME somewhere this user owns) and retry.", "DATA_DIR_UNREADABLE");
39333
+ }
38821
39334
  function lockContentRootFor(bundleId, type) {
38822
39335
  if (!bundleId || type !== "git" && type !== "npm")
38823
39336
  return;
@@ -38830,7 +39343,7 @@ function lockContentRootFor(bundleId, type) {
38830
39343
  }
38831
39344
  function writeLockfileUnlocked(entries) {
38832
39345
  const lockfilePath = getLockfilePath();
38833
- const dir = path12.dirname(lockfilePath);
39346
+ const dir = path13.dirname(lockfilePath);
38834
39347
  fs11.mkdirSync(dir, { recursive: true });
38835
39348
  writeFileAtomic(lockfilePath, `${JSON.stringify(entries, null, 2)}
38836
39349
  `);
@@ -38838,6 +39351,7 @@ function writeLockfileUnlocked(entries) {
38838
39351
  function readLockEntriesForMigration() {
38839
39352
  let existing = [];
38840
39353
  const lockfilePath = getLockfilePath();
39354
+ assertLockfilePathReadable(lockfilePath);
38841
39355
  if (fs11.existsSync(lockfilePath)) {
38842
39356
  let raw;
38843
39357
  try {
@@ -38879,10 +39393,10 @@ init_database();
38879
39393
 
38880
39394
  // src/core/bundle-id.ts
38881
39395
  import crypto3 from "crypto";
38882
- import path13 from "path";
39396
+ import path14 from "path";
38883
39397
  function slugForPath(sourcePath) {
38884
- const resolved = path13.resolve(sourcePath);
38885
- const base = path13.basename(resolved).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
39398
+ const resolved = path14.resolve(sourcePath);
39399
+ const base = path14.basename(resolved).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
38886
39400
  if (base.length > 0)
38887
39401
  return base;
38888
39402
  return `bundle-${shortHash(resolved)}`;
@@ -38905,7 +39419,7 @@ function deriveBundleIds(sources) {
38905
39419
  function ensureUniqueId(preferred, sourcePath, used) {
38906
39420
  if (!used.has(preferred))
38907
39421
  return preferred;
38908
- const suffixed = `${preferred}-${shortHash(path13.resolve(sourcePath))}`;
39422
+ const suffixed = `${preferred}-${shortHash(path14.resolve(sourcePath))}`;
38909
39423
  if (!used.has(suffixed))
38910
39424
  return suffixed;
38911
39425
  let n = 2;
@@ -38938,11 +39452,11 @@ function inferLegacyBundleIds(sources) {
38938
39452
  // scripts/akm-migrate/migrate/legacy/config-source-migration.ts
38939
39453
  import fs24 from "fs";
38940
39454
  import os from "os";
38941
- import path27 from "path";
39455
+ import path28 from "path";
38942
39456
 
38943
39457
  // src/core/adapter/adapters/agent-skills-adapter.ts
38944
39458
  import fs12 from "fs";
38945
- import path14 from "path";
39459
+ import path15 from "path";
38946
39460
 
38947
39461
  // src/core/adapter/adapters/shared.ts
38948
39462
  import { createHash } from "crypto";
@@ -39185,6 +39699,36 @@ function skillFieldDiagnostics(relPath, dirName, data) {
39185
39699
  }
39186
39700
  return diagnostics;
39187
39701
  }
39702
+ var MAX_PACKAGE_PROBE_DEPTH = 3;
39703
+ async function subtreeHasManifest(dir, entries, ctx, depth) {
39704
+ if (entries.includes(SKILL_MANIFEST))
39705
+ return true;
39706
+ if (depth >= MAX_PACKAGE_PROBE_DEPTH)
39707
+ return false;
39708
+ for (const entry of entries) {
39709
+ const child = `${dir}/${entry}`;
39710
+ const childEntries = await ctx.list(child);
39711
+ if (childEntries.length === 0)
39712
+ continue;
39713
+ if (await subtreeHasManifest(child, childEntries, ctx, depth + 1))
39714
+ return true;
39715
+ }
39716
+ return false;
39717
+ }
39718
+ async function missingManifestDiagnostics(ctx) {
39719
+ const diagnostics = [];
39720
+ for (const name of await ctx.list(".")) {
39721
+ if (name.startsWith("."))
39722
+ continue;
39723
+ const entries = await ctx.list(name);
39724
+ if (entries.length === 0)
39725
+ continue;
39726
+ if (await subtreeHasManifest(name, entries, ctx, 1))
39727
+ continue;
39728
+ diagnostics.push({ file: name, issue: "missing-skill-md", detail: `no SKILL.md in ${name}/`, fixed: false });
39729
+ }
39730
+ return diagnostics;
39731
+ }
39188
39732
  async function validate(_c, changes, ctx) {
39189
39733
  const diagnostics = [];
39190
39734
  const seenDirs = new Set;
@@ -39202,6 +39746,7 @@ async function validate(_c, changes, ctx) {
39202
39746
  seenDirs.add(pkg.conceptId);
39203
39747
  diagnostics.push(...skillFieldDiagnostics(toPosix2(change.path), pkg.dirName, parseFrontmatter(raw).data));
39204
39748
  }
39749
+ diagnostics.push(...await missingManifestDiagnostics(ctx));
39205
39750
  return diagnostics;
39206
39751
  }
39207
39752
  var agentSkillsAdapter = {
@@ -39211,7 +39756,7 @@ var agentSkillsAdapter = {
39211
39756
  recognize,
39212
39757
  validate,
39213
39758
  placeNew(c, conceptId) {
39214
- return path14.join(c.root, conceptId, SKILL_MANIFEST);
39759
+ return path15.join(c.root, conceptId, SKILL_MANIFEST);
39215
39760
  },
39216
39761
  looksLikeRoot(root) {
39217
39762
  let entries;
@@ -39224,7 +39769,7 @@ var agentSkillsAdapter = {
39224
39769
  if (!entry.isDirectory())
39225
39770
  return false;
39226
39771
  try {
39227
- return fs12.existsSync(path14.join(root, entry.name, SKILL_MANIFEST));
39772
+ return fs12.existsSync(path15.join(root, entry.name, SKILL_MANIFEST));
39228
39773
  } catch {
39229
39774
  return false;
39230
39775
  }
@@ -39234,11 +39779,11 @@ var agentSkillsAdapter = {
39234
39779
 
39235
39780
  // src/core/adapter/adapters/akm-adapter.ts
39236
39781
  import fs15 from "fs";
39237
- import path16 from "path";
39782
+ import path17 from "path";
39238
39783
 
39239
39784
  // src/indexer/passes/metadata.ts
39240
39785
  import fs13 from "fs";
39241
- import path15 from "path";
39786
+ import path16 from "path";
39242
39787
  init_common();
39243
39788
  init_warn();
39244
39789
  var SCOPE_KEYS = ["user", "agent", "run", "channel"];
@@ -39979,7 +40524,7 @@ function extractBodyOpening(body) {
39979
40524
  return `${cut.trimEnd()}\u2026`;
39980
40525
  }
39981
40526
  function applyPreContributorFields(entry, file, ctx, pkgMeta) {
39982
- const ext = path15.extname(file).toLowerCase();
40527
+ const ext = path16.extname(file).toLowerCase();
39983
40528
  if (pkgMeta) {
39984
40529
  if (pkgMeta.description && !entry.description) {
39985
40530
  entry.description = pkgMeta.description;
@@ -40019,8 +40564,8 @@ function applyPreContributorFields(entry, file, ctx, pkgMeta) {
40019
40564
  }
40020
40565
  }
40021
40566
  function applyPostContributorFields(entry, file, canonicalName, dirPath) {
40022
- const ext = path15.extname(file).toLowerCase();
40023
- const baseName = path15.basename(file, ext);
40567
+ const ext = path16.extname(file).toLowerCase();
40568
+ const baseName = path16.basename(file, ext);
40024
40569
  if (!entry.description) {
40025
40570
  entry.description = fileNameToDescription(baseName);
40026
40571
  entry.source = "filename";
@@ -40032,7 +40577,7 @@ function applyPostContributorFields(entry, file, canonicalName, dirPath) {
40032
40577
  entry.tags = [...entry.tags ?? [], ...extractDirTagsFromName(canonicalName)];
40033
40578
  entry.tags = normalizeTerms(entry.tags ?? []);
40034
40579
  entry.aliases = mergeAliases(entry.aliases, buildAliases(canonicalName, entry.tags));
40035
- entry.filename = path15.basename(file);
40580
+ entry.filename = path16.basename(file);
40036
40581
  }
40037
40582
  function normalizeTerms(values) {
40038
40583
  const normalized = new Set;
@@ -40054,7 +40599,7 @@ function buildAliases(name, tags) {
40054
40599
  return Array.from(aliases);
40055
40600
  }
40056
40601
  function extractPackageMetadata(dirPath) {
40057
- const pkgPath = path15.join(dirPath, "package.json");
40602
+ const pkgPath = path16.join(dirPath, "package.json");
40058
40603
  if (!fs13.existsSync(pkgPath))
40059
40604
  return null;
40060
40605
  try {
@@ -40076,11 +40621,11 @@ function fileNameToDescription(fileName) {
40076
40621
  return fileName.replace(/[-_]+/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().trim();
40077
40622
  }
40078
40623
  function extractTagsFromPath(filePath, rootDir) {
40079
- const rel = path15.relative(rootDir, filePath);
40080
- const parts = rel.split(path15.sep);
40624
+ const rel = path16.relative(rootDir, filePath);
40625
+ const parts = rel.split(path16.sep);
40081
40626
  const tags = new Set;
40082
40627
  for (const part of parts) {
40083
- const name = part.replace(path15.extname(part), "");
40628
+ const name = part.replace(path16.extname(part), "");
40084
40629
  for (const token of name.split(/[-_./\\]+/)) {
40085
40630
  const clean = token.toLowerCase().trim();
40086
40631
  if (clean && clean.length > 1)
@@ -40609,13 +41154,13 @@ function isReservedFileName(name) {
40609
41154
  }
40610
41155
  var WIKI_INFRA_FILES = new Set(["schema.md", "index.md", "log.md"]);
40611
41156
  function akmStashAbstains(root, absPath) {
40612
- const relPath = path16.relative(root, absPath);
40613
- if (!relPath || relPath.startsWith("..") || path16.isAbsolute(relPath))
41157
+ const relPath = path17.relative(root, absPath);
41158
+ if (!relPath || relPath.startsWith("..") || path17.isAbsolute(relPath))
40614
41159
  return false;
40615
41160
  const segments = relPath.split(/[\\/]+/).filter(Boolean);
40616
41161
  if (segments.length === 0)
40617
41162
  return false;
40618
- if (segments[0] === "env" && (absPath.endsWith(".env") || path16.basename(absPath) === ".env")) {
41163
+ if (segments[0] === "env" && (absPath.endsWith(".env") || path17.basename(absPath) === ".env")) {
40619
41164
  if (fs15.existsSync(absPath.replace(/\.env$/, ".sensitive")))
40620
41165
  return true;
40621
41166
  }
@@ -40713,7 +41258,7 @@ function recognize2(c, file) {
40713
41258
  const canonicalName = derived;
40714
41259
  const stashDir = stashDirFor(match.type);
40715
41260
  const conceptId = stashDir !== undefined ? `${stashDir}/${canonicalName}` : canonicalName;
40716
- const dirPath = path16.dirname(file.absPath);
41261
+ const dirPath = path17.dirname(file.absPath);
40717
41262
  const entry = {
40718
41263
  name: canonicalName,
40719
41264
  type: match.type,
@@ -40736,13 +41281,13 @@ function recognize2(c, file) {
40736
41281
  }, match.renderer);
40737
41282
  }
40738
41283
  function buildOverlayContext(root, relPathInput, raw) {
40739
- const absPath = path16.join(root, relPathInput);
40740
- const relPath = path16.relative(root, absPath).replace(/\\/g, "/");
40741
- const ext = path16.extname(absPath).toLowerCase();
40742
- const fileName = path16.basename(absPath);
40743
- const parentDirAbs = path16.dirname(absPath);
40744
- const parentDir = path16.basename(parentDirAbs);
40745
- const relDir = path16.dirname(relPath).replace(/\\/g, "/");
41284
+ const absPath = path17.join(root, relPathInput);
41285
+ const relPath = path17.relative(root, absPath).replace(/\\/g, "/");
41286
+ const ext = path17.extname(absPath).toLowerCase();
41287
+ const fileName = path17.basename(absPath);
41288
+ const parentDirAbs = path17.dirname(absPath);
41289
+ const parentDir = path17.basename(parentDirAbs);
41290
+ const relDir = path17.dirname(relPath).replace(/\\/g, "/");
40746
41291
  const ancestorDirs = relDir === "." ? [] : relDir.split("/").filter((seg) => seg.length > 0);
40747
41292
  let cachedFrontmatter;
40748
41293
  let frontmatterComputed = false;
@@ -40783,15 +41328,16 @@ async function validate2(c, changes, ctx) {
40783
41328
  const type = match?.type;
40784
41329
  let parsed;
40785
41330
  if (type === "task") {
40786
- let data = {};
40787
- try {
40788
- const doc = $parse(raw);
40789
- if (doc && typeof doc === "object" && !Array.isArray(doc))
40790
- data = doc;
40791
- } catch {
40792
- data = {};
41331
+ const task = parseTaskYaml(raw);
41332
+ if (!task.ok) {
41333
+ diagnostics.push({
41334
+ file: change.path,
41335
+ issue: "invalid-task-yaml",
41336
+ detail: taskYamlParseDetail(task.error),
41337
+ fixed: false
41338
+ });
40793
41339
  }
40794
- parsed = { data, content: raw, frontmatter: null };
41340
+ parsed = { data: task.data, content: raw, frontmatter: null };
40795
41341
  } else {
40796
41342
  const p = parseFrontmatter(raw);
40797
41343
  parsed = { data: p.data, content: p.content, frontmatter: p.frontmatter };
@@ -40846,18 +41392,18 @@ var akmAdapter = {
40846
41392
  const rest = posix.slice(slash + 1);
40847
41393
  const type = stashDirToType(head);
40848
41394
  if (type !== undefined && rest.length > 0) {
40849
- const typeDir = path16.join(c.root, head);
41395
+ const typeDir = path17.join(c.root, head);
40850
41396
  return assetPathForName(type, typeDir, rest);
40851
41397
  }
40852
41398
  }
40853
- return path16.join(c.root, `${posix}.md`);
41399
+ return path17.join(c.root, `${posix}.md`);
40854
41400
  },
40855
41401
  directoryList(_c) {
40856
41402
  return [...new Set(stashDirNames())];
40857
41403
  },
40858
41404
  looksLikeRoot(root) {
40859
41405
  try {
40860
- if (fs15.statSync(path16.join(root, ".stash")).isDirectory())
41406
+ if (fs15.statSync(path17.join(root, ".stash")).isDirectory())
40861
41407
  return true;
40862
41408
  } catch {}
40863
41409
  const ownedDirNames = new Set(stashDirNames());
@@ -40875,10 +41421,10 @@ var akmAdapter = {
40875
41421
  return false;
40876
41422
  const expectedType = stashDirToType(only.name);
40877
41423
  try {
40878
- const markdown = fs15.readdirSync(path16.join(root, only.name), { withFileTypes: true }).find((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".md"));
41424
+ const markdown = fs15.readdirSync(path17.join(root, only.name), { withFileTypes: true }).find((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".md"));
40879
41425
  if (!markdown)
40880
41426
  return true;
40881
- const data = parseFrontmatter(fs15.readFileSync(path16.join(root, only.name, markdown.name), "utf8")).data;
41427
+ const data = parseFrontmatter(fs15.readFileSync(path17.join(root, only.name, markdown.name), "utf8")).data;
40882
41428
  const declaredType = typeof data.type === "string" ? data.type.trim() : "";
40883
41429
  return !declaredType || declaredType === expectedType;
40884
41430
  } catch {
@@ -40889,9 +41435,9 @@ var akmAdapter = {
40889
41435
 
40890
41436
  // src/core/adapter/adapters/akm-task-adapter.ts
40891
41437
  import fs16 from "fs";
40892
- import path17 from "path";
41438
+ import path18 from "path";
40893
41439
  var COMPONENT_ID2 = "main";
40894
- var TASK_EXT = ".yml";
41440
+ var TASK_EXT = TASK_EXTENSION;
40895
41441
  var TARGET_KEYS = ["prompt", "workflow", "command"];
40896
41442
  var MAX_CONTENT_CHARS2 = 1e5;
40897
41443
  function toPosix3(p) {
@@ -40916,14 +41462,6 @@ function recognize3(c, file) {
40916
41462
  content: raw.length > MAX_CONTENT_CHARS2 ? raw.slice(0, MAX_CONTENT_CHARS2) : raw
40917
41463
  };
40918
41464
  }
40919
- function parseTaskYaml(raw) {
40920
- try {
40921
- const doc = $parse(raw);
40922
- if (doc && typeof doc === "object" && !Array.isArray(doc))
40923
- return doc;
40924
- } catch {}
40925
- return {};
40926
- }
40927
41465
  function taskDiagnostics2(relPath, data) {
40928
41466
  if (Object.keys(data).length === 0)
40929
41467
  return [];
@@ -40947,21 +41485,41 @@ async function validate3(_c, changes, ctx) {
40947
41485
  const raw = change.after ?? await ctx.readFile(change.path);
40948
41486
  if (typeof raw !== "string")
40949
41487
  continue;
40950
- if (path17.extname(change.path).toLowerCase() !== TASK_EXT)
41488
+ const ext = path18.extname(change.path).toLowerCase();
41489
+ if (ext !== TASK_EXT && ext !== TASK_NEAR_MISS_EXTENSION)
40951
41490
  continue;
40952
- diagnostics.push(...taskDiagnostics2(toPosix3(change.path), parseTaskYaml(raw)));
41491
+ const relPath = toPosix3(change.path);
41492
+ if (ext === TASK_NEAR_MISS_EXTENSION) {
41493
+ diagnostics.push({
41494
+ file: relPath,
41495
+ issue: "invalid-task-yaml",
41496
+ detail: taskExtensionDetail(relPath),
41497
+ fixed: false
41498
+ });
41499
+ }
41500
+ const parsed = parseTaskYaml(raw);
41501
+ if (!parsed.ok) {
41502
+ diagnostics.push({
41503
+ file: relPath,
41504
+ issue: "invalid-task-yaml",
41505
+ detail: taskYamlParseDetail(parsed.error),
41506
+ fixed: false
41507
+ });
41508
+ continue;
41509
+ }
41510
+ diagnostics.push(...taskDiagnostics2(relPath, parsed.data));
40953
41511
  }
40954
41512
  return diagnostics;
40955
41513
  }
40956
41514
  var akmTaskAdapter = {
40957
41515
  id: "akm-task",
40958
41516
  version: "0.9.0",
40959
- extensions: [TASK_EXT],
41517
+ extensions: [TASK_EXT, TASK_NEAR_MISS_EXTENSION],
40960
41518
  recognize: recognize3,
40961
41519
  validate: validate3,
40962
41520
  placeNew(c, conceptId) {
40963
41521
  const posix = toPosix3(conceptId);
40964
- return path17.join(c.root, /\.yml$/i.test(posix) ? posix : `${posix}.yml`);
41522
+ return path18.join(c.root, /\.yml$/i.test(posix) ? posix : `${posix}.yml`);
40965
41523
  },
40966
41524
  directoryList() {
40967
41525
  return ["."];
@@ -40974,15 +41532,15 @@ var akmTaskAdapter = {
40974
41532
  return false;
40975
41533
  }
40976
41534
  for (const entry of entries) {
40977
- if (!entry.isFile() || path17.extname(entry.name).toLowerCase() !== TASK_EXT)
41535
+ if (!entry.isFile() || path18.extname(entry.name).toLowerCase() !== TASK_EXT)
40978
41536
  continue;
40979
41537
  let raw;
40980
41538
  try {
40981
- raw = fs16.readFileSync(path17.join(root, entry.name), "utf8");
41539
+ raw = fs16.readFileSync(path18.join(root, entry.name), "utf8");
40982
41540
  } catch {
40983
41541
  continue;
40984
41542
  }
40985
- const data = parseTaskYaml(raw);
41543
+ const { data } = parseTaskYaml(raw);
40986
41544
  if (typeof data.schedule === "string" && data.schedule.trim() !== "")
40987
41545
  return true;
40988
41546
  }
@@ -40992,7 +41550,7 @@ var akmTaskAdapter = {
40992
41550
 
40993
41551
  // src/core/adapter/adapters/akm-workflow-adapter.ts
40994
41552
  import fs17 from "fs";
40995
- import path18 from "path";
41553
+ import path19 from "path";
40996
41554
  var COMPONENT_ID3 = "main";
40997
41555
  var WORKFLOW_EXTS = new Set([".md"]);
40998
41556
  var MAX_CONTENT_CHARS3 = 1e5;
@@ -41050,8 +41608,8 @@ async function validate4(c, changes, ctx) {
41050
41608
  const raw = change.after ?? await ctx.readFile(change.path);
41051
41609
  if (typeof raw !== "string")
41052
41610
  continue;
41053
- const ext = path18.extname(change.path).toLowerCase();
41054
- if (!WORKFLOW_EXTS.has(ext) || isReservedDocFile(path18.basename(change.path)) || !isWorkflowFile(raw))
41611
+ const ext = path19.extname(change.path).toLowerCase();
41612
+ if (!WORKFLOW_EXTS.has(ext) || isReservedDocFile(path19.basename(change.path)) || !isWorkflowFile(raw))
41055
41613
  continue;
41056
41614
  const relPath = toPosix4(change.path);
41057
41615
  const p = parseFrontmatter(raw);
@@ -41074,12 +41632,12 @@ function hasTopLevelWorkflowFile(root, entries) {
41074
41632
  for (const entry of entries) {
41075
41633
  if (!entry.isFile())
41076
41634
  continue;
41077
- const ext = path18.extname(entry.name).toLowerCase();
41635
+ const ext = path19.extname(entry.name).toLowerCase();
41078
41636
  if (!WORKFLOW_EXTS.has(ext) || isReservedDocFile(entry.name))
41079
41637
  continue;
41080
41638
  let raw;
41081
41639
  try {
41082
- raw = fs17.readFileSync(path18.join(root, entry.name), "utf8");
41640
+ raw = fs17.readFileSync(path19.join(root, entry.name), "utf8");
41083
41641
  } catch {
41084
41642
  continue;
41085
41643
  }
@@ -41097,8 +41655,8 @@ var akmWorkflowAdapter = {
41097
41655
  placeNew(c, conceptId) {
41098
41656
  const posix = toPosix4(conceptId);
41099
41657
  if (/\.md$/i.test(posix))
41100
- return path18.join(c.root, posix);
41101
- return path18.join(c.root, `${posix}.md`);
41658
+ return path19.join(c.root, posix);
41659
+ return path19.join(c.root, `${posix}.md`);
41102
41660
  },
41103
41661
  directoryList() {
41104
41662
  return ["."];
@@ -41116,10 +41674,10 @@ var akmWorkflowAdapter = {
41116
41674
 
41117
41675
  // src/core/adapter/adapters/claude-adapter.ts
41118
41676
  import fs18 from "fs";
41119
- import path20 from "path";
41677
+ import path21 from "path";
41120
41678
 
41121
41679
  // src/core/adapter/adapters/tool-dir-shared.ts
41122
- import path19 from "path";
41680
+ import path20 from "path";
41123
41681
  var RESERVED_FILES2 = new Set(["index.md", "log.md"]);
41124
41682
  var MAX_CONTENT_CHARS4 = 1e5;
41125
41683
  var CANONICAL_COMMAND_DIR = "commands";
@@ -41144,7 +41702,7 @@ function classify(relPath, layout) {
41144
41702
  return { type: "instruction", conceptId: layout.instructionConceptId, name: layout.instructionConceptId };
41145
41703
  }
41146
41704
  const head = segs[0];
41147
- const ext = path19.extname(base).toLowerCase();
41705
+ const ext = path20.extname(base).toLowerCase();
41148
41706
  if (layout.skillDirs.has(head)) {
41149
41707
  if (segs.length >= 3 && base === SKILL_MANIFEST2) {
41150
41708
  return { type: "skill", conceptId: `${segs[0]}/${segs[1]}`, name: segs[1] };
@@ -41193,19 +41751,19 @@ function recognizeToolDir(layout, c, file) {
41193
41751
  function placeNewToolDir(layout, c, conceptId) {
41194
41752
  const posix = toPosix5(conceptId);
41195
41753
  if (posix === layout.instructionConceptId)
41196
- return path19.join(c.root, layout.instructionFile);
41754
+ return path20.join(c.root, layout.instructionFile);
41197
41755
  const segs = posix.split("/").filter((s) => s.length > 0);
41198
41756
  const head = segs[0];
41199
41757
  const rest = segs.slice(1).join("/");
41200
41758
  if (rest.length > 0) {
41201
41759
  if (layout.skillDirs.has(head))
41202
- return path19.join(c.root, CANONICAL_SKILL_DIR, rest, SKILL_MANIFEST2);
41760
+ return path20.join(c.root, CANONICAL_SKILL_DIR, rest, SKILL_MANIFEST2);
41203
41761
  if (layout.commandDirs.has(head))
41204
- return path19.join(c.root, CANONICAL_COMMAND_DIR, `${rest}.md`);
41762
+ return path20.join(c.root, CANONICAL_COMMAND_DIR, `${rest}.md`);
41205
41763
  if (layout.agentDirs.has(head))
41206
- return path19.join(c.root, CANONICAL_AGENT_DIR, `${rest}.md`);
41764
+ return path20.join(c.root, CANONICAL_AGENT_DIR, `${rest}.md`);
41207
41765
  }
41208
- return path19.join(c.root, `${posix}.md`);
41766
+ return path20.join(c.root, `${posix}.md`);
41209
41767
  }
41210
41768
  function nameOrSignalDiagnostics(type, relPath, data, body) {
41211
41769
  if (nonEmptyString2(data.name) !== undefined || nonEmptyString2(data.description) !== undefined)
@@ -41232,7 +41790,7 @@ async function validateToolDir(layout, c, changes, ctx) {
41232
41790
  if (typeof raw !== "string")
41233
41791
  continue;
41234
41792
  const relPath = toPosix5(change.path);
41235
- diagnostics.push(...await skillDirectoryDiagnostics(relPath, seenSkillDirs, ctx));
41793
+ diagnostics.push(...await skillDirectoryDiagnostics(relPath, seenSkillDirs, ctx, layout.skillDirs));
41236
41794
  const cls = classify(change.path, layout);
41237
41795
  if (cls === null)
41238
41796
  continue;
@@ -41277,18 +41835,18 @@ function dirExists(p) {
41277
41835
  }
41278
41836
  function claudeLooksLikeRoot(root) {
41279
41837
  try {
41280
- if (!fs18.existsSync(path20.join(root, "CLAUDE.md")))
41838
+ if (!fs18.existsSync(path21.join(root, "CLAUDE.md")))
41281
41839
  return false;
41282
41840
  } catch {
41283
41841
  return false;
41284
41842
  }
41285
- return ["commands", "agents", "skills"].some((d) => dirExists(path20.join(root, d)));
41843
+ return ["commands", "agents", "skills"].some((d) => dirExists(path21.join(root, d)));
41286
41844
  }
41287
41845
  var claudeAdapter = makeToolDirAdapter(LAYOUT, claudeLooksLikeRoot);
41288
41846
 
41289
41847
  // src/core/adapter/adapters/dotenv-adapter.ts
41290
41848
  import fs19 from "fs";
41291
- import path21 from "path";
41849
+ import path22 from "path";
41292
41850
  var COMPONENT_ID4 = "main";
41293
41851
  var ENV_DIR = "env";
41294
41852
  var SECRETS_DIR = "secrets";
@@ -41394,10 +41952,10 @@ var dotenvAdapter = {
41394
41952
  const rest = posix.slice(slash + 1);
41395
41953
  const type = typeForStashDir(head);
41396
41954
  if ((type === "env" || type === "secret") && rest.length > 0) {
41397
- return assetPathForName(type, path21.join(c.root, head), rest);
41955
+ return assetPathForName(type, path22.join(c.root, head), rest);
41398
41956
  }
41399
41957
  }
41400
- return path21.join(c.root, posix);
41958
+ return path22.join(c.root, posix);
41401
41959
  },
41402
41960
  directoryList() {
41403
41961
  return [ENV_DIR, SECRETS_DIR];
@@ -41419,7 +41977,7 @@ var dotenvAdapter = {
41419
41977
  };
41420
41978
 
41421
41979
  // src/core/adapter/adapters/generic-files-adapter.ts
41422
- import path22 from "path";
41980
+ import path23 from "path";
41423
41981
  var COMPONENT_ID5 = "main";
41424
41982
  var DOCUMENT_EXTENSIONS = new Set([".md", ".markdown", ".txt", ".text"]);
41425
41983
  var RESERVED_FILES3 = new Set(["index.md", "log.md"]);
@@ -41490,8 +42048,8 @@ var genericFilesAdapter = {
41490
42048
  validate: validate6,
41491
42049
  placeNew(c, conceptId) {
41492
42050
  const posix = toPosix7(conceptId);
41493
- const hasExt = path22.extname(posix) !== "";
41494
- return path22.join(c.root, hasExt ? posix : `${posix}.md`);
42051
+ const hasExt = path23.extname(posix) !== "";
42052
+ return path23.join(c.root, hasExt ? posix : `${posix}.md`);
41495
42053
  },
41496
42054
  looksLikeRoot() {
41497
42055
  return false;
@@ -41500,7 +42058,7 @@ var genericFilesAdapter = {
41500
42058
 
41501
42059
  // src/core/adapter/adapters/llm-wiki-adapter.ts
41502
42060
  import fs20 from "fs";
41503
- import path23 from "path";
42061
+ import path24 from "path";
41504
42062
  var WIKI_COMPONENT_ID = "main";
41505
42063
  var WIKI_SOURCE_TYPE = "wiki-source";
41506
42064
  var DEFAULT_PAGE_KIND = "note";
@@ -41562,7 +42120,7 @@ function resolveXref(xref, bundleId) {
41562
42120
  return target.length > 0 ? target : null;
41563
42121
  }
41564
42122
  function resolveBodyLinks(body, fileRelPath) {
41565
- const dir = path23.posix.dirname(toPosix8(fileRelPath));
42123
+ const dir = path24.posix.dirname(toPosix8(fileRelPath));
41566
42124
  const linkRe = /\[[^\]]*\]\(([^)]+)\)/g;
41567
42125
  const out = [];
41568
42126
  const seen = new Set;
@@ -41588,10 +42146,10 @@ function resolveBodyLinks(body, fileRelPath) {
41588
42146
  continue;
41589
42147
  let resolved;
41590
42148
  if (target.startsWith("/")) {
41591
- resolved = path23.posix.normalize(target.slice(1));
42149
+ resolved = path24.posix.normalize(target.slice(1));
41592
42150
  } else {
41593
42151
  const base = dir === "." ? "" : dir;
41594
- resolved = path23.posix.normalize(path23.posix.join(base, target));
42152
+ resolved = path24.posix.normalize(path24.posix.join(base, target));
41595
42153
  }
41596
42154
  if (resolved.startsWith("../") || resolved === ".." || resolved.startsWith("/"))
41597
42155
  continue;
@@ -41772,16 +42330,16 @@ var llmWikiAdapter = {
41772
42330
  recognize: recognize7,
41773
42331
  validate: validate7,
41774
42332
  placeNew(c, conceptId) {
41775
- return path23.join(c.root, `${conceptId}.md`);
42333
+ return path24.join(c.root, `${conceptId}.md`);
41776
42334
  },
41777
42335
  directoryList(_c) {
41778
42336
  return ["."];
41779
42337
  },
41780
42338
  looksLikeRoot(root) {
41781
42339
  try {
41782
- if (!fs20.existsSync(path23.join(root, "schema.md")))
42340
+ if (!fs20.existsSync(path24.join(root, "schema.md")))
41783
42341
  return false;
41784
- return fs20.statSync(path23.join(root, PAGES_SUBDIR)).isDirectory();
42342
+ return fs20.statSync(path24.join(root, PAGES_SUBDIR)).isDirectory();
41785
42343
  } catch {
41786
42344
  return false;
41787
42345
  }
@@ -41790,7 +42348,7 @@ var llmWikiAdapter = {
41790
42348
 
41791
42349
  // src/core/adapter/adapters/okf-adapter.ts
41792
42350
  import fs21 from "fs";
41793
- import path24 from "path";
42351
+ import path25 from "path";
41794
42352
  var CONSUMED_FRONTMATTER_KEYS = [
41795
42353
  "type",
41796
42354
  "title",
@@ -41804,11 +42362,11 @@ var CONSUMED_FRONTMATTER_KEYS = [
41804
42362
  "stale_after",
41805
42363
  "okf_version"
41806
42364
  ];
41807
- function isPlainObject2(value) {
42365
+ function isPlainObject3(value) {
41808
42366
  return value !== null && typeof value === "object" && !Array.isArray(value);
41809
42367
  }
41810
42368
  function parseActorMapping(value) {
41811
- if (!isPlainObject2(value))
42369
+ if (!isPlainObject3(value))
41812
42370
  return;
41813
42371
  const by = nonEmptyString2(value.by);
41814
42372
  if (by === undefined)
@@ -41833,7 +42391,7 @@ function parseOkfSources(value) {
41833
42391
  return;
41834
42392
  const out = [];
41835
42393
  for (const item of value) {
41836
- if (!isPlainObject2(item))
42394
+ if (!isPlainObject3(item))
41837
42395
  continue;
41838
42396
  const resource = nonEmptyString2(item.resource);
41839
42397
  if (resource === undefined)
@@ -41870,7 +42428,7 @@ function isReservedFileName2(name) {
41870
42428
  return RESERVED_FILES4.has(name.toLowerCase());
41871
42429
  }
41872
42430
  function resolveOkfLinks(body, fileRelPath) {
41873
- const dir = path24.posix.dirname(toPosix9(fileRelPath));
42431
+ const dir = path25.posix.dirname(toPosix9(fileRelPath));
41874
42432
  const definitions = new Map;
41875
42433
  for (const match of body.matchAll(/^\s*\[([^\]]+)\]:\s*(\S+)/gm)) {
41876
42434
  definitions.set(match[1].trim().toLowerCase(), match[2]);
@@ -41908,10 +42466,10 @@ function resolveOkfLinks(body, fileRelPath) {
41908
42466
  continue;
41909
42467
  let resolved;
41910
42468
  if (target.startsWith("/")) {
41911
- resolved = path24.posix.normalize(target.slice(1));
42469
+ resolved = path25.posix.normalize(target.slice(1));
41912
42470
  } else {
41913
42471
  const base = dir === "." ? "" : dir;
41914
- resolved = path24.posix.normalize(path24.posix.join(base, target));
42472
+ resolved = path25.posix.normalize(path25.posix.join(base, target));
41915
42473
  }
41916
42474
  if (resolved.startsWith("../") || resolved === ".." || resolved.startsWith("/"))
41917
42475
  continue;
@@ -41939,7 +42497,7 @@ function recognize8(c, file) {
41939
42497
  const description = nonEmptyString2(data.description);
41940
42498
  const tags = readTags2(data.tags);
41941
42499
  const links = resolveOkfLinks(body, file.relPath);
41942
- const generatedMapping = isPlainObject2(data.generated) ? data.generated : undefined;
42500
+ const generatedMapping = isPlainObject3(data.generated) ? data.generated : undefined;
41943
42501
  const generatedAt = generatedMapping ? nonEmptyString2(generatedMapping.at) : undefined;
41944
42502
  const generatedBy = generatedMapping ? nonEmptyString2(generatedMapping.by) : undefined;
41945
42503
  const legacyTimestamp = nonEmptyString2(data.timestamp);
@@ -42041,7 +42599,7 @@ var okfAdapter = {
42041
42599
  return ["."];
42042
42600
  },
42043
42601
  looksLikeRoot(root) {
42044
- if (fs21.existsSync(path24.join(root, "index.md")))
42602
+ if (fs21.existsSync(path25.join(root, "index.md")))
42045
42603
  return true;
42046
42604
  const stack = [root];
42047
42605
  while (stack.length > 0) {
@@ -42057,7 +42615,7 @@ var okfAdapter = {
42057
42615
  for (const entry of entries) {
42058
42616
  if (entry.isSymbolicLink() || entry.name === ".git")
42059
42617
  continue;
42060
- const absolute = path24.join(current, entry.name);
42618
+ const absolute = path25.join(current, entry.name);
42061
42619
  if (entry.isDirectory()) {
42062
42620
  stack.push(absolute);
42063
42621
  continue;
@@ -42075,7 +42633,7 @@ var okfAdapter = {
42075
42633
 
42076
42634
  // src/core/adapter/adapters/opencode-adapter.ts
42077
42635
  import fs22 from "fs";
42078
- import path25 from "path";
42636
+ import path26 from "path";
42079
42637
  var LAYOUT2 = {
42080
42638
  adapterId: "opencode",
42081
42639
  componentId: ".opencode",
@@ -42102,17 +42660,17 @@ function dirExists2(p) {
42102
42660
  }
42103
42661
  }
42104
42662
  function opencodeLooksLikeRoot(root) {
42105
- if (CONFIG_FILES.some((f) => fileExists(path25.join(root, f))))
42663
+ if (CONFIG_FILES.some((f) => fileExists(path26.join(root, f))))
42106
42664
  return true;
42107
- if (!fileExists(path25.join(root, "AGENTS.md")))
42665
+ if (!fileExists(path26.join(root, "AGENTS.md")))
42108
42666
  return false;
42109
- return TOOL_DIRS.some((d) => dirExists2(path25.join(root, d)));
42667
+ return TOOL_DIRS.some((d) => dirExists2(path26.join(root, d)));
42110
42668
  }
42111
42669
  var opencodeAdapter = makeToolDirAdapter(LAYOUT2, opencodeLooksLikeRoot);
42112
42670
 
42113
42671
  // src/core/adapter/adapters/website-snapshot-adapter.ts
42114
42672
  import fs23 from "fs";
42115
- import path26 from "path";
42673
+ import path27 from "path";
42116
42674
  var COMPONENT_ID6 = "main";
42117
42675
  var PAGES_PREFIX = "stash/knowledge/";
42118
42676
  var MANIFEST_FILE = "manifest.json";
@@ -42205,7 +42763,7 @@ var websiteSnapshotAdapter = {
42205
42763
  looksLikeRoot(root) {
42206
42764
  let raw;
42207
42765
  try {
42208
- raw = fs23.readFileSync(path26.join(root, MANIFEST_FILE), "utf8");
42766
+ raw = fs23.readFileSync(path27.join(root, MANIFEST_FILE), "utf8");
42209
42767
  } catch {
42210
42768
  return false;
42211
42769
  }
@@ -42247,7 +42805,7 @@ function expandTilde(p) {
42247
42805
  if (p === "~")
42248
42806
  return os.homedir();
42249
42807
  if (p.startsWith("~/") || p.startsWith("~\\"))
42250
- return path27.join(os.homedir(), p.slice(2));
42808
+ return path28.join(os.homedir(), p.slice(2));
42251
42809
  return p;
42252
42810
  }
42253
42811
  function readString(value) {
@@ -42292,7 +42850,7 @@ function oldConfigMigratableSources(raw, pathResolutionBase = process.cwd()) {
42292
42850
  const p = readString(primaryEntry.path);
42293
42851
  if (descriptor) {
42294
42852
  out.push({
42295
- derivationPath: p ? path27.resolve(pathResolutionBase, expandTilde(p)) : readString(primaryEntry.url) ?? "",
42853
+ derivationPath: p ? path28.resolve(pathResolutionBase, expandTilde(p)) : readString(primaryEntry.url) ?? "",
42296
42854
  registryId: readString(primaryEntry.name),
42297
42855
  writable: typeof primaryEntry.writable === "boolean" ? primaryEntry.writable : undefined,
42298
42856
  ...typeof primaryEntry.enabled === "boolean" ? { enabled: primaryEntry.enabled } : {},
@@ -42303,7 +42861,7 @@ function oldConfigMigratableSources(raw, pathResolutionBase = process.cwd()) {
42303
42861
  }
42304
42862
  if (!out.some((source) => source.primary) && stashDir) {
42305
42863
  out.push({
42306
- derivationPath: path27.resolve(pathResolutionBase, expandTilde(stashDir)),
42864
+ derivationPath: path28.resolve(pathResolutionBase, expandTilde(stashDir)),
42307
42865
  writable: true,
42308
42866
  primary: true,
42309
42867
  descriptor: { path: stashDir }
@@ -42317,7 +42875,7 @@ function oldConfigMigratableSources(raw, pathResolutionBase = process.cwd()) {
42317
42875
  continue;
42318
42876
  const p = readString(entry.path);
42319
42877
  out.push({
42320
- derivationPath: p ? path27.resolve(pathResolutionBase, expandTilde(p)) : readString(entry.url) ?? "",
42878
+ derivationPath: p ? path28.resolve(pathResolutionBase, expandTilde(p)) : readString(entry.url) ?? "",
42321
42879
  registryId: readString(entry.name),
42322
42880
  writable: typeof entry.writable === "boolean" ? entry.writable : undefined,
42323
42881
  ...typeof entry.enabled === "boolean" ? { enabled: entry.enabled } : {},
@@ -42331,7 +42889,7 @@ function oldConfigMigratableSources(raw, pathResolutionBase = process.cwd()) {
42331
42889
  const source = readString(entry.source);
42332
42890
  const ref = readString(entry.ref);
42333
42891
  const descriptor = source ? installedSourceDescriptor(source, ref, stashRoot) : { path: stashRoot };
42334
- const resolvedRoot = path27.resolve(pathResolutionBase, expandTilde(stashRoot));
42892
+ const resolvedRoot = path28.resolve(pathResolutionBase, expandTilde(stashRoot));
42335
42893
  const needsLock = (("git" in descriptor) || ("npm" in descriptor)) && !!source && !!ref;
42336
42894
  out.push({
42337
42895
  derivationPath: resolvedRoot,
@@ -42346,8 +42904,8 @@ function oldConfigMigratableSources(raw, pathResolutionBase = process.cwd()) {
42346
42904
  const byPath = new Map;
42347
42905
  for (const source of out) {
42348
42906
  let pathIdentity;
42349
- if (path27.isAbsolute(source.derivationPath)) {
42350
- const resolved = path27.resolve(source.derivationPath);
42907
+ if (path28.isAbsolute(source.derivationPath)) {
42908
+ const resolved = path28.resolve(source.derivationPath);
42351
42909
  try {
42352
42910
  pathIdentity = fs24.realpathSync(resolved);
42353
42911
  } catch {
@@ -42479,14 +43037,14 @@ function generateTargetConfig(raw, currentConfigVersion) {
42479
43037
  import { randomBytes } from "crypto";
42480
43038
  import fs27 from "fs";
42481
43039
  import os3 from "os";
42482
- import path30 from "path";
43040
+ import path31 from "path";
42483
43041
  init_common();
42484
43042
  init_warn();
42485
43043
 
42486
43044
  // scripts/akm-migrate/migrate/legacy/legacy-layout.ts
42487
43045
  import fs25 from "fs";
42488
43046
  import os2 from "os";
42489
- import path28 from "path";
43047
+ import path29 from "path";
42490
43048
  import { fileURLToPath as fileURLToPath2 } from "url";
42491
43049
 
42492
43050
  class LegacyResolverError extends Error {
@@ -42545,9 +43103,9 @@ function toPosix11(input) {
42545
43103
  return input.replace(/\\/g, "/");
42546
43104
  }
42547
43105
  var workflowSpec2 = {
42548
- isRelevantFile: (fileName) => WORKFLOW_EXTENSIONS2.includes(path28.extname(fileName).toLowerCase()),
43106
+ isRelevantFile: (fileName) => WORKFLOW_EXTENSIONS2.includes(path29.extname(fileName).toLowerCase()),
42549
43107
  toCanonicalName: (typeRoot, filePath) => {
42550
- const rel = toPosix11(path28.relative(typeRoot, filePath));
43108
+ const rel = toPosix11(path29.relative(typeRoot, filePath));
42551
43109
  for (const ext of WORKFLOW_EXTENSIONS2) {
42552
43110
  if (rel.toLowerCase().endsWith(ext))
42553
43111
  return rel.slice(0, -ext.length);
@@ -42558,43 +43116,43 @@ var workflowSpec2 = {
42558
43116
  const lower = name.toLowerCase();
42559
43117
  for (const ext of WORKFLOW_EXTENSIONS2) {
42560
43118
  if (lower.endsWith(ext))
42561
- return path28.join(typeRoot, name);
43119
+ return path29.join(typeRoot, name);
42562
43120
  }
42563
43121
  for (const ext of WORKFLOW_EXTENSIONS2) {
42564
- const candidate = path28.join(typeRoot, `${name}${ext}`);
43122
+ const candidate = path29.join(typeRoot, `${name}${ext}`);
42565
43123
  if (fs25.existsSync(candidate))
42566
43124
  return candidate;
42567
43125
  }
42568
- return path28.join(typeRoot, `${name}.md`);
43126
+ return path29.join(typeRoot, `${name}.md`);
42569
43127
  }
42570
43128
  };
42571
43129
  var markdownSpec2 = {
42572
- isRelevantFile: (fileName) => path28.extname(fileName).toLowerCase() === ".md",
43130
+ isRelevantFile: (fileName) => path29.extname(fileName).toLowerCase() === ".md",
42573
43131
  toCanonicalName: (typeRoot, filePath) => {
42574
- const rel = toPosix11(path28.relative(typeRoot, filePath));
43132
+ const rel = toPosix11(path29.relative(typeRoot, filePath));
42575
43133
  return rel.endsWith(".md") ? rel.slice(0, -3) : rel;
42576
43134
  },
42577
43135
  toAssetPath: (typeRoot, name) => {
42578
43136
  const withExt = name.endsWith(".md") ? name : `${name}.md`;
42579
- return path28.join(typeRoot, withExt);
43137
+ return path29.join(typeRoot, withExt);
42580
43138
  }
42581
43139
  };
42582
43140
  var scriptSpec2 = {
42583
- isRelevantFile: (fileName) => SCRIPT_EXTENSIONS2.has(path28.extname(fileName).toLowerCase()),
42584
- toCanonicalName: (typeRoot, filePath) => toPosix11(path28.relative(typeRoot, filePath)),
42585
- toAssetPath: (typeRoot, name) => path28.join(typeRoot, name)
43141
+ isRelevantFile: (fileName) => SCRIPT_EXTENSIONS2.has(path29.extname(fileName).toLowerCase()),
43142
+ toCanonicalName: (typeRoot, filePath) => toPosix11(path29.relative(typeRoot, filePath)),
43143
+ toAssetPath: (typeRoot, name) => path29.join(typeRoot, name)
42586
43144
  };
42587
43145
  var ASSET_SPECS_INTERNAL = {
42588
43146
  skill: {
42589
43147
  stashDir: "skills",
42590
43148
  isRelevantFile: (fileName) => fileName === "SKILL.md",
42591
43149
  toCanonicalName: (typeRoot, filePath) => {
42592
- const relDir = toPosix11(path28.dirname(path28.relative(typeRoot, filePath)));
43150
+ const relDir = toPosix11(path29.dirname(path29.relative(typeRoot, filePath)));
42593
43151
  if (!relDir || relDir === ".")
42594
43152
  return;
42595
43153
  return relDir;
42596
43154
  },
42597
- toAssetPath: (typeRoot, name) => path28.join(typeRoot, name, "SKILL.md")
43155
+ toAssetPath: (typeRoot, name) => path29.join(typeRoot, name, "SKILL.md")
42598
43156
  },
42599
43157
  command: { stashDir: "commands", ...markdownSpec2 },
42600
43158
  agent: { stashDir: "agents", ...markdownSpec2 },
@@ -42606,10 +43164,10 @@ var ASSET_SPECS_INTERNAL = {
42606
43164
  stashDir: "env",
42607
43165
  isRelevantFile: (fileName) => fileName === ".env" || fileName.endsWith(".env"),
42608
43166
  toCanonicalName: (typeRoot, filePath) => {
42609
- const rel = toPosix11(path28.relative(typeRoot, filePath));
42610
- const fileName = path28.basename(rel);
43167
+ const rel = toPosix11(path29.relative(typeRoot, filePath));
43168
+ const fileName = path29.basename(rel);
42611
43169
  if (fileName === ".env") {
42612
- const dir = path28.dirname(rel);
43170
+ const dir = path29.dirname(rel);
42613
43171
  return dir === "." || dir === "" ? "default" : `${dir}/default`;
42614
43172
  }
42615
43173
  const stripped = rel.endsWith(".env") ? rel.slice(0, -4) : rel;
@@ -42617,15 +43175,15 @@ var ASSET_SPECS_INTERNAL = {
42617
43175
  },
42618
43176
  toAssetPath: (typeRoot, name) => {
42619
43177
  if (name === "default")
42620
- return path28.join(typeRoot, ".env");
42621
- return path28.join(typeRoot, name.endsWith(".env") ? name : `${name}.env`);
43178
+ return path29.join(typeRoot, ".env");
43179
+ return path29.join(typeRoot, name.endsWith(".env") ? name : `${name}.env`);
42622
43180
  }
42623
43181
  },
42624
43182
  secret: {
42625
43183
  stashDir: "secrets",
42626
43184
  isRelevantFile: (fileName) => !fileName.endsWith(".lock") && !fileName.endsWith(".sensitive"),
42627
- toCanonicalName: (typeRoot, filePath) => toPosix11(path28.relative(typeRoot, filePath)),
42628
- toAssetPath: (typeRoot, name) => path28.join(typeRoot, name)
43185
+ toCanonicalName: (typeRoot, filePath) => toPosix11(path29.relative(typeRoot, filePath)),
43186
+ toAssetPath: (typeRoot, name) => path29.join(typeRoot, name)
42629
43187
  },
42630
43188
  wiki: {
42631
43189
  stashDir: "wikis",
@@ -42637,14 +43195,14 @@ var ASSET_SPECS_INTERNAL = {
42637
43195
  },
42638
43196
  task: {
42639
43197
  stashDir: "tasks",
42640
- isRelevantFile: (fileName) => path28.extname(fileName).toLowerCase() === ".yml",
43198
+ isRelevantFile: (fileName) => path29.extname(fileName).toLowerCase() === ".yml",
42641
43199
  toCanonicalName: (typeRoot, filePath) => {
42642
- const rel = toPosix11(path28.relative(typeRoot, filePath));
43200
+ const rel = toPosix11(path29.relative(typeRoot, filePath));
42643
43201
  return rel.endsWith(".yml") ? rel.slice(0, -4) : rel;
42644
43202
  },
42645
43203
  toAssetPath: (typeRoot, name) => {
42646
43204
  const withExt = name.endsWith(".yml") ? name : `${name}.yml`;
42647
- return path28.join(typeRoot, withExt);
43205
+ return path29.join(typeRoot, withExt);
42648
43206
  }
42649
43207
  },
42650
43208
  session: {
@@ -42685,8 +43243,8 @@ function resolveSourcesForOrigin(origin, allSources) {
42685
43243
  if (byParsedId.length > 0)
42686
43244
  return byParsedId;
42687
43245
  } catch {}
42688
- const resolvedOrigin = path28.resolve(origin);
42689
- const byPath = allSources.filter((s) => path28.resolve(s.path) === resolvedOrigin);
43246
+ const resolvedOrigin = path29.resolve(origin);
43247
+ const byPath = allSources.filter((s) => path29.resolve(s.path) === resolvedOrigin);
42690
43248
  if (byPath.length > 0)
42691
43249
  return byPath;
42692
43250
  return [];
@@ -42827,7 +43385,7 @@ function parseGitUrl(input, originalRef) {
42827
43385
  };
42828
43386
  }
42829
43387
  function tryParseLocalRef(rawRef, explicitPath) {
42830
- const resolvedPath = path28.resolve(rawRef);
43388
+ const resolvedPath = path29.resolve(rawRef);
42831
43389
  let stat;
42832
43390
  try {
42833
43391
  stat = fs25.statSync(resolvedPath);
@@ -42855,7 +43413,7 @@ function isPathLikeRef(ref) {
42855
43413
  return false;
42856
43414
  if (ref === "." || ref === "..")
42857
43415
  return true;
42858
- if (path28.isAbsolute(ref))
43416
+ if (path29.isAbsolute(ref))
42859
43417
  return true;
42860
43418
  if (ref.startsWith("./") || ref.startsWith("../") || ref.startsWith(".\\") || ref.startsWith("..\\")) {
42861
43419
  return true;
@@ -42924,7 +43482,7 @@ function toReadableLocalId(absolutePath) {
42924
43482
  const home = os2.homedir();
42925
43483
  if (absolutePath === home)
42926
43484
  return "~";
42927
- if (absolutePath.startsWith(home + path28.sep)) {
43485
+ if (absolutePath.startsWith(home + path29.sep)) {
42928
43486
  return `~/${absolutePath.slice(home.length + 1)}`;
42929
43487
  }
42930
43488
  return absolutePath;
@@ -42934,10 +43492,10 @@ function toReadableLocalId(absolutePath) {
42934
43492
  init_common();
42935
43493
  init_warn();
42936
43494
  import fs26 from "fs";
42937
- import path29 from "path";
43495
+ import path30 from "path";
42938
43496
  var LEGACY_STASH_FILENAME = ".stash.json";
42939
43497
  function legacyStashFilePath(dirPath) {
42940
- return path29.join(dirPath, LEGACY_STASH_FILENAME);
43498
+ return path30.join(dirPath, LEGACY_STASH_FILENAME);
42941
43499
  }
42942
43500
  function inspectLegacyStashOverrides(dirPath, options) {
42943
43501
  const filePath = legacyStashFilePath(dirPath);
@@ -43051,7 +43609,7 @@ function collectDirs(root) {
43051
43609
  }
43052
43610
  for (const entry of entries) {
43053
43611
  if (entry.isDirectory() && !entry.name.startsWith("."))
43054
- walk(path30.join(dir, entry.name));
43612
+ walk(path31.join(dir, entry.name));
43055
43613
  }
43056
43614
  };
43057
43615
  walk(root);
@@ -43062,7 +43620,7 @@ function runContentMigration(stashRoots, options = {}) {
43062
43620
  const seen = new Set;
43063
43621
  const roots = [];
43064
43622
  for (const root of stashRoots) {
43065
- const resolved = path30.resolve(root);
43623
+ const resolved = path31.resolve(root);
43066
43624
  if (seen.has(resolved) || !safeIsDir(resolved))
43067
43625
  continue;
43068
43626
  seen.add(resolved);
@@ -43076,7 +43634,7 @@ function runContentMigration(stashRoots, options = {}) {
43076
43634
  rewriteSourceBackrefsInDir(dir, report);
43077
43635
  }
43078
43636
  const operationId = options.operationId ?? `direct-${process.pid}-${randomBytes(8).toString("hex")}`;
43079
- const batchPath = options.renameBatchPath ?? path30.join(os3.tmpdir(), `akm-reserved-renames-${operationId}-${randomBytes(8).toString("hex")}.json`);
43637
+ const batchPath = options.renameBatchPath ?? path31.join(os3.tmpdir(), `akm-reserved-renames-${operationId}-${randomBytes(8).toString("hex")}.json`);
43080
43638
  let batch;
43081
43639
  try {
43082
43640
  batch = loadReservedRenameBatch(batchPath, operationId);
@@ -43101,9 +43659,9 @@ function rewriteSourceBackrefsInDir(dir, report) {
43101
43659
  throw error2;
43102
43660
  }
43103
43661
  for (const entry of entries) {
43104
- if (!entry.isFile() || path30.extname(entry.name).toLowerCase() !== ".md")
43662
+ if (!entry.isFile() || path31.extname(entry.name).toLowerCase() !== ".md")
43105
43663
  continue;
43106
- const filePath = path30.join(dir, entry.name);
43664
+ const filePath = path31.join(dir, entry.name);
43107
43665
  try {
43108
43666
  const source = asNonEmptyString(parseFrontmatter(fs27.readFileSync(filePath, "utf8")).data.source);
43109
43667
  const rewritten = legacyMemoryBackrefToConceptId(source);
@@ -43159,9 +43717,9 @@ function foldEntry(dir, entry, report) {
43159
43717
  report.entriesSkipped++;
43160
43718
  return false;
43161
43719
  }
43162
- const target = path30.resolve(dir, entry.filename);
43163
- const relative = path30.relative(path30.resolve(dir), target);
43164
- if (relative.startsWith("..") || path30.isAbsolute(relative)) {
43720
+ const target = path31.resolve(dir, entry.filename);
43721
+ const relative = path31.relative(path31.resolve(dir), target);
43722
+ if (relative.startsWith("..") || path31.isAbsolute(relative)) {
43165
43723
  report.entriesSkipped++;
43166
43724
  return false;
43167
43725
  }
@@ -43173,13 +43731,13 @@ function foldEntry(dir, entry, report) {
43173
43731
  throw error2;
43174
43732
  targetExists = false;
43175
43733
  }
43176
- if (!targetExists || path30.extname(target).toLowerCase() !== ".md") {
43734
+ if (!targetExists || path31.extname(target).toLowerCase() !== ".md") {
43177
43735
  report.entriesSkipped++;
43178
43736
  return false;
43179
43737
  }
43180
43738
  try {
43181
- const realRelative = path30.relative(fs27.realpathSync(dir), fs27.realpathSync(target));
43182
- if (realRelative.startsWith("..") || path30.isAbsolute(realRelative)) {
43739
+ const realRelative = path31.relative(fs27.realpathSync(dir), fs27.realpathSync(target));
43740
+ if (realRelative.startsWith("..") || path31.isAbsolute(realRelative)) {
43183
43741
  report.entriesSkipped++;
43184
43742
  return false;
43185
43743
  }
@@ -43205,8 +43763,8 @@ function foldCuratedFields(existing, entry) {
43205
43763
  return next;
43206
43764
  }
43207
43765
  function mutateFrontmatterAtomic(filePath, mutate) {
43208
- const directory = path30.dirname(filePath);
43209
- const temporary = path30.join(directory, `.${path30.basename(filePath)}.akm-migrate-${process.pid}-${randomBytes(8).toString("hex")}`);
43766
+ const directory = path31.dirname(filePath);
43767
+ const temporary = path31.join(directory, `.${path31.basename(filePath)}.akm-migrate-${process.pid}-${randomBytes(8).toString("hex")}`);
43210
43768
  try {
43211
43769
  const mode = fs27.statSync(filePath).mode & 511;
43212
43770
  fs27.copyFileSync(filePath, temporary, fs27.constants.COPYFILE_EXCL);
@@ -43229,7 +43787,7 @@ function reservedConceptsInDir(root, dir) {
43229
43787
  for (const entry of entries) {
43230
43788
  if (!entry.isFile() || !RESERVED_BASENAMES.has(entry.name.toLowerCase()))
43231
43789
  continue;
43232
- const filePath = path30.join(dir, entry.name);
43790
+ const filePath = path31.join(dir, entry.name);
43233
43791
  const legacyType = legacyTypeForDirectory(root, dir);
43234
43792
  if (legacyType === "wiki")
43235
43793
  continue;
@@ -43241,13 +43799,13 @@ function reservedConceptsInDir(root, dir) {
43241
43799
  }
43242
43800
  function planReservedRenameBatch(stashRoots, batchPath, operationId) {
43243
43801
  const entries = [];
43244
- for (const root of stashRoots.map((value) => path30.resolve(value))) {
43802
+ for (const root of stashRoots.map((value) => path31.resolve(value))) {
43245
43803
  for (const dir of collectDirs(root)) {
43246
43804
  entries.push(...reservedConceptsInDir(root, dir));
43247
43805
  }
43248
43806
  }
43249
43807
  const batch = { formatVersion: 1, operationId, entries };
43250
- fs27.mkdirSync(path30.dirname(batchPath), { recursive: true, mode: 448 });
43808
+ fs27.mkdirSync(path31.dirname(batchPath), { recursive: true, mode: 448 });
43251
43809
  writeFileAtomic(batchPath, `${JSON.stringify(batch, null, 2)}
43252
43810
  `, 384);
43253
43811
  return batch;
@@ -43287,21 +43845,21 @@ function applyReservedRenameBatch(batch, operationId) {
43287
43845
  if (targetExists)
43288
43846
  throw new Error(`Reserved rename target already exists: ${entry.to}`);
43289
43847
  fs27.renameSync(entry.from, entry.to);
43290
- syncRenameDirectory(path30.dirname(entry.to));
43848
+ syncRenameDirectory(path31.dirname(entry.to));
43291
43849
  }
43292
43850
  }
43293
43851
  function legacyTypeForDirectory(root, dir) {
43294
- const [top] = path30.relative(root, dir).split(path30.sep);
43852
+ const [top] = path31.relative(root, dir).split(path31.sep);
43295
43853
  if (!top || top === "..")
43296
43854
  return;
43297
43855
  const exact = Object.entries(TYPE_DIRS).find(([, typeDir]) => typeDir === top)?.[0];
43298
43856
  if (exact)
43299
43857
  return exact;
43300
- const actualRoot = path30.join(root, top);
43858
+ const actualRoot = path31.join(root, top);
43301
43859
  try {
43302
43860
  const actualIdentity = fs27.realpathSync(actualRoot);
43303
43861
  return Object.entries(TYPE_DIRS).find(([, typeDir]) => {
43304
- const canonicalRoot = path30.join(root, typeDir);
43862
+ const canonicalRoot = path31.join(root, typeDir);
43305
43863
  return fs27.existsSync(canonicalRoot) && fs27.realpathSync(canonicalRoot) === actualIdentity;
43306
43864
  })?.[0];
43307
43865
  } catch (error2) {
@@ -43319,10 +43877,10 @@ function carriesAssetFrontmatter(filePath) {
43319
43877
  }
43320
43878
  function collisionSafeTarget(dir, basename) {
43321
43879
  const stem = basename.slice(0, basename.length - ".md".length);
43322
- let candidate = path30.join(dir, `${stem}-content.md`);
43880
+ let candidate = path31.join(dir, `${stem}-content.md`);
43323
43881
  let suffix = 2;
43324
43882
  while (pathExists(candidate)) {
43325
- candidate = path30.join(dir, `${stem}-content-${suffix}.md`);
43883
+ candidate = path31.join(dir, `${stem}-content-${suffix}.md`);
43326
43884
  suffix++;
43327
43885
  }
43328
43886
  return candidate;
@@ -43343,14 +43901,14 @@ function errMsg(error2) {
43343
43901
 
43344
43902
  // scripts/akm-migrate/migrate/legacy/legacy-paths.ts
43345
43903
  init_paths();
43346
- import path31 from "path";
43904
+ import path32 from "path";
43347
43905
  function getLegacyWorkflowDbPath() {
43348
- return path31.join(getDataDir(), "workflow.db");
43906
+ return path32.join(getDataDir(), "workflow.db");
43349
43907
  }
43350
43908
 
43351
43909
  // scripts/akm-migrate/migrate/legacy/proposal-fs-import.ts
43352
43910
  import fs29 from "fs";
43353
- import path34 from "path";
43911
+ import path35 from "path";
43354
43912
  init_warn();
43355
43913
 
43356
43914
  // src/registry/resolve.ts
@@ -43359,7 +43917,7 @@ init_errors();
43359
43917
  import { spawnSync as spawnSync3 } from "child_process";
43360
43918
  import fs28 from "fs";
43361
43919
  import os4 from "os";
43362
- import path32 from "path";
43920
+ import path33 from "path";
43363
43921
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
43364
43922
 
43365
43923
  // src/integrations/github.ts
@@ -43598,7 +44156,7 @@ function parseGitUrl2(input, originalRef) {
43598
44156
  };
43599
44157
  }
43600
44158
  function tryParseLocalRef2(rawRef, explicitPath) {
43601
- const resolvedPath = path32.resolve(rawRef);
44159
+ const resolvedPath = path33.resolve(rawRef);
43602
44160
  let stat;
43603
44161
  try {
43604
44162
  stat = fs28.statSync(resolvedPath);
@@ -43628,7 +44186,7 @@ function isPathLikeRef2(ref) {
43628
44186
  return false;
43629
44187
  if (ref === "." || ref === "..")
43630
44188
  return true;
43631
- if (path32.isAbsolute(ref))
44189
+ if (path33.isAbsolute(ref))
43632
44190
  return true;
43633
44191
  if (ref.startsWith("./") || ref.startsWith("../") || ref.startsWith(".\\") || ref.startsWith("..\\")) {
43634
44192
  return true;
@@ -43881,18 +44439,18 @@ function toReadableLocalId2(absolutePath) {
43881
44439
  const home = os4.homedir();
43882
44440
  if (absolutePath === home)
43883
44441
  return "~";
43884
- if (absolutePath.startsWith(home + path32.sep)) {
44442
+ if (absolutePath.startsWith(home + path33.sep)) {
43885
44443
  return `~/${absolutePath.slice(home.length + 1)}`;
43886
44444
  }
43887
44445
  return absolutePath;
43888
44446
  }
43889
44447
  function findGitRepoRoot(startDir) {
43890
- let current = path32.resolve(startDir);
44448
+ let current = path33.resolve(startDir);
43891
44449
  while (true) {
43892
- if (fs28.existsSync(path32.join(current, ".git"))) {
44450
+ if (fs28.existsSync(path33.join(current, ".git"))) {
43893
44451
  return current;
43894
44452
  }
43895
- const parent = path32.dirname(current);
44453
+ const parent = path33.dirname(current);
43896
44454
  if (parent === current)
43897
44455
  return;
43898
44456
  current = parent;
@@ -43924,7 +44482,7 @@ async function tryFetchJson(url, headers) {
43924
44482
  init_database();
43925
44483
 
43926
44484
  // scripts/akm-migrate/migrate/legacy-ref-grammar.ts
43927
- import path33 from "path";
44485
+ import path34 from "path";
43928
44486
  init_errors();
43929
44487
  var TYPE_ALIASES = {
43930
44488
  environment: "env"
@@ -43969,8 +44527,8 @@ function validateName2(name) {
43969
44527
  throw new UsageError("Null byte in asset name.", "MISSING_REQUIRED_ARGUMENT");
43970
44528
  if (/^[A-Za-z]:/.test(name))
43971
44529
  throw new UsageError("Windows drive path in asset name.", "MISSING_REQUIRED_ARGUMENT");
43972
- const normalized = path33.posix.normalize(name.replace(/\\/g, "/"));
43973
- if (path33.posix.isAbsolute(normalized))
44530
+ const normalized = path34.posix.normalize(name.replace(/\\/g, "/"));
44531
+ if (path34.posix.isAbsolute(normalized))
43974
44532
  throw new UsageError("Absolute path in asset name.", "MISSING_REQUIRED_ARGUMENT");
43975
44533
  if (normalized === ".." || normalized.startsWith("../")) {
43976
44534
  throw new UsageError("Path traversal in asset name.", "MISSING_REQUIRED_ARGUMENT");
@@ -43981,7 +44539,7 @@ function validateName2(name) {
43981
44539
  }
43982
44540
  }
43983
44541
  function normalizeName2(name) {
43984
- return path33.posix.normalize(name.replace(/\\/g, "/"));
44542
+ return path34.posix.normalize(name.replace(/\\/g, "/"));
43985
44543
  }
43986
44544
  function classifyRefGrammar(raw) {
43987
44545
  const trimmed = raw.trim();
@@ -44029,8 +44587,8 @@ function parseStoredRef(raw) {
44029
44587
 
44030
44588
  // scripts/akm-migrate/migrate/legacy/proposal-fs-import.ts
44031
44589
  function legacyProposalsRoot(stashDir, archive) {
44032
- const root = path34.join(stashDir, ".akm", "proposals");
44033
- return archive ? path34.join(root, "archive") : root;
44590
+ const root = path35.join(stashDir, ".akm", "proposals");
44591
+ return archive ? path35.join(root, "archive") : root;
44034
44592
  }
44035
44593
  function importLegacyProposalsIntoState(stateDbPath, stashRoots, finalRefMap = new Map) {
44036
44594
  try {
@@ -44055,7 +44613,7 @@ function importLegacyProposalsIntoState(stateDbPath, stashRoots, finalRefMap = n
44055
44613
  aliasCandidates.set(alias, candidates);
44056
44614
  };
44057
44615
  for (const root of stashRoots) {
44058
- const resolvedRoot = path34.resolve(root.path);
44616
+ const resolvedRoot = path35.resolve(root.path);
44059
44617
  const roots = bundleRootCandidates.get(root.bundleId) ?? new Set;
44060
44618
  roots.add(resolvedRoot);
44061
44619
  bundleRootCandidates.set(root.bundleId, roots);
@@ -44091,7 +44649,7 @@ function importLegacyProposalsIntoState(stateDbPath, stashRoots, finalRefMap = n
44091
44649
  bundleAliases.set(alias, bundleId);
44092
44650
  }
44093
44651
  for (const root of stashRoots) {
44094
- const resolved = path34.resolve(root.path);
44652
+ const resolved = path35.resolve(root.path);
44095
44653
  if (seen.has(resolved))
44096
44654
  continue;
44097
44655
  seen.add(resolved);
@@ -44125,7 +44683,7 @@ function importLegacyProposalsForStash(db, stash, bundleAliases, bundleRoots, fi
44125
44683
  for (const entry of entries) {
44126
44684
  if (!entry.isDirectory() || entry.name === "archive")
44127
44685
  continue;
44128
- const proposalDir = path34.join(root, entry.name);
44686
+ const proposalDir = path35.join(root, entry.name);
44129
44687
  const proposal = readLegacyProposalFile(proposalDir, stash, bundleAliases, bundleRoots);
44130
44688
  if (!proposal)
44131
44689
  continue;
@@ -44192,7 +44750,7 @@ function insertProposalIfAbsent(db, proposal, stashDir) {
44192
44750
  return false;
44193
44751
  }
44194
44752
  function readLegacyProposalFile(proposalDir, stash, bundleAliases, bundleRoots) {
44195
- const filePath = path34.join(proposalDir, "proposal.json");
44753
+ const filePath = path35.join(proposalDir, "proposal.json");
44196
44754
  let parsed;
44197
44755
  let source;
44198
44756
  try {
@@ -44227,7 +44785,7 @@ function readLegacyProposalFile(proposalDir, stash, bundleAliases, bundleRoots)
44227
44785
  normalizedOrigin = parseRegistryRef2(legacyOrigin).id;
44228
44786
  } catch {}
44229
44787
  }
44230
- const bundle = explicitOrigin ? bundleAliases.get(legacyOrigin) ?? (normalizedOrigin ? bundleAliases.get(normalizedOrigin) : undefined) ?? bundleAliases.get(path34.resolve(legacyOrigin)) : translated.bundle ? bundleAliases.get(translated.bundle) : stash.bundleId;
44788
+ const bundle = explicitOrigin ? bundleAliases.get(legacyOrigin) ?? (normalizedOrigin ? bundleAliases.get(normalizedOrigin) : undefined) ?? bundleAliases.get(path35.resolve(legacyOrigin)) : translated.bundle ? bundleAliases.get(translated.bundle) : stash.bundleId;
44231
44789
  if (!bundle || !bundleRoots.has(bundle))
44232
44790
  throw new Error(`unmapped legacy proposal origin: ${legacyOrigin}`);
44233
44791
  migratedBundle = bundle;
@@ -44252,13 +44810,13 @@ function readLegacyProposalFile(proposalDir, stash, bundleAliases, bundleRoots)
44252
44810
  let backupContent;
44253
44811
  if (typeof backup === "string" && backup.length > 0) {
44254
44812
  try {
44255
- backupContent = fs29.readFileSync(path34.join(proposalDir, backup), "utf8");
44813
+ backupContent = fs29.readFileSync(path35.join(proposalDir, backup), "utf8");
44256
44814
  } catch (error2) {
44257
44815
  if (error2.code !== "ENOENT")
44258
44816
  throw error2;
44259
44817
  }
44260
44818
  }
44261
- const migratedRoot = path34.resolve(bundleRoots.get(migratedBundle));
44819
+ const migratedRoot = path35.resolve(bundleRoots.get(migratedBundle));
44262
44820
  return {
44263
44821
  ...rest,
44264
44822
  ref: migratedRef,
@@ -44284,12 +44842,12 @@ function errMsg2(error2) {
44284
44842
  import crypto4 from "crypto";
44285
44843
  import fs40 from "fs";
44286
44844
  import os5 from "os";
44287
- import path45 from "path";
44845
+ import path46 from "path";
44288
44846
  init_errors();
44289
44847
 
44290
44848
  // src/core/write-source.ts
44291
44849
  import fs35 from "fs";
44292
- import path39 from "path";
44850
+ import path40 from "path";
44293
44851
 
44294
44852
  // src/indexer/index-writer-lock.ts
44295
44853
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
@@ -44307,23 +44865,23 @@ init_paths();
44307
44865
  import { spawnSync as spawnSync4 } from "child_process";
44308
44866
  import { randomBytes as randomBytes2 } from "crypto";
44309
44867
  import fs32 from "fs";
44310
- import path37 from "path";
44868
+ import path38 from "path";
44311
44869
 
44312
44870
  // src/sources/providers/provider-utils.ts
44313
44871
  import { createHash as createHash2, randomUUID as randomUUID3 } from "crypto";
44314
44872
  import fs31 from "fs";
44315
- import path36 from "path";
44873
+ import path37 from "path";
44316
44874
  init_common();
44317
44875
  init_runtime();
44318
44876
 
44319
44877
  // src/sources/include.ts
44320
44878
  init_common();
44321
44879
  import fs30 from "fs";
44322
- import path35 from "path";
44880
+ import path36 from "path";
44323
44881
  var INCLUDE_CONFIG_KEYS = ["akm"];
44324
44882
  function readPackageJsonAt(dirPath) {
44325
44883
  try {
44326
- const raw = fs30.readFileSync(path35.join(dirPath, "package.json"), "utf8");
44884
+ const raw = fs30.readFileSync(path36.join(dirPath, "package.json"), "utf8");
44327
44885
  const parsed = JSON.parse(raw);
44328
44886
  if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
44329
44887
  return parsed;
@@ -44350,8 +44908,8 @@ function extractIncludeList(pkg) {
44350
44908
  return;
44351
44909
  }
44352
44910
  function findNearestIncludeConfig(startDir, boundary) {
44353
- let current = path35.resolve(startDir);
44354
- const resolvedBoundary = path35.resolve(boundary);
44911
+ let current = path36.resolve(startDir);
44912
+ const resolvedBoundary = path36.resolve(boundary);
44355
44913
  while (isWithin(current, resolvedBoundary)) {
44356
44914
  const pkg = readPackageJsonAt(current);
44357
44915
  const include = extractIncludeList(pkg);
@@ -44360,7 +44918,7 @@ function findNearestIncludeConfig(startDir, boundary) {
44360
44918
  }
44361
44919
  if (current === resolvedBoundary)
44362
44920
  break;
44363
- const parent = path35.dirname(current);
44921
+ const parent = path36.dirname(current);
44364
44922
  if (parent === current)
44365
44923
  break;
44366
44924
  current = parent;
@@ -44369,29 +44927,29 @@ function findNearestIncludeConfig(startDir, boundary) {
44369
44927
  }
44370
44928
  function copyIncludedPaths(includeGlobs, sourceDir, destDir) {
44371
44929
  for (const entry of includeGlobs) {
44372
- const resolvedSource = path35.resolve(sourceDir, entry);
44930
+ const resolvedSource = path36.resolve(sourceDir, entry);
44373
44931
  if (!isWithin(resolvedSource, sourceDir)) {
44374
44932
  throw new Error(`Path in akm.include escapes the package root: ${entry}`);
44375
44933
  }
44376
44934
  if (!fs30.existsSync(resolvedSource)) {
44377
44935
  throw new Error(`Path in akm.include does not exist: ${entry}`);
44378
44936
  }
44379
- if (path35.basename(resolvedSource) === ".git") {
44937
+ if (path36.basename(resolvedSource) === ".git") {
44380
44938
  continue;
44381
44939
  }
44382
- const relativePath = path35.relative(sourceDir, resolvedSource);
44940
+ const relativePath = path36.relative(sourceDir, resolvedSource);
44383
44941
  if (!relativePath || relativePath === ".") {
44384
44942
  copyDirectoryContents(sourceDir, destDir);
44385
44943
  continue;
44386
44944
  }
44387
- copyPath(resolvedSource, path35.join(destDir, relativePath));
44945
+ copyPath(resolvedSource, path36.join(destDir, relativePath));
44388
44946
  }
44389
44947
  }
44390
44948
  function copyDirectoryContents(sourceDir, destinationDir) {
44391
44949
  for (const entry of fs30.readdirSync(sourceDir, { withFileTypes: true })) {
44392
44950
  if (entry.name === ".git")
44393
44951
  continue;
44394
- copyPath(path35.join(sourceDir, entry.name), path35.join(destinationDir, entry.name));
44952
+ copyPath(path36.join(sourceDir, entry.name), path36.join(destinationDir, entry.name));
44395
44953
  }
44396
44954
  }
44397
44955
  function copyPath(sourcePath, destinationPath) {
@@ -44399,7 +44957,7 @@ function copyPath(sourcePath, destinationPath) {
44399
44957
  if (stat.isSymbolicLink()) {
44400
44958
  throw new Error(`Path in akm.include must not be a symlink: ${sourcePath}`);
44401
44959
  }
44402
- fs30.mkdirSync(path35.dirname(destinationPath), { recursive: true });
44960
+ fs30.mkdirSync(path36.dirname(destinationPath), { recursive: true });
44403
44961
  if (stat.isDirectory()) {
44404
44962
  fs30.mkdirSync(destinationPath, { recursive: true });
44405
44963
  copyDirectoryContents(sourcePath, destinationPath);
@@ -44428,7 +44986,7 @@ function looksLikeAnyBundleRoot(root) {
44428
44986
  return false;
44429
44987
  }
44430
44988
  function detectStashRoot(extractedDir) {
44431
- const root = path36.resolve(extractedDir);
44989
+ const root = path37.resolve(extractedDir);
44432
44990
  if (looksLikeAnyBundleRoot(root)) {
44433
44991
  return root;
44434
44992
  }
@@ -44445,13 +45003,13 @@ function buildInstallCacheDir(cacheRootDir, source, id, version) {
44445
45003
  const identitySuffix = version === "writable" ? `-${createHash2("sha256").update(`${source}:${id}`).digest("hex").slice(0, 12)}` : "";
44446
45004
  const slug = `${source}-${safeId}${identitySuffix}`;
44447
45005
  const versionSlug = source === "local" ? uniqueSlug() : version?.replace(/[^a-zA-Z0-9_.-]+/g, "-") ?? uniqueSlug();
44448
- return path36.join(cacheRootDir, slug || source, versionSlug);
45006
+ return path37.join(cacheRootDir, slug || source, versionSlug);
44449
45007
  }
44450
45008
  function applyAkmIncludeConfig(sourceRoot, cacheDir, searchRoot = sourceRoot) {
44451
45009
  const includeConfig = findNearestIncludeConfig(sourceRoot, searchRoot);
44452
45010
  if (!includeConfig)
44453
45011
  return;
44454
- const selectedDir = path36.join(cacheDir, "selected");
45012
+ const selectedDir = path37.join(cacheDir, "selected");
44455
45013
  fs31.rmSync(selectedDir, { recursive: true, force: true });
44456
45014
  fs31.mkdirSync(selectedDir, { recursive: true });
44457
45015
  copyIncludedPaths(includeConfig.include, includeConfig.baseDir, selectedDir);
@@ -44492,7 +45050,7 @@ function findShallowestStashRoot(root) {
44492
45050
  }
44493
45051
  const { dir: current, depth } = item;
44494
45052
  if (current !== root) {
44495
- if (isDirectory(path36.join(current, ".stash"))) {
45053
+ if (isDirectory(path37.join(current, ".stash"))) {
44496
45054
  return current;
44497
45055
  }
44498
45056
  if (countStashDirs(current) >= 2) {
@@ -44512,7 +45070,7 @@ function findShallowestStashRoot(root) {
44512
45070
  continue;
44513
45071
  if (child.name === ".git" || child.name === "node_modules")
44514
45072
  continue;
44515
- queue.push({ dir: path36.join(current, child.name), depth: depth + 1 });
45073
+ queue.push({ dir: path37.join(current, child.name), depth: depth + 1 });
44516
45074
  }
44517
45075
  }
44518
45076
  return;
@@ -44565,7 +45123,7 @@ function assertNoIgnoredPathOverwrite(repoDir, targetRevision) {
44565
45123
  const changedPaths = changed.stdout.split("\x00").filter(Boolean);
44566
45124
  const conflict = ignoredPaths.find((ignoredPath) => changedPaths.some((changedPath) => changedPath === ignoredPath || changedPath.startsWith(`${ignoredPath}/`) || ignoredPath.startsWith(`${changedPath}/`)));
44567
45125
  if (conflict) {
44568
- throw new UsageError(`Git update would overwrite ignored local path ${path37.join(repoDir, conflict)}; move or remove it before update.`);
45126
+ throw new UsageError(`Git update would overwrite ignored local path ${path38.join(repoDir, conflict)}; move or remove it before update.`);
44569
45127
  }
44570
45128
  }
44571
45129
  function replaceDirectory(stagedDir, destination) {
@@ -44601,7 +45159,7 @@ function cloneRepo(cloneUrl, ref, destDir, writable = false) {
44601
45159
  }
44602
45160
  try {
44603
45161
  if (!writable) {
44604
- const gitDir = path37.join(tmpDir, ".git");
45162
+ const gitDir = path38.join(tmpDir, ".git");
44605
45163
  if (fs32.existsSync(gitDir))
44606
45164
  fs32.rmSync(gitDir, { recursive: true, force: true });
44607
45165
  }
@@ -44639,7 +45197,7 @@ function redactUrlUserinfo(text) {
44639
45197
  // src/sources/providers/git-provider.ts
44640
45198
  import { createHash as createHash3 } from "crypto";
44641
45199
  import fs34 from "fs";
44642
- import path38 from "path";
45200
+ import path39 from "path";
44643
45201
  init_errors();
44644
45202
  init_paths();
44645
45203
 
@@ -44725,11 +45283,11 @@ registerSourceProvider("git", (config) => new GitSourceProvider(config));
44725
45283
  function getCachePaths(repoUrl) {
44726
45284
  const key = createHash3("sha256").update(repoUrl).digest("hex").slice(0, 16);
44727
45285
  const cacheRoot = getRegistryIndexCacheDir();
44728
- const rootDir = path38.join(cacheRoot, `git-${key}`);
45286
+ const rootDir = path39.join(cacheRoot, `git-${key}`);
44729
45287
  return {
44730
45288
  rootDir,
44731
- repoDir: path38.join(rootDir, "repo"),
44732
- indexPath: path38.join(rootDir, "index.json")
45289
+ repoDir: path39.join(rootDir, "repo"),
45290
+ indexPath: path39.join(rootDir, "index.json")
44733
45291
  };
44734
45292
  }
44735
45293
  async function ensureGitMirror(repo, cachePaths, options) {
@@ -44744,7 +45302,7 @@ async function ensureGitMirror(repo, cachePaths, options) {
44744
45302
  isUsable: () => !requireRepoDir || hasExtractedRepo(cachePaths.repoDir),
44745
45303
  refresh: async () => {
44746
45304
  fs34.mkdirSync(cachePaths.rootDir, { recursive: true });
44747
- if (writable && fs34.existsSync(path38.join(cachePaths.repoDir, ".git"))) {
45305
+ if (writable && fs34.existsSync(path39.join(cachePaths.repoDir, ".git"))) {
44748
45306
  pullRepo(cachePaths.repoDir);
44749
45307
  } else {
44750
45308
  cloneRepo(repo.cloneUrl, repo.ref, cachePaths.repoDir, writable);
@@ -44805,14 +45363,14 @@ function hasExtractedRepo(repoDir) {
44805
45363
  try {
44806
45364
  if (!fs34.statSync(repoDir).isDirectory())
44807
45365
  return false;
44808
- if (fs34.statSync(path38.join(repoDir, "content")).isDirectory())
45366
+ if (fs34.statSync(path39.join(repoDir, "content")).isDirectory())
44809
45367
  return true;
44810
45368
  } catch {}
44811
45369
  try {
44812
45370
  if (!fs34.statSync(repoDir).isDirectory())
44813
45371
  return false;
44814
45372
  const ownedDirs = akmAdapter.directoryList?.({ id: "akm", adapter: "akm", root: repoDir, writable: false }) ?? stashDirNames();
44815
- return ownedDirs.some((dirName) => fs34.existsSync(path38.join(repoDir, dirName)));
45373
+ return ownedDirs.some((dirName) => fs34.existsSync(path39.join(repoDir, dirName)));
44816
45374
  } catch {
44817
45375
  return false;
44818
45376
  }
@@ -44888,7 +45446,7 @@ function resolveWritable(entry) {
44888
45446
  return entry.type === "filesystem";
44889
45447
  }
44890
45448
  function resolveGitContentRoot(repoPath) {
44891
- const contentPath = path39.join(repoPath, "content");
45449
+ const contentPath = path40.join(repoPath, "content");
44892
45450
  return fs35.existsSync(contentPath) && fs35.statSync(contentPath).isDirectory() ? contentPath : repoPath;
44893
45451
  }
44894
45452
 
@@ -44920,7 +45478,7 @@ registerSourceProvider("filesystem", (entry) => {
44920
45478
  init_errors();
44921
45479
  init_paths();
44922
45480
  import fs37 from "fs";
44923
- import path41 from "path";
45481
+ import path42 from "path";
44924
45482
 
44925
45483
  // src/sources/providers/tar-utils.ts
44926
45484
  init_common();
@@ -44928,7 +45486,7 @@ init_warn();
44928
45486
  init_runtime();
44929
45487
  import { createHash as createHash4 } from "crypto";
44930
45488
  import fs36 from "fs";
44931
- import path40 from "path";
45489
+ import path41 from "path";
44932
45490
  function verifyArchiveIntegrity(archivePath, expected, source) {
44933
45491
  if (!expected)
44934
45492
  return;
@@ -44988,7 +45546,7 @@ function scanExtractedFiles(dir, root) {
44988
45546
  return;
44989
45547
  }
44990
45548
  for (const entry of entries) {
44991
- const fullPath = path40.join(dir, entry.name);
45549
+ const fullPath = path41.join(dir, entry.name);
44992
45550
  if (entry.name === ".." || entry.name === ".") {
44993
45551
  throw new Error(`Post-extraction scan: suspicious entry name: ${fullPath}`);
44994
45552
  }
@@ -44999,7 +45557,7 @@ function scanExtractedFiles(dir, root) {
44999
45557
  }
45000
45558
  }
45001
45559
  if (!entry.isSymbolicLink()) {
45002
- const resolved = path40.resolve(fullPath);
45560
+ const resolved = path41.resolve(fullPath);
45003
45561
  if (!isWithin(resolved, root)) {
45004
45562
  throw new Error(`Post-extraction scan: entry escapes destination directory: ${fullPath}`);
45005
45563
  }
@@ -45019,7 +45577,7 @@ function validateTarEntries(listOutput) {
45019
45577
  if (entry.startsWith("/")) {
45020
45578
  throw new Error(`Archive contains an absolute path entry: ${entry}`);
45021
45579
  }
45022
- const normalized = path40.posix.normalize(entry);
45580
+ const normalized = path41.posix.normalize(entry);
45023
45581
  if (normalized === ".." || normalized.startsWith("../")) {
45024
45582
  throw new Error(`Archive contains a path traversal entry: ${entry}`);
45025
45583
  }
@@ -45027,8 +45585,8 @@ function validateTarEntries(listOutput) {
45027
45585
  const stripped = parts.slice(1).join("/");
45028
45586
  if (!stripped)
45029
45587
  continue;
45030
- const normalizedStripped = path40.posix.normalize(stripped);
45031
- if (normalizedStripped === ".." || normalizedStripped.startsWith("../") || path40.posix.isAbsolute(normalizedStripped)) {
45588
+ const normalizedStripped = path41.posix.normalize(stripped);
45589
+ if (normalizedStripped === ".." || normalizedStripped.startsWith("../") || path41.posix.isAbsolute(normalizedStripped)) {
45032
45590
  throw new Error(`Archive contains an unsafe entry after strip-components: ${entry}`);
45033
45591
  }
45034
45592
  }
@@ -45073,8 +45631,8 @@ async function doSyncNpm(parsed, options) {
45073
45631
  const syncedAt = (options?.now ?? new Date).toISOString();
45074
45632
  const cacheRootDir = options?.cacheRootDir ?? getRegistryCacheDir();
45075
45633
  const cacheDir = buildInstallCacheDir(cacheRootDir, resolved.source, resolved.id, resolved.resolvedVersion ?? resolved.resolvedRevision);
45076
- const archivePath = path41.join(cacheDir, "artifact.tar.gz");
45077
- const extractedDir = path41.join(cacheDir, "extracted");
45634
+ const archivePath = path42.join(cacheDir, "artifact.tar.gz");
45635
+ const extractedDir = path42.join(cacheDir, "extracted");
45078
45636
  if (!options?.force && isDirectory(extractedDir)) {
45079
45637
  try {
45080
45638
  const cachedStashRoot = detectStashRoot(extractedDir);
@@ -45145,9 +45703,9 @@ init_common();
45145
45703
  init_errors();
45146
45704
  init_paths();
45147
45705
  init_warn();
45148
- import { createHash as createHash5 } from "crypto";
45706
+ import { createHash as createHash5, randomBytes as randomBytes3 } from "crypto";
45149
45707
  import fs39 from "fs";
45150
- import path43 from "path";
45708
+ import path44 from "path";
45151
45709
 
45152
45710
  // node_modules/node-html-parser/dist/index.mjs
45153
45711
  var __create2 = Object.create;
@@ -50635,7 +51193,7 @@ async function fetchPinnedJson(url, options) {
50635
51193
  // src/sources/snapshot-fetchers/registry.ts
50636
51194
  init_warn();
50637
51195
  import fs38 from "fs";
50638
- import path42 from "path";
51196
+ import path43 from "path";
50639
51197
  import { pathToFileURL as pathToFileURL2 } from "url";
50640
51198
 
50641
51199
  // src/sources/snapshot-fetchers/bluesky.ts
@@ -52571,24 +53129,24 @@ class MatcherView {
52571
53129
  return this._matcher.separator;
52572
53130
  }
52573
53131
  getCurrentTag() {
52574
- const path42 = this._matcher.path;
52575
- return path42.length > 0 ? path42[path42.length - 1].tag : undefined;
53132
+ const path43 = this._matcher.path;
53133
+ return path43.length > 0 ? path43[path43.length - 1].tag : undefined;
52576
53134
  }
52577
53135
  getCurrentNamespace() {
52578
- const path42 = this._matcher.path;
52579
- return path42.length > 0 ? path42[path42.length - 1].namespace : undefined;
53136
+ const path43 = this._matcher.path;
53137
+ return path43.length > 0 ? path43[path43.length - 1].namespace : undefined;
52580
53138
  }
52581
53139
  getAttrValue(attrName) {
52582
- const path42 = this._matcher.path;
52583
- if (path42.length === 0)
53140
+ const path43 = this._matcher.path;
53141
+ if (path43.length === 0)
52584
53142
  return;
52585
- return path42[path42.length - 1].values?.[attrName];
53143
+ return path43[path43.length - 1].values?.[attrName];
52586
53144
  }
52587
53145
  hasAttr(attrName) {
52588
- const path42 = this._matcher.path;
52589
- if (path42.length === 0)
53146
+ const path43 = this._matcher.path;
53147
+ if (path43.length === 0)
52590
53148
  return false;
52591
- const current = path42[path42.length - 1];
53149
+ const current = path43[path43.length - 1];
52592
53150
  return current.values !== undefined && attrName in current.values;
52593
53151
  }
52594
53152
  getAnyParentAttr(attrName) {
@@ -52598,16 +53156,16 @@ class MatcherView {
52598
53156
  return this._matcher.hasAnyParentAttr(attrName);
52599
53157
  }
52600
53158
  getPosition() {
52601
- const path42 = this._matcher.path;
52602
- if (path42.length === 0)
53159
+ const path43 = this._matcher.path;
53160
+ if (path43.length === 0)
52603
53161
  return -1;
52604
- return path42[path42.length - 1].position ?? 0;
53162
+ return path43[path43.length - 1].position ?? 0;
52605
53163
  }
52606
53164
  getCounter() {
52607
- const path42 = this._matcher.path;
52608
- if (path42.length === 0)
53165
+ const path43 = this._matcher.path;
53166
+ if (path43.length === 0)
52609
53167
  return -1;
52610
- return path42[path42.length - 1].counter ?? 0;
53168
+ return path43[path43.length - 1].counter ?? 0;
52611
53169
  }
52612
53170
  getIndex() {
52613
53171
  return this.getPosition();
@@ -55620,7 +56178,7 @@ var youtubeFetcher = {
55620
56178
  var youtube_default = youtubeFetcher;
55621
56179
 
55622
56180
  // src/sources/snapshot-fetchers/registry.ts
55623
- var FETCHER_DIR = path42.join("scripts", "wiki-fetchers");
56181
+ var FETCHER_DIR = path43.join("scripts", "wiki-fetchers");
55624
56182
  var FETCHER_FILE_PATTERN = /\.(?:ts|js|mjs)$/i;
55625
56183
  var BUILTIN_FETCHERS = [
55626
56184
  github_default,
@@ -55638,14 +56196,14 @@ function isWikiSnapshotFetcher(value) {
55638
56196
  async function loadWikiSnapshotFetchers(stashDir) {
55639
56197
  const fetchers = [];
55640
56198
  if (stashDir) {
55641
- const fetcherDir = path42.join(stashDir, FETCHER_DIR);
56199
+ const fetcherDir = path43.join(stashDir, FETCHER_DIR);
55642
56200
  try {
55643
56201
  const entries = fs38.readdirSync(fetcherDir).sort();
55644
56202
  for (const entry of entries) {
55645
56203
  if (!FETCHER_FILE_PATTERN.test(entry))
55646
56204
  continue;
55647
56205
  try {
55648
- const fileUrl = pathToFileURL2(path42.join(fetcherDir, entry)).toString();
56206
+ const fileUrl = pathToFileURL2(path43.join(fetcherDir, entry)).toString();
55649
56207
  const mod = await import(fileUrl);
55650
56208
  if (isWikiSnapshotFetcher(mod.default)) {
55651
56209
  fetchers.push(mod.default);
@@ -55924,11 +56482,11 @@ function resolveFetcherStashDir(explicitStashDir) {
55924
56482
  }
55925
56483
  function getWebsiteCachePaths(siteUrl) {
55926
56484
  const key = createHash5("sha256").update(normalizeSiteUrl(siteUrl)).digest("hex").slice(0, 16);
55927
- const rootDir = path43.join(getRegistryIndexCacheDir(), `website-${key}`);
56485
+ const rootDir = path44.join(getRegistryIndexCacheDir(), `website-${key}`);
55928
56486
  return {
55929
56487
  rootDir,
55930
- stashDir: path43.join(rootDir, "stash"),
55931
- manifestPath: path43.join(rootDir, "manifest.json")
56488
+ stashDir: path44.join(rootDir, "stash"),
56489
+ manifestPath: path44.join(rootDir, "manifest.json")
55932
56490
  };
55933
56491
  }
55934
56492
  async function ensureWebsiteMirror(config, options) {
@@ -55963,7 +56521,7 @@ async function ensureWebsiteMirror(config, options) {
55963
56521
  }
55964
56522
  function hasExtractedSite(stashDir) {
55965
56523
  try {
55966
- const knowledgeDir = path43.join(stashDir, "knowledge");
56524
+ const knowledgeDir = path44.join(stashDir, "knowledge");
55967
56525
  if (!fs39.statSync(stashDir).isDirectory() || !fs39.statSync(knowledgeDir).isDirectory())
55968
56526
  return false;
55969
56527
  const pending = [knowledgeDir];
@@ -55975,7 +56533,7 @@ function hasExtractedSite(stashDir) {
55975
56533
  if (entry.isFile() && entry.name.endsWith(".md"))
55976
56534
  return true;
55977
56535
  if (entry.isDirectory())
55978
- pending.push(path43.join(dir, entry.name));
56536
+ pending.push(path44.join(dir, entry.name));
55979
56537
  }
55980
56538
  }
55981
56539
  return false;
@@ -56014,18 +56572,79 @@ async function fetchSnapshotViaRegistry(startUrl, stashDir, allowPrivateHosts, r
56014
56572
  };
56015
56573
  return dispatchSnapshotFetchers(parsed, context, stashDir);
56016
56574
  }
56575
+ var snapshotWriteHookForTests;
56576
+ function snapshotWriteHook(event) {
56577
+ snapshotWriteHookForTests?.(event);
56578
+ }
56579
+ function snapshotSiblingPrefix(stashDir, kind) {
56580
+ return `.${path44.basename(stashDir)}.${kind}-`;
56581
+ }
56582
+ function snapshotSiblingPath(stashDir, kind) {
56583
+ const unique = `${process.pid}-${randomBytes3(6).toString("hex")}`;
56584
+ return path44.join(path44.dirname(stashDir), `${snapshotSiblingPrefix(stashDir, kind)}${unique}`);
56585
+ }
56586
+ var SNAPSHOT_STAGING_SWEEP_AGE_MS = 60 * 60 * 1000;
56587
+ function sweepSnapshotStaging(stashDir) {
56588
+ const parent = path44.dirname(stashDir);
56589
+ let entries;
56590
+ try {
56591
+ entries = fs39.readdirSync(parent);
56592
+ } catch {
56593
+ return;
56594
+ }
56595
+ const prefixes = [snapshotSiblingPrefix(stashDir, "staging"), snapshotSiblingPrefix(stashDir, "retired")];
56596
+ const cutoff = Date.now() - SNAPSHOT_STAGING_SWEEP_AGE_MS;
56597
+ for (const entry of entries) {
56598
+ if (!prefixes.some((prefix) => entry.startsWith(prefix)))
56599
+ continue;
56600
+ const abandoned = path44.join(parent, entry);
56601
+ try {
56602
+ if (fs39.statSync(abandoned).mtimeMs > cutoff)
56603
+ continue;
56604
+ } catch {
56605
+ continue;
56606
+ }
56607
+ fs39.rmSync(abandoned, { recursive: true, force: true });
56608
+ }
56609
+ }
56610
+ function beginSnapshotStaging(stashDir) {
56611
+ fs39.mkdirSync(path44.dirname(stashDir), { recursive: true });
56612
+ sweepSnapshotStaging(stashDir);
56613
+ const dir = snapshotSiblingPath(stashDir, "staging");
56614
+ fs39.mkdirSync(dir, { recursive: true });
56615
+ return { dir, target: stashDir };
56616
+ }
56617
+ function publishSnapshotStaging(staging) {
56618
+ let retired;
56619
+ if (fs39.existsSync(staging.target)) {
56620
+ retired = snapshotSiblingPath(staging.target, "retired");
56621
+ fs39.renameSync(staging.target, retired);
56622
+ }
56623
+ fs39.renameSync(staging.dir, staging.target);
56624
+ if (retired)
56625
+ fs39.rmSync(retired, { recursive: true, force: true });
56626
+ }
56627
+ function discardSnapshotStaging(staging) {
56628
+ fs39.rmSync(staging.dir, { recursive: true, force: true });
56629
+ }
56017
56630
  function writeSnapshotToStash(stashDir, snapshot) {
56018
56631
  const preferredName = snapshot.preferredName ?? deriveImportPath(snapshot.url);
56019
56632
  const relPath = avoidReservedBasename2(preferredName);
56020
- const knowledgeDir = path43.join(stashDir, "knowledge");
56021
- const filePath = path43.resolve(knowledgeDir, `${relPath}.md`);
56022
- if (!isWithin(filePath, knowledgeDir)) {
56633
+ const knowledgeDir = path44.join(stashDir, "knowledge");
56634
+ if (!isWithin(path44.resolve(knowledgeDir, `${relPath}.md`), knowledgeDir)) {
56023
56635
  throw new UsageError(`Snapshot fetcher returned an unsafe preferred name: ${JSON.stringify(preferredName)}`);
56024
56636
  }
56025
- fs39.rmSync(stashDir, { recursive: true, force: true });
56026
- fs39.mkdirSync(path43.dirname(filePath), { recursive: true });
56027
- const slug = relPath.split("/").pop() ?? "index";
56028
- fs39.writeFileSync(filePath, buildMarkdownSnapshot({ url: snapshot.url, title: snapshot.title, markdown: snapshot.markdown }, slug, snapshot.tags), "utf8");
56637
+ const staging = beginSnapshotStaging(stashDir);
56638
+ try {
56639
+ const filePath = path44.resolve(path44.join(staging.dir, "knowledge"), `${relPath}.md`);
56640
+ fs39.mkdirSync(path44.dirname(filePath), { recursive: true });
56641
+ const slug = relPath.split("/").pop() ?? "index";
56642
+ fs39.writeFileSync(filePath, buildMarkdownSnapshot({ url: snapshot.url, title: snapshot.title, markdown: snapshot.markdown }, slug, snapshot.tags), "utf8");
56643
+ snapshotWriteHook({ point: "page-written", index: 1, total: 1, relPath: `knowledge/${relPath}.md` });
56644
+ publishSnapshotStaging(staging);
56645
+ } finally {
56646
+ discardSnapshotStaging(staging);
56647
+ }
56029
56648
  }
56030
56649
  async function scrapeWebsiteToStash(startUrl, stashDir, options) {
56031
56650
  const fetched = await fetchSnapshotViaRegistry(startUrl, options.fetcherStashDir ?? null, options.allowPrivateHosts, options.resolveSecret);
@@ -56037,19 +56656,32 @@ async function scrapeWebsiteToStash(startUrl, stashDir, options) {
56037
56656
  if (pages.length === 0) {
56038
56657
  throw new Error(`No content could be scraped from ${startUrl}`);
56039
56658
  }
56040
- fs39.rmSync(stashDir, { recursive: true, force: true });
56041
- const knowledgeDir = path43.join(stashDir, "knowledge");
56042
- fs39.mkdirSync(knowledgeDir, { recursive: true });
56043
- const usedPaths = new Set;
56044
- for (const page of pages) {
56045
- const relPath = avoidReservedBasename2(urlToRelativePath(page.url));
56046
- const uniquePath = uniqueSlug2(relPath, usedPaths);
56047
- const filePath = path43.join(knowledgeDir, `${uniquePath}.md`);
56048
- const dir = path43.dirname(filePath);
56049
- if (dir !== knowledgeDir)
56050
- fs39.mkdirSync(dir, { recursive: true });
56051
- const slug = uniquePath.split("/").pop() ?? "index";
56052
- fs39.writeFileSync(filePath, buildMarkdownSnapshot(page, slug), "utf8");
56659
+ const staging = beginSnapshotStaging(stashDir);
56660
+ try {
56661
+ const knowledgeDir = path44.join(staging.dir, "knowledge");
56662
+ fs39.mkdirSync(knowledgeDir, { recursive: true });
56663
+ const usedPaths = new Set;
56664
+ let written = 0;
56665
+ for (const page of pages) {
56666
+ const relPath = avoidReservedBasename2(urlToRelativePath(page.url));
56667
+ const uniquePath = uniqueSlug2(relPath, usedPaths);
56668
+ const filePath = path44.join(knowledgeDir, `${uniquePath}.md`);
56669
+ const dir = path44.dirname(filePath);
56670
+ if (dir !== knowledgeDir)
56671
+ fs39.mkdirSync(dir, { recursive: true });
56672
+ const slug = uniquePath.split("/").pop() ?? "index";
56673
+ fs39.writeFileSync(filePath, buildMarkdownSnapshot(page, slug), "utf8");
56674
+ written++;
56675
+ snapshotWriteHook({
56676
+ point: "page-written",
56677
+ index: written,
56678
+ total: pages.length,
56679
+ relPath: `knowledge/${uniquePath}.md`
56680
+ });
56681
+ }
56682
+ publishSnapshotStaging(staging);
56683
+ } finally {
56684
+ discardSnapshotStaging(staging);
56053
56685
  }
56054
56686
  }
56055
56687
  function sleep(ms) {
@@ -56593,7 +57225,7 @@ function resolveEntryContentDir(entry) {
56593
57225
  var SUSPICIOUS_ROOTS = new Set(["/", "/etc", "/bin", "/sbin", "/usr", "/var", "/tmp", "/dev", "/proc", "/sys"]);
56594
57226
 
56595
57227
  // src/tasks/parser.ts
56596
- import path44 from "path";
57228
+ import path45 from "path";
56597
57229
  init_errors();
56598
57230
 
56599
57231
  // src/tasks/task-id.ts
@@ -56666,15 +57298,21 @@ function parseTaskDocument(input) {
56666
57298
  }
56667
57299
  let target;
56668
57300
  if (hasWorkflow) {
56669
- rejectTargetFields(data, ["params"], id, filePath);
57301
+ rejectTargetFields(data, ["params", "timeoutMs", "maxSteps", "maxRetries"], id, filePath);
56670
57302
  const ref = requireString(data.workflow, "workflow", filePath);
56671
57303
  if (!ref) {
56672
57304
  throw new UsageError(`Task "${id}" has empty \`workflow\`. File: ${filePath}`, "INVALID_FLAG_VALUE");
56673
57305
  }
57306
+ const workflowTimeoutMs = readTimeout(data.timeoutMs, filePath);
57307
+ const maxSteps = readBoundedInteger(data.maxSteps, "maxSteps", 1, undefined, filePath);
57308
+ const maxRetries = readBoundedInteger(data.maxRetries, "maxRetries", 0, WORKFLOW_MAX_RETRIES, filePath);
56674
57309
  target = {
56675
57310
  kind: "workflow",
56676
57311
  ref,
56677
- params: readParams(data.params, filePath)
57312
+ params: readParams(data.params, filePath),
57313
+ ...workflowTimeoutMs !== undefined ? { timeoutMs: workflowTimeoutMs } : {},
57314
+ ...maxSteps !== undefined ? { maxSteps } : {},
57315
+ ...maxRetries !== undefined ? { maxRetries } : {}
56678
57316
  };
56679
57317
  } else if (hasCommand) {
56680
57318
  rejectTargetFields(data, ["timeoutMs"], id, filePath);
@@ -56699,6 +57337,7 @@ function parseTaskDocument(input) {
56699
57337
  };
56700
57338
  }
56701
57339
  const timeoutMs = hasCommand ? readTimeout(data.timeoutMs, filePath) : undefined;
57340
+ const redact = readRedactNames(data.redact, filePath);
56702
57341
  return {
56703
57342
  version: TASK_SCHEMA_VERSION,
56704
57343
  schemaVersion: TASK_SCHEMA_VERSION,
@@ -56711,7 +57350,8 @@ function parseTaskDocument(input) {
56711
57350
  ...when_to_use ? { when_to_use } : {},
56712
57351
  ...tags ? { tags } : {},
56713
57352
  source: { path: filePath },
56714
- timeoutMs
57353
+ timeoutMs,
57354
+ ...redact ? { redact } : {}
56715
57355
  };
56716
57356
  }
56717
57357
  var TASK_KEYS = new Set([
@@ -56729,9 +57369,21 @@ var TASK_KEYS = new Set([
56729
57369
  "engine",
56730
57370
  "model",
56731
57371
  "timeoutMs",
56732
- "llm"
57372
+ "maxSteps",
57373
+ "maxRetries",
57374
+ "llm",
57375
+ "redact"
57376
+ ]);
57377
+ var SHARED_KEYS = new Set([
57378
+ "version",
57379
+ "name",
57380
+ "description",
57381
+ "when_to_use",
57382
+ "tags",
57383
+ "schedule",
57384
+ "enabled",
57385
+ "redact"
56733
57386
  ]);
56734
- var SHARED_KEYS = new Set(["version", "name", "description", "when_to_use", "tags", "schedule", "enabled"]);
56735
57387
  function requireVersion(data, id, filePath) {
56736
57388
  if (data.version === TASK_SCHEMA_VERSION)
56737
57389
  return;
@@ -56755,7 +57407,7 @@ function resolvePromptSource(raw, filePath, id) {
56755
57407
  if (!trimmed) {
56756
57408
  throw new UsageError(`Task "${id}" has empty \`prompt\`. File: ${filePath}`, "MISSING_REQUIRED_ARGUMENT");
56757
57409
  }
56758
- if (trimmed.startsWith("./") || trimmed.startsWith("../") || path44.isAbsolute(trimmed)) {
57410
+ if (trimmed.startsWith("./") || trimmed.startsWith("../") || path45.isAbsolute(trimmed)) {
56759
57411
  return { kind: "file", path: trimmed };
56760
57412
  }
56761
57413
  if (/^[A-Za-z]:[\\/]/.test(trimmed)) {
@@ -56831,9 +57483,39 @@ function readTimeout(value, filePath) {
56831
57483
  return;
56832
57484
  if (value === null)
56833
57485
  return null;
56834
- if (typeof value === "number" && Number.isInteger(value) && value > 0)
57486
+ if (typeof value === "number" && Number.isInteger(value) && value > 0 && value <= TASK_MAX_TIMEOUT_MS)
56835
57487
  return value;
56836
- throw new UsageError(`Key "timeoutMs" must be a positive integer or null. File: ${filePath}`, "INVALID_FLAG_VALUE");
57488
+ throw new UsageError(`Key "timeoutMs" must be an integer from 1 through ${TASK_MAX_TIMEOUT_MS}, or null. File: ${filePath}`, "INVALID_FLAG_VALUE");
57489
+ }
57490
+ function readRedactNames(value, filePath) {
57491
+ if (value === undefined || value === null)
57492
+ return;
57493
+ const invalid = (detail) => {
57494
+ throw new UsageError(`Key "redact" ${detail}. File: ${filePath}`, "INVALID_FLAG_VALUE");
57495
+ };
57496
+ if (!Array.isArray(value))
57497
+ return invalid("must be a list of environment variable names");
57498
+ if (value.length > TASK_MAX_REDACT_NAMES) {
57499
+ return invalid(`accepts at most ${TASK_MAX_REDACT_NAMES} names (got ${value.length})`);
57500
+ }
57501
+ const names = [];
57502
+ for (const entry of value) {
57503
+ if (typeof entry !== "string" || !WORKFLOW_ENV_VAR_NAME_PATTERN.test(entry)) {
57504
+ return invalid(`takes environment variable NAMES only (matching ${WORKFLOW_ENV_VAR_NAME_PATTERN.source}), not values \u2014 ` + `got ${JSON.stringify(entry)}. A secret written here would be indexed and printed by \`akm show\``);
57505
+ }
57506
+ if (!names.includes(entry))
57507
+ names.push(entry);
57508
+ }
57509
+ return names.length > 0 ? names : undefined;
57510
+ }
57511
+ function readBoundedInteger(value, key, minimum, maximum, filePath) {
57512
+ if (value === undefined || value === null)
57513
+ return;
57514
+ if (typeof value === "number" && Number.isSafeInteger(value) && value >= minimum && (maximum === undefined || value <= maximum)) {
57515
+ return value;
57516
+ }
57517
+ const range = maximum === undefined ? `at least ${minimum}` : `from ${minimum} through ${maximum}`;
57518
+ throw new UsageError(`Key "${key}" must be an integer ${range}. File: ${filePath}`, "INVALID_FLAG_VALUE");
56837
57519
  }
56838
57520
  function readLlmOverrides(value, filePath) {
56839
57521
  if (value === undefined)
@@ -57099,7 +57781,7 @@ function expandTilde2(value) {
57099
57781
  if (value === "~")
57100
57782
  return os5.homedir();
57101
57783
  if (value.startsWith("~/") || value.startsWith("~\\"))
57102
- return path45.join(os5.homedir(), value.slice(2));
57784
+ return path46.join(os5.homedir(), value.slice(2));
57103
57785
  return value;
57104
57786
  }
57105
57787
  function bundlesFromConfig(config, pathResolutionBase, migrationLockEntries) {
@@ -57118,7 +57800,7 @@ function bundlesFromConfig(config, pathResolutionBase, migrationLockEntries) {
57118
57800
  const contentDir = lockRoots.get(id) ?? resolveEntryContentDir(sourceEntry);
57119
57801
  if (!contentDir)
57120
57802
  continue;
57121
- const root2 = path45.resolve(pathResolutionBase, expandTilde2(contentDir));
57803
+ const root2 = path46.resolve(pathResolutionBase, expandTilde2(contentDir));
57122
57804
  const rootIdentity = fs40.existsSync(root2) ? fs40.realpathSync(root2) : root2;
57123
57805
  const prior = roots.get(rootIdentity);
57124
57806
  if (prior && prior !== id) {
@@ -57163,21 +57845,21 @@ function resolveOrigin(origin, bundles, filePath) {
57163
57845
  return candidate;
57164
57846
  }
57165
57847
  function assertWorkflowPathSafe(bundle, name, rawRef, filePath) {
57166
- const candidate = resolveAssetPathFromName("workflow", path45.join(bundle.root, "workflows"), name);
57848
+ const candidate = resolveAssetPathFromName("workflow", path46.join(bundle.root, "workflows"), name);
57167
57849
  if (!fs40.existsSync(candidate))
57168
57850
  return;
57169
57851
  if (!fs40.statSync(candidate).isFile())
57170
57852
  throw migrationError(filePath, `legacy target "${rawRef}" is not a file in bundle "${bundle.id}".`);
57171
57853
  const realRoot = fs40.realpathSync(bundle.root);
57172
57854
  const realCandidate = fs40.realpathSync(candidate);
57173
- const relative = path45.relative(realRoot, realCandidate);
57174
- if (relative.startsWith("..") || path45.isAbsolute(relative)) {
57855
+ const relative = path46.relative(realRoot, realCandidate);
57856
+ if (relative.startsWith("..") || path46.isAbsolute(relative)) {
57175
57857
  throw migrationError(filePath, `legacy target "${rawRef}" resolves outside bundle "${bundle.id}".`);
57176
57858
  }
57177
57859
  }
57178
57860
  function assertRealPathWithin(root2, candidate, filePath, detail) {
57179
- const relative = path45.relative(fs40.realpathSync(root2), fs40.realpathSync(candidate));
57180
- if (relative.startsWith("..") || path45.isAbsolute(relative))
57861
+ const relative = path46.relative(fs40.realpathSync(root2), fs40.realpathSync(candidate));
57862
+ if (relative.startsWith("..") || path46.isAbsolute(relative))
57181
57863
  throw migrationError(filePath, detail);
57182
57864
  }
57183
57865
  function planTaskFile(filePath, containing, bundles) {
@@ -57228,7 +57910,7 @@ function planTaskFile(filePath, containing, bundles) {
57228
57910
  }
57229
57911
  const after = Buffer.from($stringify(normalized));
57230
57912
  try {
57231
- parseTaskDocument({ yaml: after.toString("utf8"), filePath, id: path45.basename(filePath, ".yml") });
57913
+ parseTaskDocument({ yaml: after.toString("utf8"), filePath, id: path46.basename(filePath, ".yml") });
57232
57914
  } catch (error2) {
57233
57915
  throw migrationError(filePath, `the converted v2 task is invalid (${error2 instanceof Error ? error2.message : String(error2)}).`);
57234
57916
  }
@@ -57254,7 +57936,7 @@ function planTaskTargetRefMigration(config, pathResolutionBase = process.cwd(),
57254
57936
  }
57255
57937
  continue;
57256
57938
  }
57257
- const tasksDir = path45.join(bundle.root, "tasks");
57939
+ const tasksDir = path46.join(bundle.root, "tasks");
57258
57940
  if (!fs40.existsSync(tasksDir))
57259
57941
  continue;
57260
57942
  const tasksStat = fs40.lstatSync(tasksDir);
@@ -57268,7 +57950,7 @@ function planTaskTargetRefMigration(config, pathResolutionBase = process.cwd(),
57268
57950
  for (const entry of fs40.readdirSync(tasksDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
57269
57951
  if (!entry.name.endsWith(".yml"))
57270
57952
  continue;
57271
- const filePath = path45.join(tasksDir, entry.name);
57953
+ const filePath = path46.join(tasksDir, entry.name);
57272
57954
  if (!entry.isFile())
57273
57955
  throw migrationError(filePath, "task migration does not follow symbolic links or special files.");
57274
57956
  assertRealPathWithin(bundle.root, filePath, filePath, "the task file resolves outside its bundle.");
@@ -57280,7 +57962,7 @@ function planTaskTargetRefMigration(config, pathResolutionBase = process.cwd(),
57280
57962
  return { rewrites, durabilityPaths, readOnlyLegacyTasks };
57281
57963
  }
57282
57964
  function legacyTaskFilesIn(bundle) {
57283
- const tasksDir = path45.join(bundle.root, "tasks");
57965
+ const tasksDir = path46.join(bundle.root, "tasks");
57284
57966
  let entries;
57285
57967
  try {
57286
57968
  entries = fs40.readdirSync(tasksDir, { withFileTypes: true });
@@ -57292,7 +57974,7 @@ function legacyTaskFilesIn(bundle) {
57292
57974
  if (!entry.isFile() || !entry.name.endsWith(".yml"))
57293
57975
  continue;
57294
57976
  try {
57295
- const doc = $parseDocument(fs40.readFileSync(path45.join(tasksDir, entry.name), "utf8"), { uniqueKeys: true });
57977
+ const doc = $parseDocument(fs40.readFileSync(path46.join(tasksDir, entry.name), "utf8"), { uniqueKeys: true });
57296
57978
  if (doc.errors.length > 0 || !$isMap(doc.contents)) {
57297
57979
  legacy.push(entry.name);
57298
57980
  continue;
@@ -57307,7 +57989,7 @@ function legacyTaskFilesIn(bundle) {
57307
57989
  return legacy;
57308
57990
  }
57309
57991
  function syncParentDirectory(filePath) {
57310
- const fd = fs40.openSync(path45.dirname(filePath), "r");
57992
+ const fd = fs40.openSync(path46.dirname(filePath), "r");
57311
57993
  try {
57312
57994
  fs40.fsyncSync(fd);
57313
57995
  } finally {
@@ -57367,7 +58049,7 @@ init_database();
57367
58049
  init_sqlite_pragmas();
57368
58050
  import crypto5 from "crypto";
57369
58051
  import fs41 from "fs";
57370
- import path46 from "path";
58052
+ import path47 from "path";
57371
58053
  class CutoverIntegrityError extends Error {
57372
58054
  constructor(message) {
57373
58055
  super(message);
@@ -57431,7 +58113,7 @@ function walkLegacyLayoutInto(map, root2) {
57431
58113
  else
57432
58114
  return;
57433
58115
  for (const [type, dirName] of Object.entries(TYPE_DIRS)) {
57434
- const typeRoot = path46.join(root2.path, dirName);
58116
+ const typeRoot = path47.join(root2.path, dirName);
57435
58117
  let files;
57436
58118
  try {
57437
58119
  files = listFilesRecursive(typeRoot);
@@ -57468,14 +58150,14 @@ function safeDerive(type, typeRoot, filePath) {
57468
58150
  }
57469
58151
  }
57470
58152
  function basenameSlug(sourcePath) {
57471
- const base = path46.basename(path46.resolve(sourcePath)).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
58153
+ const base = path47.basename(path47.resolve(sourcePath)).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
57472
58154
  return base.length > 0 ? base : "bundle";
57473
58155
  }
57474
58156
  function listFilesRecursive(dir) {
57475
58157
  const out = [];
57476
58158
  const walk = (current) => {
57477
58159
  for (const entry of fs41.readdirSync(current, { withFileTypes: true })) {
57478
- const abs = path46.join(current, entry.name);
58160
+ const abs = path47.join(current, entry.name);
57479
58161
  if (entry.isDirectory())
57480
58162
  walk(abs);
57481
58163
  else if (entry.isFile())
@@ -57488,10 +58170,10 @@ function listFilesRecursive(dir) {
57488
58170
  function samePath(a, b) {
57489
58171
  if (!b)
57490
58172
  return false;
57491
- return path46.resolve(a) === path46.resolve(b);
58173
+ return path47.resolve(a) === path47.resolve(b);
57492
58174
  }
57493
58175
  function persistRefMapJson(outputPath, map) {
57494
- fs41.mkdirSync(path46.dirname(outputPath), { recursive: true, mode: 448 });
58176
+ fs41.mkdirSync(path47.dirname(outputPath), { recursive: true, mode: 448 });
57495
58177
  const entries = Object.fromEntries([...map.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0));
57496
58178
  const payload = { formatVersion: CUTOVER_REFMAP_FORMAT, entries };
57497
58179
  writeFileAtomic(outputPath, `${JSON.stringify(payload, null, 2)}
@@ -57529,7 +58211,7 @@ function completeCutoverRefMap(inputPath, outputPath) {
57529
58211
  function loadCompletedCutoverRefMap(inputPath) {
57530
58212
  return { map: loadCutoverRefMap(inputPath) };
57531
58213
  }
57532
- var PILOT_TREATMENT_FILE = path46.join(".akm", "measurement", "treatment-pilot-2026-06-14.txt");
58214
+ var PILOT_TREATMENT_FILE = path47.join(".akm", "measurement", "treatment-pilot-2026-06-14.txt");
57533
58215
  function fsyncPilotTreatmentDirectory(directory) {
57534
58216
  let fd;
57535
58217
  try {
@@ -57548,8 +58230,8 @@ function migratePilotTreatmentFiles(stashRoots, refMap) {
57548
58230
  let migrated = 0;
57549
58231
  const seen = new Set;
57550
58232
  for (const root2 of stashRoots) {
57551
- const file = path46.join(root2.path, PILOT_TREATMENT_FILE);
57552
- const resolved = path46.resolve(file);
58233
+ const file = path47.join(root2.path, PILOT_TREATMENT_FILE);
58234
+ const resolved = path47.resolve(file);
57553
58235
  if (seen.has(resolved) || !fs41.existsSync(file))
57554
58236
  continue;
57555
58237
  seen.add(resolved);
@@ -57568,7 +58250,7 @@ function migratePilotTreatmentFiles(stashRoots, refMap) {
57568
58250
  }).join(`
57569
58251
  `);
57570
58252
  if (rewritten === original) {
57571
- fsyncPilotTreatmentDirectory(path46.dirname(file));
58253
+ fsyncPilotTreatmentDirectory(path47.dirname(file));
57572
58254
  continue;
57573
58255
  }
57574
58256
  const mode = fs41.statSync(file).mode & 511;
@@ -57586,7 +58268,7 @@ function migratePilotTreatmentFiles(stashRoots, refMap) {
57586
58268
  }
57587
58269
  fs41.renameSync(tmp, file);
57588
58270
  ownsTmp = false;
57589
- fsyncPilotTreatmentDirectory(path46.dirname(file));
58271
+ fsyncPilotTreatmentDirectory(path47.dirname(file));
57590
58272
  } catch (error2) {
57591
58273
  if (ownsTmp)
57592
58274
  fs41.rmSync(tmp, { force: true });
@@ -57677,7 +58359,7 @@ function rekeyStateDbCore(db, refMap) {
57677
58359
  function rekeyStateDb(dbPath, refMap) {
57678
58360
  const db = openDatabaseFinalizing(dbPath);
57679
58361
  try {
57680
- applyStandardPragmas(db, { dataDir: path46.dirname(dbPath) });
58362
+ applyStandardPragmas(db, { dataDir: path47.dirname(dbPath) });
57681
58363
  let report = emptyReport2();
57682
58364
  db.transaction(() => {
57683
58365
  report = rekeyStateDbCore(db, refMap);
@@ -58174,7 +58856,7 @@ assertMigrationRegistry(FROZEN_WORKFLOW_MIGRATIONS);
58174
58856
  // scripts/akm-migrate/migration-backup.ts
58175
58857
  import { randomUUID as randomUUID4 } from "crypto";
58176
58858
  import fs42 from "fs";
58177
- import path47 from "path";
58859
+ import path48 from "path";
58178
58860
  init_common();
58179
58861
 
58180
58862
  // src/core/config/config-version.ts
@@ -58225,7 +58907,7 @@ function getMigrationBackupDir(runId) {
58225
58907
  if (!/^[A-Za-z0-9._-]+$/.test(runId)) {
58226
58908
  throw new ConfigError(`Invalid migration backup run ID ${JSON.stringify(runId)}.`, "INVALID_CONFIG_FILE");
58227
58909
  }
58228
- return path47.join(getMigrationBackupRoot(), runId);
58910
+ return path48.join(getMigrationBackupRoot(), runId);
58229
58911
  }
58230
58912
  function expectedSourcePaths() {
58231
58913
  return {
@@ -58260,7 +58942,7 @@ function fsyncDirectory(directory) {
58260
58942
  }
58261
58943
  }
58262
58944
  function copyFileDurable(source, destination) {
58263
- fs42.mkdirSync(path47.dirname(destination), { recursive: true, mode: 448 });
58945
+ fs42.mkdirSync(path48.dirname(destination), { recursive: true, mode: 448 });
58264
58946
  fs42.copyFileSync(source, destination, fs42.constants.COPYFILE_EXCL);
58265
58947
  fs42.chmodSync(destination, 384);
58266
58948
  fsyncFile(destination);
@@ -58386,8 +59068,8 @@ function sqliteQuote2(value) {
58386
59068
  return `'${value.replaceAll("'", "''")}'`;
58387
59069
  }
58388
59070
  function backupSqlite(source, destination) {
58389
- const resolved = path47.resolve(source);
58390
- const releaseActivity = resolved === path47.resolve(getStateDbPathInDataDir()) ? acquireMaintenanceActivitySync("state-db") : resolved === path47.resolve(getLegacyWorkflowDbPath()) ? acquireMaintenanceActivitySync("workflow-db") : undefined;
59071
+ const resolved = path48.resolve(source);
59072
+ const releaseActivity = resolved === path48.resolve(getStateDbPathInDataDir()) ? acquireMaintenanceActivitySync("state-db") : resolved === path48.resolve(getLegacyWorkflowDbPath()) ? acquireMaintenanceActivitySync("workflow-db") : undefined;
58391
59073
  let db;
58392
59074
  try {
58393
59075
  db = openDatabaseFinalizing(source);
@@ -58404,8 +59086,8 @@ function backupSqlite(source, destination) {
58404
59086
  fsyncFile(destination);
58405
59087
  }
58406
59088
  function acquireBackupLock() {
58407
- const lockPath = path47.join(getMigrationBackupRoot(), ".lock");
58408
- fs42.mkdirSync(path47.dirname(lockPath), { recursive: true, mode: 448 });
59089
+ const lockPath = path48.join(getMigrationBackupRoot(), ".lock");
59090
+ fs42.mkdirSync(path48.dirname(lockPath), { recursive: true, mode: 448 });
58409
59091
  for (let attempt = 0;attempt < 50; attempt += 1) {
58410
59092
  const ownership = tryAcquireLockSync(lockPath, createLockPayload());
58411
59093
  if (ownership)
@@ -58435,8 +59117,8 @@ function createMigrationBackupUnlocked(options = {}) {
58435
59117
  fs42.mkdirSync(root2, { recursive: true, mode: 448 });
58436
59118
  fs42.chmodSync(root2, 448);
58437
59119
  const runId = newRunId();
58438
- const bundlePath = path47.join(root2, runId);
58439
- const temporary = path47.join(root2, `.${runId}.tmp`);
59120
+ const bundlePath = path48.join(root2, runId);
59121
+ const temporary = path48.join(root2, `.${runId}.tmp`);
58440
59122
  fs42.mkdirSync(temporary, { mode: 448 });
58441
59123
  const createdAt = new Date().toISOString();
58442
59124
  const sources = expectedSourcePaths();
@@ -58446,7 +59128,7 @@ function createMigrationBackupUnlocked(options = {}) {
58446
59128
  const sourcePath = sources[name];
58447
59129
  const sourceState = stateForName(state, name);
58448
59130
  const present = sourceState.status !== "missing";
58449
- const destination = path47.join(temporary, name);
59131
+ const destination = path48.join(temporary, name);
58450
59132
  if (present) {
58451
59133
  if (name === "config.json" || name === "index.db" && sourceState.status === "corrupt") {
58452
59134
  copyFileDurable(sourcePath, destination);
@@ -58464,13 +59146,13 @@ function createMigrationBackupUnlocked(options = {}) {
58464
59146
  formatVersion: MANIFEST_FORMAT_VERSION,
58465
59147
  version: MIGRATION_BACKUP_VERSION,
58466
59148
  targetVersion: MIGRATION_BACKUP_VERSION,
58467
- installationId: path47.basename(getMigrationOperationRoot()),
59149
+ installationId: path48.basename(getMigrationOperationRoot()),
58468
59150
  runId,
58469
59151
  createdAt,
58470
59152
  complete: true,
58471
59153
  artifacts
58472
59154
  };
58473
- writeFileAtomic(path47.join(temporary, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
59155
+ writeFileAtomic(path48.join(temporary, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
58474
59156
  `, 384);
58475
59157
  fsyncDirectory(temporary);
58476
59158
  fs42.renameSync(temporary, bundlePath);
@@ -58482,7 +59164,7 @@ function createMigrationBackupUnlocked(options = {}) {
58482
59164
  }
58483
59165
  }
58484
59166
  function parseManifest(bundlePath) {
58485
- const manifestPath = path47.join(bundlePath, "manifest.json");
59167
+ const manifestPath = path48.join(bundlePath, "manifest.json");
58486
59168
  let value;
58487
59169
  try {
58488
59170
  value = JSON.parse(readTextFileWithLimit(manifestPath, MAX_LOCAL_METADATA_BYTES, "Migration manifest"));
@@ -58490,7 +59172,7 @@ function parseManifest(bundlePath) {
58490
59172
  throw new ConfigError(`Migration backup at ${bundlePath} is incomplete or unreadable: ${error2 instanceof Error ? error2.message : String(error2)}.`, "INVALID_CONFIG_FILE");
58491
59173
  }
58492
59174
  const manifest = value;
58493
- if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || manifest.formatVersion !== MANIFEST_FORMAT_VERSION || manifest.version !== MIGRATION_BACKUP_VERSION || manifest.targetVersion !== MIGRATION_BACKUP_VERSION || manifest.installationId !== path47.basename(getMigrationOperationRoot()) || typeof manifest.runId !== "string" || path47.basename(bundlePath) !== manifest.runId || typeof manifest.createdAt !== "string" || manifest.complete !== true) {
59175
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || manifest.formatVersion !== MANIFEST_FORMAT_VERSION || manifest.version !== MIGRATION_BACKUP_VERSION || manifest.targetVersion !== MIGRATION_BACKUP_VERSION || manifest.installationId !== path48.basename(getMigrationOperationRoot()) || typeof manifest.runId !== "string" || path48.basename(bundlePath) !== manifest.runId || typeof manifest.createdAt !== "string" || manifest.complete !== true) {
58494
59176
  throw new ConfigError(`Migration backup manifest at ${manifestPath} is invalid.`, "INVALID_CONFIG_FILE");
58495
59177
  }
58496
59178
  const sources = expectedSourcePaths();
@@ -58514,7 +59196,7 @@ function verifyMigrationBackup(bundlePath = resolveBackupRun()) {
58514
59196
  const expectedFiles = new Set(["manifest.json"]);
58515
59197
  for (const name of ARTIFACT_NAMES) {
58516
59198
  const artifact = manifest.artifacts[name];
58517
- const artifactPath = path47.join(bundlePath, name);
59199
+ const artifactPath = path48.join(bundlePath, name);
58518
59200
  if (!artifact.present) {
58519
59201
  if (fs42.existsSync(artifactPath)) {
58520
59202
  throw new ConfigError(`Migration backup contains unexpected ${name}.`, "INVALID_CONFIG_FILE");
@@ -58530,7 +59212,7 @@ function verifyMigrationBackup(bundlePath = resolveBackupRun()) {
58530
59212
  throw new ConfigError(`Migration backup artifact ${name} failed semantic verification: expected ${artifact.status}, got ${inspected.status}.`, "INVALID_CONFIG_FILE");
58531
59213
  }
58532
59214
  }
58533
- if (!ownerOnlyMode(path47.join(bundlePath, "manifest.json"), false)) {
59215
+ if (!ownerOnlyMode(path48.join(bundlePath, "manifest.json"), false)) {
58534
59216
  throw new ConfigError("Migration backup manifest must have mode 0600.", "INVALID_CONFIG_FILE");
58535
59217
  }
58536
59218
  const extras = fs42.readdirSync(bundlePath).filter((name) => !expectedFiles.has(name));
@@ -58552,17 +59234,17 @@ function expandTilde3(value) {
58552
59234
  if (value === "~")
58553
59235
  return process.env.HOME ?? value;
58554
59236
  if (value.startsWith("~/") || value.startsWith("~\\")) {
58555
- return path47.join(process.env.HOME ?? "~", value.slice(2));
59237
+ return path48.join(process.env.HOME ?? "~", value.slice(2));
58556
59238
  }
58557
59239
  return value;
58558
59240
  }
58559
59241
  function resolveStashRoot(value, pathResolutionBase = process.cwd()) {
58560
- return path47.resolve(pathResolutionBase, expandTilde3(value));
59242
+ return path48.resolve(pathResolutionBase, expandTilde3(value));
58561
59243
  }
58562
59244
  function configStashRoots(configPath, pathResolutionBase = process.cwd()) {
58563
59245
  try {
58564
59246
  const raw = parseConfigText(readTextFileWithLimit(configPath, MAX_CONFIG_FILE_BYTES, "Migration config"), configPath);
58565
- const roots = new Set(oldConfigToSearchSources(raw, pathResolutionBase).map((source) => path47.resolve(source.path)));
59247
+ const roots = new Set(oldConfigToSearchSources(raw, pathResolutionBase).map((source) => path48.resolve(source.path)));
58566
59248
  const migrated = migrateConfigSourcesToBundles(raw);
58567
59249
  const bundles = migrated.bundles;
58568
59250
  if (bundles && typeof bundles === "object" && !Array.isArray(bundles)) {
@@ -58605,7 +59287,7 @@ function currentStashRoots() {
58605
59287
  }
58606
59288
  function lockFiles(directory) {
58607
59289
  try {
58608
- const paths = fs42.readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".lock")).map((entry) => path47.join(directory, entry.name));
59290
+ const paths = fs42.readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".lock")).map((entry) => path48.join(directory, entry.name));
58609
59291
  return { paths: paths.slice(0, LOCK_FILE_SAMPLE_LIMIT), overflow: paths.length > LOCK_FILE_SAMPLE_LIMIT };
58610
59292
  } catch (error2) {
58611
59293
  if (error2.code === "ENOENT")
@@ -58615,19 +59297,19 @@ function lockFiles(directory) {
58615
59297
  }
58616
59298
  function activeLockPaths(stashRoots = []) {
58617
59299
  const dataDir = getDataDir();
58618
- const configLock = path47.join(path47.dirname(getConfigPath()), "config.json.lck");
58619
- const roots = new Set([...currentStashRoots(), ...stashRoots.map((root2) => path47.resolve(root2))]);
58620
- const lockBases = [dataDir, ...[...roots].map((root2) => path47.join(root2, ".akm"))];
59300
+ const configLock = path48.join(path48.dirname(getConfigPath()), "config.json.lck");
59301
+ const roots = new Set([...currentStashRoots(), ...stashRoots.map((root2) => path48.resolve(root2))]);
59302
+ const lockBases = [dataDir, ...[...roots].map((root2) => path48.join(root2, ".akm"))];
58621
59303
  const lockDirectories = lockBases.flatMap((base) => [
58622
- path47.join(base, "extract-locks"),
58623
- path47.join(base, "maintenance-activities")
59304
+ path48.join(base, "extract-locks"),
59305
+ path48.join(base, "maintenance-activities")
58624
59306
  ]);
58625
59307
  const samples = lockDirectories.map((directory) => ({ directory, ...lockFiles(directory) }));
58626
59308
  const candidates = new Set([
58627
59309
  configLock,
58628
59310
  getLockfileLockPath(),
58629
59311
  getIndexWriterLockPath(),
58630
- ...lockBases.flatMap((base) => STATIC_OPERATION_LOCKS.map((name) => path47.join(base, name))),
59312
+ ...lockBases.flatMap((base) => STATIC_OPERATION_LOCKS.map((name) => path48.join(base, name))),
58631
59313
  ...samples.flatMap((sample) => sample.paths)
58632
59314
  ]);
58633
59315
  const blockers = [...candidates].filter((lockPath) => {
@@ -58715,7 +59397,7 @@ function resolveBackupRun(runId) {
58715
59397
  if (runId)
58716
59398
  return getMigrationBackupDir(runId);
58717
59399
  const root2 = getMigrationBackupRoot();
58718
- const runs = fs42.existsSync(root2) ? fs42.readdirSync(root2, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => ({ name: entry.name, mtime: fs42.statSync(path47.join(root2, entry.name)).mtimeMs })).sort((left, right) => left.mtime - right.mtime || left.name.localeCompare(right.name)) : [];
59400
+ const runs = fs42.existsSync(root2) ? fs42.readdirSync(root2, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => ({ name: entry.name, mtime: fs42.statSync(path48.join(root2, entry.name)).mtimeMs })).sort((left, right) => left.mtime - right.mtime || left.name.localeCompare(right.name)) : [];
58719
59401
  const latest = runs.at(-1)?.name;
58720
59402
  if (!latest)
58721
59403
  throw new ConfigError(`No migration backup runs exist under ${root2}.`, "INVALID_CONFIG_FILE");
@@ -58739,13 +59421,13 @@ function readRestoreSentinel() {
58739
59421
  return value;
58740
59422
  }
58741
59423
  function writeRestoreSentinel(sentinel) {
58742
- fs42.mkdirSync(path47.dirname(restoreSentinelPath()), { recursive: true, mode: 448 });
59424
+ fs42.mkdirSync(path48.dirname(restoreSentinelPath()), { recursive: true, mode: 448 });
58743
59425
  writeFileAtomic(restoreSentinelPath(), `${JSON.stringify(sentinel, null, 2)}
58744
59426
  `, 384);
58745
59427
  }
58746
59428
  function clearRestoreSentinel() {
58747
59429
  fs42.rmSync(restoreSentinelPath(), { force: true });
58748
- fsyncDirectory(path47.dirname(restoreSentinelPath()));
59430
+ fsyncDirectory(path48.dirname(restoreSentinelPath()));
58749
59431
  }
58750
59432
  function failRestoreAfterForTests(name) {
58751
59433
  if (process.env.AKM_TEST_MIGRATION_FAIL_RESTORE_AFTER === name) {
@@ -58758,18 +59440,18 @@ function publishBackup(bundlePath, manifest) {
58758
59440
  const artifact = manifest.artifacts[name];
58759
59441
  const destination = artifact.sourcePath;
58760
59442
  const sidecars = name === "config.json" ? [] : [`${destination}-wal`, `${destination}-shm`];
58761
- fs42.mkdirSync(path47.dirname(destination), { recursive: true, mode: 448 });
59443
+ fs42.mkdirSync(path48.dirname(destination), { recursive: true, mode: 448 });
58762
59444
  for (const sidecar of sidecars)
58763
59445
  fs42.rmSync(sidecar, { force: true });
58764
59446
  if (!artifact.present) {
58765
59447
  fs42.rmSync(destination, { force: true });
58766
- fsyncDirectory(path47.dirname(destination));
59448
+ fsyncDirectory(path48.dirname(destination));
58767
59449
  failRestoreAfterForTests(name);
58768
59450
  continue;
58769
59451
  }
58770
59452
  const stage = `${destination}.restore-stage`;
58771
59453
  fs42.rmSync(stage, { force: true });
58772
- copyFileDurable(path47.join(bundlePath, name), stage);
59454
+ copyFileDurable(path48.join(bundlePath, name), stage);
58773
59455
  const staged = inspectArtifact(name, stage);
58774
59456
  if (!sameState(staged, artifact)) {
58775
59457
  throw new ConfigError(`Staged restore artifact ${name} failed semantic verification.`, "INVALID_CONFIG_FILE");
@@ -58777,7 +59459,7 @@ function publishBackup(bundlePath, manifest) {
58777
59459
  fs42.rmSync(destination, { force: true });
58778
59460
  fs42.renameSync(stage, destination);
58779
59461
  fs42.chmodSync(destination, 384);
58780
- fsyncDirectory(path47.dirname(destination));
59462
+ fsyncDirectory(path48.dirname(destination));
58781
59463
  failRestoreAfterForTests(name);
58782
59464
  }
58783
59465
  for (const name of ARTIFACT_NAMES) {
@@ -58804,7 +59486,7 @@ function recoverInterruptedRestoreWithLocksHeld() {
58804
59486
  if (!pending)
58805
59487
  return;
58806
59488
  assertNoArtifactReplacementBlockers(undefined, {
58807
- stashRoots: configStashRoots(path47.join(getMigrationBackupDir(pending.sourceRunId), "config.json"))
59489
+ stashRoots: configStashRoots(path48.join(getMigrationBackupDir(pending.sourceRunId), "config.json"))
58808
59490
  });
58809
59491
  recoverInterruptedRestore();
58810
59492
  }
@@ -58819,7 +59501,7 @@ function restoreMigrationBackup(confirm, runId) {
58819
59501
  const pending = readRestoreSentinel();
58820
59502
  if (pending) {
58821
59503
  assertNoArtifactReplacementBlockers(undefined, {
58822
- stashRoots: configStashRoots(path47.join(getMigrationBackupDir(pending.sourceRunId), "config.json"))
59504
+ stashRoots: configStashRoots(path48.join(getMigrationBackupDir(pending.sourceRunId), "config.json"))
58823
59505
  });
58824
59506
  recoverInterruptedRestore();
58825
59507
  const backupPath = getMigrationBackupDir(pending.sourceRunId);
@@ -58833,7 +59515,7 @@ function restoreMigrationBackup(confirm, runId) {
58833
59515
  const bundlePath = resolveBackupRun(runId);
58834
59516
  const manifest = verifyMigrationBackup(bundlePath);
58835
59517
  assertNoArtifactReplacementBlockers(undefined, {
58836
- stashRoots: configStashRoots(path47.join(bundlePath, "config.json"))
59518
+ stashRoots: configStashRoots(path48.join(bundlePath, "config.json"))
58837
59519
  });
58838
59520
  const rescue = createMigrationBackupUnlocked({ allowCorruptIndex: true });
58839
59521
  writeRestoreSentinel({
@@ -58862,7 +59544,7 @@ function readApplySentinel() {
58862
59544
  let sentinel;
58863
59545
  try {
58864
59546
  const value = JSON.parse(readTextFileWithLimit(sentinelPath, MAX_LOCAL_METADATA_BYTES, "Migration apply sentinel"));
58865
- if (!value || typeof value !== "object" || Array.isArray(value) || value.formatVersion !== APPLY_SENTINEL_FORMAT || value.version !== MIGRATION_BACKUP_VERSION || typeof value.operationId !== "string" || !/^[A-Za-z0-9._-]+$/.test(value.operationId) || value.installationId !== path48.basename(getMigrationBackupRoot()) || typeof value.backupRunId !== "string" || !/^[A-Za-z0-9._-]+$/.test(value.backupRunId) || value.backupPath !== getMigrationBackupDir(value.backupRunId) || !value.targetConfig || typeof value.targetConfig !== "object" || Array.isArray(value.targetConfig) || !isMigrationLockEntries(value.migrationLockEntries) || typeof value.pathResolutionBase !== "string" || !path48.isAbsolute(value.pathResolutionBase)) {
59547
+ if (!value || typeof value !== "object" || Array.isArray(value) || value.formatVersion !== APPLY_SENTINEL_FORMAT || value.version !== MIGRATION_BACKUP_VERSION || typeof value.operationId !== "string" || !/^[A-Za-z0-9._-]+$/.test(value.operationId) || value.installationId !== path49.basename(getMigrationBackupRoot()) || typeof value.backupRunId !== "string" || !/^[A-Za-z0-9._-]+$/.test(value.backupRunId) || value.backupPath !== getMigrationBackupDir(value.backupRunId) || !value.targetConfig || typeof value.targetConfig !== "object" || Array.isArray(value.targetConfig) || !isMigrationLockEntries(value.migrationLockEntries) || typeof value.pathResolutionBase !== "string" || !path49.isAbsolute(value.pathResolutionBase)) {
58866
59548
  return { error: `Invalid migration apply sentinel at ${sentinelPath}.` };
58867
59549
  }
58868
59550
  sentinel = value;
@@ -58873,7 +59555,7 @@ function readApplySentinel() {
58873
59555
  }
58874
59556
  try {
58875
59557
  const backupStat = fs43.lstatSync(sentinel.backupPath);
58876
- if (backupStat.isSymbolicLink() || !backupStat.isDirectory() || fs43.realpathSync(path48.dirname(sentinel.backupPath)) !== fs43.realpathSync(getMigrationBackupRoot())) {
59558
+ if (backupStat.isSymbolicLink() || !backupStat.isDirectory() || fs43.realpathSync(path49.dirname(sentinel.backupPath)) !== fs43.realpathSync(getMigrationBackupRoot())) {
58877
59559
  throw new ConfigError("Migration apply backup is not a canonical installation run directory.", "INVALID_CONFIG_FILE");
58878
59560
  }
58879
59561
  const config = parseMigrationTargetConfig(JSON.stringify(sentinel.targetConfig), sentinelPath);
@@ -58891,7 +59573,7 @@ function readApplySentinel() {
58891
59573
  }
58892
59574
  function writeApplySentinel(sentinel) {
58893
59575
  const sentinelPath = getMigrationApplyJournalPath();
58894
- fs43.mkdirSync(path48.dirname(sentinelPath), { recursive: true, mode: 448 });
59576
+ fs43.mkdirSync(path49.dirname(sentinelPath), { recursive: true, mode: 448 });
58895
59577
  const serialized = `${JSON.stringify(sentinel, null, 2)}
58896
59578
  `;
58897
59579
  if (Buffer.byteLength(serialized) > MAX_LOCAL_METADATA_BYTES) {
@@ -58968,7 +59650,7 @@ function unsafeArtifact(name, state) {
58968
59650
  return `${name} is ${state.status}${state.detail ? `: ${state.detail}` : ""}`;
58969
59651
  }
58970
59652
  function completedCutoverRefMapPath() {
58971
- return path48.join(path48.dirname(getMigrationApplyJournalPath()), "completed-cutover-refmap.json");
59653
+ return path49.join(path49.dirname(getMigrationApplyJournalPath()), "completed-cutover-refmap.json");
58972
59654
  }
58973
59655
  function loadCompletedProposalRefMap() {
58974
59656
  const mapPath = completedCutoverRefMapPath();
@@ -59072,7 +59754,7 @@ function expandTilde4(value) {
59072
59754
  if (value === "~")
59073
59755
  return os6.homedir();
59074
59756
  if (value.startsWith("~/") || value.startsWith("~\\"))
59075
- return path48.join(os6.homedir(), value.slice(2));
59757
+ return path49.join(os6.homedir(), value.slice(2));
59076
59758
  return value;
59077
59759
  }
59078
59760
  function migrationLockMatchesBundle(lock, bundle) {
@@ -59104,12 +59786,12 @@ function cutoverStashRootsFromConfig(config, migrationLockEntries = [], legacySo
59104
59786
  const bundlePath = bundle.path;
59105
59787
  const lock = locksById.get(id);
59106
59788
  const lockRoot = lock && migrationLockMatchesBundle(lock, bundle) ? lock.localRoot : undefined;
59107
- const materializedRoot = typeof bundlePath === "string" && bundlePath.length > 0 ? path48.resolve(pathResolutionBase, expandTilde4(bundlePath)) : lockRoot ? path48.resolve(pathResolutionBase, expandTilde4(lockRoot)) : undefined;
59789
+ const materializedRoot = typeof bundlePath === "string" && bundlePath.length > 0 ? path49.resolve(pathResolutionBase, expandTilde4(bundlePath)) : lockRoot ? path49.resolve(pathResolutionBase, expandTilde4(lockRoot)) : undefined;
59108
59790
  const registryId = typeof bundle.registryId === "string" ? bundle.registryId : undefined;
59109
59791
  const identityIndex = identitySources.length;
59110
59792
  identitySources.push({
59111
59793
  id,
59112
- path: materializedRoot ?? path48.resolve(pathResolutionBase, ".akm", "unresolved-sources", id),
59794
+ path: materializedRoot ?? path49.resolve(pathResolutionBase, ".akm", "unresolved-sources", id),
59113
59795
  ...registryId ? { registryId } : materializedRoot === undefined ? { registryId: id } : {}
59114
59796
  });
59115
59797
  if (!materializedRoot || seenRoots.has(materializedRoot))
@@ -59127,7 +59809,7 @@ function cutoverStashRootsFromConfig(config, migrationLockEntries = [], legacySo
59127
59809
  const configured = bundles[lock.id];
59128
59810
  if (!configured || !migrationLockMatchesBundle(lock, configured) || !lock.localRoot)
59129
59811
  continue;
59130
- const materializedRoot = path48.resolve(pathResolutionBase, expandTilde4(lock.localRoot));
59812
+ const materializedRoot = path49.resolve(pathResolutionBase, expandTilde4(lock.localRoot));
59131
59813
  if (seenRoots.has(materializedRoot))
59132
59814
  continue;
59133
59815
  seenRoots.add(materializedRoot);
@@ -59149,7 +59831,7 @@ function cutoverStashRootsFromConfig(config, migrationLockEntries = [], legacySo
59149
59831
  if (legacySources.length > 0) {
59150
59832
  const exactLegacyIds = deriveLegacyBundleIds(legacySources);
59151
59833
  legacySources.forEach((source, index) => {
59152
- const root2 = path48.resolve(pathResolutionBase, source.path);
59834
+ const root2 = path49.resolve(pathResolutionBase, source.path);
59153
59835
  const legacyId = exactLegacyIds[index];
59154
59836
  const existing = legacyIdsByRoot.get(root2);
59155
59837
  if (!legacyId || existing !== undefined && existing !== legacyId) {
@@ -59163,32 +59845,32 @@ function cutoverStashRootsFromConfig(config, migrationLockEntries = [], legacySo
59163
59845
  return candidates.map((candidate) => ({
59164
59846
  path: candidate.path,
59165
59847
  bundleId: candidate.bundleId,
59166
- legacyBundleId: legacyIdsByRoot.get(path48.resolve(candidate.path)) ?? inferredLegacyBundleIds[candidate.identityIndex],
59848
+ legacyBundleId: legacyIdsByRoot.get(path49.resolve(candidate.path)) ?? inferredLegacyBundleIds[candidate.identityIndex],
59167
59849
  registryId: candidate.registryId,
59168
59850
  primary: candidate.primary
59169
59851
  }));
59170
59852
  }
59171
59853
  function legacyCutoverSources(sentinel) {
59172
- const backupConfigPath = path48.join(sentinel.backupPath, "config.json");
59854
+ const backupConfigPath = path49.join(sentinel.backupPath, "config.json");
59173
59855
  if (!fs43.existsSync(backupConfigPath))
59174
59856
  return [];
59175
59857
  const text2 = readTextFileWithLimit(backupConfigPath, MAX_CONFIG_FILE_BYTES, "Migration backup config");
59176
59858
  return oldConfigToSearchSources(parseConfigText(text2, backupConfigPath), sentinel.pathResolutionBase);
59177
59859
  }
59178
59860
  function cutoverRefMapPath(sentinel) {
59179
- return path48.join(path48.dirname(getMigrationApplyJournalPath()), `cutover-refmap-${sentinel.operationId}.json`);
59861
+ return path49.join(path49.dirname(getMigrationApplyJournalPath()), `cutover-refmap-${sentinel.operationId}.json`);
59180
59862
  }
59181
59863
  function contentMigrationReportPath() {
59182
- return path48.join(path48.dirname(getMigrationApplyJournalPath()), "content-migration-report.json");
59864
+ return path49.join(path49.dirname(getMigrationApplyJournalPath()), "content-migration-report.json");
59183
59865
  }
59184
59866
  function persistContentMigrationReport(report) {
59185
- fs43.mkdirSync(path48.dirname(contentMigrationReportPath()), { recursive: true, mode: 448 });
59867
+ fs43.mkdirSync(path49.dirname(contentMigrationReportPath()), { recursive: true, mode: 448 });
59186
59868
  writeFileAtomic(contentMigrationReportPath(), `${JSON.stringify(report, null, 2)}
59187
59869
  `, 384);
59188
59870
  }
59189
59871
  function isPathWithin(root2, candidate) {
59190
- const relative = path48.relative(path48.resolve(root2), path48.resolve(candidate));
59191
- return relative.length > 0 && !relative.startsWith(`..${path48.sep}`) && relative !== ".." && !path48.isAbsolute(relative);
59872
+ const relative = path49.relative(path49.resolve(root2), path49.resolve(candidate));
59873
+ return relative.length > 0 && !relative.startsWith(`..${path49.sep}`) && relative !== ".." && !path49.isAbsolute(relative);
59192
59874
  }
59193
59875
  function priorReservedRenames(roots) {
59194
59876
  const reportPath = contentMigrationReportPath();
@@ -59212,15 +59894,15 @@ function priorReservedRenames(roots) {
59212
59894
  if (!entry || typeof entry.from !== "string" || typeof entry.to !== "string") {
59213
59895
  throw new ConfigError(`Invalid reserved-file rename history at ${reportPath}.`, "INVALID_CONFIG_FILE");
59214
59896
  }
59215
- const from = path48.resolve(entry.from);
59216
- const to = path48.resolve(entry.to);
59897
+ const from = path49.resolve(entry.from);
59898
+ const to = path49.resolve(entry.to);
59217
59899
  const owner = roots.find((root2) => isPathWithin(root2.path, from) && isPathWithin(root2.path, to));
59218
59900
  if (!owner)
59219
59901
  continue;
59220
- const fromName = path48.basename(from).toLowerCase();
59902
+ const fromName = path49.basename(from).toLowerCase();
59221
59903
  const stem = fromName.endsWith(".md") ? fromName.slice(0, -3) : "";
59222
59904
  const targetPattern = new RegExp(`^${stem}-content(?:-(?:[2-9]|[1-9][0-9]+))?\\.md$`, "i");
59223
- if (path48.dirname(from) !== path48.dirname(to) || fromName !== "index.md" && fromName !== "log.md" || !targetPattern.test(path48.basename(to))) {
59905
+ if (path49.dirname(from) !== path49.dirname(to) || fromName !== "index.md" && fromName !== "log.md" || !targetPattern.test(path49.basename(to))) {
59224
59906
  throw new ConfigError(`Invalid reserved-file rename history at ${reportPath}.`, "INVALID_CONFIG_FILE");
59225
59907
  }
59226
59908
  if (fs43.existsSync(from) || !fs43.existsSync(to))
@@ -59233,7 +59915,7 @@ function priorReservedRenames(roots) {
59233
59915
  return reusable;
59234
59916
  }
59235
59917
  function mergePriorReservedRenames(report, roots) {
59236
- const bySource = new Map(report.reservedRenames.map((entry) => [path48.resolve(entry.from), entry]));
59918
+ const bySource = new Map(report.reservedRenames.map((entry) => [path49.resolve(entry.from), entry]));
59237
59919
  for (const entry of priorReservedRenames(roots)) {
59238
59920
  if (!bySource.has(entry.from))
59239
59921
  bySource.set(entry.from, entry);
@@ -59244,11 +59926,11 @@ function mergePriorReservedRenames(report, roots) {
59244
59926
  function reservedRenameRefMap(renames, roots) {
59245
59927
  const map = new Map;
59246
59928
  for (const rename of renames) {
59247
- const owner = roots.find((root2) => rename.from.startsWith(`${path48.resolve(root2.path)}${path48.sep}`));
59929
+ const owner = roots.find((root2) => rename.from.startsWith(`${path49.resolve(root2.path)}${path49.sep}`));
59248
59930
  if (!owner)
59249
59931
  continue;
59250
59932
  const conceptId = (absolute) => {
59251
- const relative = path48.relative(path48.resolve(owner.path), absolute).split(path48.sep).join("/");
59933
+ const relative = path49.relative(path49.resolve(owner.path), absolute).split(path49.sep).join("/");
59252
59934
  return relative.endsWith(".md") ? relative.slice(0, -3) : undefined;
59253
59935
  };
59254
59936
  const oldId = conceptId(rename.from);
@@ -59264,7 +59946,7 @@ function reservedRenameRefMap(renames, roots) {
59264
59946
  function runContentMigrationStep(sentinel, roots) {
59265
59947
  const report = mergePriorReservedRenames(runContentMigration(roots.map((root2) => root2.path), {
59266
59948
  operationId: sentinel.operationId,
59267
- renameBatchPath: path48.join(path48.dirname(getMigrationApplyJournalPath()), `reserved-renames-${sentinel.operationId}.json`)
59949
+ renameBatchPath: path49.join(path49.dirname(getMigrationApplyJournalPath()), `reserved-renames-${sentinel.operationId}.json`)
59268
59950
  }), roots);
59269
59951
  const renameMap = reservedRenameRefMap(report.reservedRenames, roots);
59270
59952
  const proposalRoots = roots.flatMap((root2) => root2.bundleId ? [
@@ -59434,7 +60116,7 @@ function warnOrphanedActiveWorkflowRuns(statePath, roots, defaultBundle) {
59434
60116
  const root2 = rootById.get(parsed.bundle ?? defaultBundle ?? "");
59435
60117
  if (!root2)
59436
60118
  continue;
59437
- const typeRoot = path48.join(root2.path, stashDirFor("workflow") ?? "workflows");
60119
+ const typeRoot = path49.join(root2.path, stashDirFor("workflow") ?? "workflows");
59438
60120
  const filePath = assetPathForName("workflow", typeRoot, parts.name);
59439
60121
  if (!fs43.existsSync(filePath))
59440
60122
  continue;
@@ -59444,7 +60126,7 @@ function warnOrphanedActiveWorkflowRuns(statePath, roots, defaultBundle) {
59444
60126
  } catch {
59445
60127
  continue;
59446
60128
  }
59447
- const diagnostics = workflowStructureDiagnostics(path48.relative(root2.path, filePath) || filePath, raw, filePath);
60129
+ const diagnostics = workflowStructureDiagnostics(path49.relative(root2.path, filePath) || filePath, raw, filePath);
59448
60130
  if (diagnostics.length === 0)
59449
60131
  continue;
59450
60132
  warn(`WORKFLOW ACTIVE run ${row.id} targets "${row.workflow_ref}" (${filePath}), which fails 0.9 workflow ` + `structural validation and can no longer be run: ${diagnostics.map((d) => d.detail).join("; ")}. ` + "AKM does not auto-translate workflow definitions between formats \u2014 update the asset to declare " + `frontmatter \`steps:\`, or run \`akm workflow abandon ${row.id}\` to clear the stale run.`);
@@ -59455,7 +60137,7 @@ function writeGeneratedTargetConfig(raw) {
59455
60137
  const { config, droppedKeys } = generateTargetConfig(raw, CURRENT_CONFIG_VERSION);
59456
60138
  const generatedPath = getMigrationGeneratedConfigPath();
59457
60139
  parseAndValidateConfigText(JSON.stringify(config), generatedPath);
59458
- fs43.mkdirSync(path48.dirname(generatedPath), { recursive: true, mode: 448 });
60140
+ fs43.mkdirSync(path49.dirname(generatedPath), { recursive: true, mode: 448 });
59459
60141
  writeFileAtomic(generatedPath, `${JSON.stringify(config, null, 2)}
59460
60142
  `, 384);
59461
60143
  return { path: generatedPath, status: "written", droppedKeys };
@@ -59496,7 +60178,7 @@ async function runMigrationApply(options = {}) {
59496
60178
  const { plan, target, migrationLockEntries } = requireEligiblePlan(options.preparedConfigPath, existing);
59497
60179
  if (plan.status === "current" && !existing.sentinel)
59498
60180
  return { plan };
59499
- const pathResolutionBase = existing.sentinel?.pathResolutionBase ?? path48.resolve(process.cwd());
60181
+ const pathResolutionBase = existing.sentinel?.pathResolutionBase ?? path49.resolve(process.cwd());
59500
60182
  const stashRoots = cutoverStashRootsFromConfig(target, migrationLockEntries, [], pathResolutionBase);
59501
60183
  assertNoArtifactReplacementBlockers(undefined, { stashRoots: stashRoots.map((root2) => root2.path) });
59502
60184
  assertMigrationLockfileReadable();
@@ -59510,7 +60192,7 @@ async function runMigrationApply(options = {}) {
59510
60192
  backupPath: backup.path,
59511
60193
  targetConfig: sanitizeConfigForWrite(target),
59512
60194
  migrationLockEntries,
59513
- pathResolutionBase: path48.resolve(process.cwd())
60195
+ pathResolutionBase: path49.resolve(process.cwd())
59514
60196
  };
59515
60197
  if (!existing.sentinel)
59516
60198
  writeApplySentinel(sentinel);