akm-cli 0.9.0-beta.50 → 0.9.0-beta.52

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 (51) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +12 -4
  3. package/dist/akm +38 -0
  4. package/dist/akm-migrate-storage +38 -0
  5. package/dist/assets/wiki/ingest-workflow-template.md +34 -12
  6. package/dist/assets/wiki/schema-template.md +4 -4
  7. package/dist/cli/parse-args.js +46 -1
  8. package/dist/cli.js +12 -6
  9. package/dist/commands/config-cli.js +18 -2
  10. package/dist/commands/env/child-env.js +47 -0
  11. package/dist/commands/env/env-cli.js +17 -2
  12. package/dist/commands/env/secret-cli.js +24 -2
  13. package/dist/commands/health/checks.js +1 -1
  14. package/dist/commands/improve/improve-auto-accept.js +30 -2
  15. package/dist/commands/improve/improve-cli.js +1 -1
  16. package/dist/commands/improve/improve-result-file.js +9 -2
  17. package/dist/commands/improve/preparation.js +10 -2
  18. package/dist/commands/improve/recombine.js +52 -15
  19. package/dist/commands/lint/env-key-rules.js +4 -0
  20. package/dist/commands/read/knowledge.js +5 -2
  21. package/dist/commands/read/search-cli.js +2 -4
  22. package/dist/commands/read/search.js +9 -6
  23. package/dist/commands/read/show.js +19 -5
  24. package/dist/commands/sources/init.js +13 -8
  25. package/dist/commands/sources/installed-stashes.js +6 -2
  26. package/dist/commands/sources/schema-repair.js +33 -47
  27. package/dist/commands/sources/source-add.js +7 -3
  28. package/dist/commands/tasks/tasks.js +38 -10
  29. package/dist/core/asset/asset-registry.js +1 -1
  30. package/dist/core/asset/asset-spec.js +4 -2
  31. package/dist/core/config/config-migration.js +12 -11
  32. package/dist/indexer/passes/memory-inference.js +3 -2
  33. package/dist/indexer/search/db-search.js +6 -4
  34. package/dist/indexer/search/search-source.js +15 -2
  35. package/dist/integrations/agent/prompts.js +1 -1
  36. package/dist/llm/memory-infer-impl.js +138 -0
  37. package/dist/llm/memory-infer.js +1 -135
  38. package/dist/migrate-storage-node.mjs +8 -0
  39. package/dist/output/renderers.js +1 -1
  40. package/dist/scripts/migrate-storage.js +463 -347
  41. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +99 -99
  42. package/dist/sources/include.js +6 -2
  43. package/dist/sources/providers/git-install.js +10 -6
  44. package/dist/sources/providers/provider-utils.js +13 -7
  45. package/dist/sources/providers/website.js +8 -3
  46. package/dist/sources/website-ingest.js +136 -20
  47. package/dist/text-import-hook.mjs +0 -0
  48. package/dist/wiki/wiki.js +15 -11
  49. package/docs/data-and-telemetry.md +2 -2
  50. package/docs/migration/release-notes/0.9.0.md +39 -0
  51. package/package.json +8 -8
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
+ import { createRequire } from "node:module";
3
4
  var __create = Object.create;
4
5
  var __getProtoOf = Object.getPrototypeOf;
5
6
  var __defProp = Object.defineProperty;
@@ -32,10 +33,10 @@ var __toESM = (mod, isNodeMode, target) => {
32
33
  };
33
34
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
35
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
35
- var __require = import.meta.require;
36
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
36
37
 
37
38
  // src/core/warn.ts
38
- import fs from "fs";
39
+ import fs from "node:fs";
39
40
  function appendToLogFile(level, args) {
40
41
  if (!logFilePath)
41
42
  return;
@@ -67,14 +68,14 @@ var require_main = __commonJS((exports, module) => {
67
68
  var os = __require("os");
68
69
  var crypto = __require("crypto");
69
70
  var TIPS = [
70
- "\u25C8 encrypted .env [www.dotenvx.com]",
71
- "\u25C8 secrets for agents [www.dotenvx.com]",
72
- "\u2301 auth for agents [www.vestauth.com]",
73
- "\u2318 custom filepath { path: '/custom/path/.env' }",
74
- "\u2318 enable debugging { debug: true }",
75
- "\u2318 override existing { override: true }",
76
- "\u2318 suppress logs { quiet: true }",
77
- "\u2318 multiple files { path: ['.env.local', '.env'] }"
71
+ " encrypted .env [www.dotenvx.com]",
72
+ " secrets for agents [www.dotenvx.com]",
73
+ " auth for agents [www.vestauth.com]",
74
+ " custom filepath { path: '/custom/path/.env' }",
75
+ " enable debugging { debug: true }",
76
+ " override existing { override: true }",
77
+ " suppress logs { quiet: true }",
78
+ " multiple files { path: ['.env.local', '.env'] }"
78
79
  ];
79
80
  function _getRandomTip() {
80
81
  return TIPS[Math.floor(Math.random() * TIPS.length)];
@@ -141,13 +142,13 @@ var require_main = __commonJS((exports, module) => {
141
142
  return DotenvModule.parse(decrypted);
142
143
  }
143
144
  function _warn(message) {
144
- console.error(`\u26A0 ${message}`);
145
+ console.error(`⚠ ${message}`);
145
146
  }
146
147
  function _debug(message) {
147
- console.log(`\u2506 ${message}`);
148
+ console.log(`┆ ${message}`);
148
149
  }
149
150
  function _log(message) {
150
- console.log(`\u25C7 ${message}`);
151
+ console.log(`◇ ${message}`);
151
152
  }
152
153
  function _dotenvKey(options) {
153
154
  if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
@@ -389,7 +390,7 @@ var init_errors = __esm(() => {
389
390
  EMBEDDING_NOT_CONFIGURED: 'Run `akm config set embedding \'{"endpoint":"...","model":"..."}\'` to enable embeddings.',
390
391
  LLM_NOT_CONFIGURED: 'Run `akm setup` or `akm config set profiles.llm.default \'{"endpoint":"...","model":"..."}\' to configure an LLM profile.',
391
392
  TEST_ISOLATION_MISSING: "Under bun test, when AKM_STASH_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.",
392
- SETUP_TMP_STASH_REFUSED: "Use a persistent directory, or set AKM_FORCE_SETUP_TMP_STASH=1 to opt in to a sandboxed setup (setup also pre-sets AKM_STASH_DIR so config and cache writes auto-isolate into $stashDir/.akm/ \u2014 host config is preserved).",
393
+ SETUP_TMP_STASH_REFUSED: "Use a persistent directory, or set AKM_FORCE_SETUP_TMP_STASH=1 to opt in to a sandboxed setup (setup also pre-sets AKM_STASH_DIR so config and cache writes auto-isolate into $stashDir/.akm/ host config is preserved).",
393
394
  UNSAFE_STASH_DIR: "Choose a path inside your home directory (e.g. ~/akm) or another empty workspace. The stash directory cannot be the filesystem root, your home directory itself, or a sensitive system path like /etc, /var, ~/.config, or ~/.ssh."
394
395
  };
395
396
  USAGE_HINTS = {
@@ -438,7 +439,7 @@ var init_errors = __esm(() => {
438
439
  });
439
440
 
440
441
  // src/commands/env/env.ts
441
- import fs3 from "fs";
442
+ import fs3 from "node:fs";
442
443
  function scanKeys(text) {
443
444
  const keys = [];
444
445
  const seen = new Set;
@@ -3909,15 +3910,15 @@ var require_errors = __commonJS((exports) => {
3909
3910
  let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, "");
3910
3911
  if (ci >= 60 && lineStr.length > 80) {
3911
3912
  const trimStart = Math.min(ci - 39, lineStr.length - 79);
3912
- lineStr = "\u2026" + lineStr.substring(trimStart);
3913
+ lineStr = "" + lineStr.substring(trimStart);
3913
3914
  ci -= trimStart - 1;
3914
3915
  }
3915
3916
  if (lineStr.length > 80)
3916
- lineStr = lineStr.substring(0, 79) + "\u2026";
3917
+ lineStr = lineStr.substring(0, 79) + "";
3917
3918
  if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) {
3918
3919
  let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]);
3919
3920
  if (prev.length > 80)
3920
- prev = prev.substring(0, 79) + `\u2026
3921
+ prev = prev.substring(0, 79) + `…
3921
3922
  `;
3922
3923
  lineStr = prev + lineStr;
3923
3924
  }
@@ -4972,8 +4973,8 @@ var require_resolve_flow_scalar = __commonJS((exports) => {
4972
4973
  r: "\r",
4973
4974
  t: "\t",
4974
4975
  v: "\v",
4975
- N: "\x85",
4976
- _: "\xA0",
4976
+ N: "…",
4977
+ _: " ",
4977
4978
  L: "\u2028",
4978
4979
  P: "\u2029",
4979
4980
  " ": " ",
@@ -7583,7 +7584,7 @@ var init_metadata_contributors = __esm(() => {
7583
7584
  });
7584
7585
 
7585
7586
  // src/indexer/passes/metadata.ts
7586
- import fs4 from "fs";
7587
+ import fs4 from "node:fs";
7587
7588
  function extractDescriptionFromComments(filePath) {
7588
7589
  let content;
7589
7590
  try {
@@ -8062,7 +8063,7 @@ function loadDocument(ctx) {
8062
8063
  const result = parseWorkflow(ctx.content(), { path: ctx.relPath });
8063
8064
  if (result.ok)
8064
8065
  return result.document;
8065
- const summary = result.errors.map((e) => `${ctx.relPath}:${e.line} \u2014 ${e.message}`).join(`
8066
+ const summary = result.errors.map((e) => `${ctx.relPath}:${e.line} ${e.message}`).join(`
8066
8067
  `);
8067
8068
  throw new UsageError(`Workflow has errors:
8068
8069
  ${summary}`);
@@ -8304,19 +8305,21 @@ var init_asset_registry = __esm(() => {
8304
8305
  });
8305
8306
 
8306
8307
  // src/core/asset/asset-spec.ts
8307
- import path2 from "path";
8308
+ import path2 from "node:path";
8309
+ function toPosix2(input) {
8310
+ return input.replace(/\\/g, "/");
8311
+ }
8308
8312
  function getAssetTypes() {
8309
8313
  return Object.keys(ASSET_SPECS_INTERNAL);
8310
8314
  }
8311
8315
  var buildTaskAction = (ref) => `akm tasks show ${ref.replace(/^task:/, "")} -> inspect; akm tasks run <id> -> run now; akm tasks remove <id> -> unschedule`, markdownSpec, SCRIPT_EXTENSIONS, scriptSpec, ASSET_SPECS_INTERNAL, TYPE_DIRS;
8312
8316
  var init_asset_spec = __esm(() => {
8313
8317
  init_renderers();
8314
- init_common();
8315
8318
  init_asset_registry();
8316
8319
  markdownSpec = {
8317
8320
  isRelevantFile: (fileName) => path2.extname(fileName).toLowerCase() === ".md",
8318
8321
  toCanonicalName: (typeRoot, filePath) => {
8319
- const rel = toPosix(path2.relative(typeRoot, filePath));
8322
+ const rel = toPosix2(path2.relative(typeRoot, filePath));
8320
8323
  return rel.endsWith(".md") ? rel.slice(0, -3) : rel;
8321
8324
  },
8322
8325
  toAssetPath: (typeRoot, name) => {
@@ -8344,7 +8347,7 @@ var init_asset_spec = __esm(() => {
8344
8347
  ]);
8345
8348
  scriptSpec = {
8346
8349
  isRelevantFile: (fileName) => SCRIPT_EXTENSIONS.has(path2.extname(fileName).toLowerCase()),
8347
- toCanonicalName: (typeRoot, filePath) => toPosix(path2.relative(typeRoot, filePath)),
8350
+ toCanonicalName: (typeRoot, filePath) => toPosix2(path2.relative(typeRoot, filePath)),
8348
8351
  toAssetPath: (typeRoot, name) => path2.join(typeRoot, name)
8349
8352
  };
8350
8353
  ASSET_SPECS_INTERNAL = {
@@ -8352,7 +8355,7 @@ var init_asset_spec = __esm(() => {
8352
8355
  stashDir: "skills",
8353
8356
  isRelevantFile: (fileName) => fileName === "SKILL.md",
8354
8357
  toCanonicalName: (typeRoot, filePath) => {
8355
- const relDir = toPosix(path2.dirname(path2.relative(typeRoot, filePath)));
8358
+ const relDir = toPosix2(path2.dirname(path2.relative(typeRoot, filePath)));
8356
8359
  if (!relDir || relDir === ".")
8357
8360
  return;
8358
8361
  return relDir;
@@ -8374,7 +8377,7 @@ var init_asset_spec = __esm(() => {
8374
8377
  stashDir: "env",
8375
8378
  isRelevantFile: (fileName) => fileName === ".env" || fileName.endsWith(".env"),
8376
8379
  toCanonicalName: (typeRoot, filePath) => {
8377
- const rel = toPosix(path2.relative(typeRoot, filePath));
8380
+ const rel = toPosix2(path2.relative(typeRoot, filePath));
8378
8381
  const fileName = path2.basename(rel);
8379
8382
  if (fileName === ".env") {
8380
8383
  const dir = path2.dirname(rel);
@@ -8389,12 +8392,12 @@ var init_asset_spec = __esm(() => {
8389
8392
  return path2.join(typeRoot, name.endsWith(".env") ? name : `${name}.env`);
8390
8393
  },
8391
8394
  rendererName: "env-file",
8392
- actionBuilder: (ref) => `akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with the whole .env injected (values never reach stdout); akm env export ${ref} --out <file> -> write a sourceable script to a file`
8395
+ actionBuilder: (ref) => `akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with the whole .env injected (prefer --clean to minimize inherited parent env; child stdout is not redacted); akm env export ${ref} --out <file> -> write a sourceable script to a file`
8393
8396
  },
8394
8397
  secret: {
8395
8398
  stashDir: "secrets",
8396
8399
  isRelevantFile: (fileName) => !fileName.endsWith(".lock") && !fileName.endsWith(".sensitive"),
8397
- toCanonicalName: (typeRoot, filePath) => toPosix(path2.relative(typeRoot, filePath)),
8400
+ toCanonicalName: (typeRoot, filePath) => toPosix2(path2.relative(typeRoot, filePath)),
8398
8401
  toAssetPath: (typeRoot, name) => path2.join(typeRoot, name),
8399
8402
  rendererName: "secret-file",
8400
8403
  actionBuilder: (ref) => `akm show ${ref} -> name only (value never shown); akm secret path ${ref} -> file path; akm secret run ${ref} <VAR> -- <command> -> run with value injected into $VAR`
@@ -8415,7 +8418,7 @@ var init_asset_spec = __esm(() => {
8415
8418
  stashDir: "tasks",
8416
8419
  isRelevantFile: (fileName) => path2.extname(fileName).toLowerCase() === ".yml",
8417
8420
  toCanonicalName: (typeRoot, filePath) => {
8418
- const rel = toPosix(path2.relative(typeRoot, filePath));
8421
+ const rel = toPosix2(path2.relative(typeRoot, filePath));
8419
8422
  return rel.endsWith(".yml") ? rel.slice(0, -4) : rel;
8420
8423
  },
8421
8424
  toAssetPath: (typeRoot, name) => {
@@ -8442,9 +8445,6 @@ var init_asset_spec = __esm(() => {
8442
8445
  });
8443
8446
 
8444
8447
  // src/core/common.ts
8445
- function toPosix(input) {
8446
- return input.replace(/\\/g, "/");
8447
- }
8448
8448
  function asNonEmptyString(value) {
8449
8449
  if (typeof value !== "string")
8450
8450
  return;
@@ -8463,7 +8463,7 @@ var init_common = __esm(() => {
8463
8463
  });
8464
8464
 
8465
8465
  // src/core/paths.ts
8466
- import path3 from "path";
8466
+ import path3 from "node:path";
8467
8467
  function isUnderBunTest(env) {
8468
8468
  return env.BUN_TEST === "1" || env.NODE_ENV === "test";
8469
8469
  }
@@ -8508,16 +8508,16 @@ import fs5 from "fs";
8508
8508
  import path5 from "path";
8509
8509
 
8510
8510
  // src/core/state-db.ts
8511
- import path4 from "path";
8511
+ import path4 from "node:path";
8512
8512
 
8513
8513
  // src/storage/managed-db.ts
8514
- import fs2 from "fs";
8515
- import path from "path";
8514
+ import fs2 from "node:fs";
8515
+ import path from "node:path";
8516
8516
 
8517
8517
  // src/storage/database.ts
8518
- import { createRequire } from "module";
8518
+ import { createRequire as createRequire2 } from "node:module";
8519
8519
  var isBun = !!process.versions?.bun;
8520
- var nodeRequire = createRequire(import.meta.url);
8520
+ var nodeRequire = createRequire2(import.meta.url);
8521
8521
  var bunSqliteProvider = {
8522
8522
  name: "bun:sqlite",
8523
8523
  supported: () => isBun,
@@ -8567,9 +8567,9 @@ function loadBetterSqlite3() {
8567
8567
  mod = nodeRequire("better-sqlite3");
8568
8568
  } catch (err) {
8569
8569
  throw new Error(`akm could not load 'better-sqlite3', the SQLite driver it needs on Node.js.
8570
- ` + ` \u2022 Reinstall akm with a working C/C++ build toolchain so its optional
8571
- ` + " '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).
8572
- ` + ` \u2022 Or run akm under Bun, which has a built-in SQLite driver and needs no native build.
8570
+ ` + ` Reinstall akm with a working C/C++ build toolchain so its optional
8571
+ ` + " 'better-sqlite3' native binding rebuilds (a global `npm i -g better-sqlite3`\n" + ` will NOT be resolved Node loads it from akm's own node_modules).
8572
+ ` + ` Or run akm under Bun, which has a built-in SQLite driver and needs no native build.
8573
8573
  ` + ` Underlying load error: ${err instanceof Error ? err.message : String(err)}`);
8574
8574
  }
8575
8575
  betterSqlite3Ctor = mod.default ?? mod;
@@ -8591,10 +8591,10 @@ function openNodeDatabase(path, opts) {
8591
8591
  init_warn();
8592
8592
 
8593
8593
  // src/runtime.ts
8594
- import { createWriteStream, statfsSync } from "fs";
8595
- import { createRequire as createRequire2 } from "module";
8594
+ import { createWriteStream, statfsSync } from "node:fs";
8595
+ import { createRequire as createRequire3 } from "node:module";
8596
8596
  var isBun2 = !!process.versions?.bun;
8597
- var nodeRequire2 = createRequire2(import.meta.url);
8597
+ var nodeRequire2 = createRequire3(import.meta.url);
8598
8598
  function statfsType(path) {
8599
8599
  try {
8600
8600
  return statfsSync(path).type;
@@ -8630,7 +8630,7 @@ function warnInvalidJournalModeOnce(raw) {
8630
8630
  if (warnedInvalid)
8631
8631
  return;
8632
8632
  warnedInvalid = true;
8633
- warn(`[akm] invalid AKM_SQLITE_JOURNAL_MODE=${JSON.stringify(raw)} \u2014 using WAL (valid: WAL, DELETE, TRUNCATE)`);
8633
+ warn(`[akm] invalid AKM_SQLITE_JOURNAL_MODE=${JSON.stringify(raw)} using WAL (valid: WAL, DELETE, TRUNCATE)`);
8634
8634
  }
8635
8635
  var FS_MAGIC_NFS = 26985;
8636
8636
  var FS_MAGIC_SMB = 20859;
@@ -8672,7 +8672,7 @@ function warnNetworkFallbackOnce(dataDir) {
8672
8672
  if (warnedNetworkFallback)
8673
8673
  return;
8674
8674
  warnedNetworkFallback = true;
8675
- warn(`[akm] network filesystem detected at ${dataDir} \u2014 WAL unsupported, using DELETE journal mode`);
8675
+ warn(`[akm] network filesystem detected at ${dataDir} WAL unsupported, using DELETE journal mode`);
8676
8676
  }
8677
8677
 
8678
8678
  // src/storage/managed-db.ts
@@ -8759,26 +8759,26 @@ var MIGRATIONS = [
8759
8759
  {
8760
8760
  id: "001-initial-schema",
8761
8761
  up: `
8762
- -- \u2500\u2500 events \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
8762
+ -- ── events ──────────────────────────────────────────────────────────────
8763
8763
  --
8764
8764
  -- Replaces events.jsonl. Indexed (query) columns:
8765
- -- id INTEGER PK \u2014 monotonic rowid; replaces byte-offset cursor.
8765
+ -- id INTEGER PK monotonic rowid; replaces byte-offset cursor.
8766
8766
  -- Callers store this as "sinceId" for resume.
8767
- -- event_type TEXT \u2014 indexed; replaces the type filter in readEvents().
8768
- -- ts TEXT \u2014 ISO-8601 UTC ms; indexed for range queries.
8769
- -- ref TEXT \u2014 nullable asset ref; indexed for ref-scoped queries.
8767
+ -- event_type TEXT indexed; replaces the type filter in readEvents().
8768
+ -- ts TEXT ISO-8601 UTC ms; indexed for range queries.
8769
+ -- ref TEXT nullable asset ref; indexed for ref-scoped queries.
8770
8770
  --
8771
8771
  -- Extensible (metadata_json) columns:
8772
- -- metadata_json TEXT \u2014 JSON object storing all non-indexed payload
8772
+ -- metadata_json TEXT JSON object storing all non-indexed payload
8773
8773
  -- fields (tags, any future structured fields).
8774
8774
  -- Maps directly to EventEnvelope.metadata.
8775
8775
  --
8776
- -- schema_version mirrors EventEnvelope.schemaVersion \u2014 always 1 for v1
8776
+ -- schema_version mirrors EventEnvelope.schemaVersion always 1 for v1
8777
8777
  -- rows. Stored as a column (not in the JSON blob) so future schema
8778
8778
  -- changes can be detected and migrated row-by-row if ever needed.
8779
8779
  --
8780
8780
  -- TTL: rows where ts < NOW() - 90 days can be deleted by a maintenance job.
8781
- -- No automatic deletion occurs here \u2014 callers call purgeOldEvents().
8781
+ -- No automatic deletion occurs here callers call purgeOldEvents().
8782
8782
  --
8783
8783
  -- ADD COLUMN extension points (future migrations):
8784
8784
  -- ALTER TABLE events ADD COLUMN stash_dir TEXT DEFAULT NULL;
@@ -8794,38 +8794,38 @@ var MIGRATIONS = [
8794
8794
  );
8795
8795
 
8796
8796
  -- Query patterns supported by these indexes:
8797
- -- SELECT \u2026 WHERE event_type = ? \u2192 idx_events_type
8798
- -- SELECT \u2026 WHERE ref = ? \u2192 idx_events_ref
8799
- -- SELECT \u2026 WHERE ts >= ? AND ts <= ? \u2192 idx_events_ts
8800
- -- SELECT \u2026 WHERE event_type = ? AND ref = ? \u2192 idx_events_type (prefix scan) + filter
8801
- -- SELECT \u2026 WHERE id > ? \u2192 PK (rowid) \u2014 no extra index needed
8797
+ -- SELECT WHERE event_type = ? idx_events_type
8798
+ -- SELECT WHERE ref = ? idx_events_ref
8799
+ -- SELECT WHERE ts >= ? AND ts <= ? idx_events_ts
8800
+ -- SELECT WHERE event_type = ? AND ref = ? idx_events_type (prefix scan) + filter
8801
+ -- SELECT WHERE id > ? PK (rowid) no extra index needed
8802
8802
  CREATE INDEX IF NOT EXISTS idx_events_type ON events(event_type);
8803
8803
  CREATE INDEX IF NOT EXISTS idx_events_ref ON events(ref);
8804
8804
  CREATE INDEX IF NOT EXISTS idx_events_ts ON events(ts);
8805
8805
 
8806
- -- \u2500\u2500 proposals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
8806
+ -- ── proposals ────────────────────────────────────────────────────────────
8807
8807
  --
8808
8808
  -- Replaces per-uuid JSON directories under <stashDir>/.akm/proposals/.
8809
8809
  --
8810
8810
  -- Indexed (query) columns:
8811
- -- id TEXT PK \u2014 UUID (crypto.randomUUID()); stable directory name.
8812
- -- stash_dir TEXT \u2014 absolute stash root; multi-stash installs need
8811
+ -- id TEXT PK UUID (crypto.randomUUID()); stable directory name.
8812
+ -- stash_dir TEXT absolute stash root; multi-stash installs need
8813
8813
  -- this to partition proposal lists per stash.
8814
- -- ref TEXT \u2014 target asset ref (e.g. "lesson:alpha");
8814
+ -- ref TEXT target asset ref (e.g. "lesson:alpha");
8815
8815
  -- indexed for ref-scoped queue views.
8816
- -- status TEXT \u2014 "pending" | "accepted" | "rejected"; indexed
8816
+ -- status TEXT "pending" | "accepted" | "rejected"; indexed
8817
8817
  -- so pending-queue queries are fast.
8818
- -- source TEXT \u2014 human-readable origin tag (e.g. "reflect").
8819
- -- created_at TEXT \u2014 ISO-8601; used for ORDER BY created_at ASC.
8820
- -- updated_at TEXT \u2014 ISO-8601; updated on accept/reject.
8818
+ -- source TEXT human-readable origin tag (e.g. "reflect").
8819
+ -- created_at TEXT ISO-8601; used for ORDER BY created_at ASC.
8820
+ -- updated_at TEXT ISO-8601; updated on accept/reject.
8821
8821
  --
8822
8822
  -- Large payload columns (NOT indexed):
8823
- -- content TEXT \u2014 full markdown text; the proposal payload body.
8824
- -- frontmatter_json TEXT \u2014 JSON of parsed frontmatter (may be NULL when
8823
+ -- content TEXT full markdown text; the proposal payload body.
8824
+ -- frontmatter_json TEXT JSON of parsed frontmatter (may be NULL when
8825
8825
  -- the content has no frontmatter block).
8826
8826
  --
8827
8827
  -- Extensible (metadata_json) columns:
8828
- -- metadata_json TEXT \u2014 JSON object for future proposal fields.
8828
+ -- metadata_json TEXT JSON object for future proposal fields.
8829
8829
  -- Current fields stored here: sourceRun,
8830
8830
  -- review, confidence, gateDecision (#577),
8831
8831
  -- backupContent, eligibilitySource.
@@ -8851,38 +8851,38 @@ var MIGRATIONS = [
8851
8851
  );
8852
8852
 
8853
8853
  -- Query patterns:
8854
- -- SELECT \u2026 WHERE stash_dir = ? AND status = ? \u2192 idx_proposals_stash_status
8855
- -- SELECT \u2026 WHERE ref = ? AND status = ? \u2192 idx_proposals_ref_status
8856
- -- SELECT \u2026 WHERE id = ? \u2192 PK
8854
+ -- SELECT WHERE stash_dir = ? AND status = ? idx_proposals_stash_status
8855
+ -- SELECT WHERE ref = ? AND status = ? idx_proposals_ref_status
8856
+ -- SELECT WHERE id = ? PK
8857
8857
  CREATE INDEX IF NOT EXISTS idx_proposals_stash_status
8858
8858
  ON proposals(stash_dir, status);
8859
8859
  CREATE INDEX IF NOT EXISTS idx_proposals_ref_status
8860
8860
  ON proposals(ref, status);
8861
8861
 
8862
- -- \u2500\u2500 task_history \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
8862
+ -- ── task_history ─────────────────────────────────────────────────────────
8863
8863
  --
8864
8864
  -- Replaces per-task JSONL files under <cacheDir>/tasks/history/.
8865
8865
  --
8866
8866
  -- Indexed (query) columns:
8867
- -- task_id TEXT PK \u2014 stable task identifier string.
8868
- -- status TEXT \u2014 terminal status (e.g. "completed", "failed",
8867
+ -- task_id TEXT PK stable task identifier string.
8868
+ -- status TEXT terminal status (e.g. "completed", "failed",
8869
8869
  -- "cancelled"); indexed for status-scoped queries.
8870
- -- started_at TEXT \u2014 ISO-8601; indexed for time-range queries.
8871
- -- target_kind TEXT \u2014 kind of the target entity (e.g. "issue",
8870
+ -- started_at TEXT ISO-8601; indexed for time-range queries.
8871
+ -- target_kind TEXT kind of the target entity (e.g. "issue",
8872
8872
  -- "workflow", "agent"); indexed for kind-scoped queries.
8873
- -- target_ref TEXT \u2014 stable ref of the target entity; indexed for
8873
+ -- target_ref TEXT stable ref of the target entity; indexed for
8874
8874
  -- per-target history lookups.
8875
8875
  --
8876
8876
  -- Non-indexed time columns:
8877
- -- completed_at TEXT \u2014 ISO-8601 or NULL if still running.
8878
- -- failed_at TEXT \u2014 ISO-8601 or NULL.
8877
+ -- completed_at TEXT ISO-8601 or NULL if still running.
8878
+ -- failed_at TEXT ISO-8601 or NULL.
8879
8879
  --
8880
8880
  -- Non-indexed diagnostic columns:
8881
- -- log_path TEXT \u2014 absolute path to the task log file, if any.
8881
+ -- log_path TEXT absolute path to the task log file, if any.
8882
8882
  --
8883
8883
  -- Extensible (metadata_json) columns:
8884
- -- metadata_json TEXT \u2014 JSON object for future task fields (exit_code,
8885
- -- runner, priority, parent_task_id, \u2026).
8884
+ -- metadata_json TEXT JSON object for future task fields (exit_code,
8885
+ -- runner, priority, parent_task_id, ).
8886
8886
  --
8887
8887
  -- ADD COLUMN extension points (future migrations):
8888
8888
  -- ALTER TABLE task_history ADD COLUMN exit_code INTEGER DEFAULT NULL;
@@ -8903,10 +8903,10 @@ var MIGRATIONS = [
8903
8903
  );
8904
8904
 
8905
8905
  -- Query patterns:
8906
- -- SELECT \u2026 WHERE task_id = ? \u2192 PK
8907
- -- SELECT \u2026 WHERE started_at >= ? AND started_at <= ? \u2192 idx_task_history_started
8908
- -- SELECT \u2026 WHERE target_kind = ? AND target_ref = ? \u2192 idx_task_history_target
8909
- -- SELECT \u2026 WHERE status = ? \u2192 idx_task_history_status
8906
+ -- SELECT WHERE task_id = ? PK
8907
+ -- SELECT WHERE started_at >= ? AND started_at <= ? idx_task_history_started
8908
+ -- SELECT WHERE target_kind = ? AND target_ref = ? idx_task_history_target
8909
+ -- SELECT WHERE status = ? idx_task_history_status
8910
8910
  CREATE INDEX IF NOT EXISTS idx_task_history_started
8911
8911
  ON task_history(started_at);
8912
8912
  CREATE INDEX IF NOT EXISTS idx_task_history_target
@@ -8975,12 +8975,12 @@ var MIGRATIONS = [
8975
8975
  );
8976
8976
 
8977
8977
  -- Query patterns supported:
8978
- -- SELECT \u2026 WHERE started_at >= ? AND started_at <= ?
8979
- -- \u2192 idx_improve_runs_started
8980
- -- SELECT \u2026 WHERE dry_run = 0
8981
- -- \u2192 idx_improve_runs_dry_run (productivity audits filter trap)
8982
- -- SELECT \u2026 WHERE stash_dir = ? AND scope_mode = ?
8983
- -- \u2192 idx_improve_runs_stash_scope
8978
+ -- SELECT WHERE started_at >= ? AND started_at <= ?
8979
+ -- idx_improve_runs_started
8980
+ -- SELECT WHERE dry_run = 0
8981
+ -- idx_improve_runs_dry_run (productivity audits filter trap)
8982
+ -- SELECT WHERE stash_dir = ? AND scope_mode = ?
8983
+ -- idx_improve_runs_stash_scope
8984
8984
  CREATE INDEX IF NOT EXISTS idx_improve_runs_started
8985
8985
  ON improve_runs(started_at);
8986
8986
  CREATE INDEX IF NOT EXISTS idx_improve_runs_dry_run
@@ -9007,9 +9007,9 @@ var MIGRATIONS = [
9007
9007
  );
9008
9008
 
9009
9009
  -- Query patterns:
9010
- -- SELECT \u2026 WHERE harness = ? \u2192 idx_extract_sessions_harness
9011
- -- SELECT \u2026 WHERE processed_at >= ? \u2192 idx_extract_sessions_processed
9012
- -- SELECT \u2026 WHERE harness = ? AND session_id = ? \u2192 PK
9010
+ -- SELECT WHERE harness = ? idx_extract_sessions_harness
9011
+ -- SELECT WHERE processed_at >= ? idx_extract_sessions_processed
9012
+ -- SELECT WHERE harness = ? AND session_id = ? PK
9013
9013
  CREATE INDEX IF NOT EXISTS idx_extract_sessions_harness
9014
9014
  ON extract_sessions_seen(harness);
9015
9015
  CREATE INDEX IF NOT EXISTS idx_extract_sessions_processed
@@ -101,10 +101,14 @@ function copyDirectoryContents(sourceDir, destinationDir) {
101
101
  }
102
102
  }
103
103
  function copyPath(sourcePath, destinationPath) {
104
- const stat = fs.statSync(sourcePath);
104
+ const stat = fs.lstatSync(sourcePath);
105
+ if (stat.isSymbolicLink()) {
106
+ throw new Error(`Path in akm.include must not be a symlink: ${sourcePath}`);
107
+ }
105
108
  fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
106
109
  if (stat.isDirectory()) {
107
- fs.cpSync(sourcePath, destinationPath, { recursive: true, force: true });
110
+ fs.mkdirSync(destinationPath, { recursive: true });
111
+ copyDirectoryContents(sourcePath, destinationPath);
108
112
  return;
109
113
  }
110
114
  fs.copyFileSync(sourcePath, destinationPath);
@@ -174,33 +174,37 @@ export function cloneRepo(cloneUrl, ref, destDir, writable = false) {
174
174
  * and emit a message that names the cause and the fix.
175
175
  */
176
176
  export function classifyCloneFailure(url, stderr, spawnError) {
177
+ const safeUrl = redactUrlUserinfo(url);
177
178
  const raw = (stderr ?? "").trim();
178
179
  const spawnMsg = spawnError?.message ?? "";
179
180
  // `git` binary not on PATH.
180
181
  if (spawnError?.code === "ENOENT") {
181
- return `Failed to clone ${url}: 'git' is not installed or not on PATH. Install git, then re-run.`;
182
+ return `Failed to clone ${safeUrl}: 'git' is not installed or not on PATH. Install git, then re-run.`;
182
183
  }
183
184
  // Auth-prompt fall-through (the headline #487 case).
184
185
  if (/could not read Username|terminal prompts disabled|Authentication failed|fatal: Authentication/i.test(raw)) {
185
- return (`Failed to clone ${url}: repository not found or private. ` +
186
+ return (`Failed to clone ${safeUrl}: repository not found or private. ` +
186
187
  `If the repository is public, double-check the URL and try again. ` +
187
188
  `If it is private, set GH_TOKEN (or configure a git credential helper) before re-running.`);
188
189
  }
189
190
  // 404-style messages from git http.
190
191
  if (/repository '.*' not found|HTTP 404|fatal: remote error|not found:|Not Found/i.test(raw)) {
191
- return (`Failed to clone ${url}: repository not found. ` +
192
+ return (`Failed to clone ${safeUrl}: repository not found. ` +
192
193
  `Check the URL — for GitHub, the form is 'owner/repo' or 'github:owner/repo'.`);
193
194
  }
194
195
  // SSH connection issues.
195
196
  if (/Permission denied \(publickey\)|kex_exchange_identification|Connection refused|Connection timed out/i.test(raw)) {
196
- return (`Failed to clone ${url}: network or SSH failure. ` +
197
+ return (`Failed to clone ${safeUrl}: network or SSH failure. ` +
197
198
  `Check connectivity, your SSH agent, and the remote host's availability.`);
198
199
  }
199
200
  // Branch / ref-specific failures.
200
201
  if (/Remote branch .* not found in upstream origin|couldn't find remote ref/i.test(raw)) {
201
- return (`Failed to clone ${url}: the requested branch/tag does not exist on the remote. ` +
202
+ return (`Failed to clone ${safeUrl}: the requested branch/tag does not exist on the remote. ` +
202
203
  `Verify the ref name and re-run.`);
203
204
  }
204
205
  const detail = raw || spawnMsg || "unknown error";
205
- return `Failed to clone ${url}: ${detail}`;
206
+ return `Failed to clone ${safeUrl}: ${redactUrlUserinfo(detail)}`;
207
+ }
208
+ function redactUrlUserinfo(text) {
209
+ return text.replace(/\b([A-Za-z][A-Za-z0-9+.-]*:\/\/)([^\s/@]+)@/g, "$1[REDACTED]@");
206
210
  }
@@ -91,14 +91,20 @@ export function copyDirectoryContents(sourceDir, destinationDir) {
91
91
  continue;
92
92
  const src = path.join(sourceDir, entry.name);
93
93
  const dest = path.join(destinationDir, entry.name);
94
- fs.mkdirSync(path.dirname(dest), { recursive: true });
95
- if (entry.isDirectory()) {
96
- fs.cpSync(src, dest, { recursive: true, force: true });
97
- }
98
- else {
99
- fs.copyFileSync(src, dest);
100
- }
94
+ copyPathWithoutSymlinks(src, dest);
95
+ }
96
+ }
97
+ function copyPathWithoutSymlinks(sourcePath, destinationPath) {
98
+ const stat = fs.lstatSync(sourcePath);
99
+ if (stat.isSymbolicLink())
100
+ return;
101
+ fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
102
+ if (stat.isDirectory()) {
103
+ fs.mkdirSync(destinationPath, { recursive: true });
104
+ copyDirectoryContents(sourcePath, destinationPath);
105
+ return;
101
106
  }
107
+ fs.copyFileSync(sourcePath, destinationPath);
102
108
  }
103
109
  export function isDirectory(target) {
104
110
  try {
@@ -2,12 +2,13 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import { registerSourceProvider } from "../provider-factory.js";
5
- import { ensureWebsiteMirror, getWebsiteCachePaths, validateWebsiteUrl } from "../website-ingest.js";
5
+ import { ensureWebsiteMirror, getWebsiteCachePaths, shouldAllowPrivateWebsiteUrlForTests, validateWebsiteUrl, } from "../website-ingest.js";
6
6
  /**
7
7
  * Website source provider — thin adapter over the shared website ingest module.
8
8
  */
9
9
  registerSourceProvider("website", (config) => {
10
- const url = validateWebsiteUrl(config.url ?? "");
10
+ const allowPrivateHosts = shouldAllowPrivateWebsiteUrlForTests(config.url ?? "");
11
+ const url = validateWebsiteUrl(config.url ?? "", { allowPrivateHosts });
11
12
  const name = config.name ?? "website";
12
13
  return {
13
14
  kind: "website",
@@ -16,7 +17,11 @@ registerSourceProvider("website", (config) => {
16
17
  return getWebsiteCachePaths(url).stashDir;
17
18
  },
18
19
  async sync(options) {
19
- await ensureWebsiteMirror(config, { requireStashDir: true, force: options?.force });
20
+ await ensureWebsiteMirror(config, {
21
+ requireStashDir: true,
22
+ force: options?.force,
23
+ ...(allowPrivateHosts ? { allowPrivateHosts: true } : {}),
24
+ });
20
25
  },
21
26
  };
22
27
  });