akm-cli 0.9.0-beta.2 → 0.9.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +248 -0
- package/dist/assets/templates/html/default.html +78 -0
- package/dist/assets/templates/html/health.html +560 -0
- package/dist/assets/templates/html/vendor/echarts.min.js +45 -0
- package/dist/cli/shared.js +21 -5
- package/dist/cli.js +36 -5
- package/dist/commands/health/html-report.js +448 -0
- package/dist/commands/health.js +97 -6
- package/dist/commands/improve/consolidate.js +15 -2
- package/dist/commands/improve/extract.js +38 -2
- package/dist/commands/improve/improve-auto-accept.js +27 -1
- package/dist/commands/improve/improve.js +167 -53
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/improve/reflect.js +25 -0
- package/dist/commands/proposal/drain.js +73 -6
- package/dist/commands/proposal/proposal-cli.js +22 -10
- package/dist/commands/proposal/proposal.js +12 -1
- package/dist/commands/proposal/validators/proposals.js +361 -338
- package/dist/commands/remember.js +6 -2
- package/dist/core/config/config-schema.js +5 -0
- package/dist/core/logs-db.js +304 -0
- package/dist/core/state-db.js +107 -14
- package/dist/indexer/db/db.js +2 -2
- package/dist/indexer/passes/memory-inference.js +61 -22
- package/dist/integrations/harnesses/claude/session-log.js +16 -4
- package/dist/llm/client.js +15 -0
- package/dist/llm/usage-persist.js +77 -0
- package/dist/llm/usage-telemetry.js +103 -0
- package/dist/output/context.js +3 -2
- package/dist/output/html-render.js +73 -0
- package/dist/output/shapes/helpers.js +17 -1
- package/dist/output/text/helpers.js +69 -1
- package/dist/scripts/migrate-storage.js +65 -14
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +14 -2
- package/dist/tasks/runner.js +99 -16
- package/dist/workflows/db.js +4 -0
- package/package.json +2 -1
|
@@ -8631,7 +8631,7 @@ function openStateDatabase(dbPath) {
|
|
|
8631
8631
|
const db = openDatabase(resolvedPath);
|
|
8632
8632
|
db.exec("PRAGMA journal_mode = WAL");
|
|
8633
8633
|
db.exec("PRAGMA foreign_keys = ON");
|
|
8634
|
-
db.exec("PRAGMA busy_timeout =
|
|
8634
|
+
db.exec("PRAGMA busy_timeout = 30000");
|
|
8635
8635
|
runMigrations2(db);
|
|
8636
8636
|
return db;
|
|
8637
8637
|
}
|
|
@@ -8706,7 +8706,9 @@ var MIGRATIONS = [
|
|
|
8706
8706
|
--
|
|
8707
8707
|
-- Extensible (metadata_json) columns:
|
|
8708
8708
|
-- metadata_json TEXT \u2014 JSON object for future proposal fields.
|
|
8709
|
-
-- Current fields stored here: sourceRun,
|
|
8709
|
+
-- Current fields stored here: sourceRun,
|
|
8710
|
+
-- review, confidence, gateDecision (#577),
|
|
8711
|
+
-- backupContent.
|
|
8710
8712
|
--
|
|
8711
8713
|
-- ADD COLUMN extension points (future migrations):
|
|
8712
8714
|
-- ALTER TABLE proposals ADD COLUMN source_run TEXT DEFAULT NULL;
|
|
@@ -8893,6 +8895,16 @@ var MIGRATIONS = [
|
|
|
8893
8895
|
CREATE INDEX IF NOT EXISTS idx_extract_sessions_processed
|
|
8894
8896
|
ON extract_sessions_seen(processed_at);
|
|
8895
8897
|
`
|
|
8898
|
+
},
|
|
8899
|
+
{
|
|
8900
|
+
id: "005-proposal-fs-imports",
|
|
8901
|
+
up: `
|
|
8902
|
+
CREATE TABLE IF NOT EXISTS proposal_fs_imports (
|
|
8903
|
+
stash_dir TEXT PRIMARY KEY,
|
|
8904
|
+
imported_at TEXT NOT NULL,
|
|
8905
|
+
imported_count INTEGER NOT NULL DEFAULT 0
|
|
8906
|
+
);
|
|
8907
|
+
`
|
|
8896
8908
|
}
|
|
8897
8909
|
];
|
|
8898
8910
|
function runMigrations2(db) {
|
package/dist/tasks/runner.js
CHANGED
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
* 4. Dispatch by target kind:
|
|
17
17
|
* • workflow → `startWorkflowRun(ref, params)`
|
|
18
18
|
* • prompt → `runAgent(profile, prompt, { stdio: "captured" })`
|
|
19
|
-
* 5. Capture stdout / stderr
|
|
19
|
+
* 5. Capture stdout / stderr as structured rows in logs.db (task_logs) and,
|
|
20
|
+
* transitionally, as a flat text tail at `<cacheDir>/tasks/logs/<id>/<ts>.log`
|
|
21
|
+
* (see docs/technical/logs-audit.md).
|
|
20
22
|
* 6. Write a history row to state.db task_history table.
|
|
21
23
|
*
|
|
22
24
|
* Returns a structured result so the CLI handler can shape it for `output()`
|
|
@@ -29,6 +31,7 @@ import { parseAssetRef } from "../core/asset/asset-ref.js";
|
|
|
29
31
|
import { resolveStashDir } from "../core/common.js";
|
|
30
32
|
import { loadConfig } from "../core/config/config.js";
|
|
31
33
|
import { NotFoundError, rethrowIfTestIsolationError } from "../core/errors.js";
|
|
34
|
+
import { buildTaskRunId, insertTaskLogLines, openLogsDatabase, } from "../core/logs-db.js";
|
|
32
35
|
import { getTaskLogDir } from "../core/paths.js";
|
|
33
36
|
import { getTaskHistory, openStateDatabase, queryTaskHistory, upsertTaskHistory } from "../core/state-db.js";
|
|
34
37
|
import { error } from "../core/warn.js";
|
|
@@ -70,7 +73,15 @@ export async function runTask(id, options = {}) {
|
|
|
70
73
|
log: logPath,
|
|
71
74
|
target: disabledTarget,
|
|
72
75
|
};
|
|
73
|
-
|
|
76
|
+
const disabledLine = `[akm tasks] task "${id}" is disabled — skipping run.`;
|
|
77
|
+
persistRunLog({
|
|
78
|
+
taskId: id,
|
|
79
|
+
startedAtIso: startedIso,
|
|
80
|
+
finishedAtIso: result.finishedAt,
|
|
81
|
+
logPath,
|
|
82
|
+
fileText: `${disabledLine}\n`,
|
|
83
|
+
dbLines: [{ line: disabledLine }],
|
|
84
|
+
});
|
|
74
85
|
appendHistory(result);
|
|
75
86
|
return result;
|
|
76
87
|
}
|
|
@@ -108,7 +119,9 @@ async function runCommandTask(input) {
|
|
|
108
119
|
throw new Error("invariant: command target");
|
|
109
120
|
const { cmd } = task.target;
|
|
110
121
|
const timeoutMs = task.timeoutMs !== undefined ? task.timeoutMs : null;
|
|
111
|
-
const
|
|
122
|
+
const header = `[akm tasks] task=${task.id} kind=command cmd=${cmd.join(" ")}`;
|
|
123
|
+
const logLines = [header];
|
|
124
|
+
const dbLines = [{ line: header }];
|
|
112
125
|
let stdout = "";
|
|
113
126
|
let stderr = "";
|
|
114
127
|
let exitCode = null;
|
|
@@ -144,24 +157,36 @@ async function runCommandTask(input) {
|
|
|
144
157
|
exitCode = proc.exitCode ?? (timedOut ? 143 : 1);
|
|
145
158
|
if (timedOut) {
|
|
146
159
|
logLines.push(`timed_out=true timeout_ms=${timeoutMs}`);
|
|
160
|
+
dbLines.push({ level: "error", line: `timed_out=true timeout_ms=${timeoutMs}` });
|
|
147
161
|
}
|
|
148
162
|
logLines.push(`exit_code=${exitCode}`);
|
|
163
|
+
dbLines.push({ level: exitCode === 0 ? "info" : "error", line: `exit_code=${exitCode}` });
|
|
149
164
|
if (stdout) {
|
|
150
165
|
logLines.push("--- stdout ---");
|
|
151
166
|
logLines.push(stdout);
|
|
167
|
+
dbLines.push(...streamLines(stdout, "stdout", "info"));
|
|
152
168
|
}
|
|
153
169
|
if (stderr) {
|
|
154
170
|
logLines.push("--- stderr ---");
|
|
155
171
|
logLines.push(stderr);
|
|
172
|
+
dbLines.push(...streamLines(stderr, "stderr", "error"));
|
|
156
173
|
}
|
|
157
174
|
}
|
|
158
175
|
catch (e) {
|
|
159
176
|
const msg = e instanceof Error ? e.message : String(e);
|
|
160
177
|
logLines.push(`spawn_error=${msg}`);
|
|
178
|
+
dbLines.push({ level: "error", line: `spawn_error=${msg}` });
|
|
161
179
|
exitCode = 1;
|
|
162
180
|
}
|
|
163
|
-
fs.writeFileSync(logPath, `${logLines.join("\n")}\n`);
|
|
164
181
|
const finishedAt = now();
|
|
182
|
+
persistRunLog({
|
|
183
|
+
taskId: task.id,
|
|
184
|
+
startedAtIso: startedAt.toISOString(),
|
|
185
|
+
finishedAtIso: finishedAt.toISOString(),
|
|
186
|
+
logPath,
|
|
187
|
+
fileText: `${logLines.join("\n")}\n`,
|
|
188
|
+
dbLines,
|
|
189
|
+
});
|
|
165
190
|
const status = exitCode === 0 ? "completed" : "failed";
|
|
166
191
|
const result = {
|
|
167
192
|
id: task.id,
|
|
@@ -196,7 +221,14 @@ async function runWorkflowTask(input) {
|
|
|
196
221
|
const finishedAt = now();
|
|
197
222
|
const status = error ? "failed" : mapWorkflowStatus(detail?.run.status);
|
|
198
223
|
const log = renderWorkflowLog({ task, detail, error });
|
|
199
|
-
|
|
224
|
+
persistRunLog({
|
|
225
|
+
taskId: task.id,
|
|
226
|
+
startedAtIso: startedAt.toISOString(),
|
|
227
|
+
finishedAtIso: finishedAt.toISOString(),
|
|
228
|
+
logPath,
|
|
229
|
+
fileText: log.fileText,
|
|
230
|
+
dbLines: log.dbLines,
|
|
231
|
+
});
|
|
200
232
|
const result = {
|
|
201
233
|
id: task.id,
|
|
202
234
|
status,
|
|
@@ -248,16 +280,17 @@ function mapWorkflowStatus(status) {
|
|
|
248
280
|
}
|
|
249
281
|
}
|
|
250
282
|
function renderWorkflowLog(input) {
|
|
251
|
-
const
|
|
252
|
-
|
|
283
|
+
const dbLines = [
|
|
284
|
+
{ line: `[akm tasks] task=${input.task.id} kind=workflow ref=${input.task.target.ref}` },
|
|
285
|
+
];
|
|
253
286
|
if (input.detail) {
|
|
254
|
-
|
|
255
|
-
|
|
287
|
+
dbLines.push({ line: `run_id=${input.detail.run.id} status=${input.detail.run.status}` });
|
|
288
|
+
dbLines.push({ line: `workflow_title=${input.detail.run.workflowTitle}` });
|
|
256
289
|
}
|
|
257
290
|
if (input.error) {
|
|
258
|
-
|
|
291
|
+
dbLines.push({ level: "error", line: `error=${input.error.message}` });
|
|
259
292
|
}
|
|
260
|
-
return `${
|
|
293
|
+
return { fileText: `${dbLines.map((entry) => entry.line).join("\n")}\n`, dbLines };
|
|
261
294
|
}
|
|
262
295
|
// ── prompt target ───────────────────────────────────────────────────────────
|
|
263
296
|
async function runPromptTask(input) {
|
|
@@ -321,7 +354,14 @@ async function runPromptTask(input) {
|
|
|
321
354
|
});
|
|
322
355
|
const finishedAt = now();
|
|
323
356
|
const log = renderPromptLog({ task, profileName: profile.name, result });
|
|
324
|
-
|
|
357
|
+
persistRunLog({
|
|
358
|
+
taskId: task.id,
|
|
359
|
+
startedAtIso: startedAt.toISOString(),
|
|
360
|
+
finishedAtIso: finishedAt.toISOString(),
|
|
361
|
+
logPath,
|
|
362
|
+
fileText: log.fileText,
|
|
363
|
+
dbLines: log.dbLines,
|
|
364
|
+
});
|
|
325
365
|
const status = result.ok ? "completed" : "failed";
|
|
326
366
|
const out = {
|
|
327
367
|
id: task.id,
|
|
@@ -359,20 +399,63 @@ async function resolvePromptText(task, stashDir) {
|
|
|
359
399
|
}
|
|
360
400
|
function renderPromptLog(input) {
|
|
361
401
|
const lines = [];
|
|
362
|
-
|
|
363
|
-
|
|
402
|
+
const dbLines = [];
|
|
403
|
+
const header = `[akm tasks] task=${input.task.id} kind=prompt profile=${input.profileName}`;
|
|
404
|
+
const summary = `ok=${input.result.ok} exit_code=${input.result.exitCode ?? "null"} duration_ms=${input.result.durationMs}`;
|
|
405
|
+
lines.push(header, summary);
|
|
406
|
+
dbLines.push({ line: header }, { level: input.result.ok ? "info" : "error", line: summary });
|
|
364
407
|
if (!input.result.ok) {
|
|
365
|
-
|
|
408
|
+
const failure = `reason=${input.result.reason ?? ""} error=${input.result.error ?? ""}`;
|
|
409
|
+
lines.push(failure);
|
|
410
|
+
dbLines.push({ level: "error", line: failure });
|
|
366
411
|
}
|
|
367
412
|
if (input.result.stdout) {
|
|
368
413
|
lines.push("--- agent stdout ---");
|
|
369
414
|
lines.push(input.result.stdout);
|
|
415
|
+
dbLines.push(...streamLines(input.result.stdout, "stdout", "info"));
|
|
370
416
|
}
|
|
371
417
|
if (input.result.stderr) {
|
|
372
418
|
lines.push("--- agent stderr ---");
|
|
373
419
|
lines.push(input.result.stderr);
|
|
420
|
+
dbLines.push(...streamLines(input.result.stderr, "stderr", "error"));
|
|
421
|
+
}
|
|
422
|
+
return { fileText: `${lines.join("\n")}\n`, dbLines };
|
|
423
|
+
}
|
|
424
|
+
/** Split captured pipe output into per-line logs.db rows (blank lines dropped). */
|
|
425
|
+
function streamLines(text, stream, level) {
|
|
426
|
+
return text
|
|
427
|
+
.split("\n")
|
|
428
|
+
.filter((line) => line.length > 0)
|
|
429
|
+
.map((line) => ({ stream, level, line }));
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Persist a finished run's log: the flat text file (so `log_path` in
|
|
433
|
+
* task_history keeps resolving for humans and older consumers) plus
|
|
434
|
+
* structured rows in logs.db keyed by `buildTaskRunId(taskId, startedAt)`.
|
|
435
|
+
*
|
|
436
|
+
* The DB write is best-effort, mirroring {@link appendHistory}: an unwritable
|
|
437
|
+
* logs.db must never fail a task run.
|
|
438
|
+
*/
|
|
439
|
+
function persistRunLog(input) {
|
|
440
|
+
fs.writeFileSync(input.logPath, input.fileText);
|
|
441
|
+
try {
|
|
442
|
+
const db = openLogsDatabase();
|
|
443
|
+
try {
|
|
444
|
+
insertTaskLogLines(db, {
|
|
445
|
+
taskId: input.taskId,
|
|
446
|
+
runId: buildTaskRunId(input.taskId, input.startedAtIso),
|
|
447
|
+
ts: input.finishedAtIso,
|
|
448
|
+
lines: input.dbLines,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
finally {
|
|
452
|
+
db.close();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
catch (err) {
|
|
456
|
+
rethrowIfTestIsolationError(err);
|
|
457
|
+
error(`[akm] task log DB write failed: ${String(err)}`);
|
|
374
458
|
}
|
|
375
|
-
return `${lines.join("\n")}\n`;
|
|
376
459
|
}
|
|
377
460
|
// ── history ─────────────────────────────────────────────────────────────────
|
|
378
461
|
function appendHistory(result) {
|
package/dist/workflows/db.js
CHANGED
|
@@ -47,6 +47,10 @@ export function openWorkflowDatabase(dbPath = getWorkflowDbPath()) {
|
|
|
47
47
|
}
|
|
48
48
|
const db = openDatabase(dbPath);
|
|
49
49
|
db.exec("PRAGMA journal_mode = WAL");
|
|
50
|
+
// #589: 30 s busy timeout, matching index.db / state.db. Without it the
|
|
51
|
+
// default is 0 ms, so any concurrent writer fails immediately with
|
|
52
|
+
// SQLITE_BUSY.
|
|
53
|
+
db.exec("PRAGMA busy_timeout = 30000");
|
|
50
54
|
db.exec("PRAGMA foreign_keys = ON");
|
|
51
55
|
ensureBaseSchema(db);
|
|
52
56
|
runMigrations(db);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.9.0-beta.
|
|
3
|
+
"version": "0.9.0-beta.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "akm (Agent Knowledge Management) — A package manager for AI agent skills, commands, tools, and knowledge. Works with Claude Code, OpenCode, Cursor, and any AI coding assistant.",
|
|
6
6
|
"keywords": [
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"test:unit": "bun test --parallel=${TEST_PARALLEL:-12} --timeout=30000 ./tests --path-ignore-patterns=tests/integration",
|
|
60
60
|
"test:integration": "bun test --parallel=${TEST_PARALLEL:-12} --timeout=30000 ./tests/integration ./tests/commands ./tests/workflows",
|
|
61
61
|
"test:node-smoke": "bun scripts/node-smoke.ts",
|
|
62
|
+
"test:node-compat": "AKM_NODE_COMPAT_TESTS=1 bun test --timeout=120000 tests/integration/node-compat.test.ts",
|
|
62
63
|
"test:sharded": "bun test ./tests --shard=1/4 & bun test ./tests --shard=2/4 & bun test ./tests --shard=3/4 & bun test ./tests --shard=4/4 & wait",
|
|
63
64
|
"test:time": "bun scripts/test-timing-report.ts",
|
|
64
65
|
"lint:isolation": "bun scripts/lint-tests-isolation.ts",
|