akm-cli 0.6.0-rc1 → 0.6.0-rc2
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 +21 -0
- package/README.md +9 -9
- package/dist/cli.js +181 -111
- package/dist/{completions.js → commands/completions.js} +1 -1
- package/dist/{config-cli.js → commands/config-cli.js} +109 -11
- package/dist/{curate.js → commands/curate.js} +8 -3
- package/dist/{info.js → commands/info.js} +15 -9
- package/dist/{init.js → commands/init.js} +4 -4
- package/dist/{install-audit.js → commands/install-audit.js} +4 -7
- package/dist/{installed-stashes.js → commands/installed-stashes.js} +77 -31
- package/dist/{migration-help.js → commands/migration-help.js} +2 -2
- package/dist/{registry-search.js → commands/registry-search.js} +8 -6
- package/dist/{remember.js → commands/remember.js} +55 -49
- package/dist/{stash-search.js → commands/search.js} +28 -69
- package/dist/{self-update.js → commands/self-update.js} +3 -3
- package/dist/{stash-show.js → commands/show.js} +103 -78
- package/dist/{stash-add.js → commands/source-add.js} +42 -32
- package/dist/{stash-clone.js → commands/source-clone.js} +12 -10
- package/dist/{stash-source-manage.js → commands/source-manage.js} +24 -24
- package/dist/{vault.js → commands/vault.js} +43 -0
- package/dist/{stash-ref.js → core/asset-ref.js} +4 -4
- package/dist/{asset-registry.js → core/asset-registry.js} +1 -1
- package/dist/{asset-spec.js → core/asset-spec.js} +1 -1
- package/dist/{config.js → core/config.js} +79 -31
- package/dist/core/errors.js +90 -0
- package/dist/{frontmatter.js → core/frontmatter.js} +5 -3
- package/dist/core/write-source.js +280 -0
- package/dist/{db-search.js → indexer/db-search.js} +25 -19
- package/dist/{db.js → indexer/db.js} +70 -47
- package/dist/{file-context.js → indexer/file-context.js} +3 -3
- package/dist/{indexer.js → indexer/indexer.js} +123 -31
- package/dist/{manifest.js → indexer/manifest.js} +10 -10
- package/dist/{matchers.js → indexer/matchers.js} +3 -6
- package/dist/{metadata.js → indexer/metadata.js} +9 -5
- package/dist/{search-source.js → indexer/search-source.js} +52 -41
- package/dist/{semantic-status.js → indexer/semantic-status.js} +2 -2
- package/dist/{walker.js → indexer/walker.js} +1 -1
- package/dist/{lockfile.js → integrations/lockfile.js} +1 -1
- package/dist/{llm-client.js → llm/client.js} +1 -1
- package/dist/{embedders → llm/embedders}/local.js +2 -2
- package/dist/{embedders → llm/embedders}/remote.js +1 -1
- package/dist/{embedders → llm/embedders}/types.js +1 -1
- package/dist/{metadata-enhance.js → llm/metadata-enhance.js} +2 -2
- package/dist/{cli-hints.js → output/cli-hints.js} +1 -0
- package/dist/{output-context.js → output/context.js} +21 -3
- package/dist/{renderers.js → output/renderers.js} +9 -65
- package/dist/{output-shapes.js → output/shapes.js} +18 -4
- package/dist/{output-text.js → output/text.js} +1 -1
- package/dist/{registry-build-index.js → registry/build-index.js} +16 -7
- package/dist/{create-provider-registry.js → registry/create-provider-registry.js} +6 -2
- package/dist/registry/factory.js +33 -0
- package/dist/{origin-resolve.js → registry/origin-resolve.js} +1 -1
- package/dist/{providers → registry/providers}/index.js +1 -1
- package/dist/{providers → registry/providers}/skills-sh.js +59 -3
- package/dist/{providers → registry/providers}/static-index.js +80 -12
- package/dist/registry/providers/types.js +25 -0
- package/dist/{registry-resolve.js → registry/resolve.js} +3 -3
- package/dist/{detect.js → setup/detect.js} +0 -27
- package/dist/{ripgrep-install.js → setup/ripgrep-install.js} +1 -1
- package/dist/{ripgrep-resolve.js → setup/ripgrep-resolve.js} +2 -2
- package/dist/{setup.js → setup/setup.js} +16 -56
- package/dist/{stash-include.js → sources/include.js} +1 -1
- package/dist/sources/provider-factory.js +36 -0
- package/dist/sources/provider.js +21 -0
- package/dist/sources/providers/filesystem.js +35 -0
- package/dist/{stash-providers → sources/providers}/git.js +53 -64
- package/dist/{stash-providers → sources/providers}/index.js +3 -4
- package/dist/sources/providers/install-types.js +14 -0
- package/dist/{stash-providers → sources/providers}/npm.js +42 -41
- package/dist/{stash-providers → sources/providers}/provider-utils.js +3 -3
- package/dist/{stash-providers → sources/providers}/sync-from-ref.js +2 -2
- package/dist/{stash-providers → sources/providers}/tar-utils.js +11 -8
- package/dist/{stash-providers → sources/providers}/website.js +29 -65
- package/dist/{stash-resolve.js → sources/resolve.js} +8 -7
- package/dist/{wiki.js → wiki/wiki.js} +12 -11
- package/dist/{workflow-authoring.js → workflows/authoring.js} +37 -14
- package/dist/{workflow-cli.js → workflows/cli.js} +2 -1
- package/dist/{workflow-db.js → workflows/db.js} +1 -1
- package/dist/workflows/document-cache.js +20 -0
- package/dist/workflows/parser.js +379 -0
- package/dist/workflows/renderer.js +78 -0
- package/dist/{workflow-runs.js → workflows/runs.js} +72 -28
- package/dist/workflows/schema.js +11 -0
- package/dist/workflows/validator.js +48 -0
- package/docs/migration/release-notes/0.6.0.md +69 -23
- package/package.json +1 -1
- package/dist/errors.js +0 -45
- package/dist/llm.js +0 -16
- package/dist/registry-factory.js +0 -19
- package/dist/ripgrep.js +0 -2
- package/dist/stash-provider-factory.js +0 -35
- package/dist/stash-provider.js +0 -3
- package/dist/stash-providers/filesystem.js +0 -71
- package/dist/stash-providers/openviking.js +0 -348
- package/dist/stash-types.js +0 -1
- package/dist/workflow-markdown.js +0 -260
- /package/dist/{common.js → core/common.js} +0 -0
- /package/dist/{markdown.js → core/markdown.js} +0 -0
- /package/dist/{paths.js → core/paths.js} +0 -0
- /package/dist/{warn.js → core/warn.js} +0 -0
- /package/dist/{search-fields.js → indexer/search-fields.js} +0 -0
- /package/dist/{usage-events.js → indexer/usage-events.js} +0 -0
- /package/dist/{github.js → integrations/github.js} +0 -0
- /package/dist/{embedder.js → llm/embedder.js} +0 -0
- /package/dist/{embedders → llm/embedders}/cache.js +0 -0
- /package/dist/{registry-provider.js → registry/types.js} +0 -0
- /package/dist/{setup-steps.js → setup/steps.js} +0 -0
- /package/dist/{registry-types.js → sources/types.js} +0 -0
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { isHttpUrl, resolveStashDir } from "
|
|
4
|
-
import { loadConfig, loadUserConfig, saveConfig } from "
|
|
5
|
-
import { UsageError } from "
|
|
6
|
-
import {
|
|
3
|
+
import { isHttpUrl, resolveStashDir } from "../core/common";
|
|
4
|
+
import { loadConfig, loadUserConfig, saveConfig } from "../core/config";
|
|
5
|
+
import { UsageError } from "../core/errors";
|
|
6
|
+
import { warn } from "../core/warn";
|
|
7
|
+
import { akmIndex } from "../indexer/indexer";
|
|
8
|
+
import { upsertLockEntry } from "../integrations/lockfile";
|
|
9
|
+
import { parseRegistryRef } from "../registry/resolve";
|
|
10
|
+
import { detectStashRoot } from "../sources/providers/provider-utils";
|
|
11
|
+
import { syncFromRef } from "../sources/providers/sync-from-ref";
|
|
12
|
+
import { ensureWebsiteMirror, validateWebsiteInputUrl } from "../sources/providers/website";
|
|
13
|
+
import { ensureWikiNameAvailable, validateWikiName } from "../wiki/wiki";
|
|
7
14
|
import { auditInstallCandidate, deriveRegistryLabels, enforceRegistryInstallPolicy, formatInstallAuditFailure, } from "./install-audit";
|
|
8
|
-
import { upsertLockEntry } from "./lockfile";
|
|
9
|
-
import { parseRegistryRef } from "./registry-resolve";
|
|
10
|
-
import { detectStashRoot } from "./stash-providers/provider-utils";
|
|
11
|
-
import { syncFromRef } from "./stash-providers/sync-from-ref";
|
|
12
|
-
import { ensureWebsiteMirror, validateWebsiteInputUrl } from "./stash-providers/website";
|
|
13
|
-
import { warn } from "./warn";
|
|
14
|
-
import { ensureWikiNameAvailable, validateWikiName } from "./wiki";
|
|
15
15
|
const VALID_OVERRIDE_TYPES = new Set(["wiki"]);
|
|
16
16
|
export async function akmAdd(input) {
|
|
17
17
|
const ref = input.ref.trim();
|
|
@@ -32,7 +32,7 @@ export async function akmAdd(input) {
|
|
|
32
32
|
}
|
|
33
33
|
const stashDir = resolveStashDir();
|
|
34
34
|
if (shouldAddAsWebsiteUrl(ref)) {
|
|
35
|
-
return
|
|
35
|
+
return addWebsiteSource(ref, stashDir, input.name ?? wikiName, input.options, wikiName);
|
|
36
36
|
}
|
|
37
37
|
// Detect local directory refs and route them to stashes[] instead of installed[]
|
|
38
38
|
try {
|
|
@@ -41,7 +41,7 @@ export async function akmAdd(input) {
|
|
|
41
41
|
if (input.trustThisInstall) {
|
|
42
42
|
warn("--trust has no effect on local directory sources; the install audit is not run for local paths.");
|
|
43
43
|
}
|
|
44
|
-
return
|
|
44
|
+
return addLocalSource(ref, parsed.sourcePath, stashDir, wikiName, input.name);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
catch {
|
|
@@ -67,29 +67,39 @@ export async function registerWikiSource(input) {
|
|
|
67
67
|
* Add a local directory as a filesystem stash source.
|
|
68
68
|
* Creates a stashes[] entry instead of an installed[] entry.
|
|
69
69
|
*/
|
|
70
|
-
async function
|
|
70
|
+
async function addLocalSource(ref, sourcePath, stashDir, wikiName, explicitName) {
|
|
71
71
|
const stashRoot = detectStashRoot(sourcePath);
|
|
72
72
|
const resolvedPath = path.resolve(stashRoot);
|
|
73
73
|
const config = loadUserConfig();
|
|
74
|
-
//
|
|
75
|
-
const
|
|
76
|
-
|
|
74
|
+
// Derive the canonical name: explicit --name wins, then wiki name, then readable path.
|
|
75
|
+
const derivedName = explicitName ?? wikiName ?? toReadableId(resolvedPath);
|
|
76
|
+
// Check for duplicates in sources[]
|
|
77
|
+
const sources = [...(config.sources ?? config.stashes ?? [])];
|
|
78
|
+
const existing = sources.find((s) => s.type === "filesystem" && s.path && path.resolve(s.path) === resolvedPath);
|
|
77
79
|
let persistedEntry;
|
|
78
80
|
if (!existing) {
|
|
79
81
|
persistedEntry = {
|
|
80
82
|
type: "filesystem",
|
|
81
83
|
path: resolvedPath,
|
|
82
|
-
name:
|
|
84
|
+
name: derivedName,
|
|
83
85
|
...(wikiName ? { wikiName } : {}),
|
|
84
86
|
};
|
|
85
|
-
|
|
86
|
-
saveConfig({ ...config, stashes });
|
|
87
|
+
sources.push(persistedEntry);
|
|
88
|
+
saveConfig({ ...config, sources, stashes: undefined });
|
|
87
89
|
}
|
|
88
90
|
else {
|
|
91
|
+
let changed = false;
|
|
92
|
+
// If --name was explicitly supplied, update the persisted name.
|
|
93
|
+
if (explicitName && existing.name !== explicitName) {
|
|
94
|
+
existing.name = explicitName;
|
|
95
|
+
changed = true;
|
|
96
|
+
}
|
|
89
97
|
if (wikiName && existing.wikiName !== wikiName) {
|
|
90
98
|
existing.wikiName = wikiName;
|
|
91
|
-
|
|
99
|
+
changed = true;
|
|
92
100
|
}
|
|
101
|
+
if (changed)
|
|
102
|
+
saveConfig({ ...config, sources, stashes: undefined });
|
|
93
103
|
persistedEntry = existing;
|
|
94
104
|
}
|
|
95
105
|
const index = await akmIndex({ stashDir });
|
|
@@ -98,7 +108,7 @@ async function addLocalStashSource(ref, sourcePath, stashDir, wikiName) {
|
|
|
98
108
|
schemaVersion: 1,
|
|
99
109
|
stashDir,
|
|
100
110
|
ref: wikiName ?? ref,
|
|
101
|
-
|
|
111
|
+
sourceAdded: {
|
|
102
112
|
type: "filesystem",
|
|
103
113
|
path: resolvedPath,
|
|
104
114
|
name: persistedEntry.name ?? toReadableId(resolvedPath),
|
|
@@ -106,7 +116,7 @@ async function addLocalStashSource(ref, sourcePath, stashDir, wikiName) {
|
|
|
106
116
|
...(persistedEntry.wikiName ? { wiki: persistedEntry.wikiName } : {}),
|
|
107
117
|
},
|
|
108
118
|
config: {
|
|
109
|
-
|
|
119
|
+
sourceCount: (updatedConfig.sources ?? updatedConfig.stashes ?? []).length,
|
|
110
120
|
installedKitCount: updatedConfig.installed?.length ?? 0,
|
|
111
121
|
},
|
|
112
122
|
index: {
|
|
@@ -118,11 +128,11 @@ async function addLocalStashSource(ref, sourcePath, stashDir, wikiName) {
|
|
|
118
128
|
},
|
|
119
129
|
};
|
|
120
130
|
}
|
|
121
|
-
async function
|
|
131
|
+
async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
|
|
122
132
|
const normalizedUrl = validateWebsiteInputUrl(ref);
|
|
123
133
|
const config = loadUserConfig();
|
|
124
|
-
const
|
|
125
|
-
let entry =
|
|
134
|
+
const sources = [...(config.sources ?? config.stashes ?? [])];
|
|
135
|
+
let entry = sources.find((stash) => stash.type === "website" && stash.url === normalizedUrl);
|
|
126
136
|
if (!entry) {
|
|
127
137
|
entry = {
|
|
128
138
|
type: "website",
|
|
@@ -131,8 +141,8 @@ async function addWebsiteStashSource(ref, stashDir, name, options, wikiName) {
|
|
|
131
141
|
...(options && Object.keys(options).length > 0 ? { options } : {}),
|
|
132
142
|
...(wikiName ? { wikiName } : {}),
|
|
133
143
|
};
|
|
134
|
-
|
|
135
|
-
saveConfig({ ...config, stashes });
|
|
144
|
+
sources.push(entry);
|
|
145
|
+
saveConfig({ ...config, sources, stashes: undefined });
|
|
136
146
|
}
|
|
137
147
|
else {
|
|
138
148
|
let changed = false;
|
|
@@ -145,7 +155,7 @@ async function addWebsiteStashSource(ref, stashDir, name, options, wikiName) {
|
|
|
145
155
|
changed = true;
|
|
146
156
|
}
|
|
147
157
|
if (changed)
|
|
148
|
-
saveConfig({ ...config, stashes });
|
|
158
|
+
saveConfig({ ...config, sources, stashes: undefined });
|
|
149
159
|
}
|
|
150
160
|
const cachePaths = await ensureWebsiteMirror(entry, { requireStashDir: true });
|
|
151
161
|
const index = await akmIndex({ stashDir });
|
|
@@ -154,7 +164,7 @@ async function addWebsiteStashSource(ref, stashDir, name, options, wikiName) {
|
|
|
154
164
|
schemaVersion: 1,
|
|
155
165
|
stashDir,
|
|
156
166
|
ref: wikiName ?? ref,
|
|
157
|
-
|
|
167
|
+
sourceAdded: {
|
|
158
168
|
type: "website",
|
|
159
169
|
url: normalizedUrl,
|
|
160
170
|
name: entry.name,
|
|
@@ -162,7 +172,7 @@ async function addWebsiteStashSource(ref, stashDir, name, options, wikiName) {
|
|
|
162
172
|
...(entry.wikiName ? { wiki: entry.wikiName } : {}),
|
|
163
173
|
},
|
|
164
174
|
config: {
|
|
165
|
-
|
|
175
|
+
sourceCount: (updatedConfig.sources ?? updatedConfig.stashes ?? []).length,
|
|
166
176
|
installedKitCount: updatedConfig.installed?.length ?? 0,
|
|
167
177
|
},
|
|
168
178
|
index: {
|
|
@@ -254,7 +264,7 @@ async function addRegistryStash(ref, stashDir, trustThisInstall, writable, wikiN
|
|
|
254
264
|
audit,
|
|
255
265
|
},
|
|
256
266
|
config: {
|
|
257
|
-
|
|
267
|
+
sourceCount: (updatedConfig.sources ?? updatedConfig.stashes ?? []).length,
|
|
258
268
|
installedKitCount: updatedConfig.installed?.length ?? 0,
|
|
259
269
|
},
|
|
260
270
|
index: {
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { findSourceForPath, getPrimarySource,
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { resolveAssetPath } from "
|
|
3
|
+
import { makeAssetRef, parseAssetRef } from "../core/asset-ref";
|
|
4
|
+
import { TYPE_DIRS } from "../core/asset-spec";
|
|
5
|
+
import { NotFoundError, UsageError } from "../core/errors";
|
|
6
|
+
import { findSourceForPath, getPrimarySource, resolveSourceEntries } from "../indexer/search-source";
|
|
7
|
+
import { isRemoteOrigin, resolveSourcesForOrigin } from "../registry/origin-resolve";
|
|
8
|
+
import { syncFromRef } from "../sources/providers/sync-from-ref";
|
|
9
|
+
import { resolveAssetPath } from "../sources/resolve";
|
|
10
10
|
export async function akmClone(options) {
|
|
11
11
|
const parsed = parseAssetRef(options.sourceRef);
|
|
12
12
|
// When --dest is provided, the working stash is optional
|
|
13
13
|
let allSources;
|
|
14
14
|
try {
|
|
15
|
-
allSources =
|
|
15
|
+
allSources = resolveSourceEntries();
|
|
16
16
|
}
|
|
17
17
|
catch (err) {
|
|
18
18
|
if (options.dest) {
|
|
@@ -56,8 +56,10 @@ export async function akmClone(options) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
if (!sourcePath) {
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
if (remoteFetched) {
|
|
60
|
+
throw new NotFoundError(`Source asset not found for ref: ${options.sourceRef} (remote package fetched but asset not found inside it)`, "ASSET_NOT_FOUND", "The remote package was fetched but doesn't contain the requested asset. Check the asset name and type.");
|
|
61
|
+
}
|
|
62
|
+
throw lastError ?? new NotFoundError(`Source asset not found for ref: ${options.sourceRef}`, "ASSET_NOT_FOUND");
|
|
61
63
|
}
|
|
62
64
|
const sourceSource = findSourceForPath(sourcePath, allSources);
|
|
63
65
|
const destName = options.newName ?? parsed.name;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { loadConfig, loadUserConfig, saveConfig } from "
|
|
3
|
-
import { UsageError } from "
|
|
4
|
-
import {
|
|
2
|
+
import { loadConfig, loadUserConfig, saveConfig } from "../core/config";
|
|
3
|
+
import { UsageError } from "../core/errors";
|
|
4
|
+
import { resolveSourceEntries } from "../indexer/search-source";
|
|
5
5
|
// ── Operations ──────────────────────────────────────────────────────────────
|
|
6
6
|
/**
|
|
7
7
|
* Add a stash source (filesystem path or remote provider URL) to config.
|
|
8
8
|
*
|
|
9
9
|
* Filesystem paths are auto-detected when `target` does not start with
|
|
10
10
|
* `http://` or `https://`. URL sources require a `providerType` option
|
|
11
|
-
* (e.g. "
|
|
11
|
+
* (e.g. "website", "git").
|
|
12
12
|
*/
|
|
13
13
|
export function addStash(opts) {
|
|
14
14
|
const { target, name, providerType, options: providerOptions, writable } = opts;
|
|
15
15
|
const config = loadUserConfig();
|
|
16
|
-
const
|
|
16
|
+
const sources = [...(config.sources ?? config.stashes ?? [])];
|
|
17
17
|
const isRemoteUrl = target.startsWith("http://") ||
|
|
18
18
|
target.startsWith("https://") ||
|
|
19
19
|
target.startsWith("git@") ||
|
|
@@ -22,11 +22,11 @@ export function addStash(opts) {
|
|
|
22
22
|
let entry;
|
|
23
23
|
if (isRemoteUrl) {
|
|
24
24
|
if (!providerType) {
|
|
25
|
-
throw new UsageError("--provider is required for URL sources (e.g. --provider
|
|
25
|
+
throw new UsageError("--provider is required for URL sources (e.g. --provider git --provider website)");
|
|
26
26
|
}
|
|
27
27
|
// Deduplicate by URL
|
|
28
|
-
if (
|
|
29
|
-
return {
|
|
28
|
+
if (sources.some((s) => s.url === target)) {
|
|
29
|
+
return { sources, added: false, message: "Source URL already configured" };
|
|
30
30
|
}
|
|
31
31
|
entry = { type: providerType, url: target };
|
|
32
32
|
if (name)
|
|
@@ -39,16 +39,16 @@ export function addStash(opts) {
|
|
|
39
39
|
else {
|
|
40
40
|
// Filesystem path
|
|
41
41
|
const resolvedPath = path.resolve(target);
|
|
42
|
-
if (
|
|
43
|
-
return {
|
|
42
|
+
if (sources.some((s) => s.path && path.resolve(s.path) === resolvedPath)) {
|
|
43
|
+
return { sources, added: false, message: "Source path already configured" };
|
|
44
44
|
}
|
|
45
45
|
entry = { type: "filesystem", path: resolvedPath };
|
|
46
46
|
if (name)
|
|
47
47
|
entry.name = name;
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
saveConfig({ ...config, stashes });
|
|
51
|
-
return {
|
|
49
|
+
sources.push(entry);
|
|
50
|
+
saveConfig({ ...config, sources, stashes: undefined });
|
|
51
|
+
return { sources, added: true, entry };
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
54
|
* Remove a stash source by URL, path, or name.
|
|
@@ -56,7 +56,7 @@ export function addStash(opts) {
|
|
|
56
56
|
*/
|
|
57
57
|
export function removeStash(target) {
|
|
58
58
|
const config = loadUserConfig();
|
|
59
|
-
const
|
|
59
|
+
const sources = [...(config.sources ?? config.stashes ?? [])];
|
|
60
60
|
const isUrl = target.startsWith("http://") ||
|
|
61
61
|
target.startsWith("https://") ||
|
|
62
62
|
target.startsWith("git@") ||
|
|
@@ -66,27 +66,27 @@ export function removeStash(target) {
|
|
|
66
66
|
// Try URL match first, then path, then name (most specific → least specific)
|
|
67
67
|
let idx = -1;
|
|
68
68
|
if (isUrl) {
|
|
69
|
-
idx =
|
|
69
|
+
idx = sources.findIndex((s) => s.url === target);
|
|
70
70
|
}
|
|
71
71
|
if (idx === -1 && resolvedPath) {
|
|
72
|
-
idx =
|
|
72
|
+
idx = sources.findIndex((s) => s.path && path.resolve(s.path) === resolvedPath);
|
|
73
73
|
}
|
|
74
74
|
if (idx === -1) {
|
|
75
|
-
idx =
|
|
75
|
+
idx = sources.findIndex((s) => s.name === target);
|
|
76
76
|
}
|
|
77
77
|
if (idx === -1) {
|
|
78
|
-
return {
|
|
78
|
+
return { sources, removed: false, message: "No matching source found" };
|
|
79
79
|
}
|
|
80
|
-
const removed =
|
|
81
|
-
saveConfig({ ...config, stashes });
|
|
82
|
-
return {
|
|
80
|
+
const removed = sources.splice(idx, 1)[0];
|
|
81
|
+
saveConfig({ ...config, sources, stashes: undefined });
|
|
82
|
+
return { sources, removed: true, entry: removed };
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
85
|
* List all stash sources (local filesystem + configured stashes).
|
|
86
86
|
*/
|
|
87
87
|
export function listStashes() {
|
|
88
88
|
const config = loadConfig();
|
|
89
|
-
const localSources =
|
|
90
|
-
const
|
|
91
|
-
return { localSources,
|
|
89
|
+
const localSources = resolveSourceEntries();
|
|
90
|
+
const sources = config.sources ?? config.stashes ?? [];
|
|
91
|
+
return { localSources, sources };
|
|
92
92
|
}
|
|
@@ -66,6 +66,49 @@ export function listKeys(vaultPath) {
|
|
|
66
66
|
const text = fs.readFileSync(vaultPath, "utf8");
|
|
67
67
|
return { keys: scanKeys(text), comments: scanComments(text) };
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Return structured `entries` pairing each key with the nearest preceding
|
|
71
|
+
* comment line (if any). This replaces the parallel `keys[]` + `comments[]`
|
|
72
|
+
* shape used internally by `listKeys` with a single merged array, which is
|
|
73
|
+
* easier for callers to consume (QA #35).
|
|
74
|
+
*
|
|
75
|
+
* Values are never included — the same privacy guarantee as `listKeys`.
|
|
76
|
+
*/
|
|
77
|
+
export function listEntries(vaultPath) {
|
|
78
|
+
if (!fs.existsSync(vaultPath))
|
|
79
|
+
return [];
|
|
80
|
+
const text = fs.readFileSync(vaultPath, "utf8");
|
|
81
|
+
const lines = text.split(/\r?\n/);
|
|
82
|
+
const seen = new Set();
|
|
83
|
+
const entries = [];
|
|
84
|
+
let pendingComment;
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
const trimmed = line.trimStart();
|
|
87
|
+
if (trimmed.startsWith("#")) {
|
|
88
|
+
// Capture the most recent comment before a key
|
|
89
|
+
pendingComment = trimmed.slice(1).trimStart() || undefined;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const m = line.match(ASSIGN_RE);
|
|
93
|
+
if (m) {
|
|
94
|
+
const key = m[1];
|
|
95
|
+
if (!seen.has(key)) {
|
|
96
|
+
seen.add(key);
|
|
97
|
+
const entry = { key };
|
|
98
|
+
if (pendingComment)
|
|
99
|
+
entry.comment = pendingComment;
|
|
100
|
+
entries.push(entry);
|
|
101
|
+
}
|
|
102
|
+
pendingComment = undefined;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Any non-comment, non-assignment line (including blank lines)
|
|
106
|
+
// breaks "nearest preceding comment line" association.
|
|
107
|
+
pendingComment = undefined;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return entries;
|
|
111
|
+
}
|
|
69
112
|
/**
|
|
70
113
|
* Read all KEY=value pairs from a vault file. Intended for programmatic
|
|
71
114
|
* callers that need to inject values into a process environment. Callers
|
|
@@ -30,7 +30,7 @@ export function makeAssetRef(type, name, origin) {
|
|
|
30
30
|
export function parseAssetRef(ref) {
|
|
31
31
|
const trimmed = ref.trim();
|
|
32
32
|
if (!trimmed)
|
|
33
|
-
throw new UsageError("Empty ref.");
|
|
33
|
+
throw new UsageError("Empty ref.", "MISSING_REQUIRED_ARGUMENT");
|
|
34
34
|
let origin;
|
|
35
35
|
let body = trimmed;
|
|
36
36
|
const boundary = trimmed.indexOf("//");
|
|
@@ -38,16 +38,16 @@ export function parseAssetRef(ref) {
|
|
|
38
38
|
origin = trimmed.slice(0, boundary);
|
|
39
39
|
body = trimmed.slice(boundary + 2);
|
|
40
40
|
if (!origin)
|
|
41
|
-
throw new UsageError("Empty origin in ref.");
|
|
41
|
+
throw new UsageError("Empty origin in ref.", "MISSING_REQUIRED_ARGUMENT");
|
|
42
42
|
}
|
|
43
43
|
const colon = body.indexOf(":");
|
|
44
44
|
if (colon <= 0) {
|
|
45
|
-
throw new UsageError(`Invalid ref "${trimmed}". Expected [origin//]type:name
|
|
45
|
+
throw new UsageError(`Invalid ref "${trimmed}". Expected [origin//]type:name, e.g. skill:deploy or knowledge:guide.md`, "MISSING_REQUIRED_ARGUMENT");
|
|
46
46
|
}
|
|
47
47
|
const rawType = body.slice(0, colon);
|
|
48
48
|
const rawName = body.slice(colon + 1);
|
|
49
49
|
if (!isAssetType(rawType)) {
|
|
50
|
-
throw new UsageError(`Invalid asset type: "${rawType}"
|
|
50
|
+
throw new UsageError(`Invalid asset type: "${rawType}".`, "MISSING_REQUIRED_ARGUMENT");
|
|
51
51
|
}
|
|
52
52
|
validateName(rawName);
|
|
53
53
|
const name = normalizeName(rawName);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* `db-search.ts` import from, eliminating the import-order dependency
|
|
11
11
|
* entirely.
|
|
12
12
|
*/
|
|
13
|
-
import { buildWorkflowAction } from "
|
|
13
|
+
import { buildWorkflowAction } from "../output/renderers";
|
|
14
14
|
/** Map asset types to their primary renderer names. */
|
|
15
15
|
export const TYPE_TO_RENDERER = {
|
|
16
16
|
script: "script-source",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { buildWorkflowAction } from "../output/renderers";
|
|
2
3
|
import { registerActionBuilder, registerTypeRenderer } from "./asset-registry";
|
|
3
4
|
import { toPosix } from "./common";
|
|
4
|
-
import { buildWorkflowAction } from "./renderers";
|
|
5
5
|
const markdownSpec = {
|
|
6
6
|
isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".md",
|
|
7
7
|
toCanonicalName: (typeRoot, filePath) => {
|