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/show.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
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/.
|
|
1
4
|
/**
|
|
2
5
|
* `akm show` — entry point.
|
|
3
6
|
*
|
|
@@ -16,14 +19,17 @@
|
|
|
16
19
|
import fs from "node:fs";
|
|
17
20
|
import path from "node:path";
|
|
18
21
|
import { parseAssetRef } from "../core/asset-ref";
|
|
22
|
+
import { asNonEmptyString } from "../core/common";
|
|
19
23
|
import { loadConfig } from "../core/config";
|
|
20
|
-
import { NotFoundError, UsageError } from "../core/errors";
|
|
24
|
+
import { NotFoundError, rethrowIfTestIsolationError, UsageError } from "../core/errors";
|
|
21
25
|
import { appendEvent, readEvents } from "../core/events";
|
|
22
|
-
import { parseFrontmatter
|
|
26
|
+
import { parseFrontmatter } from "../core/frontmatter";
|
|
23
27
|
import { closeDatabase, findEntryIdByRef, openExistingDatabase } from "../indexer/db";
|
|
24
28
|
import { ensureIndex } from "../indexer/ensure-index";
|
|
25
29
|
import { buildFileContext, buildRenderContext, getRenderer, runMatchers } from "../indexer/file-context";
|
|
30
|
+
import { listRelatedPathsForFile } from "../indexer/graph-boost";
|
|
26
31
|
import { lookup } from "../indexer/indexer";
|
|
32
|
+
import { resolveAssetPath } from "../indexer/path-resolver";
|
|
27
33
|
import { buildEditHint, findSourceForPath, isEditable, resolveSourceEntries } from "../indexer/search-source";
|
|
28
34
|
import { insertUsageEvent } from "../indexer/usage-events";
|
|
29
35
|
import { resolveSourcesForOrigin } from "../registry/origin-resolve";
|
|
@@ -139,7 +145,7 @@ export async function akmShowUnified(input) {
|
|
|
139
145
|
}
|
|
140
146
|
// Count prior shows of this ref before logging the current one.
|
|
141
147
|
const priorShowCount = recentShowCount(ref);
|
|
142
|
-
logShowEvent(ref);
|
|
148
|
+
logShowEvent(ref, undefined, input.eventSource);
|
|
143
149
|
if (priorShowCount >= 2) {
|
|
144
150
|
// Agent has shown this same asset 3+ times — inject a loop-break hint.
|
|
145
151
|
result.showLoopWarning = priorShowCount + 1;
|
|
@@ -177,7 +183,7 @@ function enforceScopeOrThrow(filePath, ref, scope) {
|
|
|
177
183
|
for (const [key, expectedValue] of expected) {
|
|
178
184
|
if (expectedValue === undefined)
|
|
179
185
|
continue;
|
|
180
|
-
const actual =
|
|
186
|
+
const actual = asNonEmptyString(fm[`scope_${key}`]);
|
|
181
187
|
if (actual !== expectedValue) {
|
|
182
188
|
throw new NotFoundError(`Asset "${ref}" exists but is out of scope (expected scope_${key}="${expectedValue}").`);
|
|
183
189
|
}
|
|
@@ -189,18 +195,51 @@ function enforceScopeOrThrow(filePath, ref, scope) {
|
|
|
189
195
|
*/
|
|
190
196
|
function recentShowCount(ref) {
|
|
191
197
|
try {
|
|
192
|
-
const { events } = readEvents({
|
|
198
|
+
const { events } = readEvents({
|
|
199
|
+
type: "show",
|
|
200
|
+
ref,
|
|
201
|
+
since: new Date(Date.now() - 60 * 60 * 1000).toISOString(),
|
|
202
|
+
});
|
|
193
203
|
return events.length;
|
|
194
204
|
}
|
|
195
205
|
catch {
|
|
196
206
|
return 0;
|
|
197
207
|
}
|
|
198
208
|
}
|
|
199
|
-
function logShowEvent(ref, existingDb) {
|
|
209
|
+
function logShowEvent(ref, existingDb, eventSource = "user") {
|
|
200
210
|
// Emit a structured event to events.jsonl so workflow-trace consumers
|
|
201
211
|
// detect akm show invocations without relying on stdout scraping.
|
|
202
212
|
const parsed = parseAssetRef(ref);
|
|
203
213
|
appendEvent({ eventType: "show", ref, metadata: { type: parsed.type, name: parsed.name } });
|
|
214
|
+
// Detect if this show is a selection from a recent search result.
|
|
215
|
+
try {
|
|
216
|
+
// D7: bound the query to the last 60 s so we never scan unbounded history
|
|
217
|
+
const { events: recentSearches } = readEvents({
|
|
218
|
+
type: "search",
|
|
219
|
+
since: new Date(Date.now() - 60_000).toISOString(),
|
|
220
|
+
});
|
|
221
|
+
const cutoffMs = Date.now() - 60_000;
|
|
222
|
+
const matchingSearch = [...recentSearches].reverse().find((e) => {
|
|
223
|
+
if (!e.ts || new Date(e.ts).getTime() < cutoffMs)
|
|
224
|
+
return false;
|
|
225
|
+
const refs = e.metadata?.resultRefs ?? [];
|
|
226
|
+
return refs.includes(ref);
|
|
227
|
+
});
|
|
228
|
+
if (matchingSearch) {
|
|
229
|
+
appendEvent({
|
|
230
|
+
eventType: "select",
|
|
231
|
+
ref,
|
|
232
|
+
metadata: {
|
|
233
|
+
query: matchingSearch.metadata?.query,
|
|
234
|
+
searchTs: matchingSearch.ts,
|
|
235
|
+
rankPosition: (matchingSearch.metadata?.resultRefs ?? []).indexOf(ref),
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
/* fire-and-forget — select is best-effort */
|
|
242
|
+
}
|
|
204
243
|
try {
|
|
205
244
|
const db = existingDb ?? openExistingDatabase();
|
|
206
245
|
try {
|
|
@@ -208,6 +247,7 @@ function logShowEvent(ref, existingDb) {
|
|
|
208
247
|
event_type: "show",
|
|
209
248
|
entry_ref: ref,
|
|
210
249
|
entry_id: findEntryIdByRef(db, ref),
|
|
250
|
+
source: eventSource,
|
|
211
251
|
});
|
|
212
252
|
}
|
|
213
253
|
finally {
|
|
@@ -215,22 +255,11 @@ function logShowEvent(ref, existingDb) {
|
|
|
215
255
|
closeDatabase(db);
|
|
216
256
|
}
|
|
217
257
|
}
|
|
218
|
-
catch {
|
|
258
|
+
catch (err) {
|
|
259
|
+
rethrowIfTestIsolationError(err);
|
|
219
260
|
/* fire-and-forget */
|
|
220
261
|
}
|
|
221
262
|
}
|
|
222
|
-
/**
|
|
223
|
-
* Resolve an asset path via the FTS5 index only. Spec §6.2's primary path.
|
|
224
|
-
*
|
|
225
|
-
* Returns `undefined` if the index has no matching row.
|
|
226
|
-
*/
|
|
227
|
-
async function resolvePathViaIndex(parsed) {
|
|
228
|
-
const entry = await lookup(parsed);
|
|
229
|
-
if (entry) {
|
|
230
|
-
return { assetPath: entry.filePath };
|
|
231
|
-
}
|
|
232
|
-
return undefined;
|
|
233
|
-
}
|
|
234
263
|
/** @internal Use akmShowUnified() for all external callers. */
|
|
235
264
|
export async function showLocal(input) {
|
|
236
265
|
const parsed = parseAssetRef(input.ref);
|
|
@@ -251,10 +280,11 @@ export async function showLocal(input) {
|
|
|
251
280
|
}
|
|
252
281
|
}
|
|
253
282
|
if (!assetPath) {
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
283
|
+
const resolvedAssetPath = await resolveAssetPath(parsed, {
|
|
284
|
+
stashDir: input.stashDir,
|
|
285
|
+
mode: "index-first",
|
|
286
|
+
});
|
|
287
|
+
assetPath = resolvedAssetPath ?? undefined;
|
|
258
288
|
}
|
|
259
289
|
if (!assetPath && parsed.origin && searchSources.length === 0) {
|
|
260
290
|
const installCmd = `akm add ${parsed.origin}`;
|
|
@@ -293,8 +323,24 @@ export async function showLocal(input) {
|
|
|
293
323
|
origin: source?.registryId ?? null,
|
|
294
324
|
editable,
|
|
295
325
|
...(!editable ? { editHint: buildEditHint(assetPath, parsed.type, parsed.name, source?.registryId) } : {}),
|
|
326
|
+
related: (() => {
|
|
327
|
+
let db;
|
|
328
|
+
try {
|
|
329
|
+
db = openExistingDatabase();
|
|
330
|
+
const related = listRelatedPathsForFile(sourceStashDir, assetPath, 5, db);
|
|
331
|
+
return { total: related.length, hits: related };
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
rethrowIfTestIsolationError(err);
|
|
335
|
+
return { total: 0, hits: [] };
|
|
336
|
+
}
|
|
337
|
+
finally {
|
|
338
|
+
if (db)
|
|
339
|
+
closeDatabase(db);
|
|
340
|
+
}
|
|
341
|
+
})(),
|
|
296
342
|
};
|
|
297
|
-
const activeRun = getActiveWorkflowRun(getCurrentWorkflowScopeKey());
|
|
343
|
+
const activeRun = await getActiveWorkflowRun(getCurrentWorkflowScopeKey());
|
|
298
344
|
if (activeRun) {
|
|
299
345
|
fullResponse.activeRun = activeRun;
|
|
300
346
|
}
|
|
@@ -354,7 +400,7 @@ function buildSummaryResponse(full, assetPath) {
|
|
|
354
400
|
const textContent = full.content ?? full.template ?? full.prompt;
|
|
355
401
|
if (textContent && !description) {
|
|
356
402
|
const parsed = parseFrontmatter(textContent);
|
|
357
|
-
description =
|
|
403
|
+
description = asNonEmptyString(parsed.data.description);
|
|
358
404
|
}
|
|
359
405
|
}
|
|
360
406
|
const summary = {
|
|
@@ -371,3 +417,76 @@ function buildSummaryResponse(full, assetPath) {
|
|
|
371
417
|
};
|
|
372
418
|
return summary;
|
|
373
419
|
}
|
|
420
|
+
// ── argv normalisation ───────────────────────────────────────────────────────
|
|
421
|
+
const SHOW_VIEW_MODES = new Set(["toc", "frontmatter", "full", "section", "lines"]);
|
|
422
|
+
/**
|
|
423
|
+
* Normalize argv so positional view-mode arguments after the asset ref
|
|
424
|
+
* are rewritten into internal flags that citty can parse.
|
|
425
|
+
*
|
|
426
|
+
* Converts:
|
|
427
|
+
* akm show knowledge:guide.md toc → akm show knowledge:guide.md --akmView toc
|
|
428
|
+
* akm show knowledge:guide.md section Auth → akm show knowledge:guide.md --akmView section --akmHeading Auth
|
|
429
|
+
* akm show knowledge:guide.md lines 1 50 → akm show knowledge:guide.md --akmView lines --akmStart 1 --akmEnd 50
|
|
430
|
+
*
|
|
431
|
+
* Legacy `--view` is intentionally unsupported.
|
|
432
|
+
* Returns a new array; the input is never modified.
|
|
433
|
+
*/
|
|
434
|
+
export function normalizeShowArgv(argv) {
|
|
435
|
+
// argv[0]=bun argv[1]=script argv[2]=subcommand argv[3]=ref argv[4..]=rest
|
|
436
|
+
if (argv[2] !== "show")
|
|
437
|
+
return argv;
|
|
438
|
+
if (argv[3] === "proposal")
|
|
439
|
+
return argv;
|
|
440
|
+
if (argv.includes("--view") || argv.includes("--heading") || argv.includes("--start") || argv.includes("--end")) {
|
|
441
|
+
throw new UsageError('Legacy show flags are no longer supported. Use positional syntax like `akm show knowledge:guide toc` or `akm show knowledge:guide section "Auth"`.');
|
|
442
|
+
}
|
|
443
|
+
// Separate global flags from positional/show-specific args
|
|
444
|
+
const prefix = argv.slice(0, 3); // [bun, script, show]
|
|
445
|
+
const rest = argv.slice(3);
|
|
446
|
+
const globalFlags = [];
|
|
447
|
+
const showArgs = [];
|
|
448
|
+
for (let i = 0; i < rest.length; i++) {
|
|
449
|
+
const arg = rest[i];
|
|
450
|
+
if (arg === "--quiet" || arg === "-q" || arg === "--for-agent" || arg === "--for-agent=true") {
|
|
451
|
+
globalFlags.push(arg);
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
if (arg.startsWith("--format=") || arg.startsWith("--detail=")) {
|
|
455
|
+
globalFlags.push(arg);
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (arg === "--format" || arg === "--detail") {
|
|
459
|
+
globalFlags.push(arg);
|
|
460
|
+
if (rest[i + 1] !== undefined) {
|
|
461
|
+
globalFlags.push(rest[i + 1]);
|
|
462
|
+
i++;
|
|
463
|
+
}
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
showArgs.push(arg);
|
|
467
|
+
}
|
|
468
|
+
// showArgs[0] = ref, showArgs[1] = potential view mode, showArgs[2..] = view params
|
|
469
|
+
const ref = showArgs[0];
|
|
470
|
+
const viewMode = showArgs[1];
|
|
471
|
+
if (!ref || !viewMode || !SHOW_VIEW_MODES.has(viewMode)) {
|
|
472
|
+
return argv;
|
|
473
|
+
}
|
|
474
|
+
const result = [...prefix, ref, "--akmView", viewMode];
|
|
475
|
+
if (viewMode === "section") {
|
|
476
|
+
// Next arg is the heading name; pass empty string when missing so the
|
|
477
|
+
// show handler can produce a clear "section not found" error.
|
|
478
|
+
const heading = showArgs[2] ?? "";
|
|
479
|
+
result.push("--akmHeading", heading);
|
|
480
|
+
}
|
|
481
|
+
else if (viewMode === "lines") {
|
|
482
|
+
// Next two args are start and end
|
|
483
|
+
const start = showArgs[2];
|
|
484
|
+
const end = showArgs[3];
|
|
485
|
+
if (start)
|
|
486
|
+
result.push("--akmStart", start);
|
|
487
|
+
if (end)
|
|
488
|
+
result.push("--akmEnd", end);
|
|
489
|
+
}
|
|
490
|
+
result.push(...globalFlags);
|
|
491
|
+
return result;
|
|
492
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
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/.
|
|
1
4
|
import fs from "node:fs";
|
|
2
5
|
import path from "node:path";
|
|
3
6
|
import { isHttpUrl, resolveStashDir } from "../core/common";
|
|
4
|
-
import { loadConfig, loadUserConfig, saveConfig } from "../core/config";
|
|
7
|
+
import { getSources, loadConfig, loadUserConfig, saveConfig } from "../core/config";
|
|
5
8
|
import { ConfigError, UsageError } from "../core/errors";
|
|
6
|
-
import { warn } from "../core/warn";
|
|
7
9
|
import { akmIndex } from "../indexer/indexer";
|
|
8
10
|
import { upsertLockEntry } from "../integrations/lockfile";
|
|
9
11
|
import { parseRegistryRef } from "../registry/resolve";
|
|
@@ -11,7 +13,6 @@ import { detectStashRoot } from "../sources/providers/provider-utils";
|
|
|
11
13
|
import { syncFromRef } from "../sources/providers/sync-from-ref";
|
|
12
14
|
import { ensureWebsiteMirror, validateWebsiteInputUrl } from "../sources/website-ingest";
|
|
13
15
|
import { ensureWikiNameAvailable, validateWikiName } from "../wiki/wiki";
|
|
14
|
-
import { auditInstallCandidate, deriveRegistryLabels, enforceRegistryInstallPolicy, formatInstallAuditFailure, } from "./install-audit";
|
|
15
16
|
const VALID_OVERRIDE_TYPES = new Set(["wiki"]);
|
|
16
17
|
export async function akmAdd(input) {
|
|
17
18
|
const ref = input.ref.trim();
|
|
@@ -38,16 +39,13 @@ export async function akmAdd(input) {
|
|
|
38
39
|
try {
|
|
39
40
|
const parsed = parseRegistryRef(ref);
|
|
40
41
|
if (parsed.source === "local") {
|
|
41
|
-
if (input.trustThisInstall) {
|
|
42
|
-
warn("--trust has no effect on local directory sources; the install audit is not run for local paths.");
|
|
43
|
-
}
|
|
44
42
|
return addLocalSource(ref, parsed.sourcePath, stashDir, wikiName, input.name);
|
|
45
43
|
}
|
|
46
44
|
}
|
|
47
45
|
catch {
|
|
48
46
|
// Not a local ref — fall through to registry install
|
|
49
47
|
}
|
|
50
|
-
return addRegistryStash(ref, stashDir, input.
|
|
48
|
+
return addRegistryStash(ref, stashDir, input.writable, wikiName);
|
|
51
49
|
}
|
|
52
50
|
export async function registerWikiSource(input) {
|
|
53
51
|
const stashDir = resolveStashDir();
|
|
@@ -59,7 +57,6 @@ export async function registerWikiSource(input) {
|
|
|
59
57
|
name,
|
|
60
58
|
overrideType: "wiki",
|
|
61
59
|
options: input.options,
|
|
62
|
-
trustThisInstall: input.trustThisInstall,
|
|
63
60
|
writable: input.writable,
|
|
64
61
|
});
|
|
65
62
|
}
|
|
@@ -74,7 +71,7 @@ async function addLocalSource(ref, sourcePath, stashDir, wikiName, explicitName)
|
|
|
74
71
|
// Derive the canonical name: explicit --name wins, then wiki name, then readable path.
|
|
75
72
|
const derivedName = explicitName ?? wikiName ?? toReadableId(resolvedPath);
|
|
76
73
|
// Check for duplicates in sources[]
|
|
77
|
-
const sources = [...(config
|
|
74
|
+
const sources = [...getSources(config)];
|
|
78
75
|
const existing = sources.find((s) => s.type === "filesystem" && s.path && path.resolve(s.path) === resolvedPath);
|
|
79
76
|
let persistedEntry;
|
|
80
77
|
if (!existing) {
|
|
@@ -85,7 +82,7 @@ async function addLocalSource(ref, sourcePath, stashDir, wikiName, explicitName)
|
|
|
85
82
|
...(wikiName ? { wikiName } : {}),
|
|
86
83
|
};
|
|
87
84
|
sources.push(persistedEntry);
|
|
88
|
-
saveConfig({ ...config, sources
|
|
85
|
+
saveConfig({ ...config, sources });
|
|
89
86
|
}
|
|
90
87
|
else {
|
|
91
88
|
let changed = false;
|
|
@@ -99,7 +96,7 @@ async function addLocalSource(ref, sourcePath, stashDir, wikiName, explicitName)
|
|
|
99
96
|
changed = true;
|
|
100
97
|
}
|
|
101
98
|
if (changed)
|
|
102
|
-
saveConfig({ ...config, sources
|
|
99
|
+
saveConfig({ ...config, sources });
|
|
103
100
|
persistedEntry = existing;
|
|
104
101
|
}
|
|
105
102
|
const index = await akmIndex({ stashDir });
|
|
@@ -116,7 +113,7 @@ async function addLocalSource(ref, sourcePath, stashDir, wikiName, explicitName)
|
|
|
116
113
|
...(persistedEntry.wikiName ? { wiki: persistedEntry.wikiName } : {}),
|
|
117
114
|
},
|
|
118
115
|
config: {
|
|
119
|
-
sourceCount: (updatedConfig
|
|
116
|
+
sourceCount: getSources(updatedConfig).length,
|
|
120
117
|
installedKitCount: updatedConfig.installed?.length ?? 0,
|
|
121
118
|
},
|
|
122
119
|
index: {
|
|
@@ -131,7 +128,7 @@ async function addLocalSource(ref, sourcePath, stashDir, wikiName, explicitName)
|
|
|
131
128
|
async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
|
|
132
129
|
const normalizedUrl = validateWebsiteInputUrl(ref);
|
|
133
130
|
const config = loadUserConfig();
|
|
134
|
-
const sources = [...(config
|
|
131
|
+
const sources = [...getSources(config)];
|
|
135
132
|
let entry = sources.find((stash) => stash.type === "website" && stash.url === normalizedUrl);
|
|
136
133
|
if (!entry) {
|
|
137
134
|
entry = {
|
|
@@ -142,7 +139,7 @@ async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
|
|
|
142
139
|
...(wikiName ? { wikiName } : {}),
|
|
143
140
|
};
|
|
144
141
|
sources.push(entry);
|
|
145
|
-
saveConfig({ ...config, sources
|
|
142
|
+
saveConfig({ ...config, sources });
|
|
146
143
|
}
|
|
147
144
|
else {
|
|
148
145
|
let changed = false;
|
|
@@ -155,7 +152,7 @@ async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
|
|
|
155
152
|
changed = true;
|
|
156
153
|
}
|
|
157
154
|
if (changed)
|
|
158
|
-
saveConfig({ ...config, sources
|
|
155
|
+
saveConfig({ ...config, sources });
|
|
159
156
|
}
|
|
160
157
|
const cachePaths = await ensureWebsiteMirror(entry, { requireStashDir: true });
|
|
161
158
|
const index = await akmIndex({ stashDir });
|
|
@@ -172,7 +169,7 @@ async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
|
|
|
172
169
|
...(entry.wikiName ? { wiki: entry.wikiName } : {}),
|
|
173
170
|
},
|
|
174
171
|
config: {
|
|
175
|
-
sourceCount: (updatedConfig
|
|
172
|
+
sourceCount: getSources(updatedConfig).length,
|
|
176
173
|
installedKitCount: updatedConfig.installed?.length ?? 0,
|
|
177
174
|
},
|
|
178
175
|
index: {
|
|
@@ -186,38 +183,14 @@ async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
|
|
|
186
183
|
}
|
|
187
184
|
/**
|
|
188
185
|
* Install a stash from a registry (npm, github, git) by dispatching to the
|
|
189
|
-
* matching syncable provider
|
|
190
|
-
* persisting the lock entry.
|
|
186
|
+
* matching syncable provider and persisting the lock entry.
|
|
191
187
|
*/
|
|
192
|
-
async function addRegistryStash(ref, stashDir,
|
|
188
|
+
async function addRegistryStash(ref, stashDir, writable, wikiName) {
|
|
193
189
|
const parsedRef = parseRegistryRef(ref);
|
|
194
190
|
if (writable === true && parsedRef.source !== "git") {
|
|
195
191
|
throw new ConfigError("writable: true is only supported on filesystem and git sources", "INVALID_CONFIG_FILE");
|
|
196
192
|
}
|
|
197
|
-
|
|
198
|
-
// so we keep parity with the historical behavior where `enforceRegistryInstallPolicy`
|
|
199
|
-
// ran before `extractTarGzSecure` etc.
|
|
200
|
-
const config = loadConfig();
|
|
201
|
-
const synced = await syncFromRef(ref, { trustThisInstall, writable });
|
|
202
|
-
const registryLabels = deriveRegistryLabels({
|
|
203
|
-
source: synced.source,
|
|
204
|
-
ref: synced.ref,
|
|
205
|
-
artifactUrl: synced.artifactUrl,
|
|
206
|
-
});
|
|
207
|
-
enforceRegistryInstallPolicy(registryLabels, config, ref);
|
|
208
|
-
// Post-sync hook: install audit. Throws when blocked unless `--trust` is set
|
|
209
|
-
// (in which case the audit report still surfaces in the response).
|
|
210
|
-
const audit = auditInstallCandidate({
|
|
211
|
-
rootDir: synced.extractedDir,
|
|
212
|
-
source: synced.source,
|
|
213
|
-
ref: synced.ref,
|
|
214
|
-
registryLabels,
|
|
215
|
-
config,
|
|
216
|
-
trustThisInstall,
|
|
217
|
-
});
|
|
218
|
-
if (audit.blocked) {
|
|
219
|
-
throw new Error(formatInstallAuditFailure(synced.ref, audit));
|
|
220
|
-
}
|
|
193
|
+
const synced = await syncFromRef(ref, { writable });
|
|
221
194
|
const replaced = (loadConfig().installed ?? []).find((entry) => entry.id === synced.id);
|
|
222
195
|
const updatedConfig = upsertInstalledRegistryEntry({
|
|
223
196
|
id: synced.id,
|
|
@@ -265,10 +238,9 @@ async function addRegistryStash(ref, stashDir, trustThisInstall, writable, wikiN
|
|
|
265
238
|
cacheDir: synced.cacheDir,
|
|
266
239
|
extractedDir: synced.extractedDir,
|
|
267
240
|
installedAt: synced.syncedAt,
|
|
268
|
-
audit,
|
|
269
241
|
},
|
|
270
242
|
config: {
|
|
271
|
-
sourceCount: (updatedConfig
|
|
243
|
+
sourceCount: getSources(updatedConfig).length,
|
|
272
244
|
installedKitCount: updatedConfig.installed?.length ?? 0,
|
|
273
245
|
},
|
|
274
246
|
index: {
|
|
@@ -1,3 +1,6 @@
|
|
|
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/.
|
|
1
4
|
import fs from "node:fs";
|
|
2
5
|
import path from "node:path";
|
|
3
6
|
import { makeAssetRef, parseAssetRef } from "../core/asset-ref";
|
|
@@ -1,5 +1,9 @@
|
|
|
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/.
|
|
1
4
|
import path from "node:path";
|
|
2
|
-
import {
|
|
5
|
+
import { isRemoteUrl } from "../core/common";
|
|
6
|
+
import { getSources, loadConfig, loadUserConfig, saveConfig } from "../core/config";
|
|
3
7
|
import { ConfigError, UsageError } from "../core/errors";
|
|
4
8
|
import { resolveSourceEntries } from "../indexer/search-source";
|
|
5
9
|
// ── Operations ──────────────────────────────────────────────────────────────
|
|
@@ -19,14 +23,9 @@ export function addStash(opts) {
|
|
|
19
23
|
throw new ConfigError("writable: true is only supported on filesystem and git sources", "INVALID_CONFIG_FILE");
|
|
20
24
|
}
|
|
21
25
|
const config = loadUserConfig();
|
|
22
|
-
const sources = [...(config
|
|
23
|
-
const isRemoteUrl = target.startsWith("http://") ||
|
|
24
|
-
target.startsWith("https://") ||
|
|
25
|
-
target.startsWith("git@") ||
|
|
26
|
-
target.startsWith("ssh://") ||
|
|
27
|
-
target.startsWith("git://");
|
|
26
|
+
const sources = [...getSources(config)];
|
|
28
27
|
let entry;
|
|
29
|
-
if (isRemoteUrl) {
|
|
28
|
+
if (isRemoteUrl(target)) {
|
|
30
29
|
if (!providerType) {
|
|
31
30
|
throw new UsageError("--provider is required for URL sources (e.g. --provider git --provider website)");
|
|
32
31
|
}
|
|
@@ -53,7 +52,7 @@ export function addStash(opts) {
|
|
|
53
52
|
entry.name = name;
|
|
54
53
|
}
|
|
55
54
|
sources.push(entry);
|
|
56
|
-
saveConfig({ ...config, sources
|
|
55
|
+
saveConfig({ ...config, sources });
|
|
57
56
|
return { sources, added: true, entry };
|
|
58
57
|
}
|
|
59
58
|
/**
|
|
@@ -62,16 +61,12 @@ export function addStash(opts) {
|
|
|
62
61
|
*/
|
|
63
62
|
export function removeStash(target) {
|
|
64
63
|
const config = loadUserConfig();
|
|
65
|
-
const sources = [...(config
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
target.startsWith("git@") ||
|
|
69
|
-
target.startsWith("ssh://") ||
|
|
70
|
-
target.startsWith("git://");
|
|
71
|
-
const resolvedPath = !isUrl ? path.resolve(target) : undefined;
|
|
64
|
+
const sources = [...getSources(config)];
|
|
65
|
+
const isUrlTarget = isRemoteUrl(target);
|
|
66
|
+
const resolvedPath = !isUrlTarget ? path.resolve(target) : undefined;
|
|
72
67
|
// Try URL match first, then path, then name (most specific → least specific)
|
|
73
68
|
let idx = -1;
|
|
74
|
-
if (
|
|
69
|
+
if (isUrlTarget) {
|
|
75
70
|
idx = sources.findIndex((s) => s.url === target);
|
|
76
71
|
}
|
|
77
72
|
if (idx === -1 && resolvedPath) {
|
|
@@ -84,7 +79,7 @@ export function removeStash(target) {
|
|
|
84
79
|
return { sources, removed: false, message: "No matching source found" };
|
|
85
80
|
}
|
|
86
81
|
const removed = sources.splice(idx, 1)[0];
|
|
87
|
-
saveConfig({ ...config, sources
|
|
82
|
+
saveConfig({ ...config, sources });
|
|
88
83
|
return { sources, removed: true, entry: removed };
|
|
89
84
|
}
|
|
90
85
|
/**
|
|
@@ -93,6 +88,6 @@ export function removeStash(target) {
|
|
|
93
88
|
export function listStashes() {
|
|
94
89
|
const config = loadConfig();
|
|
95
90
|
const localSources = resolveSourceEntries();
|
|
96
|
-
const sources = config
|
|
91
|
+
const sources = getSources(config);
|
|
97
92
|
return { localSources, sources };
|
|
98
93
|
}
|