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
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, 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
|
},
|
|
@@ -156,6 +160,10 @@ const searchCommand = defineCommand({
|
|
|
156
160
|
},
|
|
157
161
|
async run({ args }) {
|
|
158
162
|
await runWithJsonErrors(async () => {
|
|
163
|
+
const query = (args.query ?? "").trim();
|
|
164
|
+
if (!query) {
|
|
165
|
+
throw new UsageError('A search query is required. Usage: akm search "<query>" [--type <type>] [--limit <n>]', "MISSING_REQUIRED_ARGUMENT");
|
|
166
|
+
}
|
|
159
167
|
const type = args.type;
|
|
160
168
|
const limitRaw = args.limit ? parseInt(args.limit, 10) : undefined;
|
|
161
169
|
if (limitRaw !== undefined && Number.isNaN(limitRaw)) {
|
|
@@ -163,7 +171,7 @@ const searchCommand = defineCommand({
|
|
|
163
171
|
}
|
|
164
172
|
const limit = limitRaw;
|
|
165
173
|
const source = parseSearchSource(args.source);
|
|
166
|
-
const result = await akmSearch({ query
|
|
174
|
+
const result = await akmSearch({ query, type, limit, source });
|
|
167
175
|
output("search", result);
|
|
168
176
|
});
|
|
169
177
|
},
|
|
@@ -204,7 +212,7 @@ const addCommand = defineCommand({
|
|
|
204
212
|
description: "Path, URL, or registry ref (website URL, npm package, owner/repo, git URL, or local directory)",
|
|
205
213
|
required: true,
|
|
206
214
|
},
|
|
207
|
-
provider: { type: "string", description: "Provider type (e.g.
|
|
215
|
+
provider: { type: "string", description: "Provider type (e.g. website, npm). Required for URL sources." },
|
|
208
216
|
options: { type: "string", description: 'Provider options as JSON (e.g. \'{"apiKey":"key"}\').' },
|
|
209
217
|
name: { type: "string", description: "Human-friendly name for the source" },
|
|
210
218
|
writable: {
|
|
@@ -262,7 +270,7 @@ const addCommand = defineCommand({
|
|
|
262
270
|
}
|
|
263
271
|
const websiteOptions = buildWebsiteOptions(args);
|
|
264
272
|
if (args.type === "wiki") {
|
|
265
|
-
const { registerWikiSource } = await import("./
|
|
273
|
+
const { registerWikiSource } = await import("./commands/source-add");
|
|
266
274
|
const result = await registerWikiSource({
|
|
267
275
|
ref,
|
|
268
276
|
name: args.name,
|
|
@@ -378,7 +386,7 @@ const upgradeCommand = defineCommand({
|
|
|
378
386
|
output("upgrade", check);
|
|
379
387
|
return;
|
|
380
388
|
}
|
|
381
|
-
const skipChecksum =
|
|
389
|
+
const skipChecksum = getHyphenatedBoolean(args, "skip-checksum");
|
|
382
390
|
const result = await performUpgrade(check, { force: args.force, skipChecksum });
|
|
383
391
|
output("upgrade", result);
|
|
384
392
|
});
|
|
@@ -427,7 +435,6 @@ const showCommand = defineCommand({
|
|
|
427
435
|
throw new UsageError(`Unknown view mode: ${akmView}. Expected one of: full|toc|frontmatter|section|lines`);
|
|
428
436
|
}
|
|
429
437
|
}
|
|
430
|
-
// Map CLI detail level to ShowDetailLevel for the show function
|
|
431
438
|
const cliDetail = getOutputMode().detail;
|
|
432
439
|
const showDetail = cliDetail === "summary" ? "summary" : undefined;
|
|
433
440
|
const result = await akmShowUnified({ ref: args.ref, view, detail: showDetail });
|
|
@@ -671,7 +678,7 @@ const registryCommand = defineCommand({
|
|
|
671
678
|
throw new UsageError("Registry URL must start with http:// or https://");
|
|
672
679
|
}
|
|
673
680
|
if (args.url.startsWith("http://")) {
|
|
674
|
-
const allowInsecure =
|
|
681
|
+
const allowInsecure = getHyphenatedBoolean(args, "allow-insecure");
|
|
675
682
|
if (!allowInsecure) {
|
|
676
683
|
throw new UsageError("Registry URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious index. " +
|
|
677
684
|
"Use https:// or pass --allow-insecure if you have explicitly accepted the risk.");
|
|
@@ -752,11 +759,10 @@ const registryCommand = defineCommand({
|
|
|
752
759
|
},
|
|
753
760
|
async run({ args }) {
|
|
754
761
|
await runWithJsonErrors(async () => {
|
|
755
|
-
const argsRecord = args;
|
|
756
762
|
const result = await buildRegistryIndex({
|
|
757
763
|
manualEntriesPath: args.manual,
|
|
758
|
-
npmRegistryBase:
|
|
759
|
-
githubApiBase:
|
|
764
|
+
npmRegistryBase: getHyphenatedArg(args, "npm-registry"),
|
|
765
|
+
githubApiBase: getHyphenatedArg(args, "github-api"),
|
|
760
766
|
});
|
|
761
767
|
const outPath = writeRegistryIndex(result.index, args.out);
|
|
762
768
|
output("registry-build-index", {
|
|
@@ -868,24 +874,30 @@ function readKnowledgeContent(source) {
|
|
|
868
874
|
preferredName: path.basename(resolvedSource, path.extname(resolvedSource)),
|
|
869
875
|
};
|
|
870
876
|
}
|
|
871
|
-
function writeMarkdownAsset(options) {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
877
|
+
async function writeMarkdownAsset(options) {
|
|
878
|
+
// Resolve write target via the v1 precedence chain (`--target` →
|
|
879
|
+
// `defaultWriteTarget` → working stash). Per spec §10 step 5, this is the
|
|
880
|
+
// single dispatch point — `core/write-source.ts` owns all kind-branching.
|
|
881
|
+
const cfg = loadConfig();
|
|
882
|
+
const { source, config } = resolveWriteTarget(cfg, options.target);
|
|
883
|
+
const typeRoot = path.join(source.path, options.type === "knowledge" ? "knowledge" : "memories");
|
|
875
884
|
const normalizedName = normalizeMarkdownAssetName(options.name, inferAssetName(options.content, options.fallbackPrefix, options.preferredName));
|
|
885
|
+
// Pre-flight: existence + force semantics. The helper itself overwrites
|
|
886
|
+
// unconditionally; the CLI surfaces a friendlier UsageError before any
|
|
887
|
+
// disk activity when --force is absent.
|
|
876
888
|
const assetPath = resolveAssetPathFromName(options.type, typeRoot, normalizedName);
|
|
877
889
|
if (!isWithin(assetPath, typeRoot)) {
|
|
878
890
|
throw new UsageError(`Resolved ${options.type} path escapes the stash: "${normalizedName}"`);
|
|
879
891
|
}
|
|
880
892
|
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
|
|
893
|
+
throw new UsageError(`${options.type === "knowledge" ? "Knowledge" : "Memory"} "${normalizedName}" already exists. Re-run with --force to overwrite it.`, "RESOURCE_ALREADY_EXISTS");
|
|
882
894
|
}
|
|
883
|
-
|
|
884
|
-
|
|
895
|
+
// Delegate the actual write (and optional git commit/push) to the helper.
|
|
896
|
+
const result = await writeAssetToSource(source, config, { type: options.type, name: normalizedName }, options.content);
|
|
885
897
|
return {
|
|
886
|
-
ref:
|
|
887
|
-
path:
|
|
888
|
-
stashDir,
|
|
898
|
+
ref: result.ref,
|
|
899
|
+
path: result.path,
|
|
900
|
+
stashDir: source.path,
|
|
889
901
|
};
|
|
890
902
|
}
|
|
891
903
|
const workflowStartCommand = defineCommand({
|
|
@@ -1023,8 +1035,8 @@ const workflowCreateCommand = defineCommand({
|
|
|
1023
1035
|
default: false,
|
|
1024
1036
|
},
|
|
1025
1037
|
},
|
|
1026
|
-
run({ args }) {
|
|
1027
|
-
return runWithJsonErrors(() => {
|
|
1038
|
+
async run({ args }) {
|
|
1039
|
+
return runWithJsonErrors(async () => {
|
|
1028
1040
|
const namePattern = /^[a-z0-9][a-z0-9._/-]*$/;
|
|
1029
1041
|
if (!namePattern.test(args.name)) {
|
|
1030
1042
|
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 +1049,10 @@ const workflowCreateCommand = defineCommand({
|
|
|
1037
1049
|
from: args.from,
|
|
1038
1050
|
force: args.force,
|
|
1039
1051
|
});
|
|
1052
|
+
// Index the newly-written workflow so `akm workflow start` can resolve
|
|
1053
|
+
// a workflowEntryId without requiring an explicit `akm index` call
|
|
1054
|
+
// first. Uses the same incremental index path that `akm add` uses.
|
|
1055
|
+
await akmIndex({ stashDir: result.stashDir });
|
|
1040
1056
|
output("workflow-create", { ok: true, ...result });
|
|
1041
1057
|
});
|
|
1042
1058
|
},
|
|
@@ -1050,6 +1066,55 @@ const workflowTemplateCommand = defineCommand({
|
|
|
1050
1066
|
process.stdout.write(getWorkflowTemplate());
|
|
1051
1067
|
},
|
|
1052
1068
|
});
|
|
1069
|
+
const workflowValidateCommand = defineCommand({
|
|
1070
|
+
meta: {
|
|
1071
|
+
name: "validate",
|
|
1072
|
+
description: "Validate a workflow markdown file or ref and print any errors",
|
|
1073
|
+
},
|
|
1074
|
+
args: {
|
|
1075
|
+
target: {
|
|
1076
|
+
type: "positional",
|
|
1077
|
+
description: "Workflow ref (workflow:<name>) or filesystem path to a workflow .md",
|
|
1078
|
+
required: true,
|
|
1079
|
+
},
|
|
1080
|
+
},
|
|
1081
|
+
async run({ args }) {
|
|
1082
|
+
return runWithJsonErrors(async () => {
|
|
1083
|
+
const filePath = await resolveWorkflowFilePath(args.target);
|
|
1084
|
+
const { parse } = validateWorkflowSource(filePath);
|
|
1085
|
+
if (parse.ok) {
|
|
1086
|
+
output("workflow-validate", {
|
|
1087
|
+
ok: true,
|
|
1088
|
+
path: filePath,
|
|
1089
|
+
title: parse.document.title,
|
|
1090
|
+
stepCount: parse.document.steps.length,
|
|
1091
|
+
});
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
throw new UsageError(formatWorkflowErrors(filePath, parse.errors));
|
|
1095
|
+
});
|
|
1096
|
+
},
|
|
1097
|
+
});
|
|
1098
|
+
async function resolveWorkflowFilePath(target) {
|
|
1099
|
+
if (!target.startsWith("workflow:"))
|
|
1100
|
+
return target;
|
|
1101
|
+
const parsed = parseAssetRef(target);
|
|
1102
|
+
if (parsed.type !== "workflow") {
|
|
1103
|
+
throw new UsageError(`Expected a workflow ref (workflow:<name>), got "${target}".`);
|
|
1104
|
+
}
|
|
1105
|
+
const config = loadConfig();
|
|
1106
|
+
const allSources = resolveSourceEntries(undefined, config);
|
|
1107
|
+
const searchSources = resolveSourcesForOrigin(parsed.origin, allSources);
|
|
1108
|
+
for (const source of searchSources) {
|
|
1109
|
+
try {
|
|
1110
|
+
return await resolveAssetPath(source.path, "workflow", parsed.name);
|
|
1111
|
+
}
|
|
1112
|
+
catch {
|
|
1113
|
+
/* try next source */
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
throw new UsageError(`Workflow not found for ref: workflow:${parsed.name}`);
|
|
1117
|
+
}
|
|
1053
1118
|
const workflowResumeCommand = defineCommand({
|
|
1054
1119
|
meta: {
|
|
1055
1120
|
name: "resume",
|
|
@@ -1079,6 +1144,7 @@ const workflowCommand = defineCommand({
|
|
|
1079
1144
|
create: workflowCreateCommand,
|
|
1080
1145
|
template: workflowTemplateCommand,
|
|
1081
1146
|
resume: workflowResumeCommand,
|
|
1147
|
+
validate: workflowValidateCommand,
|
|
1082
1148
|
},
|
|
1083
1149
|
run({ args }) {
|
|
1084
1150
|
return runWithJsonErrors(() => {
|
|
@@ -1108,6 +1174,10 @@ const rememberCommand = defineCommand({
|
|
|
1108
1174
|
description: "Overwrite an existing memory with the same name",
|
|
1109
1175
|
default: false,
|
|
1110
1176
|
},
|
|
1177
|
+
description: {
|
|
1178
|
+
type: "string",
|
|
1179
|
+
description: "Short description written to frontmatter (persisted as the memory's description field)",
|
|
1180
|
+
},
|
|
1111
1181
|
tag: {
|
|
1112
1182
|
type: "string",
|
|
1113
1183
|
description: "Tag to add to the memory (repeatable: --tag foo --tag bar)",
|
|
@@ -1130,6 +1200,10 @@ const rememberCommand = defineCommand({
|
|
|
1130
1200
|
description: "Call the configured LLM to propose tags and description (requires LLM config)",
|
|
1131
1201
|
default: false,
|
|
1132
1202
|
},
|
|
1203
|
+
target: {
|
|
1204
|
+
type: "string",
|
|
1205
|
+
description: "Override the write destination. Accepts a source name from your config; falls back to defaultWriteTarget then the working stash.",
|
|
1206
|
+
},
|
|
1133
1207
|
},
|
|
1134
1208
|
async run({ args }) {
|
|
1135
1209
|
return runWithJsonErrors(async () => {
|
|
@@ -1138,15 +1212,15 @@ const rememberCommand = defineCommand({
|
|
|
1138
1212
|
// Collect all --tag occurrences directly from process.argv because citty
|
|
1139
1213
|
// only exposes the last value for repeated string flags.
|
|
1140
1214
|
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.
|
|
1215
|
+
const hasStructuredArgs = rawTags.length > 0 || !!args.expires || !!args.source || !!args.description || args.auto || args.enrich;
|
|
1143
1216
|
if (!hasStructuredArgs) {
|
|
1144
|
-
const result = writeMarkdownAsset({
|
|
1217
|
+
const result = await writeMarkdownAsset({
|
|
1145
1218
|
type: "memory",
|
|
1146
1219
|
content: body,
|
|
1147
1220
|
name: args.name,
|
|
1148
1221
|
fallbackPrefix: "memory",
|
|
1149
1222
|
force: args.force,
|
|
1223
|
+
target: args.target,
|
|
1150
1224
|
});
|
|
1151
1225
|
output("remember", { ok: true, ...result });
|
|
1152
1226
|
return;
|
|
@@ -1154,7 +1228,8 @@ const rememberCommand = defineCommand({
|
|
|
1154
1228
|
// ── Accumulate metadata from all three modes ──────────────────────────
|
|
1155
1229
|
// Start with CLI args (Mode 1: always)
|
|
1156
1230
|
const tags = [...rawTags];
|
|
1157
|
-
|
|
1231
|
+
// --description is persisted as-is; LLM enrichment may fill it if absent.
|
|
1232
|
+
let description = args.description || undefined;
|
|
1158
1233
|
let source = args.source;
|
|
1159
1234
|
let observed_at;
|
|
1160
1235
|
let expires;
|
|
@@ -1209,12 +1284,13 @@ const rememberCommand = defineCommand({
|
|
|
1209
1284
|
subjective,
|
|
1210
1285
|
});
|
|
1211
1286
|
const contentWithFrontmatter = `${frontmatterBlock}\n${body}`;
|
|
1212
|
-
const result = writeMarkdownAsset({
|
|
1287
|
+
const result = await writeMarkdownAsset({
|
|
1213
1288
|
type: "memory",
|
|
1214
1289
|
content: contentWithFrontmatter,
|
|
1215
1290
|
name: args.name,
|
|
1216
1291
|
fallbackPrefix: "memory",
|
|
1217
1292
|
force: args.force,
|
|
1293
|
+
target: args.target,
|
|
1218
1294
|
});
|
|
1219
1295
|
output("remember", { ok: true, ...result });
|
|
1220
1296
|
});
|
|
@@ -1240,17 +1316,22 @@ const importKnowledgeCommand = defineCommand({
|
|
|
1240
1316
|
description: "Overwrite an existing knowledge document with the same name",
|
|
1241
1317
|
default: false,
|
|
1242
1318
|
},
|
|
1319
|
+
target: {
|
|
1320
|
+
type: "string",
|
|
1321
|
+
description: "Override the write destination. Accepts a source name from your config; falls back to defaultWriteTarget then the working stash.",
|
|
1322
|
+
},
|
|
1243
1323
|
},
|
|
1244
1324
|
async run({ args }) {
|
|
1245
1325
|
return runWithJsonErrors(async () => {
|
|
1246
1326
|
const { content, preferredName } = readKnowledgeContent(args.source);
|
|
1247
|
-
const result = writeMarkdownAsset({
|
|
1327
|
+
const result = await writeMarkdownAsset({
|
|
1248
1328
|
type: "knowledge",
|
|
1249
1329
|
content,
|
|
1250
1330
|
name: args.name,
|
|
1251
1331
|
fallbackPrefix: "knowledge",
|
|
1252
1332
|
preferredName,
|
|
1253
1333
|
force: args.force,
|
|
1334
|
+
target: args.target,
|
|
1254
1335
|
});
|
|
1255
1336
|
output("import", { ok: true, source: args.source, ...result });
|
|
1256
1337
|
});
|
|
@@ -1266,7 +1347,7 @@ const hintsCommand = defineCommand({
|
|
|
1266
1347
|
},
|
|
1267
1348
|
run({ args }) {
|
|
1268
1349
|
if (args.detail !== "normal" && args.detail !== "full") {
|
|
1269
|
-
throw new UsageError(`Invalid value for --detail: ${args.detail}. Expected one of: normal|full
|
|
1350
|
+
throw new UsageError(`Invalid value for --detail: ${args.detail}. Expected one of: normal|full.`, "INVALID_DETAIL_VALUE");
|
|
1270
1351
|
}
|
|
1271
1352
|
process.stdout.write(loadHints(args.detail));
|
|
1272
1353
|
},
|
|
@@ -1446,14 +1527,14 @@ const vaultListCommand = defineCommand({
|
|
|
1446
1527
|
},
|
|
1447
1528
|
run({ args }) {
|
|
1448
1529
|
return runWithJsonErrors(async () => {
|
|
1449
|
-
const { listKeys } = await import("./vault.js");
|
|
1530
|
+
const { listKeys, listEntries } = await import("./commands/vault.js");
|
|
1450
1531
|
if (args.ref) {
|
|
1451
1532
|
const { name, absPath } = resolveVaultPath(args.ref);
|
|
1452
1533
|
if (!fs.existsSync(absPath)) {
|
|
1453
1534
|
throw new NotFoundError(`Vault not found: vault:${name}`);
|
|
1454
1535
|
}
|
|
1455
|
-
const
|
|
1456
|
-
output("vault-list", { ref: `vault:${name}`, path: absPath,
|
|
1536
|
+
const entries = listEntries(absPath);
|
|
1537
|
+
output("vault-list", { ref: `vault:${name}`, path: absPath, entries });
|
|
1457
1538
|
return;
|
|
1458
1539
|
}
|
|
1459
1540
|
const vaults = listVaultsRecursive(listKeys);
|
|
@@ -1468,7 +1549,7 @@ const vaultCreateCommand = defineCommand({
|
|
|
1468
1549
|
},
|
|
1469
1550
|
run({ args }) {
|
|
1470
1551
|
return runWithJsonErrors(async () => {
|
|
1471
|
-
const { createVault } = await import("./vault.js");
|
|
1552
|
+
const { createVault } = await import("./commands/vault.js");
|
|
1472
1553
|
const { name, absPath } = resolveVaultPath(args.name);
|
|
1473
1554
|
createVault(absPath);
|
|
1474
1555
|
output("vault-create", { ref: `vault:${name}`, path: absPath });
|
|
@@ -1492,7 +1573,7 @@ const vaultSetCommand = defineCommand({
|
|
|
1492
1573
|
},
|
|
1493
1574
|
run({ args }) {
|
|
1494
1575
|
return runWithJsonErrors(async () => {
|
|
1495
|
-
const { setKey } = await import("./vault.js");
|
|
1576
|
+
const { setKey } = await import("./commands/vault.js");
|
|
1496
1577
|
const { name, absPath } = resolveVaultPath(args.ref);
|
|
1497
1578
|
let realKey;
|
|
1498
1579
|
let realValue;
|
|
@@ -1518,7 +1599,7 @@ const vaultUnsetCommand = defineCommand({
|
|
|
1518
1599
|
},
|
|
1519
1600
|
run({ args }) {
|
|
1520
1601
|
return runWithJsonErrors(async () => {
|
|
1521
|
-
const { unsetKey } = await import("./vault.js");
|
|
1602
|
+
const { unsetKey } = await import("./commands/vault.js");
|
|
1522
1603
|
const { name, absPath } = resolveVaultPath(args.ref);
|
|
1523
1604
|
if (!fs.existsSync(absPath)) {
|
|
1524
1605
|
throw new NotFoundError(`Vault not found: vault:${name}`);
|
|
@@ -1544,7 +1625,7 @@ const vaultLoadCommand = defineCommand({
|
|
|
1544
1625
|
if (!fs.existsSync(absPath)) {
|
|
1545
1626
|
throw new NotFoundError(`Vault not found: vault:${name}`);
|
|
1546
1627
|
}
|
|
1547
|
-
const { buildShellExportScript } = await import("./vault.js");
|
|
1628
|
+
const { buildShellExportScript } = await import("./commands/vault.js");
|
|
1548
1629
|
const crypto = await import("node:crypto");
|
|
1549
1630
|
const os = await import("node:os");
|
|
1550
1631
|
// Parse via dotenv (no expansion, no code execution) and build a
|
|
@@ -1575,13 +1656,13 @@ const vaultShowCommand = defineCommand({
|
|
|
1575
1656
|
},
|
|
1576
1657
|
run({ args }) {
|
|
1577
1658
|
return runWithJsonErrors(async () => {
|
|
1578
|
-
const {
|
|
1659
|
+
const { listEntries } = await import("./commands/vault.js");
|
|
1579
1660
|
const { name, absPath } = resolveVaultPath(args.ref);
|
|
1580
1661
|
if (!fs.existsSync(absPath)) {
|
|
1581
1662
|
throw new NotFoundError(`Vault not found: vault:${name}`);
|
|
1582
1663
|
}
|
|
1583
|
-
const
|
|
1584
|
-
output("vault-list", { ref: `vault:${name}`, path: absPath,
|
|
1664
|
+
const entries = listEntries(absPath);
|
|
1665
|
+
output("vault-list", { ref: `vault:${name}`, path: absPath, entries });
|
|
1585
1666
|
});
|
|
1586
1667
|
},
|
|
1587
1668
|
});
|
|
@@ -1603,7 +1684,7 @@ const vaultCommand = defineCommand({
|
|
|
1603
1684
|
if (hasVaultSubcommand(args))
|
|
1604
1685
|
return;
|
|
1605
1686
|
// Default action: list all vaults
|
|
1606
|
-
const { listKeys } = await import("./vault.js");
|
|
1687
|
+
const { listKeys } = await import("./commands/vault.js");
|
|
1607
1688
|
output("vault-list", { vaults: listVaultsRecursive(listKeys) });
|
|
1608
1689
|
});
|
|
1609
1690
|
},
|
|
@@ -1616,7 +1697,7 @@ const wikiCreateCommand = defineCommand({
|
|
|
1616
1697
|
},
|
|
1617
1698
|
run({ args }) {
|
|
1618
1699
|
return runWithJsonErrors(async () => {
|
|
1619
|
-
const { createWiki } = await import("./wiki.js");
|
|
1700
|
+
const { createWiki } = await import("./wiki/wiki.js");
|
|
1620
1701
|
const stashDir = resolveStashDir();
|
|
1621
1702
|
const result = createWiki(stashDir, args.name);
|
|
1622
1703
|
output("wiki-create", result);
|
|
@@ -1646,7 +1727,7 @@ const wikiRegisterCommand = defineCommand({
|
|
|
1646
1727
|
},
|
|
1647
1728
|
run({ args }) {
|
|
1648
1729
|
return runWithJsonErrors(async () => {
|
|
1649
|
-
const { registerWikiSource } = await import("./
|
|
1730
|
+
const { registerWikiSource } = await import("./commands/source-add");
|
|
1650
1731
|
const result = await registerWikiSource({
|
|
1651
1732
|
ref: args.ref.trim(),
|
|
1652
1733
|
name: args.name,
|
|
@@ -1662,7 +1743,7 @@ const wikiListCommand = defineCommand({
|
|
|
1662
1743
|
meta: { name: "list", description: "List wikis with page/raw counts and last-modified timestamps" },
|
|
1663
1744
|
run() {
|
|
1664
1745
|
return runWithJsonErrors(async () => {
|
|
1665
|
-
const { listWikis } = await import("./wiki.js");
|
|
1746
|
+
const { listWikis } = await import("./wiki/wiki.js");
|
|
1666
1747
|
const stashDir = resolveStashDir();
|
|
1667
1748
|
const wikis = listWikis(stashDir);
|
|
1668
1749
|
output("wiki-list", { wikis });
|
|
@@ -1676,7 +1757,7 @@ const wikiShowCommand = defineCommand({
|
|
|
1676
1757
|
},
|
|
1677
1758
|
run({ args }) {
|
|
1678
1759
|
return runWithJsonErrors(async () => {
|
|
1679
|
-
const { showWiki } = await import("./wiki.js");
|
|
1760
|
+
const { showWiki } = await import("./wiki/wiki.js");
|
|
1680
1761
|
const stashDir = resolveStashDir();
|
|
1681
1762
|
const result = showWiki(stashDir, args.name);
|
|
1682
1763
|
output("wiki-show", result);
|
|
@@ -1706,9 +1787,9 @@ const wikiRemoveCommand = defineCommand({
|
|
|
1706
1787
|
if (!args.force) {
|
|
1707
1788
|
throw new UsageError("Refusing to remove without --force. Pass `--force` to confirm.");
|
|
1708
1789
|
}
|
|
1709
|
-
const withSources =
|
|
1710
|
-
const { removeWiki } = await import("./wiki.js");
|
|
1711
|
-
const { akmIndex } = await import("./indexer");
|
|
1790
|
+
const withSources = getHyphenatedBoolean(args, "with-sources");
|
|
1791
|
+
const { removeWiki } = await import("./wiki/wiki.js");
|
|
1792
|
+
const { akmIndex } = await import("./indexer/indexer");
|
|
1712
1793
|
const stashDir = resolveStashDir();
|
|
1713
1794
|
const result = removeWiki(stashDir, args.name, { withSources });
|
|
1714
1795
|
await akmIndex({ stashDir });
|
|
@@ -1726,7 +1807,7 @@ const wikiPagesCommand = defineCommand({
|
|
|
1726
1807
|
},
|
|
1727
1808
|
run({ args }) {
|
|
1728
1809
|
return runWithJsonErrors(async () => {
|
|
1729
|
-
const { listPages } = await import("./wiki.js");
|
|
1810
|
+
const { listPages } = await import("./wiki/wiki.js");
|
|
1730
1811
|
const stashDir = resolveStashDir();
|
|
1731
1812
|
const pages = listPages(stashDir, args.name);
|
|
1732
1813
|
output("wiki-pages", { wiki: args.name, pages });
|
|
@@ -1745,7 +1826,7 @@ const wikiSearchCommand = defineCommand({
|
|
|
1745
1826
|
},
|
|
1746
1827
|
run({ args }) {
|
|
1747
1828
|
return runWithJsonErrors(async () => {
|
|
1748
|
-
const { resolveWikiSource, searchInWiki } = await import("./wiki.js");
|
|
1829
|
+
const { resolveWikiSource, searchInWiki } = await import("./wiki/wiki.js");
|
|
1749
1830
|
const stashDir = resolveStashDir();
|
|
1750
1831
|
resolveWikiSource(stashDir, args.name);
|
|
1751
1832
|
const parsedLimit = args.limit ? Number(args.limit) : undefined;
|
|
@@ -1767,7 +1848,7 @@ const wikiStashCommand = defineCommand({
|
|
|
1767
1848
|
},
|
|
1768
1849
|
run({ args }) {
|
|
1769
1850
|
return runWithJsonErrors(async () => {
|
|
1770
|
-
const { stashRaw } = await import("./wiki.js");
|
|
1851
|
+
const { stashRaw } = await import("./wiki/wiki.js");
|
|
1771
1852
|
const { content, preferredName } = readKnowledgeContent(args.source);
|
|
1772
1853
|
const stashDir = resolveStashDir();
|
|
1773
1854
|
const result = stashRaw({
|
|
@@ -1792,7 +1873,7 @@ const wikiLintCommand = defineCommand({
|
|
|
1792
1873
|
async run({ args }) {
|
|
1793
1874
|
let findingCount = 0;
|
|
1794
1875
|
await runWithJsonErrors(async () => {
|
|
1795
|
-
const { lintWiki } = await import("./wiki.js");
|
|
1876
|
+
const { lintWiki } = await import("./wiki/wiki.js");
|
|
1796
1877
|
const stashDir = resolveStashDir();
|
|
1797
1878
|
const report = lintWiki(stashDir, args.name);
|
|
1798
1879
|
output("wiki-lint", report);
|
|
@@ -1812,7 +1893,7 @@ const wikiIngestCommand = defineCommand({
|
|
|
1812
1893
|
},
|
|
1813
1894
|
run({ args }) {
|
|
1814
1895
|
return runWithJsonErrors(async () => {
|
|
1815
|
-
const { buildIngestWorkflow } = await import("./wiki.js");
|
|
1896
|
+
const { buildIngestWorkflow } = await import("./wiki/wiki.js");
|
|
1816
1897
|
const stashDir = resolveStashDir();
|
|
1817
1898
|
const result = buildIngestWorkflow(stashDir, args.name);
|
|
1818
1899
|
output("wiki-ingest", result);
|
|
@@ -1841,7 +1922,7 @@ const wikiCommand = defineCommand({
|
|
|
1841
1922
|
if (hasWikiSubcommand(args))
|
|
1842
1923
|
return;
|
|
1843
1924
|
// Default action: list wikis
|
|
1844
|
-
const { listWikis } = await import("./wiki.js");
|
|
1925
|
+
const { listWikis } = await import("./wiki/wiki.js");
|
|
1845
1926
|
output("wiki-list", { wikis: listWikis(resolveStashDir()) });
|
|
1846
1927
|
});
|
|
1847
1928
|
},
|
|
@@ -1919,7 +2000,7 @@ try {
|
|
|
1919
2000
|
}
|
|
1920
2001
|
catch (error) {
|
|
1921
2002
|
const message = error instanceof Error ? error.message : String(error);
|
|
1922
|
-
const hint =
|
|
2003
|
+
const hint = extractHint(error);
|
|
1923
2004
|
const exitCode = classifyExitCode(error);
|
|
1924
2005
|
const code = error instanceof UsageError || error instanceof ConfigError || error instanceof NotFoundError
|
|
1925
2006
|
? error.code
|
|
@@ -1947,7 +2028,7 @@ async function runWithJsonErrors(fn) {
|
|
|
1947
2028
|
}
|
|
1948
2029
|
catch (error) {
|
|
1949
2030
|
const message = error instanceof Error ? error.message : String(error);
|
|
1950
|
-
const hint =
|
|
2031
|
+
const hint = extractHint(error);
|
|
1951
2032
|
const exitCode = classifyExitCode(error);
|
|
1952
2033
|
// Surface machine-readable error code from typed errors when present so
|
|
1953
2034
|
// scripts can branch on `.code` instead of message-string matching.
|
|
@@ -1958,25 +2039,14 @@ async function runWithJsonErrors(fn) {
|
|
|
1958
2039
|
process.exit(exitCode);
|
|
1959
2040
|
}
|
|
1960
2041
|
}
|
|
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"}\'.';
|
|
2042
|
+
/**
|
|
2043
|
+
* Extract an actionable hint from an error instance. Hints live on the error
|
|
2044
|
+
* classes themselves (see src/errors.ts) — either supplied explicitly at the
|
|
2045
|
+
* throw site, or derived from the error code via the per-class default mapping.
|
|
2046
|
+
*/
|
|
2047
|
+
function extractHint(error) {
|
|
2048
|
+
if (error instanceof Error && "hint" in error && typeof error.hint === "function") {
|
|
2049
|
+
return error.hint();
|
|
1980
2050
|
}
|
|
1981
2051
|
return undefined;
|
|
1982
2052
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { getAssetTypes } from "
|
|
4
|
+
import { getAssetTypes } from "../core/asset-spec";
|
|
5
5
|
// ── Known flag values ────────────────────────────────────────────────────────
|
|
6
6
|
const FLAG_VALUES = {
|
|
7
7
|
"--format": ["json", "text", "yaml", "jsonl"],
|