akm-cli 0.7.4 → 0.7.5

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 (37) hide show
  1. package/{CHANGELOG.md → .github/CHANGELOG.md} +33 -0
  2. package/.github/LICENSE +374 -0
  3. package/dist/cli.js +241 -170
  4. package/dist/commands/curate.js +1 -0
  5. package/dist/commands/distill.js +14 -4
  6. package/dist/commands/events.js +10 -1
  7. package/dist/commands/migration-help.js +2 -2
  8. package/dist/commands/propose.js +36 -16
  9. package/dist/commands/reflect.js +40 -14
  10. package/dist/commands/remember.js +1 -1
  11. package/dist/commands/show.js +19 -44
  12. package/dist/commands/vault.js +5 -10
  13. package/dist/core/asset-registry.js +1 -1
  14. package/dist/core/asset-spec.js +1 -1
  15. package/dist/core/config.js +13 -0
  16. package/dist/core/events.js +19 -2
  17. package/dist/indexer/db-search.js +35 -235
  18. package/dist/indexer/db.js +15 -5
  19. package/dist/indexer/ensure-index.js +72 -0
  20. package/dist/indexer/graph-extraction.js +10 -0
  21. package/dist/indexer/indexer.js +38 -22
  22. package/dist/integrations/agent/prompts.js +95 -15
  23. package/dist/integrations/agent/spawn.js +65 -12
  24. package/dist/llm/client.js +40 -2
  25. package/dist/llm/graph-extract.js +2 -4
  26. package/dist/llm/memory-infer.js +7 -4
  27. package/dist/output/cli-hints.js +17 -8
  28. package/dist/output/renderers.js +6 -1
  29. package/dist/output/shapes.js +8 -3
  30. package/dist/output/text.js +18 -19
  31. package/dist/sources/providers/git.js +43 -1
  32. package/dist/workflows/db.js +9 -0
  33. package/dist/workflows/runs.js +25 -8
  34. package/dist/workflows/scope-key.js +76 -0
  35. package/docs/migration/release-notes/0.7.4.md +1 -1
  36. package/docs/migration/release-notes/0.7.5.md +20 -0
  37. package/package.json +2 -2
@@ -12,20 +12,22 @@ import { resolveAssetPath } from "../sources/resolve";
12
12
  import { formatWorkflowErrors } from "./authoring";
13
13
  import { closeWorkflowDatabase, openWorkflowDatabase } from "./db";
14
14
  import { parseWorkflow } from "./parser";
15
+ import { getCurrentWorkflowScopeKey } from "./scope-key";
15
16
  export async function startWorkflowRun(ref, params = {}) {
16
17
  const asset = await loadWorkflowAsset(ref);
17
18
  const workflowDb = openWorkflowDatabase();
18
19
  try {
19
20
  const now = new Date().toISOString();
20
21
  const runId = randomUUID();
22
+ const scopeKey = getCurrentWorkflowScopeKey();
21
23
  const currentStepId = asset.steps[0]?.id ?? null;
22
24
  const workflowEntryId = resolveWorkflowEntryId(asset.sourcePath, asset.ref);
23
25
  workflowDb.transaction(() => {
24
26
  workflowDb
25
27
  .prepare(`INSERT INTO workflow_runs (
26
- id, workflow_ref, workflow_entry_id, workflow_title, status, params_json, current_step_id, created_at, updated_at
27
- ) VALUES (?, ?, ?, ?, 'active', ?, ?, ?, ?)`)
28
- .run(runId, asset.ref, workflowEntryId, asset.title, JSON.stringify(params), currentStepId, now, now);
28
+ id, workflow_ref, scope_key, workflow_entry_id, workflow_title, status, params_json, current_step_id, created_at, updated_at
29
+ ) VALUES (?, ?, ?, ?, ?, 'active', ?, ?, ?, ?)`)
30
+ .run(runId, asset.ref, scopeKey, workflowEntryId, asset.title, JSON.stringify(params), currentStepId, now, now);
29
31
  const insertStep = workflowDb.prepare(`INSERT INTO workflow_run_steps (
30
32
  run_id, step_id, step_title, instructions, completion_json, sequence_index, status
31
33
  ) VALUES (?, ?, ?, ?, ?, ?, 'pending')`);
@@ -56,11 +58,24 @@ export function getWorkflowStatus(runId) {
56
58
  closeWorkflowDatabase(workflowDb);
57
59
  }
58
60
  }
61
+ export function hasWorkflowRun(runId) {
62
+ const workflowDb = openWorkflowDatabase();
63
+ try {
64
+ const row = workflowDb.prepare("SELECT 1 FROM workflow_runs WHERE id = ? LIMIT 1").get(runId);
65
+ return !!row;
66
+ }
67
+ finally {
68
+ closeWorkflowDatabase(workflowDb);
69
+ }
70
+ }
59
71
  export function listWorkflowRuns(input) {
60
72
  const workflowDb = openWorkflowDatabase();
61
73
  try {
62
74
  const filters = [];
63
75
  const params = [];
76
+ const scopeKey = getCurrentWorkflowScopeKey();
77
+ filters.push("scope_key = ?");
78
+ params.push(scopeKey);
64
79
  if (input?.workflowRef) {
65
80
  const parsed = parseAssetRef(input.workflowRef);
66
81
  if (parsed.type !== "workflow") {
@@ -211,9 +226,10 @@ async function resolveRunSpecifier(db, specifier, params) {
211
226
  throw new UsageError(`Expected a workflow ref or workflow run id, got "${specifier}".`);
212
227
  }
213
228
  const ref = `${parsed.origin ? `${parsed.origin}//` : ""}workflow:${parsed.name}`;
229
+ const scopeKey = getCurrentWorkflowScopeKey();
214
230
  const active = db
215
- .prepare("SELECT * FROM workflow_runs WHERE workflow_ref = ? AND status = 'active' ORDER BY updated_at DESC LIMIT 1")
216
- .get(ref);
231
+ .prepare("SELECT * FROM workflow_runs WHERE workflow_ref = ? AND scope_key = ? AND status = 'active' ORDER BY updated_at DESC LIMIT 1")
232
+ .get(ref, scopeKey);
217
233
  if (active) {
218
234
  if (params && Object.keys(params).length > 0) {
219
235
  throw new UsageError(`--params can only be set on a new run; ${ref} already has an active run`);
@@ -358,6 +374,7 @@ function toWorkflowRunSummary(run) {
358
374
  return {
359
375
  id: run.id,
360
376
  workflowRef: run.workflow_ref,
377
+ scopeKey: run.scope_key,
361
378
  workflowEntryId: run.workflow_entry_id,
362
379
  workflowTitle: run.workflow_title,
363
380
  status: run.status,
@@ -435,12 +452,12 @@ function parseJsonArray(value) {
435
452
  }
436
453
  return undefined;
437
454
  }
438
- export function getActiveWorkflowRun() {
455
+ export function getActiveWorkflowRun(scopeKey = getCurrentWorkflowScopeKey()) {
439
456
  try {
440
457
  const workflowDb = openWorkflowDatabase();
441
458
  const row = workflowDb
442
- .query("SELECT id, current_step_id, workflow_ref FROM workflow_runs WHERE status IN ('active', 'blocked') ORDER BY updated_at DESC LIMIT 1")
443
- .get();
459
+ .query("SELECT id, current_step_id, workflow_ref FROM workflow_runs WHERE scope_key = ? AND status IN ('active', 'blocked') ORDER BY updated_at DESC LIMIT 1")
460
+ .get(scopeKey);
444
461
  closeWorkflowDatabase(workflowDb);
445
462
  if (!row)
446
463
  return null;
@@ -0,0 +1,76 @@
1
+ import { createHash } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { isWithin, resolveStashDir, safeRealpath, toPosix } from "../core/common";
5
+ const PROJECT_CONFIG_RELATIVE_PATH = path.join(".akm", "config.json");
6
+ export function getCurrentWorkflowScopeKey() {
7
+ const anchor = resolveWorkflowScopeAnchor(process.cwd());
8
+ const normalized = normalizeScopePath(anchor);
9
+ const digest = createHash("sha256").update(normalized).digest("hex");
10
+ return `dir:v1:${digest}`;
11
+ }
12
+ export function resolveWorkflowScopeAnchor(startDir) {
13
+ const cwd = safeRealpath(startDir);
14
+ const projectRoot = findNearestProjectConfigRoot(cwd);
15
+ if (projectRoot)
16
+ return projectRoot;
17
+ const gitRoot = findNearestGitRoot(cwd);
18
+ if (gitRoot)
19
+ return gitRoot;
20
+ try {
21
+ const stashDir = safeRealpath(resolveStashDir({ readOnly: true }));
22
+ if (isWithin(cwd, stashDir))
23
+ return stashDir;
24
+ }
25
+ catch {
26
+ // Ignore stash resolution failures and fall back to cwd.
27
+ }
28
+ return cwd;
29
+ }
30
+ function findNearestProjectConfigRoot(startDir) {
31
+ let currentDir = startDir;
32
+ while (true) {
33
+ const configPath = path.join(currentDir, PROJECT_CONFIG_RELATIVE_PATH);
34
+ if (isFile(configPath)) {
35
+ return safeRealpath(currentDir);
36
+ }
37
+ const parentDir = path.dirname(currentDir);
38
+ if (parentDir === currentDir)
39
+ return null;
40
+ currentDir = parentDir;
41
+ }
42
+ }
43
+ function findNearestGitRoot(startDir) {
44
+ let currentDir = startDir;
45
+ while (true) {
46
+ const gitPath = path.join(currentDir, ".git");
47
+ if (exists(gitPath)) {
48
+ return safeRealpath(currentDir);
49
+ }
50
+ const parentDir = path.dirname(currentDir);
51
+ if (parentDir === currentDir)
52
+ return null;
53
+ currentDir = parentDir;
54
+ }
55
+ }
56
+ function exists(candidate) {
57
+ try {
58
+ fs.accessSync(candidate, fs.constants.F_OK);
59
+ return true;
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
65
+ function isFile(candidate) {
66
+ try {
67
+ return fs.statSync(candidate).isFile();
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ }
73
+ function normalizeScopePath(value) {
74
+ const posix = toPosix(value);
75
+ return process.platform === "win32" ? posix.toLowerCase() : posix;
76
+ }
@@ -14,4 +14,4 @@ This is a patch release that fixes the publish process. No functional changes fr
14
14
  - **`.stash.json` no longer drives incremental stale detection** — editing `.stash.json` alone no longer forces directories to rescan during incremental indexing.
15
15
  - **One-shot URL ingest for `akm import` and `akm wiki stash`** — both commands now accept a single HTTP/HTTPS URL in addition to file paths and stdin. `akm import <url>` fetches the exact page, converts it to markdown, and writes it into `knowledge/` using a URL-path-derived default name. `akm wiki stash <wiki> <url>` fetches the exact page, converts it to markdown, and writes it into `wikis/<wiki>/raw/`. Neither command registers a persistent website source or crawls linked pages.
16
16
 
17
- For stash authors on the 0.7.x pre-release line: `.stash.json` remains supported for compatibility in this release, but it is deprecated and will be removed in v0.8.0. That timeline is intentional: during this aggressive pre-release phase-out window, compatibility shims do not stay around until 1.0 unless they still earn their cost. Prefer frontmatter for markdown assets and structured code comments for scripts, and migrate any remaining `.stash.json` metadata before taking the 0.8 upgrade.
17
+ For stash authors on the 0.7.x pre-release line: `.stash.json` remains supported for compatibility in this release, but it is deprecated and will be removed in v0.8.0. That timeline is intentional: during this aggressive pre-release phase-out window, compatibility shims do not stay around until 1.0 unless they still earn their cost. Prefer frontmatter for markdown assets and structured code comments for scripts, and migrate any remaining `.stash.json` metadata before taking the 0.8 upgrade.
@@ -0,0 +1,20 @@
1
+ Migration notes for akm v0.7.5
2
+
3
+ 0.7.5 is a patch release that rolls up all changes shipped on `main` after v0.7.4.
4
+
5
+ - **Workflow runs are now workspace-scoped** — ref-based workflow commands (`workflow next`, `workflow status`, `workflow list`) now resolve runs within the current project/worktree/directory instead of sharing one global active-run pool across every repo and local sandbox. This fixes cross-repo blocking and leakage for workflow refs such as `workflow:github-issues-parallel-implementer`. Direct run-id commands still target the exact run.
6
+ - **Workflow help/docs now explain scope semantics** — CLI help, operator docs, and workflow-facing hints now describe the current-scope behavior so teams know that ref-based run resolution is local to the current workspace.
7
+ - **`show` now prefers fresh index state** — stale index reads auto-refresh instead of silently falling back to a raw filesystem path, reducing search/show drift.
8
+ - **External agent + LLM parsing is more defensive** — reflect/propose and related local-model JSON parsing paths are hardened against malformed or partial output.
9
+ - **Reflect temp-file handling is safer** — draft files for reflection flows now live in OS temp space instead of the stash.
10
+ - **Memory inference respects token budgets** — long inputs are now trimmed to the configured LLM budget.
11
+ - **Vault and save follow-ups landed** — vault path/run flows and named git stash selection in `akm save` received targeted fixes and regression coverage.
12
+ - **Release/migration packaging is tighter** — published static files, changelog resolution, and rerunnable release automation received follow-up fixes.
13
+
14
+ No manual migration is required for most users, but note these operator-facing details:
15
+
16
+ 1. If you were relying on ref-based workflow runs being globally shared across repos or directories, that behavior is gone. Use direct run IDs when you intentionally need to resume a specific run created elsewhere.
17
+ 2. Existing older workflow runs created before the new workspace scope key was introduced are still accessible by direct run ID, but ref-based commands now prefer runs created in the current scope.
18
+ 3. `akm help migrate latest` now resolves to 0.7.5 and uses the published `.github/CHANGELOG.md` path.
19
+
20
+ Full changelog: https://github.com/itlackey/akm/blob/main/.github/CHANGELOG.md
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "type": "module",
5
5
  "description": "akm (Agent Kit Manager) — A package manager for AI agent skills, commands, tools, and knowledge. Works with Claude Code, OpenCode, Cursor, and any AI coding assistant.",
6
6
  "keywords": [
@@ -34,7 +34,7 @@
34
34
  "files": [
35
35
  "dist",
36
36
  "README.md",
37
- "CHANGELOG.md",
37
+ ".github/CHANGELOG.md",
38
38
  "LICENSE",
39
39
  "docs/migration/release-notes"
40
40
  ],