akm-cli 0.9.8 → 0.9.9-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/dist/commands/health/checks.js +40 -0
- package/dist/commands/health.js +57 -31
- package/dist/commands/migrate-cli.js +29 -189
- package/dist/commands/sources/add-cli.js +7 -0
- package/dist/commands/sources/installed-stashes.js +36 -8
- package/dist/commands/sources/self-update.js +104 -62
- package/dist/commands/sources/source-add.js +6 -5
- package/dist/commands/sources/sources-cli.js +7 -18
- package/dist/commands/tasks/tasks-cli.js +4 -3
- package/dist/commands/tasks/tasks.js +13 -6
- package/dist/core/adapter/adapter-ids.js +35 -0
- package/dist/core/adapter/adapters/index.js +29 -0
- package/dist/core/adapter/detect-adapter.js +91 -3
- package/dist/core/config/config.js +1 -1
- package/dist/core/config/schema/sources-bundles.js +23 -0
- package/dist/core/extra-params.js +1 -1
- package/dist/core/state/migrations.js +2 -4
- package/dist/core/state-db.js +31 -8
- package/dist/indexer/indexer.js +64 -1
- package/dist/scripts/akm-migrate-node.js +86534 -20434
- package/dist/scripts/akm-migrate.js +86415 -20280
- package/dist/tasks/backends/cron.js +21 -6
- package/dist/tasks/resolve-akm-bin.js +1 -1
- package/docs/README.md +1 -0
- package/docs/integration/bundling-akm.md +276 -0
- package/docs/migration/v0.9.0-troubleshooting.md +10 -14
- package/docs/migration/v0.9.1-to-v0.9.2.md +12 -16
- package/docs/reference/cli.md +59 -31
- package/docs/reference/tasks.md +4 -10
- package/package.json +2 -1
- package/dist/commands/migrate/config-extra-params.js +0 -61
- package/dist/commands/migrate/dead-residue.js +0 -113
- package/dist/commands/migrate/stale-txn.js +0 -49
|
@@ -151,7 +151,7 @@ export function parseAndValidateConfigText(text, sourcePath) {
|
|
|
151
151
|
// rewritten onto the first-class engine field they now shadow. This used
|
|
152
152
|
// to happen silently, in memory, on every load; that ran forever and never
|
|
153
153
|
// converged. The lift itself is now `akm migrate apply`'s job (see
|
|
154
|
-
//
|
|
154
|
+
// scripts/akm-migrate/migrate/config-extra-params.ts) and persists to disk, so a
|
|
155
155
|
// config that has not been migrated yet fails closed here instead of
|
|
156
156
|
// silently drifting from what's on disk.
|
|
157
157
|
const where = sourcePath ? ` at ${sourcePath}` : "";
|
|
@@ -7,9 +7,15 @@
|
|
|
7
7
|
* change.
|
|
8
8
|
*/
|
|
9
9
|
import { z } from "zod";
|
|
10
|
+
// Dependency-free mirror of the adapter registry's id list (#909) — see
|
|
11
|
+
// adapter-ids.ts's header for why config imports this leaf rather than
|
|
12
|
+
// `core/adapter/registry.ts` (which would pull in all 11 concrete adapters
|
|
13
|
+
// and, transitively, the indexer modules they delegate to).
|
|
14
|
+
import { VALID_ADAPTER_IDS } from "../../adapter/adapter-ids.js";
|
|
10
15
|
import { isBundleSlug } from "../../asset/asset-ref.js";
|
|
11
16
|
import { hasRegistryUrlCredentials, REGISTRY_CREDENTIALS_UNSUPPORTED } from "../../registry-url.js";
|
|
12
17
|
import { httpUrl, nonEmptyString, positiveInt } from "./primitives.js";
|
|
18
|
+
const VALID_ADAPTER_IDS_SET = new Set(VALID_ADAPTER_IDS);
|
|
13
19
|
// ── Sources / registries / installed ────────────────────────────────────────
|
|
14
20
|
const SourceConfigEntryOptionsSchema = z.record(z.unknown());
|
|
15
21
|
export const SourceConfigEntrySchema = z
|
|
@@ -165,6 +171,23 @@ export const BundleConfigEntrySchema = z
|
|
|
165
171
|
message: "writable: true is only supported on path and git bundle sources",
|
|
166
172
|
});
|
|
167
173
|
}
|
|
174
|
+
// #909: an unrecognized `components.*.adapter` used to silently fall back
|
|
175
|
+
// to `akm` at detect-time with no error and no disclosure — a typo
|
|
176
|
+
// (`agent_skills`, `akm-native`, …) then silently changed which files got
|
|
177
|
+
// indexed. Reject it here instead, the same treatment other enum-valued
|
|
178
|
+
// config fields get, listing the accepted values (derived from the
|
|
179
|
+
// adapter registry, never hardcoded — see adapter-ids.ts).
|
|
180
|
+
if (componentEntry !== undefined) {
|
|
181
|
+
const [componentName, componentValue] = componentEntry;
|
|
182
|
+
const adapterValue = componentValue.adapter;
|
|
183
|
+
if (adapterValue !== undefined && !VALID_ADAPTER_IDS_SET.has(adapterValue)) {
|
|
184
|
+
ctx.addIssue({
|
|
185
|
+
code: z.ZodIssueCode.custom,
|
|
186
|
+
path: ["components", componentName, "adapter"],
|
|
187
|
+
message: `unrecognized adapter "${adapterValue}"; expected one of: ${VALID_ADAPTER_IDS.join(", ")}`,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
168
191
|
});
|
|
169
192
|
/**
|
|
170
193
|
* `bundles` map. Keys are workspace bundle slugs (spec §11.1 / D-R5 charset).
|
|
@@ -125,7 +125,7 @@ export function formatExtraParamsIssue(label, issue) {
|
|
|
125
125
|
* (#852, following #815).
|
|
126
126
|
*
|
|
127
127
|
* Pure: never touches the filesystem. Two callers use this differently:
|
|
128
|
-
* `akm migrate apply` (
|
|
128
|
+
* `akm migrate apply` (scripts/akm-migrate/migrate/config-extra-params.ts) uses the
|
|
129
129
|
* returned `config` to persist the rewrite to disk, once; `parseAndValidateConfigText`
|
|
130
130
|
* (src/core/config/config.ts) calls this only to detect whether a lift is
|
|
131
131
|
* needed and discards `config` — an unmigrated config fails closed there
|
|
@@ -1187,8 +1187,7 @@ export function runMigrations(db, options) {
|
|
|
1187
1187
|
}
|
|
1188
1188
|
if (!options.allowHistoricalDestructiveStateUpgrade) {
|
|
1189
1189
|
throw new Error("Refusing to migrate an existing unversioned state.db during an ordinary managed open. " +
|
|
1190
|
-
"Run `akm upgrade
|
|
1191
|
-
"or `akm upgrade --state-only` where akm cannot reinstall itself (container/global install).");
|
|
1190
|
+
"Run `akm upgrade` (or `akm migrate apply`) to snapshot it before migration 001 and apply it deliberately.");
|
|
1192
1191
|
}
|
|
1193
1192
|
const ledger = assertMigrationLedger(lockedDb, STATE_MIGRATIONS);
|
|
1194
1193
|
if (ledger.migrationIds.length !== 0) {
|
|
@@ -1218,8 +1217,7 @@ export function runMigrations(db, options) {
|
|
|
1218
1217
|
assertMigrationLedger(lockedDb, STATE_MIGRATIONS);
|
|
1219
1218
|
if (!options?.allowHistoricalDestructiveStateUpgrade) {
|
|
1220
1219
|
throw new Error(`Refusing to apply historical destructive state migration ${migration.id} during an ordinary managed open. ` +
|
|
1221
|
-
"Run `akm upgrade
|
|
1222
|
-
"or `akm upgrade --state-only` where akm cannot reinstall itself (container/global install).");
|
|
1220
|
+
"Run `akm upgrade` (or `akm migrate apply`) to create a sibling state.db safety copy and apply it deliberately.");
|
|
1223
1221
|
}
|
|
1224
1222
|
if (!options.beforeHistoricalDestructiveMigration) {
|
|
1225
1223
|
throw new Error(`Historical destructive state migration ${migration.id} requires a verified safety-copy hook.`);
|
package/dist/core/state-db.js
CHANGED
|
@@ -407,8 +407,8 @@ export function openStateDatabase(dbPath, options) {
|
|
|
407
407
|
existingUnversionedDatabase = ledger.migrationIds.length === 0;
|
|
408
408
|
if (existingUnversionedDatabase && !options?.allowHistoricalDestructiveStateUpgrade) {
|
|
409
409
|
throw new Error("Refusing to migrate an existing unversioned state.db during an ordinary managed open. " +
|
|
410
|
-
"Run `akm upgrade
|
|
411
|
-
"
|
|
410
|
+
"Run `akm upgrade` (or `akm migrate apply`) to create a verified snapshot before migration 001 " +
|
|
411
|
+
"and apply it deliberately.");
|
|
412
412
|
}
|
|
413
413
|
}
|
|
414
414
|
finally {
|
|
@@ -499,13 +499,36 @@ export function openStateDatabase(dbPath, options) {
|
|
|
499
499
|
}
|
|
500
500
|
}
|
|
501
501
|
/**
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
502
|
+
* Read-only: the state migration IDs the running akm would apply to `dbPath`,
|
|
503
|
+
* in ledger order. Empty when the database is missing or current. Throws on a
|
|
504
|
+
* ledger this akm cannot own (newer, or not an exact ordered prefix) -- the
|
|
505
|
+
* same refusal a managed open makes.
|
|
505
506
|
*/
|
|
506
|
-
export function
|
|
507
|
+
export function listPendingStateMigrations(dbPath = getStateDbPath()) {
|
|
507
508
|
if (!fs.existsSync(dbPath))
|
|
508
|
-
return
|
|
509
|
+
return [];
|
|
510
|
+
const preflight = openDatabase(dbPath, { readonly: true });
|
|
511
|
+
try {
|
|
512
|
+
preflight.exec("PRAGMA busy_timeout = 30000");
|
|
513
|
+
const ledger = assertMigrationLedger(preflight, STATE_MIGRATIONS);
|
|
514
|
+
return STATE_MIGRATIONS.slice(ledger.migrationIds.length).map((migration) => migration.id);
|
|
515
|
+
}
|
|
516
|
+
finally {
|
|
517
|
+
preflight.close();
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Apply every pending state migration, historical-destructive ones included:
|
|
522
|
+
* the one step `akm upgrade` and `akm migrate apply` share, and the only
|
|
523
|
+
* caller that may admit migration 018 (an ordinary managed open refuses it by
|
|
524
|
+
* design). Missing/current databases are no-ops. A pre-018 exact ledger, or an
|
|
525
|
+
* unversioned database, is snapshotted beside state.db and verified before the
|
|
526
|
+
* immutable released migration runs.
|
|
527
|
+
*/
|
|
528
|
+
export function upgradeHistoricalStateDatabase(dbPath = getStateDbPath()) {
|
|
529
|
+
const pending = listPendingStateMigrations(dbPath);
|
|
530
|
+
if (pending.length === 0)
|
|
531
|
+
return { upgraded: false, applied: [] };
|
|
509
532
|
let safetyCopyPath;
|
|
510
533
|
try {
|
|
511
534
|
const db = openStateDatabase(dbPath, {
|
|
@@ -521,7 +544,7 @@ export function upgradeHistoricalStateDatabase(dbPath = getStateDbPath()) {
|
|
|
521
544
|
const recovery = safetyCopyPath ? ` Verified safety copy: ${safetyCopyPath}.` : "";
|
|
522
545
|
throw new Error(`${detail}${recovery}`);
|
|
523
546
|
}
|
|
524
|
-
return safetyCopyPath ? { upgraded: true, safetyCopyPath } : { upgraded:
|
|
547
|
+
return safetyCopyPath ? { upgraded: true, applied: pending, safetyCopyPath } : { upgraded: true, applied: pending };
|
|
525
548
|
}
|
|
526
549
|
/**
|
|
527
550
|
* Run `fn` against state.db, owning the handle unless one is borrowed. The loan
|
package/dist/indexer/indexer.js
CHANGED
|
@@ -13,7 +13,7 @@ import { classifyPathAccess, describeInaccessiblePath } from "../core/path-acces
|
|
|
13
13
|
import { getDbPath } from "../core/paths.js";
|
|
14
14
|
import { SCRIPT_EXTENSIONS } from "../core/recognition-util.js";
|
|
15
15
|
import { withStateDb } from "../core/state-db.js";
|
|
16
|
-
import { isVerbose, warn, warnVerbose } from "../core/warn.js";
|
|
16
|
+
import { isVerbose, warn, warnOnce, warnVerbose } from "../core/warn.js";
|
|
17
17
|
import { disposeLoweredExecutionDispatchLease, } from "../integrations/agent/execution-lowering.js";
|
|
18
18
|
import { isLlmFeatureEnabled } from "../llm/feature-gate.js";
|
|
19
19
|
import { resolveIndexPassExecution } from "../llm/index-passes.js";
|
|
@@ -750,6 +750,67 @@ function sourceSnapshotRemovals(db, currentStashDir, bundleId, currentDirs, allI
|
|
|
750
750
|
reason: { kind: "not-in-source-snapshot" },
|
|
751
751
|
}));
|
|
752
752
|
}
|
|
753
|
+
/**
|
|
754
|
+
* Warn ONCE per process (#908) when the chosen adapter for a component
|
|
755
|
+
* entirely skips a top-level directory that holds files the `akm` adapter —
|
|
756
|
+
* the format-neutral superset — would have indexed. `detectAdapterId` now
|
|
757
|
+
* corrects this for AUTO-DETECTION (a mixed layout detects as `akm`); this
|
|
758
|
+
* covers the case detection cannot see, an EXPLICITLY configured narrow
|
|
759
|
+
* adapter (`components.<name>.adapter: "agent-skills"`, say) sitting next to
|
|
760
|
+
* ordinary akm content. One line for the whole process — not one per bundle,
|
|
761
|
+
* not one per directory — naming the count and the directories is enough to
|
|
762
|
+
* point an operator at the fix.
|
|
763
|
+
*/
|
|
764
|
+
function warnIfAdapterSkipsAkmContent(component, files, adapter) {
|
|
765
|
+
if (adapter.id === "akm")
|
|
766
|
+
return;
|
|
767
|
+
const akm = adapterForId("akm");
|
|
768
|
+
if (!akm)
|
|
769
|
+
return;
|
|
770
|
+
const byTopDir = new Map();
|
|
771
|
+
for (const file of files) {
|
|
772
|
+
const top = file.ancestorDirs[0];
|
|
773
|
+
if (!top)
|
|
774
|
+
continue; // a root-level file is not a "skipped directory" concern
|
|
775
|
+
const group = byTopDir.get(top);
|
|
776
|
+
if (group)
|
|
777
|
+
group.push(file);
|
|
778
|
+
else
|
|
779
|
+
byTopDir.set(top, [file]);
|
|
780
|
+
}
|
|
781
|
+
const akmComponent = { ...component, adapter: "akm" };
|
|
782
|
+
let skippedCount = 0;
|
|
783
|
+
const skippedDirs = [];
|
|
784
|
+
for (const [dir, dirFiles] of byTopDir) {
|
|
785
|
+
const chosenRecognizesAny = dirFiles.some((file) => {
|
|
786
|
+
try {
|
|
787
|
+
return adapter.recognize(component, file) !== null;
|
|
788
|
+
}
|
|
789
|
+
catch {
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
if (chosenRecognizesAny)
|
|
794
|
+
continue; // the chosen adapter owns this dir; nothing skipped
|
|
795
|
+
const akmCandidates = dirFiles.filter((file) => {
|
|
796
|
+
try {
|
|
797
|
+
return akm.recognize(akmComponent, file) !== null;
|
|
798
|
+
}
|
|
799
|
+
catch {
|
|
800
|
+
return false;
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
if (akmCandidates.length === 0)
|
|
804
|
+
continue; // akm would drop it too — not a shadowing case
|
|
805
|
+
skippedCount += akmCandidates.length;
|
|
806
|
+
skippedDirs.push(dir);
|
|
807
|
+
}
|
|
808
|
+
if (skippedCount === 0)
|
|
809
|
+
return;
|
|
810
|
+
skippedDirs.sort();
|
|
811
|
+
warnOnce("adapter-skip-akm-content", `${adapter.id} adapter skipped ${skippedCount} file${skippedCount === 1 ? "" : "s"} in ` +
|
|
812
|
+
`${skippedDirs.map((dir) => `${dir}/`).join(", ")} — set components.<name>.adapter to "akm" to index them`);
|
|
813
|
+
}
|
|
753
814
|
function buildSourceScanPlans(db, allSourceEntries, isIncremental, reconcileMissingDirs) {
|
|
754
815
|
const componentBySource = buildComponentBySource(allSourceEntries);
|
|
755
816
|
const handoffDirs = new Set();
|
|
@@ -775,6 +836,8 @@ function buildSourceScanPlans(db, allSourceEntries, isIncremental, reconcileMiss
|
|
|
775
836
|
});
|
|
776
837
|
const dirGroups = groupFileContextsByDir(walked.files);
|
|
777
838
|
const adapter = adapterForId(component.adapter);
|
|
839
|
+
if (adapter)
|
|
840
|
+
warnIfAdapterSkipsAkmContent(component, walked.files, adapter);
|
|
778
841
|
return {
|
|
779
842
|
currentStashDir,
|
|
780
843
|
component,
|