akm-cli 0.9.1 → 0.9.2-alpha.2
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 +103 -28
- package/README.md +3 -1
- package/SECURITY.md +1 -1
- package/STABILITY.md +1 -1
- package/dist/akm +2 -2
- package/dist/akm-migrate +2 -2
- package/dist/assets/hints/cli-hints-full.md +14 -9
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/models.json +35 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
- package/dist/assets/tasks/core/extract.yml +6 -5
- package/dist/assets/tasks/core/improve.yml +6 -5
- package/dist/assets/tasks/core/index-refresh.yml +6 -5
- package/dist/assets/tasks/core/sync.yml +6 -5
- package/dist/assets/tasks/core/version-check.yml +6 -5
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
- package/dist/cli/confirm.js +2 -2
- package/dist/cli/parse-args.js +3 -24
- package/dist/cli/retired-commands.js +1 -1
- package/dist/cli/shared.js +2 -2
- package/dist/cli.js +11 -9
- package/dist/commands/agent/agent-dispatch.js +55 -89
- package/dist/commands/agent/contribute-cli.js +12 -45
- package/dist/commands/command/builtin-action.js +32 -0
- package/dist/commands/command/command-cli.js +99 -0
- package/dist/commands/command/command-execution.js +308 -0
- package/dist/commands/command/execution-source-loader.js +176 -0
- package/dist/commands/command/portable-template.js +60 -0
- package/dist/commands/config-cli.js +10 -4
- package/dist/commands/env/env.js +4 -2
- package/dist/commands/feedback-cli.js +1 -1
- package/dist/commands/health/checks.js +241 -29
- package/dist/commands/health/html-report.js +0 -14
- package/dist/commands/health/report-view-model.js +0 -1
- package/dist/commands/health/surfaces.js +6 -7
- package/dist/commands/health/types.js +0 -2
- package/dist/commands/health.js +63 -18
- package/dist/commands/improve/collapse-detector.js +5 -6
- package/dist/commands/improve/consolidate.js +251 -214
- package/dist/commands/improve/distill/promote-memory.js +71 -34
- package/dist/commands/improve/distill/quality-gate.js +17 -5
- package/dist/commands/improve/distill.js +232 -155
- package/dist/commands/improve/eligibility.js +112 -79
- package/dist/commands/improve/execution.js +57 -0
- package/dist/commands/improve/extract-cli.js +5 -5
- package/dist/commands/improve/extract-prompt.js +64 -22
- package/dist/commands/improve/extract.js +608 -360
- package/dist/commands/improve/improve-strategies.js +43 -14
- package/dist/commands/improve/improve.js +249 -29
- package/dist/commands/improve/loop-stages.js +11 -17
- package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
- package/dist/commands/improve/outcome-loop.js +22 -38
- package/dist/commands/improve/planner.js +134 -0
- package/dist/commands/improve/preparation.js +730 -409
- package/dist/commands/improve/reflect.js +386 -223
- package/dist/commands/improve/run-context.js +3 -4
- package/dist/commands/improve/salience.js +6 -58
- package/dist/commands/improve/session-asset.js +12 -12
- package/dist/commands/lint/index.js +101 -29
- package/dist/commands/migrate-cli.js +11 -69
- package/dist/commands/migration-tool.js +6 -9
- package/dist/commands/models-cli.js +27 -0
- package/dist/commands/proposal/drain.js +258 -186
- package/dist/commands/proposal/proposal-cli.js +32 -10
- package/dist/commands/proposal/proposal.js +2 -5
- package/dist/commands/proposal/propose.js +192 -172
- package/dist/commands/proposal/repository.js +54 -91
- package/dist/commands/proposal/validators/proposal-validators.js +9 -7
- package/dist/commands/read/curate.js +53 -22
- package/dist/commands/read/registry-search.js +25 -9
- package/dist/commands/read/remember-cli.js +14 -2
- package/dist/commands/read/search.js +10 -4
- package/dist/commands/read/show.js +139 -153
- package/dist/commands/registry-cli.js +16 -7
- package/dist/commands/remember.js +33 -18
- package/dist/commands/sources/add-cli.js +19 -178
- package/dist/commands/sources/bundle-cli.js +15 -3
- package/dist/commands/sources/dangerous-env-audit.js +135 -0
- package/dist/commands/sources/info.js +2 -1
- package/dist/commands/sources/installed-stashes.js +901 -177
- package/dist/commands/sources/schema-repair.js +174 -95
- package/dist/commands/sources/self-update.js +30 -74
- package/dist/commands/sources/source-add.js +3 -5
- package/dist/commands/sources/sources-cli.js +2 -15
- package/dist/commands/sources/update-transaction.js +220 -0
- package/dist/commands/tasks/tasks-cli.js +3 -3
- package/dist/commands/tasks/tasks.js +736 -317
- package/dist/commands/workflow-cli.js +2 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
- package/dist/core/adapter/adapters/akm-adapter.js +85 -35
- package/dist/core/adapter/adapters/akm-lint.js +54 -39
- package/dist/core/adapter/adapters/akm-metadata.js +45 -45
- package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
- package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
- package/dist/core/adapter/adapters/index.js +0 -9
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
- package/dist/core/adapter/adapters/okf-adapter.js +4 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
- package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
- package/dist/core/adapter/execution-source.js +308 -0
- package/dist/core/adapter/recognize-match.js +36 -13
- package/dist/core/adapter/registry.js +0 -9
- package/dist/core/asset/stash-meta.js +94 -4
- package/dist/core/common.js +6 -11
- package/dist/core/config/config-io.js +3 -3
- package/dist/core/config/config-schema.js +18 -40
- package/dist/core/config/config-sources.js +11 -21
- package/dist/core/config/config-walker.js +31 -13
- package/dist/core/config/config.js +23 -26
- package/dist/core/config/schema/engines.js +8 -7
- package/dist/core/config/schema/improve-processes.js +29 -5
- package/dist/core/config/schema/index-config.js +0 -27
- package/dist/core/config/schema/primitives.js +1 -23
- package/dist/core/config/schema/sources-bundles.js +13 -16
- package/dist/core/errors.js +2 -0
- package/dist/core/events.js +68 -32
- package/dist/core/extra-params.js +1 -0
- package/dist/core/improve-result.js +315 -0
- package/dist/core/lesson-lint.js +0 -6
- package/dist/core/maintenance-barrier.js +4 -4
- package/dist/core/network-policy.js +152 -0
- package/dist/core/paths.js +1 -1
- package/dist/core/recognition-util.js +4 -4
- package/dist/core/registry-url.js +456 -0
- package/dist/core/state/migrations.js +161 -47
- package/dist/core/state-db.js +453 -80
- package/dist/core/system-error.js +32 -0
- package/dist/core/time.js +2 -12
- package/dist/core/write-source.js +0 -18
- package/dist/execution/directory-identity.js +52 -0
- package/dist/execution/executable-identity.js +107 -0
- package/dist/execution/guarded-source.js +398 -0
- package/dist/execution/json.js +95 -0
- package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
- package/dist/execution/record.js +55 -0
- package/dist/execution/resolved-request.js +730 -0
- package/dist/execution/source.js +320 -0
- package/dist/indexer/bundle-identity-guard.js +5 -4
- package/dist/indexer/db/graph-db.js +33 -0
- package/dist/indexer/graph/graph-boost.js +3 -4
- package/dist/indexer/graph/graph-extraction.js +562 -373
- package/dist/indexer/index-written-assets.js +78 -39
- package/dist/indexer/indexer.js +471 -432
- package/dist/indexer/installations.js +6 -0
- package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
- package/dist/indexer/materialize-embeddings.js +155 -0
- package/dist/indexer/passes/memory-inference.js +227 -174
- package/dist/indexer/passes/metadata.js +263 -118
- package/dist/indexer/scan/doc-to-entry.js +7 -10
- package/dist/indexer/scan/drain-dir.js +51 -23
- package/dist/indexer/search/db-search.js +156 -50
- package/dist/indexer/search/fts-query.js +40 -40
- package/dist/indexer/search/ranking.js +36 -1
- package/dist/indexer/search/search-attribution.js +3 -1
- package/dist/indexer/search/search-fields.js +23 -14
- package/dist/indexer/search/search-hit-enrichers.js +1 -1
- package/dist/indexer/search/search-source.js +7 -16
- package/dist/indexer/search/semantic-status.js +10 -1
- package/dist/indexer/usage/show-usage.js +105 -0
- package/dist/indexer/usage/usage-events.js +7 -2
- package/dist/indexer/walk/matchers.js +40 -10
- package/dist/indexer/walk/path-resolver.js +5 -2
- package/dist/indexer/walk/walker.js +20 -2
- package/dist/integrations/agent/builder-shared.js +3 -6
- package/dist/integrations/agent/conversation-fallback.js +16 -0
- package/dist/integrations/agent/engine-resolution.js +87 -87
- package/dist/integrations/agent/execution-cascade.js +566 -0
- package/dist/integrations/agent/execution-definitions.js +211 -0
- package/dist/integrations/agent/execution-lowering.js +811 -0
- package/dist/integrations/agent/execution-preparation.js +67 -0
- package/dist/integrations/agent/index.js +0 -2
- package/dist/integrations/agent/inline-execution.js +74 -0
- package/dist/integrations/agent/model-map.js +515 -0
- package/dist/integrations/agent/persona-fallback.js +30 -0
- package/dist/integrations/agent/request-lowering.js +186 -0
- package/dist/integrations/agent/runner-dispatch.js +230 -37
- package/dist/integrations/agent/runner.js +12 -83
- package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
- package/dist/integrations/harnesses/claude/index.js +1 -5
- package/dist/integrations/harnesses/claude/session-log.js +3 -33
- package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +4 -44
- package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
- package/dist/integrations/harnesses/opencode/index.js +0 -2
- package/dist/integrations/harnesses/opencode/session-log.js +14 -204
- package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
- package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/shared.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -3
- package/dist/integrations/lockfile.js +82 -79
- package/dist/integrations/session-logs/index.js +6 -17
- package/dist/integrations/session-logs/provider-base.js +1 -29
- package/dist/llm/client.js +10 -5
- package/dist/llm/embedder.js +6 -7
- package/dist/llm/embedders/local.js +37 -88
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +75 -50
- package/dist/llm/index-passes.js +43 -5
- package/dist/llm/memory-infer.js +8 -6
- package/dist/llm/metadata-enhance.js +5 -3
- package/dist/llm/structured-call.js +122 -25
- package/dist/output/format-exempt.js +1 -1
- package/dist/output/render-registry.js +0 -16
- package/dist/output/renderers.js +12 -7
- package/dist/output/shapes/curate.js +1 -0
- package/dist/output/shapes/helpers.js +10 -2
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/command-format.js +31 -33
- package/dist/output/text/health-format.js +1 -29
- package/dist/output/text/migrate.js +6 -56
- package/dist/output/text/proposal-format.js +16 -1
- package/dist/output/text/workflow-format.js +16 -0
- package/dist/registry/network.js +279 -0
- package/dist/registry/pinned-request-helper.js +247 -0
- package/dist/registry/pinned-transport.js +717 -0
- package/dist/registry/providers/skills-sh.js +18 -6
- package/dist/registry/providers/static-index.js +20 -7
- package/dist/registry/resolve.js +53 -28
- package/dist/scripts/akm-migrate-node.js +19334 -52269
- package/dist/scripts/akm-migrate.js +19270 -51612
- package/dist/setup/registry-stash-loader.js +64 -20
- package/dist/setup/semantic-assets.js +9 -34
- package/dist/setup/setup.js +12 -30
- package/dist/setup/source-identity.js +17 -0
- package/dist/setup/steps/sources.js +36 -15
- package/dist/setup/steps/tasks.js +39 -11
- package/dist/sources/providers/git-provider.js +3 -3
- package/dist/sources/providers/npm.js +2 -2
- package/dist/sources/providers/provider-utils.js +4 -3
- package/dist/sources/providers/website.js +11 -7
- package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
- package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
- package/dist/sources/website-url.js +73 -0
- package/dist/storage/engines/sqlite-migrations.js +81 -26
- package/dist/storage/managed-db.js +27 -24
- package/dist/storage/repositories/events-repository.js +3 -0
- package/dist/storage/repositories/index-connection.js +42 -10
- package/dist/storage/repositories/index-entries-repository.js +203 -229
- package/dist/storage/repositories/index-entry-mapper.js +8 -12
- package/dist/storage/repositories/index-entry-schema.js +255 -0
- package/dist/storage/repositories/index-fts-repository.js +64 -71
- package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
- package/dist/storage/repositories/index-meta-repository.js +0 -11
- package/dist/storage/repositories/index-schema.js +74 -350
- package/dist/storage/repositories/index-utility-repository.js +12 -17
- package/dist/storage/repositories/index-vec-repository.js +56 -7
- package/dist/storage/repositories/proposals-repository.js +4 -127
- package/dist/storage/repositories/registry-cache.js +2 -1
- package/dist/storage/repositories/task-history-repository.js +20 -40
- package/dist/storage/repositories/workflow-runs-repository.js +228 -129
- package/dist/storage/sqlite-read-snapshot.js +148 -0
- package/dist/tasks/backends/cron.js +170 -42
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +787 -202
- package/dist/tasks/backends/schtasks.js +282 -83
- package/dist/tasks/embedded.js +7 -7
- package/dist/tasks/frozen-script.js +50 -0
- package/dist/tasks/resolve-akm-bin.js +5 -1
- package/dist/tasks/runner.js +239 -251
- package/dist/tasks/runtime-v3.js +281 -0
- package/dist/tasks/scheduler-binding.js +272 -0
- package/dist/tasks/scheduler-invocation.js +57 -43
- package/dist/tasks/scheduler-sync.js +654 -0
- package/dist/tasks/source-v3.js +752 -0
- package/dist/tasks/standalone-script-entry.js +5 -0
- package/dist/tasks/task-id.js +29 -0
- package/dist/workflows/authoring/authoring.js +15 -32
- package/dist/workflows/exec/dispatch-redaction.js +14 -8
- package/dist/workflows/exec/exec-unit.js +7 -28
- package/dist/workflows/exec/frozen-judge.js +57 -89
- package/dist/workflows/exec/lowering-notices.js +23 -0
- package/dist/workflows/exec/native-executor.js +301 -458
- package/dist/workflows/exec/param-secrets.js +4 -3
- package/dist/workflows/exec/run-workflow.js +26 -32
- package/dist/workflows/exec/step-work.js +105 -109
- package/dist/workflows/exec/unit-dispatch.js +103 -27
- package/dist/workflows/exec/unit-writer.js +3 -3
- package/dist/workflows/exec/worktree.js +2 -2
- package/dist/workflows/ir/compile.js +86 -72
- package/dist/workflows/ir/environment-v4.js +328 -0
- package/dist/workflows/ir/freeze-v4.js +122 -0
- package/dist/workflows/ir/plan-hash.js +13 -7
- package/dist/workflows/ir/schema-v4.js +525 -0
- package/dist/workflows/ir/schema.js +25 -284
- package/dist/workflows/ir/source-freeze-v4.js +506 -0
- package/dist/workflows/parser.js +27 -24
- package/dist/workflows/program/schema.js +1 -2
- package/dist/workflows/renderer.js +42 -29
- package/dist/workflows/resource-limits.js +4 -5
- package/dist/workflows/runtime/agent-identity.js +11 -13
- package/dist/workflows/runtime/plan-classifier.js +8 -8
- package/dist/workflows/runtime/runs.js +27 -43
- package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
- package/dist/workflows/source-files.js +373 -0
- package/dist/workflows/source-ir/compile.js +196 -0
- package/dist/workflows/source-ir/github-yaml.js +577 -0
- package/dist/workflows/source-ir/ordering.js +38 -0
- package/dist/workflows/source-ir/program.js +50 -0
- package/dist/workflows/source-ir/result.js +26 -0
- package/dist/workflows/source-ir/schema.js +772 -0
- package/dist/workflows/source-ir/semantics.js +242 -0
- package/dist/workflows/source-ir/uses.js +14 -0
- package/docs/README.md +2 -0
- package/docs/migration/README.md +3 -1
- package/docs/migration/release-notes/0.9.2.md +55 -0
- package/docs/migration/release-notes/README.md +5 -0
- package/docs/migration/v0.8-to-v0.9.md +76 -1077
- package/docs/migration/v0.9.0-troubleshooting.md +104 -516
- package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
- package/docs/reference/README.md +1 -0
- package/docs/reference/cli.md +230 -98
- package/docs/reference/configuration.md +159 -36
- package/docs/reference/data-and-telemetry.md +19 -1
- package/docs/reference/supported-formats.md +23 -3
- package/docs/reference/tasks.md +182 -0
- package/docs/reference/workflow-schema.md +91 -40
- package/docs/reference/workflows.md +33 -6
- package/package.json +10 -6
- package/schemas/akm-config.json +372 -224
- package/schemas/akm-task.json +324 -80
- package/schemas/akm-workflow.json +6 -9
- package/dist/core/migration-operation.js +0 -75
- package/dist/integrations/agent/model-aliases.js +0 -74
- package/dist/tasks/parser.js +0 -380
- package/dist/tasks/schema.js +0 -123
- package/dist/tasks/validator.js +0 -80
- package/dist/workflows/ir/freeze.js +0 -320
- package/dist/workflows/runtime/document-cache.js +0 -13
|
@@ -6,7 +6,6 @@ import path from "node:path";
|
|
|
6
6
|
import { parseBundleRef } from "../../core/asset/asset-ref.js";
|
|
7
7
|
import { parseFrontmatter } from "../../core/asset/frontmatter.js";
|
|
8
8
|
import { asNonEmptyString } from "../../core/common.js";
|
|
9
|
-
import { loadUserConfig } from "../../core/config/config.js";
|
|
10
9
|
import { isVerbose, warn } from "../../core/warn.js";
|
|
11
10
|
export const SCOPE_KEYS = ["user", "agent", "run", "channel"];
|
|
12
11
|
// ── Quality semantics (v1 spec §4.2) ────────────────────────────────────────
|
|
@@ -180,10 +179,8 @@ export function validateStashEntry(entry) {
|
|
|
180
179
|
if (typeof e.derivedFrom === "string" && e.derivedFrom.trim().length > 0) {
|
|
181
180
|
result.derivedFrom = e.derivedFrom.trim();
|
|
182
181
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (typeof e.bodyOpening === "string" && e.bodyOpening.trim().length > 0) {
|
|
186
|
-
result.bodyOpening = e.bodyOpening;
|
|
182
|
+
if (typeof e.content === "string" && e.content.trim().length > 0) {
|
|
183
|
+
result.content = e.content;
|
|
187
184
|
}
|
|
188
185
|
if (typeof e.scope === "object" && e.scope !== null && !Array.isArray(e.scope)) {
|
|
189
186
|
const scope = normalizeScopeObject(e.scope);
|
|
@@ -835,36 +832,13 @@ export function isEnrichmentComplete(entry) {
|
|
|
835
832
|
const hasSearchHints = Array.isArray(entry.searchHints) && entry.searchHints.length > 0;
|
|
836
833
|
return hasDescription && hasTags && hasSearchHints;
|
|
837
834
|
}
|
|
838
|
-
// ──
|
|
835
|
+
// ── Native Markdown search projection ──────────────────────────────────────
|
|
839
836
|
/**
|
|
840
|
-
* Maximum
|
|
841
|
-
*
|
|
842
|
-
*
|
|
837
|
+
* Maximum native Markdown prose carried by the low-weight `content` field.
|
|
838
|
+
* Structured fields remain separate and higher-weighted; this bound prevents
|
|
839
|
+
* large documents from dominating index size or embedding inputs.
|
|
843
840
|
*/
|
|
844
|
-
export const
|
|
845
|
-
/**
|
|
846
|
-
* Minimum characters retained when the cap truncates at a word boundary. A
|
|
847
|
-
* boundary cut that would retain less than this falls back to a hard cut, so
|
|
848
|
-
* one pathological long token cannot gut the capture.
|
|
849
|
-
*/
|
|
850
|
-
const BODY_OPENING_MIN_RETAINED_CHARS = 250;
|
|
851
|
-
/**
|
|
852
|
-
* True when `index.indexBodyOpening` is enabled in the user config.
|
|
853
|
-
*
|
|
854
|
-
* The gate is the GLOBAL user config, read directly by the metadata pass so
|
|
855
|
-
* every indexing entry point (stash walk, flat walk, write-path indexing)
|
|
856
|
-
* honors the flag without parameter plumbing. Fail-open: an unreadable or
|
|
857
|
-
* invalid config must never break indexing (CLI entry points surface config
|
|
858
|
-
* errors loudly on their own), so any load failure reads as "off".
|
|
859
|
-
*/
|
|
860
|
-
function isBodyOpeningIndexingEnabled() {
|
|
861
|
-
try {
|
|
862
|
-
return loadUserConfig().index?.indexBodyOpening === true;
|
|
863
|
-
}
|
|
864
|
-
catch {
|
|
865
|
-
return false;
|
|
866
|
-
}
|
|
867
|
-
}
|
|
841
|
+
export const MARKDOWN_CONTENT_MAX_CHARS = 16_384;
|
|
868
842
|
/**
|
|
869
843
|
* Locate a leading nested frontmatter block in a body: up to three blank
|
|
870
844
|
* lines, then a `---` line, closed by a later `---` line. Mirrors the
|
|
@@ -892,7 +866,7 @@ function findInnerFrontmatterBlock(lines) {
|
|
|
892
866
|
* marker — in the outer frontmatter data OR in a nested inner block at the
|
|
893
867
|
* top of the body (both producer layouts exist; see base-linter's
|
|
894
868
|
* `extractFrontmatterRefs`). Session bodies are raw transcripts, never a
|
|
895
|
-
*
|
|
869
|
+
* searchable content projection.
|
|
896
870
|
*/
|
|
897
871
|
function hasSessionMemoryMarker(fmData, body) {
|
|
898
872
|
if (typeof fmData.akm_memory_kind === "string")
|
|
@@ -930,96 +904,271 @@ function isFrontmatterShaped(lines, block) {
|
|
|
930
904
|
}
|
|
931
905
|
return true;
|
|
932
906
|
}
|
|
907
|
+
function truncateUnicodeSafe(text, maxChars) {
|
|
908
|
+
if (text.length <= maxChars)
|
|
909
|
+
return text;
|
|
910
|
+
let cut = text.slice(0, maxChars);
|
|
911
|
+
const lastCode = cut.charCodeAt(cut.length - 1);
|
|
912
|
+
if (lastCode >= 0xd800 && lastCode <= 0xdbff)
|
|
913
|
+
cut = cut.slice(0, -1);
|
|
914
|
+
const boundary = cut.lastIndexOf(" ");
|
|
915
|
+
if (boundary >= Math.floor(maxChars * 0.9))
|
|
916
|
+
cut = cut.slice(0, boundary);
|
|
917
|
+
return cut.trimEnd();
|
|
918
|
+
}
|
|
919
|
+
function parseMarkdownFenceOpening(line) {
|
|
920
|
+
const match = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line);
|
|
921
|
+
const run = match?.[1];
|
|
922
|
+
if (!run)
|
|
923
|
+
return undefined;
|
|
924
|
+
const marker = run[0];
|
|
925
|
+
// CommonMark forbids backticks in the info string of a backtick fence.
|
|
926
|
+
if (marker === "`" && match?.[2]?.includes("`"))
|
|
927
|
+
return undefined;
|
|
928
|
+
return { marker, length: run.length };
|
|
929
|
+
}
|
|
930
|
+
function isMarkdownFenceClosing(line, fence) {
|
|
931
|
+
let cursor = 0;
|
|
932
|
+
while (cursor < line.length && cursor < 3 && line[cursor] === " ")
|
|
933
|
+
cursor += 1;
|
|
934
|
+
const runStart = cursor;
|
|
935
|
+
while (cursor < line.length && line[cursor] === fence.marker)
|
|
936
|
+
cursor += 1;
|
|
937
|
+
if (cursor - runStart < fence.length)
|
|
938
|
+
return false;
|
|
939
|
+
return /^[\t ]*$/.test(line.slice(cursor));
|
|
940
|
+
}
|
|
941
|
+
function stripMarkdownHtmlComments(line, state) {
|
|
942
|
+
let cursor = 0;
|
|
943
|
+
let visible = "";
|
|
944
|
+
while (cursor < line.length) {
|
|
945
|
+
if (state.inComment) {
|
|
946
|
+
const close = line.indexOf("-->", cursor);
|
|
947
|
+
if (close < 0)
|
|
948
|
+
return visible;
|
|
949
|
+
state.inComment = false;
|
|
950
|
+
cursor = close + 3;
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
const open = line.indexOf("<!--", cursor);
|
|
954
|
+
if (open < 0)
|
|
955
|
+
return `${visible}${line.slice(cursor)}`;
|
|
956
|
+
visible += line.slice(cursor, open);
|
|
957
|
+
state.inComment = true;
|
|
958
|
+
cursor = open + 4;
|
|
959
|
+
}
|
|
960
|
+
return visible;
|
|
961
|
+
}
|
|
962
|
+
function findBalancedMarkdownClose(text, openAt, open, close) {
|
|
963
|
+
let depth = 1;
|
|
964
|
+
for (let cursor = openAt + 1; cursor < text.length; cursor += 1) {
|
|
965
|
+
const char = text[cursor];
|
|
966
|
+
if (char === "\\") {
|
|
967
|
+
cursor += 1;
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
if (char === open)
|
|
971
|
+
depth += 1;
|
|
972
|
+
if (char !== close)
|
|
973
|
+
continue;
|
|
974
|
+
depth -= 1;
|
|
975
|
+
if (depth === 0)
|
|
976
|
+
return cursor;
|
|
977
|
+
}
|
|
978
|
+
return undefined;
|
|
979
|
+
}
|
|
933
980
|
/**
|
|
934
|
-
*
|
|
935
|
-
*
|
|
936
|
-
*
|
|
937
|
-
* fenced code blocks (``` or ~~~, including their contents), and a leading
|
|
938
|
-
* nested frontmatter block — skipped only when its interior is actually
|
|
939
|
-
* frontmatter-shaped, so prose wrapped in decorative `---` lines is still
|
|
940
|
-
* captured; then collect consecutive non-blank lines until the paragraph
|
|
941
|
-
* ends. Deliberate asymmetry: a `---` row after captured prose ENDS the
|
|
942
|
-
* paragraph and keeps it (favoring the callout/thematic-break reading over
|
|
943
|
-
* CommonMark's setext-H2), while a `=+` row can only be a setext underline
|
|
944
|
-
* and so discards the pending lines as heading text. The result is capped at
|
|
945
|
-
* {@link BODY_OPENING_MAX_CHARS} chars — truncated at the last word boundary
|
|
946
|
-
* that still retains a substantial prefix, with a trailing ellipsis. Returns
|
|
947
|
-
* `undefined` when the body has no prose (frontmatter-only files,
|
|
948
|
-
* headings/fences-only bodies).
|
|
981
|
+
* Find the closing parenthesis of an inline link destination. Parentheses in
|
|
982
|
+
* angle-delimited destinations and quoted titles are data, while unquoted
|
|
983
|
+
* parentheses remain balanced. Backslash escapes suppress delimiter meaning.
|
|
949
984
|
*/
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
let
|
|
956
|
-
let
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
const fenceMatch = trimmed.match(/^(`{3,}|~{3,})/);
|
|
961
|
-
if (inFence) {
|
|
962
|
-
// Fence interiors are never prose (and may be secrets-adjacent command
|
|
963
|
-
// text); skip until the matching closing marker.
|
|
964
|
-
if (fenceMatch && fenceMatch[1].charAt(0) === fenceChar)
|
|
965
|
-
inFence = false;
|
|
985
|
+
function findMarkdownDestinationClose(text, openAt) {
|
|
986
|
+
let depth = 1;
|
|
987
|
+
let phase = "before-destination";
|
|
988
|
+
let quote;
|
|
989
|
+
let parenthesizedTitle = false;
|
|
990
|
+
let titleSeparator = false;
|
|
991
|
+
for (let cursor = openAt + 1; cursor < text.length; cursor += 1) {
|
|
992
|
+
const char = text[cursor];
|
|
993
|
+
if (char === "\\") {
|
|
994
|
+
cursor += 1;
|
|
966
995
|
continue;
|
|
967
996
|
}
|
|
968
|
-
if (
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
if (
|
|
972
|
-
|
|
997
|
+
if (phase === "before-destination") {
|
|
998
|
+
if (/\s/u.test(char ?? ""))
|
|
999
|
+
continue;
|
|
1000
|
+
if (char === "<") {
|
|
1001
|
+
phase = "angle-destination";
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
if (char === ")")
|
|
1005
|
+
return cursor;
|
|
1006
|
+
phase = "bare-destination";
|
|
1007
|
+
}
|
|
1008
|
+
if (phase === "angle-destination") {
|
|
1009
|
+
if (char === ">")
|
|
1010
|
+
phase = "after-destination";
|
|
973
1011
|
continue;
|
|
974
1012
|
}
|
|
975
|
-
if (
|
|
976
|
-
if (
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1013
|
+
if (phase === "title") {
|
|
1014
|
+
if (quote) {
|
|
1015
|
+
if (char === quote) {
|
|
1016
|
+
quote = undefined;
|
|
1017
|
+
phase = "after-destination";
|
|
1018
|
+
titleSeparator = false;
|
|
1019
|
+
}
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
if (parenthesizedTitle) {
|
|
1023
|
+
if (char === "(")
|
|
1024
|
+
depth += 1;
|
|
1025
|
+
if (char !== ")")
|
|
1026
|
+
continue;
|
|
1027
|
+
depth -= 1;
|
|
1028
|
+
if (depth === 1) {
|
|
1029
|
+
parenthesizedTitle = false;
|
|
1030
|
+
phase = "after-destination";
|
|
1031
|
+
titleSeparator = false;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
if (phase === "after-destination") {
|
|
1037
|
+
if (/\s/u.test(char ?? "")) {
|
|
1038
|
+
titleSeparator = true;
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
1041
|
+
if (char === ")")
|
|
1042
|
+
return cursor;
|
|
1043
|
+
if (titleSeparator && (char === '"' || char === "'")) {
|
|
1044
|
+
quote = char;
|
|
1045
|
+
phase = "title";
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
1048
|
+
if (titleSeparator && char === "(") {
|
|
1049
|
+
depth += 1;
|
|
1050
|
+
parenthesizedTitle = true;
|
|
1051
|
+
phase = "title";
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
// Invalid trailing bytes are still consumed conservatively until the
|
|
1055
|
+
// balanced outer close. Quotes here are ordinary bytes, never titles.
|
|
1056
|
+
phase = "bare-destination";
|
|
1057
|
+
}
|
|
1058
|
+
if (phase !== "bare-destination")
|
|
1059
|
+
continue;
|
|
1060
|
+
if (char === "(") {
|
|
1061
|
+
depth += 1;
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
if (char === ")") {
|
|
1065
|
+
depth -= 1;
|
|
1066
|
+
if (depth === 0)
|
|
1067
|
+
return cursor;
|
|
980
1068
|
continue;
|
|
981
1069
|
}
|
|
982
|
-
if (
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1070
|
+
if (/\s/u.test(char ?? "") && depth === 1) {
|
|
1071
|
+
phase = "after-destination";
|
|
1072
|
+
titleSeparator = true;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
return undefined;
|
|
1076
|
+
}
|
|
1077
|
+
const MAX_MARKDOWN_LINK_NESTING = 32;
|
|
1078
|
+
/** Retain recursively projected link labels while dropping complete destinations. */
|
|
1079
|
+
function stripMarkdownLinkDestinations(text, nesting = 0) {
|
|
1080
|
+
let visible = "";
|
|
1081
|
+
let cursor = 0;
|
|
1082
|
+
while (cursor < text.length) {
|
|
1083
|
+
const isImage = text[cursor] === "!" && text[cursor + 1] === "[";
|
|
1084
|
+
const labelOpen = isImage ? cursor + 1 : cursor;
|
|
1085
|
+
if (text[labelOpen] !== "[") {
|
|
1086
|
+
visible += text[cursor];
|
|
1087
|
+
cursor += 1;
|
|
987
1088
|
continue;
|
|
988
1089
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1090
|
+
const labelClose = findBalancedMarkdownClose(text, labelOpen, "[", "]");
|
|
1091
|
+
const destinationOpen = labelClose === undefined ? undefined : labelClose + 1;
|
|
1092
|
+
if (destinationOpen === undefined || text[destinationOpen] !== "(") {
|
|
1093
|
+
visible += text[cursor];
|
|
1094
|
+
cursor += 1;
|
|
994
1095
|
continue;
|
|
995
1096
|
}
|
|
996
|
-
const
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
break; // paragraph complete
|
|
1002
|
-
continue; // still searching for the first prose line
|
|
1097
|
+
const destinationClose = findMarkdownDestinationClose(text, destinationOpen);
|
|
1098
|
+
if (destinationClose === undefined) {
|
|
1099
|
+
visible += text[cursor];
|
|
1100
|
+
cursor += 1;
|
|
1101
|
+
continue;
|
|
1003
1102
|
}
|
|
1004
|
-
|
|
1103
|
+
// Labels may themselves contain images/links. Recursively project them so
|
|
1104
|
+
// an inner destination cannot become visible when its outer link is
|
|
1105
|
+
// removed. At an adversarial nesting depth, omit the label rather than
|
|
1106
|
+
// leaking its unparsed bytes into the search projection.
|
|
1107
|
+
if (nesting < MAX_MARKDOWN_LINK_NESTING) {
|
|
1108
|
+
visible += stripMarkdownLinkDestinations(text.slice(labelOpen + 1, labelClose), nesting + 1);
|
|
1109
|
+
}
|
|
1110
|
+
cursor = destinationClose + 1;
|
|
1005
1111
|
}
|
|
1006
|
-
|
|
1112
|
+
return visible;
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Derive the one low-weight search projection for an AKM-native Markdown body.
|
|
1116
|
+
* Frontmatter is removed by the caller. This projection drops comments,
|
|
1117
|
+
* fenced code, raw link targets, and structural punctuation while retaining
|
|
1118
|
+
* prose, headings, link labels, and inline identifiers. It never receives
|
|
1119
|
+
* secret/env/session bytes; that policy is enforced at the adapter metadata
|
|
1120
|
+
* boundary below.
|
|
1121
|
+
*/
|
|
1122
|
+
export function projectMarkdownContent(body) {
|
|
1123
|
+
const lines = body.split(/\r?\n/);
|
|
1124
|
+
const innerBlock = findInnerFrontmatterBlock(lines);
|
|
1125
|
+
const start = innerBlock && isFrontmatterShaped(lines, innerBlock) ? innerBlock.close + 1 : 0;
|
|
1126
|
+
const projected = [];
|
|
1127
|
+
let fence;
|
|
1128
|
+
const htmlComment = { inComment: false };
|
|
1129
|
+
for (let i = start; i < lines.length; i += 1) {
|
|
1130
|
+
const rawLine = lines[i];
|
|
1131
|
+
if (fence) {
|
|
1132
|
+
if (isMarkdownFenceClosing(rawLine, fence))
|
|
1133
|
+
fence = undefined;
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
// A leading fence owns the whole line, including any info string that
|
|
1137
|
+
// resembles HTML. Comment state therefore cannot begin inside a fence.
|
|
1138
|
+
if (!htmlComment.inComment) {
|
|
1139
|
+
const openingFence = parseMarkdownFenceOpening(rawLine);
|
|
1140
|
+
if (openingFence) {
|
|
1141
|
+
fence = openingFence;
|
|
1142
|
+
continue;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
let trimmed = stripMarkdownHtmlComments(rawLine, htmlComment).trim();
|
|
1146
|
+
const openingFence = parseMarkdownFenceOpening(trimmed);
|
|
1147
|
+
if (openingFence) {
|
|
1148
|
+
fence = openingFence;
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
if (!trimmed || /^(-{3,}|\*{3,}|_{3,}|=+)$/.test(trimmed))
|
|
1152
|
+
continue;
|
|
1153
|
+
if (/^\s*\[[^\]]+\]:\s*\S+/.test(trimmed))
|
|
1154
|
+
continue;
|
|
1155
|
+
if (/^<[^>]+>$/.test(trimmed))
|
|
1156
|
+
continue;
|
|
1157
|
+
// Preserve human-facing labels and inline identifiers, never destinations.
|
|
1158
|
+
trimmed = stripMarkdownLinkDestinations(trimmed);
|
|
1159
|
+
trimmed = trimmed.replace(/`{1,2}([^`]+)`{1,2}/g, "$1");
|
|
1160
|
+
trimmed = trimmed.replace(/^#{1,6}\s+/, "");
|
|
1161
|
+
trimmed = trimmed.replace(/^(?:>|[-+*]|\d+[.)])\s+/, "");
|
|
1162
|
+
trimmed = trimmed.replace(/<[^>]+>/g, " ");
|
|
1163
|
+
trimmed = trimmed.replace(/[|]+/g, " ");
|
|
1164
|
+
trimmed = trimmed.replace(/\s+/g, " ").trim();
|
|
1165
|
+
if (trimmed)
|
|
1166
|
+
projected.push(trimmed);
|
|
1167
|
+
}
|
|
1168
|
+
const text = projected.join(" ").replace(/\s+/g, " ").trim();
|
|
1007
1169
|
if (!text)
|
|
1008
1170
|
return undefined;
|
|
1009
|
-
|
|
1010
|
-
return text;
|
|
1011
|
-
// Cap: prefer a word-boundary cut (never mid-token), but only when it keeps
|
|
1012
|
-
// a substantial prefix; append a one-char ellipsis inside the budget.
|
|
1013
|
-
const slice = text.slice(0, BODY_OPENING_MAX_CHARS - 1);
|
|
1014
|
-
const lastBoundary = Math.max(slice.lastIndexOf(" "), slice.lastIndexOf("\n"), slice.lastIndexOf("\t"));
|
|
1015
|
-
let cut = lastBoundary >= BODY_OPENING_MIN_RETAINED_CHARS ? slice.slice(0, lastBoundary) : slice;
|
|
1016
|
-
// slice() counts UTF-16 code units, so the no-boundary fallback can end on
|
|
1017
|
-
// the high half of a surrogate pair — a lone surrogate that corrupts the
|
|
1018
|
-
// FTS/embedding text. Drop it rather than emit invalid UTF-16.
|
|
1019
|
-
const lastCode = cut.charCodeAt(cut.length - 1);
|
|
1020
|
-
if (lastCode >= 0xd800 && lastCode <= 0xdbff)
|
|
1021
|
-
cut = cut.slice(0, -1);
|
|
1022
|
-
return `${cut.trimEnd()}…`;
|
|
1171
|
+
return truncateUnicodeSafe(text, MARKDOWN_CONTENT_MAX_CHARS);
|
|
1023
1172
|
}
|
|
1024
1173
|
// ── Metadata Generation ─────────────────────────────────────────────────────
|
|
1025
1174
|
/**
|
|
@@ -1060,16 +1209,12 @@ export function applyPreContributorFields(entry, file, ctx, pkgMeta) {
|
|
|
1060
1209
|
applyWikiFrontmatter(entry, parsed.data);
|
|
1061
1210
|
// D2 (#730): reread the namespaced `provenance:` block promoteProposal stamps.
|
|
1062
1211
|
applyProvenanceFrontmatter(entry, parsed.data);
|
|
1063
|
-
//
|
|
1064
|
-
//
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
if (isBodyOpeningIndexingEnabled() && !hasSessionMemoryMarker(parsed.data, parsed.content)) {
|
|
1070
|
-
const bodyOpening = extractBodyOpening(parsed.content);
|
|
1071
|
-
if (bodyOpening)
|
|
1072
|
-
entry.bodyOpening = bodyOpening;
|
|
1212
|
+
// Native Markdown has one bounded low-weight body projection. Sensitive
|
|
1213
|
+
// types and raw session/checkpoint material never cross this boundary.
|
|
1214
|
+
if (entry.type !== "env" && entry.type !== "session" && !hasSessionMemoryMarker(parsed.data, parsed.content)) {
|
|
1215
|
+
const contentProjection = projectMarkdownContent(parsed.content);
|
|
1216
|
+
if (contentProjection)
|
|
1217
|
+
entry.content = contentProjection;
|
|
1073
1218
|
}
|
|
1074
1219
|
// Extract parameters from template placeholders ($1, $ARGUMENTS, {{named}})
|
|
1075
1220
|
if (entry.type === "command") {
|
|
@@ -6,13 +6,11 @@
|
|
|
6
6
|
* `indexDocumentFromEntry` (`adapters/akm-adapter.ts`). akm 0.9.0 Chunk 5,
|
|
7
7
|
* milestone F4a (M2 groundwork).
|
|
8
8
|
*
|
|
9
|
-
* The
|
|
10
|
-
* dispatched adapter's `recognize`)
|
|
11
|
-
* `
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* it. So the persist path must reconstruct the exact `IndexDocument` the old
|
|
15
|
-
* pipeline stored FROM the `IndexDocument` the new pipeline produces.
|
|
9
|
+
* The indexer drains a single `IndexDocument` stream (`drainDirDocuments` × the
|
|
10
|
+
* dispatched adapter's `recognize`) and persists its canonical projection in
|
|
11
|
+
* `entries.document_json`. Every reader (`rowToIndexedEntry` →
|
|
12
|
+
* `DbIndexedEntry.entry`) consumes that same projection, and parity tests pin
|
|
13
|
+
* the boundary.
|
|
16
14
|
*
|
|
17
15
|
* That reconstruction is lossless by construction: the akm adapter's `recognize`
|
|
18
16
|
* assembles a full `IndexDocument` (P1/P2 → fold → P4) and then maps it onto the
|
|
@@ -20,8 +18,8 @@
|
|
|
20
18
|
* onto named members, every other search-surface/provenance field onto
|
|
21
19
|
* `documentJson` (the `DOCUMENT_JSON_CARRIED_FIELDS` set). This function reverses
|
|
22
20
|
* that mapping exactly, so `indexDocumentToStashEntry(recognize(file))` deep-
|
|
23
|
-
* equals the
|
|
24
|
-
* `tests/integration/shadow-scan-parity.test.ts` gate pins
|
|
21
|
+
* equals the current persisted document for the same file (the
|
|
22
|
+
* `tests/integration/shadow-scan-parity.test.ts` gate pins this boundary).
|
|
25
23
|
*
|
|
26
24
|
* Two deliberate non-round-trip fields:
|
|
27
25
|
* - `filename` — dropped by `indexDocumentFromEntry` (no IndexDocument home),
|
|
@@ -123,7 +121,6 @@ export function indexDocumentToStashEntry(doc) {
|
|
|
123
121
|
entry.toc = dj.toc;
|
|
124
122
|
if (dj.parameters !== undefined)
|
|
125
123
|
entry.parameters = dj.parameters;
|
|
126
|
-
assignString(entry, "bodyOpening", dj.bodyOpening);
|
|
127
124
|
if (dj.source !== undefined)
|
|
128
125
|
entry.source = dj.source;
|
|
129
126
|
assignString(entry, "category", dj.category);
|
|
@@ -14,26 +14,23 @@
|
|
|
14
14
|
* - **Broken-workflow drop (item 3).** The live path dropped a broken workflow
|
|
15
15
|
* via the renderer contributor's throw → metadata-pass skip-with-warning; the
|
|
16
16
|
* `akm` adapter's synchronous `foldRecognizedMetadata` SWALLOWS the parse
|
|
17
|
-
* error, so a broken workflow would otherwise silently index. We
|
|
18
|
-
*
|
|
17
|
+
* error, so a broken workflow would otherwise silently index. We run the
|
|
18
|
+
* shared source-IR compiler on drained workflow docs and DROP
|
|
19
19
|
* the entry with the same `Skipped workflow …` warning
|
|
20
20
|
* ({@link buildMetadataSkipWarning}), so the workflow-skip summary counts it.
|
|
21
|
-
* - **Workflow-document side-table (workflow-md only).** The valid parsed
|
|
22
|
-
* `WorkflowDocument` is handed to the persist layer through the same
|
|
23
|
-
* `document-cache` side channel the live renderer contributor used, keyed by
|
|
24
|
-
* the reconstructed entry — so `takeWorkflowDocument(entry)` in the persist
|
|
25
|
-
* loop writes the `workflow_documents` row exactly as before.
|
|
26
21
|
*
|
|
27
22
|
* `doc.hash` (= sha256 of the file content) is surfaced per recognized file so
|
|
28
23
|
* the persist layer can populate the `content_hash` column (item 2). It is keyed
|
|
29
24
|
* by the file's absolute path rather than by the entry object.
|
|
30
25
|
*
|
|
31
|
-
* Pure of DB/global state
|
|
32
|
-
*
|
|
26
|
+
* Pure of DB/global state; a new leaf (nothing imports it back), so it joins no
|
|
27
|
+
* import cycle.
|
|
33
28
|
*/
|
|
29
|
+
import path from "node:path";
|
|
34
30
|
import { akmAdapter } from "../../core/adapter/adapters/akm-adapter.js";
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
31
|
+
import { canonicalizeWorkflowName } from "../../core/recognition-util.js";
|
|
32
|
+
import { resolveUniqueWorkflowSource, WorkflowSourceRejectionError, workflowNameForSourcePath, } from "../../workflows/source-files.js";
|
|
33
|
+
import { compileWorkflowSource } from "../../workflows/source-ir/compile.js";
|
|
37
34
|
import { buildMetadataSkipWarning } from "../passes/metadata.js";
|
|
38
35
|
import { buildFileContext } from "../walk/file-context.js";
|
|
39
36
|
import { indexDocumentToStashEntry } from "./doc-to-entry.js";
|
|
@@ -53,7 +50,34 @@ export function drainDirDocuments(adapter, component, fileContexts) {
|
|
|
53
50
|
const warnings = [];
|
|
54
51
|
const hashByFile = new Map();
|
|
55
52
|
const conceptIdByFile = new Map();
|
|
53
|
+
const rejectedPaths = new Set();
|
|
54
|
+
const rejectedConceptIds = new Set();
|
|
55
|
+
const workflowLookups = new Map();
|
|
56
56
|
for (const file of fileContexts) {
|
|
57
|
+
const workflowName = workflowNameForSourcePath(component.root, adapter.id, file.absPath);
|
|
58
|
+
if (workflowName !== undefined) {
|
|
59
|
+
const canonicalName = canonicalizeWorkflowName(workflowName);
|
|
60
|
+
if (!workflowLookups.has(canonicalName))
|
|
61
|
+
workflowLookups.set(canonicalName, workflowName);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const [canonicalName, workflowName] of [...workflowLookups].sort(([left], [right]) => comparePaths(left, right))) {
|
|
65
|
+
try {
|
|
66
|
+
resolveUniqueWorkflowSource(component.root, adapter.id, workflowName);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (!(error instanceof WorkflowSourceRejectionError))
|
|
70
|
+
throw error;
|
|
71
|
+
rejectedConceptIds.add(adapter.id === "akm" ? `workflows/${canonicalName}` : canonicalName);
|
|
72
|
+
for (const relativePath of error.sourcePaths) {
|
|
73
|
+
rejectedPaths.add(path.join(component.root, relativePath));
|
|
74
|
+
}
|
|
75
|
+
warnings.push(error.message);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (const file of fileContexts) {
|
|
79
|
+
if (rejectedPaths.has(file.absPath))
|
|
80
|
+
continue;
|
|
57
81
|
const doc = adapter.recognize(component, file);
|
|
58
82
|
if (doc === null)
|
|
59
83
|
continue;
|
|
@@ -62,9 +86,9 @@ export function drainDirDocuments(adapter, component, fileContexts) {
|
|
|
62
86
|
continue;
|
|
63
87
|
}
|
|
64
88
|
const entry = indexDocumentToStashEntry(doc);
|
|
65
|
-
// Workflow docs: drop-with-warning if broken; otherwise cache
|
|
66
|
-
//
|
|
67
|
-
const dropWarning = handleWorkflowDoc(doc,
|
|
89
|
+
// Workflow docs: drop-with-warning if broken; otherwise cache a lossless
|
|
90
|
+
// runtime projection when the current executor can represent the source.
|
|
91
|
+
const dropWarning = handleWorkflowDoc(doc, file, component.root);
|
|
68
92
|
if (dropWarning !== null) {
|
|
69
93
|
warnings.push(dropWarning);
|
|
70
94
|
continue;
|
|
@@ -74,7 +98,10 @@ export function drainDirDocuments(adapter, component, fileContexts) {
|
|
|
74
98
|
conceptIdByFile.set(file.absPath, doc.conceptId);
|
|
75
99
|
entries.push(entry);
|
|
76
100
|
}
|
|
77
|
-
return { entries, warnings, hashByFile, conceptIdByFile };
|
|
101
|
+
return { entries, warnings, hashByFile, conceptIdByFile, rejectedPaths, rejectedConceptIds };
|
|
102
|
+
}
|
|
103
|
+
function comparePaths(left, right) {
|
|
104
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
78
105
|
}
|
|
79
106
|
/**
|
|
80
107
|
* `(stashRoot, files) → StashFile` drop-in for the deleted flat-walk matcher
|
|
@@ -98,18 +125,19 @@ export function recognizeStashEntries(stashRoot, files) {
|
|
|
98
125
|
: { entries: drained.entries };
|
|
99
126
|
}
|
|
100
127
|
/**
|
|
101
|
-
* If `doc` is a workflow,
|
|
102
|
-
* warning when it is broken, or
|
|
103
|
-
*
|
|
104
|
-
* `null` immediately.
|
|
128
|
+
* If `doc` is a workflow, compile it through source IR: return a
|
|
129
|
+
* `Skipped workflow …` drop warning when it is broken, or return `null` when
|
|
130
|
+
* it compiles. Non-workflow docs return `null` immediately.
|
|
105
131
|
*/
|
|
106
|
-
function handleWorkflowDoc(doc,
|
|
107
|
-
if (
|
|
132
|
+
function handleWorkflowDoc(doc, file, workspaceRoot) {
|
|
133
|
+
if (doc.type !== "workflow" ||
|
|
134
|
+
(doc.adapterId !== "akm" && doc.adapterId !== "akm-workflow") ||
|
|
135
|
+
(docRenderer(doc) !== WORKFLOW_MD_RENDERER && doc.adapterId !== "akm-workflow")) {
|
|
108
136
|
return null;
|
|
109
|
-
|
|
137
|
+
}
|
|
138
|
+
const result = compileWorkflowSource(file.content(), { path: file.relPath, workspaceRoot });
|
|
110
139
|
if (!result.ok)
|
|
111
140
|
return workflowDropWarning(file, result.errors);
|
|
112
|
-
cacheWorkflowDocument(entry, result.document);
|
|
113
141
|
return null;
|
|
114
142
|
}
|
|
115
143
|
/** The winning renderer name the `akm` adapter carries on `documentJson.renderer`, or `undefined`. */
|