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,10 +1,10 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { deriveCanonicalAssetName, deriveCanonicalAssetNameFromStashRoot, isRelevantAssetFile } from "
|
|
4
|
-
import { isAssetType } from "
|
|
3
|
+
import { deriveCanonicalAssetName, deriveCanonicalAssetNameFromStashRoot, isRelevantAssetFile, } from "../core/asset-spec";
|
|
4
|
+
import { isAssetType } from "../core/common";
|
|
5
|
+
import { parseFrontmatter, toStringOrUndefined } from "../core/frontmatter";
|
|
6
|
+
import { warn } from "../core/warn";
|
|
5
7
|
import { buildFileContext, buildRenderContext, getRenderer, runMatchers } from "./file-context";
|
|
6
|
-
import { parseFrontmatter, toStringOrUndefined } from "./frontmatter";
|
|
7
|
-
import { warn } from "./warn";
|
|
8
8
|
// ── Load / Write ────────────────────────────────────────────────────────────
|
|
9
9
|
const STASH_FILENAME = ".stash.json";
|
|
10
10
|
export function stashFilePath(dirPath) {
|
|
@@ -568,7 +568,11 @@ export async function generateMetadataFlat(stashRoot, files) {
|
|
|
568
568
|
}
|
|
569
569
|
function buildMetadataSkipWarning(filePath, assetType, error) {
|
|
570
570
|
const detail = error instanceof Error ? error.message : String(error);
|
|
571
|
-
|
|
571
|
+
// Workflow errors are already multi-line `path:line — message` blocks; print
|
|
572
|
+
// them as-is so the author sees a flat list without a redundant prefix.
|
|
573
|
+
const warning = assetType === "workflow"
|
|
574
|
+
? `Skipped workflow ${filePath}:\n${detail}`
|
|
575
|
+
: `Skipped malformed ${assetType} asset at ${filePath}: ${detail}`;
|
|
572
576
|
warn(warning);
|
|
573
577
|
return warning;
|
|
574
578
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { resolveStashDir } from "
|
|
4
|
-
import { loadConfig } from "
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { resolveStashDir } from "../core/common";
|
|
4
|
+
import { loadConfig } from "../core/config";
|
|
5
|
+
import { resolveSourceProviderFactory } from "../sources/provider-factory";
|
|
6
|
+
// Eager side-effect imports so all built-in source providers self-register
|
|
7
|
+
// before resolveEntryContentDir() runs.
|
|
8
|
+
import "../sources/providers/index";
|
|
9
|
+
import { warn } from "../core/warn";
|
|
10
|
+
import { ensureGitMirror, getCachePaths, parseGitRepoUrl } from "../sources/providers/git";
|
|
11
|
+
import { ensureWebsiteMirror } from "../sources/providers/website";
|
|
8
12
|
// Legacy "context-hub" / "github" type aliases are normalized to "git" at
|
|
9
13
|
// config-load time (see src/config.ts), so this set only contains the canonical
|
|
10
14
|
// type.
|
|
@@ -17,7 +21,7 @@ const GIT_STASH_TYPES = new Set(["git"]);
|
|
|
17
21
|
* 1. The primary stash directory (the entry marked `primary: true`, or the
|
|
18
22
|
* legacy top-level `stashDir`). Always emitted, even when the directory
|
|
19
23
|
* does not yet exist on disk, so callers can use it as the clone target.
|
|
20
|
-
* 2. Each entry in `config.stashes[]` (in declared order), excluding the
|
|
24
|
+
* 2. Each entry in `config.sources ?? config.stashes[]` (in declared order), excluding the
|
|
21
25
|
* one already emitted as the primary.
|
|
22
26
|
* 3. Each entry in `config.installed[]` (registry-managed stashes).
|
|
23
27
|
*
|
|
@@ -25,7 +29,7 @@ const GIT_STASH_TYPES = new Set(["git"]);
|
|
|
25
29
|
* for each provider kind. Disabled entries (`enabled: false`) and entries
|
|
26
30
|
* whose disk path doesn't exist are filtered after deduplication.
|
|
27
31
|
*/
|
|
28
|
-
export function
|
|
32
|
+
export function resolveSourceEntries(overrideStashDir, existingConfig) {
|
|
29
33
|
const stashDir = overrideStashDir ?? resolveStashDir();
|
|
30
34
|
const config = existingConfig ?? loadConfig();
|
|
31
35
|
const sources = [{ path: stashDir }];
|
|
@@ -49,7 +53,7 @@ export function resolveStashSources(overrideStashDir, existingConfig) {
|
|
|
49
53
|
// (1) + (2) Single pass over declared stashes — primary first if present,
|
|
50
54
|
// then the rest in declared order. The primary's directory is already
|
|
51
55
|
// injected as `sources[0]` above, so we only need to dedupe the source set.
|
|
52
|
-
const stashes = config.stashes ?? [];
|
|
56
|
+
const stashes = config.sources ?? config.stashes ?? [];
|
|
53
57
|
const primaryIdx = stashes.findIndex((entry) => entry.primary === true);
|
|
54
58
|
const ordered = [];
|
|
55
59
|
if (primaryIdx >= 0) {
|
|
@@ -78,43 +82,54 @@ export function resolveStashSources(overrideStashDir, existingConfig) {
|
|
|
78
82
|
}
|
|
79
83
|
/**
|
|
80
84
|
* Resolve the content directory the indexer should walk for a given config
|
|
81
|
-
* entry. Returns `undefined` if the entry has no walkable content
|
|
82
|
-
*
|
|
85
|
+
* entry. Returns `undefined` if the entry has no walkable content
|
|
86
|
+
* so the caller can skip it.
|
|
87
|
+
*
|
|
88
|
+
* Single source of truth: each provider owns its own path. We instantiate the
|
|
89
|
+
* registered {@link import("../sources/provider").SourceProvider} for the entry
|
|
90
|
+
* and call `provider.path()`. This replaces the old per-kind switch ladder
|
|
91
|
+
* (filesystem path / git cache / website cache) that lived here in 0.6.0 —
|
|
92
|
+
* see spec §10 step 4 and §7 "Removed from 0.6.0".
|
|
93
|
+
*
|
|
94
|
+
* The git case still does one extra step: the provider returns the cloned
|
|
95
|
+
* repo dir, but the indexer walks the `content/` subdirectory inside it.
|
|
96
|
+
* That convention is part of the akm content layout, not a provider concern,
|
|
97
|
+
* so it stays here.
|
|
83
98
|
*/
|
|
84
99
|
function resolveEntryContentDir(entry) {
|
|
85
|
-
|
|
86
|
-
|
|
100
|
+
const factory = resolveSourceProviderFactory(entry.type);
|
|
101
|
+
if (!factory)
|
|
102
|
+
return undefined;
|
|
103
|
+
let provider;
|
|
104
|
+
try {
|
|
105
|
+
provider = factory(entry);
|
|
87
106
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const cachePaths = getCachePaths(repo.canonicalUrl);
|
|
92
|
-
// The content/ subdirectory inside the extracted repo is the actual
|
|
93
|
-
// stash root containing DOC.md / SKILL.md files that the walker indexes.
|
|
94
|
-
return path.join(cachePaths.repoDir, "content");
|
|
95
|
-
}
|
|
96
|
-
catch (err) {
|
|
97
|
-
warn(`Warning: failed to resolve git stash cache for "${entry.url}": ${err instanceof Error ? err.message : String(err)}`);
|
|
98
|
-
return undefined;
|
|
99
|
-
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
warn(`Warning: failed to construct ${entry.type} source provider for "${entry.name ?? entry.url ?? entry.path}": ${err instanceof Error ? err.message : String(err)}`);
|
|
109
|
+
return undefined;
|
|
100
110
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
catch (err) {
|
|
106
|
-
warn(`Warning: failed to resolve website stash cache for "${entry.url}": ${err instanceof Error ? err.message : String(err)}`);
|
|
107
|
-
return undefined;
|
|
108
|
-
}
|
|
111
|
+
let dir;
|
|
112
|
+
try {
|
|
113
|
+
dir = provider.path();
|
|
109
114
|
}
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
catch (err) {
|
|
116
|
+
warn(`Warning: failed to resolve ${entry.type} source path for "${entry.name ?? entry.url ?? entry.path}": ${err instanceof Error ? err.message : String(err)}`);
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
// Git providers expose the cloned repo root as their path. The akm content
|
|
120
|
+
// layout puts indexable files under `<repo>/content/`, so the walker needs
|
|
121
|
+
// that subdirectory. This is a content-layout convention, not a provider
|
|
122
|
+
// capability — keep it here.
|
|
123
|
+
if (GIT_STASH_TYPES.has(entry.type)) {
|
|
124
|
+
return path.join(dir, "content");
|
|
125
|
+
}
|
|
126
|
+
return dir;
|
|
112
127
|
}
|
|
113
128
|
/**
|
|
114
129
|
* Convenience: returns just the directory paths, preserving priority order.
|
|
115
130
|
*/
|
|
116
131
|
export function resolveAllStashDirs(overrideStashDir) {
|
|
117
|
-
return
|
|
132
|
+
return resolveSourceEntries(overrideStashDir).map((s) => s.path);
|
|
118
133
|
}
|
|
119
134
|
/**
|
|
120
135
|
* Find which source a file path belongs to.
|
|
@@ -204,10 +219,10 @@ function isValidDirectory(dir) {
|
|
|
204
219
|
/**
|
|
205
220
|
* Ensure all cache-backed stash providers are refreshed so their cache
|
|
206
221
|
* directories exist on disk. Must be called (async) before
|
|
207
|
-
* `
|
|
222
|
+
* `resolveSourceEntries()` so the content directories pass the
|
|
208
223
|
* `isValidDirectory()` check.
|
|
209
224
|
*/
|
|
210
|
-
export async function
|
|
225
|
+
export async function ensureSourceCaches(config) {
|
|
211
226
|
const cfg = config ?? loadConfig();
|
|
212
227
|
for (const entry of cfg.stashes ?? []) {
|
|
213
228
|
if (!GIT_STASH_TYPES.has(entry.type) || !entry.url || entry.enabled === false)
|
|
@@ -232,7 +247,3 @@ export async function ensureStashCaches(config) {
|
|
|
232
247
|
}
|
|
233
248
|
}
|
|
234
249
|
}
|
|
235
|
-
/** @deprecated Use ensureStashCaches instead. */
|
|
236
|
-
export const ensureGitCaches = ensureStashCaches;
|
|
237
|
-
/** @deprecated Use ensureStashCaches instead. */
|
|
238
|
-
export const ensureContextHubCaches = ensureStashCaches;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { getCacheDir, getSemanticStatusPath } from "../core/paths";
|
|
4
|
+
import { DEFAULT_LOCAL_MODEL } from "../llm/embedder";
|
|
5
5
|
export function deriveSemanticProviderFingerprint(embedding) {
|
|
6
6
|
if (embedding?.endpoint) {
|
|
7
7
|
return `remote:${embedding.endpoint}|${embedding.model}|${embedding.dimension ?? "default"}`;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import fs from "node:fs";
|
|
9
9
|
import path from "node:path";
|
|
10
|
-
import { isRelevantAssetFile } from "
|
|
10
|
+
import { isRelevantAssetFile } from "../core/asset-spec";
|
|
11
11
|
import { buildFileContext } from "./file-context";
|
|
12
12
|
const SKIP_DIRS = new Set([".git", "node_modules", "bin", ".cache"]);
|
|
13
13
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { getConfigDir } from "
|
|
3
|
+
import { getConfigDir } from "../core/config";
|
|
4
4
|
// ── Paths ───────────────────────────────────────────────────────────────────
|
|
5
5
|
const LOCKFILE_NAME = "akm.lock";
|
|
6
6
|
const LEGACY_LOCKFILE_NAME = "stash.lock";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* `llm.ts` re-exports everything from this module for backward compatibility.
|
|
9
9
|
*/
|
|
10
|
-
import { fetchWithTimeout } from "
|
|
10
|
+
import { fetchWithTimeout } from "../core/common";
|
|
11
11
|
export async function chatCompletion(config, messages, options) {
|
|
12
12
|
const headers = { "Content-Type": "application/json" };
|
|
13
13
|
if (config.apiKey) {
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* shared instance for the production code path.
|
|
8
8
|
*/
|
|
9
9
|
import path from "node:path";
|
|
10
|
-
import { getCacheDir } from "
|
|
11
|
-
import { warn } from "
|
|
10
|
+
import { getCacheDir } from "../../core/paths";
|
|
11
|
+
import { warn } from "../../core/warn";
|
|
12
12
|
/**
|
|
13
13
|
* Default local transformer model for embeddings.
|
|
14
14
|
* `bge-small-en-v1.5` scores higher on MTEB benchmarks than the previous
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Calls the configured `/embeddings` endpoint and L2-normalizes the returned
|
|
5
5
|
* vectors so the scoring pipeline's L2-to-cosine conversion is correct.
|
|
6
6
|
*/
|
|
7
|
-
import { fetchWithTimeout, isHttpUrl } from "
|
|
7
|
+
import { fetchWithTimeout, isHttpUrl } from "../../core/common";
|
|
8
8
|
const REMOTE_BATCH_SIZE = 100;
|
|
9
9
|
export class RemoteEmbedder {
|
|
10
10
|
config;
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Split out of `llm.ts` so the higher-level workflow (prompting the LLM to
|
|
5
5
|
* improve descriptions/tags/searchHints) lives separately from the low-level
|
|
6
|
-
* transport client in `
|
|
6
|
+
* transport client in `client.ts`.
|
|
7
7
|
*/
|
|
8
|
-
import { chatCompletion, parseJsonResponse } from "./
|
|
8
|
+
import { chatCompletion, parseJsonResponse } from "./client";
|
|
9
9
|
const SYSTEM_PROMPT = `You are a metadata generator for a developer asset registry. Given a script/skill/command/agent entry, generate improved metadata. Respond with ONLY valid JSON, no markdown fencing.`;
|
|
10
10
|
/**
|
|
11
11
|
* Use an LLM to enhance a stash entry's metadata: improve description,
|
|
@@ -121,6 +121,7 @@ akm remember --name release-retro < notes.md # Save multiline memory from stdi
|
|
|
121
121
|
akm import ./docs/auth-flow.md # Import a file as knowledge
|
|
122
122
|
akm import - --name scratch-notes < notes.md # Import stdin as a knowledge doc
|
|
123
123
|
akm workflow create ship-release # Create a workflow asset in the stash
|
|
124
|
+
akm workflow validate workflows/foo.md # Validate a workflow file or ref; lists every error
|
|
124
125
|
akm workflow next workflow:ship-release # Start or resume the next workflow step
|
|
125
126
|
akm feedback skill:code-review --positive # Record that an asset helped
|
|
126
127
|
akm feedback agent:reviewer --negative # Record that an asset missed the mark
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Initialized from `cli.ts` before `runMain`.
|
|
10
10
|
*/
|
|
11
|
-
import { UsageError } from "
|
|
11
|
+
import { UsageError } from "../core/errors";
|
|
12
12
|
export const OUTPUT_FORMATS = ["json", "yaml", "text", "jsonl"];
|
|
13
13
|
export const DETAIL_LEVELS = ["brief", "normal", "full", "summary", "agent"];
|
|
14
14
|
export function parseOutputFormat(value) {
|
|
@@ -16,14 +16,14 @@ export function parseOutputFormat(value) {
|
|
|
16
16
|
return undefined;
|
|
17
17
|
if (OUTPUT_FORMATS.includes(value))
|
|
18
18
|
return value;
|
|
19
|
-
throw new UsageError(`Invalid value for --format: ${value}. Expected one of: ${OUTPUT_FORMATS.join("|")}
|
|
19
|
+
throw new UsageError(`Invalid value for --format: ${value}. Expected one of: ${OUTPUT_FORMATS.join("|")}`, "INVALID_FORMAT_VALUE");
|
|
20
20
|
}
|
|
21
21
|
export function parseDetailLevel(value) {
|
|
22
22
|
if (!value)
|
|
23
23
|
return undefined;
|
|
24
24
|
if (DETAIL_LEVELS.includes(value))
|
|
25
25
|
return value;
|
|
26
|
-
throw new UsageError(`Invalid value for --detail: ${value}. Expected one of: ${DETAIL_LEVELS.join("|")}
|
|
26
|
+
throw new UsageError(`Invalid value for --detail: ${value}. Expected one of: ${DETAIL_LEVELS.join("|")}`, "INVALID_DETAIL_VALUE");
|
|
27
27
|
}
|
|
28
28
|
export function parseFlagValue(argv, flag) {
|
|
29
29
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -38,6 +38,24 @@ export function parseFlagValue(argv, flag) {
|
|
|
38
38
|
export function hasBooleanFlag(argv, flag) {
|
|
39
39
|
return argv.some((arg) => arg === flag || arg === `${flag}=true`);
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Read a hyphenated arg out of citty's parsed `args` object.
|
|
43
|
+
*
|
|
44
|
+
* citty does not auto-camelise hyphenated arg keys (see `--max-pages`,
|
|
45
|
+
* `--with-sources` for the existing convention), so command handlers end up
|
|
46
|
+
* casting `args` to a string-indexed record at every read site. This helper
|
|
47
|
+
* encapsulates the cast.
|
|
48
|
+
*/
|
|
49
|
+
export function getHyphenatedArg(args, key) {
|
|
50
|
+
if (typeof args !== "object" || args === null)
|
|
51
|
+
return undefined;
|
|
52
|
+
const value = args[key];
|
|
53
|
+
return value === undefined ? undefined : value;
|
|
54
|
+
}
|
|
55
|
+
/** Boolean variant of {@link getHyphenatedArg} for `--<flag>` switches. */
|
|
56
|
+
export function getHyphenatedBoolean(args, key) {
|
|
57
|
+
return Boolean(getHyphenatedArg(args, key));
|
|
58
|
+
}
|
|
41
59
|
/**
|
|
42
60
|
* Resolve output mode from a synthetic argv array and config defaults.
|
|
43
61
|
* Pure function — no IO. Suitable for unit tests.
|
|
@@ -8,15 +8,13 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import fs from "node:fs";
|
|
10
10
|
import path from "node:path";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { extractDescriptionFromComments, loadStashFile } from "
|
|
17
|
-
import {
|
|
18
|
-
import { listKeys as listVaultKeys } from "./vault";
|
|
19
|
-
import { parseWorkflowMarkdown, WorkflowValidationError } from "./workflow-markdown";
|
|
11
|
+
import { listKeys as listVaultKeys } from "../commands/vault";
|
|
12
|
+
import { hasErrnoCode } from "../core/common";
|
|
13
|
+
import { parseFrontmatter, toStringOrUndefined } from "../core/frontmatter";
|
|
14
|
+
import { extractFrontmatterOnly, extractLineRange, extractSection, formatToc, parseMarkdownToc, } from "../core/markdown";
|
|
15
|
+
import { registerRenderer } from "../indexer/file-context";
|
|
16
|
+
import { extractDescriptionFromComments, loadStashFile } from "../indexer/metadata";
|
|
17
|
+
import { buildWorkflowAction, workflowMdRenderer } from "../workflows/renderer";
|
|
20
18
|
// ── Interpreter auto-detection map ───────────────────────────────────────────
|
|
21
19
|
const INTERPRETER_MAP = {
|
|
22
20
|
".sh": "bash",
|
|
@@ -153,12 +151,7 @@ function deriveName(ctx) {
|
|
|
153
151
|
const ext = path.extname(ctx.relPath);
|
|
154
152
|
return ext ? ctx.relPath.slice(0, -ext.length) : ctx.relPath;
|
|
155
153
|
}
|
|
156
|
-
|
|
157
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
158
|
-
}
|
|
159
|
-
export function buildWorkflowAction(ref) {
|
|
160
|
-
return `Resume the active run or start a new run with \`akm workflow next ${shellQuote(ref)}\`.`;
|
|
161
|
-
}
|
|
154
|
+
export { buildWorkflowAction };
|
|
162
155
|
/**
|
|
163
156
|
* Load the matching StashEntry for a file path from the directory's .stash.json.
|
|
164
157
|
*/
|
|
@@ -465,56 +458,7 @@ const memoryMdRenderer = {
|
|
|
465
458
|
},
|
|
466
459
|
};
|
|
467
460
|
// ── 6. workflow-md ───────────────────────────────────────────────────────────
|
|
468
|
-
|
|
469
|
-
name: "workflow-md",
|
|
470
|
-
buildShowResponse(ctx) {
|
|
471
|
-
const name = deriveName(ctx);
|
|
472
|
-
const workflow = parseWorkflowForRendering(ctx.content());
|
|
473
|
-
const ref = makeAssetRef("workflow", name, ctx.origin);
|
|
474
|
-
return {
|
|
475
|
-
type: "workflow",
|
|
476
|
-
name,
|
|
477
|
-
path: ctx.absPath,
|
|
478
|
-
action: buildWorkflowAction(ref),
|
|
479
|
-
description: workflow.description,
|
|
480
|
-
workflowTitle: workflow.title,
|
|
481
|
-
parameters: workflow.parameters?.map((parameter) => parameter.name),
|
|
482
|
-
workflowParameters: workflow.parameters,
|
|
483
|
-
steps: workflow.steps,
|
|
484
|
-
};
|
|
485
|
-
},
|
|
486
|
-
extractMetadata(entry, ctx) {
|
|
487
|
-
const workflow = parseWorkflowForRendering(ctx.content());
|
|
488
|
-
const hints = new Set(entry.searchHints ?? []);
|
|
489
|
-
hints.add(workflow.title);
|
|
490
|
-
for (const step of workflow.steps) {
|
|
491
|
-
hints.add(step.title);
|
|
492
|
-
hints.add(step.id);
|
|
493
|
-
hints.add(step.instructions);
|
|
494
|
-
for (const criterion of step.completionCriteria ?? []) {
|
|
495
|
-
hints.add(criterion);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
entry.searchHints = Array.from(hints).filter(Boolean);
|
|
499
|
-
if (workflow.parameters?.length) {
|
|
500
|
-
entry.parameters = workflow.parameters.map((parameter) => ({
|
|
501
|
-
name: parameter.name,
|
|
502
|
-
...(parameter.description ? { description: parameter.description } : {}),
|
|
503
|
-
}));
|
|
504
|
-
}
|
|
505
|
-
},
|
|
506
|
-
};
|
|
507
|
-
function parseWorkflowForRendering(content) {
|
|
508
|
-
try {
|
|
509
|
-
return parseWorkflowMarkdown(content);
|
|
510
|
-
}
|
|
511
|
-
catch (error) {
|
|
512
|
-
if (error instanceof WorkflowValidationError) {
|
|
513
|
-
throw new UsageError(error.message);
|
|
514
|
-
}
|
|
515
|
-
throw error;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
461
|
+
// Defined in src/workflows/renderer.ts and imported above.
|
|
518
462
|
// ── 7. script-source ─────────────────────────────────────────────────────────
|
|
519
463
|
const scriptSourceRenderer = {
|
|
520
464
|
name: "script-source",
|
|
@@ -81,10 +81,20 @@ export function shapeAssetHit(hit, detail) {
|
|
|
81
81
|
}
|
|
82
82
|
export function shapeSearchHit(hit, detail) {
|
|
83
83
|
if (hit.type === "registry") {
|
|
84
|
-
if (detail === "brief")
|
|
85
|
-
|
|
84
|
+
if (detail === "brief") {
|
|
85
|
+
// RegistrySearchHit uses `title` (not `name`); always project installRef
|
|
86
|
+
// and score so callers can use the result without --detail full (QA #28).
|
|
87
|
+
const out = pickFields(hit, ["title", "name", "installRef", "score"]);
|
|
88
|
+
// Normalise: if only title exists, expose it as `name` for consistency
|
|
89
|
+
if (out.title && !out.name)
|
|
90
|
+
out.name = out.title;
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
86
93
|
if (detail === "normal") {
|
|
87
|
-
|
|
94
|
+
const out = capDescription(pickFields(hit, ["title", "name", "description", "action", "installRef", "score", "curated"]), NORMAL_DESCRIPTION_LIMIT);
|
|
95
|
+
if (out.title && !out.name)
|
|
96
|
+
out.name = out.title;
|
|
97
|
+
return out;
|
|
88
98
|
}
|
|
89
99
|
return hit;
|
|
90
100
|
}
|
|
@@ -176,6 +186,10 @@ export function shapeShowOutput(result, detail, forAgent = false) {
|
|
|
176
186
|
"cwd",
|
|
177
187
|
"keys",
|
|
178
188
|
"comments",
|
|
189
|
+
// path and editable are always projected so JSON consumers can locate and
|
|
190
|
+
// edit the asset without needing --detail full (QA #7).
|
|
191
|
+
"path",
|
|
192
|
+
"editable",
|
|
179
193
|
]);
|
|
180
194
|
if (detail !== "full") {
|
|
181
195
|
return base;
|
|
@@ -183,7 +197,7 @@ export function shapeShowOutput(result, detail, forAgent = false) {
|
|
|
183
197
|
return {
|
|
184
198
|
schemaVersion: 1,
|
|
185
199
|
...base,
|
|
186
|
-
...pickFields(result, ["
|
|
200
|
+
...pickFields(result, ["editHint"]),
|
|
187
201
|
};
|
|
188
202
|
}
|
|
189
203
|
export function pickFields(source, fields) {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Pure functions — no IO.
|
|
7
7
|
*/
|
|
8
|
-
import { formatInstallAuditSummary } from "
|
|
8
|
+
import { formatInstallAuditSummary } from "../commands/install-audit";
|
|
9
9
|
export function outputJsonl(command, shaped) {
|
|
10
10
|
if (command === "search" || command === "registry-search") {
|
|
11
11
|
const r = shaped;
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the v2 JSON registry index consumed by the `static-index` registry
|
|
3
|
+
* provider. This module emits artifacts that conform to the v2 schema; the
|
|
4
|
+
* schema itself is the input contract owned by `src/registry/providers/static-index.ts`
|
|
5
|
+
* (see v1 architecture spec §3.3 — "the v2 JSON index schema belongs to
|
|
6
|
+
* static-index"). When the schema changes, both the parser in `static-index.ts`
|
|
7
|
+
* and the JSON Schema in `docs/technical/registry-index.schema.json` must be
|
|
8
|
+
* updated together with this builder.
|
|
9
|
+
*/
|
|
1
10
|
import fs from "node:fs";
|
|
2
11
|
import os from "node:os";
|
|
3
12
|
import path from "node:path";
|
|
4
|
-
import { fetchWithRetry, jsonWithByteCap } from "
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
13
|
+
import { fetchWithRetry, jsonWithByteCap } from "../core/common";
|
|
14
|
+
import { generateMetadataFlat, loadStashFile } from "../indexer/metadata";
|
|
15
|
+
import { walkStashFlat } from "../indexer/walker";
|
|
16
|
+
import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "../integrations/github";
|
|
17
|
+
import { copyIncludedPaths, findNearestIncludeConfig } from "../sources/include";
|
|
18
|
+
import { detectStashRoot } from "../sources/providers/provider-utils";
|
|
19
|
+
import { extractTarGzSecure } from "../sources/providers/tar-utils";
|
|
7
20
|
import { parseRegistryIndex } from "./providers/static-index";
|
|
8
|
-
import { copyIncludedPaths, findNearestIncludeConfig } from "./stash-include";
|
|
9
|
-
import { detectStashRoot } from "./stash-providers/provider-utils";
|
|
10
|
-
import { extractTarGzSecure } from "./stash-providers/tar-utils";
|
|
11
|
-
import { walkStashFlat } from "./walker";
|
|
12
21
|
const DEFAULT_NPM_REGISTRY_BASE = "https://registry.npmjs.org";
|
|
13
22
|
const DEFAULT_MANUAL_ENTRIES_PATH = path.resolve("manual-entries.json");
|
|
14
23
|
const DEFAULT_OUTPUT_PATH = path.resolve("index.json");
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Generic factory-map utility.
|
|
3
3
|
*
|
|
4
4
|
* Creates a lightweight registry that maps string keys to factory functions.
|
|
5
|
-
* Both registry
|
|
6
|
-
* (
|
|
5
|
+
* Both registry/factory.ts (registry discovery) and sources/provider-factory.ts
|
|
6
|
+
* (source providers) are built on this utility.
|
|
7
7
|
*/
|
|
8
8
|
export function createProviderRegistry() {
|
|
9
9
|
const map = new Map();
|
|
@@ -14,5 +14,9 @@ export function createProviderRegistry() {
|
|
|
14
14
|
resolve(type) {
|
|
15
15
|
return map.get(type) ?? null;
|
|
16
16
|
},
|
|
17
|
+
/** Snapshot of all registered keys. Iteration order matches insertion order. */
|
|
18
|
+
list() {
|
|
19
|
+
return [...map.keys()];
|
|
20
|
+
},
|
|
17
21
|
};
|
|
18
22
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry provider factory map.
|
|
3
|
+
*
|
|
4
|
+
* Maps registry provider type identifiers (e.g. "static-index", "skills-sh")
|
|
5
|
+
* to factory functions that create RegistryProvider instances.
|
|
6
|
+
*
|
|
7
|
+
* "Registry" here refers to the kit discovery registries (static index files,
|
|
8
|
+
* skills.sh API) — not to be confused with the source provider factory map in
|
|
9
|
+
* `sources/provider-factory.ts` or the installed-source operations in
|
|
10
|
+
* `installed-stashes.ts`.
|
|
11
|
+
*
|
|
12
|
+
* Phase 6 (v1 architecture refactor): factories are now the
|
|
13
|
+
* `RegistryProviderFactory` type owned by `src/registry/providers/types.ts`.
|
|
14
|
+
* The legacy alias in `src/registry-provider.ts` is kept as a thin re-export
|
|
15
|
+
* for transitional callers and will be removed after the dust settles.
|
|
16
|
+
*/
|
|
17
|
+
import { createProviderRegistry } from "./create-provider-registry";
|
|
18
|
+
// ── Factory map ─────────────────────────────────────────────────────────────
|
|
19
|
+
const registry = createProviderRegistry();
|
|
20
|
+
export function registerProvider(type, factory) {
|
|
21
|
+
registry.register(type, factory);
|
|
22
|
+
}
|
|
23
|
+
export function resolveProviderFactory(type) {
|
|
24
|
+
return registry.resolve(type);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Iterate over all registered registry providers. Used by the orchestrator
|
|
28
|
+
* (`src/commands/registry-search.ts`) to fan out queries through the same
|
|
29
|
+
* `RegistryProvider` interface regardless of provider kind.
|
|
30
|
+
*/
|
|
31
|
+
export function listProviderTypes() {
|
|
32
|
+
return registry.list();
|
|
33
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* providers with the provider registry. This replaces the individual
|
|
6
6
|
* side-effect imports that were duplicated in registry-search.ts.
|
|
7
7
|
*
|
|
8
|
-
* Mirrors the pattern used by `
|
|
8
|
+
* Mirrors the pattern used by `sources/providers/index.ts`.
|
|
9
9
|
*/
|
|
10
10
|
import "./static-index";
|
|
11
11
|
import "./skills-sh";
|