akm-cli 0.5.0 → 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.
Files changed (118) hide show
  1. package/CHANGELOG.md +53 -5
  2. package/README.md +9 -9
  3. package/dist/cli.js +379 -1448
  4. package/dist/{completions.js → commands/completions.js} +1 -1
  5. package/dist/{config-cli.js → commands/config-cli.js} +109 -11
  6. package/dist/commands/curate.js +263 -0
  7. package/dist/{info.js → commands/info.js} +17 -11
  8. package/dist/{init.js → commands/init.js} +4 -4
  9. package/dist/{install-audit.js → commands/install-audit.js} +14 -2
  10. package/dist/{installed-kits.js → commands/installed-stashes.js} +122 -50
  11. package/dist/commands/migration-help.js +141 -0
  12. package/dist/{registry-search.js → commands/registry-search.js} +68 -9
  13. package/dist/commands/remember.js +178 -0
  14. package/dist/{stash-search.js → commands/search.js} +28 -69
  15. package/dist/{self-update.js → commands/self-update.js} +3 -3
  16. package/dist/{stash-show.js → commands/show.js} +106 -81
  17. package/dist/{stash-add.js → commands/source-add.js} +133 -67
  18. package/dist/{stash-clone.js → commands/source-clone.js} +15 -13
  19. package/dist/{stash-source-manage.js → commands/source-manage.js} +24 -24
  20. package/dist/{vault.js → commands/vault.js} +43 -0
  21. package/dist/{stash-ref.js → core/asset-ref.js} +4 -4
  22. package/dist/{asset-registry.js → core/asset-registry.js} +30 -6
  23. package/dist/{asset-spec.js → core/asset-spec.js} +13 -6
  24. package/dist/{common.js → core/common.js} +147 -50
  25. package/dist/{config.js → core/config.js} +288 -29
  26. package/dist/core/errors.js +90 -0
  27. package/dist/{frontmatter.js → core/frontmatter.js} +64 -8
  28. package/dist/{paths.js → core/paths.js} +4 -4
  29. package/dist/core/write-source.js +280 -0
  30. package/dist/{local-search.js → indexer/db-search.js} +49 -32
  31. package/dist/{db.js → indexer/db.js} +210 -81
  32. package/dist/{file-context.js → indexer/file-context.js} +3 -3
  33. package/dist/{indexer.js → indexer/indexer.js} +153 -30
  34. package/dist/{manifest.js → indexer/manifest.js} +10 -10
  35. package/dist/{matchers.js → indexer/matchers.js} +4 -7
  36. package/dist/{metadata.js → indexer/metadata.js} +9 -5
  37. package/dist/{search-source.js → indexer/search-source.js} +97 -55
  38. package/dist/{semantic-status.js → indexer/semantic-status.js} +2 -2
  39. package/dist/{walker.js → indexer/walker.js} +1 -1
  40. package/dist/{lockfile.js → integrations/lockfile.js} +29 -2
  41. package/dist/{llm.js → llm/client.js} +12 -48
  42. package/dist/llm/embedder.js +127 -0
  43. package/dist/llm/embedders/cache.js +47 -0
  44. package/dist/llm/embedders/local.js +152 -0
  45. package/dist/llm/embedders/remote.js +121 -0
  46. package/dist/llm/embedders/types.js +39 -0
  47. package/dist/llm/metadata-enhance.js +53 -0
  48. package/dist/output/cli-hints.js +301 -0
  49. package/dist/output/context.js +95 -0
  50. package/dist/{renderers.js → output/renderers.js} +57 -61
  51. package/dist/output/shapes.js +212 -0
  52. package/dist/output/text.js +520 -0
  53. package/dist/{registry-build-index.js → registry/build-index.js} +48 -32
  54. package/dist/{create-provider-registry.js → registry/create-provider-registry.js} +6 -2
  55. package/dist/registry/factory.js +33 -0
  56. package/dist/{origin-resolve.js → registry/origin-resolve.js} +1 -1
  57. package/dist/registry/providers/index.js +11 -0
  58. package/dist/{providers → registry/providers}/skills-sh.js +60 -4
  59. package/dist/{providers → registry/providers}/static-index.js +126 -56
  60. package/dist/registry/providers/types.js +25 -0
  61. package/dist/{registry-resolve.js → registry/resolve.js} +10 -6
  62. package/dist/{detect.js → setup/detect.js} +0 -27
  63. package/dist/{ripgrep-install.js → setup/ripgrep-install.js} +1 -1
  64. package/dist/{ripgrep-resolve.js → setup/ripgrep-resolve.js} +2 -2
  65. package/dist/{setup.js → setup/setup.js} +162 -129
  66. package/dist/setup/steps.js +45 -0
  67. package/dist/{kit-include.js → sources/include.js} +1 -1
  68. package/dist/sources/provider-factory.js +36 -0
  69. package/dist/sources/provider.js +21 -0
  70. package/dist/sources/providers/filesystem.js +35 -0
  71. package/dist/{stash-providers → sources/providers}/git.js +218 -28
  72. package/dist/{stash-providers → sources/providers}/index.js +4 -4
  73. package/dist/sources/providers/install-types.js +14 -0
  74. package/dist/sources/providers/npm.js +160 -0
  75. package/dist/sources/providers/provider-utils.js +173 -0
  76. package/dist/sources/providers/sync-from-ref.js +45 -0
  77. package/dist/sources/providers/tar-utils.js +154 -0
  78. package/dist/{stash-providers → sources/providers}/website.js +60 -20
  79. package/dist/{stash-resolve.js → sources/resolve.js} +13 -12
  80. package/dist/{wiki.js → wiki/wiki.js} +18 -17
  81. package/dist/{workflow-authoring.js → workflows/authoring.js} +48 -17
  82. package/dist/{workflow-cli.js → workflows/cli.js} +2 -1
  83. package/dist/{workflow-db.js → workflows/db.js} +1 -1
  84. package/dist/workflows/document-cache.js +20 -0
  85. package/dist/workflows/parser.js +379 -0
  86. package/dist/workflows/renderer.js +78 -0
  87. package/dist/{workflow-runs.js → workflows/runs.js} +84 -30
  88. package/dist/workflows/schema.js +11 -0
  89. package/dist/workflows/validator.js +48 -0
  90. package/docs/README.md +30 -0
  91. package/docs/migration/release-notes/0.0.13.md +4 -0
  92. package/docs/migration/release-notes/0.1.0.md +6 -0
  93. package/docs/migration/release-notes/0.2.0.md +6 -0
  94. package/docs/migration/release-notes/0.3.0.md +5 -0
  95. package/docs/migration/release-notes/0.5.0.md +6 -0
  96. package/docs/migration/release-notes/0.6.0.md +75 -0
  97. package/docs/migration/release-notes/README.md +21 -0
  98. package/package.json +3 -2
  99. package/dist/embedder.js +0 -351
  100. package/dist/errors.js +0 -34
  101. package/dist/migration-help.js +0 -110
  102. package/dist/registry-factory.js +0 -19
  103. package/dist/registry-install.js +0 -532
  104. package/dist/ripgrep.js +0 -2
  105. package/dist/stash-provider-factory.js +0 -35
  106. package/dist/stash-provider.js +0 -1
  107. package/dist/stash-providers/filesystem.js +0 -41
  108. package/dist/stash-providers/openviking.js +0 -348
  109. package/dist/stash-providers/provider-utils.js +0 -11
  110. package/dist/stash-types.js +0 -1
  111. package/dist/workflow-markdown.js +0 -251
  112. /package/dist/{markdown.js → core/markdown.js} +0 -0
  113. /package/dist/{warn.js → core/warn.js} +0 -0
  114. /package/dist/{search-fields.js → indexer/search-fields.js} +0 -0
  115. /package/dist/{usage-events.js → indexer/usage-events.js} +0 -0
  116. /package/dist/{github.js → integrations/github.js} +0 -0
  117. /package/dist/{registry-provider.js → registry/types.js} +0 -0
  118. /package/dist/{registry-types.js → sources/types.js} +0 -0
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Embedded "agent CLI hints" rendered by `akm hints` when no other source
3
+ * is available.
4
+ *
5
+ * Extracted from `src/cli.ts` so it does not bloat the CLI module and so
6
+ * docs/CI tooling can re-use the same constants. Two flavors:
7
+ * `EMBEDDED_HINTS` (default reference, ~40 lines) and
8
+ * `EMBEDDED_HINTS_FULL` (`--detail full`, ~250 lines).
9
+ */
10
+ const EMBEDDED_HINTS = `# akm CLI
11
+
12
+ You have access to a searchable library of scripts, skills, commands, agents, knowledge documents, workflows, wikis, and memories via \`akm\`. Search your sources first before writing something from scratch.
13
+
14
+ ## Quick Reference
15
+
16
+ \`\`\`sh
17
+ akm search "<query>" # Search all sources
18
+ akm curate "<task>" # Curate the best matches for a task
19
+ akm search "<query>" --type workflow # Filter to workflow assets
20
+ akm search "<query>" --source both # Also search registries
21
+ akm show <ref> # View asset details
22
+ akm workflow next <ref> # Start or resume a workflow
23
+ akm remember "Deployment needs VPN access" # Record a memory in your stash
24
+ akm import ./notes/release-checklist.md # Import a knowledge doc into your stash
25
+ akm wiki list # List available wikis
26
+ akm wiki ingest <name> # Print the ingest workflow for a wiki
27
+ akm feedback <ref> --positive|--negative # Record whether an asset helped
28
+ akm add <ref> # Add a source (npm, GitHub, git, local dir)
29
+ akm clone <ref> # Copy an asset to the working stash (optional --dest arg to clone to specific location)
30
+ akm save # Commit (and push if writable remote) changes in the primary stash
31
+ akm registry search "<query>" # Search all registries
32
+ \`\`\`
33
+
34
+ ## Primary Asset Types
35
+
36
+ | Type | What \`akm show\` returns |
37
+ | --- | --- |
38
+ | script | A \`run\` command you can execute directly |
39
+ | skill | Instructions to follow (read the full content) |
40
+ | command | A prompt template with placeholders to fill in |
41
+ | agent | A system prompt with model and tool hints |
42
+ | knowledge | A reference doc (use \`toc\` or \`section "..."\` to navigate) |
43
+ | workflow | Parsed steps plus workflow-specific execution commands |
44
+ | memory | Recalled context (read the content for background information) |
45
+ | vault | Key names only; use vault commands to inspect or load values safely |
46
+ | wiki | A page in a multi-wiki knowledge base. For any wiki task, start with \`akm wiki list\`, then \`akm wiki ingest <name>\` for the workflow. Run \`akm wiki -h\` for the full surface. |
47
+
48
+ When an asset meaningfully helps or fails, record that with \`akm feedback\` so
49
+ future search ranking can learn from real usage.
50
+
51
+ Run \`akm -h\` for the full command reference.
52
+ `;
53
+ const EMBEDDED_HINTS_FULL = `# akm CLI — Full Reference
54
+
55
+ You have access to a searchable library of scripts, skills, commands, agents, knowledge documents, workflows, wikis, and memories via \`akm\`. Search your sources first before writing something from scratch.
56
+
57
+ ## Search
58
+
59
+ \`\`\`sh
60
+ akm search "<query>" # Search all sources
61
+ akm curate "<task>" # Curate the best matches for a task
62
+ akm search "<query>" --type workflow # Filter by asset type
63
+ akm search "<query>" --source both # Also search registries
64
+ akm search "<query>" --source registry # Search registries only
65
+ akm search "<query>" --limit 10 # Limit results
66
+ akm search "<query>" --detail full # Include scores, paths, timing
67
+ \`\`\`
68
+
69
+ | Flag | Values | Default |
70
+ | --- | --- | --- |
71
+ | \`--type\` | \`skill\`, \`command\`, \`agent\`, \`knowledge\`, \`workflow\`, \`script\`, \`memory\`, \`vault\`, \`wiki\`, \`any\` | \`any\` |
72
+ | \`--source\` | \`stash\`, \`registry\`, \`both\` | \`stash\` |
73
+ | \`--limit\` | number | \`20\` |
74
+ | \`--format\` | \`json\`, \`jsonl\`, \`text\`, \`yaml\` | \`json\` |
75
+ | \`--detail\` | \`brief\`, \`normal\`, \`full\`, \`summary\`, \`agent\` | \`brief\` |
76
+ | \`--for-agent\` | boolean (deprecated — use \`--detail agent\`) | \`false\` |
77
+
78
+ ## Curate
79
+
80
+ Combine search + follow-up hints into a dense summary for a task or prompt.
81
+
82
+ \`\`\`sh
83
+ akm curate "plan a release" # Pick top matches across asset types
84
+ akm curate "deploy a Bun app" --limit 3 # Keep the summary shorter
85
+ akm curate "review architecture" --type workflow # Restrict to one asset type
86
+ \`\`\`
87
+
88
+ ## Show
89
+
90
+ Display an asset by ref. Knowledge assets support view modes as positional arguments.
91
+
92
+ \`\`\`sh
93
+ akm show script:deploy.sh # Show script (returns run command)
94
+ akm show skill:code-review # Show skill (returns full content)
95
+ akm show command:release # Show command (returns template)
96
+ akm show agent:architect # Show agent (returns system prompt)
97
+ akm show workflow:ship-release # Show parsed workflow steps
98
+ akm show knowledge:guide toc # Table of contents
99
+ akm show knowledge:guide section "Auth" # Specific section
100
+ akm show knowledge:guide lines 10 30 # Line range
101
+ akm show knowledge:my-doc # Show content (local or remote)
102
+ \`\`\`
103
+
104
+ | Type | Key fields returned |
105
+ | --- | --- |
106
+ | script | \`run\`, \`setup\`, \`cwd\` |
107
+ | skill | \`content\` (full SKILL.md) |
108
+ | command | \`template\`, \`description\`, \`parameters\` |
109
+ | agent | \`prompt\`, \`description\`, \`modelHint\`, \`toolPolicy\` |
110
+ | knowledge | \`content\` (with view modes: \`full\`, \`toc\`, \`frontmatter\`, \`section\`, \`lines\`) |
111
+ | workflow | \`workflowTitle\`, \`workflowParameters\`, \`steps\` |
112
+ | memory | \`content\` (recalled context) |
113
+ | vault | \`keys\`, \`comments\` |
114
+ | wiki | \`content\` (same view modes as knowledge). For any wiki task, run \`akm wiki list\` then \`akm wiki ingest <name>\` for the workflow. |
115
+
116
+ ## Capture Knowledge While You Work
117
+
118
+ \`\`\`sh
119
+ akm remember "Deployment needs VPN access" # Record a memory in your stash
120
+ akm remember --name release-retro < notes.md # Save multiline memory from stdin
121
+ akm import ./docs/auth-flow.md # Import a file as knowledge
122
+ akm import - --name scratch-notes < notes.md # Import stdin as a knowledge doc
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
125
+ akm workflow next workflow:ship-release # Start or resume the next workflow step
126
+ akm feedback skill:code-review --positive # Record that an asset helped
127
+ akm feedback agent:reviewer --negative # Record that an asset missed the mark
128
+ \`\`\`
129
+
130
+ Use \`akm feedback\` whenever an asset materially helps or fails so future search
131
+ ranking can learn from actual usage.
132
+
133
+ ## Wikis
134
+
135
+ Multi-wiki knowledge bases (Karpathy-style). A stash-owned wiki lives at
136
+ \`<stashDir>/wikis/<name>/\`; external directories or repos can also be registered
137
+ as first-class wikis. akm owns lifecycle + raw-slug + lint + index regeneration
138
+ for stash-owned wikis; page edits use your native Read/Write/Edit tools.
139
+
140
+ \`\`\`sh
141
+ akm wiki list # List wikis (name, pages, raws, last-modified)
142
+ akm wiki create research # Scaffold a new wiki
143
+ akm wiki register ics-docs ~/code/ics-documentation # Register an external wiki
144
+ akm wiki show research # Path, description, counts, last 3 log entries
145
+ akm wiki pages research # Page refs + descriptions (excludes schema/index/log/raw)
146
+ akm wiki search research "attention" # Scoped search (equivalent to --type wiki --wiki research)
147
+ akm wiki stash research ./paper.md # Copy source into raw/<slug>.md (never overwrites)
148
+ echo "..." | akm wiki stash research - # stdin form
149
+ akm wiki lint research # Structural checks: orphans, broken xrefs, uncited raws, stale index
150
+ akm wiki ingest research # Print the ingest workflow for this wiki (no action)
151
+ akm wiki remove research --force # Delete pages/schema/index/log; preserves raw/
152
+ akm wiki remove research --force --with-sources # Full nuke, including raw/
153
+ \`\`\`
154
+
155
+ **For any wiki task, start with \`akm wiki list\`, then \`akm wiki ingest <name>\`
156
+ to get the step-by-step workflow.** Wiki pages are also addressable as
157
+ \`wiki:<name>/<page-path>\` and show up in stash-wide \`akm search\` as
158
+ \`type: wiki\`. Files under \`raw/\` and the wiki root infrastructure files
159
+ \`schema.md\`, \`index.md\`, and \`log.md\` are not indexed and do not appear in
160
+ search results. No \`--llm\` anywhere — akm never reasons about page content.
161
+
162
+ ## Vaults
163
+
164
+ Encrypted-at-rest key/value stores for secrets. Each vault is a \`.env\`-format
165
+ file at \`<stashDir>/vaults/<name>.env\`.
166
+
167
+ \`\`\`sh
168
+ akm vault create prod # Create a new vault
169
+ akm vault set prod DB_URL postgres://... # Set a key (or KEY=VALUE combined form)
170
+ akm vault set prod DB_URL=postgres://... # Combined KEY=VALUE form also works
171
+ akm vault unset prod DB_URL # Remove a key
172
+ akm vault list vault:prod # List key names (no values)
173
+ akm vault show vault:prod # Same as list (alias)
174
+ akm vault load vault:prod # Print export statements to source
175
+ \`\`\`
176
+
177
+ ## Workflows
178
+
179
+ Step-based workflows stored as \`<stashDir>/workflows/<name>.md\`.
180
+
181
+ \`\`\`sh
182
+ akm workflow template # Print a starter workflow template
183
+ akm workflow create ship-release # Scaffold a new workflow asset
184
+ akm workflow start workflow:ship-release # Start a new run
185
+ akm workflow next workflow:ship-release # Advance to the next step (or auto-start)
186
+ akm workflow complete <run-id> # Mark a step complete and advance
187
+ akm workflow status <run-id> # Show current run status
188
+ akm workflow resume <run-id> # Resume a blocked or failed run
189
+ akm workflow list # List all workflow runs
190
+ \`\`\`
191
+
192
+ ## Clone
193
+
194
+ Copy an asset to the working stash or a custom destination for editing.
195
+
196
+ \`\`\`sh
197
+ akm clone <ref> # Clone to working stash
198
+ akm clone <ref> --name new-name # Rename on clone
199
+ akm clone <ref> --dest ./project/.claude # Clone to custom location
200
+ akm clone <ref> --force # Overwrite existing
201
+ akm clone "npm:@scope/pkg//script:deploy.sh" # Clone from remote package
202
+ \`\`\`
203
+
204
+ When \`--dest\` is provided, \`akm init\` is not required first.
205
+
206
+ ## Save
207
+
208
+ Commit local changes in a git-backed stash. Behaviour adapts automatically:
209
+
210
+ - **Not a git repo** — no-op (silent skip)
211
+ - **Git repo, no remote** — stage and commit only (the default stash always falls here)
212
+ - **Git repo, has remote, not writable** — stage and commit only
213
+ - **Git repo, has remote, \`writable: true\`** — stage, commit, and push
214
+
215
+ \`\`\`sh
216
+ akm save # Save primary stash (timestamp message)
217
+ akm save -m "Add deploy skill" # Save with explicit message
218
+ akm save my-skills # Save a named writable git stash
219
+ akm save my-skills -m "Update patterns" # Save named stash with message
220
+ \`\`\`
221
+
222
+ The \`--writable\` flag on \`akm add\` opts a remote git stash into push-on-save:
223
+
224
+ \`\`\`sh
225
+ akm add git@github.com:org/skills.git --provider git --name my-skills --writable
226
+ \`\`\`
227
+
228
+ ## Add & Manage Sources
229
+
230
+ \`\`\`sh
231
+ akm add <ref> # Add a source
232
+ akm add @scope/stash # From npm (managed)
233
+ akm add owner/repo # From GitHub (managed)
234
+ akm add ./path/to/local/stash # Local directory
235
+ akm add git@github.com:org/repo.git --provider git --name my-skills --writable
236
+ akm enable skills.sh # Enable the skills.sh registry
237
+ akm disable skills.sh # Disable the skills.sh registry
238
+ akm list # List all sources
239
+ akm list --kind managed # List managed sources only
240
+ akm remove <target> # Remove by id, ref, path, or name
241
+ akm update --all # Update all managed sources
242
+ akm update <target> --force # Force re-download
243
+ \`\`\`
244
+
245
+ ## Registries
246
+
247
+ \`\`\`sh
248
+ akm registry list # List configured registries
249
+ akm registry add <url> # Add a registry
250
+ akm registry add <url> --name my-team # Add with label
251
+ akm registry add <url> --provider skills-sh # Specify provider type
252
+ akm registry remove <url-or-name> # Remove a registry
253
+ akm registry search "<query>" # Search all registries
254
+ akm registry search "<query>" --assets # Include asset-level results
255
+ akm registry build-index # Build ./index.json
256
+ akm registry build-index --out dist/index.json # Build to a custom path
257
+ \`\`\`
258
+
259
+ ## Configuration
260
+
261
+ \`\`\`sh
262
+ akm config list # Show current config
263
+ akm config get <key> # Read a value
264
+ akm config set <key> <value> # Set a value
265
+ akm config unset <key> # Remove a key
266
+ akm config path --all # Show all config paths
267
+ \`\`\`
268
+
269
+ ## Other Commands
270
+
271
+ \`\`\`sh
272
+ akm init # Initialize working stash
273
+ akm index # Rebuild search index
274
+ akm index --full # Full reindex
275
+ akm list # List all sources
276
+ akm upgrade # Upgrade akm using its install method
277
+ akm upgrade --check # Check for updates
278
+ akm help migrate 0.6.0 # Print migration notes for a release (or: latest)
279
+ akm hints # Print this reference
280
+ akm completions # Print bash completion script
281
+ akm completions --install # Install completions
282
+ \`\`\`
283
+
284
+ ## Output Control
285
+
286
+ All commands accept \`--format\` and \`--detail\` flags:
287
+
288
+ - \`--format json\` (default) — structured JSON
289
+ - \`--format jsonl\` — one JSON object per line (streaming-friendly)
290
+ - \`--format text\` — human-readable plain text
291
+ - \`--format yaml\` — YAML output
292
+ - \`--detail brief\` (default) — compact output
293
+ - \`--detail normal\` — adds tags, refs, origins
294
+ - \`--detail full\` — includes scores, paths, timing, debug info
295
+ - \`--detail summary\` — metadata only (no content/template/prompt), under 200 tokens
296
+ - \`--detail agent\` — agent-optimized output: strips non-actionable fields
297
+ - \`--for-agent\` — deprecated alias for \`--detail agent\`
298
+
299
+ Run \`akm -h\` or \`akm <command> -h\` for per-command help.
300
+ `;
301
+ export { EMBEDDED_HINTS, EMBEDDED_HINTS_FULL };
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Process-level output mode singleton.
3
+ *
4
+ * Output mode (format + detail + forAgent) is parsed once at startup from
5
+ * `process.argv` and the persisted user config. All subsequent `output()`
6
+ * calls read from this in-memory singleton instead of re-scanning argv and
7
+ * re-loading config on every call.
8
+ *
9
+ * Initialized from `cli.ts` before `runMain`.
10
+ */
11
+ import { UsageError } from "../core/errors";
12
+ export const OUTPUT_FORMATS = ["json", "yaml", "text", "jsonl"];
13
+ export const DETAIL_LEVELS = ["brief", "normal", "full", "summary", "agent"];
14
+ export function parseOutputFormat(value) {
15
+ if (!value)
16
+ return undefined;
17
+ if (OUTPUT_FORMATS.includes(value))
18
+ return value;
19
+ throw new UsageError(`Invalid value for --format: ${value}. Expected one of: ${OUTPUT_FORMATS.join("|")}`, "INVALID_FORMAT_VALUE");
20
+ }
21
+ export function parseDetailLevel(value) {
22
+ if (!value)
23
+ return undefined;
24
+ if (DETAIL_LEVELS.includes(value))
25
+ return value;
26
+ throw new UsageError(`Invalid value for --detail: ${value}. Expected one of: ${DETAIL_LEVELS.join("|")}`, "INVALID_DETAIL_VALUE");
27
+ }
28
+ export function parseFlagValue(argv, flag) {
29
+ for (let i = 0; i < argv.length; i++) {
30
+ const arg = argv[i];
31
+ if (arg === flag)
32
+ return argv[i + 1];
33
+ if (arg.startsWith(`${flag}=`))
34
+ return arg.slice(flag.length + 1);
35
+ }
36
+ return undefined;
37
+ }
38
+ export function hasBooleanFlag(argv, flag) {
39
+ return argv.some((arg) => arg === flag || arg === `${flag}=true`);
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
+ }
59
+ /**
60
+ * Resolve output mode from a synthetic argv array and config defaults.
61
+ * Pure function — no IO. Suitable for unit tests.
62
+ */
63
+ export function resolveOutputMode(argv, defaults = {}) {
64
+ const format = parseOutputFormat(parseFlagValue(argv, "--format")) ?? defaults?.format ?? "json";
65
+ const detail = parseDetailLevel(parseFlagValue(argv, "--detail")) ?? defaults?.detail ?? "brief";
66
+ // `--detail=agent` is the preferred preset. `--for-agent` is kept for one
67
+ // release cycle as an alias so existing scripts and docs keep working.
68
+ const forAgent = detail === "agent" || hasBooleanFlag(argv, "--for-agent");
69
+ return { format, detail, forAgent };
70
+ }
71
+ let _mode;
72
+ /**
73
+ * Initialize the process-level output mode. Must be called once at startup
74
+ * before any code calls `getOutputMode()`. Subsequent calls overwrite.
75
+ */
76
+ export function initOutputMode(argv, defaults = {}) {
77
+ _mode = resolveOutputMode(argv, defaults);
78
+ return _mode;
79
+ }
80
+ /**
81
+ * Read the process-level output mode. Throws if `initOutputMode()` was not
82
+ * called first — that is a programmer error, not a runtime condition.
83
+ */
84
+ export function getOutputMode() {
85
+ if (!_mode) {
86
+ throw new Error("OutputMode not initialized. Call initOutputMode() before getOutputMode().");
87
+ }
88
+ return _mode;
89
+ }
90
+ /**
91
+ * Reset the singleton. Test-only utility.
92
+ */
93
+ export function resetOutputMode() {
94
+ _mode = undefined;
95
+ }
@@ -8,15 +8,13 @@
8
8
  */
9
9
  import fs from "node:fs";
10
10
  import path from "node:path";
11
- import { hasErrnoCode } from "./common";
12
- import { UsageError } from "./errors";
13
- import { registerRenderer } from "./file-context";
14
- import { parseFrontmatter, toStringOrUndefined } from "./frontmatter";
15
- import { extractFrontmatterOnly, extractLineRange, extractSection, formatToc, parseMarkdownToc } from "./markdown";
16
- import { extractDescriptionFromComments, loadStashFile } from "./metadata";
17
- import { makeAssetRef } from "./stash-ref";
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
- function shellQuote(value) {
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
  */
@@ -411,58 +404,61 @@ const memoryMdRenderer = {
411
404
  content: ctx.content(),
412
405
  };
413
406
  },
414
- };
415
- // ── 6. workflow-md ───────────────────────────────────────────────────────────
416
- const workflowMdRenderer = {
417
- name: "workflow-md",
418
- buildShowResponse(ctx) {
419
- const name = deriveName(ctx);
420
- const workflow = parseWorkflowForRendering(ctx.content());
421
- const ref = makeAssetRef("workflow", name, ctx.origin);
422
- return {
423
- type: "workflow",
424
- name,
425
- path: ctx.absPath,
426
- action: buildWorkflowAction(ref),
427
- description: workflow.description,
428
- workflowTitle: workflow.title,
429
- parameters: workflow.parameters?.map((parameter) => parameter.name),
430
- workflowParameters: workflow.parameters,
431
- steps: workflow.steps,
432
- };
433
- },
434
407
  extractMetadata(entry, ctx) {
435
- const workflow = parseWorkflowForRendering(ctx.content());
436
- const hints = new Set(entry.searchHints ?? []);
437
- hints.add(workflow.title);
438
- for (const step of workflow.steps) {
439
- hints.add(step.title);
440
- hints.add(step.id);
441
- hints.add(step.instructions);
442
- for (const criterion of step.completionCriteria ?? []) {
443
- hints.add(criterion);
408
+ try {
409
+ const parsed = parseFrontmatter(ctx.content());
410
+ const fm = parsed.data;
411
+ // Description from frontmatter
412
+ const desc = toStringOrUndefined(fm.description);
413
+ if (desc && !entry.description) {
414
+ entry.description = desc;
415
+ entry.source = "frontmatter";
416
+ entry.confidence = 0.9;
417
+ }
418
+ // Tags from frontmatter
419
+ if (Array.isArray(fm.tags) && fm.tags.length > 0) {
420
+ const fmTags = fm.tags.filter((t) => typeof t === "string" && t.trim().length > 0);
421
+ if (fmTags.length > 0) {
422
+ entry.tags = Array.from(new Set([...(entry.tags ?? []), ...fmTags]));
423
+ }
424
+ }
425
+ // Build searchHints from structured memory metadata fields
426
+ const hints = new Set(entry.searchHints ?? []);
427
+ const source = toStringOrUndefined(fm.source);
428
+ if (source)
429
+ hints.add(source);
430
+ // observed_at: prefer frontmatter value, fall back to file mtime
431
+ const fmObservedAt = toStringOrUndefined(fm.observed_at);
432
+ if (fmObservedAt) {
433
+ hints.add(`observed_at:${fmObservedAt}`);
434
+ }
435
+ else {
436
+ // mtime fallback: format as ISO date (YYYY-MM-DD)
437
+ try {
438
+ const mtime = ctx.stat().mtime;
439
+ const isoDate = mtime.toISOString().slice(0, 10);
440
+ hints.add(`observed_at:${isoDate}`);
441
+ }
442
+ catch {
443
+ // Non-fatal: skip mtime fallback on stat error
444
+ }
445
+ }
446
+ const expires = toStringOrUndefined(fm.expires);
447
+ if (expires)
448
+ hints.add(`expires:${expires}`);
449
+ if (fm.subjective === true)
450
+ hints.add("subjective");
451
+ if (hints.size > 0) {
452
+ entry.searchHints = Array.from(hints).filter(Boolean);
444
453
  }
445
454
  }
446
- entry.searchHints = Array.from(hints).filter(Boolean);
447
- if (workflow.parameters?.length) {
448
- entry.parameters = workflow.parameters.map((parameter) => ({
449
- name: parameter.name,
450
- ...(parameter.description ? { description: parameter.description } : {}),
451
- }));
455
+ catch {
456
+ // Non-fatal: skip metadata extraction on error
452
457
  }
453
458
  },
454
459
  };
455
- function parseWorkflowForRendering(content) {
456
- try {
457
- return parseWorkflowMarkdown(content);
458
- }
459
- catch (error) {
460
- if (error instanceof WorkflowValidationError) {
461
- throw new UsageError(error.message);
462
- }
463
- throw error;
464
- }
465
- }
460
+ // ── 6. workflow-md ───────────────────────────────────────────────────────────
461
+ // Defined in src/workflows/renderer.ts and imported above.
466
462
  // ── 7. script-source ─────────────────────────────────────────────────────────
467
463
  const scriptSourceRenderer = {
468
464
  name: "script-source",