akm-cli 0.7.4 → 0.8.0-rc.10
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 +224 -1
- package/README.md +22 -6
- 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/shared.js +129 -0
- package/dist/cli.js +2631 -1440
- package/dist/commands/add-cli.js +279 -0
- 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 +2122 -0
- package/dist/commands/curate.js +45 -3
- package/dist/commands/db-cli.js +23 -0
- package/dist/commands/distill-promotion-policy.js +660 -0
- package/dist/commands/distill.js +1081 -73
- package/dist/commands/env.js +213 -0
- package/dist/commands/eval-cases.js +43 -0
- package/dist/commands/events.js +15 -24
- package/dist/commands/extract-cli.js +127 -0
- package/dist/commands/extract-prompt.js +204 -0
- package/dist/commands/extract.js +477 -0
- package/dist/commands/feedback-cli.js +331 -0
- package/dist/commands/graph.js +477 -0
- package/dist/commands/health.js +1302 -0
- package/dist/commands/help/help-accept.md +12 -0
- package/dist/commands/help/help-improve.md +69 -0
- package/dist/commands/help/help-proposals.md +18 -0
- package/dist/commands/help/help-propose.md +17 -0
- package/dist/commands/help/help-reject.md +11 -0
- package/dist/commands/history.js +54 -46
- package/dist/commands/improve-auto-accept.js +97 -0
- package/dist/commands/improve-cli.js +217 -0
- package/dist/commands/improve-profiles.js +166 -0
- package/dist/commands/improve-result-file.js +167 -0
- package/dist/commands/improve.js +2373 -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/env-key-rules.js +154 -0
- package/dist/commands/lint/index.js +196 -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/workflow-linter.js +56 -0
- package/dist/commands/lint.js +4 -0
- package/dist/commands/migration-help.js +3 -0
- package/dist/commands/proposal.js +67 -12
- package/dist/commands/propose.js +120 -45
- package/dist/commands/reflect.js +1104 -60
- package/dist/commands/registry-cli.js +150 -0
- package/dist/commands/registry-search.js +5 -2
- package/dist/commands/remember-cli.js +257 -0
- package/dist/commands/remember.js +70 -7
- package/dist/commands/schema-repair.js +203 -0
- package/dist/commands/search.js +115 -14
- package/dist/commands/secret.js +173 -0
- package/dist/commands/self-update.js +3 -0
- package/dist/commands/show.js +158 -60
- 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 +437 -0
- package/dist/commands/url-checker.js +42 -0
- package/dist/core/action-contributors.js +28 -0
- package/dist/core/asset-ref.js +17 -2
- package/dist/core/asset-registry.js +12 -17
- package/dist/core/asset-serialize.js +88 -0
- package/dist/core/asset-spec.js +67 -1
- package/dist/core/common.js +182 -0
- package/dist/core/concurrent.js +25 -0
- package/dist/core/config-io.js +347 -0
- package/dist/core/config-migration.js +622 -0
- package/dist/core/config-schema.js +534 -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 +364 -968
- package/dist/core/errors.js +42 -20
- package/dist/core/events.js +105 -135
- package/dist/core/file-lock.js +104 -0
- package/dist/core/frontmatter.js +75 -8
- 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 +280 -14
- package/dist/core/proposal-quality-validators.js +380 -0
- package/dist/core/proposal-validators.js +69 -0
- package/dist/core/proposals.js +512 -42
- package/dist/core/state-db.js +1068 -0
- package/dist/core/text-truncation.js +107 -0
- package/dist/core/time.js +54 -0
- package/dist/core/tty.js +59 -0
- package/dist/core/warn.js +64 -1
- package/dist/core/write-source.js +3 -0
- package/dist/indexer/db-backup.js +391 -0
- package/dist/indexer/db-search.js +198 -489
- package/dist/indexer/db.js +990 -108
- package/dist/indexer/ensure-index.js +136 -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 -114
- package/dist/indexer/index-context.js +4 -0
- package/dist/indexer/indexer.js +547 -309
- package/dist/indexer/llm-cache.js +52 -0
- package/dist/indexer/manifest.js +3 -0
- package/dist/indexer/matchers.js +167 -160
- package/dist/indexer/memory-inference.js +152 -74
- package/dist/indexer/metadata-contributors.js +29 -0
- package/dist/indexer/metadata.js +275 -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 +6 -17
- 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 +250 -36
- package/dist/integrations/agent/runner.js +151 -0
- package/dist/integrations/agent/sdk-runner.js +126 -0
- package/dist/integrations/agent/spawn.js +183 -35
- package/dist/integrations/github.js +3 -0
- package/dist/integrations/lockfile.js +32 -69
- package/dist/integrations/session-logs/index.js +69 -0
- package/dist/integrations/session-logs/inline-refs.js +35 -0
- package/dist/integrations/session-logs/pre-filter.js +152 -0
- package/dist/integrations/session-logs/providers/claude-code.js +282 -0
- package/dist/integrations/session-logs/providers/opencode.js +258 -0
- package/dist/integrations/session-logs/types.js +4 -0
- package/dist/llm/call-ai.js +62 -0
- package/dist/llm/client.js +79 -88
- package/dist/llm/embedder.js +20 -29
- package/dist/llm/embedders/cache.js +3 -7
- package/dist/llm/embedders/local.js +42 -1
- package/dist/llm/embedders/remote.js +20 -8
- package/dist/llm/embedders/types.js +3 -7
- package/dist/llm/feature-gate.js +95 -48
- package/dist/llm/graph-extract.js +676 -72
- package/dist/llm/index-passes.js +44 -29
- package/dist/llm/memory-infer.js +80 -71
- package/dist/llm/metadata-enhance.js +42 -29
- package/dist/llm/prompts/extract-session.md +80 -0
- package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
- package/dist/output/cli-hints-full.md +292 -0
- package/dist/output/cli-hints-short.md +66 -0
- package/dist/output/cli-hints.js +7 -311
- package/dist/output/context.js +60 -8
- package/dist/output/renderers.js +306 -258
- package/dist/output/shapes/curate.js +56 -0
- package/dist/output/shapes/distill.js +10 -0
- package/dist/output/shapes/env-list.js +19 -0
- package/dist/output/shapes/events.js +11 -0
- package/dist/output/shapes/helpers.js +424 -0
- package/dist/output/shapes/history.js +7 -0
- package/dist/output/shapes/passthrough.js +102 -0
- package/dist/output/shapes/proposal-accept.js +7 -0
- package/dist/output/shapes/proposal-diff.js +7 -0
- package/dist/output/shapes/proposal-list.js +7 -0
- package/dist/output/shapes/proposal-producer.js +11 -0
- package/dist/output/shapes/proposal-reject.js +7 -0
- package/dist/output/shapes/proposal-show.js +7 -0
- package/dist/output/shapes/registry-search.js +6 -0
- package/dist/output/shapes/registry.js +30 -0
- package/dist/output/shapes/search.js +6 -0
- package/dist/output/shapes/secret-list.js +19 -0
- package/dist/output/shapes/show.js +6 -0
- package/dist/output/shapes/vault-list.js +19 -0
- package/dist/output/shapes.js +51 -511
- package/dist/output/text/add.js +6 -0
- package/dist/output/text/clone.js +6 -0
- package/dist/output/text/config.js +6 -0
- package/dist/output/text/curate.js +6 -0
- package/dist/output/text/distill.js +7 -0
- package/dist/output/text/enable-disable.js +7 -0
- package/dist/output/text/events.js +10 -0
- package/dist/output/text/feedback.js +6 -0
- package/dist/output/text/helpers.js +1039 -0
- package/dist/output/text/history.js +7 -0
- package/dist/output/text/import.js +6 -0
- package/dist/output/text/index.js +6 -0
- package/dist/output/text/info.js +6 -0
- package/dist/output/text/init.js +6 -0
- package/dist/output/text/list.js +6 -0
- package/dist/output/text/proposal-producer.js +8 -0
- package/dist/output/text/proposal.js +11 -0
- package/dist/output/text/registry-commands.js +11 -0
- package/dist/output/text/registry.js +30 -0
- package/dist/output/text/remember.js +6 -0
- package/dist/output/text/remove.js +6 -0
- package/dist/output/text/save.js +6 -0
- package/dist/output/text/search.js +6 -0
- package/dist/output/text/show.js +6 -0
- package/dist/output/text/update.js +6 -0
- package/dist/output/text/upgrade.js +6 -0
- package/dist/output/text/vault.js +16 -0
- package/dist/output/text/wiki.js +15 -0
- package/dist/output/text/workflow.js +14 -0
- package/dist/output/text.js +44 -1093
- package/dist/registry/build-index.js +3 -0
- package/dist/registry/create-provider-registry.js +3 -0
- package/dist/registry/factory.js +4 -1
- package/dist/registry/origin-resolve.js +3 -0
- package/dist/registry/providers/index.js +3 -0
- package/dist/registry/providers/skills-sh.js +71 -50
- 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 +17750 -0
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -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 +179 -20
- 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 +227 -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 +141 -2
- 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 +91 -89
- package/dist/workflows/schema.js +3 -0
- package/dist/workflows/scope-key.js +79 -0
- package/dist/workflows/validator.js +4 -8
- package/dist/workflows/workflow-template.md +24 -0
- package/docs/README.md +10 -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.4.md +1 -1
- package/docs/migration/release-notes/0.7.5.md +20 -0
- 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 +29 -11
- package/dist/commands/install-audit.js +0 -381
- package/dist/commands/vault.js +0 -333
- 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
|
*
|
|
@@ -9,33 +12,31 @@
|
|
|
9
12
|
* edit-hints, summary-detail truncation) lives below in this file. The flow:
|
|
10
13
|
*
|
|
11
14
|
* 1. Special-case wiki-root refs (`wiki:<name>` with no page path).
|
|
12
|
-
* 2.
|
|
13
|
-
* 3.
|
|
14
|
-
* no matching row — covers the "indexed yet?" gap when the user has
|
|
15
|
-
* just added a file and not run `akm index`.
|
|
15
|
+
* 2. Auto-index when stale so the index is current.
|
|
16
|
+
* 3. Ask `indexer.lookup(ref)` for the row in the FTS index.
|
|
16
17
|
* 4. Render the file via the matcher/renderer pipeline.
|
|
17
|
-
*
|
|
18
|
-
* Step (2) is the v1 spec change: reading is the indexer's job. Step (3) is a
|
|
19
|
-
* pragmatic safety net (NOT remote provider fallback, which the spec
|
|
20
|
-
* forbids — "Show: Local FTS5 index only. No remote provider fallback.").
|
|
21
18
|
*/
|
|
22
19
|
import fs from "node:fs";
|
|
23
20
|
import path from "node:path";
|
|
24
21
|
import { parseAssetRef } from "../core/asset-ref";
|
|
22
|
+
import { asNonEmptyString } from "../core/common";
|
|
25
23
|
import { loadConfig } from "../core/config";
|
|
26
|
-
import { NotFoundError, UsageError } from "../core/errors";
|
|
24
|
+
import { NotFoundError, rethrowIfTestIsolationError, UsageError } from "../core/errors";
|
|
27
25
|
import { appendEvent, readEvents } from "../core/events";
|
|
28
|
-
import { parseFrontmatter
|
|
26
|
+
import { parseFrontmatter } from "../core/frontmatter";
|
|
29
27
|
import { closeDatabase, findEntryIdByRef, openExistingDatabase } from "../indexer/db";
|
|
28
|
+
import { ensureIndex } from "../indexer/ensure-index";
|
|
30
29
|
import { buildFileContext, buildRenderContext, getRenderer, runMatchers } from "../indexer/file-context";
|
|
30
|
+
import { listRelatedPathsForFile } from "../indexer/graph-boost";
|
|
31
31
|
import { lookup } from "../indexer/indexer";
|
|
32
|
+
import { resolveAssetPath } from "../indexer/path-resolver";
|
|
32
33
|
import { buildEditHint, findSourceForPath, isEditable, resolveSourceEntries } from "../indexer/search-source";
|
|
33
34
|
import { insertUsageEvent } from "../indexer/usage-events";
|
|
34
35
|
import { resolveSourcesForOrigin } from "../registry/origin-resolve";
|
|
35
36
|
// Eagerly import source providers to trigger self-registration.
|
|
36
37
|
import "../sources/providers/index";
|
|
37
|
-
import { resolveAssetPath } from "../sources/resolve";
|
|
38
38
|
import { getActiveWorkflowRun } from "../workflows/runs";
|
|
39
|
+
import { getCurrentWorkflowScopeKey } from "../workflows/scope-key";
|
|
39
40
|
/**
|
|
40
41
|
* Show a wiki root (no page path) — returns the same payload as
|
|
41
42
|
* `akm wiki show <name>`.
|
|
@@ -128,7 +129,12 @@ export async function akmShowUnified(input) {
|
|
|
128
129
|
new NotFoundError(`Wiki not found: ${parsed.name}. Run \`akm wiki create ${parsed.name}\` to create it.`));
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
|
-
//
|
|
132
|
+
// Auto-index when stale so the index is current before lookup.
|
|
133
|
+
const allSources = resolveSourceEntries();
|
|
134
|
+
if (allSources.length > 0) {
|
|
135
|
+
await ensureIndex(allSources[0].path);
|
|
136
|
+
}
|
|
137
|
+
// Try local filesystem (FTS5 index lookup)
|
|
132
138
|
const result = await showLocal(input);
|
|
133
139
|
// Scope filter narrows resolution: if --scope was supplied, the asset's
|
|
134
140
|
// frontmatter scope must satisfy every supplied key. We re-read the file
|
|
@@ -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,43 +255,10 @@ function logShowEvent(ref, existingDb) {
|
|
|
215
255
|
closeDatabase(db);
|
|
216
256
|
}
|
|
217
257
|
}
|
|
218
|
-
catch {
|
|
219
|
-
/* fire-and-forget */
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Resolve an asset path to a file via:
|
|
224
|
-
* 1. `indexer.lookup(ref)` — the spec's primary path (§6.2).
|
|
225
|
-
* 2. On-disk type-dir traversal — fallback for files not yet indexed.
|
|
226
|
-
*
|
|
227
|
-
* Returns `undefined` if neither path finds a match.
|
|
228
|
-
*/
|
|
229
|
-
async function resolvePathViaIndexThenDisk(parsed, searchSourceDirs) {
|
|
230
|
-
// Step 1: indexer
|
|
231
|
-
try {
|
|
232
|
-
const entry = await lookup(parsed);
|
|
233
|
-
if (entry) {
|
|
234
|
-
return { assetPath: entry.filePath };
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
258
|
catch (err) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
// continue to disk fallback
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
// Step 2: on-disk type-dir traversal
|
|
244
|
-
let lastError;
|
|
245
|
-
for (const dir of searchSourceDirs) {
|
|
246
|
-
try {
|
|
247
|
-
const assetPath = await resolveAssetPath(dir, parsed.type, parsed.name);
|
|
248
|
-
return { assetPath, lastError };
|
|
249
|
-
}
|
|
250
|
-
catch (err) {
|
|
251
|
-
lastError = err instanceof Error ? err : new Error(String(err));
|
|
252
|
-
}
|
|
259
|
+
rethrowIfTestIsolationError(err);
|
|
260
|
+
/* fire-and-forget */
|
|
253
261
|
}
|
|
254
|
-
return lastError ? { assetPath: "", lastError } : undefined;
|
|
255
262
|
}
|
|
256
263
|
/** @internal Use akmShowUnified() for all external callers. */
|
|
257
264
|
export async function showLocal(input) {
|
|
@@ -273,13 +280,11 @@ export async function showLocal(input) {
|
|
|
273
280
|
}
|
|
274
281
|
}
|
|
275
282
|
if (!assetPath) {
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
lastError = resolved.lastError;
|
|
282
|
-
}
|
|
283
|
+
const resolvedAssetPath = await resolveAssetPath(parsed, {
|
|
284
|
+
stashDir: input.stashDir,
|
|
285
|
+
mode: "index-first",
|
|
286
|
+
});
|
|
287
|
+
assetPath = resolvedAssetPath ?? undefined;
|
|
283
288
|
}
|
|
284
289
|
if (!assetPath && parsed.origin && searchSources.length === 0) {
|
|
285
290
|
const installCmd = `akm add ${parsed.origin}`;
|
|
@@ -318,8 +323,24 @@ export async function showLocal(input) {
|
|
|
318
323
|
origin: source?.registryId ?? null,
|
|
319
324
|
editable,
|
|
320
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
|
+
})(),
|
|
321
342
|
};
|
|
322
|
-
const activeRun = getActiveWorkflowRun();
|
|
343
|
+
const activeRun = await getActiveWorkflowRun(getCurrentWorkflowScopeKey());
|
|
323
344
|
if (activeRun) {
|
|
324
345
|
fullResponse.activeRun = activeRun;
|
|
325
346
|
}
|
|
@@ -379,7 +400,7 @@ function buildSummaryResponse(full, assetPath) {
|
|
|
379
400
|
const textContent = full.content ?? full.template ?? full.prompt;
|
|
380
401
|
if (textContent && !description) {
|
|
381
402
|
const parsed = parseFrontmatter(textContent);
|
|
382
|
-
description =
|
|
403
|
+
description = asNonEmptyString(parsed.data.description);
|
|
383
404
|
}
|
|
384
405
|
}
|
|
385
406
|
const summary = {
|
|
@@ -396,3 +417,80 @@ function buildSummaryResponse(full, assetPath) {
|
|
|
396
417
|
};
|
|
397
418
|
return summary;
|
|
398
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" ||
|
|
451
|
+
arg === "-q" ||
|
|
452
|
+
arg === "--verbose" ||
|
|
453
|
+
arg === "--for-agent" ||
|
|
454
|
+
arg === "--for-agent=true") {
|
|
455
|
+
globalFlags.push(arg);
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (arg.startsWith("--format=") || arg.startsWith("--detail=") || arg.startsWith("--shape=")) {
|
|
459
|
+
globalFlags.push(arg);
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
if (arg === "--format" || arg === "--detail" || arg === "--shape") {
|
|
463
|
+
globalFlags.push(arg);
|
|
464
|
+
if (rest[i + 1] !== undefined) {
|
|
465
|
+
globalFlags.push(rest[i + 1]);
|
|
466
|
+
i++;
|
|
467
|
+
}
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
showArgs.push(arg);
|
|
471
|
+
}
|
|
472
|
+
// showArgs[0] = ref, showArgs[1] = potential view mode, showArgs[2..] = view params
|
|
473
|
+
const ref = showArgs[0];
|
|
474
|
+
const viewMode = showArgs[1];
|
|
475
|
+
if (!ref || !viewMode || !SHOW_VIEW_MODES.has(viewMode)) {
|
|
476
|
+
return argv;
|
|
477
|
+
}
|
|
478
|
+
const result = [...prefix, ref, "--akmView", viewMode];
|
|
479
|
+
if (viewMode === "section") {
|
|
480
|
+
// Next arg is the heading name; pass empty string when missing so the
|
|
481
|
+
// show handler can produce a clear "section not found" error.
|
|
482
|
+
const heading = showArgs[2] ?? "";
|
|
483
|
+
result.push("--akmHeading", heading);
|
|
484
|
+
}
|
|
485
|
+
else if (viewMode === "lines") {
|
|
486
|
+
// Next two args are start and end
|
|
487
|
+
const start = showArgs[2];
|
|
488
|
+
const end = showArgs[3];
|
|
489
|
+
if (start)
|
|
490
|
+
result.push("--akmStart", start);
|
|
491
|
+
if (end)
|
|
492
|
+
result.push("--akmEnd", end);
|
|
493
|
+
}
|
|
494
|
+
result.push(...globalFlags);
|
|
495
|
+
return result;
|
|
496
|
+
}
|
|
@@ -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
|
}
|