@deftai/directive-core 0.107.0 → 0.109.0
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/dist/architecture/sor-preflight.js +2 -0
- package/dist/cache/operations.js +1 -1
- package/dist/check/cached-orchestrator.js +28 -3
- package/dist/check/gate-lists.d.ts +14 -0
- package/dist/check/gate-lists.js +26 -3
- package/dist/check/named-cause.js +18 -3
- package/dist/check/session-completed-ac.d.ts +1 -1
- package/dist/check/session-completed-ac.js +1 -1
- package/dist/codebase/provider.js +7 -1
- package/dist/consumer-check-contract/evaluate.d.ts +47 -0
- package/dist/consumer-check-contract/evaluate.js +181 -15
- package/dist/delivery-attempt/evaluate.d.ts +9 -1
- package/dist/delivery-attempt/evaluate.js +69 -0
- package/dist/delivery-attempt/handoff.js +1 -1
- package/dist/delivery-attempt/index.d.ts +1 -1
- package/dist/delivery-attempt/index.js +1 -1
- package/dist/deposit/live-procedure-exclusions.d.ts +18 -0
- package/dist/deposit/live-procedure-exclusions.js +110 -0
- package/dist/deposit/live-procedure-targets.d.ts +45 -0
- package/dist/deposit/live-procedure-targets.js +274 -0
- package/dist/deposit/python-free.d.ts +6 -0
- package/dist/deposit/python-free.js +15 -0
- package/dist/deposit/rewrite-deposit-links.d.ts +42 -0
- package/dist/deposit/rewrite-deposit-links.js +148 -0
- package/dist/deposit/run-stage-content-pack.d.ts +2 -0
- package/dist/deposit/run-stage-content-pack.js +3 -0
- package/dist/deposit/stage-content-pack.d.ts +17 -0
- package/dist/deposit/stage-content-pack.js +91 -0
- package/dist/design-critique/citation-grammar.d.ts +60 -0
- package/dist/design-critique/citation-grammar.js +303 -0
- package/dist/design-critique/completed-arc-record.d.ts +62 -0
- package/dist/design-critique/completed-arc-record.js +277 -0
- package/dist/freshness/bind.d.ts +32 -3
- package/dist/freshness/bind.js +58 -12
- package/dist/hooks/classify/host-session-identity.d.ts +89 -0
- package/dist/hooks/classify/host-session-identity.js +467 -0
- package/dist/hooks/classify/index.d.ts +3 -2
- package/dist/hooks/classify/index.js +3 -2
- package/dist/hooks/classify/paths.d.ts +9 -0
- package/dist/hooks/classify/paths.js +39 -0
- package/dist/hooks/classify/stdin.d.ts +5 -0
- package/dist/hooks/classify/stdin.js +94 -1
- package/dist/hooks/dest-form.d.ts +20 -1
- package/dist/hooks/dest-form.js +158 -21
- package/dist/hooks/dispatcher.d.ts +58 -3
- package/dist/hooks/dispatcher.js +762 -73
- package/dist/hooks/fixtures/cases.d.ts +22 -2
- package/dist/hooks/fixtures/cases.js +228 -0
- package/dist/hooks/git-destructive-log.d.ts +32 -0
- package/dist/hooks/git-destructive-log.js +46 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/scope.js +3 -1
- package/dist/hooks/shell-write-targets.d.ts +10 -0
- package/dist/hooks/shell-write-targets.js +274 -0
- package/dist/hooks/tools.d.ts +20 -2
- package/dist/hooks/tools.js +30 -1
- package/dist/init-deposit/agent-hooks.d.ts +2 -1
- package/dist/init-deposit/agent-hooks.js +8 -1
- package/dist/init-deposit/gitignore.d.ts +7 -0
- package/dist/init-deposit/gitignore.js +24 -0
- package/dist/init-deposit/hook-runtime-travel.d.ts +77 -0
- package/dist/init-deposit/hook-runtime-travel.js +269 -0
- package/dist/init-deposit/init-deposit.js +3 -0
- package/dist/init-deposit/refresh.js +7 -0
- package/dist/init-deposit/runtime-writers.d.ts +14 -0
- package/dist/init-deposit/runtime-writers.js +33 -0
- package/dist/intake/clause-derivation.js +9 -3
- package/dist/intake/issue-ingest.d.ts +6 -1
- package/dist/intake/issue-ingest.js +83 -4
- package/dist/intake/platform-capabilities.d.ts +9 -2
- package/dist/intake/platform-capabilities.js +38 -9
- package/dist/intake/reconcile-issues.js +13 -13
- package/dist/lifecycle/brief-envelope.d.ts +25 -0
- package/dist/lifecycle/brief-envelope.js +42 -0
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.js +1 -0
- package/dist/literal-acceptance/evaluate.js +14 -5
- package/dist/literal-acceptance/index.d.ts +1 -1
- package/dist/literal-acceptance/index.js +1 -1
- package/dist/literal-acceptance/run.d.ts +2 -0
- package/dist/literal-acceptance/run.js +19 -1
- package/dist/orphan-active/candidate-scope.d.ts +53 -0
- package/dist/orphan-active/candidate-scope.js +157 -0
- package/dist/orphan-active/evaluate.d.ts +52 -0
- package/dist/orphan-active/evaluate.js +250 -128
- package/dist/orphan-active/index.d.ts +2 -0
- package/dist/orphan-active/index.js +2 -0
- package/dist/orphan-active/issue-state.d.ts +121 -0
- package/dist/orphan-active/issue-state.js +278 -0
- package/dist/platform/cursor-managed-runtime.d.ts +95 -0
- package/dist/platform/cursor-managed-runtime.js +241 -0
- package/dist/platform/index.d.ts +1 -0
- package/dist/platform/index.js +1 -0
- package/dist/platform/platform-capabilities.d.ts +23 -1
- package/dist/platform/platform-capabilities.js +55 -15
- package/dist/policy/ceremony-dial.js +5 -9
- package/dist/policy/host-hooks.js +4 -9
- package/dist/policy/merge-approval-head.js +7 -6
- package/dist/policy/org-force-on-migration.js +5 -10
- package/dist/policy/plan-extensions.d.ts +14 -2
- package/dist/policy/plan-extensions.js +24 -3
- package/dist/policy/product-signal.js +5 -10
- package/dist/policy/require-human-merge.js +5 -9
- package/dist/policy/resolve.js +77 -16
- package/dist/policy/value-feedback.js +9 -18
- package/dist/pr-closeout-attestable/evaluate.d.ts +80 -0
- package/dist/pr-closeout-attestable/evaluate.js +306 -0
- package/dist/pr-closeout-attestable/index.d.ts +2 -0
- package/dist/pr-closeout-attestable/index.js +2 -0
- package/dist/pr-closing-keywords/gh.js +32 -9
- package/dist/pr-closing-keywords/main.d.ts +1 -0
- package/dist/pr-closing-keywords/main.js +68 -3
- package/dist/pr-closing-keywords/types.d.ts +2 -0
- package/dist/pr-merge-readiness/gh.js +32 -9
- package/dist/pr-protected-issues/gh.d.ts +6 -2
- package/dist/pr-protected-issues/gh.js +42 -11
- package/dist/pr-wait-mergeable/cascade.d.ts +9 -1
- package/dist/pr-wait-mergeable/cascade.js +21 -1
- package/dist/pr-wait-mergeable/types.d.ts +2 -0
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
- package/dist/pr-wait-mergeable/wrappers.js +14 -3
- package/dist/preflight/evaluate.d.ts +15 -0
- package/dist/preflight/evaluate.js +29 -3
- package/dist/product-first-done-gate/acceptance-resolver.d.ts +14 -2
- package/dist/product-first-done-gate/acceptance-resolver.js +13 -2
- package/dist/product-first-done-gate/evaluate.js +23 -9
- package/dist/product-first-done-gate/types.js +2 -0
- package/dist/release/cli-drift-report.d.ts +68 -0
- package/dist/release/cli-drift-report.js +189 -0
- package/dist/release/consumer-hard-stops.d.ts +48 -0
- package/dist/release/consumer-hard-stops.js +140 -0
- package/dist/release/consumer-readiness-disclosure.d.ts +18 -0
- package/dist/release/consumer-readiness-disclosure.js +51 -0
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.js +3 -0
- package/dist/release/issue-state-fetch.d.ts +5 -3
- package/dist/release/issue-state-fetch.js +86 -21
- package/dist/release/native-steps.js +1 -0
- package/dist/release/pipeline.js +74 -0
- package/dist/release/run-consumer-readiness.d.ts +15 -0
- package/dist/release/run-consumer-readiness.js +24 -0
- package/dist/release/types.d.ts +20 -0
- package/dist/render/constants.d.ts +4 -0
- package/dist/render/constants.js +11 -8
- package/dist/render/export-spec.js +31 -5
- package/dist/render/index.d.ts +1 -1
- package/dist/render/index.js +1 -1
- package/dist/render/prd-render.d.ts +4 -1
- package/dist/render/prd-render.js +82 -23
- package/dist/render/project-render.js +14 -7
- package/dist/render/scope-outlook.d.ts +2 -0
- package/dist/render/scope-outlook.js +3 -0
- package/dist/render/spec-render.js +2 -2
- package/dist/resolution/package-manager.d.ts +33 -4
- package/dist/resolution/package-manager.js +210 -9
- package/dist/review-monitor/constants.d.ts +7 -0
- package/dist/review-monitor/constants.js +7 -0
- package/dist/review-monitor/github-lease.d.ts +98 -1
- package/dist/review-monitor/github-lease.js +186 -4
- package/dist/review-monitor/lease-comment.d.ts +36 -0
- package/dist/review-monitor/lease-comment.js +103 -2
- package/dist/run-summary/types.d.ts +2 -2
- package/dist/scm/build-command.d.ts +2 -2
- package/dist/scm/build-command.js +2 -2
- package/dist/scm/call-shape.d.ts +25 -0
- package/dist/scm/call-shape.js +59 -0
- package/dist/scm/call.d.ts +7 -4
- package/dist/scm/call.js +48 -9
- package/dist/scm/design-critique-chip.d.ts +1 -0
- package/dist/scm/design-critique-chip.js +22 -6
- package/dist/scm/gh-rest.d.ts +34 -13
- package/dist/scm/gh-rest.js +168 -15
- package/dist/scm/index.d.ts +2 -0
- package/dist/scm/index.js +2 -0
- package/dist/scm/readiness.d.ts +2 -0
- package/dist/scm/readiness.js +28 -4
- package/dist/scm/spawn-status.d.ts +33 -0
- package/dist/scm/spawn-status.js +53 -0
- package/dist/scope/acceptance-evidence.d.ts +1 -1
- package/dist/scope/project-definition-sync.js +14 -7
- package/dist/scope/transition.js +4 -14
- package/dist/session/ac-pass-banking.d.ts +2 -2
- package/dist/session/ac-pass-banking.js +2 -2
- package/dist/session/git.d.ts +24 -0
- package/dist/session/git.js +138 -2
- package/dist/session/host-session-owner.d.ts +53 -0
- package/dist/session/host-session-owner.js +89 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/session/occupancy.d.ts +259 -13
- package/dist/session/occupancy.js +877 -33
- package/dist/session/ritual-sentinel.d.ts +5 -0
- package/dist/session/ritual-sentinel.js +5 -0
- package/dist/session/session-ready.d.ts +5 -1
- package/dist/session/session-ready.js +98 -8
- package/dist/session/session-start.d.ts +5 -1
- package/dist/session/session-start.js +32 -17
- package/dist/session/toolchain-preflight.d.ts +13 -3
- package/dist/session/toolchain-preflight.js +93 -18
- package/dist/session/verify-ac-session-cache.d.ts +2 -2
- package/dist/session/verify-ac-session-cache.js +2 -2
- package/dist/session/verify-session-ritual.d.ts +11 -0
- package/dist/session/verify-session-ritual.js +60 -15
- package/dist/slice/constants.d.ts +1 -1
- package/dist/spec-authority/constants.d.ts +10 -2
- package/dist/spec-authority/constants.js +53 -8
- package/dist/spec-authority/resolver.d.ts +3 -0
- package/dist/spec-authority/resolver.js +55 -7
- package/dist/subprocess/max-buffer.d.ts +14 -0
- package/dist/subprocess/max-buffer.js +15 -0
- package/dist/swarm/complete-cohort.d.ts +2 -0
- package/dist/swarm/complete-cohort.js +21 -13
- package/dist/swarm/index.d.ts +1 -0
- package/dist/swarm/index.js +1 -0
- package/dist/swarm/launch-cli.js +53 -33
- package/dist/swarm/launch.d.ts +8 -0
- package/dist/swarm/launch.js +63 -43
- package/dist/swarm/pre-dispatch-cli.js +2 -1
- package/dist/swarm/pre-dispatch.js +10 -1
- package/dist/swarm/subagent-status-dir.d.ts +27 -0
- package/dist/swarm/subagent-status-dir.js +42 -0
- package/dist/swarm/worktrees.d.ts +17 -3
- package/dist/swarm/worktrees.js +48 -4
- package/dist/triage/evaluate/worktrees.js +153 -6
- package/dist/triage/scope/mutations-core.d.ts +2 -6
- package/dist/triage/scope/mutations-core.js +23 -55
- package/dist/triage/scope-drift/add-ignore.js +5 -39
- package/dist/triage/subscribe/index.d.ts +2 -6
- package/dist/triage/subscribe/index.js +15 -65
- package/dist/triage/welcome/writers.js +15 -74
- package/dist/umbrella-current-shape/index.d.ts +51 -3
- package/dist/umbrella-current-shape/index.js +106 -18
- package/dist/validate-content/deposit-required.d.ts +39 -0
- package/dist/validate-content/deposit-required.js +147 -0
- package/dist/validate-content/index.d.ts +1 -0
- package/dist/validate-content/index.js +1 -0
- package/dist/validate-content/validate-links.d.ts +2 -3
- package/dist/validate-content/validate-links.js +29 -2
- package/dist/vbrief-activate/activate.d.ts +7 -2
- package/dist/vbrief-activate/activate.js +26 -13
- package/dist/vbrief-build/index.d.ts +2 -1
- package/dist/vbrief-build/index.js +2 -1
- package/dist/vbrief-build/parity-scenarios.js +5 -5
- package/dist/vbrief-build/project-definition-io.d.ts +63 -3
- package/dist/vbrief-build/project-definition-io.js +357 -31
- package/dist/vbrief-build/project-definition-mutation.d.ts +32 -0
- package/dist/vbrief-build/project-definition-mutation.js +43 -0
- package/dist/vbrief-validate/conformance.d.ts +1 -0
- package/dist/vbrief-validate/conformance.js +85 -8
- package/dist/vbrief-validate/main.js +2 -0
- package/dist/vbrief-validate/precutover.js +5 -10
- package/dist/verify-ac/clauses.d.ts +62 -2
- package/dist/verify-ac/clauses.js +183 -111
- package/dist/verify-ac/evaluate.d.ts +9 -0
- package/dist/verify-ac/evaluate.js +32 -9
- package/dist/verify-ac/index.d.ts +1 -1
- package/dist/verify-ac/index.js +1 -1
- package/dist/verify-env/node-runtime.d.ts +8 -4
- package/dist/verify-env/node-runtime.js +9 -6
- package/dist/verify-env/toolchain-check.d.ts +35 -3
- package/dist/verify-env/toolchain-check.js +155 -36
- package/dist/verify-source/deposit-closure.d.ts +23 -0
- package/dist/verify-source/deposit-closure.js +162 -0
- package/dist/verify-source/index.d.ts +2 -0
- package/dist/verify-source/index.js +2 -0
- package/dist/verify-source/project-definition-mutation-boundary.d.ts +104 -0
- package/dist/verify-source/project-definition-mutation-boundary.js +326 -0
- package/dist/verify-source/semantic-single-source.d.ts +36 -0
- package/dist/verify-source/semantic-single-source.js +349 -0
- package/package.json +7 -3
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flatten-aware rewrite of relative markdown links from the source tree onto
|
|
3
|
+
* the C1 packed layout (#3937).
|
|
4
|
+
*
|
|
5
|
+
* Source checkout: content/X lives under content/; root main.md / SKILL.md stay
|
|
6
|
+
* at the repo root. Deposit: content/ children flatten to the payload root, and
|
|
7
|
+
* harness entries land beside them. A naive `./content/` strip cannot express
|
|
8
|
+
* the reverse `../../main.md` escape. This rewrite maps each relative target
|
|
9
|
+
* through the source path, then emits a deposit-relative href.
|
|
10
|
+
*/
|
|
11
|
+
import { posix } from "node:path";
|
|
12
|
+
import { extractLinkTargets, shouldSkipLinkTarget } from "../validate-content/link-parser.js";
|
|
13
|
+
export const DEPOSIT_LINK_REWRITE_VERSION = "1";
|
|
14
|
+
export const REWRITE_MARKER_PREFIX = "<!-- deft:deposit-link-rewrite";
|
|
15
|
+
const HARNESS_PACK_FILES = new Set(["main.md", "SKILL.md", "Taskfile.yml"]);
|
|
16
|
+
export function isUrlLikeTarget(target) {
|
|
17
|
+
return (target.startsWith("http://") ||
|
|
18
|
+
target.startsWith("https://") ||
|
|
19
|
+
target.startsWith("mailto:") ||
|
|
20
|
+
target.startsWith("#"));
|
|
21
|
+
}
|
|
22
|
+
/** Map a repo-relative source path to its packed-deposit relative path. */
|
|
23
|
+
export function mapSourceToPackRelative(sourceRel) {
|
|
24
|
+
let n = sourceRel.replace(/\\/g, "/");
|
|
25
|
+
if (n.length > 1 && n.endsWith("/"))
|
|
26
|
+
n = n.slice(0, -1);
|
|
27
|
+
if (n === "." || n === "")
|
|
28
|
+
return ".";
|
|
29
|
+
if (HARNESS_PACK_FILES.has(n) || n.startsWith("tasks/") || n.startsWith(".githooks/")) {
|
|
30
|
+
return n;
|
|
31
|
+
}
|
|
32
|
+
if (n === "content")
|
|
33
|
+
return ".";
|
|
34
|
+
if (n.startsWith("content/"))
|
|
35
|
+
return n.slice("content/".length);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/** Inverse: packed path -> source path the file was copied from. */
|
|
39
|
+
export function sourceRelForPackRel(packRel) {
|
|
40
|
+
const n = packRel.replace(/\\/g, "/");
|
|
41
|
+
if (HARNESS_PACK_FILES.has(n) || n.startsWith("tasks/") || n.startsWith(".githooks/")) {
|
|
42
|
+
return n;
|
|
43
|
+
}
|
|
44
|
+
return `content/${n}`;
|
|
45
|
+
}
|
|
46
|
+
export function resolveSourceTargetRel(sourceFileRel, rawPath) {
|
|
47
|
+
const sourceDir = posix.dirname(sourceFileRel.replace(/\\/g, "/"));
|
|
48
|
+
return posix.normalize(posix.join(sourceDir, rawPath)).replace(/\\/g, "/");
|
|
49
|
+
}
|
|
50
|
+
export function splitLinkHash(target) {
|
|
51
|
+
const hash = target.indexOf("#");
|
|
52
|
+
const query = target.indexOf("?");
|
|
53
|
+
let cut = -1;
|
|
54
|
+
if (hash === -1)
|
|
55
|
+
cut = query;
|
|
56
|
+
else if (query === -1)
|
|
57
|
+
cut = hash;
|
|
58
|
+
else
|
|
59
|
+
cut = Math.min(hash, query);
|
|
60
|
+
if (cut === -1)
|
|
61
|
+
return { path: target, suffix: "" };
|
|
62
|
+
return { path: target.slice(0, cut), suffix: target.slice(cut) };
|
|
63
|
+
}
|
|
64
|
+
export function rewriteRelativeLink(options) {
|
|
65
|
+
const { sourceFileRel, packFileRel, target } = options;
|
|
66
|
+
if (isUrlLikeTarget(target) || shouldSkipLinkTarget(target)) {
|
|
67
|
+
return { next: target, rewritten: false, packMapped: true };
|
|
68
|
+
}
|
|
69
|
+
const { path: rawPath, suffix } = splitLinkHash(target);
|
|
70
|
+
if (!rawPath)
|
|
71
|
+
return { next: target, rewritten: false, packMapped: true };
|
|
72
|
+
const trailingSlash = rawPath.endsWith("/");
|
|
73
|
+
const sourceTarget = resolveSourceTargetRel(sourceFileRel, rawPath);
|
|
74
|
+
if (sourceTarget === ".." || sourceTarget.startsWith("../")) {
|
|
75
|
+
return { next: target, rewritten: false, packMapped: false };
|
|
76
|
+
}
|
|
77
|
+
const packTarget = mapSourceToPackRelative(sourceTarget);
|
|
78
|
+
if (packTarget === null) {
|
|
79
|
+
return { next: target, rewritten: false, packMapped: false };
|
|
80
|
+
}
|
|
81
|
+
const packDir = posix.dirname(packFileRel.replace(/\\/g, "/"));
|
|
82
|
+
let rel = posix.relative(packDir, packTarget).replace(/\\/g, "/");
|
|
83
|
+
if (rel === "")
|
|
84
|
+
rel = ".";
|
|
85
|
+
if (trailingSlash && rel !== "." && !rel.endsWith("/"))
|
|
86
|
+
rel = `${rel}/`;
|
|
87
|
+
if (rawPath.startsWith("./") && !rel.startsWith(".") && !rel.startsWith("/")) {
|
|
88
|
+
rel = `./${rel}`;
|
|
89
|
+
}
|
|
90
|
+
const next = `${rel}${suffix}`;
|
|
91
|
+
return { next, rewritten: next !== target, packMapped: true };
|
|
92
|
+
}
|
|
93
|
+
export function rewriteMarker(sourceRel) {
|
|
94
|
+
return `${REWRITE_MARKER_PREFIX} v=${DEPOSIT_LINK_REWRITE_VERSION} source="${sourceRel.replace(/\\/g, "/")}" -->`;
|
|
95
|
+
}
|
|
96
|
+
export function rewriteMarkdownDepositLinks(options) {
|
|
97
|
+
const lines = options.content.split("\n");
|
|
98
|
+
let rewriteCount = 0;
|
|
99
|
+
const out = [];
|
|
100
|
+
for (const line of lines) {
|
|
101
|
+
const targets = extractLinkTargets(line);
|
|
102
|
+
if (targets.length === 0) {
|
|
103
|
+
out.push(line);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
let nextLine = line;
|
|
107
|
+
for (let i = targets.length - 1; i >= 0; i -= 1) {
|
|
108
|
+
const target = targets[i];
|
|
109
|
+
if (target === undefined)
|
|
110
|
+
continue;
|
|
111
|
+
const result = rewriteRelativeLink({
|
|
112
|
+
sourceFileRel: options.sourceFileRel,
|
|
113
|
+
packFileRel: options.packFileRel,
|
|
114
|
+
target,
|
|
115
|
+
});
|
|
116
|
+
if (!result.rewritten)
|
|
117
|
+
continue;
|
|
118
|
+
const needle = `](${target})`;
|
|
119
|
+
const at = nextLine.lastIndexOf(needle);
|
|
120
|
+
if (at === -1)
|
|
121
|
+
continue;
|
|
122
|
+
nextLine = `${nextLine.slice(0, at)}](${result.next})${nextLine.slice(at + needle.length)}`;
|
|
123
|
+
rewriteCount += 1;
|
|
124
|
+
}
|
|
125
|
+
out.push(nextLine);
|
|
126
|
+
}
|
|
127
|
+
if (rewriteCount === 0) {
|
|
128
|
+
return { content: options.content, rewriteCount: 0 };
|
|
129
|
+
}
|
|
130
|
+
let joined = out.join("\n");
|
|
131
|
+
if (!joined.includes(REWRITE_MARKER_PREFIX)) {
|
|
132
|
+
const marker = rewriteMarker(options.sourceFileRel);
|
|
133
|
+
if (joined.startsWith("<!--")) {
|
|
134
|
+
const end = joined.indexOf("-->");
|
|
135
|
+
if (end !== -1) {
|
|
136
|
+
joined = `${joined.slice(0, end + 3)}\n${marker}${joined.slice(end + 3)}`;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
joined = `${marker}\n${joined}`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
joined = `${marker}\n${joined}`;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { content: joined, rewriteCount };
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=rewrite-deposit-links.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stage the @deftai/directive-content pack the same way prepack does, then
|
|
3
|
+
* rewrite relative markdown links onto the flattened deposit layout (#3937).
|
|
4
|
+
*
|
|
5
|
+
* Must not write into the source tree: destDir is the pack root (packages/content
|
|
6
|
+
* during prepack, or an out-of-tree temp dir in tests).
|
|
7
|
+
*/
|
|
8
|
+
/** Copy content/ + harness + engine entries, then rewrite markdown links. */
|
|
9
|
+
export declare function stageContentPack(options: {
|
|
10
|
+
readonly repoRoot: string;
|
|
11
|
+
readonly destDir: string;
|
|
12
|
+
}): {
|
|
13
|
+
filesRewritten: number;
|
|
14
|
+
};
|
|
15
|
+
/** prepack cwd is packages/content. */
|
|
16
|
+
export declare function runPrepackFromContentPackage(pkgDir: string): void;
|
|
17
|
+
//# sourceMappingURL=stage-content-pack.d.ts.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stage the @deftai/directive-content pack the same way prepack does, then
|
|
3
|
+
* rewrite relative markdown links onto the flattened deposit layout (#3937).
|
|
4
|
+
*
|
|
5
|
+
* Must not write into the source tree: destDir is the pack root (packages/content
|
|
6
|
+
* during prepack, or an out-of-tree temp dir in tests).
|
|
7
|
+
*/
|
|
8
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, } from "node:fs";
|
|
9
|
+
import { join, relative } from "node:path";
|
|
10
|
+
import { containedWrite } from "../fs/contained-write.js";
|
|
11
|
+
import { rewriteMarkdownDepositLinks, sourceRelForPackRel } from "./rewrite-deposit-links.js";
|
|
12
|
+
const ENGINE_ENTRIES = [".githooks", "Taskfile.yml", "tasks"];
|
|
13
|
+
const HARNESS_ENTRIES = ["main.md", "SKILL.md"];
|
|
14
|
+
function keepCopy(src) {
|
|
15
|
+
return !src.includes("__pycache__") && !src.endsWith(".pyc") && !src.endsWith(".py");
|
|
16
|
+
}
|
|
17
|
+
function rewriteStagedMarkdown(destDir) {
|
|
18
|
+
let filesRewritten = 0;
|
|
19
|
+
const walk = (dir) => {
|
|
20
|
+
let entries;
|
|
21
|
+
try {
|
|
22
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
const full = join(dir, entry.name);
|
|
29
|
+
if (entry.isDirectory()) {
|
|
30
|
+
walk(full);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (!entry.isFile() || !entry.name.endsWith(".md"))
|
|
34
|
+
continue;
|
|
35
|
+
const packRel = relative(destDir, full).replace(/\\/g, "/");
|
|
36
|
+
const original = readFileSync(full, "utf8");
|
|
37
|
+
const { content, rewriteCount } = rewriteMarkdownDepositLinks({
|
|
38
|
+
content: original,
|
|
39
|
+
sourceFileRel: sourceRelForPackRel(packRel),
|
|
40
|
+
packFileRel: packRel,
|
|
41
|
+
});
|
|
42
|
+
if (rewriteCount > 0 && content !== original) {
|
|
43
|
+
containedWrite({
|
|
44
|
+
root: destDir,
|
|
45
|
+
target: packRel,
|
|
46
|
+
data: content,
|
|
47
|
+
mode: "replace",
|
|
48
|
+
});
|
|
49
|
+
filesRewritten += 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
walk(destDir);
|
|
54
|
+
return filesRewritten;
|
|
55
|
+
}
|
|
56
|
+
/** Copy content/ + harness + engine entries, then rewrite markdown links. */
|
|
57
|
+
export function stageContentPack(options) {
|
|
58
|
+
const { repoRoot, destDir } = options;
|
|
59
|
+
mkdirSync(destDir, { recursive: true });
|
|
60
|
+
const contentSrc = join(repoRoot, "content");
|
|
61
|
+
for (const name of readdirSync(contentSrc)) {
|
|
62
|
+
const from = join(contentSrc, name);
|
|
63
|
+
const to = join(destDir, name);
|
|
64
|
+
if (existsSync(to))
|
|
65
|
+
rmSync(to, { recursive: true, force: true });
|
|
66
|
+
cpSync(from, to, { recursive: true, filter: (src) => keepCopy(src) });
|
|
67
|
+
}
|
|
68
|
+
for (const name of ENGINE_ENTRIES) {
|
|
69
|
+
const from = join(repoRoot, name);
|
|
70
|
+
if (!existsSync(from))
|
|
71
|
+
continue;
|
|
72
|
+
const to = join(destDir, name);
|
|
73
|
+
if (existsSync(to))
|
|
74
|
+
rmSync(to, { recursive: true, force: true });
|
|
75
|
+
cpSync(from, to, { recursive: true, filter: (src) => keepCopy(src) });
|
|
76
|
+
}
|
|
77
|
+
for (const name of HARNESS_ENTRIES) {
|
|
78
|
+
const from = join(repoRoot, name);
|
|
79
|
+
if (!existsSync(from))
|
|
80
|
+
continue;
|
|
81
|
+
cpSync(from, join(destDir, name));
|
|
82
|
+
}
|
|
83
|
+
const filesRewritten = rewriteStagedMarkdown(destDir);
|
|
84
|
+
return { filesRewritten };
|
|
85
|
+
}
|
|
86
|
+
/** prepack cwd is packages/content. */
|
|
87
|
+
export function runPrepackFromContentPackage(pkgDir) {
|
|
88
|
+
const repoRoot = join(pkgDir, "..", "..");
|
|
89
|
+
stageContentPack({ repoRoot, destDir: pkgDir });
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=stage-content-pack.js.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Citation grammar for the design-critique completed-arc record (#3831).
|
|
3
|
+
*
|
|
4
|
+
* One parser answers both questions the ingest predicate asks: which comment
|
|
5
|
+
* ids a synthesis cites, and whether it claims a verified-claims table. The
|
|
6
|
+
* closed accepted set and the refused positions are published in
|
|
7
|
+
* `content/contracts/design-critique.md` `## Citation grammar`; this module is
|
|
8
|
+
* the only implementation of that grammar.
|
|
9
|
+
*
|
|
10
|
+
* Which code-span convention governs. The intake cross-ref scanners
|
|
11
|
+
* (`intake/markdown-scanners.ts`) delete code spans, so for them a backticked
|
|
12
|
+
* id is an example and never a reference. The citation scan takes the opposite
|
|
13
|
+
* polarity for the id token only: a balanced single-backtick id is an accepted
|
|
14
|
+
* citation, because arc comments are hand-written prose and the mandated lean
|
|
15
|
+
* heading is itself `**Lean:**`. A keyword inside a code span or a fence stays
|
|
16
|
+
* an example in both layers. Positions are classified, never stripped -- the
|
|
17
|
+
* strippers delete a span with its contents, which destroys the digits. The
|
|
18
|
+
* prior art for classify-not-strip is `classifyHit`
|
|
19
|
+
* (`pr-closing-keywords/detect.ts`).
|
|
20
|
+
*
|
|
21
|
+
* The fence scan is local rather than borrowed from that detector because the
|
|
22
|
+
* detector's opener regex is anchored at column zero, so a CommonMark fence
|
|
23
|
+
* indented one to three spaces reads as prose. Relaxing it there would move
|
|
24
|
+
* hits into the #737 false-positive class and change what that gate suppresses,
|
|
25
|
+
* which is a separate decision from this one.
|
|
26
|
+
*/
|
|
27
|
+
export type CitationKind = "lean" | "table" | "comment";
|
|
28
|
+
export type CitationRejectionClass = "code-fence" | "inline-code" | "blockquote" | "strikethrough" | "negation";
|
|
29
|
+
export type Citation = {
|
|
30
|
+
readonly id: number;
|
|
31
|
+
readonly kind: CitationKind;
|
|
32
|
+
};
|
|
33
|
+
export type RejectedCitation = {
|
|
34
|
+
readonly id: number;
|
|
35
|
+
readonly reason: CitationRejectionClass;
|
|
36
|
+
};
|
|
37
|
+
export type CitationScan = {
|
|
38
|
+
/** Accepted citations in document order, deduplicated on (id, kind). */
|
|
39
|
+
readonly citations: readonly Citation[];
|
|
40
|
+
/** Keyword-anchored or permalink occurrences refused by position. */
|
|
41
|
+
readonly rejected: readonly RejectedCitation[];
|
|
42
|
+
/** Every 8-or-more digit run in the body, for deterministic diagnostics. */
|
|
43
|
+
readonly idShapedRuns: readonly number[];
|
|
44
|
+
};
|
|
45
|
+
/** The published closed set, in the order the contract lists it. */
|
|
46
|
+
export declare const ACCEPTED_CITATION_FORMS: readonly string[];
|
|
47
|
+
/**
|
|
48
|
+
* Refused positions are exactly the five published classes. An indented code
|
|
49
|
+
* block and an HTML comment are deliberately not classified: a four-space
|
|
50
|
+
* indent is also ordinary list-continuation content, so refusing it would block
|
|
51
|
+
* valid records more often than it would catch example text.
|
|
52
|
+
*/
|
|
53
|
+
export declare function classifyPosition(body: string, offset: number): CitationRejectionClass | null;
|
|
54
|
+
/**
|
|
55
|
+
* Parse the closed citation grammar out of one comment body. Refused positions
|
|
56
|
+
* are reported rather than dropped so a block detail can echo the observation
|
|
57
|
+
* instead of guessing at a cause.
|
|
58
|
+
*/
|
|
59
|
+
export declare function scanCitations(body: string): CitationScan;
|
|
60
|
+
//# sourceMappingURL=citation-grammar.d.ts.map
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Citation grammar for the design-critique completed-arc record (#3831).
|
|
3
|
+
*
|
|
4
|
+
* One parser answers both questions the ingest predicate asks: which comment
|
|
5
|
+
* ids a synthesis cites, and whether it claims a verified-claims table. The
|
|
6
|
+
* closed accepted set and the refused positions are published in
|
|
7
|
+
* `content/contracts/design-critique.md` `## Citation grammar`; this module is
|
|
8
|
+
* the only implementation of that grammar.
|
|
9
|
+
*
|
|
10
|
+
* Which code-span convention governs. The intake cross-ref scanners
|
|
11
|
+
* (`intake/markdown-scanners.ts`) delete code spans, so for them a backticked
|
|
12
|
+
* id is an example and never a reference. The citation scan takes the opposite
|
|
13
|
+
* polarity for the id token only: a balanced single-backtick id is an accepted
|
|
14
|
+
* citation, because arc comments are hand-written prose and the mandated lean
|
|
15
|
+
* heading is itself `**Lean:**`. A keyword inside a code span or a fence stays
|
|
16
|
+
* an example in both layers. Positions are classified, never stripped -- the
|
|
17
|
+
* strippers delete a span with its contents, which destroys the digits. The
|
|
18
|
+
* prior art for classify-not-strip is `classifyHit`
|
|
19
|
+
* (`pr-closing-keywords/detect.ts`).
|
|
20
|
+
*
|
|
21
|
+
* The fence scan is local rather than borrowed from that detector because the
|
|
22
|
+
* detector's opener regex is anchored at column zero, so a CommonMark fence
|
|
23
|
+
* indented one to three spaces reads as prose. Relaxing it there would move
|
|
24
|
+
* hits into the #737 false-positive class and change what that gate suppresses,
|
|
25
|
+
* which is a separate decision from this one.
|
|
26
|
+
*/
|
|
27
|
+
/** The published closed set, in the order the contract lists it. */
|
|
28
|
+
export const ACCEPTED_CITATION_FORMS = [
|
|
29
|
+
"successor lean 12345678",
|
|
30
|
+
"successor lean: 12345678",
|
|
31
|
+
"successor lean `12345678`",
|
|
32
|
+
"**successor lean:** 12345678",
|
|
33
|
+
"#issuecomment-12345678",
|
|
34
|
+
"/issues/comments/12345678",
|
|
35
|
+
];
|
|
36
|
+
const EMPHASIS = "(?:\\*\\*|\\*)";
|
|
37
|
+
const KEYWORD = "(successor[ \\t]+lean|verified-claims[ \\t]+table|lean|comment)";
|
|
38
|
+
/**
|
|
39
|
+
* Colon and horizontal whitespace only. One of the three branches must consume
|
|
40
|
+
* a colon or a space, so `lean12345678` is not a citation.
|
|
41
|
+
*/
|
|
42
|
+
const SEPARATOR = `(?::${EMPHASIS}?[ \\t]*|${EMPHASIS}:?[ \\t]+|[ \\t]+)`;
|
|
43
|
+
/** Bare decimal, or a balanced single-backtick decimal. Nothing else. */
|
|
44
|
+
const ID_TOKEN = "(?:`(\\d{8,})`|(\\d{8,})\\b)";
|
|
45
|
+
const CITATION_RE = new RegExp(`${EMPHASIS}?\\b${KEYWORD}${SEPARATOR}${ID_TOKEN}` +
|
|
46
|
+
"|#issuecomment-(\\d{8,})" +
|
|
47
|
+
"|/issues/comments/(\\d{8,})", "gi");
|
|
48
|
+
const ID_RUN_RE = /\d{8,}/g;
|
|
49
|
+
const BLOCKQUOTE_RE = /^ {0,3}>/;
|
|
50
|
+
/** CommonMark fence delimiter: up to three leading spaces, the run, then the rest. */
|
|
51
|
+
const FENCE_LINE_RE = /^ {0,3}(`{3,}|~{3,})(.*)$/;
|
|
52
|
+
/** The last sentence break in a slice, and everything after it. */
|
|
53
|
+
const LAST_SENTENCE_RE = /[.!?;][^.!?;]*$/;
|
|
54
|
+
/** Everything up to the first sentence break. */
|
|
55
|
+
const FIRST_SENTENCE_RE = /^[^.!?;]*/;
|
|
56
|
+
/** The closed set of explicit negation markers the class recognises. */
|
|
57
|
+
const NEGATION_MARKER = "\\b(?:cannot|never|no longer" +
|
|
58
|
+
"|(?:do|does|did|is|are|was|were|has|have|had|will|would|shall|should|must|can|could|may|might)\\s+not" +
|
|
59
|
+
"|(?:do|does|did|is|are|was|were|has|have|had|wo|would|should|must|ca|could|sha)n['\\u2019]t)\\b";
|
|
60
|
+
/**
|
|
61
|
+
* Explicit negation of the citation itself, ending within three plain words of
|
|
62
|
+
* the keyword. A bare marker anywhere in the sentence prefix is not enough: it
|
|
63
|
+
* reads `without a doubt, successor lean N is accepted` and `not only successor
|
|
64
|
+
* lean N but also the table` as refusals, which blocks a valid record.
|
|
65
|
+
*/
|
|
66
|
+
const NEGATED_CITATION_RE = new RegExp(`${NEGATION_MARKER}(?:\\s+[A-Za-z][A-Za-z'\\u2019-]*){0,3}\\s*$`, "i");
|
|
67
|
+
/** The same markers, unanchored, for the complement clause after a citation. */
|
|
68
|
+
const NEGATION_ANYWHERE_RE = new RegExp(NEGATION_MARKER, "i");
|
|
69
|
+
/**
|
|
70
|
+
* A negated verb of denial affirms the citation rather than refusing it, so the
|
|
71
|
+
* negation belongs to the verb and not to the occurrence: `we cannot deny that
|
|
72
|
+
* successor lean N binds` cites N. The verb set is closed and published --
|
|
73
|
+
* negating `deny`, `doubt`, `dispute`, `contest`, or `question` affirms the
|
|
74
|
+
* complement clause those verbs introduce.
|
|
75
|
+
*
|
|
76
|
+
* A trailing `that` alone is not the signal, because `that` is also a
|
|
77
|
+
* determiner. `do not use that successor lean N` and the cleft `the record is
|
|
78
|
+
* not that successor lean N` are genuine refusals, and both keep the negation
|
|
79
|
+
* class. The anchor is the end of the sentence segment, so a second negation
|
|
80
|
+
* before the keyword still binds: `we do not doubt that this does not bind
|
|
81
|
+
* successor lean N` is refused.
|
|
82
|
+
*
|
|
83
|
+
* The carve-out only suspends a prefix rule that already fired; it never
|
|
84
|
+
* refuses on its own, so it cannot widen the refused set. It also does not
|
|
85
|
+
* accept the citation outright. The complement clause is what carries the
|
|
86
|
+
* claim, so a negation anywhere in the rest of that sentence keeps the refusal:
|
|
87
|
+
* `we do not doubt that successor lean N does not bind` says the lean does not
|
|
88
|
+
* bind. That scan runs to the sentence break rather than to a clause boundary,
|
|
89
|
+
* which refuses more than it strictly must inside this one carve-out -- the
|
|
90
|
+
* fail-closed direction, and every such body was already refused before it.
|
|
91
|
+
*/
|
|
92
|
+
const DENIAL_COMPLEMENT_RE = /\b(?:den(?:y|ies|ied|ying)|doubt(?:s|ed|ing)?|disput(?:e|es|ed|ing)|contest(?:s|ed|ing)?|question(?:s|ed|ing)?)\s+that\s*$/i;
|
|
93
|
+
function kindForKeyword(keyword) {
|
|
94
|
+
if (keyword === undefined)
|
|
95
|
+
return "comment";
|
|
96
|
+
const normalized = keyword.toLowerCase().replace(/[ \t]+/g, " ");
|
|
97
|
+
if (normalized.startsWith("verified-claims"))
|
|
98
|
+
return "table";
|
|
99
|
+
if (normalized === "comment")
|
|
100
|
+
return "comment";
|
|
101
|
+
return "lean";
|
|
102
|
+
}
|
|
103
|
+
function lineBounds(text, offset) {
|
|
104
|
+
const start = text.lastIndexOf("\n", offset - 1) + 1;
|
|
105
|
+
const found = text.indexOf("\n", offset);
|
|
106
|
+
return { start, end: found === -1 ? text.length : found };
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* CommonMark inline code: a run of N backticks opens a span that only a run of
|
|
110
|
+
* exactly N backticks closes. Counting single backticks would read a
|
|
111
|
+
* double-backtick span as balanced and let documentation prose cite.
|
|
112
|
+
*
|
|
113
|
+
* `text` is the whole span from the enclosing block start, not one line: a code
|
|
114
|
+
* span may carry a newline, so a line-scoped count cannot see the opener.
|
|
115
|
+
*/
|
|
116
|
+
function isInsideInlineCode(text) {
|
|
117
|
+
let openRun = 0;
|
|
118
|
+
let i = 0;
|
|
119
|
+
while (i < text.length) {
|
|
120
|
+
if (text[i] !== "`") {
|
|
121
|
+
i += 1;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
let run = 0;
|
|
125
|
+
while (i + run < text.length && text[i + run] === "`")
|
|
126
|
+
run += 1;
|
|
127
|
+
if (openRun === 0) {
|
|
128
|
+
openRun = run;
|
|
129
|
+
}
|
|
130
|
+
else if (run === openRun) {
|
|
131
|
+
openRun = 0;
|
|
132
|
+
}
|
|
133
|
+
i += run;
|
|
134
|
+
}
|
|
135
|
+
return openRun !== 0;
|
|
136
|
+
}
|
|
137
|
+
function isStruckThrough(text) {
|
|
138
|
+
let runs = 0;
|
|
139
|
+
let i = 0;
|
|
140
|
+
while (i < text.length - 1) {
|
|
141
|
+
if (text[i] === "~" && text[i + 1] === "~") {
|
|
142
|
+
runs += 1;
|
|
143
|
+
i += 2;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
i += 1;
|
|
147
|
+
}
|
|
148
|
+
return runs % 2 === 1;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* A negation binds the sentence it sits in, not the whole line. Scoping to the
|
|
152
|
+
* text after the last sentence break keeps `this is not stale. Bound contract
|
|
153
|
+
* is successor lean N` an affirmative citation.
|
|
154
|
+
*/
|
|
155
|
+
function isNegated(line, column) {
|
|
156
|
+
const before = line.slice(0, column);
|
|
157
|
+
const tail = LAST_SENTENCE_RE.exec(before);
|
|
158
|
+
const segment = tail === null ? before : before.slice(tail.index + 1);
|
|
159
|
+
if (!NEGATED_CITATION_RE.test(segment))
|
|
160
|
+
return false;
|
|
161
|
+
if (!DENIAL_COMPLEMENT_RE.test(segment))
|
|
162
|
+
return true;
|
|
163
|
+
const after = line.slice(column);
|
|
164
|
+
return NEGATION_ANYWHERE_RE.test(FIRST_SENTENCE_RE.exec(after)?.[0] ?? after);
|
|
165
|
+
}
|
|
166
|
+
function fenceDelimiter(line) {
|
|
167
|
+
const match = FENCE_LINE_RE.exec(line);
|
|
168
|
+
if (match === null)
|
|
169
|
+
return null;
|
|
170
|
+
const run = match[1] ?? "";
|
|
171
|
+
const info = match[2] ?? "";
|
|
172
|
+
const char = run.startsWith("~") ? "~" : "`";
|
|
173
|
+
// A backtick opener's info string may not contain a backtick.
|
|
174
|
+
if (char === "`" && info.includes("`"))
|
|
175
|
+
return null;
|
|
176
|
+
return { char, len: run.length, info };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Walk the lines up to `offset` once and record both block facts the classifier
|
|
180
|
+
* needs. The fence stack follows CommonMark: opener and closer share the
|
|
181
|
+
* character, the closer is at least as long, and a closer carries no info
|
|
182
|
+
* string, so `` ```ts more text `` inside an open fence is content and not a
|
|
183
|
+
* closer. Inline scanning restarts after a blank line or a fence delimiter
|
|
184
|
+
* because neither a code span nor a strikethrough run crosses one. A blockquote
|
|
185
|
+
* marker carries forward to the blank line that ends the quote, so an unmarked
|
|
186
|
+
* lazy-continuation line is still quoted text.
|
|
187
|
+
*
|
|
188
|
+
* A fence delimiter also ends the quote block, and a line inside an open fence
|
|
189
|
+
* never opens one: a `>` in a fenced example is example text, so the marker must
|
|
190
|
+
* not survive the closing fence and refuse the citation that follows it. Lazy
|
|
191
|
+
* continuation is a paragraph rule, and a fence line is not paragraph text. A
|
|
192
|
+
* fence marked by `>` on every line is not a fence here -- `fenceDelimiter`
|
|
193
|
+
* requires the run at the line start -- so the line-level blockquote test still
|
|
194
|
+
* refuses a citation inside a blockquoted fence.
|
|
195
|
+
*/
|
|
196
|
+
function blockPosition(body, offset) {
|
|
197
|
+
let open = null;
|
|
198
|
+
let inlineStart = 0;
|
|
199
|
+
let quotedBlock = false;
|
|
200
|
+
let lineStart = 0;
|
|
201
|
+
while (lineStart < offset) {
|
|
202
|
+
let lineEnd = body.indexOf("\n", lineStart);
|
|
203
|
+
const partial = lineEnd === -1 || lineEnd > offset;
|
|
204
|
+
if (partial) {
|
|
205
|
+
lineEnd = Math.min(body.length, offset);
|
|
206
|
+
}
|
|
207
|
+
const line = body.slice(lineStart, lineEnd);
|
|
208
|
+
const fence = fenceDelimiter(line);
|
|
209
|
+
if (fence !== null) {
|
|
210
|
+
if (open === null) {
|
|
211
|
+
open = fence;
|
|
212
|
+
}
|
|
213
|
+
else if (fence.char === open.char &&
|
|
214
|
+
fence.len >= open.len &&
|
|
215
|
+
fence.info.trim().length === 0) {
|
|
216
|
+
open = null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (!partial) {
|
|
220
|
+
const blank = line.trim().length === 0;
|
|
221
|
+
if (blank || fence !== null) {
|
|
222
|
+
inlineStart = lineEnd + 1;
|
|
223
|
+
quotedBlock = false;
|
|
224
|
+
}
|
|
225
|
+
else if (open === null && BLOCKQUOTE_RE.test(line)) {
|
|
226
|
+
quotedBlock = true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (lineEnd >= offset) {
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
lineStart = lineEnd + 1;
|
|
233
|
+
}
|
|
234
|
+
return { insideFence: open !== null, inlineStart, quotedBlock };
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Refused positions are exactly the five published classes. An indented code
|
|
238
|
+
* block and an HTML comment are deliberately not classified: a four-space
|
|
239
|
+
* indent is also ordinary list-continuation content, so refusing it would block
|
|
240
|
+
* valid records more often than it would catch example text.
|
|
241
|
+
*/
|
|
242
|
+
export function classifyPosition(body, offset) {
|
|
243
|
+
const block = blockPosition(body, offset);
|
|
244
|
+
if (block.insideFence)
|
|
245
|
+
return "code-fence";
|
|
246
|
+
const { start, end } = lineBounds(body, offset);
|
|
247
|
+
const line = body.slice(start, end);
|
|
248
|
+
if (BLOCKQUOTE_RE.test(line) || block.quotedBlock)
|
|
249
|
+
return "blockquote";
|
|
250
|
+
const preceding = body.slice(block.inlineStart, offset);
|
|
251
|
+
if (isInsideInlineCode(preceding))
|
|
252
|
+
return "inline-code";
|
|
253
|
+
if (isStruckThrough(preceding))
|
|
254
|
+
return "strikethrough";
|
|
255
|
+
if (isNegated(line, offset - start))
|
|
256
|
+
return "negation";
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
function idShapedRuns(body) {
|
|
260
|
+
const runs = [];
|
|
261
|
+
const seen = new Set();
|
|
262
|
+
const re = new RegExp(ID_RUN_RE.source, ID_RUN_RE.flags);
|
|
263
|
+
for (const match of body.matchAll(re)) {
|
|
264
|
+
const id = Number(match[0]);
|
|
265
|
+
if (!Number.isSafeInteger(id) || id <= 0 || seen.has(id))
|
|
266
|
+
continue;
|
|
267
|
+
seen.add(id);
|
|
268
|
+
runs.push(id);
|
|
269
|
+
}
|
|
270
|
+
return runs;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Parse the closed citation grammar out of one comment body. Refused positions
|
|
274
|
+
* are reported rather than dropped so a block detail can echo the observation
|
|
275
|
+
* instead of guessing at a cause.
|
|
276
|
+
*/
|
|
277
|
+
export function scanCitations(body) {
|
|
278
|
+
const citations = [];
|
|
279
|
+
const rejected = [];
|
|
280
|
+
const seen = new Set();
|
|
281
|
+
const re = new RegExp(CITATION_RE.source, CITATION_RE.flags);
|
|
282
|
+
for (const match of body.matchAll(re)) {
|
|
283
|
+
const raw = match[2] ?? match[3] ?? match[4] ?? match[5];
|
|
284
|
+
if (raw === undefined)
|
|
285
|
+
continue;
|
|
286
|
+
const id = Number(raw);
|
|
287
|
+
if (!Number.isSafeInteger(id) || id <= 0)
|
|
288
|
+
continue;
|
|
289
|
+
const reason = classifyPosition(body, match.index ?? 0);
|
|
290
|
+
if (reason !== null) {
|
|
291
|
+
rejected.push({ id, reason });
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const kind = kindForKeyword(match[1]);
|
|
295
|
+
const key = `${id}|${kind}`;
|
|
296
|
+
if (seen.has(key))
|
|
297
|
+
continue;
|
|
298
|
+
seen.add(key);
|
|
299
|
+
citations.push({ id, kind });
|
|
300
|
+
}
|
|
301
|
+
return { citations, rejected, idShapedRuns: idShapedRuns(body) };
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=citation-grammar.js.map
|