akm-cli 0.9.0-beta.53 → 0.9.0-beta.55

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 (123) hide show
  1. package/dist/cli/clack.js +56 -0
  2. package/dist/cli/confirm.js +1 -1
  3. package/dist/cli.js +5 -3
  4. package/dist/commands/agent/contribute-cli.js +2 -3
  5. package/dist/commands/env/env-cli.js +187 -202
  6. package/dist/commands/env/secret-cli.js +109 -121
  7. package/dist/commands/feedback-cli.js +152 -155
  8. package/dist/commands/health/advisories.js +151 -0
  9. package/dist/commands/health/html-report.js +33 -10
  10. package/dist/commands/health/improve-metrics.js +754 -0
  11. package/dist/commands/health/llm-usage.js +65 -0
  12. package/dist/commands/health/md-report.js +103 -0
  13. package/dist/commands/health/metrics.js +278 -0
  14. package/dist/commands/health/task-runs.js +135 -0
  15. package/dist/commands/health/types.js +18 -0
  16. package/dist/commands/health/windows.js +196 -0
  17. package/dist/commands/health.js +15 -1492
  18. package/dist/commands/improve/anti-collapse.js +170 -0
  19. package/dist/commands/improve/collapse-detector.js +3 -2
  20. package/dist/commands/improve/consolidate.js +636 -633
  21. package/dist/commands/improve/dedup.js +1 -1
  22. package/dist/commands/improve/distill/content-repair.js +202 -0
  23. package/dist/commands/improve/distill/promote-memory.js +228 -0
  24. package/dist/commands/improve/distill/quality-gate.js +233 -0
  25. package/dist/commands/improve/distill-guards.js +127 -0
  26. package/dist/commands/improve/distill.js +49 -575
  27. package/dist/commands/improve/extract-cli.js +74 -76
  28. package/dist/commands/improve/extract.js +6 -4
  29. package/dist/commands/improve/hot-probation.js +45 -0
  30. package/dist/commands/improve/improve-auto-accept.js +3 -2
  31. package/dist/commands/improve/improve-cli.js +14 -13
  32. package/dist/commands/improve/improve-result-file.js +2 -1
  33. package/dist/commands/improve/improve.js +6 -5
  34. package/dist/commands/improve/loop-stages.js +19 -21
  35. package/dist/commands/improve/outcome-loop.js +18 -16
  36. package/dist/commands/improve/preparation.js +23 -5
  37. package/dist/commands/improve/procedural.js +10 -31
  38. package/dist/commands/improve/recombine.js +19 -43
  39. package/dist/commands/improve/reflect.js +1 -1
  40. package/dist/commands/improve/schema-similarity-gate.js +168 -0
  41. package/dist/commands/improve/shared.js +48 -0
  42. package/dist/commands/observability-cli.js +4 -4
  43. package/dist/commands/proposal/drain-policies.js +2 -2
  44. package/dist/commands/proposal/drain.js +1 -1
  45. package/dist/commands/proposal/legacy-import.js +115 -0
  46. package/dist/commands/proposal/proposal-cli.js +3 -3
  47. package/dist/commands/proposal/proposal.js +2 -1
  48. package/dist/commands/proposal/propose.js +1 -1
  49. package/dist/commands/proposal/repository.js +829 -0
  50. package/dist/commands/proposal/validators/proposals.js +5 -920
  51. package/dist/commands/read/curate.js +4 -4
  52. package/dist/commands/read/remember-cli.js +132 -137
  53. package/dist/commands/read/search-cli.js +7 -5
  54. package/dist/commands/read/search.js +7 -3
  55. package/dist/commands/read/show.js +3 -5
  56. package/dist/commands/registry-cli.js +76 -87
  57. package/dist/commands/sources/add-cli.js +91 -95
  58. package/dist/commands/sources/history.js +1 -1
  59. package/dist/commands/sources/init.js +12 -0
  60. package/dist/commands/sources/schema-repair.js +1 -1
  61. package/dist/commands/sources/sources-cli.js +3 -3
  62. package/dist/commands/sources/stash-cli.js +2 -2
  63. package/dist/commands/tasks/default-tasks.js +12 -0
  64. package/dist/commands/tasks/tasks-cli.js +1 -2
  65. package/dist/commands/wiki-cli.js +2 -3
  66. package/dist/core/common.js +3 -3
  67. package/dist/core/config/config-schema.js +6 -0
  68. package/dist/core/config/config.js +12 -0
  69. package/dist/core/deep-merge.js +38 -0
  70. package/dist/core/events.js +2 -1
  71. package/dist/core/logs-db.js +8 -13
  72. package/dist/core/paths.js +14 -14
  73. package/dist/core/state-db.js +13 -1140
  74. package/dist/core/warn.js +21 -0
  75. package/dist/indexer/db/db.js +72 -709
  76. package/dist/indexer/db/entry-mapper.js +41 -0
  77. package/dist/indexer/db/schema.js +516 -0
  78. package/dist/indexer/ensure-index.js +3 -2
  79. package/dist/indexer/feedback/utility-policy.js +85 -0
  80. package/dist/indexer/graph/graph-extraction.js +2 -1
  81. package/dist/indexer/index-writer-lock.js +18 -0
  82. package/dist/indexer/indexer.js +94 -27
  83. package/dist/indexer/read-preflight.js +23 -0
  84. package/dist/indexer/search/fts-query.js +51 -0
  85. package/dist/indexer/walk/walker.js +21 -13
  86. package/dist/integrations/agent/detect.js +9 -0
  87. package/dist/integrations/agent/index.js +1 -1
  88. package/dist/integrations/agent/spawn.js +15 -66
  89. package/dist/llm/client.js +12 -0
  90. package/dist/llm/embedder.js +26 -2
  91. package/dist/llm/embedders/local.js +7 -1
  92. package/dist/output/text/helpers.js +13 -0
  93. package/dist/scripts/migrate-storage.js +6903 -7424
  94. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +49 -44
  95. package/dist/setup/detect.js +9 -0
  96. package/dist/setup/legacy-config.js +106 -0
  97. package/dist/setup/prompt.js +57 -0
  98. package/dist/setup/providers.js +14 -0
  99. package/dist/setup/registry-stash-loader.js +12 -0
  100. package/dist/setup/semantic-assets.js +124 -0
  101. package/dist/setup/setup.js +25 -1608
  102. package/dist/setup/steps/connection.js +734 -0
  103. package/dist/setup/steps/output.js +31 -0
  104. package/dist/setup/steps/platforms.js +124 -0
  105. package/dist/setup/steps/semantic.js +27 -0
  106. package/dist/setup/steps/sources.js +222 -0
  107. package/dist/setup/steps/stashdir.js +42 -0
  108. package/dist/setup/steps/tasks.js +152 -0
  109. package/dist/storage/repositories/canaries-repository.js +107 -0
  110. package/dist/storage/repositories/consolidation-repository.js +38 -0
  111. package/dist/storage/repositories/embeddings-repository.js +72 -0
  112. package/dist/storage/repositories/events-repository.js +187 -0
  113. package/dist/storage/repositories/extract-sessions-repository.js +96 -0
  114. package/dist/storage/repositories/improve-runs-repository.js +130 -0
  115. package/dist/storage/repositories/index-db.js +4 -7
  116. package/dist/storage/repositories/proposals-repository.js +220 -0
  117. package/dist/storage/repositories/recombine-repository.js +213 -0
  118. package/dist/storage/repositories/task-history-repository.js +93 -0
  119. package/dist/storage/sqlite-pragmas.js +3 -3
  120. package/dist/tasks/backends/index.js +9 -0
  121. package/dist/tasks/runner.js +11 -1
  122. package/package.json +2 -2
  123. package/dist/commands/improve/homeostatic.js +0 -497
@@ -18,16 +18,14 @@
18
18
  import { spawnSync } from "node:child_process";
19
19
  import fs from "node:fs";
20
20
  import path from "node:path";
21
- import { defineCommand } from "citty";
22
21
  import { getStringArg } from "../../cli/parse-args.js";
23
- import { defineGroupCommand, output, runWithJsonErrors } from "../../cli/shared.js";
22
+ import { defineGroupCommand, defineJsonCommand, output } from "../../cli/shared.js";
24
23
  import { deriveCanonicalAssetName } from "../../core/asset/asset-spec.js";
25
24
  import { loadConfig } from "../../core/config/config.js";
26
25
  import { makeSecretRef, resolveSecretPath } from "../../core/env-secret-ref.js";
27
26
  import { ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
28
27
  import { appendEvent } from "../../core/events.js";
29
28
  import { resolveSourceEntries } from "../../indexer/search/search-source.js";
30
- import { getHyphenatedArg } from "../../output/context.js";
31
29
  import { readStdin } from "../../runtime.js";
32
30
  import { buildChildEnv } from "./child-env.js";
33
31
  function parseKeyListFlag(raw) {
@@ -70,18 +68,16 @@ function listSecretsRecursive() {
70
68
  }
71
69
  return result;
72
70
  }
73
- const secretListCommand = defineCommand({
71
+ const secretListCommand = defineJsonCommand({
74
72
  meta: {
75
73
  name: "list",
76
74
  description: "List all secrets across all stashes by name (the file contents are never shown)",
77
75
  },
78
- run() {
79
- return runWithJsonErrors(async () => {
80
- output("secret-list", { secrets: listSecretsRecursive() });
81
- });
76
+ async run() {
77
+ output("secret-list", { secrets: listSecretsRecursive() });
82
78
  },
83
79
  });
84
- const secretSetCommand = defineCommand({
80
+ const secretSetCommand = defineJsonCommand({
85
81
  meta: {
86
82
  name: "set",
87
83
  description: "Create or overwrite a secret. The value is read from stdin by default (never via argv). Use --from-file <path> to import an existing file byte-exact, or --from-env <VAR> to read from an environment variable. Multi-line values are allowed.",
@@ -99,49 +95,47 @@ const secretSetCommand = defineCommand({
99
95
  "from-file": { type: "string", description: "Read the value from this file (stored byte-exact)" },
100
96
  "from-env": { type: "string", description: "Read the value from the named environment variable" },
101
97
  },
102
- run({ args }) {
103
- return runWithJsonErrors(async () => {
104
- const { setSecret } = await import("./secret.js");
105
- const { name, absPath, source } = resolveSecretPath(args.ref, { subPath: getStringArg(args, "path") });
106
- const fromEnv = getHyphenatedArg(args, "from-env");
107
- const fromFile = getHyphenatedArg(args, "from-file");
108
- if (fromEnv !== undefined && fromFile !== undefined) {
109
- throw new UsageError("Pass only one of --from-file or --from-env (or use stdin).", "INVALID_FLAG_VALUE");
98
+ async run({ args }) {
99
+ const { setSecret } = await import("./secret.js");
100
+ const { name, absPath, source } = resolveSecretPath(args.ref, { subPath: getStringArg(args, "path") });
101
+ const fromEnv = args["from-env"];
102
+ const fromFile = args["from-file"];
103
+ if (fromEnv !== undefined && fromFile !== undefined) {
104
+ throw new UsageError("Pass only one of --from-file or --from-env (or use stdin).", "INVALID_FLAG_VALUE");
105
+ }
106
+ const MAX_SECRET_BYTES = 5 * 1024 * 1024; // 5 MB
107
+ let value;
108
+ if (fromFile !== undefined) {
109
+ if (!fs.existsSync(fromFile)) {
110
+ throw new NotFoundError(`File not found: ${fromFile}`, "FILE_NOT_FOUND");
110
111
  }
111
- const MAX_SECRET_BYTES = 5 * 1024 * 1024; // 5 MB
112
- let value;
113
- if (fromFile !== undefined) {
114
- if (!fs.existsSync(fromFile)) {
115
- throw new NotFoundError(`File not found: ${fromFile}`, "FILE_NOT_FOUND");
116
- }
117
- value = fs.readFileSync(fromFile);
118
- if (value.byteLength > MAX_SECRET_BYTES) {
119
- throw new UsageError("Secret exceeds the 5 MB limit.");
120
- }
112
+ value = fs.readFileSync(fromFile);
113
+ if (value.byteLength > MAX_SECRET_BYTES) {
114
+ throw new UsageError("Secret exceeds the 5 MB limit.");
121
115
  }
122
- else if (fromEnv !== undefined) {
123
- const envVal = process.env[fromEnv];
124
- if (envVal === undefined) {
125
- throw new UsageError(`Environment variable "${fromEnv}" is not set.`, "INVALID_FLAG_VALUE");
126
- }
127
- value = Buffer.from(envVal, "utf8");
116
+ }
117
+ else if (fromEnv !== undefined) {
118
+ const envVal = process.env[fromEnv];
119
+ if (envVal === undefined) {
120
+ throw new UsageError(`Environment variable "${fromEnv}" is not set.`, "INVALID_FLAG_VALUE");
128
121
  }
129
- else {
130
- if (process.stdin.isTTY) {
131
- process.stderr.write(`Enter value for secret "${name}" (Ctrl-D when done):\n`);
132
- }
133
- const stdinBuf = await readStdin(MAX_SECRET_BYTES, () => new UsageError("Secret exceeds the 5 MB limit."));
134
- // Strip a single trailing newline so `echo "$TOKEN" | akm secret set`
135
- // stores the token without the shell-added newline. Use --from-file for
136
- // byte-exact storage of multi-line material (PEM keys, certs).
137
- value = Buffer.from(stdinBuf.toString("utf8").replace(/\n$/, ""), "utf8");
122
+ value = Buffer.from(envVal, "utf8");
123
+ }
124
+ else {
125
+ if (process.stdin.isTTY) {
126
+ process.stderr.write(`Enter value for secret "${name}" (Ctrl-D when done):\n`);
138
127
  }
139
- setSecret(absPath, value);
140
- output("secret-set", { ref: makeSecretRef(name, source) });
141
- });
128
+ const stdinBuf = await readStdin(MAX_SECRET_BYTES, () => new UsageError("Secret exceeds the 5 MB limit."));
129
+ // Strip a single trailing newline so `echo "$TOKEN" | akm secret set`
130
+ // stores the token without the shell-added newline. Use --from-file for
131
+ // byte-exact storage of multi-line material (PEM keys, certs).
132
+ value = Buffer.from(stdinBuf.toString("utf8").replace(/\n$/, ""), "utf8");
133
+ }
134
+ setSecret(absPath, value);
135
+ output("secret-set", { ref: makeSecretRef(name, source) });
142
136
  },
143
137
  });
144
- const secretPathCommand = defineCommand({
138
+ const secretPathCommand = defineJsonCommand({
145
139
  meta: {
146
140
  name: "path",
147
141
  description: "Print the absolute secret file path for the Docker `_FILE` convention, e.g. `MY_SECRET_FILE=$(akm secret path secret:deploy-key)`.",
@@ -149,17 +143,15 @@ const secretPathCommand = defineCommand({
149
143
  args: {
150
144
  ref: { type: "positional", description: "Secret ref", required: true },
151
145
  },
152
- run({ args }) {
153
- return runWithJsonErrors(async () => {
154
- const { name, absPath, source } = resolveSecretPath(args.ref);
155
- if (!fs.existsSync(absPath)) {
156
- throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
157
- }
158
- process.stdout.write(`${absPath}\n`);
159
- });
146
+ async run({ args }) {
147
+ const { name, absPath, source } = resolveSecretPath(args.ref);
148
+ if (!fs.existsSync(absPath)) {
149
+ throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
150
+ }
151
+ process.stdout.write(`${absPath}\n`);
160
152
  },
161
153
  });
162
- const secretRunCommand = defineCommand({
154
+ const secretRunCommand = defineJsonCommand({
163
155
  meta: {
164
156
  name: "run",
165
157
  description: "Run a command with a secret's value injected into an env var: `akm secret run <ref> <VAR> -- <command>`. The value is set as $VAR in the child process only. Pass --clean to start the child with a minimal inherited environment instead of the full parent environment.",
@@ -177,82 +169,78 @@ const secretRunCommand = defineCommand({
177
169
  description: "When used with --clean, also inherit these parent env vars (comma-separated). Ignored without --clean.",
178
170
  },
179
171
  },
180
- run({ args }) {
181
- return runWithJsonErrors(async () => {
182
- // Validate the target env var name FIRST (before the command split) so a
183
- // dangerous/invalid name is rejected regardless of how the command is
184
- // supplied and so the failure does not depend on argv parsing.
185
- const varName = args.var;
186
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(varName)) {
187
- throw new UsageError(`"${varName}" is not a valid environment variable name.`, "INVALID_FLAG_VALUE");
188
- }
189
- const { isDangerousEnvKey } = await import("../lint/env-key-rules.js");
190
- if (isDangerousEnvKey(varName)) {
191
- throw new UsageError(`Refusing to inject a secret into "${varName}": it is a known process-hijacking variable (e.g. LD_PRELOAD, PATH).`, "INVALID_FLAG_VALUE");
192
- }
193
- const dashIndex = process.argv.indexOf("--");
194
- if (dashIndex < 0 || dashIndex === process.argv.length - 1) {
195
- throw new UsageError("Missing command. Usage: akm secret run <ref> <VAR> -- <command>");
196
- }
197
- const command = process.argv.slice(dashIndex + 1);
198
- const { name, absPath, source } = resolveSecretPath(args.ref);
199
- if (!fs.existsSync(absPath)) {
200
- throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
172
+ async run({ args }) {
173
+ // Validate the target env var name FIRST (before the command split) so a
174
+ // dangerous/invalid name is rejected regardless of how the command is
175
+ // supplied and so the failure does not depend on argv parsing.
176
+ const varName = args.var;
177
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(varName)) {
178
+ throw new UsageError(`"${varName}" is not a valid environment variable name.`, "INVALID_FLAG_VALUE");
179
+ }
180
+ const { isDangerousEnvKey } = await import("../lint/env-key-rules.js");
181
+ if (isDangerousEnvKey(varName)) {
182
+ throw new UsageError(`Refusing to inject a secret into "${varName}": it is a known process-hijacking variable (e.g. LD_PRELOAD, PATH).`, "INVALID_FLAG_VALUE");
183
+ }
184
+ const dashIndex = process.argv.indexOf("--");
185
+ if (dashIndex < 0 || dashIndex === process.argv.length - 1) {
186
+ throw new UsageError("Missing command. Usage: akm secret run <ref> <VAR> -- <command>");
187
+ }
188
+ const command = process.argv.slice(dashIndex + 1);
189
+ const { name, absPath, source } = resolveSecretPath(args.ref);
190
+ if (!fs.existsSync(absPath)) {
191
+ throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
192
+ }
193
+ const { readValue } = await import("./secret.js");
194
+ const mergedEnv = buildChildEnv(process.env, {
195
+ clean: args.clean === true,
196
+ inherit: parseKeyListFlag(args.inherit) ?? [],
197
+ });
198
+ mergedEnv[varName] = readValue(absPath).toString("utf8");
199
+ // Audit trail: record access by ref + var name only — never the value.
200
+ appendEvent({
201
+ eventType: "secret_access",
202
+ ref: makeSecretRef(name, source),
203
+ metadata: { var: varName },
204
+ });
205
+ const result = spawnSync(command[0], command.slice(1), {
206
+ stdio: "inherit",
207
+ env: mergedEnv,
208
+ });
209
+ if (result.error) {
210
+ const err = result.error;
211
+ if (err.code === "ENOENT") {
212
+ throw new NotFoundError(`Command not found: ${command[0]}`, "FILE_NOT_FOUND", `Install '${command[0]}' or add its directory to PATH before invoking 'akm secret run'.`);
201
213
  }
202
- const { readValue } = await import("./secret.js");
203
- const mergedEnv = buildChildEnv(process.env, {
204
- clean: getHyphenatedArg(args, "clean") === true,
205
- inherit: parseKeyListFlag(getHyphenatedArg(args, "inherit")) ?? [],
206
- });
207
- mergedEnv[varName] = readValue(absPath).toString("utf8");
208
- // Audit trail: record access by ref + var name only — never the value.
209
- appendEvent({
210
- eventType: "secret_access",
211
- ref: makeSecretRef(name, source),
212
- metadata: { var: varName },
213
- });
214
- const result = spawnSync(command[0], command.slice(1), {
215
- stdio: "inherit",
216
- env: mergedEnv,
217
- });
218
- if (result.error) {
219
- const err = result.error;
220
- if (err.code === "ENOENT") {
221
- throw new NotFoundError(`Command not found: ${command[0]}`, "FILE_NOT_FOUND", `Install '${command[0]}' or add its directory to PATH before invoking 'akm secret run'.`);
222
- }
223
- if (err.code === "EACCES") {
224
- throw new ConfigError(`Command not executable: ${command[0]}`, "STASH_DIR_UNREADABLE", `Add execute permission ('chmod +x ${command[0]}') or invoke via an interpreter.`);
225
- }
226
- throw err;
214
+ if (err.code === "EACCES") {
215
+ throw new ConfigError(`Command not executable: ${command[0]}`, "STASH_DIR_UNREADABLE", `Add execute permission ('chmod +x ${command[0]}') or invoke via an interpreter.`);
227
216
  }
228
- process.exit(result.status ?? 0);
229
- });
217
+ throw err;
218
+ }
219
+ process.exit(result.status ?? 0);
230
220
  },
231
221
  });
232
- const secretRemoveCommand = defineCommand({
222
+ const secretRemoveCommand = defineJsonCommand({
233
223
  meta: { name: "remove", description: "Remove a secret (and its .sensitive marker, if any)" },
234
224
  args: {
235
225
  ref: { type: "positional", description: "Secret ref", required: true },
236
226
  yes: { type: "boolean", alias: "y", description: "Skip confirmation prompt", default: false },
237
227
  },
238
- run({ args }) {
239
- return runWithJsonErrors(async () => {
240
- const { name, absPath, source } = resolveSecretPath(args.ref);
241
- const { confirmDestructive } = await import("../../cli/confirm.js");
242
- const confirmed = await confirmDestructive(`Remove secret "${args.ref}"? This cannot be undone.`, {
243
- yes: args.yes === true,
244
- });
245
- if (!confirmed) {
246
- process.stderr.write("Aborted.\n");
247
- return;
248
- }
249
- const { removeSecret } = await import("./secret.js");
250
- if (!fs.existsSync(absPath)) {
251
- throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
252
- }
253
- const removed = removeSecret(absPath);
254
- output("secret-remove", { ref: makeSecretRef(name, source), removed });
228
+ async run({ args }) {
229
+ const { name, absPath, source } = resolveSecretPath(args.ref);
230
+ const { confirmDestructive } = await import("../../cli/confirm.js");
231
+ const confirmed = await confirmDestructive(`Remove secret "${args.ref}"? This cannot be undone.`, {
232
+ yes: args.yes === true,
255
233
  });
234
+ if (!confirmed) {
235
+ process.stderr.write("Aborted.\n");
236
+ return;
237
+ }
238
+ const { removeSecret } = await import("./secret.js");
239
+ if (!fs.existsSync(absPath)) {
240
+ throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
241
+ }
242
+ const removed = removeSecret(absPath);
243
+ output("secret-remove", { ref: makeSecretRef(name, source), removed });
256
244
  },
257
245
  });
258
246
  export const secretCommand = defineGroupCommand({