akm-cli 0.6.0-rc1 → 0.6.0
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 +33 -0
- package/README.md +9 -9
- package/dist/cli.js +199 -114
- 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} +69 -3
- package/dist/{stash-show.js → commands/show.js} +104 -84
- 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} +133 -56
- 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} +79 -47
- package/dist/{file-context.js → indexer/file-context.js} +3 -3
- package/dist/{indexer.js → indexer/indexer.js} +132 -33
- 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} +3 -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} +2 -2
- 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} +34 -18
- 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 +91 -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
package/dist/cli.js
CHANGED
|
@@ -2,41 +2,45 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { defineCommand, runMain } from "citty";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
5
|
+
import { generateBashCompletions, installBashCompletions } from "./commands/completions";
|
|
6
|
+
import { getConfigValue, listConfig, setConfigValue, unsetConfigValue } from "./commands/config-cli";
|
|
7
|
+
import { akmCurate } from "./commands/curate";
|
|
8
|
+
import { assembleInfo } from "./commands/info";
|
|
9
|
+
import { akmInit } from "./commands/init";
|
|
10
|
+
import { akmListSources, akmRemove, akmUpdate } from "./commands/installed-stashes";
|
|
11
|
+
import { renderMigrationHelp } from "./commands/migration-help";
|
|
12
|
+
import { searchRegistry } from "./commands/registry-search";
|
|
13
|
+
import { buildMemoryFrontmatter, parseDuration, readMemoryContent, runAutoHeuristics, runLlmEnrich, } from "./commands/remember";
|
|
14
|
+
import { akmSearch, parseSearchSource } from "./commands/search";
|
|
15
|
+
import { checkForUpdate, performUpgrade } from "./commands/self-update";
|
|
16
|
+
import { akmShowUnified } from "./commands/show";
|
|
17
|
+
import { akmAdd } from "./commands/source-add";
|
|
18
|
+
import { akmClone } from "./commands/source-clone";
|
|
19
|
+
import { addStash } from "./commands/source-manage";
|
|
20
|
+
import { parseAssetRef } from "./core/asset-ref";
|
|
21
|
+
import { deriveCanonicalAssetName, resolveAssetPathFromName } from "./core/asset-spec";
|
|
22
|
+
import { isWithin, resolveStashDir, tryReadStdinText } from "./core/common";
|
|
23
|
+
import { DEFAULT_CONFIG, getConfigPath, loadConfig, loadUserConfig, saveConfig } from "./core/config";
|
|
24
|
+
import { ConfigError, NotFoundError, UsageError } from "./core/errors";
|
|
25
|
+
import { getCacheDir, getDbPath, getDefaultStashDir } from "./core/paths";
|
|
26
|
+
import { setQuiet, warn } from "./core/warn";
|
|
27
|
+
import { resolveWriteTarget, writeAssetToSource } from "./core/write-source";
|
|
28
|
+
import { closeDatabase, findEntryIdByRef, openDatabase } from "./indexer/db";
|
|
29
|
+
import { akmIndex } from "./indexer/indexer";
|
|
30
|
+
import { resolveSourceEntries } from "./indexer/search-source";
|
|
31
|
+
import { insertUsageEvent } from "./indexer/usage-events";
|
|
32
|
+
import { EMBEDDED_HINTS, EMBEDDED_HINTS_FULL } from "./output/cli-hints";
|
|
33
|
+
import { getHyphenatedArg, getHyphenatedBoolean, getOutputMode, initOutputMode, parseFlagValue, } from "./output/context";
|
|
34
|
+
import { shapeForCommand } from "./output/shapes";
|
|
35
|
+
import { formatPlain, outputJsonl } from "./output/text";
|
|
36
|
+
import { buildRegistryIndex, writeRegistryIndex } from "./registry/build-index";
|
|
37
|
+
import { resolveSourcesForOrigin } from "./registry/origin-resolve";
|
|
38
|
+
import { saveGitStash } from "./sources/providers/git";
|
|
39
|
+
import { resolveAssetPath } from "./sources/resolve";
|
|
35
40
|
import { pkgVersion } from "./version";
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import { completeWorkflowStep, getNextWorkflowStep, getWorkflowStatus, listWorkflowRuns, resumeWorkflowRun, startWorkflowRun, } from "./workflow-runs";
|
|
41
|
+
import { createWorkflowAsset, formatWorkflowErrors, getWorkflowTemplate, validateWorkflowSource, } from "./workflows/authoring";
|
|
42
|
+
import { hasWorkflowSubcommand, parseWorkflowJsonObject, parseWorkflowStepState, WORKFLOW_STEP_STATES, } from "./workflows/cli";
|
|
43
|
+
import { completeWorkflowStep, getNextWorkflowStep, getWorkflowStatus, listWorkflowRuns, resumeWorkflowRun, startWorkflowRun, } from "./workflows/runs";
|
|
40
44
|
const MAX_CAPTURED_ASSET_SLUG_LENGTH = 64;
|
|
41
45
|
const SKILLS_SH_NAME = "skills.sh";
|
|
42
46
|
const SKILLS_SH_URL = "https://skills.sh";
|
|
@@ -84,9 +88,9 @@ function output(command, result) {
|
|
|
84
88
|
}
|
|
85
89
|
/**
|
|
86
90
|
* Module Naming:
|
|
87
|
-
* -
|
|
88
|
-
* -
|
|
89
|
-
* - registry
|
|
91
|
+
* - sources/* : Asset operations (search, show, add, clone)
|
|
92
|
+
* - sources/providers/* : Runtime data source providers (filesystem, git, website, npm)
|
|
93
|
+
* - registry/* : Discovery from remote registries (npm, GitHub)
|
|
90
94
|
* - installed-stashes : Unified source operations (list, remove, update)
|
|
91
95
|
*/
|
|
92
96
|
const setupCommand = defineCommand({
|
|
@@ -96,7 +100,7 @@ const setupCommand = defineCommand({
|
|
|
96
100
|
},
|
|
97
101
|
async run() {
|
|
98
102
|
await runWithJsonErrors(async () => {
|
|
99
|
-
const { runSetupWizard } = await import("./setup");
|
|
103
|
+
const { runSetupWizard } = await import("./setup/setup");
|
|
100
104
|
await runSetupWizard();
|
|
101
105
|
});
|
|
102
106
|
},
|
|
@@ -111,7 +115,10 @@ const initCommand = defineCommand({
|
|
|
111
115
|
},
|
|
112
116
|
async run({ args }) {
|
|
113
117
|
await runWithJsonErrors(async () => {
|
|
114
|
-
|
|
118
|
+
// Accept both historical spellings for backwards compatibility with
|
|
119
|
+
// older docs/scripts that used `--stashDir`.
|
|
120
|
+
const legacyDir = parseFlagValue(process.argv, "--stashDir") ?? parseFlagValue(process.argv, "--stash-dir");
|
|
121
|
+
const result = await akmInit({ dir: args.dir ?? legacyDir });
|
|
115
122
|
output("init", result);
|
|
116
123
|
});
|
|
117
124
|
},
|
|
@@ -156,6 +163,10 @@ const searchCommand = defineCommand({
|
|
|
156
163
|
},
|
|
157
164
|
async run({ args }) {
|
|
158
165
|
await runWithJsonErrors(async () => {
|
|
166
|
+
const query = (args.query ?? "").trim();
|
|
167
|
+
if (!query) {
|
|
168
|
+
throw new UsageError('A search query is required. Usage: akm search "<query>" [--type <type>] [--limit <n>]', "MISSING_REQUIRED_ARGUMENT");
|
|
169
|
+
}
|
|
159
170
|
const type = args.type;
|
|
160
171
|
const limitRaw = args.limit ? parseInt(args.limit, 10) : undefined;
|
|
161
172
|
if (limitRaw !== undefined && Number.isNaN(limitRaw)) {
|
|
@@ -163,7 +174,7 @@ const searchCommand = defineCommand({
|
|
|
163
174
|
}
|
|
164
175
|
const limit = limitRaw;
|
|
165
176
|
const source = parseSearchSource(args.source);
|
|
166
|
-
const result = await akmSearch({ query
|
|
177
|
+
const result = await akmSearch({ query, type, limit, source });
|
|
167
178
|
output("search", result);
|
|
168
179
|
});
|
|
169
180
|
},
|
|
@@ -204,7 +215,7 @@ const addCommand = defineCommand({
|
|
|
204
215
|
description: "Path, URL, or registry ref (website URL, npm package, owner/repo, git URL, or local directory)",
|
|
205
216
|
required: true,
|
|
206
217
|
},
|
|
207
|
-
provider: { type: "string", description: "Provider type (e.g.
|
|
218
|
+
provider: { type: "string", description: "Provider type (e.g. website, npm). Required for URL sources." },
|
|
208
219
|
options: { type: "string", description: 'Provider options as JSON (e.g. \'{"apiKey":"key"}\').' },
|
|
209
220
|
name: { type: "string", description: "Human-friendly name for the source" },
|
|
210
221
|
writable: {
|
|
@@ -262,7 +273,7 @@ const addCommand = defineCommand({
|
|
|
262
273
|
}
|
|
263
274
|
const websiteOptions = buildWebsiteOptions(args);
|
|
264
275
|
if (args.type === "wiki") {
|
|
265
|
-
const { registerWikiSource } = await import("./
|
|
276
|
+
const { registerWikiSource } = await import("./commands/source-add");
|
|
266
277
|
const result = await registerWikiSource({
|
|
267
278
|
ref,
|
|
268
279
|
name: args.name,
|
|
@@ -370,6 +381,11 @@ const upgradeCommand = defineCommand({
|
|
|
370
381
|
description: "Skip checksum verification (not recommended)",
|
|
371
382
|
default: false,
|
|
372
383
|
},
|
|
384
|
+
"skip-post-upgrade": {
|
|
385
|
+
type: "boolean",
|
|
386
|
+
description: "Skip the post-upgrade `akm index` rebuild (config auto-migration still runs on next `akm` invocation)",
|
|
387
|
+
default: false,
|
|
388
|
+
},
|
|
373
389
|
},
|
|
374
390
|
async run({ args }) {
|
|
375
391
|
await runWithJsonErrors(async () => {
|
|
@@ -378,8 +394,9 @@ const upgradeCommand = defineCommand({
|
|
|
378
394
|
output("upgrade", check);
|
|
379
395
|
return;
|
|
380
396
|
}
|
|
381
|
-
const skipChecksum =
|
|
382
|
-
const
|
|
397
|
+
const skipChecksum = getHyphenatedBoolean(args, "skip-checksum");
|
|
398
|
+
const skipPostUpgrade = getHyphenatedBoolean(args, "skip-post-upgrade");
|
|
399
|
+
const result = await performUpgrade(check, { force: args.force, skipChecksum, skipPostUpgrade });
|
|
383
400
|
output("upgrade", result);
|
|
384
401
|
});
|
|
385
402
|
},
|
|
@@ -427,7 +444,6 @@ const showCommand = defineCommand({
|
|
|
427
444
|
throw new UsageError(`Unknown view mode: ${akmView}. Expected one of: full|toc|frontmatter|section|lines`);
|
|
428
445
|
}
|
|
429
446
|
}
|
|
430
|
-
// Map CLI detail level to ShowDetailLevel for the show function
|
|
431
447
|
const cliDetail = getOutputMode().detail;
|
|
432
448
|
const showDetail = cliDetail === "summary" ? "summary" : undefined;
|
|
433
449
|
const result = await akmShowUnified({ ref: args.ref, view, detail: showDetail });
|
|
@@ -671,7 +687,7 @@ const registryCommand = defineCommand({
|
|
|
671
687
|
throw new UsageError("Registry URL must start with http:// or https://");
|
|
672
688
|
}
|
|
673
689
|
if (args.url.startsWith("http://")) {
|
|
674
|
-
const allowInsecure =
|
|
690
|
+
const allowInsecure = getHyphenatedBoolean(args, "allow-insecure");
|
|
675
691
|
if (!allowInsecure) {
|
|
676
692
|
throw new UsageError("Registry URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious index. " +
|
|
677
693
|
"Use https:// or pass --allow-insecure if you have explicitly accepted the risk.");
|
|
@@ -752,11 +768,10 @@ const registryCommand = defineCommand({
|
|
|
752
768
|
},
|
|
753
769
|
async run({ args }) {
|
|
754
770
|
await runWithJsonErrors(async () => {
|
|
755
|
-
const argsRecord = args;
|
|
756
771
|
const result = await buildRegistryIndex({
|
|
757
772
|
manualEntriesPath: args.manual,
|
|
758
|
-
npmRegistryBase:
|
|
759
|
-
githubApiBase:
|
|
773
|
+
npmRegistryBase: getHyphenatedArg(args, "npm-registry"),
|
|
774
|
+
githubApiBase: getHyphenatedArg(args, "github-api"),
|
|
760
775
|
});
|
|
761
776
|
const outPath = writeRegistryIndex(result.index, args.out);
|
|
762
777
|
output("registry-build-index", {
|
|
@@ -775,7 +790,7 @@ const registryCommand = defineCommand({
|
|
|
775
790
|
const feedbackCommand = defineCommand({
|
|
776
791
|
meta: {
|
|
777
792
|
name: "feedback",
|
|
778
|
-
description: "Record positive or negative feedback for
|
|
793
|
+
description: "Record positive or negative feedback for any indexed stash asset",
|
|
779
794
|
},
|
|
780
795
|
args: {
|
|
781
796
|
ref: { type: "positional", description: "Asset ref (type:name)", required: true },
|
|
@@ -789,6 +804,7 @@ const feedbackCommand = defineCommand({
|
|
|
789
804
|
if (!ref) {
|
|
790
805
|
throw new UsageError("Asset ref is required. Usage: akm feedback <ref> --positive|--negative");
|
|
791
806
|
}
|
|
807
|
+
parseAssetRef(ref);
|
|
792
808
|
if (args.positive && args.negative) {
|
|
793
809
|
throw new UsageError("Specify either --positive or --negative, not both.");
|
|
794
810
|
}
|
|
@@ -799,9 +815,14 @@ const feedbackCommand = defineCommand({
|
|
|
799
815
|
const metadata = args.note ? JSON.stringify({ note: args.note }) : undefined;
|
|
800
816
|
const db = openDatabase();
|
|
801
817
|
try {
|
|
818
|
+
const entryId = findEntryIdByRef(db, ref);
|
|
819
|
+
if (entryId === undefined) {
|
|
820
|
+
throw new UsageError(`Ref "${ref}" is not in the current index. Run "akm index" and try again.`);
|
|
821
|
+
}
|
|
802
822
|
insertUsageEvent(db, {
|
|
803
823
|
event_type: "feedback",
|
|
804
824
|
entry_ref: ref,
|
|
825
|
+
entry_id: entryId,
|
|
805
826
|
signal,
|
|
806
827
|
metadata,
|
|
807
828
|
});
|
|
@@ -868,24 +889,30 @@ function readKnowledgeContent(source) {
|
|
|
868
889
|
preferredName: path.basename(resolvedSource, path.extname(resolvedSource)),
|
|
869
890
|
};
|
|
870
891
|
}
|
|
871
|
-
function writeMarkdownAsset(options) {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
892
|
+
async function writeMarkdownAsset(options) {
|
|
893
|
+
// Resolve write target via the v1 precedence chain (`--target` →
|
|
894
|
+
// `defaultWriteTarget` → working stash). Per spec §10 step 5, this is the
|
|
895
|
+
// single dispatch point — `core/write-source.ts` owns all kind-branching.
|
|
896
|
+
const cfg = loadConfig();
|
|
897
|
+
const { source, config } = resolveWriteTarget(cfg, options.target);
|
|
898
|
+
const typeRoot = path.join(source.path, options.type === "knowledge" ? "knowledge" : "memories");
|
|
875
899
|
const normalizedName = normalizeMarkdownAssetName(options.name, inferAssetName(options.content, options.fallbackPrefix, options.preferredName));
|
|
900
|
+
// Pre-flight: existence + force semantics. The helper itself overwrites
|
|
901
|
+
// unconditionally; the CLI surfaces a friendlier UsageError before any
|
|
902
|
+
// disk activity when --force is absent.
|
|
876
903
|
const assetPath = resolveAssetPathFromName(options.type, typeRoot, normalizedName);
|
|
877
904
|
if (!isWithin(assetPath, typeRoot)) {
|
|
878
905
|
throw new UsageError(`Resolved ${options.type} path escapes the stash: "${normalizedName}"`);
|
|
879
906
|
}
|
|
880
907
|
if (fs.existsSync(assetPath) && !options.force) {
|
|
881
|
-
throw new UsageError(`${options.type === "knowledge" ? "Knowledge" : "Memory"} "${normalizedName}" already exists. Re-run with --force to overwrite it
|
|
908
|
+
throw new UsageError(`${options.type === "knowledge" ? "Knowledge" : "Memory"} "${normalizedName}" already exists. Re-run with --force to overwrite it.`, "RESOURCE_ALREADY_EXISTS");
|
|
882
909
|
}
|
|
883
|
-
|
|
884
|
-
|
|
910
|
+
// Delegate the actual write (and optional git commit/push) to the helper.
|
|
911
|
+
const result = await writeAssetToSource(source, config, { type: options.type, name: normalizedName }, options.content);
|
|
885
912
|
return {
|
|
886
|
-
ref:
|
|
887
|
-
path:
|
|
888
|
-
stashDir,
|
|
913
|
+
ref: result.ref,
|
|
914
|
+
path: result.path,
|
|
915
|
+
stashDir: source.path,
|
|
889
916
|
};
|
|
890
917
|
}
|
|
891
918
|
const workflowStartCommand = defineCommand({
|
|
@@ -1023,8 +1050,8 @@ const workflowCreateCommand = defineCommand({
|
|
|
1023
1050
|
default: false,
|
|
1024
1051
|
},
|
|
1025
1052
|
},
|
|
1026
|
-
run({ args }) {
|
|
1027
|
-
return runWithJsonErrors(() => {
|
|
1053
|
+
async run({ args }) {
|
|
1054
|
+
return runWithJsonErrors(async () => {
|
|
1028
1055
|
const namePattern = /^[a-z0-9][a-z0-9._/-]*$/;
|
|
1029
1056
|
if (!namePattern.test(args.name)) {
|
|
1030
1057
|
throw new UsageError("Workflow name must start with a lowercase letter or digit and contain only lowercase letters, digits, hyphens, dots, underscores, and slashes.");
|
|
@@ -1037,6 +1064,10 @@ const workflowCreateCommand = defineCommand({
|
|
|
1037
1064
|
from: args.from,
|
|
1038
1065
|
force: args.force,
|
|
1039
1066
|
});
|
|
1067
|
+
// Index the newly-written workflow so `akm workflow start` can resolve
|
|
1068
|
+
// a workflowEntryId without requiring an explicit `akm index` call
|
|
1069
|
+
// first. Uses the same incremental index path that `akm add` uses.
|
|
1070
|
+
await akmIndex({ stashDir: result.stashDir });
|
|
1040
1071
|
output("workflow-create", { ok: true, ...result });
|
|
1041
1072
|
});
|
|
1042
1073
|
},
|
|
@@ -1050,6 +1081,55 @@ const workflowTemplateCommand = defineCommand({
|
|
|
1050
1081
|
process.stdout.write(getWorkflowTemplate());
|
|
1051
1082
|
},
|
|
1052
1083
|
});
|
|
1084
|
+
const workflowValidateCommand = defineCommand({
|
|
1085
|
+
meta: {
|
|
1086
|
+
name: "validate",
|
|
1087
|
+
description: "Validate a workflow markdown file or ref and print any errors",
|
|
1088
|
+
},
|
|
1089
|
+
args: {
|
|
1090
|
+
target: {
|
|
1091
|
+
type: "positional",
|
|
1092
|
+
description: "Workflow ref (workflow:<name>) or filesystem path to a workflow .md",
|
|
1093
|
+
required: true,
|
|
1094
|
+
},
|
|
1095
|
+
},
|
|
1096
|
+
async run({ args }) {
|
|
1097
|
+
return runWithJsonErrors(async () => {
|
|
1098
|
+
const filePath = await resolveWorkflowFilePath(args.target);
|
|
1099
|
+
const { parse } = validateWorkflowSource(filePath);
|
|
1100
|
+
if (parse.ok) {
|
|
1101
|
+
output("workflow-validate", {
|
|
1102
|
+
ok: true,
|
|
1103
|
+
path: filePath,
|
|
1104
|
+
title: parse.document.title,
|
|
1105
|
+
stepCount: parse.document.steps.length,
|
|
1106
|
+
});
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
throw new UsageError(formatWorkflowErrors(filePath, parse.errors));
|
|
1110
|
+
});
|
|
1111
|
+
},
|
|
1112
|
+
});
|
|
1113
|
+
async function resolveWorkflowFilePath(target) {
|
|
1114
|
+
if (!target.startsWith("workflow:"))
|
|
1115
|
+
return target;
|
|
1116
|
+
const parsed = parseAssetRef(target);
|
|
1117
|
+
if (parsed.type !== "workflow") {
|
|
1118
|
+
throw new UsageError(`Expected a workflow ref (workflow:<name>), got "${target}".`);
|
|
1119
|
+
}
|
|
1120
|
+
const config = loadConfig();
|
|
1121
|
+
const allSources = resolveSourceEntries(undefined, config);
|
|
1122
|
+
const searchSources = resolveSourcesForOrigin(parsed.origin, allSources);
|
|
1123
|
+
for (const source of searchSources) {
|
|
1124
|
+
try {
|
|
1125
|
+
return await resolveAssetPath(source.path, "workflow", parsed.name);
|
|
1126
|
+
}
|
|
1127
|
+
catch {
|
|
1128
|
+
/* try next source */
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
throw new UsageError(`Workflow not found for ref: workflow:${parsed.name}`);
|
|
1132
|
+
}
|
|
1053
1133
|
const workflowResumeCommand = defineCommand({
|
|
1054
1134
|
meta: {
|
|
1055
1135
|
name: "resume",
|
|
@@ -1079,6 +1159,7 @@ const workflowCommand = defineCommand({
|
|
|
1079
1159
|
create: workflowCreateCommand,
|
|
1080
1160
|
template: workflowTemplateCommand,
|
|
1081
1161
|
resume: workflowResumeCommand,
|
|
1162
|
+
validate: workflowValidateCommand,
|
|
1082
1163
|
},
|
|
1083
1164
|
run({ args }) {
|
|
1084
1165
|
return runWithJsonErrors(() => {
|
|
@@ -1108,6 +1189,10 @@ const rememberCommand = defineCommand({
|
|
|
1108
1189
|
description: "Overwrite an existing memory with the same name",
|
|
1109
1190
|
default: false,
|
|
1110
1191
|
},
|
|
1192
|
+
description: {
|
|
1193
|
+
type: "string",
|
|
1194
|
+
description: "Short description written to frontmatter (persisted as the memory's description field)",
|
|
1195
|
+
},
|
|
1111
1196
|
tag: {
|
|
1112
1197
|
type: "string",
|
|
1113
1198
|
description: "Tag to add to the memory (repeatable: --tag foo --tag bar)",
|
|
@@ -1130,6 +1215,10 @@ const rememberCommand = defineCommand({
|
|
|
1130
1215
|
description: "Call the configured LLM to propose tags and description (requires LLM config)",
|
|
1131
1216
|
default: false,
|
|
1132
1217
|
},
|
|
1218
|
+
target: {
|
|
1219
|
+
type: "string",
|
|
1220
|
+
description: "Override the write destination. Accepts a source name from your config; falls back to defaultWriteTarget then the working stash.",
|
|
1221
|
+
},
|
|
1133
1222
|
},
|
|
1134
1223
|
async run({ args }) {
|
|
1135
1224
|
return runWithJsonErrors(async () => {
|
|
@@ -1138,15 +1227,15 @@ const rememberCommand = defineCommand({
|
|
|
1138
1227
|
// Collect all --tag occurrences directly from process.argv because citty
|
|
1139
1228
|
// only exposes the last value for repeated string flags.
|
|
1140
1229
|
const rawTags = parseAllFlagValues("--tag");
|
|
1141
|
-
const hasStructuredArgs = rawTags.length > 0 || !!args.expires || !!args.source || args.auto || args.enrich;
|
|
1142
|
-
// Zero-flag path: write bare memory (no frontmatter). Preserve existing behaviour.
|
|
1230
|
+
const hasStructuredArgs = rawTags.length > 0 || !!args.expires || !!args.source || !!args.description || args.auto || args.enrich;
|
|
1143
1231
|
if (!hasStructuredArgs) {
|
|
1144
|
-
const result = writeMarkdownAsset({
|
|
1232
|
+
const result = await writeMarkdownAsset({
|
|
1145
1233
|
type: "memory",
|
|
1146
1234
|
content: body,
|
|
1147
1235
|
name: args.name,
|
|
1148
1236
|
fallbackPrefix: "memory",
|
|
1149
1237
|
force: args.force,
|
|
1238
|
+
target: args.target,
|
|
1150
1239
|
});
|
|
1151
1240
|
output("remember", { ok: true, ...result });
|
|
1152
1241
|
return;
|
|
@@ -1154,7 +1243,8 @@ const rememberCommand = defineCommand({
|
|
|
1154
1243
|
// ── Accumulate metadata from all three modes ──────────────────────────
|
|
1155
1244
|
// Start with CLI args (Mode 1: always)
|
|
1156
1245
|
const tags = [...rawTags];
|
|
1157
|
-
|
|
1246
|
+
// --description is persisted as-is; LLM enrichment may fill it if absent.
|
|
1247
|
+
let description = args.description || undefined;
|
|
1158
1248
|
let source = args.source;
|
|
1159
1249
|
let observed_at;
|
|
1160
1250
|
let expires;
|
|
@@ -1209,12 +1299,13 @@ const rememberCommand = defineCommand({
|
|
|
1209
1299
|
subjective,
|
|
1210
1300
|
});
|
|
1211
1301
|
const contentWithFrontmatter = `${frontmatterBlock}\n${body}`;
|
|
1212
|
-
const result = writeMarkdownAsset({
|
|
1302
|
+
const result = await writeMarkdownAsset({
|
|
1213
1303
|
type: "memory",
|
|
1214
1304
|
content: contentWithFrontmatter,
|
|
1215
1305
|
name: args.name,
|
|
1216
1306
|
fallbackPrefix: "memory",
|
|
1217
1307
|
force: args.force,
|
|
1308
|
+
target: args.target,
|
|
1218
1309
|
});
|
|
1219
1310
|
output("remember", { ok: true, ...result });
|
|
1220
1311
|
});
|
|
@@ -1240,17 +1331,22 @@ const importKnowledgeCommand = defineCommand({
|
|
|
1240
1331
|
description: "Overwrite an existing knowledge document with the same name",
|
|
1241
1332
|
default: false,
|
|
1242
1333
|
},
|
|
1334
|
+
target: {
|
|
1335
|
+
type: "string",
|
|
1336
|
+
description: "Override the write destination. Accepts a source name from your config; falls back to defaultWriteTarget then the working stash.",
|
|
1337
|
+
},
|
|
1243
1338
|
},
|
|
1244
1339
|
async run({ args }) {
|
|
1245
1340
|
return runWithJsonErrors(async () => {
|
|
1246
1341
|
const { content, preferredName } = readKnowledgeContent(args.source);
|
|
1247
|
-
const result = writeMarkdownAsset({
|
|
1342
|
+
const result = await writeMarkdownAsset({
|
|
1248
1343
|
type: "knowledge",
|
|
1249
1344
|
content,
|
|
1250
1345
|
name: args.name,
|
|
1251
1346
|
fallbackPrefix: "knowledge",
|
|
1252
1347
|
preferredName,
|
|
1253
1348
|
force: args.force,
|
|
1349
|
+
target: args.target,
|
|
1254
1350
|
});
|
|
1255
1351
|
output("import", { ok: true, source: args.source, ...result });
|
|
1256
1352
|
});
|
|
@@ -1266,7 +1362,7 @@ const hintsCommand = defineCommand({
|
|
|
1266
1362
|
},
|
|
1267
1363
|
run({ args }) {
|
|
1268
1364
|
if (args.detail !== "normal" && args.detail !== "full") {
|
|
1269
|
-
throw new UsageError(`Invalid value for --detail: ${args.detail}. Expected one of: normal|full
|
|
1365
|
+
throw new UsageError(`Invalid value for --detail: ${args.detail}. Expected one of: normal|full.`, "INVALID_DETAIL_VALUE");
|
|
1270
1366
|
}
|
|
1271
1367
|
process.stdout.write(loadHints(args.detail));
|
|
1272
1368
|
},
|
|
@@ -1446,14 +1542,14 @@ const vaultListCommand = defineCommand({
|
|
|
1446
1542
|
},
|
|
1447
1543
|
run({ args }) {
|
|
1448
1544
|
return runWithJsonErrors(async () => {
|
|
1449
|
-
const { listKeys } = await import("./vault.js");
|
|
1545
|
+
const { listKeys, listEntries } = await import("./commands/vault.js");
|
|
1450
1546
|
if (args.ref) {
|
|
1451
1547
|
const { name, absPath } = resolveVaultPath(args.ref);
|
|
1452
1548
|
if (!fs.existsSync(absPath)) {
|
|
1453
1549
|
throw new NotFoundError(`Vault not found: vault:${name}`);
|
|
1454
1550
|
}
|
|
1455
|
-
const
|
|
1456
|
-
output("vault-list", { ref: `vault:${name}`, path: absPath,
|
|
1551
|
+
const entries = listEntries(absPath);
|
|
1552
|
+
output("vault-list", { ref: `vault:${name}`, path: absPath, entries });
|
|
1457
1553
|
return;
|
|
1458
1554
|
}
|
|
1459
1555
|
const vaults = listVaultsRecursive(listKeys);
|
|
@@ -1468,7 +1564,7 @@ const vaultCreateCommand = defineCommand({
|
|
|
1468
1564
|
},
|
|
1469
1565
|
run({ args }) {
|
|
1470
1566
|
return runWithJsonErrors(async () => {
|
|
1471
|
-
const { createVault } = await import("./vault.js");
|
|
1567
|
+
const { createVault } = await import("./commands/vault.js");
|
|
1472
1568
|
const { name, absPath } = resolveVaultPath(args.name);
|
|
1473
1569
|
createVault(absPath);
|
|
1474
1570
|
output("vault-create", { ref: `vault:${name}`, path: absPath });
|
|
@@ -1492,7 +1588,7 @@ const vaultSetCommand = defineCommand({
|
|
|
1492
1588
|
},
|
|
1493
1589
|
run({ args }) {
|
|
1494
1590
|
return runWithJsonErrors(async () => {
|
|
1495
|
-
const { setKey } = await import("./vault.js");
|
|
1591
|
+
const { setKey } = await import("./commands/vault.js");
|
|
1496
1592
|
const { name, absPath } = resolveVaultPath(args.ref);
|
|
1497
1593
|
let realKey;
|
|
1498
1594
|
let realValue;
|
|
@@ -1518,7 +1614,7 @@ const vaultUnsetCommand = defineCommand({
|
|
|
1518
1614
|
},
|
|
1519
1615
|
run({ args }) {
|
|
1520
1616
|
return runWithJsonErrors(async () => {
|
|
1521
|
-
const { unsetKey } = await import("./vault.js");
|
|
1617
|
+
const { unsetKey } = await import("./commands/vault.js");
|
|
1522
1618
|
const { name, absPath } = resolveVaultPath(args.ref);
|
|
1523
1619
|
if (!fs.existsSync(absPath)) {
|
|
1524
1620
|
throw new NotFoundError(`Vault not found: vault:${name}`);
|
|
@@ -1544,7 +1640,7 @@ const vaultLoadCommand = defineCommand({
|
|
|
1544
1640
|
if (!fs.existsSync(absPath)) {
|
|
1545
1641
|
throw new NotFoundError(`Vault not found: vault:${name}`);
|
|
1546
1642
|
}
|
|
1547
|
-
const { buildShellExportScript } = await import("./vault.js");
|
|
1643
|
+
const { buildShellExportScript } = await import("./commands/vault.js");
|
|
1548
1644
|
const crypto = await import("node:crypto");
|
|
1549
1645
|
const os = await import("node:os");
|
|
1550
1646
|
// Parse via dotenv (no expansion, no code execution) and build a
|
|
@@ -1575,13 +1671,13 @@ const vaultShowCommand = defineCommand({
|
|
|
1575
1671
|
},
|
|
1576
1672
|
run({ args }) {
|
|
1577
1673
|
return runWithJsonErrors(async () => {
|
|
1578
|
-
const {
|
|
1674
|
+
const { listEntries } = await import("./commands/vault.js");
|
|
1579
1675
|
const { name, absPath } = resolveVaultPath(args.ref);
|
|
1580
1676
|
if (!fs.existsSync(absPath)) {
|
|
1581
1677
|
throw new NotFoundError(`Vault not found: vault:${name}`);
|
|
1582
1678
|
}
|
|
1583
|
-
const
|
|
1584
|
-
output("vault-list", { ref: `vault:${name}`, path: absPath,
|
|
1679
|
+
const entries = listEntries(absPath);
|
|
1680
|
+
output("vault-list", { ref: `vault:${name}`, path: absPath, entries });
|
|
1585
1681
|
});
|
|
1586
1682
|
},
|
|
1587
1683
|
});
|
|
@@ -1603,7 +1699,7 @@ const vaultCommand = defineCommand({
|
|
|
1603
1699
|
if (hasVaultSubcommand(args))
|
|
1604
1700
|
return;
|
|
1605
1701
|
// Default action: list all vaults
|
|
1606
|
-
const { listKeys } = await import("./vault.js");
|
|
1702
|
+
const { listKeys } = await import("./commands/vault.js");
|
|
1607
1703
|
output("vault-list", { vaults: listVaultsRecursive(listKeys) });
|
|
1608
1704
|
});
|
|
1609
1705
|
},
|
|
@@ -1616,7 +1712,7 @@ const wikiCreateCommand = defineCommand({
|
|
|
1616
1712
|
},
|
|
1617
1713
|
run({ args }) {
|
|
1618
1714
|
return runWithJsonErrors(async () => {
|
|
1619
|
-
const { createWiki } = await import("./wiki.js");
|
|
1715
|
+
const { createWiki } = await import("./wiki/wiki.js");
|
|
1620
1716
|
const stashDir = resolveStashDir();
|
|
1621
1717
|
const result = createWiki(stashDir, args.name);
|
|
1622
1718
|
output("wiki-create", result);
|
|
@@ -1646,7 +1742,7 @@ const wikiRegisterCommand = defineCommand({
|
|
|
1646
1742
|
},
|
|
1647
1743
|
run({ args }) {
|
|
1648
1744
|
return runWithJsonErrors(async () => {
|
|
1649
|
-
const { registerWikiSource } = await import("./
|
|
1745
|
+
const { registerWikiSource } = await import("./commands/source-add");
|
|
1650
1746
|
const result = await registerWikiSource({
|
|
1651
1747
|
ref: args.ref.trim(),
|
|
1652
1748
|
name: args.name,
|
|
@@ -1662,7 +1758,7 @@ const wikiListCommand = defineCommand({
|
|
|
1662
1758
|
meta: { name: "list", description: "List wikis with page/raw counts and last-modified timestamps" },
|
|
1663
1759
|
run() {
|
|
1664
1760
|
return runWithJsonErrors(async () => {
|
|
1665
|
-
const { listWikis } = await import("./wiki.js");
|
|
1761
|
+
const { listWikis } = await import("./wiki/wiki.js");
|
|
1666
1762
|
const stashDir = resolveStashDir();
|
|
1667
1763
|
const wikis = listWikis(stashDir);
|
|
1668
1764
|
output("wiki-list", { wikis });
|
|
@@ -1676,7 +1772,7 @@ const wikiShowCommand = defineCommand({
|
|
|
1676
1772
|
},
|
|
1677
1773
|
run({ args }) {
|
|
1678
1774
|
return runWithJsonErrors(async () => {
|
|
1679
|
-
const { showWiki } = await import("./wiki.js");
|
|
1775
|
+
const { showWiki } = await import("./wiki/wiki.js");
|
|
1680
1776
|
const stashDir = resolveStashDir();
|
|
1681
1777
|
const result = showWiki(stashDir, args.name);
|
|
1682
1778
|
output("wiki-show", result);
|
|
@@ -1706,9 +1802,9 @@ const wikiRemoveCommand = defineCommand({
|
|
|
1706
1802
|
if (!args.force) {
|
|
1707
1803
|
throw new UsageError("Refusing to remove without --force. Pass `--force` to confirm.");
|
|
1708
1804
|
}
|
|
1709
|
-
const withSources =
|
|
1710
|
-
const { removeWiki } = await import("./wiki.js");
|
|
1711
|
-
const { akmIndex } = await import("./indexer");
|
|
1805
|
+
const withSources = getHyphenatedBoolean(args, "with-sources");
|
|
1806
|
+
const { removeWiki } = await import("./wiki/wiki.js");
|
|
1807
|
+
const { akmIndex } = await import("./indexer/indexer");
|
|
1712
1808
|
const stashDir = resolveStashDir();
|
|
1713
1809
|
const result = removeWiki(stashDir, args.name, { withSources });
|
|
1714
1810
|
await akmIndex({ stashDir });
|
|
@@ -1726,7 +1822,7 @@ const wikiPagesCommand = defineCommand({
|
|
|
1726
1822
|
},
|
|
1727
1823
|
run({ args }) {
|
|
1728
1824
|
return runWithJsonErrors(async () => {
|
|
1729
|
-
const { listPages } = await import("./wiki.js");
|
|
1825
|
+
const { listPages } = await import("./wiki/wiki.js");
|
|
1730
1826
|
const stashDir = resolveStashDir();
|
|
1731
1827
|
const pages = listPages(stashDir, args.name);
|
|
1732
1828
|
output("wiki-pages", { wiki: args.name, pages });
|
|
@@ -1745,7 +1841,7 @@ const wikiSearchCommand = defineCommand({
|
|
|
1745
1841
|
},
|
|
1746
1842
|
run({ args }) {
|
|
1747
1843
|
return runWithJsonErrors(async () => {
|
|
1748
|
-
const { resolveWikiSource, searchInWiki } = await import("./wiki.js");
|
|
1844
|
+
const { resolveWikiSource, searchInWiki } = await import("./wiki/wiki.js");
|
|
1749
1845
|
const stashDir = resolveStashDir();
|
|
1750
1846
|
resolveWikiSource(stashDir, args.name);
|
|
1751
1847
|
const parsedLimit = args.limit ? Number(args.limit) : undefined;
|
|
@@ -1767,7 +1863,7 @@ const wikiStashCommand = defineCommand({
|
|
|
1767
1863
|
},
|
|
1768
1864
|
run({ args }) {
|
|
1769
1865
|
return runWithJsonErrors(async () => {
|
|
1770
|
-
const { stashRaw } = await import("./wiki.js");
|
|
1866
|
+
const { stashRaw } = await import("./wiki/wiki.js");
|
|
1771
1867
|
const { content, preferredName } = readKnowledgeContent(args.source);
|
|
1772
1868
|
const stashDir = resolveStashDir();
|
|
1773
1869
|
const result = stashRaw({
|
|
@@ -1792,7 +1888,7 @@ const wikiLintCommand = defineCommand({
|
|
|
1792
1888
|
async run({ args }) {
|
|
1793
1889
|
let findingCount = 0;
|
|
1794
1890
|
await runWithJsonErrors(async () => {
|
|
1795
|
-
const { lintWiki } = await import("./wiki.js");
|
|
1891
|
+
const { lintWiki } = await import("./wiki/wiki.js");
|
|
1796
1892
|
const stashDir = resolveStashDir();
|
|
1797
1893
|
const report = lintWiki(stashDir, args.name);
|
|
1798
1894
|
output("wiki-lint", report);
|
|
@@ -1812,7 +1908,7 @@ const wikiIngestCommand = defineCommand({
|
|
|
1812
1908
|
},
|
|
1813
1909
|
run({ args }) {
|
|
1814
1910
|
return runWithJsonErrors(async () => {
|
|
1815
|
-
const { buildIngestWorkflow } = await import("./wiki.js");
|
|
1911
|
+
const { buildIngestWorkflow } = await import("./wiki/wiki.js");
|
|
1816
1912
|
const stashDir = resolveStashDir();
|
|
1817
1913
|
const result = buildIngestWorkflow(stashDir, args.name);
|
|
1818
1914
|
output("wiki-ingest", result);
|
|
@@ -1841,7 +1937,7 @@ const wikiCommand = defineCommand({
|
|
|
1841
1937
|
if (hasWikiSubcommand(args))
|
|
1842
1938
|
return;
|
|
1843
1939
|
// Default action: list wikis
|
|
1844
|
-
const { listWikis } = await import("./wiki.js");
|
|
1940
|
+
const { listWikis } = await import("./wiki/wiki.js");
|
|
1845
1941
|
output("wiki-list", { wikis: listWikis(resolveStashDir()) });
|
|
1846
1942
|
});
|
|
1847
1943
|
},
|
|
@@ -1919,7 +2015,7 @@ try {
|
|
|
1919
2015
|
}
|
|
1920
2016
|
catch (error) {
|
|
1921
2017
|
const message = error instanceof Error ? error.message : String(error);
|
|
1922
|
-
const hint =
|
|
2018
|
+
const hint = extractHint(error);
|
|
1923
2019
|
const exitCode = classifyExitCode(error);
|
|
1924
2020
|
const code = error instanceof UsageError || error instanceof ConfigError || error instanceof NotFoundError
|
|
1925
2021
|
? error.code
|
|
@@ -1947,7 +2043,7 @@ async function runWithJsonErrors(fn) {
|
|
|
1947
2043
|
}
|
|
1948
2044
|
catch (error) {
|
|
1949
2045
|
const message = error instanceof Error ? error.message : String(error);
|
|
1950
|
-
const hint =
|
|
2046
|
+
const hint = extractHint(error);
|
|
1951
2047
|
const exitCode = classifyExitCode(error);
|
|
1952
2048
|
// Surface machine-readable error code from typed errors when present so
|
|
1953
2049
|
// scripts can branch on `.code` instead of message-string matching.
|
|
@@ -1958,25 +2054,14 @@ async function runWithJsonErrors(fn) {
|
|
|
1958
2054
|
process.exit(exitCode);
|
|
1959
2055
|
}
|
|
1960
2056
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
return "Run `akm list` to view your sources, then retry with one of those values.";
|
|
1970
|
-
if (message.includes("remote package fetched but asset not found"))
|
|
1971
|
-
return "The remote package was fetched but doesn't contain the requested asset. Check the asset name and type.";
|
|
1972
|
-
if (message.includes("Invalid value for --source"))
|
|
1973
|
-
return "Pick one of: stash, registry, both.";
|
|
1974
|
-
if (message.includes("Invalid value for --format"))
|
|
1975
|
-
return "Pick one of: json, jsonl, text, yaml.";
|
|
1976
|
-
if (message.includes("Invalid value for --detail"))
|
|
1977
|
-
return "Pick one of: brief, normal, full, summary, agent.";
|
|
1978
|
-
if (message.includes("expected JSON object with endpoint and model")) {
|
|
1979
|
-
return 'Quote JSON values in your shell, for example: akm config set embedding \'{"endpoint":"http://localhost:11434/v1/embeddings","model":"nomic-embed-text"}\'.';
|
|
2057
|
+
/**
|
|
2058
|
+
* Extract an actionable hint from an error instance. Hints live on the error
|
|
2059
|
+
* classes themselves (see src/errors.ts) — either supplied explicitly at the
|
|
2060
|
+
* throw site, or derived from the error code via the per-class default mapping.
|
|
2061
|
+
*/
|
|
2062
|
+
function extractHint(error) {
|
|
2063
|
+
if (error instanceof Error && "hint" in error && typeof error.hint === "function") {
|
|
2064
|
+
return error.hint();
|
|
1980
2065
|
}
|
|
1981
2066
|
return undefined;
|
|
1982
2067
|
}
|