akm-cli 0.7.5 → 0.8.0-rc.10
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/{.github/CHANGELOG.md → CHANGELOG.md} +192 -2
- package/README.md +22 -6
- package/SECURITY.md +93 -0
- package/dist/cli/config-migrate.js +144 -0
- package/dist/cli/config-validate.js +39 -0
- package/dist/cli/confirm.js +73 -0
- package/dist/cli/parse-args.js +133 -0
- package/dist/cli/shared.js +129 -0
- package/dist/cli.js +2569 -1449
- package/dist/commands/add-cli.js +279 -0
- package/dist/commands/agent-dispatch.js +110 -0
- package/dist/commands/agent-support.js +68 -0
- package/dist/commands/completions.js +3 -0
- package/dist/commands/config-cli.js +130 -534
- package/dist/commands/consolidate.js +2122 -0
- package/dist/commands/curate.js +44 -3
- package/dist/commands/db-cli.js +23 -0
- package/dist/commands/distill-promotion-policy.js +660 -0
- package/dist/commands/distill.js +1075 -77
- package/dist/commands/env.js +213 -0
- package/dist/commands/eval-cases.js +43 -0
- package/dist/commands/events.js +5 -23
- package/dist/commands/extract-cli.js +127 -0
- package/dist/commands/extract-prompt.js +204 -0
- package/dist/commands/extract.js +477 -0
- package/dist/commands/feedback-cli.js +331 -0
- package/dist/commands/graph.js +477 -0
- package/dist/commands/health.js +1302 -0
- package/dist/commands/help/help-accept.md +12 -0
- package/dist/commands/help/help-improve.md +69 -0
- package/dist/commands/help/help-proposals.md +18 -0
- package/dist/commands/help/help-propose.md +17 -0
- package/dist/commands/help/help-reject.md +11 -0
- package/dist/commands/history.js +54 -46
- package/dist/commands/improve-auto-accept.js +97 -0
- package/dist/commands/improve-cli.js +217 -0
- package/dist/commands/improve-profiles.js +166 -0
- package/dist/commands/improve-result-file.js +167 -0
- package/dist/commands/improve.js +2373 -0
- package/dist/commands/info.js +5 -2
- package/dist/commands/init.js +50 -2
- package/dist/commands/installed-stashes.js +102 -139
- package/dist/commands/knowledge.js +136 -0
- package/dist/commands/lint/agent-linter.js +49 -0
- package/dist/commands/lint/base-linter.js +479 -0
- package/dist/commands/lint/command-linter.js +49 -0
- package/dist/commands/lint/default-linter.js +16 -0
- package/dist/commands/lint/env-key-rules.js +154 -0
- package/dist/commands/lint/index.js +196 -0
- package/dist/commands/lint/knowledge-linter.js +16 -0
- package/dist/commands/lint/markdown-insertion.js +343 -0
- package/dist/commands/lint/memory-linter.js +61 -0
- package/dist/commands/lint/registry.js +36 -0
- package/dist/commands/lint/skill-linter.js +45 -0
- package/dist/commands/lint/task-linter.js +50 -0
- package/dist/commands/lint/types.js +4 -0
- package/dist/commands/lint/workflow-linter.js +56 -0
- package/dist/commands/lint.js +4 -0
- package/dist/commands/migration-help.js +5 -2
- package/dist/commands/proposal.js +67 -12
- package/dist/commands/propose.js +86 -31
- package/dist/commands/reflect.js +1091 -73
- package/dist/commands/registry-cli.js +150 -0
- package/dist/commands/registry-search.js +5 -2
- package/dist/commands/remember-cli.js +257 -0
- package/dist/commands/remember.js +69 -6
- package/dist/commands/schema-repair.js +203 -0
- package/dist/commands/search.js +115 -14
- package/dist/commands/secret.js +173 -0
- package/dist/commands/self-update.js +3 -0
- package/dist/commands/show.js +148 -25
- package/dist/commands/source-add.js +17 -45
- package/dist/commands/source-clone.js +3 -0
- package/dist/commands/source-manage.js +14 -19
- package/dist/commands/tasks.js +437 -0
- package/dist/commands/url-checker.js +42 -0
- package/dist/core/action-contributors.js +28 -0
- package/dist/core/asset-ref.js +17 -2
- package/dist/core/asset-registry.js +12 -17
- package/dist/core/asset-serialize.js +88 -0
- package/dist/core/asset-spec.js +67 -1
- package/dist/core/common.js +182 -0
- package/dist/core/concurrent.js +25 -0
- package/dist/core/config-io.js +347 -0
- package/dist/core/config-migration.js +622 -0
- package/dist/core/config-schema.js +534 -0
- package/dist/core/config-sources.js +108 -0
- package/dist/core/config-types.js +4 -0
- package/dist/core/config-walker.js +337 -0
- package/dist/core/config.js +364 -981
- package/dist/core/errors.js +42 -20
- package/dist/core/events.js +91 -138
- package/dist/core/file-lock.js +104 -0
- package/dist/core/frontmatter.js +75 -8
- package/dist/core/lesson-lint.js +3 -0
- package/dist/core/markdown.js +20 -0
- package/dist/core/memory-belief.js +62 -0
- package/dist/core/memory-contradiction-detect.js +274 -0
- package/dist/core/memory-improve.js +806 -0
- package/dist/core/parse.js +158 -0
- package/dist/core/paths.js +280 -14
- package/dist/core/proposal-quality-validators.js +380 -0
- package/dist/core/proposal-validators.js +69 -0
- package/dist/core/proposals.js +512 -42
- package/dist/core/state-db.js +1068 -0
- package/dist/core/text-truncation.js +107 -0
- package/dist/core/time.js +54 -0
- package/dist/core/tty.js +59 -0
- package/dist/core/warn.js +64 -1
- package/dist/core/write-source.js +3 -0
- package/dist/indexer/db-backup.js +391 -0
- package/dist/indexer/db-search.js +163 -254
- package/dist/indexer/db.js +975 -103
- package/dist/indexer/ensure-index.js +64 -0
- package/dist/indexer/file-context.js +3 -0
- package/dist/indexer/graph-boost.js +376 -101
- package/dist/indexer/graph-db.js +391 -0
- package/dist/indexer/graph-dedup.js +95 -0
- package/dist/indexer/graph-extraction.js +550 -124
- package/dist/indexer/index-context.js +4 -0
- package/dist/indexer/indexer.js +523 -301
- package/dist/indexer/llm-cache.js +52 -0
- package/dist/indexer/manifest.js +3 -0
- package/dist/indexer/matchers.js +167 -160
- package/dist/indexer/memory-inference.js +152 -74
- package/dist/indexer/metadata-contributors.js +29 -0
- package/dist/indexer/metadata.js +275 -196
- package/dist/indexer/path-resolver.js +92 -0
- package/dist/indexer/project-context.js +192 -0
- package/dist/indexer/ranking-contributors.js +331 -0
- package/dist/indexer/ranking.js +81 -0
- package/dist/indexer/search-fields.js +5 -9
- package/dist/indexer/search-hit-enrichers.js +111 -0
- package/dist/indexer/search-source.js +44 -10
- package/dist/indexer/semantic-status.js +6 -17
- package/dist/indexer/staleness-detect.js +447 -0
- package/dist/indexer/usage-events.js +12 -9
- package/dist/indexer/walker.js +28 -0
- package/dist/integrations/agent/builders.js +135 -0
- package/dist/integrations/agent/config.js +122 -230
- package/dist/integrations/agent/detect.js +3 -0
- package/dist/integrations/agent/index.js +7 -13
- package/dist/integrations/agent/model-aliases.js +55 -0
- package/dist/integrations/agent/profiles.js +70 -5
- package/dist/integrations/agent/prompts.js +214 -80
- package/dist/integrations/agent/runner.js +151 -0
- package/dist/integrations/agent/sdk-runner.js +126 -0
- package/dist/integrations/agent/spawn.js +118 -23
- package/dist/integrations/github.js +3 -0
- package/dist/integrations/lockfile.js +32 -69
- package/dist/integrations/session-logs/index.js +69 -0
- package/dist/integrations/session-logs/inline-refs.js +35 -0
- package/dist/integrations/session-logs/pre-filter.js +152 -0
- package/dist/integrations/session-logs/providers/claude-code.js +282 -0
- package/dist/integrations/session-logs/providers/opencode.js +258 -0
- package/dist/integrations/session-logs/types.js +4 -0
- package/dist/llm/call-ai.js +62 -0
- package/dist/llm/client.js +77 -124
- package/dist/llm/embedder.js +20 -29
- package/dist/llm/embedders/cache.js +3 -7
- package/dist/llm/embedders/local.js +42 -1
- package/dist/llm/embedders/remote.js +20 -8
- package/dist/llm/embedders/types.js +3 -7
- package/dist/llm/feature-gate.js +95 -48
- package/dist/llm/graph-extract.js +676 -70
- package/dist/llm/index-passes.js +44 -29
- package/dist/llm/memory-infer.js +77 -71
- package/dist/llm/metadata-enhance.js +42 -29
- package/dist/llm/prompts/extract-session.md +80 -0
- package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
- package/dist/output/cli-hints-full.md +292 -0
- package/dist/output/cli-hints-short.md +66 -0
- package/dist/output/cli-hints.js +7 -320
- package/dist/output/context.js +60 -8
- package/dist/output/renderers.js +300 -257
- package/dist/output/shapes/curate.js +56 -0
- package/dist/output/shapes/distill.js +10 -0
- package/dist/output/shapes/env-list.js +19 -0
- package/dist/output/shapes/events.js +11 -0
- package/dist/output/shapes/helpers.js +424 -0
- package/dist/output/shapes/history.js +7 -0
- package/dist/output/shapes/passthrough.js +102 -0
- package/dist/output/shapes/proposal-accept.js +7 -0
- package/dist/output/shapes/proposal-diff.js +7 -0
- package/dist/output/shapes/proposal-list.js +7 -0
- package/dist/output/shapes/proposal-producer.js +11 -0
- package/dist/output/shapes/proposal-reject.js +7 -0
- package/dist/output/shapes/proposal-show.js +7 -0
- package/dist/output/shapes/registry-search.js +6 -0
- package/dist/output/shapes/registry.js +30 -0
- package/dist/output/shapes/search.js +6 -0
- package/dist/output/shapes/secret-list.js +19 -0
- package/dist/output/shapes/show.js +6 -0
- package/dist/output/shapes/vault-list.js +19 -0
- package/dist/output/shapes.js +51 -516
- package/dist/output/text/add.js +6 -0
- package/dist/output/text/clone.js +6 -0
- package/dist/output/text/config.js +6 -0
- package/dist/output/text/curate.js +6 -0
- package/dist/output/text/distill.js +7 -0
- package/dist/output/text/enable-disable.js +7 -0
- package/dist/output/text/events.js +10 -0
- package/dist/output/text/feedback.js +6 -0
- package/dist/output/text/helpers.js +1039 -0
- package/dist/output/text/history.js +7 -0
- package/dist/output/text/import.js +6 -0
- package/dist/output/text/index.js +6 -0
- package/dist/output/text/info.js +6 -0
- package/dist/output/text/init.js +6 -0
- package/dist/output/text/list.js +6 -0
- package/dist/output/text/proposal-producer.js +8 -0
- package/dist/output/text/proposal.js +11 -0
- package/dist/output/text/registry-commands.js +11 -0
- package/dist/output/text/registry.js +30 -0
- package/dist/output/text/remember.js +6 -0
- package/dist/output/text/remove.js +6 -0
- package/dist/output/text/save.js +6 -0
- package/dist/output/text/search.js +6 -0
- package/dist/output/text/show.js +6 -0
- package/dist/output/text/update.js +6 -0
- package/dist/output/text/upgrade.js +6 -0
- package/dist/output/text/vault.js +16 -0
- package/dist/output/text/wiki.js +15 -0
- package/dist/output/text/workflow.js +14 -0
- package/dist/output/text.js +44 -1092
- package/dist/registry/build-index.js +3 -0
- package/dist/registry/create-provider-registry.js +3 -0
- package/dist/registry/factory.js +4 -1
- package/dist/registry/origin-resolve.js +3 -0
- package/dist/registry/providers/index.js +3 -0
- package/dist/registry/providers/skills-sh.js +71 -50
- package/dist/registry/providers/static-index.js +53 -48
- package/dist/registry/providers/types.js +3 -24
- package/dist/registry/resolve.js +11 -16
- package/dist/registry/types.js +3 -0
- package/dist/scripts/migrate-storage.js +17750 -0
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -0
- package/dist/scripts/migrations/v16-to-v17.js +141 -0
- package/dist/setup/detect.js +3 -0
- package/dist/setup/ripgrep-install.js +3 -0
- package/dist/setup/ripgrep-resolve.js +3 -0
- package/dist/setup/setup.js +775 -37
- package/dist/setup/steps.js +3 -15
- package/dist/sources/include.js +3 -0
- package/dist/sources/provider-factory.js +5 -12
- package/dist/sources/provider.js +3 -20
- package/dist/sources/providers/filesystem.js +19 -23
- package/dist/sources/providers/git.js +138 -21
- package/dist/sources/providers/index.js +3 -0
- package/dist/sources/providers/install-types.js +3 -13
- package/dist/sources/providers/npm.js +3 -4
- package/dist/sources/providers/provider-utils.js +3 -0
- package/dist/sources/providers/sync-from-ref.js +3 -11
- package/dist/sources/providers/tar-utils.js +3 -0
- package/dist/sources/providers/website.js +18 -22
- package/dist/sources/resolve.js +3 -0
- package/dist/sources/types.js +3 -0
- package/dist/sources/website-ingest.js +7 -0
- package/dist/tasks/backends/cron.js +203 -0
- package/dist/tasks/backends/exec-utils.js +28 -0
- package/dist/tasks/backends/index.js +24 -0
- package/dist/tasks/backends/launchd-template.xml +19 -0
- package/dist/tasks/backends/launchd.js +187 -0
- package/dist/tasks/backends/schtasks-template.xml +29 -0
- package/dist/tasks/backends/schtasks.js +215 -0
- package/dist/tasks/parser.js +211 -0
- package/dist/tasks/resolveAkmBin.js +87 -0
- package/dist/tasks/runner.js +458 -0
- package/dist/tasks/schedule.js +227 -0
- package/dist/tasks/schema.js +15 -0
- package/dist/tasks/validator.js +62 -0
- package/dist/version.js +3 -0
- package/dist/wiki/index-template.md +12 -0
- package/dist/wiki/ingest-workflow-template.md +54 -0
- package/dist/wiki/log-template.md +8 -0
- package/dist/wiki/schema-template.md +61 -0
- package/dist/wiki/wiki-templates.js +15 -0
- package/dist/wiki/wiki.js +13 -61
- package/dist/workflows/authoring.js +8 -25
- package/dist/workflows/cli.js +3 -0
- package/dist/workflows/db.js +140 -10
- package/dist/workflows/document-cache.js +3 -10
- package/dist/workflows/parser.js +3 -0
- package/dist/workflows/renderer.js +11 -3
- package/dist/workflows/runs.js +77 -92
- package/dist/workflows/schema.js +3 -0
- package/dist/workflows/scope-key.js +3 -0
- package/dist/workflows/validator.js +4 -8
- package/dist/workflows/workflow-template.md +24 -0
- package/docs/README.md +10 -2
- package/docs/data-and-telemetry.md +225 -0
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.7.5.md +2 -2
- package/docs/migration/release-notes/0.8.0.md +48 -0
- package/docs/migration/v0.7-to-v0.8.md +1307 -0
- package/package.json +30 -12
- package/.github/LICENSE +0 -374
- package/dist/commands/install-audit.js +0 -381
- package/dist/commands/vault.js +0 -328
- package/dist/templates/wiki-templates.js +0 -100
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Task asset schema. A task pairs a cron-style schedule with exactly one of:
|
|
6
|
+
*
|
|
7
|
+
* • a workflow target — invoked via `startWorkflowRun()`
|
|
8
|
+
* • a prompt target — invoked via `runAgent()` against the configured
|
|
9
|
+
* agent harness (e.g. `opencode run`)
|
|
10
|
+
* • a command target — invoked directly via `Bun.spawn()`, no AI agent
|
|
11
|
+
*
|
|
12
|
+
* Tasks are stored as pure YAML files at `<stash>/tasks/<id>.yml`. Multi-line
|
|
13
|
+
* inline prompts use a YAML block scalar (`prompt: |`).
|
|
14
|
+
*/
|
|
15
|
+
export const TASK_SCHEMA_VERSION = 1;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Validate a parsed {@link TaskDocument} for runnability.
|
|
6
|
+
*
|
|
7
|
+
* Enforces:
|
|
8
|
+
* • the schedule is parseable and translates to the active backend
|
|
9
|
+
* • the workflow ref resolves (workflow targets)
|
|
10
|
+
* • the asset/file source exists (prompt targets)
|
|
11
|
+
* • the agent profile resolves (prompt targets)
|
|
12
|
+
*
|
|
13
|
+
* Validation is deliberately split from parsing: callers that only want to
|
|
14
|
+
* read frontmatter (e.g. `tasks list`) can skip these checks, while
|
|
15
|
+
* `tasks add` and `tasks run` should always run them.
|
|
16
|
+
*/
|
|
17
|
+
import fs from "node:fs";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { parseAssetRef } from "../core/asset-ref";
|
|
20
|
+
import { resolveStashDir } from "../core/common";
|
|
21
|
+
import { loadConfig } from "../core/config";
|
|
22
|
+
import { NotFoundError } from "../core/errors";
|
|
23
|
+
import { requireAgentProfile } from "../integrations/agent";
|
|
24
|
+
import { resolveAssetPath } from "../sources/resolve";
|
|
25
|
+
import { parseSchedule } from "./schedule";
|
|
26
|
+
export async function validateTaskDocument(task, options) {
|
|
27
|
+
// Schedule must parse and translate.
|
|
28
|
+
parseSchedule(task.schedule, options.backend);
|
|
29
|
+
if (task.target.kind === "workflow") {
|
|
30
|
+
const stashDir = options.stashDir ?? resolveStashDir();
|
|
31
|
+
const ref = parseAssetRef(task.target.ref);
|
|
32
|
+
if (ref.type !== "workflow") {
|
|
33
|
+
throw new NotFoundError(`Task "${task.id}" workflow target must be a workflow ref (got "${task.target.ref}").`, "WORKFLOW_NOT_FOUND");
|
|
34
|
+
}
|
|
35
|
+
await resolveAssetPath(stashDir, "workflow", ref.name);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (task.target.kind !== "prompt") {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// Prompt target. Resolve the profile unconditionally — when no profile is
|
|
42
|
+
// set on the task, requireAgentProfile falls back to defaults.agent and
|
|
43
|
+
// throws a clear error if neither is configured. Catching this at
|
|
44
|
+
// `tasks add` / `tasks sync` time is much more useful than failing only
|
|
45
|
+
// when the OS scheduler fires.
|
|
46
|
+
const config = loadConfig();
|
|
47
|
+
requireAgentProfile(config, task.target.profile);
|
|
48
|
+
const src = task.target.source;
|
|
49
|
+
if (src.kind === "asset") {
|
|
50
|
+
const stashDir = options.stashDir ?? resolveStashDir();
|
|
51
|
+
const ref = parseAssetRef(src.ref);
|
|
52
|
+
await resolveAssetPath(stashDir, ref.type, ref.name);
|
|
53
|
+
}
|
|
54
|
+
else if (src.kind === "file") {
|
|
55
|
+
const taskDir = path.dirname(task.source.path);
|
|
56
|
+
const resolved = path.isAbsolute(src.path) ? src.path : path.resolve(taskDir, src.path);
|
|
57
|
+
if (!fs.existsSync(resolved) || !fs.statSync(resolved).isFile()) {
|
|
58
|
+
throw new NotFoundError(`Prompt file not found for task "${task.id}": ${src.path} (resolved to ${resolved}).`, "FILE_NOT_FOUND");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// inline source is always valid post-parse.
|
|
62
|
+
}
|
package/dist/version.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
1
4
|
import fs from "node:fs";
|
|
2
5
|
import path from "node:path";
|
|
3
6
|
// Version: prefer compile-time define, then package.json, then fallback
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Catalog of pages in the {{WIKI_NAME}} wiki. Regenerated by `akm index`.
|
|
3
|
+
wikiRole: index
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# {{WIKI_NAME}} — index
|
|
7
|
+
|
|
8
|
+
_This file is regenerated on every `akm index` run. Manual edits are
|
|
9
|
+
preserved until the next regeneration, then replaced._
|
|
10
|
+
|
|
11
|
+
_(no pages yet — create one with your editor, or ingest a source with `akm
|
|
12
|
+
wiki stash {{WIKI_NAME}} <path>`.)_
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Ingest workflow for wiki:{{WIKI_NAME}}
|
|
2
|
+
|
|
3
|
+
Wiki location: {{WIKI_DIR}}
|
|
4
|
+
Schema: {{SCHEMA_PATH}}
|
|
5
|
+
|
|
6
|
+
Follow these steps. akm commands handle the invariants; use your native
|
|
7
|
+
Read/Write/Edit tools for page edits.
|
|
8
|
+
|
|
9
|
+
1. **Read the schema.** Open `{{SCHEMA_PATH}}`. It defines the voice, page
|
|
10
|
+
kinds, contradiction policy, and any wiki-specific conventions. Do not
|
|
11
|
+
skip this step even on familiar wikis — the schema may have changed.
|
|
12
|
+
|
|
13
|
+
2. **File the source under `raw/`.**
|
|
14
|
+
```sh
|
|
15
|
+
akm wiki stash {{WIKI_NAME}} <path-or-url-to-source>
|
|
16
|
+
# or: cat <source> | akm wiki stash {{WIKI_NAME}} -
|
|
17
|
+
```
|
|
18
|
+
Returns `{ slug, path, ref }`. The raw copy is immutable — never edit it.
|
|
19
|
+
|
|
20
|
+
3. **Find related existing pages.**
|
|
21
|
+
```sh
|
|
22
|
+
akm wiki search {{WIKI_NAME}} "<key terms from the source>"
|
|
23
|
+
```
|
|
24
|
+
Read the top hits with `akm show wiki:{{WIKI_NAME}}/<page>`. Use
|
|
25
|
+
`akm show wiki:{{WIKI_NAME}}/<page> toc` for large pages.
|
|
26
|
+
|
|
27
|
+
4. **Decide for each candidate.** For each related page:
|
|
28
|
+
- **Append**: add a section or paragraph under the relevant heading.
|
|
29
|
+
Include the raw source in the page's `sources:` frontmatter list.
|
|
30
|
+
- **Contradict**: note the tension explicitly; don't silently overwrite.
|
|
31
|
+
Follow the schema's contradiction policy.
|
|
32
|
+
- **Skip**: source doesn't add to this page — move on.
|
|
33
|
+
|
|
34
|
+
5. **Create new pages for concepts/entities the source introduces.** Each
|
|
35
|
+
new page must have frontmatter with `description`, `pageKind`,
|
|
36
|
+
`xrefs`, and `sources`. Cross-reference with related pages both
|
|
37
|
+
directions.
|
|
38
|
+
|
|
39
|
+
6. **Update xrefs both ways.** If page A now xrefs page B, page B must xref
|
|
40
|
+
page A. `akm wiki lint {{WIKI_NAME}}` will flag violations.
|
|
41
|
+
|
|
42
|
+
7. **Append to `log.md`.** One entry per ingest: date, source slug, one-line
|
|
43
|
+
summary, refs to created/edited pages. Newest at the top.
|
|
44
|
+
|
|
45
|
+
8. **Regenerate the index + verify.**
|
|
46
|
+
```sh
|
|
47
|
+
akm index
|
|
48
|
+
akm wiki lint {{WIKI_NAME}}
|
|
49
|
+
```
|
|
50
|
+
Resolve any lint findings before calling the ingest done.
|
|
51
|
+
|
|
52
|
+
That's it. `akm` never calls an LLM — reasoning is your job; it just owns
|
|
53
|
+
the invariants (raw immutability, unique slugs, ref validation, index
|
|
54
|
+
regeneration, structural lint).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Rules that govern this wiki. Read before ingesting, searching, or editing pages.
|
|
3
|
+
wikiRole: schema
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# {{WIKI_NAME}} wiki schema
|
|
7
|
+
|
|
8
|
+
This wiki follows the three-layer pattern:
|
|
9
|
+
|
|
10
|
+
- `raw/` — immutable ingested sources (never edit)
|
|
11
|
+
- `<page>.md` and `<topic>/<page>.md` — agent-authored pages
|
|
12
|
+
- `schema.md` (this file), `index.md`, `log.md` — wiki-level metadata
|
|
13
|
+
|
|
14
|
+
## Page frontmatter
|
|
15
|
+
|
|
16
|
+
Every page should carry frontmatter so akm can index and link it:
|
|
17
|
+
|
|
18
|
+
```yaml
|
|
19
|
+
---
|
|
20
|
+
description: one-sentence summary used in search and lint
|
|
21
|
+
pageKind: entity | concept | question | note | <your-custom-kind>
|
|
22
|
+
xrefs:
|
|
23
|
+
- wiki:{{WIKI_NAME}}/other-page
|
|
24
|
+
sources:
|
|
25
|
+
- raw/<slug>.md
|
|
26
|
+
---
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`pageKind` accepts any non-empty string. Add new categories freely; they
|
|
30
|
+
will surface in `index.md` as new sections after the next `akm index` run.
|
|
31
|
+
|
|
32
|
+
## Three operations
|
|
33
|
+
|
|
34
|
+
### Ingest
|
|
35
|
+
|
|
36
|
+
1. Copy the new source into `raw/` with `akm wiki stash {{WIKI_NAME}} <path>`.
|
|
37
|
+
2. Find related pages: `akm wiki search {{WIKI_NAME}} "<terms>"`.
|
|
38
|
+
3. For each related page: append a section, note a contradiction, or create a
|
|
39
|
+
new page. Update xrefs on both sides.
|
|
40
|
+
4. Cite the raw source in each touched page's `sources:` frontmatter.
|
|
41
|
+
5. Append one entry to `log.md` describing what was assimilated.
|
|
42
|
+
|
|
43
|
+
### Query
|
|
44
|
+
|
|
45
|
+
1. `akm wiki search {{WIKI_NAME}} "<question>"` — find candidate pages.
|
|
46
|
+
2. `akm show wiki:{{WIKI_NAME}}/<page>` — read the top hits.
|
|
47
|
+
3. Compose the answer from the wiki; cite raw sources only when the wiki
|
|
48
|
+
points at them.
|
|
49
|
+
|
|
50
|
+
### Lint
|
|
51
|
+
|
|
52
|
+
1. `akm wiki lint {{WIKI_NAME}}` — deterministic structural checks.
|
|
53
|
+
2. Resolve each finding: link orphans, fix broken xrefs, add descriptions,
|
|
54
|
+
cite uncited raws, refresh the index.
|
|
55
|
+
|
|
56
|
+
## Hard rules
|
|
57
|
+
|
|
58
|
+
- `raw/` is immutable. Never edit ingested sources.
|
|
59
|
+
- Cross-references must point at pages that actually exist.
|
|
60
|
+
- Prefer appending to an existing page over duplicating one.
|
|
61
|
+
- Cite the raw source id (e.g. `raw/2026-04-foo.md`) when copying claims.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import indexTemplate from "./index-template.md" with { type: "text" };
|
|
5
|
+
import logTemplate from "./log-template.md" with { type: "text" };
|
|
6
|
+
import schemaTemplate from "./schema-template.md" with { type: "text" };
|
|
7
|
+
export function buildSchemaMd(wikiName) {
|
|
8
|
+
return schemaTemplate.replaceAll("{{WIKI_NAME}}", wikiName);
|
|
9
|
+
}
|
|
10
|
+
export function buildIndexMd(wikiName) {
|
|
11
|
+
return indexTemplate.replaceAll("{{WIKI_NAME}}", wikiName);
|
|
12
|
+
}
|
|
13
|
+
export function buildLogMd(wikiName) {
|
|
14
|
+
return logTemplate.replaceAll("{{WIKI_NAME}}", wikiName);
|
|
15
|
+
}
|
package/dist/wiki/wiki.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
1
4
|
/**
|
|
2
5
|
* Multi-wiki support for akm (issue #119).
|
|
3
6
|
*
|
|
@@ -42,12 +45,13 @@ import fs from "node:fs";
|
|
|
42
45
|
import path from "node:path";
|
|
43
46
|
import { parse as yamlParse } from "yaml";
|
|
44
47
|
import { akmSearch } from "../commands/search";
|
|
45
|
-
import { isWithin } from "../core/common";
|
|
46
|
-
import { loadUserConfig, saveConfig } from "../core/config";
|
|
48
|
+
import { isWithin, todayIso } from "../core/common";
|
|
49
|
+
import { getSources, loadUserConfig, saveConfig } from "../core/config";
|
|
47
50
|
import { NotFoundError, UsageError } from "../core/errors";
|
|
48
51
|
import { parseFrontmatter, parseFrontmatterBlock } from "../core/frontmatter";
|
|
49
52
|
import { resolveSourceEntries } from "../indexer/search-source";
|
|
50
|
-
import
|
|
53
|
+
import ingestWorkflowTemplate from "./ingest-workflow-template.md" with { type: "text" };
|
|
54
|
+
import { buildIndexMd, buildLogMd, buildSchemaMd } from "./wiki-templates";
|
|
51
55
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
52
56
|
export const WIKIS_SUBDIR = "wikis";
|
|
53
57
|
export const SCHEMA_MD = "schema.md";
|
|
@@ -399,12 +403,11 @@ export function removeWiki(stashDir, name, options = {}) {
|
|
|
399
403
|
const isStashWiki = fs.existsSync(wikiDir) && isRecognizedStashWiki(wikiDir);
|
|
400
404
|
if (!isStashWiki && external) {
|
|
401
405
|
const config = loadUserConfig();
|
|
402
|
-
const filteredSources = (config
|
|
406
|
+
const filteredSources = getSources(config).filter((entry) => entry.wikiName !== name);
|
|
403
407
|
const installed = (config.installed ?? []).filter((entry) => entry.wikiName !== name);
|
|
404
408
|
saveConfig({
|
|
405
409
|
...config,
|
|
406
410
|
sources: filteredSources.length > 0 ? filteredSources : undefined,
|
|
407
|
-
stashes: undefined,
|
|
408
411
|
installed: installed.length > 0 ? installed : undefined,
|
|
409
412
|
});
|
|
410
413
|
return {
|
|
@@ -665,7 +668,7 @@ function withRawFrontmatter(content, slug) {
|
|
|
665
668
|
// tag the wikiRole for the indexer.
|
|
666
669
|
if (content.startsWith("---"))
|
|
667
670
|
return content;
|
|
668
|
-
const date =
|
|
671
|
+
const date = todayIso();
|
|
669
672
|
return `---\nwikiRole: raw\ningestedAt: ${date}\nslug: ${slug}\n---\n\n${content}`;
|
|
670
673
|
}
|
|
671
674
|
function ensureTrailingNewline(value) {
|
|
@@ -960,60 +963,9 @@ export function regenerateAllWikiIndexes(stashDir) {
|
|
|
960
963
|
export function buildIngestWorkflow(stashDir, name) {
|
|
961
964
|
const wikiDir = resolveWikiSource(stashDir, name).path;
|
|
962
965
|
const schemaPath = path.join(wikiDir, SCHEMA_MD);
|
|
963
|
-
const workflow =
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
Follow these steps. akm commands handle the invariants; use your native
|
|
969
|
-
Read/Write/Edit tools for page edits.
|
|
970
|
-
|
|
971
|
-
1. **Read the schema.** Open \`${schemaPath}\`. It defines the voice, page
|
|
972
|
-
kinds, contradiction policy, and any wiki-specific conventions. Do not
|
|
973
|
-
skip this step even on familiar wikis — the schema may have changed.
|
|
974
|
-
|
|
975
|
-
2. **File the source under \`raw/\`.**
|
|
976
|
-
\`\`\`sh
|
|
977
|
-
akm wiki stash ${name} <path-or-url-to-source>
|
|
978
|
-
# or: cat <source> | akm wiki stash ${name} -
|
|
979
|
-
\`\`\`
|
|
980
|
-
Returns \`{ slug, path, ref }\`. The raw copy is immutable — never edit it.
|
|
981
|
-
|
|
982
|
-
3. **Find related existing pages.**
|
|
983
|
-
\`\`\`sh
|
|
984
|
-
akm wiki search ${name} "<key terms from the source>"
|
|
985
|
-
\`\`\`
|
|
986
|
-
Read the top hits with \`akm show wiki:${name}/<page>\`. Use
|
|
987
|
-
\`akm show wiki:${name}/<page> toc\` for large pages.
|
|
988
|
-
|
|
989
|
-
4. **Decide for each candidate.** For each related page:
|
|
990
|
-
- **Append**: add a section or paragraph under the relevant heading.
|
|
991
|
-
Include the raw source in the page's \`sources:\` frontmatter list.
|
|
992
|
-
- **Contradict**: note the tension explicitly; don't silently overwrite.
|
|
993
|
-
Follow the schema's contradiction policy.
|
|
994
|
-
- **Skip**: source doesn't add to this page — move on.
|
|
995
|
-
|
|
996
|
-
5. **Create new pages for concepts/entities the source introduces.** Each
|
|
997
|
-
new page must have frontmatter with \`description\`, \`pageKind\`,
|
|
998
|
-
\`xrefs\`, and \`sources\`. Cross-reference with related pages both
|
|
999
|
-
directions.
|
|
1000
|
-
|
|
1001
|
-
6. **Update xrefs both ways.** If page A now xrefs page B, page B must xref
|
|
1002
|
-
page A. \`akm wiki lint ${name}\` will flag violations.
|
|
1003
|
-
|
|
1004
|
-
7. **Append to \`log.md\`.** One entry per ingest: date, source slug, one-line
|
|
1005
|
-
summary, refs to created/edited pages. Newest at the top.
|
|
1006
|
-
|
|
1007
|
-
8. **Regenerate the index + verify.**
|
|
1008
|
-
\`\`\`sh
|
|
1009
|
-
akm index
|
|
1010
|
-
akm wiki lint ${name}
|
|
1011
|
-
\`\`\`
|
|
1012
|
-
Resolve any lint findings before calling the ingest done.
|
|
1013
|
-
|
|
1014
|
-
That's it. \`akm\` never calls an LLM — reasoning is your job; it just owns
|
|
1015
|
-
the invariants (raw immutability, unique slugs, ref validation, index
|
|
1016
|
-
regeneration, structural lint).
|
|
1017
|
-
`;
|
|
966
|
+
const workflow = ingestWorkflowTemplate
|
|
967
|
+
.replaceAll("{{WIKI_NAME}}", name)
|
|
968
|
+
.replaceAll("{{WIKI_DIR}}", wikiDir)
|
|
969
|
+
.replaceAll("{{SCHEMA_PATH}}", schemaPath);
|
|
1018
970
|
return { wiki: name, path: wikiDir, schemaPath, workflow };
|
|
1019
971
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
1
4
|
import fs from "node:fs";
|
|
2
5
|
import path from "node:path";
|
|
3
6
|
import { resolveAssetPathFromName } from "../core/asset-spec";
|
|
@@ -5,6 +8,7 @@ import { isWithin, resolveStashDir } from "../core/common";
|
|
|
5
8
|
import { UsageError } from "../core/errors";
|
|
6
9
|
import { warn } from "../core/warn";
|
|
7
10
|
import { parseWorkflow } from "./parser";
|
|
11
|
+
import workflowTemplate from "./workflow-template.md" with { type: "text" };
|
|
8
12
|
const DEFAULT_WORKFLOW_TEMPLATE = renderWorkflowTemplate({
|
|
9
13
|
title: "Example Workflow",
|
|
10
14
|
firstStepTitle: "First Step",
|
|
@@ -134,29 +138,8 @@ export function validateWorkflowSource(target) {
|
|
|
134
138
|
return { path: target, parse: parseWorkflow(content, { path: target }) };
|
|
135
139
|
}
|
|
136
140
|
function renderWorkflowTemplate(input) {
|
|
137
|
-
return
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
params:
|
|
142
|
-
example_param: Explain this parameter
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
# Workflow: ${input.title}
|
|
146
|
-
|
|
147
|
-
## Step: ${input.firstStepTitle}
|
|
148
|
-
Step ID: ${input.firstStepId}
|
|
149
|
-
|
|
150
|
-
### Instructions
|
|
151
|
-
Describe what to do in this step.
|
|
152
|
-
|
|
153
|
-
### Completion Criteria
|
|
154
|
-
- Confirm the first step is complete
|
|
155
|
-
|
|
156
|
-
## Step: Second Step
|
|
157
|
-
Step ID: second-step
|
|
158
|
-
|
|
159
|
-
### Instructions
|
|
160
|
-
Describe what happens next.
|
|
161
|
-
`;
|
|
141
|
+
return workflowTemplate
|
|
142
|
+
.replace("{{TITLE}}", input.title)
|
|
143
|
+
.replace("{{FIRST_STEP_TITLE}}", input.firstStepTitle)
|
|
144
|
+
.replace("{{FIRST_STEP_ID}}", input.firstStepId);
|
|
162
145
|
}
|
package/dist/workflows/cli.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
1
4
|
import { UsageError } from "../core/errors";
|
|
2
5
|
export const WORKFLOW_STEP_STATES = [
|
|
3
6
|
"completed",
|
package/dist/workflows/db.js
CHANGED
|
@@ -1,7 +1,44 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
1
4
|
import { Database } from "bun:sqlite";
|
|
2
5
|
import fs from "node:fs";
|
|
3
6
|
import path from "node:path";
|
|
4
7
|
import { getWorkflowDbPath } from "../core/paths";
|
|
8
|
+
/**
|
|
9
|
+
* workflow.db — Durable SQLite database for workflow run state.
|
|
10
|
+
*
|
|
11
|
+
* Owns the `workflow_runs` and `workflow_run_steps` tables that track active /
|
|
12
|
+
* completed workflow executions. Like `state.db` (and unlike `index.db`), the
|
|
13
|
+
* rows here are NON-REGENERABLE — losing them is data loss. Schema must evolve
|
|
14
|
+
* via incremental, additive migrations recorded in `schema_migrations`.
|
|
15
|
+
*
|
|
16
|
+
* ## Migration-safety contract
|
|
17
|
+
*
|
|
18
|
+
* The `schema_migrations` table records every applied migration by a stable
|
|
19
|
+
* string ID. `runMigrations(db)` is idempotent: new installs run every
|
|
20
|
+
* migration in order; upgrades run only the ones not yet applied. The
|
|
21
|
+
* migration framework here intentionally mirrors `src/core/state-db.ts` so
|
|
22
|
+
* future schema evolution follows a single proven pattern.
|
|
23
|
+
*
|
|
24
|
+
* Permitted schema evolution operations (always migration-safe in SQLite):
|
|
25
|
+
* - ALTER TABLE … ADD COLUMN <name> <type> DEFAULT <value>
|
|
26
|
+
* - CREATE INDEX IF NOT EXISTS …
|
|
27
|
+
* - CREATE TABLE IF NOT EXISTS … (additive new tables)
|
|
28
|
+
*
|
|
29
|
+
* ## Bootstrapping pre-versioning databases
|
|
30
|
+
*
|
|
31
|
+
* Workflow databases created before this file gained `schema_migrations`
|
|
32
|
+
* already have the `workflow_runs.scope_key` column applied by the previous
|
|
33
|
+
* ad-hoc `PRAGMA table_info` + `ALTER TABLE` code. To avoid re-running the
|
|
34
|
+
* migration (which would no-op but still wastes work and clutters logs), the
|
|
35
|
+
* runner detects this state and back-fills the `schema_migrations` row for
|
|
36
|
+
* the scope-key migration before evaluating the migration list. See
|
|
37
|
+
* `bootstrapPreVersioningDb()`.
|
|
38
|
+
*
|
|
39
|
+
* @module workflows/db
|
|
40
|
+
*/
|
|
41
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
5
42
|
export function openWorkflowDatabase(dbPath = getWorkflowDbPath()) {
|
|
6
43
|
const dir = path.dirname(dbPath);
|
|
7
44
|
if (!fs.existsSync(dir)) {
|
|
@@ -10,18 +47,29 @@ export function openWorkflowDatabase(dbPath = getWorkflowDbPath()) {
|
|
|
10
47
|
const db = new Database(dbPath);
|
|
11
48
|
db.exec("PRAGMA journal_mode = WAL");
|
|
12
49
|
db.exec("PRAGMA foreign_keys = ON");
|
|
13
|
-
|
|
50
|
+
ensureBaseSchema(db);
|
|
51
|
+
runMigrations(db);
|
|
14
52
|
return db;
|
|
15
53
|
}
|
|
16
54
|
export function closeWorkflowDatabase(db) {
|
|
17
55
|
db.close();
|
|
18
56
|
}
|
|
19
|
-
|
|
57
|
+
// ── Base schema ──────────────────────────────────────────────────────────────
|
|
58
|
+
/**
|
|
59
|
+
* Create the baseline `workflow_runs` and `workflow_run_steps` tables if they
|
|
60
|
+
* do not exist. These statements are idempotent: existing databases keep their
|
|
61
|
+
* current schema, and migrations evolve them further.
|
|
62
|
+
*
|
|
63
|
+
* NOTE: the `scope_key` column on `workflow_runs` is intentionally NOT declared
|
|
64
|
+
* here. It is added by migration `001-add-scope-key`. Fresh databases will run
|
|
65
|
+
* the migration immediately on first open; pre-versioning databases that
|
|
66
|
+
* already have the column are bootstrapped — see {@link runMigrations}.
|
|
67
|
+
*/
|
|
68
|
+
function ensureBaseSchema(db) {
|
|
20
69
|
db.exec(`
|
|
21
70
|
CREATE TABLE IF NOT EXISTS workflow_runs (
|
|
22
71
|
id TEXT PRIMARY KEY,
|
|
23
72
|
workflow_ref TEXT NOT NULL,
|
|
24
|
-
scope_key TEXT,
|
|
25
73
|
workflow_entry_id INTEGER,
|
|
26
74
|
workflow_title TEXT NOT NULL,
|
|
27
75
|
status TEXT NOT NULL CHECK (status IN ('active', 'completed', 'blocked', 'failed')),
|
|
@@ -53,12 +101,94 @@ function ensureWorkflowSchema(db) {
|
|
|
53
101
|
CREATE INDEX IF NOT EXISTS idx_workflow_run_steps_run_sequence
|
|
54
102
|
ON workflow_run_steps(run_id, sequence_index);
|
|
55
103
|
`);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* All workflow.db migrations in application order. New migrations are
|
|
107
|
+
* APPENDED — never inserted in the middle or reordered.
|
|
108
|
+
*/
|
|
109
|
+
const MIGRATIONS = [
|
|
110
|
+
// ── Migration 001 — add scope_key column ────────────────────────────────────
|
|
111
|
+
//
|
|
112
|
+
// Adds the `scope_key` column to `workflow_runs` so runs can be partitioned
|
|
113
|
+
// per stash/scope. Pre-versioning databases that already have this column
|
|
114
|
+
// are bootstrapped before this migration runs — see runMigrations().
|
|
115
|
+
{
|
|
116
|
+
id: "001-add-scope-key",
|
|
117
|
+
up: `
|
|
118
|
+
ALTER TABLE workflow_runs ADD COLUMN scope_key TEXT;
|
|
119
|
+
|
|
120
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_runs_scope_ref_status
|
|
121
|
+
ON workflow_runs(scope_key, workflow_ref, status);
|
|
122
|
+
`,
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
/**
|
|
126
|
+
* Stable id of the scope_key migration. Exported for bootstrap detection and
|
|
127
|
+
* tests.
|
|
128
|
+
*/
|
|
129
|
+
const SCOPE_KEY_MIGRATION_ID = "001-add-scope-key";
|
|
130
|
+
/**
|
|
131
|
+
* Create the migrations table if it does not exist. Called unconditionally on
|
|
132
|
+
* every open so a fresh database bootstraps correctly.
|
|
133
|
+
*/
|
|
134
|
+
function ensureMigrationsTable(db) {
|
|
135
|
+
db.exec(`
|
|
136
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
137
|
+
id TEXT PRIMARY KEY,
|
|
138
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
139
|
+
);
|
|
140
|
+
`);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Detect whether a column exists on a given table.
|
|
144
|
+
*/
|
|
145
|
+
function hasColumn(db, table, column) {
|
|
146
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
147
|
+
return rows.some((r) => r.name === column);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Back-fill `schema_migrations` rows for any schema state that existed before
|
|
151
|
+
* this file gained migration tracking.
|
|
152
|
+
*
|
|
153
|
+
* The pre-versioning code added the `scope_key` column on `workflow_runs` via
|
|
154
|
+
* an ad-hoc PRAGMA / ALTER TABLE pair. Those databases must not re-run the
|
|
155
|
+
* scope_key migration (the ALTER would fail with "duplicate column name"
|
|
156
|
+
* since the migration body does not use IF NOT EXISTS — SQLite does not
|
|
157
|
+
* support that clause on ALTER TABLE ADD COLUMN). Instead, we mark the
|
|
158
|
+
* migration as already applied.
|
|
159
|
+
*
|
|
160
|
+
* This function is a no-op on fresh databases: the `scope_key` column does
|
|
161
|
+
* not exist, so the migration runs normally and records itself.
|
|
162
|
+
*/
|
|
163
|
+
function bootstrapPreVersioningDb(db) {
|
|
164
|
+
const alreadyRecorded = db.prepare("SELECT 1 FROM schema_migrations WHERE id = ?").get(SCOPE_KEY_MIGRATION_ID);
|
|
165
|
+
if (alreadyRecorded)
|
|
166
|
+
return;
|
|
167
|
+
if (hasColumn(db, "workflow_runs", "scope_key")) {
|
|
168
|
+
db.prepare("INSERT INTO schema_migrations (id) VALUES (?)").run(SCOPE_KEY_MIGRATION_ID);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Apply every pending migration in a single transaction per migration.
|
|
173
|
+
*
|
|
174
|
+
* Each migration is applied in its own transaction so a failure in migration N
|
|
175
|
+
* does not roll back already-applied migrations 1..N-1. The migration row is
|
|
176
|
+
* inserted AFTER the DDL succeeds — a crash mid-migration leaves no row and
|
|
177
|
+
* the migration is retried on next open.
|
|
178
|
+
*
|
|
179
|
+
* Called automatically by {@link openWorkflowDatabase}.
|
|
180
|
+
*/
|
|
181
|
+
export function runMigrations(db) {
|
|
182
|
+
ensureMigrationsTable(db);
|
|
183
|
+
bootstrapPreVersioningDb(db);
|
|
184
|
+
const appliedRows = db.prepare("SELECT id FROM schema_migrations").all();
|
|
185
|
+
const applied = new Set(appliedRows.map((r) => r.id));
|
|
186
|
+
for (const migration of MIGRATIONS) {
|
|
187
|
+
if (applied.has(migration.id))
|
|
188
|
+
continue;
|
|
189
|
+
db.transaction(() => {
|
|
190
|
+
db.exec(migration.up);
|
|
191
|
+
db.prepare("INSERT INTO schema_migrations (id) VALUES (?)").run(migration.id);
|
|
192
|
+
})();
|
|
62
193
|
}
|
|
63
|
-
db.exec("CREATE INDEX IF NOT EXISTS idx_workflow_runs_scope_ref_status ON workflow_runs(scope_key, workflow_ref, status)");
|
|
64
194
|
}
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* `entry_json` column or widening `StashEntry` with a workflow-shaped field.
|
|
5
|
-
*
|
|
6
|
-
* The renderer is called during metadata generation; the indexer writes the
|
|
7
|
-
* document to `workflow_documents` after `upsertEntry` returns the row id.
|
|
8
|
-
* A WeakMap keyed by the entry object preserves the parse work between the
|
|
9
|
-
* two phases without leaking memory if the entry is dropped.
|
|
10
|
-
*/
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
11
4
|
const cache = new WeakMap();
|
|
12
5
|
export function cacheWorkflowDocument(entry, doc) {
|
|
13
6
|
cache.set(entry, doc);
|
package/dist/workflows/parser.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
1
4
|
/**
|
|
2
5
|
* Show + indexing renderer for workflow assets.
|
|
3
6
|
*
|
|
@@ -8,6 +11,7 @@
|
|
|
8
11
|
*/
|
|
9
12
|
import { makeAssetRef } from "../core/asset-ref";
|
|
10
13
|
import { UsageError } from "../core/errors";
|
|
14
|
+
import { registerMetadataContributor } from "../indexer/metadata-contributors";
|
|
11
15
|
import { cacheWorkflowDocument } from "./document-cache";
|
|
12
16
|
import { parseWorkflow } from "./parser";
|
|
13
17
|
function shellQuote(value) {
|
|
@@ -54,8 +58,12 @@ export const workflowMdRenderer = {
|
|
|
54
58
|
})),
|
|
55
59
|
};
|
|
56
60
|
},
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
};
|
|
62
|
+
registerMetadataContributor({
|
|
63
|
+
name: "workflow-document-metadata",
|
|
64
|
+
appliesTo: ({ rendererName }) => rendererName === "workflow-md",
|
|
65
|
+
contribute(entry, { renderContext }) {
|
|
66
|
+
const doc = loadDocument(renderContext);
|
|
59
67
|
const hints = new Set(entry.searchHints ?? []);
|
|
60
68
|
hints.add(doc.title);
|
|
61
69
|
for (const step of doc.steps) {
|
|
@@ -75,4 +83,4 @@ export const workflowMdRenderer = {
|
|
|
75
83
|
}
|
|
76
84
|
cacheWorkflowDocument(entry, doc);
|
|
77
85
|
},
|
|
78
|
-
};
|
|
86
|
+
});
|