akm-cli 0.7.5 → 0.8.0-rc.6
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/{.github/CHANGELOG.md → CHANGELOG.md} +113 -2
- package/README.md +20 -4
- package/SECURITY.md +93 -0
- package/dist/cli/config-migrate.js +144 -0
- package/dist/cli/config-validate.js +39 -0
- package/dist/cli/confirm.js +73 -0
- package/dist/cli/parse-args.js +133 -0
- package/dist/cli.js +1995 -551
- package/dist/commands/agent-dispatch.js +110 -0
- package/dist/commands/agent-support.js +68 -0
- package/dist/commands/completions.js +3 -0
- package/dist/commands/config-cli.js +130 -534
- package/dist/commands/consolidate.js +1531 -0
- package/dist/commands/curate.js +44 -3
- package/dist/commands/db-cli.js +23 -0
- package/dist/commands/distill-promotion-policy.js +660 -0
- package/dist/commands/distill.js +990 -75
- package/dist/commands/eval-cases.js +43 -0
- package/dist/commands/events.js +5 -23
- package/dist/commands/graph.js +477 -0
- package/dist/commands/health.js +400 -0
- package/dist/commands/help/help-accept.md +9 -0
- package/dist/commands/help/help-improve.md +77 -0
- package/dist/commands/help/help-proposals.md +15 -0
- package/dist/commands/help/help-propose.md +17 -0
- package/dist/commands/help/help-reject.md +8 -0
- package/dist/commands/history.js +54 -46
- package/dist/commands/improve-profiles.js +146 -0
- package/dist/commands/improve-result-file.js +103 -0
- package/dist/commands/improve.js +2175 -0
- package/dist/commands/info.js +5 -2
- package/dist/commands/init.js +50 -2
- package/dist/commands/installed-stashes.js +102 -139
- package/dist/commands/knowledge.js +136 -0
- package/dist/commands/lint/agent-linter.js +49 -0
- package/dist/commands/lint/base-linter.js +479 -0
- package/dist/commands/lint/command-linter.js +49 -0
- package/dist/commands/lint/default-linter.js +16 -0
- package/dist/commands/lint/index.js +183 -0
- package/dist/commands/lint/knowledge-linter.js +16 -0
- package/dist/commands/lint/markdown-insertion.js +343 -0
- package/dist/commands/lint/memory-linter.js +61 -0
- package/dist/commands/lint/registry.js +36 -0
- package/dist/commands/lint/skill-linter.js +45 -0
- package/dist/commands/lint/task-linter.js +50 -0
- package/dist/commands/lint/types.js +4 -0
- package/dist/commands/lint/vault-key-rules.js +139 -0
- package/dist/commands/lint/workflow-linter.js +56 -0
- package/dist/commands/lint.js +4 -0
- package/dist/commands/migration-help.js +5 -2
- package/dist/commands/proposal.js +66 -12
- package/dist/commands/propose.js +86 -31
- package/dist/commands/reflect.js +1119 -73
- package/dist/commands/registry-search.js +5 -2
- package/dist/commands/remember.js +69 -6
- package/dist/commands/schema-repair.js +203 -0
- package/dist/commands/search.js +115 -14
- package/dist/commands/self-update.js +3 -0
- package/dist/commands/show.js +144 -25
- package/dist/commands/source-add.js +17 -45
- package/dist/commands/source-clone.js +3 -0
- package/dist/commands/source-manage.js +14 -19
- package/dist/commands/tasks.js +438 -0
- package/dist/commands/url-checker.js +42 -0
- package/dist/commands/vault.js +130 -77
- package/dist/core/action-contributors.js +28 -0
- package/dist/core/asset-ref.js +7 -0
- package/dist/core/asset-registry.js +7 -16
- package/dist/core/asset-serialize.js +88 -0
- package/dist/core/asset-spec.js +22 -0
- package/dist/core/common.js +157 -0
- package/dist/core/concurrent.js +25 -0
- package/dist/core/config-io.js +347 -0
- package/dist/core/config-migration.js +625 -0
- package/dist/core/config-schema.js +501 -0
- package/dist/core/config-sources.js +108 -0
- package/dist/core/config-types.js +4 -0
- package/dist/core/config-walker.js +337 -0
- package/dist/core/config.js +327 -987
- package/dist/core/errors.js +40 -19
- package/dist/core/events.js +91 -138
- package/dist/core/file-lock.js +104 -0
- package/dist/core/frontmatter.js +3 -6
- package/dist/core/lesson-lint.js +3 -0
- package/dist/core/markdown.js +20 -0
- package/dist/core/memory-belief.js +62 -0
- package/dist/core/memory-contradiction-detect.js +274 -0
- package/dist/core/memory-improve.js +806 -0
- package/dist/core/parse.js +158 -0
- package/dist/core/paths.js +326 -14
- package/dist/core/proposal-quality-validators.js +364 -0
- package/dist/core/proposal-validators.js +69 -0
- package/dist/core/proposals.js +498 -42
- package/dist/core/state-db.js +927 -0
- package/dist/core/text-truncation.js +107 -0
- package/dist/core/time.js +54 -0
- package/dist/core/warn.js +62 -1
- package/dist/core/write-source.js +3 -0
- package/dist/indexer/db-backup.js +391 -0
- package/dist/indexer/db-search.js +152 -253
- package/dist/indexer/db.js +933 -103
- package/dist/indexer/ensure-index.js +64 -0
- package/dist/indexer/file-context.js +3 -0
- package/dist/indexer/graph-boost.js +376 -101
- package/dist/indexer/graph-db.js +391 -0
- package/dist/indexer/graph-dedup.js +95 -0
- package/dist/indexer/graph-extraction.js +550 -124
- package/dist/indexer/index-context.js +4 -0
- package/dist/indexer/indexer.js +506 -291
- package/dist/indexer/llm-cache.js +47 -0
- package/dist/indexer/manifest.js +3 -0
- package/dist/indexer/matchers.js +148 -160
- package/dist/indexer/memory-inference.js +99 -74
- package/dist/indexer/metadata-contributors.js +29 -0
- package/dist/indexer/metadata.js +255 -196
- package/dist/indexer/path-resolver.js +92 -0
- package/dist/indexer/project-context.js +192 -0
- package/dist/indexer/ranking-contributors.js +331 -0
- package/dist/indexer/ranking.js +81 -0
- package/dist/indexer/search-fields.js +5 -9
- package/dist/indexer/search-hit-enrichers.js +111 -0
- package/dist/indexer/search-source.js +44 -10
- package/dist/indexer/semantic-status.js +5 -16
- package/dist/indexer/staleness-detect.js +447 -0
- package/dist/indexer/usage-events.js +12 -9
- package/dist/indexer/walker.js +28 -0
- package/dist/integrations/agent/builders.js +135 -0
- package/dist/integrations/agent/config.js +122 -230
- package/dist/integrations/agent/detect.js +3 -0
- package/dist/integrations/agent/index.js +7 -13
- package/dist/integrations/agent/model-aliases.js +55 -0
- package/dist/integrations/agent/profiles.js +70 -5
- package/dist/integrations/agent/prompts.js +150 -74
- package/dist/integrations/agent/runner.js +151 -0
- package/dist/integrations/agent/sdk-runner.js +126 -0
- package/dist/integrations/agent/spawn.js +118 -23
- package/dist/integrations/github.js +3 -0
- package/dist/integrations/lockfile.js +32 -69
- package/dist/integrations/session-logs/index.js +68 -0
- package/dist/integrations/session-logs/providers/claude-code.js +59 -0
- package/dist/integrations/session-logs/providers/opencode.js +55 -0
- package/dist/integrations/session-logs/types.js +4 -0
- package/dist/llm/call-ai.js +62 -0
- package/dist/llm/client.js +72 -124
- package/dist/llm/embedder.js +3 -19
- package/dist/llm/embedders/cache.js +3 -7
- package/dist/llm/embedders/local.js +3 -0
- package/dist/llm/embedders/remote.js +20 -8
- package/dist/llm/embedders/types.js +3 -7
- package/dist/llm/feature-gate.js +89 -48
- package/dist/llm/graph-extract.js +676 -70
- package/dist/llm/index-passes.js +9 -23
- package/dist/llm/memory-infer.js +52 -71
- package/dist/llm/metadata-enhance.js +42 -29
- package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
- package/dist/output/cli-hints-full.md +281 -0
- package/dist/output/cli-hints-short.md +65 -0
- package/dist/output/cli-hints.js +5 -318
- package/dist/output/context.js +3 -0
- package/dist/output/renderers.js +223 -256
- package/dist/output/shapes.js +150 -105
- package/dist/output/text.js +318 -30
- package/dist/registry/build-index.js +3 -0
- package/dist/registry/create-provider-registry.js +3 -0
- package/dist/registry/factory.js +3 -0
- package/dist/registry/origin-resolve.js +3 -0
- package/dist/registry/providers/index.js +3 -0
- package/dist/registry/providers/skills-sh.js +70 -49
- package/dist/registry/providers/static-index.js +53 -48
- package/dist/registry/providers/types.js +3 -24
- package/dist/registry/resolve.js +11 -16
- package/dist/registry/types.js +3 -0
- package/dist/scripts/migrate-storage.js +17307 -0
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +8900 -0
- package/dist/scripts/migrations/v16-to-v17.js +141 -0
- package/dist/setup/detect.js +3 -0
- package/dist/setup/ripgrep-install.js +3 -0
- package/dist/setup/ripgrep-resolve.js +3 -0
- package/dist/setup/setup.js +775 -37
- package/dist/setup/steps.js +3 -15
- package/dist/sources/include.js +3 -0
- package/dist/sources/provider-factory.js +5 -12
- package/dist/sources/provider.js +3 -20
- package/dist/sources/providers/filesystem.js +19 -23
- package/dist/sources/providers/git.js +7 -5
- package/dist/sources/providers/index.js +3 -0
- package/dist/sources/providers/install-types.js +3 -13
- package/dist/sources/providers/npm.js +3 -4
- package/dist/sources/providers/provider-utils.js +3 -0
- package/dist/sources/providers/sync-from-ref.js +3 -11
- package/dist/sources/providers/tar-utils.js +3 -0
- package/dist/sources/providers/website.js +18 -22
- package/dist/sources/resolve.js +3 -0
- package/dist/sources/types.js +3 -0
- package/dist/sources/website-ingest.js +7 -0
- package/dist/tasks/backends/cron.js +203 -0
- package/dist/tasks/backends/exec-utils.js +28 -0
- package/dist/tasks/backends/index.js +24 -0
- package/dist/tasks/backends/launchd-template.xml +19 -0
- package/dist/tasks/backends/launchd.js +187 -0
- package/dist/tasks/backends/schtasks-template.xml +29 -0
- package/dist/tasks/backends/schtasks.js +215 -0
- package/dist/tasks/parser.js +211 -0
- package/dist/tasks/resolveAkmBin.js +87 -0
- package/dist/tasks/runner.js +458 -0
- package/dist/tasks/schedule.js +211 -0
- package/dist/tasks/schema.js +15 -0
- package/dist/tasks/validator.js +62 -0
- package/dist/version.js +3 -0
- package/dist/wiki/index-template.md +12 -0
- package/dist/wiki/ingest-workflow-template.md +54 -0
- package/dist/wiki/log-template.md +8 -0
- package/dist/wiki/schema-template.md +61 -0
- package/dist/wiki/wiki-templates.js +15 -0
- package/dist/wiki/wiki.js +13 -61
- package/dist/workflows/authoring.js +8 -25
- package/dist/workflows/cli.js +3 -0
- package/dist/workflows/db.js +140 -10
- package/dist/workflows/document-cache.js +3 -10
- package/dist/workflows/parser.js +3 -0
- package/dist/workflows/renderer.js +11 -3
- package/dist/workflows/runs.js +62 -91
- package/dist/workflows/schema.js +3 -0
- package/dist/workflows/scope-key.js +3 -0
- package/dist/workflows/validator.js +4 -8
- package/dist/workflows/workflow-template.md +24 -0
- package/docs/README.md +9 -2
- package/docs/data-and-telemetry.md +225 -0
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.7.5.md +2 -2
- package/docs/migration/release-notes/0.8.0.md +48 -0
- package/docs/migration/v0.7-to-v0.8.md +1307 -0
- package/package.json +20 -8
- package/.github/LICENSE +0 -374
- package/dist/commands/install-audit.js +0 -381
- package/dist/templates/wiki-templates.js +0 -100
package/dist/commands/history.js
CHANGED
|
@@ -1,53 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* Event sources:
|
|
6
|
-
* - `usage_events` SQLite table: search, show, and feedback events recorded
|
|
7
|
-
* by the local indexer during normal CLI use.
|
|
8
|
-
* - `events.jsonl` append-only stream (opt-in via `--include-proposals`):
|
|
9
|
-
* proposal lifecycle events (`promoted`, `rejected`) emitted by
|
|
10
|
-
* `akm proposal accept` / `akm proposal reject`. Use this flag to see
|
|
11
|
-
* the full proposal review trail alongside usage events.
|
|
12
|
-
*
|
|
13
|
-
* The two sources are merged and sorted chronologically (oldest first) so
|
|
14
|
-
* consumers see a coherent lifecycle trail in a single output.
|
|
15
|
-
*/
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
16
4
|
import { parseAssetRef } from "../core/asset-ref";
|
|
17
5
|
import { UsageError } from "../core/errors";
|
|
18
6
|
import { readEvents } from "../core/events";
|
|
7
|
+
import { listProposals } from "../core/proposals";
|
|
8
|
+
import { isoToSqlite, parseSinceToIso } from "../core/time";
|
|
19
9
|
import { closeDatabase, openExistingDatabase } from "../indexer/db";
|
|
20
10
|
// Proposal lifecycle event types emitted by the proposal substrate (#225).
|
|
21
11
|
const PROPOSAL_EVENT_TYPES = new Set(["promoted", "rejected"]);
|
|
22
12
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
23
|
-
function normalizeSince(since) {
|
|
24
|
-
// Accept "YYYY-MM-DD", "YYYY-MM-DDTHH:MM:SSZ", epoch ms, or anything Date can parse.
|
|
25
|
-
const trimmed = since.trim();
|
|
26
|
-
if (!trimmed) {
|
|
27
|
-
throw new UsageError("--since cannot be empty.", "INVALID_FLAG_VALUE");
|
|
28
|
-
}
|
|
29
|
-
// Pure-digit input → epoch milliseconds
|
|
30
|
-
if (/^\d+$/.test(trimmed)) {
|
|
31
|
-
const ms = Number.parseInt(trimmed, 10);
|
|
32
|
-
const d = new Date(ms);
|
|
33
|
-
if (Number.isNaN(d.getTime())) {
|
|
34
|
-
throw new UsageError(`Invalid --since value: ${since}`, "INVALID_FLAG_VALUE");
|
|
35
|
-
}
|
|
36
|
-
return d
|
|
37
|
-
.toISOString()
|
|
38
|
-
.replace("T", " ")
|
|
39
|
-
.replace(/\.\d+Z$/, "");
|
|
40
|
-
}
|
|
41
|
-
const parsed = new Date(trimmed);
|
|
42
|
-
if (Number.isNaN(parsed.getTime())) {
|
|
43
|
-
throw new UsageError(`Invalid --since value: ${since}. Expected ISO timestamp (e.g. 2026-04-01T00:00:00Z) or epoch ms.`, "INVALID_FLAG_VALUE");
|
|
44
|
-
}
|
|
45
|
-
// Match the "YYYY-MM-DD HH:MM:SS" format SQLite's datetime('now') stores.
|
|
46
|
-
return parsed
|
|
47
|
-
.toISOString()
|
|
48
|
-
.replace("T", " ")
|
|
49
|
-
.replace(/\.\d+Z$/, "");
|
|
50
|
-
}
|
|
51
13
|
function parseMetadata(raw) {
|
|
52
14
|
if (!raw)
|
|
53
15
|
return null;
|
|
@@ -66,6 +28,7 @@ function toEntry(row) {
|
|
|
66
28
|
entryId: row.entry_id,
|
|
67
29
|
query: row.query,
|
|
68
30
|
signal: row.signal,
|
|
31
|
+
source: row.source,
|
|
69
32
|
metadata: parseMetadata(row.metadata),
|
|
70
33
|
createdAt: row.created_at,
|
|
71
34
|
};
|
|
@@ -104,7 +67,7 @@ export async function akmHistory(options = {}) {
|
|
|
104
67
|
parseAssetRef(trimmed);
|
|
105
68
|
normalizedRef = trimmed;
|
|
106
69
|
}
|
|
107
|
-
const sinceNormalized = options.since !== undefined ?
|
|
70
|
+
const sinceNormalized = options.since !== undefined ? isoToSqlite(parseSinceToIso(options.since)) : undefined;
|
|
108
71
|
const db = options.db ?? openExistingDatabase();
|
|
109
72
|
const ownsDb = options.db === undefined;
|
|
110
73
|
try {
|
|
@@ -118,8 +81,12 @@ export async function akmHistory(options = {}) {
|
|
|
118
81
|
conditions.push("created_at >= ?");
|
|
119
82
|
params.push(sinceNormalized);
|
|
120
83
|
}
|
|
84
|
+
if (options.source !== undefined) {
|
|
85
|
+
conditions.push("source = ?");
|
|
86
|
+
params.push(options.source);
|
|
87
|
+
}
|
|
121
88
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
122
|
-
const sql = `SELECT id, event_type, query, entry_id, entry_ref, signal, metadata, created_at
|
|
89
|
+
const sql = `SELECT id, event_type, query, entry_id, entry_ref, signal, metadata, source, created_at
|
|
123
90
|
FROM usage_events ${where}
|
|
124
91
|
ORDER BY id ASC`;
|
|
125
92
|
const rows = db.prepare(sql).all(...params);
|
|
@@ -128,7 +95,7 @@ export async function akmHistory(options = {}) {
|
|
|
128
95
|
const sources = ["usage_events"];
|
|
129
96
|
const proposalEntries = [];
|
|
130
97
|
if (options.includeProposals === true) {
|
|
131
|
-
sources.push("
|
|
98
|
+
sources.push("state.db");
|
|
132
99
|
// Convert sinceNormalized ("YYYY-MM-DD HH:MM:SS") to ISO for readEvents
|
|
133
100
|
// which uses `ts >= since` where `ts` is ISO-8601.
|
|
134
101
|
const sinceIso = sinceNormalized !== undefined ? `${sinceNormalized.replace(" ", "T")}Z` : undefined;
|
|
@@ -154,6 +121,7 @@ export async function akmHistory(options = {}) {
|
|
|
154
121
|
entryId: null,
|
|
155
122
|
query: null,
|
|
156
123
|
signal: null,
|
|
124
|
+
source: null,
|
|
157
125
|
metadata: event.metadata ?? null,
|
|
158
126
|
createdAt,
|
|
159
127
|
});
|
|
@@ -169,6 +137,45 @@ export async function akmHistory(options = {}) {
|
|
|
169
137
|
return 1;
|
|
170
138
|
return a.id - b.id;
|
|
171
139
|
});
|
|
140
|
+
// ── Accept-rate-per-source (F-4 / #385) ─────────────────────────────────
|
|
141
|
+
let acceptRateBySource;
|
|
142
|
+
if (options.acceptRateBySource) {
|
|
143
|
+
const stashDir = options.stashDir;
|
|
144
|
+
if (stashDir) {
|
|
145
|
+
const bySource = new Map();
|
|
146
|
+
const countProposals = (statuses, includeArchive) => {
|
|
147
|
+
for (const status of statuses) {
|
|
148
|
+
const proposals = listProposals(stashDir, { status, includeArchive });
|
|
149
|
+
for (const p of proposals) {
|
|
150
|
+
const src = p.source || "(unknown)";
|
|
151
|
+
const entry = bySource.get(src) ?? { accepted: 0, rejected: 0, pending: 0 };
|
|
152
|
+
if (status === "accepted")
|
|
153
|
+
entry.accepted++;
|
|
154
|
+
else if (status === "rejected")
|
|
155
|
+
entry.rejected++;
|
|
156
|
+
else
|
|
157
|
+
entry.pending++;
|
|
158
|
+
bySource.set(src, entry);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
countProposals(["pending"], false);
|
|
163
|
+
countProposals(["accepted", "rejected"], true);
|
|
164
|
+
acceptRateBySource = Array.from(bySource.entries())
|
|
165
|
+
.map(([source, counts]) => {
|
|
166
|
+
const decided = counts.accepted + counts.rejected;
|
|
167
|
+
return {
|
|
168
|
+
source,
|
|
169
|
+
total: decided + counts.pending,
|
|
170
|
+
accepted: counts.accepted,
|
|
171
|
+
rejected: counts.rejected,
|
|
172
|
+
pending: counts.pending,
|
|
173
|
+
acceptRate: decided > 0 ? counts.accepted / decided : null,
|
|
174
|
+
};
|
|
175
|
+
})
|
|
176
|
+
.sort((a, b) => b.total - a.total); // Most active source first
|
|
177
|
+
}
|
|
178
|
+
}
|
|
172
179
|
const response = {
|
|
173
180
|
schemaVersion: 1,
|
|
174
181
|
...(normalizedRef !== undefined ? { ref: normalizedRef } : {}),
|
|
@@ -176,6 +183,7 @@ export async function akmHistory(options = {}) {
|
|
|
176
183
|
totalCount: entries.length,
|
|
177
184
|
entries,
|
|
178
185
|
sources,
|
|
186
|
+
...(acceptRateBySource !== undefined ? { acceptRateBySource } : {}),
|
|
179
187
|
};
|
|
180
188
|
return response;
|
|
181
189
|
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import { parseAssetRef } from "../core/asset-ref";
|
|
5
|
+
import { warn } from "../core/warn";
|
|
6
|
+
/** Profile name used as the final fallback when nothing else resolves. */
|
|
7
|
+
const FALLBACK_PROFILE_NAME = "default";
|
|
8
|
+
// Built-in default allowed types per process
|
|
9
|
+
export const DEFAULT_ALLOWED_TYPES = {
|
|
10
|
+
reflect: ["agent", "command", "knowledge", "lesson", "memory", "skill", "wiki", "workflow"],
|
|
11
|
+
distill: ["memory"],
|
|
12
|
+
consolidate: ["memory"],
|
|
13
|
+
};
|
|
14
|
+
const BUILTIN_PROFILES = {
|
|
15
|
+
default: {
|
|
16
|
+
description: "Standard improve pass — all sub-processes, markdown asset types.",
|
|
17
|
+
processes: {
|
|
18
|
+
reflect: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.reflect },
|
|
19
|
+
distill: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.distill },
|
|
20
|
+
consolidate: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.consolidate },
|
|
21
|
+
memoryInference: { enabled: true },
|
|
22
|
+
graphExtraction: { enabled: true },
|
|
23
|
+
feedbackDistillation: { enabled: true },
|
|
24
|
+
// validation: deliberately undefined — third-tier classifier is opt-in.
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
quick: {
|
|
28
|
+
description: "Reflect-only pass — no distill, consolidate, memoryInference, or graphExtraction.",
|
|
29
|
+
processes: {
|
|
30
|
+
reflect: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.reflect },
|
|
31
|
+
distill: { enabled: false },
|
|
32
|
+
consolidate: { enabled: false },
|
|
33
|
+
memoryInference: { enabled: false },
|
|
34
|
+
graphExtraction: { enabled: false },
|
|
35
|
+
feedbackDistillation: { enabled: false },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
thorough: {
|
|
39
|
+
// Reserved for future divergence; for now behaviorally identical to
|
|
40
|
+
// `default`. Documented here so callers picking `--profile thorough` do
|
|
41
|
+
// not expect a different code path until we wire stricter limits in.
|
|
42
|
+
description: "All sub-processes enabled (currently identical to default; reserved for future divergence).",
|
|
43
|
+
processes: {
|
|
44
|
+
reflect: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.reflect },
|
|
45
|
+
distill: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.distill },
|
|
46
|
+
consolidate: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.consolidate },
|
|
47
|
+
memoryInference: { enabled: true },
|
|
48
|
+
graphExtraction: { enabled: true },
|
|
49
|
+
feedbackDistillation: { enabled: true },
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
"memory-focus": {
|
|
53
|
+
description: "Memory and lesson improvement only — no distill or consolidate.",
|
|
54
|
+
processes: {
|
|
55
|
+
reflect: { enabled: true, allowedTypes: ["memory", "lesson"] },
|
|
56
|
+
distill: { enabled: false },
|
|
57
|
+
consolidate: { enabled: false },
|
|
58
|
+
memoryInference: { enabled: true },
|
|
59
|
+
graphExtraction: { enabled: false },
|
|
60
|
+
feedbackDistillation: { enabled: false },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Default enabled-state for known improve processes when neither the user
|
|
66
|
+
* profile nor the built-in default profile specifies an override.
|
|
67
|
+
*
|
|
68
|
+
* These mirror the legacy `LlmFeatureFlags` defaults so callers that bypass
|
|
69
|
+
* the profile system (rare — most run through `resolveImproveProfile`) get
|
|
70
|
+
* the same answer.
|
|
71
|
+
*/
|
|
72
|
+
const IMPROVE_PROCESS_DEFAULTS = {
|
|
73
|
+
reflect: true,
|
|
74
|
+
distill: true,
|
|
75
|
+
consolidate: true,
|
|
76
|
+
memoryInference: true,
|
|
77
|
+
graphExtraction: true,
|
|
78
|
+
feedbackDistillation: true,
|
|
79
|
+
validation: false,
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Compute the effective enabled-state for a named improve process.
|
|
83
|
+
*
|
|
84
|
+
* Resolution order: explicit `profile.processes.<name>.enabled` (boolean) →
|
|
85
|
+
* the built-in {@link IMPROVE_PROCESS_DEFAULTS} fallback → `false`.
|
|
86
|
+
*/
|
|
87
|
+
export function resolveProcessEnabled(processName, profile) {
|
|
88
|
+
const processes = profile.processes;
|
|
89
|
+
const entry = processes?.[processName];
|
|
90
|
+
if (entry && typeof entry.enabled === "boolean")
|
|
91
|
+
return entry.enabled;
|
|
92
|
+
return IMPROVE_PROCESS_DEFAULTS[processName] ?? false;
|
|
93
|
+
}
|
|
94
|
+
function deepMerge(base, override) {
|
|
95
|
+
if (typeof base !== "object" || base === null)
|
|
96
|
+
return override ?? base;
|
|
97
|
+
const result = { ...base };
|
|
98
|
+
for (const key of Object.keys(override)) {
|
|
99
|
+
const ov = override[key];
|
|
100
|
+
// Treat `null` the same as `undefined` so user overrides never wipe a
|
|
101
|
+
// built-in field with `null`. The on-disk parser already strips nulls,
|
|
102
|
+
// but the programmatic API exposes this path and callers occasionally
|
|
103
|
+
// pass JSON-shaped objects with explicit nulls.
|
|
104
|
+
if (ov !== undefined && ov !== null) {
|
|
105
|
+
const bv = base[key];
|
|
106
|
+
if (typeof bv === "object" && bv !== null && typeof ov === "object" && ov !== null && !Array.isArray(bv)) {
|
|
107
|
+
result[key] = deepMerge(bv, ov);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
result[key] = ov;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
export function resolveImproveProfile(name, config) {
|
|
117
|
+
const requestedName = name ??
|
|
118
|
+
(typeof config.defaults?.improve === "string" ? config.defaults.improve : undefined) ??
|
|
119
|
+
FALLBACK_PROFILE_NAME;
|
|
120
|
+
const hasBuiltin = requestedName in BUILTIN_PROFILES;
|
|
121
|
+
const hasUserDefined = !!config.profiles?.improve?.[requestedName];
|
|
122
|
+
let effectiveName = requestedName;
|
|
123
|
+
if (!hasBuiltin && !hasUserDefined && requestedName !== FALLBACK_PROFILE_NAME) {
|
|
124
|
+
warn(`[akm] Improve profile "${requestedName}" not found in built-ins or config. ` +
|
|
125
|
+
`Falling back to "${FALLBACK_PROFILE_NAME}".`);
|
|
126
|
+
effectiveName = FALLBACK_PROFILE_NAME;
|
|
127
|
+
}
|
|
128
|
+
const builtin = BUILTIN_PROFILES[effectiveName] ?? BUILTIN_PROFILES[FALLBACK_PROFILE_NAME];
|
|
129
|
+
const userOverride = config.profiles?.improve?.[effectiveName] ?? {};
|
|
130
|
+
return deepMerge(builtin, userOverride);
|
|
131
|
+
}
|
|
132
|
+
export function shouldSkipRef(ref, processName, profile) {
|
|
133
|
+
const cfg = profile.processes?.[processName];
|
|
134
|
+
// Check if the process itself is disabled
|
|
135
|
+
if (cfg?.enabled === false)
|
|
136
|
+
return { skip: true, reason: "process-disabled" };
|
|
137
|
+
const parsed = parseAssetRef(ref);
|
|
138
|
+
const allowed = cfg?.allowedTypes ?? DEFAULT_ALLOWED_TYPES[processName];
|
|
139
|
+
if (!allowed.includes(parsed.type))
|
|
140
|
+
return { skip: true, reason: "type-filter" };
|
|
141
|
+
// Hardcoded: wiki raw directories are never processed by any improve process.
|
|
142
|
+
if (parsed.type === "wiki" && parsed.name.split("/")[1] === "raw") {
|
|
143
|
+
return { skip: true, reason: "raw-wiki" };
|
|
144
|
+
}
|
|
145
|
+
return { skip: false, reason: "" };
|
|
146
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Helpers for persisting the `akm improve` result envelope.
|
|
6
|
+
*
|
|
7
|
+
* v0.8.0 behavioural default change:
|
|
8
|
+
* - Default: the full result is recorded as a single row in the
|
|
9
|
+
* `improve_runs` table of `state.db` (migration 003). Stdout is empty.
|
|
10
|
+
* The existing `[improve] ...` progress log lines on stderr remain the
|
|
11
|
+
* canonical console UX.
|
|
12
|
+
* - `--json-to-stdout` restores the prior behaviour: full JSON to stdout,
|
|
13
|
+
* nothing written to state.db.
|
|
14
|
+
*
|
|
15
|
+
* v0.8.0 storage change (this module): the previous on-disk artifact at
|
|
16
|
+
* `<stash>/.akm/runs/<runId>/improve-result.json` is no longer written. The
|
|
17
|
+
* canonical record now lives in `improve_runs` (see
|
|
18
|
+
* `src/core/state-db.ts`). Pre-existing files from older runs are not
|
|
19
|
+
* deleted by this change — they become historical artifacts. Zero current
|
|
20
|
+
* code paths read them, so no consumers needed to update.
|
|
21
|
+
*
|
|
22
|
+
* Run-id format: ISO-8601 timestamp (colons/dots replaced by `-`) plus an
|
|
23
|
+
* 8-char hex random suffix. There is no existing canonical run-id helper for
|
|
24
|
+
* persistent per-command artefacts on disk — the `workflow_runs` table uses
|
|
25
|
+
* `randomUUID()` but is database-scoped, and `consolidate-journal.json` is a
|
|
26
|
+
* single-slot artefact. We mint a fresh timestamped id for each improve run.
|
|
27
|
+
*/
|
|
28
|
+
import crypto from "node:crypto";
|
|
29
|
+
import path from "node:path";
|
|
30
|
+
import { openStateDatabase, recordImproveRun } from "../core/state-db";
|
|
31
|
+
/**
|
|
32
|
+
* Build a stable run-id for a single improve invocation.
|
|
33
|
+
*
|
|
34
|
+
* Shape: `<iso-8601-utc-with-dashes>-<8 hex chars>`, e.g.
|
|
35
|
+
* `2026-05-19T17-30-22-123Z-a1b2c3d4`.
|
|
36
|
+
*
|
|
37
|
+
* The hex suffix protects against same-millisecond collisions when multiple
|
|
38
|
+
* runs happen back-to-back in tests or scripts.
|
|
39
|
+
*/
|
|
40
|
+
export function buildImproveRunId(now = new Date()) {
|
|
41
|
+
const iso = now.toISOString().replace(/[:.]/g, "-");
|
|
42
|
+
const rand = crypto.randomBytes(4).toString("hex");
|
|
43
|
+
return `${iso}-${rand}`;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Return a stable, human-recognisable reference for a given improve run.
|
|
47
|
+
*
|
|
48
|
+
* Historical compatibility shim: callers used to receive a stash-relative
|
|
49
|
+
* file path like `.akm/runs/<runId>/improve-result.json`. With the state.db
|
|
50
|
+
* migration, no such file exists, but several callers still log "wrote to
|
|
51
|
+
* <path>" style messages. Returning a `state.db//improve_runs/<runId>`
|
|
52
|
+
* locator preserves the "the result is at <thing>" signature so existing
|
|
53
|
+
* log lines and error messages continue to make sense without rewriting
|
|
54
|
+
* every call site.
|
|
55
|
+
*/
|
|
56
|
+
export function relativeImproveResultPath(runId) {
|
|
57
|
+
return path.join("state.db", "improve_runs", runId);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Persist the full improve result into the `improve_runs` table of state.db.
|
|
61
|
+
*
|
|
62
|
+
* Backwards-compatible signature: the function name, argument list, and
|
|
63
|
+
* return type all match the pre-0.8.0 file-writing helper. The returned
|
|
64
|
+
* string is the `state.db//improve_runs/<runId>` locator (see
|
|
65
|
+
* {@link relativeImproveResultPath}), which is intended for log messages
|
|
66
|
+
* only — no caller should treat it as a filesystem path. Zero current
|
|
67
|
+
* readers existed for the previous file path, so this is a pure storage
|
|
68
|
+
* swap.
|
|
69
|
+
*
|
|
70
|
+
* The state.db row carries the scope and dry-run flag from `result.scope`
|
|
71
|
+
* and `result.dryRun`, plus the full result JSON for full fidelity. The
|
|
72
|
+
* dry-run column is indexed so productivity audits can filter cleanly
|
|
73
|
+
* (closes the dry-run/real-run artifact-trap recorded in MEMORY.md
|
|
74
|
+
* `feedback_akm_dryrun_artifact_trap`).
|
|
75
|
+
*/
|
|
76
|
+
export function writeImproveResultFile(stashDir, runId, result) {
|
|
77
|
+
const db = openStateDatabase();
|
|
78
|
+
try {
|
|
79
|
+
const startedAt = new Date().toISOString();
|
|
80
|
+
recordImproveRun(db, {
|
|
81
|
+
id: runId,
|
|
82
|
+
startedAt,
|
|
83
|
+
completedAt: startedAt,
|
|
84
|
+
stashDir,
|
|
85
|
+
dryRun: Boolean(result.dryRun),
|
|
86
|
+
profile: null,
|
|
87
|
+
scopeMode: result.scope?.mode ?? "all",
|
|
88
|
+
scopeValue: result.scope?.value ?? null,
|
|
89
|
+
guidance: result.guidance ?? null,
|
|
90
|
+
ok: Boolean(result.ok),
|
|
91
|
+
result,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
try {
|
|
96
|
+
db.close();
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// best-effort
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return relativeImproveResultPath(runId);
|
|
103
|
+
}
|