@c4a/context-cli 0.7.14-beta.1 → 0.7.15
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/cli.js +1082 -1242
- package/indexers/contracts/profile-contract.json +245 -245
- package/indexers/release-manifest.json +1 -1
- package/package.json +12 -12
- package/parserEntryWorker.js +8 -0
- package/plugins/VERSION +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/commands/context.md +11 -0
- package/plugins/codex/.codex-plugin/plugin.json +2 -2
- package/plugins/codex/skills/context/SKILL.md +11 -0
- package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor/commands/c4a-context.md +11 -0
- package/plugins/skills/context/SKILL.md +11 -0
- package/providers/context/manifest.json +23 -23
- package/providers/context/provider.yaml +1 -1
- package/providers/context/resources/dialogue/knowledge-review.md +14 -6
- package/providers/context/resources/manuals/guides/knowledge-updates.md +50 -2
- package/providers/context/resources/manuals/guides/lark-resources.md +38 -0
- package/providers/context/resources/procedures/document-capture.md +7 -0
- package/providers/context/resources/procedures/knowledge-review.md +26 -5
- package/providers/context/resources/procedures/knowledge-updates.md +50 -2
- package/providers/context/resources/procedures/production-requirements.md +23 -0
- package/providers/context/resources/procedures/runtime-event-delivery.md +7 -1
- package/providers/context/resources/procedures/work-start-report.md +83 -0
- package/providers/context/resources/templates/work-start-report.md +7 -1
- package/providers/context/skills/work-production-stage/SKILL.md +3 -1
package/cli.js
CHANGED
|
@@ -41982,6 +41982,12 @@ var getSourceType = (sourceDefinition) => {
|
|
|
41982
41982
|
}), captureLark = (definition) => {
|
|
41983
41983
|
const sourceDefinition = bindSourceType(definition.source, "lark", "captureLark source");
|
|
41984
41984
|
const sourceId = getSourceName(sourceDefinition);
|
|
41985
|
+
for (const key of ["images", "gifs"]) {
|
|
41986
|
+
const value = definition.resources?.[key];
|
|
41987
|
+
if (value !== undefined && value !== "bundle" && value !== "reference-only") {
|
|
41988
|
+
throw new TypeError(`captureLark resources.${key} must be bundle or reference-only`);
|
|
41989
|
+
}
|
|
41990
|
+
}
|
|
41985
41991
|
const maxBytesPerResource = definition.resources?.maxBytesPerResource ?? 20 * 1024 * 1024;
|
|
41986
41992
|
const maxTotalBytes = definition.resources?.maxTotalBytes ?? 200 * 1024 * 1024;
|
|
41987
41993
|
if (!Number.isSafeInteger(maxBytesPerResource) || maxBytesPerResource < 1) {
|
|
@@ -42000,6 +42006,8 @@ var getSourceType = (sourceDefinition) => {
|
|
|
42000
42006
|
writes: [sourceSnapshotResource(sourceDefinition, "lark")],
|
|
42001
42007
|
source: sourceDefinition,
|
|
42002
42008
|
resources: {
|
|
42009
|
+
...definition.resources?.images === undefined ? {} : { images: definition.resources.images },
|
|
42010
|
+
...definition.resources?.gifs === undefined ? {} : { gifs: definition.resources.gifs },
|
|
42003
42011
|
videos: definition.resources?.videos ?? "reference-only",
|
|
42004
42012
|
maxBytesPerResource,
|
|
42005
42013
|
maxTotalBytes
|
|
@@ -64786,6 +64794,8 @@ Restore the authorized source and retry preparation.
|
|
|
64786
64794
|
...scopes.map((scope2) => `- ${scope2.scope}: ${join25(directory, productionSourceFile(scope2.scope))}`),
|
|
64787
64795
|
"",
|
|
64788
64796
|
"Use code skeletons and document outlines to identify the authorized capability families and document tasks, then selectively read full material to decide reader topics. Navigation is not a complete feature inventory. Keep unchecked scope pending; do not parse all code or maintain per-symbol disposition just to plan.",
|
|
64797
|
+
"Configured sources are the knowledge workspace coverage boundary, not a new investigation assignment on every request. First distinguish the user's current task, its actual source dependencies, and unrelated configured sources. Reuse approved content; a source-level pending entry alone does not prove missing knowledge or require new articles.",
|
|
64798
|
+
"Report source baseline/read failures separately from content gaps. For an unrelated configured source, explain that its availability check is unresolved outside this task; do not promise a new code investigation. If the Route still requires resolution, report that precise workflow limitation without deleting source configuration, clearing runtime state, or claiming the source was investigated.",
|
|
64789
64799
|
"Scale planning to the current request. For one or two documents or a clearly bounded module, decide which articles to add or revise and where they belong; do not redesign the whole knowledge base. Start with related existing topics, expand reading only when needed, and reuse applicable decisions. A large module may need several topics, but not investigation of unrelated modules.",
|
|
64790
64800
|
"Separate the whole requested outcome from the current writing batch. Use one batch unless actual dependencies or useful parallel work justify more; do not invent page counts or dependencies. A first useful delivery does not settle remaining authorized work.",
|
|
64791
64801
|
"Use question and brief to describe the reader task and useful depth: a checked file/symbol or document section with a concrete next step for navigation, or the behavior, conditions and steps needed for explanation. Reuse or revise existing articles without replacing valid detail with generic summaries; split distinct tasks, not sources or symbols.",
|
|
@@ -68458,7 +68468,7 @@ function runtimeEventPendingAgentHint(result) {
|
|
|
68458
68468
|
requires_network_access: true,
|
|
68459
68469
|
plan_command: "context logs plan --format json",
|
|
68460
68470
|
command: "context logs flush --format json",
|
|
68461
|
-
message: "Runtime logs are queued locally.
|
|
68471
|
+
message: "Runtime logs are queued locally. If delivery is already authorized and the destination is unchanged, run context logs flush --format json directly. Use the delivery plan when the destination or required host network permission is not yet established."
|
|
68462
68472
|
};
|
|
68463
68473
|
}
|
|
68464
68474
|
function queueContextRuntimeEvent(input) {
|
|
@@ -68950,6 +68960,10 @@ function renderAgents(projectName, language) {
|
|
|
68950
68960
|
"- Context 完成只证明知识工作流状态,不证明 Git 提交范围安全。保留任务开始前已有的工作树变更,只按明确路径暂存;不要用 `git add -A` 把无关修改、删除或未跟踪目录带入提交。",
|
|
68951
68961
|
"- 发布知识包前可按产品需要定制 `src/package-templates/kb/wikis/index.md`;不要编辑生成后的 `dist/` 页面。",
|
|
68952
68962
|
"",
|
|
68963
|
+
"## 导航与分类",
|
|
68964
|
+
"",
|
|
68965
|
+
"新增或修订知识前,读取现有知识地图、总览和相关栏目正文,理解并复用分类意图。按当前 Route 的 knowledge-updates 指引安排文章、命名与顺序。新增来源或产品不自动新增顶层目录;确需调整顶层名称、用途或结构时,在开工报告或现有计划展示前后结构与复用不足的理由,取得具体方案确认后应用。已有明确批准不重复询问,常规落位不新增人审。将长期栏目用途简要维护在本文件或现有组织说明中。",
|
|
68966
|
+
"",
|
|
68953
68967
|
"## 图表风格(可修改)",
|
|
68954
68968
|
"",
|
|
68955
68969
|
"- 默认简约:约 1px 细线;文字与线条使用随主题变化的默认色,不加彩色装饰。Mermaid 不硬编码调色板、背景或主题初始化;线宽由支持它的展示端设置,不因此阻塞写作。",
|
|
@@ -68992,6 +69006,10 @@ function renderAgents(projectName, language) {
|
|
|
68992
69006
|
"- Context completion proves knowledge-workflow state, not Git commit safety. Preserve worktree changes that existed before the task, stage only explicit paths, and never use `git add -A` to mix unrelated modifications, deletions, or untracked directories into the deliverable.",
|
|
68993
69007
|
"- Customize `src/package-templates/kb/wikis/index.md` for the product before publishing a knowledge package; do not edit generated `dist/` pages.",
|
|
68994
69008
|
"",
|
|
69009
|
+
"## Navigation and Classification",
|
|
69010
|
+
"",
|
|
69011
|
+
"Before adding or revising knowledge, read the current map, overview and relevant category articles to understand and reuse their intent. Follow the current Route's knowledge-updates guidance for placement, names and order. A new source or product does not automatically warrant a top-level category. Present top-level name, purpose or structure changes with a before/after tree and reuse rationale in the work-start report or current plan, and obtain approval of the concrete proposal before applying it. Reuse explicit prior approval; ordinary placements add no review gate. Keep lasting category intent concise in this file or the existing organization guide.",
|
|
69012
|
+
"",
|
|
68995
69013
|
"## Diagram style (user editable)",
|
|
68996
69014
|
"",
|
|
68997
69015
|
"- Keep diagrams minimal: approximately 1px lines, theme-default text and stroke colors, no colorful decoration. Avoid fixed Mermaid palettes, backgrounds and theme initialization. Configure line width in a capable viewer; unsupported styling never blocks authoring.",
|
|
@@ -70276,6 +70294,9 @@ function formatPackageBuildSummary(pkg) {
|
|
|
70276
70294
|
lines.push(` warning: ${warning.path} references ${warning.target}, which ${explanation}.`);
|
|
70277
70295
|
}
|
|
70278
70296
|
const optimization = pkg.resources.delivery.optimization;
|
|
70297
|
+
for (const warning of optimization?.warnings ?? []) {
|
|
70298
|
+
lines.push(` image replaced with placeholder: ${warning.path}: ${warning.reason}`);
|
|
70299
|
+
}
|
|
70279
70300
|
if (optimization?.state === "applied") {
|
|
70280
70301
|
lines.push(` asset optimization: ${optimization.processor}/${optimization.mode}, saved ${optimization.savedBytes} byte(s), largest ${optimization.largestOutputBytes}/${optimization.maxImageBytes} byte(s), total ${optimization.outputBytes}/${optimization.maxTotalImageBytes} byte(s)`);
|
|
70281
70302
|
} else if (pkg.resources.delivery.state === "git-raw") {
|
|
@@ -70758,21 +70779,23 @@ var siteMarkdownConfig, siteThemeScript, siteThemeCss = `
|
|
|
70758
70779
|
.VPNavBarSearch .DocSearch-Button-Container { display: flex; flex: 1; min-width: 0; align-items: center; }
|
|
70759
70780
|
.VPNavBarSearch .DocSearch-Button-Keys { margin-left: auto; }
|
|
70760
70781
|
.VPSidebar { border-top: 1px solid var(--vp-c-divider); border-right: 1px solid var(--vp-c-divider); scrollbar-width: thin; }
|
|
70761
|
-
.VPSidebar .group + .group { border: 0; padding-top:
|
|
70782
|
+
.VPSidebar .group, .VPSidebar .group + .group { border: 0; padding-top: 0; }
|
|
70762
70783
|
.VPSidebarItem .link { min-width: 0; overflow: hidden; }
|
|
70763
70784
|
.VPSidebarItem .text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px !important; line-height: 20px !important; font-weight: 450 !important; }
|
|
70764
|
-
.VPSidebarItem.level-0 { padding-bottom:
|
|
70785
|
+
.VPSidebarItem.level-0 { padding-bottom: 0 !important; }
|
|
70765
70786
|
.VPSidebarItem .indicator { display: none; }
|
|
70766
|
-
.VPSidebarItem .text { padding:
|
|
70767
|
-
.VPSidebarItem .item { min-height:
|
|
70787
|
+
.VPSidebarItem .text { padding: 0 !important; }
|
|
70788
|
+
.VPSidebarItem .item { height: 38px; min-height: 38px; align-items: center; padding: 0 16px 0 26px; border-radius: 0; }
|
|
70789
|
+
.VPSidebarItem.is-link:not(.is-active) > .item > .link > p.text { color: var(--vp-c-text-2); }
|
|
70790
|
+
.VPSidebarItem.is-link:not(.is-active) > .item > .link:hover > p.text { color: var(--vp-c-brand-1); }
|
|
70768
70791
|
.VPSidebarItem.is-active > .item { background: var(--vp-c-brand-soft); box-shadow: none; }
|
|
70769
70792
|
.VPSidebarItem.is-active > .item .text { color: var(--vp-c-brand-1) !important; font-weight: 550 !important; }
|
|
70770
70793
|
.VPSidebarItem .items { margin-left: 0; padding-left: 0 !important; border-left: 0 !important; }
|
|
70771
|
-
.VPSidebarItem.level-1 > .item { padding-left:
|
|
70772
|
-
.VPSidebarItem.level-2 > .item { padding-left:
|
|
70773
|
-
.VPSidebarItem.level-3 > .item { padding-left:
|
|
70774
|
-
.VPSidebarItem.level-4 > .item { padding-left:
|
|
70775
|
-
.VPSidebarItem.level-5 > .item { padding-left:
|
|
70794
|
+
.VPSidebarItem.level-1 > .item { padding-left: 40px; }
|
|
70795
|
+
.VPSidebarItem.level-2 > .item { padding-left: 54px; }
|
|
70796
|
+
.VPSidebarItem.level-3 > .item { padding-left: 68px; }
|
|
70797
|
+
.VPSidebarItem.level-4 > .item { padding-left: 82px; }
|
|
70798
|
+
.VPSidebarItem.level-5 > .item { padding-left: 96px; }
|
|
70776
70799
|
.VPSidebar .group { width: 100% !important; }
|
|
70777
70800
|
.VPSidebarItem .item:hover { background: var(--vp-c-default-soft); }
|
|
70778
70801
|
.VPDoc .container, .VPDoc > .container > .content, .VPDoc .content-container { max-width: none !important; min-width: 0 !important; }
|
|
@@ -71503,15 +71526,23 @@ function sidebar(entries2) {
|
|
|
71503
71526
|
return entries2.map((entry) => ({
|
|
71504
71527
|
text: entry.title,
|
|
71505
71528
|
...entry.href ? { link: entry.href } : {},
|
|
71506
|
-
|
|
71529
|
+
items: sidebar(entry.children),
|
|
71530
|
+
...entry.children.length ? { collapsed: true } : {}
|
|
71507
71531
|
}));
|
|
71508
71532
|
}
|
|
71509
71533
|
function siteSections(entries2) {
|
|
71534
|
+
const directoriesFirst = (siblings) => {
|
|
71535
|
+
const ordered = [
|
|
71536
|
+
...siblings.filter((entry) => entry.children.length > 0),
|
|
71537
|
+
...siblings.filter((entry) => entry.children.length === 0)
|
|
71538
|
+
];
|
|
71539
|
+
return ordered.map((entry) => ({ ...entry, children: directoriesFirst(entry.children) }));
|
|
71540
|
+
};
|
|
71510
71541
|
const pageLinks = (entry) => [
|
|
71511
71542
|
...entry.href ? [entry.href.split("#")[0]] : [],
|
|
71512
71543
|
...entry.children.flatMap(pageLinks)
|
|
71513
71544
|
];
|
|
71514
|
-
return entries2.map((entry) => ({
|
|
71545
|
+
return entries2.map((entry) => ({ ...entry, children: directoriesFirst(entry.children) })).map((entry) => ({
|
|
71515
71546
|
key: entry.key,
|
|
71516
71547
|
title: entry.title,
|
|
71517
71548
|
href: `/sections/${createHash16("sha256").update(entry.key).digest("hex").slice(0, 20)}.html`,
|
|
@@ -72168,7 +72199,11 @@ async function loadSharpProcessor() {
|
|
|
72168
72199
|
}
|
|
72169
72200
|
return {
|
|
72170
72201
|
async optimize(bytes, definition2) {
|
|
72171
|
-
|
|
72202
|
+
const image = sharp(bytes, { failOn: "error", animated: false });
|
|
72203
|
+
const metadata = await image.metadata();
|
|
72204
|
+
if ((metadata.pages ?? 1) > 1)
|
|
72205
|
+
return bytes;
|
|
72206
|
+
let pipeline2 = image.rotate();
|
|
72172
72207
|
if (definition2.maxDimension !== undefined) {
|
|
72173
72208
|
pipeline2 = pipeline2.resize({
|
|
72174
72209
|
width: definition2.maxDimension,
|
|
@@ -72192,6 +72227,8 @@ async function adaptiveVariants(input) {
|
|
|
72192
72227
|
let smallest = input.asset.bytes.byteLength;
|
|
72193
72228
|
for (const definition2 of input.definitions) {
|
|
72194
72229
|
const output = await input.processor.optimize(input.asset.bytes, definition2);
|
|
72230
|
+
if (output === input.asset.bytes)
|
|
72231
|
+
break;
|
|
72195
72232
|
if (!isWebp(output)) {
|
|
72196
72233
|
throw new ContextError(ExitCode.WorkspaceStateError, "package asset optimizer returned invalid WebP bytes", {
|
|
72197
72234
|
category: ErrorCategory.WorkspaceStateInvalid,
|
|
@@ -72207,17 +72244,6 @@ async function adaptiveVariants(input) {
|
|
|
72207
72244
|
}
|
|
72208
72245
|
return variants;
|
|
72209
72246
|
}
|
|
72210
|
-
function budgetError(input) {
|
|
72211
|
-
return new ContextError(ExitCode.WorkspaceStateError, "bundled images cannot meet the package size budget", {
|
|
72212
|
-
category: ErrorCategory.WorkspaceStateInvalid,
|
|
72213
|
-
reason_code: "package.assets.image-budget-exceeded",
|
|
72214
|
-
output_bytes: input.outputBytes,
|
|
72215
|
-
max_image_bytes: input.maxImageBytes,
|
|
72216
|
-
max_total_image_bytes: input.maxTotalImageBytes,
|
|
72217
|
-
oversized_paths: input.oversized.map((asset) => asset.packageRelPath),
|
|
72218
|
-
next: "Reduce or replace the reported source images, then rerun context build."
|
|
72219
|
-
});
|
|
72220
|
-
}
|
|
72221
72247
|
async function optimizePackageAssetFiles(input) {
|
|
72222
72248
|
const maxImageBytes = input.maxImageBytes ?? PACKAGE_ASSET_MAX_IMAGE_BYTES;
|
|
72223
72249
|
const maxTotalImageBytes = input.maxTotalImageBytes ?? PACKAGE_ASSET_MAX_TOTAL_IMAGE_BYTES;
|
|
@@ -72243,23 +72269,38 @@ async function optimizePackageAssetFiles(input) {
|
|
|
72243
72269
|
const processor = input.processor ?? await loadSharpProcessor();
|
|
72244
72270
|
const definitions = input.definition === undefined ? DEFAULT_OPTIMIZATION_PROFILES : [input.definition];
|
|
72245
72271
|
const variantsByPath = new Map;
|
|
72272
|
+
const omittedImages = new Set;
|
|
72273
|
+
const warnings = [];
|
|
72246
72274
|
for (const asset of candidates) {
|
|
72247
|
-
|
|
72275
|
+
try {
|
|
72276
|
+
variantsByPath.set(asset.packageRelPath, await adaptiveVariants({ asset, processor, definitions }));
|
|
72277
|
+
} catch (error) {
|
|
72278
|
+
if (error instanceof ContextError)
|
|
72279
|
+
throw error;
|
|
72280
|
+
warnings.push({ path: asset.packageRelPath, reason: error instanceof Error ? error.message : String(error) });
|
|
72281
|
+
omittedImages.add(asset.packageRelPath);
|
|
72282
|
+
variantsByPath.set(asset.packageRelPath, [{ bytes: asset.bytes }]);
|
|
72283
|
+
}
|
|
72248
72284
|
}
|
|
72249
72285
|
const selectedIndex = new Map;
|
|
72250
72286
|
for (const asset of candidates) {
|
|
72251
72287
|
const variants = variantsByPath.get(asset.packageRelPath);
|
|
72288
|
+
if (omittedImages.has(asset.packageRelPath))
|
|
72289
|
+
continue;
|
|
72252
72290
|
let index2 = input.definition !== undefined && variants.length > 1 ? 1 : 0;
|
|
72253
72291
|
if (asset.bytes.byteLength > maxImageBytes) {
|
|
72254
72292
|
const fitting = variants.findIndex((variant) => variant.bytes.byteLength <= maxImageBytes);
|
|
72255
72293
|
if (fitting < 0) {
|
|
72256
|
-
|
|
72294
|
+
omittedImages.add(asset.packageRelPath);
|
|
72295
|
+
warnings.push({ path: asset.packageRelPath, reason: "Image exceeds the per-image delivery budget after optimization" });
|
|
72257
72296
|
}
|
|
72258
|
-
index2 = fitting;
|
|
72297
|
+
index2 = Math.max(0, fitting);
|
|
72259
72298
|
}
|
|
72260
72299
|
selectedIndex.set(asset.packageRelPath, index2);
|
|
72261
72300
|
}
|
|
72262
72301
|
const selectedBytes = (asset) => {
|
|
72302
|
+
if (omittedImages.has(asset.packageRelPath))
|
|
72303
|
+
return new Uint8Array;
|
|
72263
72304
|
const variants = variantsByPath.get(asset.packageRelPath);
|
|
72264
72305
|
return variants[selectedIndex.get(asset.packageRelPath) ?? 0].bytes;
|
|
72265
72306
|
};
|
|
@@ -72267,6 +72308,8 @@ async function optimizePackageAssetFiles(input) {
|
|
|
72267
72308
|
while (outputBytes > maxTotalImageBytes) {
|
|
72268
72309
|
let best;
|
|
72269
72310
|
for (const asset of candidates) {
|
|
72311
|
+
if (omittedImages.has(asset.packageRelPath))
|
|
72312
|
+
continue;
|
|
72270
72313
|
const variants = variantsByPath.get(asset.packageRelPath);
|
|
72271
72314
|
const currentIndex = selectedIndex.get(asset.packageRelPath) ?? 0;
|
|
72272
72315
|
const nextIndex = currentIndex + 1;
|
|
@@ -72278,7 +72321,13 @@ async function optimizePackageAssetFiles(input) {
|
|
|
72278
72321
|
best = { asset, nextIndex, saving };
|
|
72279
72322
|
}
|
|
72280
72323
|
if (best === undefined) {
|
|
72281
|
-
|
|
72324
|
+
const largest = candidates.filter((asset) => !omittedImages.has(asset.packageRelPath)).sort((a, b) => selectedBytes(b).byteLength - selectedBytes(a).byteLength || a.packageRelPath.localeCompare(b.packageRelPath))[0];
|
|
72325
|
+
if (largest === undefined)
|
|
72326
|
+
break;
|
|
72327
|
+
outputBytes -= selectedBytes(largest).byteLength;
|
|
72328
|
+
omittedImages.add(largest.packageRelPath);
|
|
72329
|
+
warnings.push({ path: largest.packageRelPath, reason: "Image exceeds the total delivery budget after optimization" });
|
|
72330
|
+
continue;
|
|
72282
72331
|
}
|
|
72283
72332
|
selectedIndex.set(best.asset.packageRelPath, best.nextIndex);
|
|
72284
72333
|
outputBytes -= best.saving;
|
|
@@ -72286,6 +72335,8 @@ async function optimizePackageAssetFiles(input) {
|
|
|
72286
72335
|
const optimizedByInputPath = new Map;
|
|
72287
72336
|
const optimizedTargetByOriginal = new Map;
|
|
72288
72337
|
for (const asset of candidates) {
|
|
72338
|
+
if (omittedImages.has(asset.packageRelPath))
|
|
72339
|
+
continue;
|
|
72289
72340
|
const output = selectedBytes(asset);
|
|
72290
72341
|
if (output.byteLength >= asset.bytes.byteLength)
|
|
72291
72342
|
continue;
|
|
@@ -72293,14 +72344,14 @@ async function optimizePackageAssetFiles(input) {
|
|
|
72293
72344
|
optimizedByInputPath.set(asset.packageRelPath, { ...asset, packageRelPath, bytes: output });
|
|
72294
72345
|
optimizedTargetByOriginal.set(asset.packageRelPath, packageRelPath);
|
|
72295
72346
|
}
|
|
72296
|
-
const assets = input.assets.map((asset) => optimizedByInputPath.get(asset.packageRelPath) ?? asset);
|
|
72297
|
-
outputBytes = candidates.reduce((sum, asset) => sum + (optimizedByInputPath.get(asset.packageRelPath)?.bytes.byteLength ?? asset.bytes.byteLength), 0);
|
|
72298
|
-
const largestOutputBytes = Math.max(0, ...candidates.map((asset) => optimizedByInputPath.get(asset.packageRelPath)?.bytes.byteLength ?? asset.bytes.byteLength));
|
|
72347
|
+
const assets = input.assets.filter((asset) => !omittedImages.has(asset.packageRelPath)).map((asset) => optimizedByInputPath.get(asset.packageRelPath) ?? asset);
|
|
72348
|
+
outputBytes = candidates.filter((asset) => !omittedImages.has(asset.packageRelPath)).reduce((sum, asset) => sum + (optimizedByInputPath.get(asset.packageRelPath)?.bytes.byteLength ?? asset.bytes.byteLength), 0);
|
|
72349
|
+
const largestOutputBytes = Math.max(0, ...candidates.filter((asset) => !omittedImages.has(asset.packageRelPath)).map((asset) => optimizedByInputPath.get(asset.packageRelPath)?.bytes.byteLength ?? asset.bytes.byteLength));
|
|
72299
72350
|
return {
|
|
72300
72351
|
assets,
|
|
72301
72352
|
optimizedTargetByOriginal,
|
|
72302
72353
|
summary: {
|
|
72303
|
-
state: optimizedByInputPath.size > 0 ? "applied" : "configured-no-benefit",
|
|
72354
|
+
state: omittedImages.size > 0 ? "partial" : optimizedByInputPath.size > 0 ? "applied" : "configured-no-benefit",
|
|
72304
72355
|
candidateFiles: candidates.length,
|
|
72305
72356
|
originalBytes,
|
|
72306
72357
|
outputBytes,
|
|
@@ -72308,6 +72359,8 @@ async function optimizePackageAssetFiles(input) {
|
|
|
72308
72359
|
maxImageBytes,
|
|
72309
72360
|
maxTotalImageBytes,
|
|
72310
72361
|
largestOutputBytes,
|
|
72362
|
+
...warnings.length === 0 ? {} : { warnings },
|
|
72363
|
+
...omittedImages.size === 0 ? {} : { omittedImages: [...omittedImages] },
|
|
72311
72364
|
processor: "sharp",
|
|
72312
72365
|
mode: input.definition?.mode ?? "webp"
|
|
72313
72366
|
}
|
|
@@ -72510,6 +72563,7 @@ async function deliverPackageAssetFiles(input) {
|
|
|
72510
72563
|
return {
|
|
72511
72564
|
assets: optimization.assets,
|
|
72512
72565
|
targetByOriginal: optimization.optimizedTargetByOriginal,
|
|
72566
|
+
...optimization.summary.omittedImages === undefined ? {} : { omittedImages: optimization.summary.omittedImages },
|
|
72513
72567
|
summary: {
|
|
72514
72568
|
state: "bundled",
|
|
72515
72569
|
sourceFiles: input.assets.length,
|
|
@@ -72707,7 +72761,15 @@ async function writeSelectedPackageKnowledge(input) {
|
|
|
72707
72761
|
const results = await Promise.allSettled(projectedPages.slice(offset, offset + 8).map(async (projected) => {
|
|
72708
72762
|
assertSafeRenderedPath2(projected.pageOutputPath, "knowledge path");
|
|
72709
72763
|
const outputPath = join62(input.projectRoot, input.pkg.outDir, projected.pageOutputPath);
|
|
72710
|
-
|
|
72764
|
+
let mediaContent = projected.content;
|
|
72765
|
+
const omitted = new Set((delivered.omittedImages ?? []).map((path2) => packageMarkdownTarget(projected.pageOutputPath, path2)));
|
|
72766
|
+
for (const link of markdownReaderLinks(mediaContent).reverse()) {
|
|
72767
|
+
if (!omitted.has(link.target))
|
|
72768
|
+
continue;
|
|
72769
|
+
const label2 = link.label.replace(/[<>\[\]_*`]/gu, "");
|
|
72770
|
+
mediaContent = mediaContent.slice(0, link.start) + `[Image omitted: ${label2 || "image"}; see article sources]` + mediaContent.slice(link.end);
|
|
72771
|
+
}
|
|
72772
|
+
const rewritten = replaceMarkdownInlineLinkTargets(mediaContent, (link) => {
|
|
72711
72773
|
for (const [inputPath, outputPath2] of delivered.targetByOriginal) {
|
|
72712
72774
|
if (link.target === packageMarkdownTarget(projected.pageOutputPath, inputPath)) {
|
|
72713
72775
|
return /^https:\/\//u.test(outputPath2) ? outputPath2 : packageMarkdownTarget(projected.pageOutputPath, outputPath2);
|
|
@@ -73453,7 +73515,8 @@ async function buildProjectPackagesInternal(projectRoot, options) {
|
|
|
73453
73515
|
const revisionInterruptedProduction = !!production && !!await readApprovedRevision2(projectRoot);
|
|
73454
73516
|
await finishApprovedRevision(projectRoot);
|
|
73455
73517
|
const { readKnowledgeUpdate } = await Promise.resolve().then(() => (init_knowledgeUpdate(), exports_knowledgeUpdate));
|
|
73456
|
-
|
|
73518
|
+
const productionEnded = !production || dispatchProductionStage(production, productionCapabilitiesSchema.parse({})).state === "ended";
|
|
73519
|
+
if (productionEnded && !revisionInterruptedProduction && !production?.delivery && !maintenanceActive && !await readTaskRollback(projectRoot) && !await readApprovedRevision2(projectRoot) && !await readKnowledgeUpdate(projectRoot) && (await readProjectCloseStatus(projectRoot)).state === "ready") {
|
|
73457
73520
|
const { clearCompletedLifecycle: clearCompletedLifecycle2 } = await Promise.resolve().then(() => (init_lifecycleCleanup(), exports_lifecycleCleanup));
|
|
73458
73521
|
await clearCompletedLifecycle2(projectRoot);
|
|
73459
73522
|
}
|
|
@@ -73559,6 +73622,7 @@ var init_packageBuilder = __esm(() => {
|
|
|
73559
73622
|
init_packageSiteAddress();
|
|
73560
73623
|
init_knowledgeMap2();
|
|
73561
73624
|
init_approvedFileRead();
|
|
73625
|
+
init_productionStage();
|
|
73562
73626
|
init_productionStageStore();
|
|
73563
73627
|
init_productionDelivery();
|
|
73564
73628
|
init_writeLock();
|
|
@@ -82582,7 +82646,7 @@ async function productionWorkflowRoute(input) {
|
|
|
82582
82646
|
revision,
|
|
82583
82647
|
reason_code: resolved.reasonCode,
|
|
82584
82648
|
availability: resolved.availability,
|
|
82585
|
-
summary: report ? `Present the report and wait. After approval, write {stage: ${stage.id}, decision: approved} to ${path2}.` : writing ? "Read the issued task directories. Coordinate them sequentially unless this caller supports independent Agents; only the coordinator submits shared state." : repair ? "Add revision tasks for rejected articles using the Review feedback; accepted production responsibilities remain unchanged." : investigate ? `
|
|
82649
|
+
summary: report ? `Present the report and wait. After approval, write {stage: ${stage.id}, decision: approved} to ${path2}.` : writing ? "Read the issued task directories. Coordinate them sequentially unless this caller supports independent Agents; only the coordinator submits shared state." : repair ? "Add revision tasks for rejected articles using the Review feedback; accepted production responsibilities remain unchanged." : investigate ? `Review pending configured scopes: ${stage.pending_scopes.filter((scope2) => !stage.gaps.some((gap2) => gap2.scope === scope2)).join(", ")}. Check their relevance to the current request and existing approved content before assigning investigation. Submit supported article tasks and remaining pending_scopes; do not infer missing articles from this list or repeat accepted work.` : resolved.node === "resolve-production-gap" ? `Source availability gaps: ${stage.gaps.map((gap2) => `${gap2.scope}: ${gap2.reason}`).join("; ")}. These failures do not establish missing knowledge or a new investigation assignment. Identify which sources the current task actually depends on; report unrelated configured-source failures separately. Preserve the configuration and follow the current resolution action.` : "Prepare the current stage's eligible task directories.",
|
|
82586
82650
|
commands: report || prepare || writing || repair || investigate || gap ? [{
|
|
82587
82651
|
command: command2,
|
|
82588
82652
|
effect: "write",
|
|
@@ -86520,7 +86584,7 @@ class FidelityTracker {
|
|
|
86520
86584
|
var LARK_EMPTY_SUB_PAGE_LIST_CODE = "lark.capture.sub-page-list-empty";
|
|
86521
86585
|
|
|
86522
86586
|
// src/lib/larkDocxResources.ts
|
|
86523
|
-
import { createHash as
|
|
86587
|
+
import { createHash as createHash25 } from "node:crypto";
|
|
86524
86588
|
function elementName(node3) {
|
|
86525
86589
|
return Object.keys(node3).find((key) => key !== ":@" && key !== "#text");
|
|
86526
86590
|
}
|
|
@@ -86596,18 +86660,18 @@ function resourceIdentity(kind, attrs) {
|
|
|
86596
86660
|
];
|
|
86597
86661
|
return candidates.find((value) => value !== undefined && value.length > 0 && !isTransientLarkMediaUrl(value));
|
|
86598
86662
|
}
|
|
86599
|
-
function registerLarkResource(ctx, blockType, kind, attrs,
|
|
86663
|
+
function registerLarkResource(ctx, blockType, kind, attrs, title2) {
|
|
86600
86664
|
const identity = resourceIdentity(kind, attrs);
|
|
86601
86665
|
const href = attrs.href ?? attrs.url;
|
|
86602
86666
|
const locator = identity !== undefined ? `lark:${kind}:${identity}` : href !== undefined && !isTransientLarkMediaUrl(href) ? href : undefined;
|
|
86603
|
-
const resolvedLocator = locator ?? `lark:${kind}:unresolved:${
|
|
86667
|
+
const resolvedLocator = locator ?? `lark:${kind}:unresolved:${createHash25("sha256").update(JSON.stringify(Object.fromEntries(Object.entries(attrs).sort(([left], [right]) => left.localeCompare(right))))).digest("hex").slice(0, 12)}`;
|
|
86604
86668
|
if (locator === undefined) {
|
|
86605
86669
|
ctx.tracker.flag(blockType, "resource has no stable token, source id, or non-transient URL", "error");
|
|
86606
86670
|
}
|
|
86607
86671
|
const resource = {
|
|
86608
86672
|
kind,
|
|
86609
86673
|
locator: resolvedLocator,
|
|
86610
|
-
...
|
|
86674
|
+
...title2 !== undefined && title2.length > 0 ? { title: title2 } : {},
|
|
86611
86675
|
attributes: Object.fromEntries(Object.entries(attrs).filter(([, value]) => value.length > 0 && !isTransientLarkMediaUrl(value)))
|
|
86612
86676
|
};
|
|
86613
86677
|
ctx.resources.push(resource);
|
|
@@ -86625,32 +86689,32 @@ function renderLarkResource(name3, nodes, attrs, ctx) {
|
|
|
86625
86689
|
if (name3 === "cite") {
|
|
86626
86690
|
if (attrs.type === "user")
|
|
86627
86691
|
return `@${attrs["user-name"] ?? (normalizeInline(textContent(nodes)) || "user")}`;
|
|
86628
|
-
const
|
|
86692
|
+
const title3 = attrs.title ?? (normalizeInline(textContent(nodes)) || "Referenced document");
|
|
86629
86693
|
const docId = attrs["doc-id"] ?? attrs.token;
|
|
86630
|
-
const resource2 = registerLarkResource(ctx, name3, "cite", attrs,
|
|
86694
|
+
const resource2 = registerLarkResource(ctx, name3, "cite", attrs, title3);
|
|
86631
86695
|
if (docId === undefined) {
|
|
86632
86696
|
ctx.tracker.flag(name3, "cite has no doc-id or token", "error");
|
|
86633
|
-
return `${escapeMarkdownLabel(
|
|
86697
|
+
return `${escapeMarkdownLabel(title3)} <!-- ${resource2.locator} -->`;
|
|
86634
86698
|
}
|
|
86635
|
-
return `[${escapeMarkdownLabel(
|
|
86699
|
+
return `[${escapeMarkdownLabel(title3)}](${stableDocumentUrl(ctx.sourceUrl, attrs["file-type"], docId)}) <!-- ${resource2.locator} -->`;
|
|
86636
86700
|
}
|
|
86637
86701
|
if (name3 === "img" || name3 === "image") {
|
|
86638
|
-
const
|
|
86639
|
-
const resource2 = registerLarkResource(ctx, name3, "image", attrs,
|
|
86702
|
+
const title3 = attrs.alt ?? attrs.name ?? "Image";
|
|
86703
|
+
const resource2 = registerLarkResource(ctx, name3, "image", attrs, title3);
|
|
86640
86704
|
return `
|
|
86641
86705
|
|
|
86642
|
-
> Image: ${
|
|
86706
|
+
> Image: ${title3} (${resource2.locator})
|
|
86643
86707
|
|
|
86644
86708
|
`;
|
|
86645
86709
|
}
|
|
86646
86710
|
if (name3 === "source" || name3 === "file" || name3 === "attachment") {
|
|
86647
86711
|
const isVideo = attrs.mime?.startsWith("video/") === true || attrs.type === "video";
|
|
86648
86712
|
const kind2 = isVideo ? "video" : "file";
|
|
86649
|
-
const
|
|
86650
|
-
const resource2 = registerLarkResource(ctx, name3, kind2, attrs,
|
|
86713
|
+
const title3 = attrs.name ?? (isVideo ? "Video" : "File");
|
|
86714
|
+
const resource2 = registerLarkResource(ctx, name3, kind2, attrs, title3);
|
|
86651
86715
|
return `
|
|
86652
86716
|
|
|
86653
|
-
> ${isVideo ? "Video" : "File"}: ${
|
|
86717
|
+
> ${isVideo ? "Video" : "File"}: ${title3} (${resource2.locator})
|
|
86654
86718
|
|
|
86655
86719
|
`;
|
|
86656
86720
|
}
|
|
@@ -86670,7 +86734,7 @@ ${safeFence(source2, attrs.type === "mermaid" ? "mermaid" : "text")}
|
|
|
86670
86734
|
}
|
|
86671
86735
|
if (name3 === "diagram") {
|
|
86672
86736
|
const source2 = textContent(nodes).trim();
|
|
86673
|
-
const resourceAttrs = source2.length > 0 ? { ...attrs, "content-hash":
|
|
86737
|
+
const resourceAttrs = source2.length > 0 ? { ...attrs, "content-hash": createHash25("sha256").update(source2, "utf8").digest("hex") } : attrs;
|
|
86674
86738
|
const resource2 = registerLarkResource(ctx, name3, "diagram", resourceAttrs, attrs.title ?? "Diagram");
|
|
86675
86739
|
if (source2.length > 0) {
|
|
86676
86740
|
resource2.inline_content = true;
|
|
@@ -86689,92 +86753,92 @@ ${safeFence(source2, attrs.type ?? "text")}
|
|
|
86689
86753
|
`;
|
|
86690
86754
|
}
|
|
86691
86755
|
if (name3 === "chat_card") {
|
|
86692
|
-
const
|
|
86693
|
-
const resource2 = registerLarkResource(ctx, name3, "chat", attrs,
|
|
86756
|
+
const title3 = attrs.name ?? "Chat";
|
|
86757
|
+
const resource2 = registerLarkResource(ctx, name3, "chat", attrs, title3);
|
|
86694
86758
|
return `
|
|
86695
86759
|
|
|
86696
|
-
> Chat: ${
|
|
86760
|
+
> Chat: ${title3} (${resource2.locator})
|
|
86697
86761
|
|
|
86698
86762
|
`;
|
|
86699
86763
|
}
|
|
86700
86764
|
if (name3 === "readonly-block") {
|
|
86701
|
-
const
|
|
86765
|
+
const title3 = attrs.type ?? "Read-only embedded block";
|
|
86702
86766
|
const kind2 = attrs.type === "diagram" ? "diagram" : "embed";
|
|
86703
|
-
const resource2 = registerLarkResource(ctx, name3, kind2, attrs,
|
|
86767
|
+
const resource2 = registerLarkResource(ctx, name3, kind2, attrs, title3);
|
|
86704
86768
|
return kind2 === "diagram" ? `
|
|
86705
86769
|
|
|
86706
86770
|
> Diagram: ${resource2.locator}
|
|
86707
86771
|
|
|
86708
86772
|
` : `
|
|
86709
86773
|
|
|
86710
|
-
> Embedded block: ${
|
|
86774
|
+
> Embedded block: ${title3} (${resource2.locator})
|
|
86711
86775
|
|
|
86712
86776
|
`;
|
|
86713
86777
|
}
|
|
86714
86778
|
const kind = name3 === "sheet" ? "sheet" : "base";
|
|
86715
|
-
const
|
|
86716
|
-
const resource = registerLarkResource(ctx, name3, kind, attrs,
|
|
86779
|
+
const title2 = attrs.title ?? (kind === "sheet" ? "Embedded Sheet" : "Embedded Base");
|
|
86780
|
+
const resource = registerLarkResource(ctx, name3, kind, attrs, title2);
|
|
86717
86781
|
const details = [attrs["table-id"], attrs["sheet-id"], attrs["view-id"]].filter(Boolean).join(" / ");
|
|
86718
86782
|
return `
|
|
86719
86783
|
|
|
86720
|
-
> ${
|
|
86784
|
+
> ${title2}${details.length > 0 ? ` — ${details}` : ""} (${resource.locator})
|
|
86721
86785
|
|
|
86722
86786
|
`;
|
|
86723
86787
|
}
|
|
86724
86788
|
function renderLarkSubPage(nodes, attrs, ctx) {
|
|
86725
|
-
const
|
|
86789
|
+
const title2 = attrs.title ?? (normalizeInline(textContent(nodes)) || "Untitled subpage");
|
|
86726
86790
|
const docId = attrs["doc-id"] ?? attrs.token;
|
|
86727
|
-
const resource = registerLarkResource(ctx, "sub-page", "document", attrs,
|
|
86791
|
+
const resource = registerLarkResource(ctx, "sub-page", "document", attrs, title2);
|
|
86728
86792
|
if (docId === undefined) {
|
|
86729
86793
|
ctx.tracker.flag("sub-page", "sub-page has no doc-id or token", "error");
|
|
86730
|
-
return `${escapeMarkdownLabel(
|
|
86794
|
+
return `${escapeMarkdownLabel(title2)} <!-- ${resource.locator} -->`;
|
|
86731
86795
|
}
|
|
86732
|
-
return `[${escapeMarkdownLabel(
|
|
86796
|
+
return `[${escapeMarkdownLabel(title2)}](${stableDocumentUrl(ctx.sourceUrl, attrs["file-type"], docId)}) <!-- ${resource.locator} -->`;
|
|
86733
86797
|
}
|
|
86734
86798
|
function renderLarkBookmark(nodes, attrs, ctx) {
|
|
86735
86799
|
const href = attrs.href ?? attrs.url;
|
|
86736
|
-
const
|
|
86737
|
-
const resource = registerLarkResource(ctx, "bookmark", "bookmark", attrs,
|
|
86800
|
+
const title2 = attrs.name ?? attrs.title ?? (normalizeInline(textContent(nodes)) || href) ?? "Bookmark";
|
|
86801
|
+
const resource = registerLarkResource(ctx, "bookmark", "bookmark", attrs, title2);
|
|
86738
86802
|
if (href === undefined || isTransientLarkMediaUrl(href)) {
|
|
86739
86803
|
ctx.tracker.flag("bookmark", "bookmark has no stable non-transient URL", "error");
|
|
86740
86804
|
return `
|
|
86741
86805
|
|
|
86742
|
-
> Bookmark: ${escapeMarkdownLabel(
|
|
86806
|
+
> Bookmark: ${escapeMarkdownLabel(title2)} <!-- ${resource.locator} -->
|
|
86743
86807
|
|
|
86744
86808
|
`;
|
|
86745
86809
|
}
|
|
86746
86810
|
return `
|
|
86747
86811
|
|
|
86748
|
-
> Bookmark: [${escapeMarkdownLabel(
|
|
86812
|
+
> Bookmark: [${escapeMarkdownLabel(title2)}](${href}) <!-- ${resource.locator} -->
|
|
86749
86813
|
|
|
86750
86814
|
`;
|
|
86751
86815
|
}
|
|
86752
86816
|
function renderLarkSyncedReference(attrs, ctx) {
|
|
86753
86817
|
const sourceToken = attrs["src-token"];
|
|
86754
86818
|
const sourceBlockId = attrs["src-block-id"];
|
|
86755
|
-
const
|
|
86756
|
-
const resource = registerLarkResource(ctx, "synced_reference", "synced-reference", attrs,
|
|
86819
|
+
const title2 = attrs.title ?? attrs.name ?? "Synced reference";
|
|
86820
|
+
const resource = registerLarkResource(ctx, "synced_reference", "synced-reference", attrs, title2);
|
|
86757
86821
|
if (sourceToken === undefined || sourceBlockId === undefined) {
|
|
86758
86822
|
ctx.tracker.flag("synced_reference", "synced_reference requires both src-token and src-block-id", "error");
|
|
86759
86823
|
return `
|
|
86760
86824
|
|
|
86761
|
-
> ${escapeMarkdownLabel(
|
|
86825
|
+
> ${escapeMarkdownLabel(title2)} <!-- ${resource.locator} -->
|
|
86762
86826
|
|
|
86763
86827
|
`;
|
|
86764
86828
|
}
|
|
86765
86829
|
const target = `${stableDocumentUrl(ctx.sourceUrl, "docx", sourceToken)}#${encodeURIComponent(sourceBlockId)}`;
|
|
86766
86830
|
return `
|
|
86767
86831
|
|
|
86768
|
-
> [${escapeMarkdownLabel(
|
|
86832
|
+
> [${escapeMarkdownLabel(title2)}](${target}) <!-- ${resource.locator} -->
|
|
86769
86833
|
|
|
86770
86834
|
`;
|
|
86771
86835
|
}
|
|
86772
86836
|
var init_larkDocxResources = () => {};
|
|
86773
86837
|
|
|
86774
86838
|
// src/lib/larkDocxXml.ts
|
|
86775
|
-
import { createHash as
|
|
86839
|
+
import { createHash as createHash26 } from "node:crypto";
|
|
86776
86840
|
function sha256(value) {
|
|
86777
|
-
return `sha256:${
|
|
86841
|
+
return `sha256:${createHash26("sha256").update(value, "utf8").digest("hex")}`;
|
|
86778
86842
|
}
|
|
86779
86843
|
function elementName2(node3) {
|
|
86780
86844
|
return Object.keys(node3).find((key) => key !== ":@" && key !== "#text");
|
|
@@ -86825,9 +86889,9 @@ function renderList(nodes, ctx, ordered) {
|
|
|
86825
86889
|
const name3 = elementName2(item);
|
|
86826
86890
|
ctx.tracker.discover(name3);
|
|
86827
86891
|
ctx.tracker.convert(name3);
|
|
86828
|
-
const
|
|
86892
|
+
const body2 = normalizeMarkdown(renderChildren(elementChildren2(item, name3), { ...ctx, mode: "inline" }));
|
|
86829
86893
|
const marker = ordered ? `${index2 + 1}.` : "-";
|
|
86830
|
-
return
|
|
86894
|
+
return body2.split(`
|
|
86831
86895
|
`).map((line, lineIndex) => lineIndex === 0 ? `${marker} ${line}` : ` ${line}`).join(`
|
|
86832
86896
|
`);
|
|
86833
86897
|
}).join(`
|
|
@@ -86903,15 +86967,15 @@ function renderChecklistItem(blockType, nodes, attrs, ctx) {
|
|
|
86903
86967
|
const checked = attrs.checked;
|
|
86904
86968
|
const state = done ?? checked;
|
|
86905
86969
|
const stateIsValid = (state === "true" || state === "false") && (done === undefined || checked === undefined || done === checked);
|
|
86906
|
-
const
|
|
86970
|
+
const body2 = normalizeInline2(renderChildren(nodes, { ...ctx, mode: "inline" }));
|
|
86907
86971
|
if (!stateIsValid) {
|
|
86908
86972
|
ctx.tracker.flag(blockType, `${blockType} requires one unambiguous boolean done or checked attribute`, "warning", "lark.capture.checkbox-state-invalid", "projection");
|
|
86909
86973
|
return `
|
|
86910
|
-
- [?] ${
|
|
86974
|
+
- [?] ${body2}
|
|
86911
86975
|
`;
|
|
86912
86976
|
}
|
|
86913
86977
|
return `
|
|
86914
|
-
- [${state === "true" ? "x" : " "}] ${
|
|
86978
|
+
- [${state === "true" ? "x" : " "}] ${body2}
|
|
86915
86979
|
`;
|
|
86916
86980
|
}
|
|
86917
86981
|
function meaningfulAttributes(attrs, excluded2) {
|
|
@@ -86921,8 +86985,8 @@ function auditableAttributes(attrs) {
|
|
|
86921
86985
|
return Object.entries(attrs).filter(([, value]) => value.length > 0).map(([key, value]) => [key, isTransientLarkMediaUrl(value) ? "[redacted-transient-url]" : value]).sort(([left], [right]) => left.localeCompare(right));
|
|
86922
86986
|
}
|
|
86923
86987
|
function renderPollOption(blockType, nodes, attrs, ctx) {
|
|
86924
|
-
const
|
|
86925
|
-
const label2 =
|
|
86988
|
+
const body2 = normalizeInline2(renderChildren(nodes, { ...ctx, mode: "inline" }));
|
|
86989
|
+
const label2 = body2 || attrs.name || attrs.title || attrs.label || attrs.value;
|
|
86926
86990
|
const details = meaningfulAttributes(attrs, new Set(["name", "title", "label", "value"])).map(([key, value]) => `${key}=${value}`);
|
|
86927
86991
|
if (label2 === undefined && details.length === 0) {
|
|
86928
86992
|
ctx.tracker.flag(blockType, `${blockType} has no visible label or metadata`, "warning", "lark.capture.poll-option-empty", "projection");
|
|
@@ -86933,10 +86997,10 @@ function renderPollOption(blockType, nodes, attrs, ctx) {
|
|
|
86933
86997
|
`;
|
|
86934
86998
|
}
|
|
86935
86999
|
function renderPoll(nodes, attrs, ctx) {
|
|
86936
|
-
const
|
|
86937
|
-
const resource = registerLarkResource(ctx, "poll", "poll", attrs,
|
|
87000
|
+
const title2 = attrs.name ?? attrs.title ?? "Untitled poll";
|
|
87001
|
+
const resource = registerLarkResource(ctx, "poll", "poll", attrs, title2);
|
|
86938
87002
|
const href = attrs.href ?? attrs.url;
|
|
86939
|
-
const label2 = href !== undefined && !isTransientLarkMediaUrl(href) ? `[${escapeMarkdownLabel2(
|
|
87003
|
+
const label2 = href !== undefined && !isTransientLarkMediaUrl(href) ? `[${escapeMarkdownLabel2(title2)}](${href})` : escapeMarkdownLabel2(title2);
|
|
86940
87004
|
const details = meaningfulAttributes(attrs, new Set(["name", "title", "href", "url"])).map(([key, value]) => `${key}=${value}`);
|
|
86941
87005
|
const children = normalizeMarkdown(renderChildren(nodes, { ...ctx, mode: "block" }));
|
|
86942
87006
|
resource.inline_content = children.length > 0;
|
|
@@ -86953,15 +87017,15 @@ ${lines.join(`
|
|
|
86953
87017
|
`;
|
|
86954
87018
|
}
|
|
86955
87019
|
function renderUnknown(name3, nodes, attrs, ctx) {
|
|
86956
|
-
const
|
|
87020
|
+
const body2 = normalizeMarkdown(renderChildren(nodes, ctx));
|
|
86957
87021
|
const exportedAttrs = auditableAttributes(attrs);
|
|
86958
|
-
if (
|
|
87022
|
+
if (body2.length === 0 && exportedAttrs.length === 0) {
|
|
86959
87023
|
ctx.tracker.skip(name3, "unknown empty block omitted", "warning");
|
|
86960
87024
|
return "";
|
|
86961
87025
|
}
|
|
86962
87026
|
ctx.tracker.convert(name3);
|
|
86963
87027
|
ctx.tracker.flag(name3, "block was preserved through the generic non-interactive projection; inspect source.xml for the original structure", "warning", "lark.capture.generic-projection", "projection");
|
|
86964
|
-
const digest6 =
|
|
87028
|
+
const digest6 = createHash26("sha256").update(JSON.stringify({ name: name3, attributes: exportedAttrs, text: normalizeInline2(textContent2(nodes)) }), "utf8").digest("hex").slice(0, 12);
|
|
86965
87029
|
const locator = `lark:block:${name3}:${digest6}`;
|
|
86966
87030
|
ctx.resources.push({
|
|
86967
87031
|
kind: "embed",
|
|
@@ -86972,7 +87036,7 @@ function renderUnknown(name3, nodes, attrs, ctx) {
|
|
|
86972
87036
|
const lines = [
|
|
86973
87037
|
`> Lark block (generic projection): \`${name3}\` <!-- ${locator} -->`,
|
|
86974
87038
|
...exportedAttrs.length > 0 ? [`> Exported attributes: ${JSON.stringify(Object.fromEntries(exportedAttrs))}`] : [],
|
|
86975
|
-
...
|
|
87039
|
+
...body2.length > 0 ? [body2] : []
|
|
86976
87040
|
];
|
|
86977
87041
|
return `
|
|
86978
87042
|
|
|
@@ -87021,10 +87085,10 @@ ${"#".repeat(Math.min(Math.max(level, 1), 6))} ${normalizeInline2(renderChildren
|
|
|
87021
87085
|
}
|
|
87022
87086
|
if (["p", "paragraph", "div", "section"].includes(name3)) {
|
|
87023
87087
|
ctx.tracker.convert(name3);
|
|
87024
|
-
const
|
|
87025
|
-
return ctx.mode === "inline" ?
|
|
87088
|
+
const body2 = renderChildren(nodes, { ...ctx, mode: "inline" });
|
|
87089
|
+
return ctx.mode === "inline" ? body2 : `
|
|
87026
87090
|
|
|
87027
|
-
${
|
|
87091
|
+
${body2}
|
|
87028
87092
|
|
|
87029
87093
|
`;
|
|
87030
87094
|
}
|
|
@@ -87064,8 +87128,8 @@ ${body}
|
|
|
87064
87128
|
}
|
|
87065
87129
|
if (name3 === "code") {
|
|
87066
87130
|
ctx.tracker.convert(name3);
|
|
87067
|
-
const
|
|
87068
|
-
return ctx.mode === "code" ?
|
|
87131
|
+
const body2 = renderChildren(nodes, { ...ctx, mode: "code" });
|
|
87132
|
+
return ctx.mode === "code" ? body2 : `\`${body2.replace(/`/gu, "\\`")}\``;
|
|
87069
87133
|
}
|
|
87070
87134
|
if (name3 === "pre") {
|
|
87071
87135
|
ctx.tracker.convert(name3);
|
|
@@ -87117,10 +87181,10 @@ ${renderTable(nodes, ctx)}
|
|
|
87117
87181
|
if (name3 === "callout" || name3 === "blockquote" || name3 === "quote") {
|
|
87118
87182
|
ctx.tracker.convert(name3);
|
|
87119
87183
|
const prefix = attrs.emoji === undefined ? "" : `${attrs.emoji} `;
|
|
87120
|
-
const
|
|
87184
|
+
const body2 = normalizeMarkdown(renderChildren(nodes, { ...ctx, mode: "block" }));
|
|
87121
87185
|
return `
|
|
87122
87186
|
|
|
87123
|
-
${
|
|
87187
|
+
${body2.split(`
|
|
87124
87188
|
`).map((line, index2) => `> ${index2 === 0 ? prefix : ""}${line}`).join(`
|
|
87125
87189
|
`)}
|
|
87126
87190
|
|
|
@@ -87176,10 +87240,10 @@ function projectLarkDocxXml(input) {
|
|
|
87176
87240
|
mode: "block"
|
|
87177
87241
|
}));
|
|
87178
87242
|
const titleNode = rootChildren.find((node3) => elementName2(node3) === "title");
|
|
87179
|
-
const
|
|
87243
|
+
const title2 = titleNode === undefined ? undefined : normalizeInline2(textContent2(elementChildren2(titleNode, "title")));
|
|
87180
87244
|
return {
|
|
87181
87245
|
markdown,
|
|
87182
|
-
...
|
|
87246
|
+
...title2 !== undefined && title2.length > 0 ? { title: title2 } : {},
|
|
87183
87247
|
auditXml: sanitizeAuditXml(input.xml),
|
|
87184
87248
|
rawContentHash,
|
|
87185
87249
|
resources,
|
|
@@ -87252,6 +87316,32 @@ var init_larkDocxXml = __esm(() => {
|
|
|
87252
87316
|
]);
|
|
87253
87317
|
});
|
|
87254
87318
|
|
|
87319
|
+
// src/lib/larkImagePolicy.ts
|
|
87320
|
+
function omitLarkImage(resource, policy, items, replacements, mediaType, failure2) {
|
|
87321
|
+
if (resource.kind !== "image" && !mediaType?.startsWith("image/"))
|
|
87322
|
+
return false;
|
|
87323
|
+
const gif = mediaType === "image/gif" || /\.gif$/iu.test(resource.title ?? "") || Object.entries(resource.attributes).some(([key, value]) => ["mime_type", "content_type"].includes(key) && value === "image/gif");
|
|
87324
|
+
if (!failure2 && policy.images !== "reference-only" && !(gif && policy.gifs === "reference-only"))
|
|
87325
|
+
return false;
|
|
87326
|
+
const title2 = (resource.title ?? "image").replace(/[\r\n<>]/gu, " ");
|
|
87327
|
+
replacements.set(resource.locator, `> Image omitted: ${title2}. ${failure2 ? "Resource size limit exceeded." : "Excluded by selected policy."} See the source document. <!-- ${resource.locator} -->`);
|
|
87328
|
+
items.push({
|
|
87329
|
+
kind: resource.kind,
|
|
87330
|
+
locator: resource.locator,
|
|
87331
|
+
status: "reference-only",
|
|
87332
|
+
required: false,
|
|
87333
|
+
asset_paths: [],
|
|
87334
|
+
reason_code: failure2 ? "image-budget-exceeded" : "image-excluded-by-policy",
|
|
87335
|
+
reason: failure2 ?? "Image bytes were not retained; the selected image policy preserves a placeholder and source reference"
|
|
87336
|
+
});
|
|
87337
|
+
return true;
|
|
87338
|
+
}
|
|
87339
|
+
var LarkResourceBudgetError;
|
|
87340
|
+
var init_larkImagePolicy = __esm(() => {
|
|
87341
|
+
LarkResourceBudgetError = class LarkResourceBudgetError extends Error {
|
|
87342
|
+
};
|
|
87343
|
+
});
|
|
87344
|
+
|
|
87255
87345
|
// src/lib/larkResourceCommand.ts
|
|
87256
87346
|
function stableJson(value) {
|
|
87257
87347
|
if (value === undefined)
|
|
@@ -87321,9 +87411,9 @@ var init_larkResourceCommand = __esm(() => {
|
|
|
87321
87411
|
});
|
|
87322
87412
|
|
|
87323
87413
|
// src/lib/larkResourceMaterialization.ts
|
|
87324
|
-
import { createHash as
|
|
87325
|
-
import { mkdtemp as mkdtemp4, readFile as
|
|
87326
|
-
import { extname as extname13, join as
|
|
87414
|
+
import { createHash as createHash27 } from "node:crypto";
|
|
87415
|
+
import { mkdtemp as mkdtemp4, readFile as readFile71, readdir as readdir21, rm as rm19 } from "node:fs/promises";
|
|
87416
|
+
import { extname as extname13, join as join91 } from "node:path";
|
|
87327
87417
|
import { tmpdir } from "node:os";
|
|
87328
87418
|
function countByKind(items, status) {
|
|
87329
87419
|
const counts2 = new Map;
|
|
@@ -87335,7 +87425,7 @@ function countByKind(items, status) {
|
|
|
87335
87425
|
return Object.fromEntries([...counts2].sort(([left], [right]) => left.localeCompare(right)));
|
|
87336
87426
|
}
|
|
87337
87427
|
function resourceDigest(resource) {
|
|
87338
|
-
return
|
|
87428
|
+
return createHash27("sha256").update(`${resource.kind}\x00${resource.locator}`, "utf8").digest("hex").slice(0, 20);
|
|
87339
87429
|
}
|
|
87340
87430
|
function safeLabel(value, fallback) {
|
|
87341
87431
|
const normalized = (value ?? fallback).replace(/[\r\n]+/gu, " ").trim();
|
|
@@ -87428,10 +87518,10 @@ function findBooleanField(value, name3) {
|
|
|
87428
87518
|
}
|
|
87429
87519
|
async function downloadedFile(input) {
|
|
87430
87520
|
if (input.localPath !== undefined) {
|
|
87431
|
-
const bytes = await
|
|
87521
|
+
const bytes = await readFile71(input.localPath);
|
|
87432
87522
|
return { path: input.localPath, bytes, mediaType: mediaTypeFor(input.localPath, bytes) };
|
|
87433
87523
|
}
|
|
87434
|
-
const tempRoot = await mkdtemp4(
|
|
87524
|
+
const tempRoot = await mkdtemp4(join91(tmpdir(), "context-lark-resource-"));
|
|
87435
87525
|
try {
|
|
87436
87526
|
await runLarkResourceCommand(input.runner, [
|
|
87437
87527
|
"docs",
|
|
@@ -87448,11 +87538,11 @@ async function downloadedFile(input) {
|
|
|
87448
87538
|
"--format",
|
|
87449
87539
|
"json"
|
|
87450
87540
|
], { cwd: tempRoot });
|
|
87451
|
-
const entries2 = (await
|
|
87541
|
+
const entries2 = (await readdir21(tempRoot, { withFileTypes: true })).filter((entry) => entry.isFile());
|
|
87452
87542
|
if (entries2.length !== 1)
|
|
87453
87543
|
throw new Error(`media download produced ${entries2.length} files, expected exactly one`);
|
|
87454
87544
|
const path3 = entries2[0]?.name ?? "resource.bin";
|
|
87455
|
-
const bytes = await
|
|
87545
|
+
const bytes = await readFile71(join91(tempRoot, path3));
|
|
87456
87546
|
return { path: path3, bytes, mediaType: mediaTypeFor(path3, bytes) };
|
|
87457
87547
|
} finally {
|
|
87458
87548
|
await rm19(tempRoot, { recursive: true, force: true });
|
|
@@ -87543,9 +87633,9 @@ async function sheetMaterialization(resource, runner2, identity) {
|
|
|
87543
87633
|
const sheetId = resource.attributes["sheet-id"];
|
|
87544
87634
|
if (token === undefined || sheetId === undefined)
|
|
87545
87635
|
throw new Error("embedded Sheet requires token and sheet-id");
|
|
87546
|
-
const tempRoot = await mkdtemp4(
|
|
87636
|
+
const tempRoot = await mkdtemp4(join91(tmpdir(), "context-lark-sheet-"));
|
|
87547
87637
|
try {
|
|
87548
|
-
const outputPath =
|
|
87638
|
+
const outputPath = join91(tempRoot, "sheet.json");
|
|
87549
87639
|
const stdout = await runLarkResourceCommand(runner2, [
|
|
87550
87640
|
"sheets",
|
|
87551
87641
|
"+csv-get",
|
|
@@ -87565,13 +87655,13 @@ async function sheetMaterialization(resource, runner2, identity) {
|
|
|
87565
87655
|
if (findBooleanField(receipt2, "truncated") === true || findBooleanField(receipt2, "complete") === false) {
|
|
87566
87656
|
throw new Error("embedded Sheet read was truncated");
|
|
87567
87657
|
}
|
|
87568
|
-
const payload = JSON.parse(await
|
|
87658
|
+
const payload = JSON.parse(await readFile71(outputPath, "utf8"));
|
|
87569
87659
|
const csv = findStringField(payload, new Set(["annotated_csv", "csv", "content", "text"]));
|
|
87570
87660
|
if (csv === undefined)
|
|
87571
87661
|
throw new Error("embedded Sheet response has no CSV payload");
|
|
87572
87662
|
const digest6 = resourceDigest(resource);
|
|
87573
87663
|
const path3 = `materialized/sheet/${digest6}.csv`;
|
|
87574
|
-
const
|
|
87664
|
+
const title2 = safeLabel(resource.title, "Embedded Sheet");
|
|
87575
87665
|
return {
|
|
87576
87666
|
asset: {
|
|
87577
87667
|
path: path3,
|
|
@@ -87580,7 +87670,7 @@ async function sheetMaterialization(resource, runner2, identity) {
|
|
|
87580
87670
|
role: "evidence",
|
|
87581
87671
|
source: { kind: resource.kind, locator: resource.locator }
|
|
87582
87672
|
},
|
|
87583
|
-
replacement: `#### ${
|
|
87673
|
+
replacement: `#### ${title2}
|
|
87584
87674
|
|
|
87585
87675
|
${markdownTable2(parseCsv(csv))}
|
|
87586
87676
|
|
|
@@ -87680,7 +87770,7 @@ async function baseMaterialization(resource, runner2, identity) {
|
|
|
87680
87770
|
`;
|
|
87681
87771
|
const digest6 = resourceDigest(resource);
|
|
87682
87772
|
const path3 = `materialized/base/${digest6}.json`;
|
|
87683
|
-
const
|
|
87773
|
+
const title2 = safeLabel(resource.title, "Embedded Base");
|
|
87684
87774
|
return {
|
|
87685
87775
|
asset: {
|
|
87686
87776
|
path: path3,
|
|
@@ -87689,7 +87779,7 @@ async function baseMaterialization(resource, runner2, identity) {
|
|
|
87689
87779
|
role: "evidence",
|
|
87690
87780
|
source: { kind: resource.kind, locator: resource.locator }
|
|
87691
87781
|
},
|
|
87692
|
-
replacement: `#### ${
|
|
87782
|
+
replacement: `#### ${title2}
|
|
87693
87783
|
|
|
87694
87784
|
${markdownTable2(baseRows(records, fieldOrder))}
|
|
87695
87785
|
|
|
@@ -87701,7 +87791,7 @@ async function whiteboardMaterialization(resource, runner2, identity) {
|
|
|
87701
87791
|
if (token === undefined)
|
|
87702
87792
|
throw new Error(`${resource.kind} has no whiteboard token`);
|
|
87703
87793
|
const preview = await downloadedFile({ runner: runner2, identity, token, type: "whiteboard" });
|
|
87704
|
-
const tempRoot = await mkdtemp4(
|
|
87794
|
+
const tempRoot = await mkdtemp4(join91(tmpdir(), "context-lark-whiteboard-"));
|
|
87705
87795
|
let rawPayload;
|
|
87706
87796
|
try {
|
|
87707
87797
|
await runLarkResourceCommand(runner2, [
|
|
@@ -87719,14 +87809,14 @@ async function whiteboardMaterialization(resource, runner2, identity) {
|
|
|
87719
87809
|
"--format",
|
|
87720
87810
|
"json"
|
|
87721
87811
|
], { cwd: tempRoot });
|
|
87722
|
-
rawPayload = JSON.parse(await
|
|
87812
|
+
rawPayload = JSON.parse(await readFile71(join91(tempRoot, "raw.json"), "utf8"));
|
|
87723
87813
|
} finally {
|
|
87724
87814
|
await rm19(tempRoot, { recursive: true, force: true });
|
|
87725
87815
|
}
|
|
87726
87816
|
const digest6 = resourceDigest(resource);
|
|
87727
87817
|
const previewPath = `materialized/${resource.kind}/${digest6}${extensionFor2(preview.mediaType, preview.path)}`;
|
|
87728
87818
|
const rawPath = `materialized/${resource.kind}/${digest6}.json`;
|
|
87729
|
-
const
|
|
87819
|
+
const title2 = markdownLabel(safeLabel(resource.title, resource.kind === "diagram" ? "Diagram" : "Whiteboard"));
|
|
87730
87820
|
return {
|
|
87731
87821
|
assets: [
|
|
87732
87822
|
{
|
|
@@ -87745,20 +87835,20 @@ async function whiteboardMaterialization(resource, runner2, identity) {
|
|
|
87745
87835
|
source: { kind: resource.kind, locator: resource.locator }
|
|
87746
87836
|
}
|
|
87747
87837
|
],
|
|
87748
|
-
replacement: `})
|
|
87749
87839
|
|
|
87750
87840
|
[Raw snapshot](${sourceAssetTarget(rawPath)}) <!-- ${resource.locator} -->`
|
|
87751
87841
|
};
|
|
87752
87842
|
}
|
|
87753
87843
|
function placeholderFor(resource) {
|
|
87754
|
-
const
|
|
87844
|
+
const title2 = safeLabel(resource.title, resource.kind);
|
|
87755
87845
|
switch (resource.kind) {
|
|
87756
87846
|
case "image":
|
|
87757
|
-
return `> Image: ${
|
|
87847
|
+
return `> Image: ${title2} (${resource.locator})`;
|
|
87758
87848
|
case "video":
|
|
87759
|
-
return `> Video: ${
|
|
87849
|
+
return `> Video: ${title2} (${resource.locator})`;
|
|
87760
87850
|
case "file":
|
|
87761
|
-
return `> File: ${
|
|
87851
|
+
return `> File: ${title2} (${resource.locator})`;
|
|
87762
87852
|
case "whiteboard":
|
|
87763
87853
|
return `> Whiteboard: ${resource.locator}`;
|
|
87764
87854
|
case "diagram":
|
|
@@ -87780,7 +87870,7 @@ function referenceOnlyReason(resource) {
|
|
|
87780
87870
|
}
|
|
87781
87871
|
function materializationReport(items) {
|
|
87782
87872
|
const hasRequiredFailure = items.some((item) => item.status === "failed" && item.required && !isNonBlockingDocumentResourceFailureReasonCode(item.reason_code));
|
|
87783
|
-
const hasOptionalFailure = items.some((item) => item.status === "failed") || items.some((item) => item.status === "reference-only" && item.kind === "poll" && item.reason?.includes("absent") === true);
|
|
87873
|
+
const hasOptionalFailure = items.some((item) => item.status === "failed" || item.reason_code === "image-budget-exceeded") || items.some((item) => item.status === "reference-only" && item.kind === "poll" && item.reason?.includes("absent") === true);
|
|
87784
87874
|
return {
|
|
87785
87875
|
status: hasRequiredFailure ? "error" : hasOptionalFailure ? "warning" : "complete",
|
|
87786
87876
|
discovered: countByKind(items),
|
|
@@ -87800,16 +87890,16 @@ function resourceFailureReasonCode(resource, error) {
|
|
|
87800
87890
|
return /\b2890003\b/u.test(message) ? DOCUMENT_RESOURCE_SOURCE_MISSING_REASON_CODE : undefined;
|
|
87801
87891
|
}
|
|
87802
87892
|
function unavailableReplacement(resource, reasonCode) {
|
|
87803
|
-
const
|
|
87893
|
+
const title2 = markdownLabel(safeLabel(resource.title, resource.kind));
|
|
87804
87894
|
const reason = reasonCode === DOCUMENT_RESOURCE_PERMISSION_DENIED_REASON_CODE ? "export permission denied" : "source no longer exists";
|
|
87805
|
-
return `> Resource unavailable: ${
|
|
87895
|
+
return `> Resource unavailable: ${title2} (${resource.kind}; ${reason}). <!-- ${resource.locator} -->`;
|
|
87806
87896
|
}
|
|
87807
87897
|
function assertBudget(asset, policy, total) {
|
|
87808
87898
|
if (asset.bytes.byteLength > policy.maxBytesPerResource) {
|
|
87809
|
-
throw new
|
|
87899
|
+
throw new LarkResourceBudgetError(`resource is ${asset.bytes.byteLength} bytes, above maxBytesPerResource=${policy.maxBytesPerResource}`);
|
|
87810
87900
|
}
|
|
87811
87901
|
if (total + asset.bytes.byteLength > policy.maxTotalBytes) {
|
|
87812
|
-
throw new
|
|
87902
|
+
throw new LarkResourceBudgetError(`materialized resources exceed maxTotalBytes=${policy.maxTotalBytes}`);
|
|
87813
87903
|
}
|
|
87814
87904
|
}
|
|
87815
87905
|
async function materializeLarkResources(input) {
|
|
@@ -87825,6 +87915,8 @@ async function materializeLarkResources(input) {
|
|
|
87825
87915
|
return;
|
|
87826
87916
|
seen.add(key);
|
|
87827
87917
|
const required = REQUIRED_KINDS.has(resource.kind);
|
|
87918
|
+
if (omitLarkImage(resource, input.policy, items, replacements))
|
|
87919
|
+
return;
|
|
87828
87920
|
try {
|
|
87829
87921
|
if (resource.kind === "diagram" && resource.inline_content === true) {
|
|
87830
87922
|
replacements.set(resource.locator, "");
|
|
@@ -87911,6 +88003,8 @@ async function materializeLarkResources(input) {
|
|
|
87911
88003
|
type: "media",
|
|
87912
88004
|
...input.mediaFiles?.[token] === undefined ? {} : { localPath: input.mediaFiles[token] }
|
|
87913
88005
|
});
|
|
88006
|
+
if (omitLarkImage(resource, input.policy, items, replacements, downloaded.mediaType))
|
|
88007
|
+
return;
|
|
87914
88008
|
const digest6 = resourceDigest(resource);
|
|
87915
88009
|
const extension2 = extensionFor2(downloaded.mediaType, downloaded.path);
|
|
87916
88010
|
const asset = {
|
|
@@ -87923,12 +88017,14 @@ async function materializeLarkResources(input) {
|
|
|
87923
88017
|
assertBudget(asset, input.policy, totalBytes);
|
|
87924
88018
|
totalBytes += asset.bytes.byteLength;
|
|
87925
88019
|
assets.push(asset);
|
|
87926
|
-
const
|
|
88020
|
+
const title2 = markdownLabel(safeLabel(resource.title, resource.kind));
|
|
87927
88021
|
const target = sourceAssetTarget(asset.path);
|
|
87928
|
-
const replacement = downloaded.mediaType.startsWith("image/") ? ` <!-- ${resource.locator} -->` : `[${title2}](${target}) <!-- ${resource.locator} -->`;
|
|
87929
88023
|
replacements.set(resource.locator, replacement);
|
|
87930
88024
|
items.push({ kind: resource.kind, locator: resource.locator, status: "materialized", required, asset_paths: [asset.path] });
|
|
87931
88025
|
} catch (error) {
|
|
88026
|
+
if (error instanceof LarkResourceBudgetError && omitLarkImage(resource, input.policy, items, replacements, undefined, error.message))
|
|
88027
|
+
return;
|
|
87932
88028
|
const reasonCode = resourceFailureReasonCode(resource, error);
|
|
87933
88029
|
if (isNonBlockingDocumentResourceFailureReasonCode(reasonCode)) {
|
|
87934
88030
|
replacements.set(resource.locator, unavailableReplacement(resource, reasonCode));
|
|
@@ -87973,6 +88069,7 @@ function applyLarkResourceReplacements(markdown, resources, replacements) {
|
|
|
87973
88069
|
}
|
|
87974
88070
|
var REQUIRED_KINDS, MEDIA_TYPES_BY_EXTENSION;
|
|
87975
88071
|
var init_larkResourceMaterialization = __esm(() => {
|
|
88072
|
+
init_larkImagePolicy();
|
|
87976
88073
|
init_src3();
|
|
87977
88074
|
init_larkResourceCommand();
|
|
87978
88075
|
REQUIRED_KINDS = new Set([
|
|
@@ -88145,9 +88242,9 @@ function payloadShapeSummary(payload) {
|
|
|
88145
88242
|
}
|
|
88146
88243
|
function extractDocsFetchContent(payload, requestedFormat) {
|
|
88147
88244
|
const document4 = payload.document && typeof payload.document === "object" ? payload.document : undefined;
|
|
88148
|
-
const
|
|
88245
|
+
const title2 = stringValue(payload.title) ?? stringValue(document4?.title);
|
|
88149
88246
|
const markdown = stringValue(payload.markdown) ?? stringValue(document4?.markdown);
|
|
88150
|
-
const withTitle = (result) =>
|
|
88247
|
+
const withTitle = (result) => title2 === undefined ? result : { ...result, title: title2 };
|
|
88151
88248
|
if (markdown !== undefined) {
|
|
88152
88249
|
if (requestedFormat === "xml") {
|
|
88153
88250
|
throw new LarkCliError(`${LARK_BIN} docs +fetch returned Markdown despite --doc-format xml; capture stopped because the response cannot provide auditable rich-block fidelity. Upgrade lark-cli and retry.`, 0, "");
|
|
@@ -88265,7 +88362,7 @@ function userIdentityUnavailable(error) {
|
|
|
88265
88362
|
async function fetchDocsResponse(input, docsFetchPlan, runner2, identity) {
|
|
88266
88363
|
const chunks = [];
|
|
88267
88364
|
let contentFormat;
|
|
88268
|
-
let
|
|
88365
|
+
let title2;
|
|
88269
88366
|
let revisionId;
|
|
88270
88367
|
let unsupportedShape;
|
|
88271
88368
|
const assets = [];
|
|
@@ -88295,7 +88392,7 @@ async function fetchDocsResponse(input, docsFetchPlan, runner2, identity) {
|
|
|
88295
88392
|
}
|
|
88296
88393
|
contentFormat = extracted.format;
|
|
88297
88394
|
if (page === 0 && extracted.title !== undefined)
|
|
88298
|
-
|
|
88395
|
+
title2 = extracted.title;
|
|
88299
88396
|
revisionId ??= extractDocsFetchRevisionId(payload);
|
|
88300
88397
|
assets.push(...extractDocsFetchAssets(payload));
|
|
88301
88398
|
if (extracted.body !== undefined && extracted.body.length > 0) {
|
|
@@ -88317,19 +88414,19 @@ async function fetchDocsResponse(input, docsFetchPlan, runner2, identity) {
|
|
|
88317
88414
|
throw new LarkCliError(`${LARK_BIN} docs +fetch exceeded ${MAX_FETCH_PAGES} pagination calls; likely a server-side issue`, 0, "");
|
|
88318
88415
|
}
|
|
88319
88416
|
}
|
|
88320
|
-
const
|
|
88417
|
+
const body2 = chunks.join(`
|
|
88321
88418
|
|
|
88322
88419
|
`);
|
|
88323
|
-
if (
|
|
88420
|
+
if (body2.trim().length === 0 && (title2 === undefined || title2.length === 0)) {
|
|
88324
88421
|
if (unsupportedShape !== undefined) {
|
|
88325
88422
|
throw new LarkCliError(`${LARK_BIN} docs +fetch returned an unsupported payload shape (${unsupportedShape}). Expected data.markdown or data.document.content; this is a format adapter issue, not a permission error.`, 0, "");
|
|
88326
88423
|
}
|
|
88327
88424
|
throw new LarkCliError("document is empty — it may not exist or you lack permission", 0, "");
|
|
88328
88425
|
}
|
|
88329
88426
|
return {
|
|
88330
|
-
body,
|
|
88427
|
+
body: body2,
|
|
88331
88428
|
contentFormat,
|
|
88332
|
-
...
|
|
88429
|
+
...title2 !== undefined ? { title: title2 } : {},
|
|
88333
88430
|
...revisionId !== undefined ? { revisionId } : {},
|
|
88334
88431
|
assets
|
|
88335
88432
|
};
|
|
@@ -88382,8 +88479,8 @@ async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
|
88382
88479
|
break;
|
|
88383
88480
|
fetched = await fetchDocsResponse(input, docsFetchPlan, runner2, accessIdentity);
|
|
88384
88481
|
}
|
|
88385
|
-
let
|
|
88386
|
-
let
|
|
88482
|
+
let body2 = fetched.body;
|
|
88483
|
+
let title2 = fetched.title;
|
|
88387
88484
|
const revisionId = fetched.revisionId;
|
|
88388
88485
|
const assets = [...fetched.assets];
|
|
88389
88486
|
let fidelity = emptyFidelityReport();
|
|
@@ -88396,8 +88493,8 @@ async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
|
88396
88493
|
items: []
|
|
88397
88494
|
};
|
|
88398
88495
|
if (projection !== undefined) {
|
|
88399
|
-
|
|
88400
|
-
|
|
88496
|
+
body2 = projection.markdown;
|
|
88497
|
+
title2 ??= projection.title;
|
|
88401
88498
|
fidelity = projection.fidelity;
|
|
88402
88499
|
assets.push({
|
|
88403
88500
|
path: "source.xml",
|
|
@@ -88410,7 +88507,7 @@ async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
|
88410
88507
|
}
|
|
88411
88508
|
});
|
|
88412
88509
|
}
|
|
88413
|
-
const resources = [...new Map([...projection?.resources ?? [], ...larkMarkdownImageResources(
|
|
88510
|
+
const resources = [...new Map([...projection?.resources ?? [], ...larkMarkdownImageResources(body2)].map((resource) => [resource.locator, resource])).values()];
|
|
88414
88511
|
if (resources.length) {
|
|
88415
88512
|
const policy = {
|
|
88416
88513
|
...DEFAULT_RESOURCE_POLICY,
|
|
@@ -88439,8 +88536,8 @@ async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
|
88439
88536
|
});
|
|
88440
88537
|
}
|
|
88441
88538
|
});
|
|
88442
|
-
|
|
88443
|
-
|
|
88539
|
+
body2 = applyLarkResourceReplacements(body2, resources, materialized.replacements);
|
|
88540
|
+
body2 = replaceLarkMarkdownImages(body2, materialized.replacements);
|
|
88444
88541
|
assets.push(...materialized.assets.map((asset) => ({
|
|
88445
88542
|
path: asset.path,
|
|
88446
88543
|
bytes: asset.bytes,
|
|
@@ -88455,12 +88552,12 @@ async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
|
88455
88552
|
resourceMaterialization,
|
|
88456
88553
|
resources
|
|
88457
88554
|
}));
|
|
88458
|
-
if (
|
|
88555
|
+
if (title2 !== undefined && title2.length > 0 && !/^#\s/.test(body2)) {
|
|
88459
88556
|
return {
|
|
88460
|
-
markdown: `# ${
|
|
88557
|
+
markdown: `# ${title2}
|
|
88461
88558
|
|
|
88462
|
-
${
|
|
88463
|
-
title,
|
|
88559
|
+
${body2}`,
|
|
88560
|
+
title: title2,
|
|
88464
88561
|
...revisionId !== undefined ? { revisionId } : {},
|
|
88465
88562
|
assets,
|
|
88466
88563
|
fidelity,
|
|
@@ -88470,8 +88567,8 @@ ${body}`,
|
|
|
88470
88567
|
};
|
|
88471
88568
|
}
|
|
88472
88569
|
return {
|
|
88473
|
-
markdown:
|
|
88474
|
-
...
|
|
88570
|
+
markdown: body2,
|
|
88571
|
+
...title2 !== undefined ? { title: title2 } : {},
|
|
88475
88572
|
...revisionId !== undefined ? { revisionId } : {},
|
|
88476
88573
|
assets,
|
|
88477
88574
|
fidelity,
|
|
@@ -88564,8 +88661,8 @@ var init_sensitiveSourceLiteral = __esm(() => {
|
|
|
88564
88661
|
var LARK_DOCUMENT_NORMALIZER_VERSION = "lark-document-normalizer.v1";
|
|
88565
88662
|
|
|
88566
88663
|
// src/project/documentCaptureLark.ts
|
|
88567
|
-
import { readdir as
|
|
88568
|
-
import { basename as basename10, extname as extname14, join as
|
|
88664
|
+
import { readdir as readdir22, readFile as readFile72 } from "node:fs/promises";
|
|
88665
|
+
import { basename as basename10, extname as extname14, join as join92 } from "node:path";
|
|
88569
88666
|
function titleFromMarkdown2(markdown, fallbackPath) {
|
|
88570
88667
|
const heading2 = markdown.split(`
|
|
88571
88668
|
`).find((line) => /^#\s+\S/u.test(line));
|
|
@@ -88584,7 +88681,7 @@ function countLines3(markdown) {
|
|
|
88584
88681
|
}
|
|
88585
88682
|
async function fileContentMatches(path3, content3) {
|
|
88586
88683
|
try {
|
|
88587
|
-
const current2 = await
|
|
88684
|
+
const current2 = await readFile72(path3);
|
|
88588
88685
|
const expected = typeof content3 === "string" ? Buffer.from(content3, "utf8") : Buffer.from(content3);
|
|
88589
88686
|
return current2.equals(expected);
|
|
88590
88687
|
} catch {
|
|
@@ -88592,7 +88689,7 @@ async function fileContentMatches(path3, content3) {
|
|
|
88592
88689
|
}
|
|
88593
88690
|
}
|
|
88594
88691
|
function sourceManifestPath2(entry) {
|
|
88595
|
-
return entry.snapshot?.manifest ??
|
|
88692
|
+
return entry.snapshot?.manifest ?? join92(entry.materializedAt, "manifest.json");
|
|
88596
88693
|
}
|
|
88597
88694
|
function larkRuntimeError(message, detail) {
|
|
88598
88695
|
return new ContextError(ExitCode.ExternalToolError, message, {
|
|
@@ -88718,12 +88815,12 @@ function assetManifestEntry(asset, assetRoot) {
|
|
|
88718
88815
|
};
|
|
88719
88816
|
}
|
|
88720
88817
|
async function listSnapshotAssetFiles(root2, assetRoot) {
|
|
88721
|
-
const assetsRoot =
|
|
88818
|
+
const assetsRoot = join92(root2, assetRoot);
|
|
88722
88819
|
const files = [];
|
|
88723
88820
|
const visit4 = async (dir, prefix = assetRoot) => {
|
|
88724
88821
|
let entries2;
|
|
88725
88822
|
try {
|
|
88726
|
-
entries2 = await
|
|
88823
|
+
entries2 = await readdir22(dir, { withFileTypes: true });
|
|
88727
88824
|
} catch (error) {
|
|
88728
88825
|
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
88729
88826
|
return;
|
|
@@ -88731,7 +88828,7 @@ async function listSnapshotAssetFiles(root2, assetRoot) {
|
|
|
88731
88828
|
}
|
|
88732
88829
|
for (const entry of entries2) {
|
|
88733
88830
|
const relPath = `${prefix}/${entry.name}`;
|
|
88734
|
-
const absolutePath =
|
|
88831
|
+
const absolutePath = join92(dir, entry.name);
|
|
88735
88832
|
if (entry.isDirectory()) {
|
|
88736
88833
|
await visit4(absolutePath, relPath);
|
|
88737
88834
|
continue;
|
|
@@ -88746,7 +88843,7 @@ async function listSnapshotAssetFiles(root2, assetRoot) {
|
|
|
88746
88843
|
}
|
|
88747
88844
|
async function staleSnapshotAssetPaths(input) {
|
|
88748
88845
|
const existingPaths = await listSnapshotAssetFiles(input.materializedAtAbsPath, input.assetRoot);
|
|
88749
|
-
return existingPaths.filter((path3) => !input.currentPaths.has(path3)).map((path3) =>
|
|
88846
|
+
return existingPaths.filter((path3) => !input.currentPaths.has(path3)).map((path3) => join92(input.materializedAtAbsPath, path3));
|
|
88750
88847
|
}
|
|
88751
88848
|
function normalizeLarkError(error, sourceName) {
|
|
88752
88849
|
if (error instanceof ContextError)
|
|
@@ -88828,7 +88925,7 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
88828
88925
|
});
|
|
88829
88926
|
}
|
|
88830
88927
|
const documentPath = normalizeSnapshotRelativePath(entry.module === undefined ? "index.md" : `${entry.module}.md`);
|
|
88831
|
-
const
|
|
88928
|
+
const title2 = entry.title ?? fetched.title ?? titleFromMarkdown2(normalized, documentPath);
|
|
88832
88929
|
const locator = target.kind === "url" ? target.value : `${target.kind}:${target.value}`;
|
|
88833
88930
|
const assetRoot = entry.module === undefined ? "assets" : `assets/${entry.module}`;
|
|
88834
88931
|
const reportPath = normalizeSnapshotRelativePath(`${assetRoot}/capture-report.json`);
|
|
@@ -88850,13 +88947,13 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
88850
88947
|
const snapshotFiles = [{
|
|
88851
88948
|
path: documentPath,
|
|
88852
88949
|
bytes: normalized,
|
|
88853
|
-
title,
|
|
88950
|
+
title: title2,
|
|
88854
88951
|
locator
|
|
88855
88952
|
}];
|
|
88856
88953
|
const manifestPath = sourceManifestPath2(entry);
|
|
88857
|
-
const manifestAbsPath =
|
|
88954
|
+
const manifestAbsPath = join92(input.projectRoot, manifestPath);
|
|
88858
88955
|
const materializedAt = entry.materializedAt;
|
|
88859
|
-
const materializedAtAbsPath =
|
|
88956
|
+
const materializedAtAbsPath = join92(input.projectRoot, materializedAt);
|
|
88860
88957
|
const manifest = createDocumentSnapshotManifest({
|
|
88861
88958
|
sourceType: "lark",
|
|
88862
88959
|
sourceName: resolved.sourceName,
|
|
@@ -88890,13 +88987,13 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
88890
88987
|
}));
|
|
88891
88988
|
try {
|
|
88892
88989
|
const requestedWrites = [{
|
|
88893
|
-
path:
|
|
88990
|
+
path: join92(materializedAtAbsPath, documentPath),
|
|
88894
88991
|
bytes: normalized
|
|
88895
88992
|
}];
|
|
88896
88993
|
for (const asset of assets) {
|
|
88897
88994
|
if (asset.bytes !== undefined) {
|
|
88898
88995
|
requestedWrites.push({
|
|
88899
|
-
path:
|
|
88996
|
+
path: join92(materializedAtAbsPath, asset.entry.path),
|
|
88900
88997
|
bytes: asset.bytes
|
|
88901
88998
|
});
|
|
88902
88999
|
}
|
|
@@ -88913,7 +89010,7 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
88913
89010
|
currentPaths: new Set(assets.filter((asset) => asset.bytes !== undefined).map((asset) => asset.entry.path))
|
|
88914
89011
|
});
|
|
88915
89012
|
await applyAtomicFileBatch({
|
|
88916
|
-
transactionRoot:
|
|
89013
|
+
transactionRoot: join92(input.projectRoot, ".tmp", "context-runtime", "capture-transactions"),
|
|
88917
89014
|
writes,
|
|
88918
89015
|
removals
|
|
88919
89016
|
});
|
|
@@ -88973,7 +89070,7 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
88973
89070
|
},
|
|
88974
89071
|
documents: [{
|
|
88975
89072
|
path: documentPath,
|
|
88976
|
-
title,
|
|
89073
|
+
title: title2,
|
|
88977
89074
|
line_count: countLines3(normalized)
|
|
88978
89075
|
}],
|
|
88979
89076
|
assets: assets.map((asset) => ({
|
|
@@ -89016,7 +89113,7 @@ __export(exports_actionInputWorkspace, {
|
|
|
89016
89113
|
assertActionInputWorkspace: () => assertActionInputWorkspace
|
|
89017
89114
|
});
|
|
89018
89115
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
89019
|
-
import { dirname as
|
|
89116
|
+
import { dirname as dirname39, resolve as resolve31 } from "node:path";
|
|
89020
89117
|
function assertActionInputWorkspace(cwd, inputPath) {
|
|
89021
89118
|
if (inputPath === "-")
|
|
89022
89119
|
return;
|
|
@@ -89029,7 +89126,7 @@ function assertActionInputWorkspace(cwd, inputPath) {
|
|
|
89029
89126
|
} catch {
|
|
89030
89127
|
return;
|
|
89031
89128
|
}
|
|
89032
|
-
const owner = findContextProjectRoot(
|
|
89129
|
+
const owner = findContextProjectRoot(dirname39(file));
|
|
89033
89130
|
if (owner === null || realpathSync2(owner.projectRoot) === realpathSync2(current2.projectRoot))
|
|
89034
89131
|
return;
|
|
89035
89132
|
throw new ContextError(ExitCode.WorkspaceStateError, "The completion input belongs to a different Context workspace. Run the workflow CLI and read/write this task's .tmp files in the same intended workspace. No tasks were submitted.", {
|
|
@@ -89059,7 +89156,7 @@ __export(exports_articleRetirement, {
|
|
|
89059
89156
|
retireArticles: () => retireArticles,
|
|
89060
89157
|
articleRetirementSchema: () => articleRetirementSchema
|
|
89061
89158
|
});
|
|
89062
|
-
import { readFile as
|
|
89159
|
+
import { readFile as readFile78, readdir as readdir23 } from "node:fs/promises";
|
|
89063
89160
|
import { posix as posix10 } from "node:path";
|
|
89064
89161
|
function rebuildInput(revision) {
|
|
89065
89162
|
return JSON.stringify({ id: `retirement-${revision.slice(7)}`, operation: "rebuild", timing: "priority", targets: [] });
|
|
@@ -89080,7 +89177,7 @@ function invalid2(reason, message, details = {}) {
|
|
|
89080
89177
|
}
|
|
89081
89178
|
async function text9(root2, path3) {
|
|
89082
89179
|
try {
|
|
89083
|
-
return await
|
|
89180
|
+
return await readFile78(await safeProjectTarget(root2, path3), "utf8");
|
|
89084
89181
|
} catch (error) {
|
|
89085
89182
|
if (error.code === "ENOENT")
|
|
89086
89183
|
return;
|
|
@@ -89118,7 +89215,7 @@ async function retireArticles(input) {
|
|
|
89118
89215
|
invalid2("article-retirement-active-review", "Finish the active candidate/revision delivery before retiring approved pages.");
|
|
89119
89216
|
}
|
|
89120
89217
|
const before = await text9(input.projectRoot, "knowledge/structure.yaml");
|
|
89121
|
-
const structure = before === undefined ? {} :
|
|
89218
|
+
const structure = before === undefined ? {} : import_yaml42.default.parse(before);
|
|
89122
89219
|
const articles = validateArticleStructureEntries(structure.articles ?? []);
|
|
89123
89220
|
const byPath = new Map(articles.map((article) => [article.path, article]));
|
|
89124
89221
|
const selected = new Set(value.targets.map((target) => target.path));
|
|
@@ -89192,7 +89289,7 @@ async function retireArticles(input) {
|
|
|
89192
89289
|
}
|
|
89193
89290
|
async function inspectTemplates(directory2) {
|
|
89194
89291
|
const absolute = await safeProjectTarget(input.projectRoot, directory2);
|
|
89195
|
-
const entries2 = await
|
|
89292
|
+
const entries2 = await readdir23(absolute, { withFileTypes: true }).catch((error) => {
|
|
89196
89293
|
if (error.code === "ENOENT")
|
|
89197
89294
|
return [];
|
|
89198
89295
|
throw error;
|
|
@@ -89241,9 +89338,9 @@ async function retireArticles(input) {
|
|
|
89241
89338
|
return [target.replacement ? { ...group, target: { artifact_ref: byPath.get(target.replacement).article_id } } : group];
|
|
89242
89339
|
});
|
|
89243
89340
|
const updated = updateKnowledgeMap(map4, { expected_revision: map4.revision, upsert, remove: [] });
|
|
89244
|
-
change(KNOWLEDGE_MAP_PATH, await text9(input.projectRoot, KNOWLEDGE_MAP_PATH),
|
|
89341
|
+
change(KNOWLEDGE_MAP_PATH, await text9(input.projectRoot, KNOWLEDGE_MAP_PATH), import_yaml42.default.stringify(updated));
|
|
89245
89342
|
}
|
|
89246
|
-
change("knowledge/structure.yaml", before,
|
|
89343
|
+
change("knowledge/structure.yaml", before, import_yaml42.default.stringify({ ...structure, articles: articles.filter((article) => !selected.has(article.path)) }));
|
|
89247
89344
|
targets.sort((a, b) => a.path.localeCompare(b.path));
|
|
89248
89345
|
const revision = indexerProtocolDigest({ value, readDigests, targets });
|
|
89249
89346
|
const preview = {
|
|
@@ -89287,7 +89384,7 @@ async function retireArticles(input) {
|
|
|
89287
89384
|
};
|
|
89288
89385
|
});
|
|
89289
89386
|
}
|
|
89290
|
-
var
|
|
89387
|
+
var import_yaml42, articleRetirementSchema, next = "context status --format json";
|
|
89291
89388
|
var init_articleRetirement = __esm(() => {
|
|
89292
89389
|
init_zod();
|
|
89293
89390
|
init_src2();
|
|
@@ -89306,7 +89403,7 @@ var init_articleRetirement = __esm(() => {
|
|
|
89306
89403
|
init_durableSingleFileTransaction();
|
|
89307
89404
|
init_durableMultiFileTransaction();
|
|
89308
89405
|
init_writeLock();
|
|
89309
|
-
|
|
89406
|
+
import_yaml42 = __toESM(require_dist(), 1);
|
|
89310
89407
|
articleRetirementSchema = exports_external.object({
|
|
89311
89408
|
reason: exports_external.string().trim().min(1),
|
|
89312
89409
|
targets: exports_external.array(exports_external.object({ path: exports_external.string().min(1), replacement: exports_external.string().min(1).optional() }).strict()).min(1)
|
|
@@ -89326,14 +89423,14 @@ function schemaOutputFormat(value) {
|
|
|
89326
89423
|
}
|
|
89327
89424
|
function writeSchemaOutput(value, format2) {
|
|
89328
89425
|
process.stdout.write(format2 === "json" ? `${JSON.stringify(value, null, 2)}
|
|
89329
|
-
` :
|
|
89426
|
+
` : import_yaml43.default.stringify(value));
|
|
89330
89427
|
}
|
|
89331
|
-
var
|
|
89428
|
+
var import_yaml43;
|
|
89332
89429
|
var init_schemaOutput = __esm(() => {
|
|
89333
89430
|
init_errors3();
|
|
89334
89431
|
init_cliFeedback();
|
|
89335
89432
|
init_exitCode();
|
|
89336
|
-
|
|
89433
|
+
import_yaml43 = __toESM(require_dist(), 1);
|
|
89337
89434
|
});
|
|
89338
89435
|
|
|
89339
89436
|
// src/project/writeLockRecovery.ts
|
|
@@ -89342,9 +89439,9 @@ __export(exports_writeLockRecovery, {
|
|
|
89342
89439
|
recoverWriterLock: () => recoverWriterLock,
|
|
89343
89440
|
inspectWriterLock: () => inspectWriterLock
|
|
89344
89441
|
});
|
|
89345
|
-
import { lstat as lstat10, mkdir as mkdir33, readFile as
|
|
89346
|
-
import { join as
|
|
89347
|
-
import { createHash as
|
|
89442
|
+
import { lstat as lstat10, mkdir as mkdir33, readFile as readFile79, readdir as readdir24, rename as rename8, rmdir as rmdir2 } from "node:fs/promises";
|
|
89443
|
+
import { join as join97 } from "node:path";
|
|
89444
|
+
import { createHash as createHash29, randomUUID as randomUUID7 } from "node:crypto";
|
|
89348
89445
|
async function inspectWriterLock(root2) {
|
|
89349
89446
|
const path3 = await safeProjectTarget(root2, lockRelative);
|
|
89350
89447
|
let stat10;
|
|
@@ -89358,7 +89455,7 @@ async function inspectWriterLock(root2) {
|
|
|
89358
89455
|
if (!stat10.isDirectory() || stat10.isSymbolicLink())
|
|
89359
89456
|
throw new Error("Writer lock must be a real directory.");
|
|
89360
89457
|
const ownerPath = await safeProjectTarget(root2, `${lockRelative}/owner.json`);
|
|
89361
|
-
const bytes = await
|
|
89458
|
+
const bytes = await readFile79(ownerPath, "utf8");
|
|
89362
89459
|
const owner = JSON.parse(bytes);
|
|
89363
89460
|
if (owner.protocol !== "context.project-write-lock.v1" || !Number.isSafeInteger(owner.pid) || owner.pid <= 0) {
|
|
89364
89461
|
throw new Error("Writer lock owner is invalid; preserve the lock for diagnosis.");
|
|
@@ -89370,7 +89467,7 @@ async function inspectWriterLock(root2) {
|
|
|
89370
89467
|
const code3 = error.code;
|
|
89371
89468
|
processState = code3 === "ESRCH" ? "not-running" : code3 === "EPERM" ? "running" : "unknown";
|
|
89372
89469
|
}
|
|
89373
|
-
const digest6 = `sha256:${
|
|
89470
|
+
const digest6 = `sha256:${createHash29("sha256").update(`${stat10.dev}:${stat10.ino}:${stat10.birthtimeMs}:${bytes}`).digest("hex")}`;
|
|
89374
89471
|
return { ...owner, process_state: processState, digest: digest6, path: lockRelative };
|
|
89375
89472
|
}
|
|
89376
89473
|
async function recoverWriterLock(input) {
|
|
@@ -89390,19 +89487,19 @@ async function recoverWriterLock(input) {
|
|
|
89390
89487
|
};
|
|
89391
89488
|
if (input.plan_digest !== before.digest)
|
|
89392
89489
|
throw new Error("Writer lock changed; preview recovery again.");
|
|
89393
|
-
const path3 =
|
|
89394
|
-
const guard =
|
|
89490
|
+
const path3 = join97(input.projectRoot, lockRelative);
|
|
89491
|
+
const guard = join97(path3, ".recovery");
|
|
89395
89492
|
await mkdir33(guard);
|
|
89396
89493
|
let archived = false;
|
|
89397
89494
|
try {
|
|
89398
89495
|
const current2 = await inspectWriterLock(input.projectRoot);
|
|
89399
89496
|
if (current2?.digest !== before.digest || current2.process_state !== "not-running")
|
|
89400
89497
|
throw new Error("Writer lock changed or owner resumed; keep the lock.");
|
|
89401
|
-
const names = await
|
|
89498
|
+
const names = await readdir24(path3);
|
|
89402
89499
|
if (names.some((name3) => name3 !== "owner.json" && name3 !== ".recovery"))
|
|
89403
89500
|
throw new Error("Unexpected lock contents; preserve for diagnosis.");
|
|
89404
89501
|
const archive = `.tmp/context-runtime/locks/recovered-write-${randomUUID7()}.lock`;
|
|
89405
|
-
await rename8(path3,
|
|
89502
|
+
await rename8(path3, join97(input.projectRoot, archive));
|
|
89406
89503
|
archived = true;
|
|
89407
89504
|
return { action: "writer-lock-recovered", archived_lock: archive, next: "context task recover --format json" };
|
|
89408
89505
|
} finally {
|
|
@@ -89426,12 +89523,12 @@ __export(exports_taskRecovery, {
|
|
|
89426
89523
|
RECOVERY_COMMAND: () => RECOVERY_COMMAND
|
|
89427
89524
|
});
|
|
89428
89525
|
import { existsSync as existsSync26 } from "node:fs";
|
|
89429
|
-
import { dirname as
|
|
89430
|
-
import { lstat as lstat11, readdir as
|
|
89526
|
+
import { dirname as dirname40, join as join98 } from "node:path";
|
|
89527
|
+
import { lstat as lstat11, readdir as readdir25, readFile as readFile80 } from "node:fs/promises";
|
|
89431
89528
|
async function recoveryText(root2, path3) {
|
|
89432
89529
|
const target = await safeProjectTarget(root2, path3);
|
|
89433
89530
|
try {
|
|
89434
|
-
return await
|
|
89531
|
+
return await readFile80(target, "utf8");
|
|
89435
89532
|
} catch (error) {
|
|
89436
89533
|
if (error.code === "ENOENT")
|
|
89437
89534
|
return;
|
|
@@ -89444,7 +89541,7 @@ async function recoveryJournals(root2) {
|
|
|
89444
89541
|
await safeProjectTarget(root2, path3);
|
|
89445
89542
|
let stat10;
|
|
89446
89543
|
try {
|
|
89447
|
-
stat10 = await lstat11(
|
|
89544
|
+
stat10 = await lstat11(join98(root2, path3));
|
|
89448
89545
|
} catch (error) {
|
|
89449
89546
|
if (error.code === "ENOENT")
|
|
89450
89547
|
return;
|
|
@@ -89455,7 +89552,7 @@ async function recoveryJournals(root2) {
|
|
|
89455
89552
|
if (stat10.isDirectory()) {
|
|
89456
89553
|
if (depth > 3)
|
|
89457
89554
|
throw new TypeError("Unexpected transaction directory depth; preserve it for diagnosis.");
|
|
89458
|
-
for (const name3 of (await
|
|
89555
|
+
for (const name3 of (await readdir25(join98(root2, path3))).sort())
|
|
89459
89556
|
await visit4(`${path3}/${name3}`, depth + 1);
|
|
89460
89557
|
} else if (stat10.isFile())
|
|
89461
89558
|
entries2.push({ path: path3, digest: indexerProtocolDigest(await recoveryText(root2, path3)) });
|
|
@@ -89465,10 +89562,10 @@ async function recoveryJournals(root2) {
|
|
|
89465
89562
|
}
|
|
89466
89563
|
function recoveryResources() {
|
|
89467
89564
|
try {
|
|
89468
|
-
const root2 =
|
|
89565
|
+
const root2 = dirname40(contextWorkflowProviderPath());
|
|
89469
89566
|
const resources = {
|
|
89470
|
-
skill:
|
|
89471
|
-
issue_template:
|
|
89567
|
+
skill: join98(root2, "skills/recover-workspace/SKILL.md"),
|
|
89568
|
+
issue_template: join98(root2, "resources/templates/recovery-issue.md")
|
|
89472
89569
|
};
|
|
89473
89570
|
if (!Object.values(resources).every((path3) => existsSync26(path3)))
|
|
89474
89571
|
throw new Error("Recovery resources are absent from this Provider.");
|
|
@@ -89553,8 +89650,8 @@ var exports_taskLocalSourceAdjustment = {};
|
|
|
89553
89650
|
__export(exports_taskLocalSourceAdjustment, {
|
|
89554
89651
|
adjustLocalRevisionSources: () => adjustLocalRevisionSources
|
|
89555
89652
|
});
|
|
89556
|
-
import { readFile as
|
|
89557
|
-
import { join as
|
|
89653
|
+
import { readFile as readFile81 } from "node:fs/promises";
|
|
89654
|
+
import { join as join99 } from "node:path";
|
|
89558
89655
|
async function adjustLocalRevisionSources(root2, input) {
|
|
89559
89656
|
const { readMaintenance: readMaintenance2 } = await Promise.resolve().then(() => (init_maintenanceStorage(), exports_maintenanceStorage));
|
|
89560
89657
|
if ((await readMaintenance2(root2)).active && await readProductionStage(root2))
|
|
@@ -89578,7 +89675,7 @@ async function adjustLocalRevisionSources(root2, input) {
|
|
|
89578
89675
|
if (input.refresh && (!current2.refresh_sources || indexerProtocolDigest([...current2.refresh_sources].sort()) !== indexerProtocolDigest([...selected].sort()))) {
|
|
89579
89676
|
throw new TypeError("No matching acquisition adjustment exists. Run task adjust without refresh first.");
|
|
89580
89677
|
}
|
|
89581
|
-
const raw = await
|
|
89678
|
+
const raw = await readFile81(join99(root2, await revisionStoragePath(root2)), "utf8");
|
|
89582
89679
|
let next2;
|
|
89583
89680
|
const discardIds = new Set;
|
|
89584
89681
|
if (!input.refresh) {
|
|
@@ -89681,7 +89778,7 @@ ${input.instruction}` : revision.instruction
|
|
|
89681
89778
|
content: content3
|
|
89682
89779
|
}];
|
|
89683
89780
|
if (discardIds.size > 0) {
|
|
89684
|
-
const ledger = await
|
|
89781
|
+
const ledger = await readFile81(join99(root2, CANDIDATE_LEDGER_FILE), "utf8").catch((error) => {
|
|
89685
89782
|
if (error.code === "ENOENT")
|
|
89686
89783
|
return;
|
|
89687
89784
|
throw error;
|
|
@@ -89803,7 +89900,7 @@ var init_taskSourceAdjustment = __esm(() => {
|
|
|
89803
89900
|
});
|
|
89804
89901
|
|
|
89805
89902
|
// src/project/managedDocumentImport.ts
|
|
89806
|
-
import { readFile as
|
|
89903
|
+
import { readFile as readFile87 } from "node:fs/promises";
|
|
89807
89904
|
async function importManagedDocument(projectRoot, value) {
|
|
89808
89905
|
const input = inputSchema.parse(value);
|
|
89809
89906
|
if (input.type !== "sessions" && input.changes !== undefined)
|
|
@@ -89812,7 +89909,7 @@ async function importManagedDocument(projectRoot, value) {
|
|
|
89812
89909
|
const path3 = await assertManagedDocumentPath(projectRoot, input.type, input.name);
|
|
89813
89910
|
let previous3;
|
|
89814
89911
|
try {
|
|
89815
|
-
previous3 = await
|
|
89912
|
+
previous3 = await readFile87(path3, "utf8");
|
|
89816
89913
|
} catch (error) {
|
|
89817
89914
|
if (!(error && typeof error === "object" && ("code" in error) && error.code === "ENOENT"))
|
|
89818
89915
|
throw error;
|
|
@@ -89859,14 +89956,14 @@ var init_managedDocumentImport = __esm(() => {
|
|
|
89859
89956
|
});
|
|
89860
89957
|
|
|
89861
89958
|
// src/project/larkDocumentImport.ts
|
|
89862
|
-
import { readFile as
|
|
89959
|
+
import { readFile as readFile88 } from "node:fs/promises";
|
|
89863
89960
|
async function importLarkDocument(projectRoot, value) {
|
|
89864
89961
|
const input = schema3.parse(value);
|
|
89865
89962
|
const responsePages = [];
|
|
89866
89963
|
for (const path3 of input.response_files) {
|
|
89867
89964
|
assertActionInputWorkspace(projectRoot, path3);
|
|
89868
89965
|
const { resolve: resolve8 } = await import("node:path");
|
|
89869
|
-
responsePages.push(await
|
|
89966
|
+
responsePages.push(await readFile88(resolve8(projectRoot, path3), "utf8"));
|
|
89870
89967
|
}
|
|
89871
89968
|
const mediaFiles = {};
|
|
89872
89969
|
for (const [token, path3] of Object.entries(input.media_files ?? {})) {
|
|
@@ -89934,11 +90031,11 @@ var exports_managedDocumentRename = {};
|
|
|
89934
90031
|
__export(exports_managedDocumentRename, {
|
|
89935
90032
|
renameManagedDocument: () => renameManagedDocument
|
|
89936
90033
|
});
|
|
89937
|
-
import { readFile as
|
|
89938
|
-
import { join as
|
|
90034
|
+
import { readFile as readFile89 } from "node:fs/promises";
|
|
90035
|
+
import { join as join107, posix as posix11 } from "node:path";
|
|
89939
90036
|
async function optionalText2(path3) {
|
|
89940
90037
|
try {
|
|
89941
|
-
return await
|
|
90038
|
+
return await readFile89(path3, "utf8");
|
|
89942
90039
|
} catch (error) {
|
|
89943
90040
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
|
|
89944
90041
|
return;
|
|
@@ -90022,12 +90119,12 @@ async function renameManagedDocument(input) {
|
|
|
90022
90119
|
if (path3.split("/").includes("..") || path3.startsWith("/"))
|
|
90023
90120
|
throw new TypeError("Source references contain an unsafe knowledge path; repair it before renaming.");
|
|
90024
90121
|
await safeProjectTarget(input.projectRoot, path3);
|
|
90025
|
-
const before = await optionalText2(
|
|
90122
|
+
const before = await optionalText2(join107(input.projectRoot, path3));
|
|
90026
90123
|
if (before === undefined)
|
|
90027
90124
|
continue;
|
|
90028
90125
|
let after;
|
|
90029
90126
|
if (path3.endsWith(".yaml")) {
|
|
90030
|
-
const parsed =
|
|
90127
|
+
const parsed = import_yaml49.default.parse(before);
|
|
90031
90128
|
const updated = replace2(parsed);
|
|
90032
90129
|
if (path3 === "knowledge/structure.yaml") {
|
|
90033
90130
|
const rewritten = articles.map((article) => ({ ...article, sections: article.sections.map((section) => ({
|
|
@@ -90042,7 +90139,7 @@ async function renameManagedDocument(input) {
|
|
|
90042
90139
|
}
|
|
90043
90140
|
if (JSON.stringify(parsed) === JSON.stringify(updated))
|
|
90044
90141
|
continue;
|
|
90045
|
-
after =
|
|
90142
|
+
after = import_yaml49.default.stringify(updated);
|
|
90046
90143
|
} else
|
|
90047
90144
|
after = before.replace(reference2, nextRef);
|
|
90048
90145
|
if (path3 === "src/index.ts")
|
|
@@ -90094,7 +90191,7 @@ async function renameManagedDocument(input) {
|
|
|
90094
90191
|
};
|
|
90095
90192
|
});
|
|
90096
90193
|
}
|
|
90097
|
-
var
|
|
90194
|
+
var import_yaml49;
|
|
90098
90195
|
var init_managedDocumentRename = __esm(() => {
|
|
90099
90196
|
init_markdownLinks();
|
|
90100
90197
|
init_src2();
|
|
@@ -90106,12 +90203,12 @@ var init_managedDocumentRename = __esm(() => {
|
|
|
90106
90203
|
init_durableSingleFileTransaction();
|
|
90107
90204
|
init_durableMultiFileTransaction();
|
|
90108
90205
|
init_writeLock();
|
|
90109
|
-
|
|
90206
|
+
import_yaml49 = __toESM(require_dist(), 1);
|
|
90110
90207
|
});
|
|
90111
90208
|
|
|
90112
90209
|
// src/cli.ts
|
|
90113
90210
|
import { existsSync as existsSync36, realpathSync as realpathSync3 } from "node:fs";
|
|
90114
|
-
import { dirname as
|
|
90211
|
+
import { dirname as dirname47, join as join111 } from "node:path";
|
|
90115
90212
|
import { fileURLToPath as fileURLToPath10, pathToFileURL as pathToFileURL3 } from "node:url";
|
|
90116
90213
|
|
|
90117
90214
|
// ../../node_modules/.bun/commander@11.1.0/node_modules/commander/esm.mjs
|
|
@@ -90166,7 +90263,7 @@ init_dist();
|
|
|
90166
90263
|
init_cliFeedback();
|
|
90167
90264
|
init_errors3();
|
|
90168
90265
|
init_exitCode();
|
|
90169
|
-
import { join as
|
|
90266
|
+
import { join as join89 } from "node:path";
|
|
90170
90267
|
|
|
90171
90268
|
// src/project/status.ts
|
|
90172
90269
|
init_productionPlanning();
|
|
@@ -101001,200 +101098,21 @@ init_workflowFacts();
|
|
|
101001
101098
|
init_workflowProvider();
|
|
101002
101099
|
init_workflowTypes();
|
|
101003
101100
|
|
|
101004
|
-
// src/project/
|
|
101005
|
-
|
|
101006
|
-
|
|
101007
|
-
|
|
101008
|
-
"Omit all": "全部不收录",
|
|
101009
|
-
"Copy review results": "复制审核结果",
|
|
101010
|
-
"Toggle theme": "切换明暗主题",
|
|
101011
|
-
"Pages to review": "待审页面",
|
|
101012
|
-
"candidate filters": "按审核状态筛选",
|
|
101013
|
-
approved: "已批准",
|
|
101014
|
-
omitted: "不收录",
|
|
101015
|
-
pending: "待审核",
|
|
101016
|
-
"Search pages or modules": "搜索页面或模块",
|
|
101017
|
-
"Page content": "页面内容",
|
|
101018
|
-
Previous: "上一页",
|
|
101019
|
-
Next: "下一页",
|
|
101020
|
-
"Next pending": "下一个待审",
|
|
101021
|
-
"Review results": "审核结果",
|
|
101022
|
-
"These choices take effect only after you send the review code back to the conversation. Each segment is at most 980 characters. Send every segment before applying.": "将审核码发回会话后,这些选择才会生效。每段不超过 980 个字符;如果有多段,请全部发送后再应用。",
|
|
101023
|
-
"Previous segment": "上一段",
|
|
101024
|
-
"Next segment": "下一段",
|
|
101025
|
-
"review code": "审核码",
|
|
101026
|
-
Close: "关闭",
|
|
101027
|
-
Copy: "复制",
|
|
101028
|
-
"{count} pages · {scope} · {pending} pending · {approved} approved · {rejected} omitted": "{count} 页 · {scope} · {pending} 待审核 · {approved} 已批准 · {rejected} 不收录",
|
|
101029
|
-
"All collections": "全部分类",
|
|
101030
|
-
"Set all {count} pages in {group} to {status}?": "将 {group} 的全部 {count} 页设为“{status}”?",
|
|
101031
|
-
"Set all {count} pages to {status}?": "将全部 {count} 页设为“{status}”?",
|
|
101032
|
-
"Choices changed. Copy the updated code before applying.": "选择已变更,请复制更新后的审核码再应用。",
|
|
101033
|
-
"Select at least one page decision; pending pages remain for later review.": "请至少选择一页的审核结果;待审页面留待后续处理。",
|
|
101034
|
-
"Segment {part}/{total} · {length}/980 characters": "第 {part}/{total} 段 · {length}/980 字符",
|
|
101035
|
-
"No review code yet": "暂未生成审核码",
|
|
101036
|
-
"{approved} approved · {rejected} not included · {pending} pending": "{approved} 页批准 · {rejected} 页不收录 · {pending} 页待审核",
|
|
101037
|
-
"Pages not included": "不收录的页面",
|
|
101038
|
-
"Open review results": "查看审核结果",
|
|
101039
|
-
"{count} pending pages remain": "还有 {count} 页待审核",
|
|
101040
|
-
Copied: "已复制",
|
|
101041
|
-
"Copy manually from the textarea": "请从文本框中手动复制",
|
|
101042
|
-
"No draft candidates.": "当前没有待审页面。",
|
|
101043
|
-
"Nothing to review.": "没有需要审核的内容。",
|
|
101044
|
-
"No candidates match the current filters.": "没有符合当前筛选条件的页面。",
|
|
101045
|
-
"Adjust the candidate filters to continue reviewing.": "请调整筛选条件,继续审核。",
|
|
101046
|
-
"{count} items": "{count} 页",
|
|
101047
|
-
"evidence unavailable": "来源不可用",
|
|
101048
|
-
"Source snapshot unavailable. Restore it before approving this candidate, or omit the page.": "来源快照不可用。请先恢复来源再批准,或选择不收录此页。",
|
|
101049
|
-
"Source Markdown": "Markdown 原文",
|
|
101050
|
-
"Source locations": "来源位置",
|
|
101051
|
-
Approve: "批准",
|
|
101052
|
-
Omit: "不收录",
|
|
101053
|
-
"Need changes? Leave this page pending and ask the agent to repair it. Other reviewed pages can be approved.": "需要修改?本页保留待审,并让 Agent 返修。已审核的其他页面可以先批准。"
|
|
101054
|
-
};
|
|
101055
|
-
|
|
101056
|
-
// src/project/reviewCode.ts
|
|
101057
|
-
function createReviewCodeCodec() {
|
|
101058
|
-
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
101059
|
-
function checksum(text7) {
|
|
101060
|
-
let crc = 4294967295;
|
|
101061
|
-
for (let i2 = 0;i2 < text7.length; i2++) {
|
|
101062
|
-
crc ^= text7.charCodeAt(i2);
|
|
101063
|
-
for (let bit = 0;bit < 8; bit++)
|
|
101064
|
-
crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
|
|
101065
|
-
}
|
|
101066
|
-
return ((crc ^ 4294967295) >>> 0).toString(16).padStart(8, "0");
|
|
101067
|
-
}
|
|
101068
|
-
function pack(bytes) {
|
|
101069
|
-
let value = 0, bits = 0, result = "";
|
|
101070
|
-
for (const byte of bytes) {
|
|
101071
|
-
value = value << 8 | byte;
|
|
101072
|
-
bits += 8;
|
|
101073
|
-
while (bits >= 6) {
|
|
101074
|
-
bits -= 6;
|
|
101075
|
-
result += alphabet[value >>> bits & 63];
|
|
101076
|
-
}
|
|
101077
|
-
}
|
|
101078
|
-
if (bits)
|
|
101079
|
-
result += alphabet[value << 6 - bits & 63];
|
|
101080
|
-
return result;
|
|
101081
|
-
}
|
|
101082
|
-
function unpack(text7) {
|
|
101083
|
-
if (!/^[A-Za-z0-9_-]*$/.test(text7))
|
|
101084
|
-
throw new Error("Invalid review code encoding");
|
|
101085
|
-
let value = 0, bits = 0;
|
|
101086
|
-
const bytes = [];
|
|
101087
|
-
for (const char of text7) {
|
|
101088
|
-
value = value << 6 | alphabet.indexOf(char);
|
|
101089
|
-
bits += 6;
|
|
101090
|
-
if (bits >= 8) {
|
|
101091
|
-
bits -= 8;
|
|
101092
|
-
bytes.push(value >>> bits & 255);
|
|
101093
|
-
}
|
|
101094
|
-
}
|
|
101095
|
-
if (pack(bytes) !== text7)
|
|
101096
|
-
throw new Error("Noncanonical review code encoding");
|
|
101097
|
-
return bytes;
|
|
101098
|
-
}
|
|
101099
|
-
function hash3(text7) {
|
|
101100
|
-
if (!/^[a-f0-9]{64}$/.test(text7))
|
|
101101
|
-
throw new Error("Review requires a complete candidate digest");
|
|
101102
|
-
return pack(Array.from({ length: 32 }, (_, i2) => Number.parseInt(text7.slice(i2 * 2, i2 * 2 + 2), 16)));
|
|
101103
|
-
}
|
|
101104
|
-
function unhash(text7) {
|
|
101105
|
-
const bytes = unpack(text7);
|
|
101106
|
-
if (bytes.length !== 32)
|
|
101107
|
-
throw new Error("Invalid candidate digest");
|
|
101108
|
-
return bytes.map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
101109
|
-
}
|
|
101110
|
-
function encode(scope2, idsHash, contentHash2, statuses) {
|
|
101111
|
-
if (!/^[a-z][a-z0-9-]*$/.test(scope2) || !statuses.length || statuses.length > 1e6 || statuses.some((status) => status !== "approved" && status !== "rejected" && status !== "pending")) {
|
|
101112
|
-
throw new Error("Select at least one review decision and keep undecided pages pending");
|
|
101113
|
-
}
|
|
101114
|
-
if (statuses.every((s) => s === "pending"))
|
|
101115
|
-
throw new Error("Select at least one review decision");
|
|
101116
|
-
const mode = statuses.every((s) => s === "approved") ? "a" : statuses.every((s) => s === "rejected") ? "r" : statuses.includes("pending") ? "p" : "b";
|
|
101117
|
-
const bytes = Array(Math.ceil(statuses.length / (mode === "p" ? 4 : 8))).fill(0);
|
|
101118
|
-
if (mode === "p")
|
|
101119
|
-
statuses.forEach((s, i2) => {
|
|
101120
|
-
bytes[i2 >> 2] |= (s === "approved" ? 1 : s === "rejected" ? 2 : 0) << i2 % 4 * 2;
|
|
101121
|
-
});
|
|
101122
|
-
if (mode === "b")
|
|
101123
|
-
statuses.forEach((s, i2) => {
|
|
101124
|
-
if (s === "rejected")
|
|
101125
|
-
bytes[i2 >> 3] |= 1 << i2 % 8;
|
|
101126
|
-
});
|
|
101127
|
-
const body = ["CR1", scope2, statuses.length, hash3(idsHash), hash3(contentHash2), mode, mode === "b" || mode === "p" ? pack(bytes) : ""].join(".");
|
|
101128
|
-
const code = `${body}.${checksum(body)}`;
|
|
101129
|
-
if (code.length <= 980)
|
|
101130
|
-
return [code];
|
|
101131
|
-
const total = Math.ceil(code.length / 900);
|
|
101132
|
-
if (total > 200)
|
|
101133
|
-
throw new Error("Review decisions exceed 200 segments; use a smaller collection scope");
|
|
101134
|
-
const identity = checksum(code);
|
|
101135
|
-
return Array.from({ length: total }, (_, i2) => `CRP1.${identity}.${i2 + 1}.${total}.${code.slice(i2 * 900, (i2 + 1) * 900)}`);
|
|
101136
|
-
}
|
|
101137
|
-
function decode2(input) {
|
|
101138
|
-
if (input.length > 250000)
|
|
101139
|
-
throw new Error("Review code exceeds the supported size");
|
|
101140
|
-
const lines = input.trim().split(/\s+/);
|
|
101141
|
-
let code = lines[0];
|
|
101142
|
-
if (code.startsWith("CRP1.")) {
|
|
101143
|
-
const parts = new Map;
|
|
101144
|
-
let identity = "", total = 0;
|
|
101145
|
-
for (const line of lines) {
|
|
101146
|
-
const match = /^CRP1\.([a-f0-9]{8})\.([1-9][0-9]*)\.([1-9][0-9]*)\.(.+)$/.exec(line);
|
|
101147
|
-
if (!match || line.length > 980)
|
|
101148
|
-
throw new Error("Invalid review code segment");
|
|
101149
|
-
const index2 = Number(match[2]), count2 = Number(match[3]);
|
|
101150
|
-
if (count2 > 200 || index2 > count2 || parts.has(index2) || total && (total !== count2 || identity !== match[1])) {
|
|
101151
|
-
throw new Error("Duplicate or mixed review code segments");
|
|
101152
|
-
}
|
|
101153
|
-
identity = match[1];
|
|
101154
|
-
total = count2;
|
|
101155
|
-
parts.set(index2, match[4]);
|
|
101156
|
-
}
|
|
101157
|
-
if (parts.size !== total)
|
|
101158
|
-
throw new Error(`Missing review code segments: received ${parts.size} of ${total}; collect all segments before applying`);
|
|
101159
|
-
code = Array.from({ length: total }, (_, i2) => parts.get(i2 + 1)).join("");
|
|
101160
|
-
if (checksum(code) !== identity)
|
|
101161
|
-
throw new Error("Review code segment checksum mismatch");
|
|
101162
|
-
} else if (lines.length !== 1 || code.length > 980)
|
|
101163
|
-
throw new Error("Copy each complete review code segment unchanged");
|
|
101164
|
-
const fields = code.split(".");
|
|
101165
|
-
if (fields.length !== 8 || fields[0] !== "CR1" || checksum(fields.slice(0, 7).join(".")) !== fields[7]) {
|
|
101166
|
-
throw new Error("Review code is damaged or unsupported; copy it again from the report");
|
|
101167
|
-
}
|
|
101168
|
-
const [, scope2, countText, ids, content3, mode, data2] = fields;
|
|
101169
|
-
if (!/^[a-z][a-z0-9-]*$/.test(scope2) || !/^[1-9][0-9]*$/.test(countText))
|
|
101170
|
-
throw new Error("Invalid review scope");
|
|
101171
|
-
const count = Number(countText);
|
|
101172
|
-
if (count > 1e6 || !["a", "r", "b", "p"].includes(mode))
|
|
101173
|
-
throw new Error("Invalid review decisions");
|
|
101174
|
-
const bytes = unpack(data2);
|
|
101175
|
-
const perByte = mode === "p" ? 4 : 8;
|
|
101176
|
-
if (mode === "b" || mode === "p" ? bytes.length !== Math.ceil(count / perByte) || count % perByte !== 0 && bytes.at(-1) >>> count % perByte * (mode === "p" ? 2 : 1) !== 0 : data2 !== "") {
|
|
101177
|
-
throw new Error("Invalid review decision bitmap");
|
|
101178
|
-
}
|
|
101179
|
-
const statuses = Array.from({ length: count }, (_, i2) => {
|
|
101180
|
-
if (mode === "p") {
|
|
101181
|
-
const value = bytes[i2 >> 2] >>> i2 % 4 * 2 & 3;
|
|
101182
|
-
if (value === 3)
|
|
101183
|
-
throw new Error("Invalid pending review bitmap");
|
|
101184
|
-
return value === 1 ? "approved" : value === 2 ? "rejected" : "pending";
|
|
101185
|
-
}
|
|
101186
|
-
return mode === "r" || mode === "b" && bytes[i2 >> 3] & 1 << i2 % 8 ? "rejected" : "approved";
|
|
101187
|
-
});
|
|
101188
|
-
if (statuses.every((s) => s === "pending"))
|
|
101189
|
-
throw new Error("Review contains no decisions");
|
|
101190
|
-
return { scope: scope2, count, idsHash: unhash(ids), contentHash: unhash(content3), statuses };
|
|
101191
|
-
}
|
|
101192
|
-
return { encode, decode: decode2 };
|
|
101193
|
-
}
|
|
101101
|
+
// src/project/reviewHtml.ts
|
|
101102
|
+
init_candidateLedger();
|
|
101103
|
+
import { mkdir as mkdir30, writeFile as writeFile24 } from "node:fs/promises";
|
|
101104
|
+
import { dirname as dirname37, isAbsolute as isAbsolute15, join as join87, resolve as resolve28 } from "node:path";
|
|
101194
101105
|
|
|
101195
|
-
// src/project/
|
|
101106
|
+
// src/project/reviewSiteModel.ts
|
|
101107
|
+
init_src2();
|
|
101196
101108
|
init_unified();
|
|
101197
101109
|
init_remark_parse();
|
|
101110
|
+
var import_yaml40 = __toESM(require_dist(), 1);
|
|
101111
|
+
import { readFile as readFile68 } from "node:fs/promises";
|
|
101112
|
+
import { join as join86 } from "node:path";
|
|
101113
|
+
import { execFile as execFile10 } from "node:child_process";
|
|
101114
|
+
import { promisify as promisify10 } from "node:util";
|
|
101115
|
+
import { createHash as createHash24 } from "node:crypto";
|
|
101198
101116
|
|
|
101199
101117
|
// ../../node_modules/.bun/mdast-util-gfm-autolink-literal@2.0.1/node_modules/mdast-util-gfm-autolink-literal/lib/index.js
|
|
101200
101118
|
init_development();
|
|
@@ -104152,7 +104070,45 @@ function remarkGfm(options) {
|
|
|
104152
104070
|
fromMarkdownExtensions.push(gfmFromMarkdown());
|
|
104153
104071
|
toMarkdownExtensions.push(gfmToMarkdown(settings));
|
|
104154
104072
|
}
|
|
104073
|
+
// src/project/reviewSiteModel.ts
|
|
104074
|
+
init_workspace();
|
|
104075
|
+
|
|
104076
|
+
// src/project/reviewFeedback.ts
|
|
104077
|
+
import { readdir as readdir20, readFile as readFile67 } from "node:fs/promises";
|
|
104078
|
+
import { join as join85 } from "node:path";
|
|
104079
|
+
async function readPendingReviewFeedback(root2, candidates) {
|
|
104080
|
+
const directory = join85(root2, ".tmp/context-runtime/review-feedback");
|
|
104081
|
+
let files;
|
|
104082
|
+
try {
|
|
104083
|
+
files = await readdir20(directory);
|
|
104084
|
+
} catch (e) {
|
|
104085
|
+
if (e.code === "ENOENT")
|
|
104086
|
+
return [];
|
|
104087
|
+
throw e;
|
|
104088
|
+
}
|
|
104089
|
+
const pending = new Map(candidates.map((c) => [c.record.candidate_id, c.record.fingerprint]));
|
|
104090
|
+
const results = new Map;
|
|
104091
|
+
const receipts = [];
|
|
104092
|
+
for (const file of files.filter((f) => /^[a-f0-9]+\.json$/u.test(f)).sort()) {
|
|
104093
|
+
const receipt2 = JSON.parse(await readFile67(join85(directory, file), "utf8"));
|
|
104094
|
+
receipts.push(receipt2);
|
|
104095
|
+
}
|
|
104096
|
+
for (const receipt2 of receipts.sort((a, b) => a.created_at.localeCompare(b.created_at))) {
|
|
104097
|
+
for (const repair of receipt2.repairs)
|
|
104098
|
+
if (pending.get(repair.candidate_id) === repair.fingerprint)
|
|
104099
|
+
results.set(repair.candidate_id, { candidate_id: repair.candidate_id, path: repair.path, instruction: repair.instruction, command: repair.command });
|
|
104100
|
+
}
|
|
104101
|
+
return [...results.values()];
|
|
104102
|
+
}
|
|
104103
|
+
|
|
104104
|
+
// src/project/reviewSiteModel.ts
|
|
104105
|
+
init_knowledgeMap2();
|
|
104106
|
+
init_approvedKnowledgeMetadata();
|
|
104107
|
+
init_approvedFileRead();
|
|
104108
|
+
|
|
104155
104109
|
// src/project/reviewMarkdown.ts
|
|
104110
|
+
init_unified();
|
|
104111
|
+
init_remark_parse();
|
|
104156
104112
|
function escapeReviewHtml(value) {
|
|
104157
104113
|
return value.replace(/&/gu, "&").replace(/</gu, "<").replace(/>/gu, ">").replace(/"/gu, """);
|
|
104158
104114
|
}
|
|
@@ -104234,172 +104190,286 @@ function renderReviewMarkdown(markdown, pageTitle) {
|
|
|
104234
104190
|
return (pageTitle && !hasPageHeading ? `<h1>${escapeReviewHtml(pageTitle)}</h1>` : "") + render(tree);
|
|
104235
104191
|
}
|
|
104236
104192
|
|
|
104237
|
-
// src/project/
|
|
104238
|
-
|
|
104239
|
-
|
|
104240
|
-
|
|
104241
|
-
|
|
104242
|
-
|
|
104243
|
-
|
|
104244
|
-
|
|
104245
|
-
|
|
104246
|
-
|
|
104247
|
-
|
|
104248
|
-
|
|
104249
|
-
|
|
104250
|
-
|
|
104251
|
-
|
|
104252
|
-
|
|
104253
|
-
|
|
104254
|
-
|
|
104255
|
-
|
|
104256
|
-
|
|
104257
|
-
|
|
104258
|
-
|
|
104259
|
-
|
|
104260
|
-
}
|
|
104261
|
-
|
|
104262
|
-
|
|
104263
|
-
|
|
104264
|
-
|
|
104265
|
-
|
|
104266
|
-
|
|
104267
|
-
|
|
104268
|
-
|
|
104269
|
-
|
|
104270
|
-
|
|
104271
|
-
|
|
104272
|
-
[
|
|
104273
|
-
|
|
104274
|
-
|
|
104275
|
-
|
|
104276
|
-
|
|
104277
|
-
|
|
104278
|
-
|
|
104279
|
-
|
|
104280
|
-
|
|
104281
|
-
|
|
104282
|
-
|
|
104283
|
-
|
|
104284
|
-
|
|
104285
|
-
|
|
104286
|
-
|
|
104287
|
-
|
|
104288
|
-
|
|
104289
|
-
|
|
104290
|
-
|
|
104291
|
-
|
|
104292
|
-
|
|
104293
|
-
.
|
|
104294
|
-
|
|
104295
|
-
|
|
104296
|
-
|
|
104297
|
-
|
|
104298
|
-
|
|
104299
|
-
|
|
104300
|
-
|
|
104301
|
-
|
|
104302
|
-
|
|
104303
|
-
|
|
104304
|
-
|
|
104305
|
-
|
|
104306
|
-
|
|
104307
|
-
|
|
104308
|
-
|
|
104309
|
-
|
|
104310
|
-
|
|
104311
|
-
|
|
104312
|
-
|
|
104313
|
-
|
|
104314
|
-
|
|
104315
|
-
|
|
104316
|
-
|
|
104317
|
-
|
|
104318
|
-
|
|
104319
|
-
|
|
104320
|
-
|
|
104321
|
-
|
|
104322
|
-
|
|
104323
|
-
|
|
104324
|
-
|
|
104325
|
-
|
|
104326
|
-
|
|
104327
|
-
|
|
104328
|
-
|
|
104329
|
-
|
|
104330
|
-
|
|
104331
|
-
|
|
104332
|
-
|
|
104333
|
-
.
|
|
104334
|
-
.
|
|
104335
|
-
|
|
104336
|
-
|
|
104337
|
-
|
|
104338
|
-
|
|
104339
|
-
|
|
104340
|
-
|
|
104341
|
-
|
|
104342
|
-
|
|
104343
|
-
|
|
104344
|
-
|
|
104345
|
-
|
|
104346
|
-
|
|
104347
|
-
|
|
104348
|
-
|
|
104349
|
-
|
|
104350
|
-
|
|
104351
|
-
|
|
104352
|
-
|
|
104353
|
-
|
|
104354
|
-
|
|
104355
|
-
|
|
104356
|
-
|
|
104357
|
-
|
|
104358
|
-
|
|
104359
|
-
|
|
104360
|
-
|
|
104361
|
-
|
|
104362
|
-
|
|
104363
|
-
|
|
104364
|
-
.
|
|
104365
|
-
|
|
104366
|
-
|
|
104367
|
-
|
|
104368
|
-
|
|
104369
|
-
|
|
104370
|
-
|
|
104371
|
-
.
|
|
104372
|
-
.
|
|
104373
|
-
.
|
|
104374
|
-
.
|
|
104375
|
-
|
|
104376
|
-
|
|
104377
|
-
|
|
104378
|
-
|
|
104379
|
-
|
|
104380
|
-
|
|
104381
|
-
|
|
104382
|
-
|
|
104383
|
-
|
|
104384
|
-
|
|
104385
|
-
|
|
104386
|
-
|
|
104387
|
-
|
|
104388
|
-
|
|
104389
|
-
|
|
104390
|
-
|
|
104391
|
-
|
|
104392
|
-
|
|
104393
|
-
|
|
104394
|
-
.
|
|
104395
|
-
|
|
104396
|
-
|
|
104397
|
-
|
|
104193
|
+
// src/project/reviewSiteModel.ts
|
|
104194
|
+
var hash3 = (s) => createHash24("sha256").update(s).digest("hex");
|
|
104195
|
+
var body = (s) => s.replace(/^---\r?\n[\s\S]*?\r?\n---\s*/u, "").replace(/<!--[^]*?-->/gu, "").trim();
|
|
104196
|
+
var title = (s, fallback) => {
|
|
104197
|
+
const front = /^---\r?\n([\s\S]*?)\r?\n---/u.exec(s);
|
|
104198
|
+
const value = front ? import_yaml40.parse(front[1])?.title : undefined;
|
|
104199
|
+
return typeof value === "string" ? value : /^#\s+(.+)$/mu.exec(s)?.[1] ?? fallback;
|
|
104200
|
+
};
|
|
104201
|
+
async function optional2(path3) {
|
|
104202
|
+
try {
|
|
104203
|
+
return await readFile68(path3, "utf8");
|
|
104204
|
+
} catch (e) {
|
|
104205
|
+
if (e.code === "ENOENT")
|
|
104206
|
+
return;
|
|
104207
|
+
throw e;
|
|
104208
|
+
}
|
|
104209
|
+
}
|
|
104210
|
+
async function reviewSiteBaselineHash(root2, reviewedPaths) {
|
|
104211
|
+
const files = await readApprovedMarkdownFiles(root2);
|
|
104212
|
+
return hash3(JSON.stringify([
|
|
104213
|
+
await optional2(join86(root2, "src/knowledge-map.yaml")) ?? null,
|
|
104214
|
+
files.map((f) => [f.relPath, reviewedPaths.includes(f.relPath) ? hash3(f.content) : title(f.content, f.relPath)]).sort((a, b) => a[0].localeCompare(b[0]))
|
|
104215
|
+
]));
|
|
104216
|
+
}
|
|
104217
|
+
function reviewBodyDiff(previous3, next) {
|
|
104218
|
+
function blocks(markdown) {
|
|
104219
|
+
const text9 = body(markdown);
|
|
104220
|
+
const tree = unified().use(remarkParse).use(remarkGfm).parse(text9);
|
|
104221
|
+
const definitions = tree.children.filter((node3) => node3.type === "definition").map((node3) => text9.slice(node3.position?.start.offset, node3.position?.end.offset)).join(`
|
|
104222
|
+
`);
|
|
104223
|
+
return tree.children.filter((node3) => node3.type !== "definition").map((node3) => renderReviewMarkdown(text9.slice(node3.position?.start.offset, node3.position?.end.offset) + `
|
|
104224
|
+
|
|
104225
|
+
` + definitions));
|
|
104226
|
+
}
|
|
104227
|
+
const before = blocks(previous3), after = blocks(next);
|
|
104228
|
+
const remaining = [...before];
|
|
104229
|
+
let omitted = false;
|
|
104230
|
+
const result = after.map((block) => {
|
|
104231
|
+
const exact = remaining.indexOf(block);
|
|
104232
|
+
if (exact >= 0) {
|
|
104233
|
+
remaining.splice(exact, 1);
|
|
104234
|
+
if (/^<h[1-6]>/u.test(block)) {
|
|
104235
|
+
omitted = false;
|
|
104236
|
+
return block;
|
|
104237
|
+
}
|
|
104238
|
+
if (omitted)
|
|
104239
|
+
return "";
|
|
104240
|
+
omitted = true;
|
|
104241
|
+
return '<div class="unchanged" data-label="unchanged">Unchanged content omitted.</div>';
|
|
104242
|
+
}
|
|
104243
|
+
omitted = false;
|
|
104244
|
+
return `<section class="changed"><span class="badge modify">Modify</span>${block}</section>`;
|
|
104245
|
+
});
|
|
104246
|
+
if (remaining.length)
|
|
104247
|
+
result.push(`<section class="changed"><span class="badge modify">Modify</span><details open><summary data-label="removed">Previous or removed content</summary>${remaining.join(`
|
|
104248
|
+
`)}</details></section>`);
|
|
104249
|
+
return result.join(`
|
|
104250
|
+
`);
|
|
104251
|
+
}
|
|
104252
|
+
async function collectReviewSiteModel(root2, candidates) {
|
|
104253
|
+
const pendingFeedback = new Map((await readPendingReviewFeedback(root2, candidates)).map((r) => [r.candidate_id, r.instruction]));
|
|
104254
|
+
const current2 = await readKnowledgeMap(root2);
|
|
104255
|
+
const metadata = await readApprovedKnowledgeMetadataIndex(root2);
|
|
104256
|
+
const articles = metadata.structure?.articles ?? [];
|
|
104257
|
+
const files = await readApprovedMarkdownFiles(root2);
|
|
104258
|
+
const byPath = new Map(files.map((f) => [f.relPath, f.content]));
|
|
104259
|
+
const pages = files.map((f) => ({
|
|
104260
|
+
id: articles.find((a) => a.path === f.relPath)?.article_id ?? f.relPath,
|
|
104261
|
+
path: f.relPath,
|
|
104262
|
+
title: title(f.content, f.relPath),
|
|
104263
|
+
change: "unchanged",
|
|
104264
|
+
html: "",
|
|
104265
|
+
sources: []
|
|
104266
|
+
}));
|
|
104267
|
+
for (const { record: r } of candidates) {
|
|
104268
|
+
const found = pages.find((p) => p.id === r.article_id || p.path === (r.approved_revision?.previous_path ?? r.path));
|
|
104269
|
+
const old = found && byPath.get(found.path);
|
|
104270
|
+
const next = r.indexer_candidate.sections.map((s) => s.markdown).join(`
|
|
104271
|
+
|
|
104272
|
+
`);
|
|
104273
|
+
const page = {
|
|
104274
|
+
id: r.article_id,
|
|
104275
|
+
candidate_id: r.candidate_id,
|
|
104276
|
+
title: r.review.title,
|
|
104277
|
+
path: r.path,
|
|
104278
|
+
...pendingFeedback.has(r.candidate_id) ? { revisionInstruction: pendingFeedback.get(r.candidate_id) } : {},
|
|
104279
|
+
...found && found.path !== r.path ? { previousPath: found.path } : {},
|
|
104280
|
+
change: found ? "modify" : "new",
|
|
104281
|
+
html: old === undefined ? renderReviewMarkdown(next.replace(/^# [^\n]+\n*/u, "")) : reviewBodyDiff(body(old).replace(/^# [^\n]+\n*/u, ""), next.replace(/^# [^\n]+\n*/u, "")),
|
|
104282
|
+
sources: [...r.source_refs, ...r.indexer_candidate.sections.flatMap((s) => s.references.map((ref2) => JSON.stringify(ref2)))]
|
|
104283
|
+
};
|
|
104284
|
+
if (found)
|
|
104285
|
+
pages.splice(pages.indexOf(found), 1, page);
|
|
104286
|
+
else
|
|
104287
|
+
pages.push(page);
|
|
104288
|
+
}
|
|
104289
|
+
let baseline = current2, navigationBaseline = files.length ? "current" : "empty";
|
|
104290
|
+
if (files.length) {
|
|
104291
|
+
try {
|
|
104292
|
+
const { stdout } = await promisify10(execFile10)("git", ["show", "HEAD:./src/knowledge-map.yaml"], { cwd: root2, timeout: 5000, maxBuffer: 4 * 1024 * 1024 });
|
|
104293
|
+
baseline = validateKnowledgeMap(import_yaml40.parse(stdout));
|
|
104294
|
+
navigationBaseline = "git-head";
|
|
104295
|
+
} catch {}
|
|
104296
|
+
}
|
|
104297
|
+
const nodes = (current2?.entries ?? []).map((n) => {
|
|
104298
|
+
const old = baseline?.entries.find((b) => b.key === n.key);
|
|
104299
|
+
const page = n.target && pages.find((p) => p.id === n.target?.artifact_ref);
|
|
104300
|
+
return {
|
|
104301
|
+
key: n.key,
|
|
104302
|
+
parent: n.parent ?? null,
|
|
104303
|
+
title: n.title,
|
|
104304
|
+
order: n.order ?? 0,
|
|
104305
|
+
...page ? { page: page.id } : {},
|
|
104306
|
+
...old && old.title !== n.title ? { oldTitle: old.title } : {},
|
|
104307
|
+
change: navigationBaseline === "empty" || !old ? "new" : JSON.stringify(old) !== JSON.stringify(n) ? "modify" : "unchanged"
|
|
104308
|
+
};
|
|
104309
|
+
});
|
|
104310
|
+
for (const n of baseline?.entries ?? [])
|
|
104311
|
+
if (!nodes.some((v) => v.key === n.key))
|
|
104312
|
+
nodes.push({
|
|
104313
|
+
key: n.key,
|
|
104314
|
+
parent: n.parent ?? null,
|
|
104315
|
+
title: n.title,
|
|
104316
|
+
order: n.order ?? 0,
|
|
104317
|
+
removed: true,
|
|
104318
|
+
change: "modify"
|
|
104319
|
+
});
|
|
104320
|
+
const unplaced = pages.filter((p) => p.candidate_id && !nodes.some((n) => n.page === p.id));
|
|
104321
|
+
if (unplaced.length) {
|
|
104322
|
+
nodes.push({ key: "review-unplaced", parent: null, title: "Unplaced articles", order: Number.MAX_SAFE_INTEGER, change: "new" });
|
|
104323
|
+
for (const [i2, p] of unplaced.entries())
|
|
104324
|
+
nodes.push({ key: `review-page-${p.id}`, parent: "review-unplaced", title: p.title, order: i2, page: p.id, change: p.change });
|
|
104325
|
+
}
|
|
104326
|
+
const pkgText = await optional2(join86(root2, "package.json"));
|
|
104327
|
+
const pkg = pkgText ? JSON.parse(pkgText) : {};
|
|
104328
|
+
const project = await optional2(join86(root2, "src/index.ts")) === undefined ? undefined : await loadContextProjectModule(root2);
|
|
104329
|
+
const siteTitle = project?.project.packages.flatMap((p) => p.kind === "package.kb" && p.site?.title ? [p.site.title] : [])[0];
|
|
104330
|
+
return { title: siteTitle ?? pkg.name ?? "Knowledge review", baselineHash: await reviewSiteBaselineHash(root2, candidates.map((c) => c.record.approved_revision?.previous_path ?? c.record.path)), nodes, pages, navigationBaseline };
|
|
104331
|
+
}
|
|
104332
|
+
var reviewHtmlJson = (value) => JSON.stringify(value).replace(/</gu, "\\u003c").replace(/\u2028/gu, "\\u2028").replace(/\u2029/gu, "\\u2029");
|
|
104333
|
+
|
|
104334
|
+
// src/project/reviewFeedbackCode.ts
|
|
104335
|
+
function createReviewFeedbackCodec() {
|
|
104336
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
104337
|
+
function checksum(text9) {
|
|
104338
|
+
let crc = 4294967295;
|
|
104339
|
+
for (let i2 = 0;i2 < text9.length; i2++) {
|
|
104340
|
+
crc ^= text9.charCodeAt(i2);
|
|
104341
|
+
for (let j = 0;j < 8; j++)
|
|
104342
|
+
crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
|
|
104343
|
+
}
|
|
104344
|
+
return ((crc ^ 4294967295) >>> 0).toString(16).padStart(8, "0");
|
|
104345
|
+
}
|
|
104346
|
+
function validate(value) {
|
|
104347
|
+
if (!value || !/^[a-z][a-z0-9-]*$/.test(value.scope) || ![value.idsHash, value.contentHash, value.baselineHash].every((h) => typeof h === "string" && /^[a-f0-9]{64}$/.test(h)) || !Array.isArray(value.statuses) || !value.statuses.length || value.statuses.length > 1e5 || value.statuses.some((s) => !["approved", "rejected", "pending", "revised"].includes(s)) || value.statuses.every((s) => s === "pending") || !Array.isArray(value.repairs))
|
|
104348
|
+
throw new Error("Invalid review feedback scope or decisions");
|
|
104349
|
+
const seen = new Set;
|
|
104350
|
+
for (const repair of value.repairs) {
|
|
104351
|
+
if (!repair || !Number.isInteger(repair.index) || seen.has(repair.index) || value.statuses[repair.index] !== "revised" || typeof repair.instruction !== "string" || !repair.instruction.trim() || repair.instruction.length > 20000)
|
|
104352
|
+
throw new Error("Invalid or conflicting revision instruction");
|
|
104353
|
+
seen.add(repair.index);
|
|
104354
|
+
}
|
|
104355
|
+
if (value.statuses.filter((s) => s === "revised").length !== seen.size)
|
|
104356
|
+
throw new Error("Missing revision instruction");
|
|
104357
|
+
return value;
|
|
104358
|
+
}
|
|
104359
|
+
function encode(value) {
|
|
104360
|
+
validate(value);
|
|
104361
|
+
const bytes = new TextEncoder().encode(JSON.stringify({ scope: value.scope, idsHash: value.idsHash, contentHash: value.contentHash, baselineHash: value.baselineHash, statuses: value.statuses }));
|
|
104362
|
+
let bits = 0, carry = 0, data2 = "";
|
|
104363
|
+
for (const byte of bytes) {
|
|
104364
|
+
carry = carry << 8 | byte;
|
|
104365
|
+
bits += 8;
|
|
104366
|
+
while (bits >= 6) {
|
|
104367
|
+
bits -= 6;
|
|
104368
|
+
data2 += alphabet[carry >>> bits & 63];
|
|
104369
|
+
}
|
|
104370
|
+
}
|
|
104371
|
+
if (bits)
|
|
104372
|
+
data2 += alphabet[carry << 6 - bits & 63];
|
|
104373
|
+
const body2 = `CR2.${data2}`;
|
|
104374
|
+
const repairs = value.repairs.map((r) => JSON.stringify([r.index, r.instruction])).join(`
|
|
104375
|
+
`);
|
|
104376
|
+
const trailer = repairs ? `
|
|
104377
|
+
${repairs}` : "";
|
|
104378
|
+
const code3 = `${body2}.${checksum(body2 + trailer)}${trailer}`;
|
|
104379
|
+
if (code3.length > 250000)
|
|
104380
|
+
throw new Error("Review feedback is too large; shorten instructions or review a smaller scope");
|
|
104381
|
+
return code3;
|
|
104382
|
+
}
|
|
104383
|
+
function decode2(raw) {
|
|
104384
|
+
const code3 = raw.trim();
|
|
104385
|
+
if (code3.length > 250000)
|
|
104386
|
+
throw new Error("Review feedback exceeds supported size");
|
|
104387
|
+
const [header, ...lines] = code3.split(`
|
|
104388
|
+
`);
|
|
104389
|
+
const trailer = lines.length ? `
|
|
104390
|
+
${lines.join(`
|
|
104391
|
+
`)}` : "";
|
|
104392
|
+
const match = /^CR2\.([A-Za-z0-9_-]+)\.([a-f0-9]{8})$/.exec(header);
|
|
104393
|
+
if (!match || checksum(`CR2.${match[1]}` + trailer) !== match[2])
|
|
104394
|
+
throw new Error("Review feedback is damaged or incomplete; copy the full code again");
|
|
104395
|
+
let bits = 0, carry = 0;
|
|
104396
|
+
const bytes = [];
|
|
104397
|
+
for (const char of match[1]) {
|
|
104398
|
+
carry = carry << 6 | alphabet.indexOf(char);
|
|
104399
|
+
bits += 6;
|
|
104400
|
+
if (bits >= 8) {
|
|
104401
|
+
bits -= 8;
|
|
104402
|
+
bytes.push(carry >>> bits & 255);
|
|
104403
|
+
}
|
|
104404
|
+
}
|
|
104405
|
+
const headerValue = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(new Uint8Array(bytes)));
|
|
104406
|
+
const value = validate({ ...headerValue, repairs: lines.map((line) => {
|
|
104407
|
+
const row = JSON.parse(line);
|
|
104408
|
+
if (!Array.isArray(row) || row.length !== 2)
|
|
104409
|
+
throw new Error("Invalid revision instruction line");
|
|
104410
|
+
return { index: row[0], instruction: row[1] };
|
|
104411
|
+
}) });
|
|
104412
|
+
if (encode(value) !== code3)
|
|
104413
|
+
throw new Error("Noncanonical review feedback encoding");
|
|
104414
|
+
return value;
|
|
104398
104415
|
}
|
|
104416
|
+
return { encode, decode: decode2 };
|
|
104417
|
+
}
|
|
104418
|
+
|
|
104419
|
+
// src/project/reviewSiteClient.ts
|
|
104420
|
+
var REVIEW_SITE_CLIENT = String.raw`
|
|
104421
|
+
const $=id=>document.getElementById(id);
|
|
104422
|
+
let language=(navigator.languages?.[0]||navigator.language||'en').startsWith('zh')?'zh':'en';
|
|
104423
|
+
const words={newRoots:['新增一级目录','New top-level categories'],ackRoots:['我已知晓本次新增一级目录','I acknowledge the new top-level categories'],home:['待审核内容','Pages to review'],unchanged:['本次未变更内容略。','Unchanged content omitted.'],previous:['查看旧文本','Previous text'],removed:['删除的内容','Removed content'],unplaced:['待落位文章','Unplaced articles'],approve:['批准这篇','Approve page'],reject:['拒绝这篇','Reject page'],revise:['修订','Revise'],approved:['已批准','Approved'],rejected:['已拒绝','Rejected'],revised:['已修订','Revision requested'],cancel:['取消','Cancel'],copy:['复制审核码','Copy review code'],allApprove:['全部批准','Approve all'],allReject:['全部拒绝','Reject all'],note:['输入修订意见','Enter revision instructions'],guide:['逐篇阅读并批准或拒绝后,复制审核码回复给 Agent。需要修改的文章请填写修订意见。','Read each page, approve or reject, then copy the review code back to your Agent. Enter instructions for pages needing revision.'],known:['知道了','Got it'],close:['关闭','Close'],notReviewed:['尚未完成审核','Not yet reviewed'],confirmAll:['建议逐篇阅读并确认。除非已读完所有待审核文章,否则请勿一次性全部批准。已有拒绝和修订意见将保留。','Read and confirm each page. Approve all only after reading every pending page. Existing rejections and revisions are preserved.'],confirm:['已阅读,全部批准','Read all, approve'],copied:['审核码已复制','Review code copied'],failed:['复制失败,请手动复制下方完整内容','Copy failed. Copy the complete text below manually.'],instructions:['请回到和 Agent 的会话窗口粘贴已复制内容进行回复即可继续~','Return to your conversation with the Agent and reply with the copied content to continue.'],long:['超过 1000 字符,飞书表单可能不接受。飞书场景下建议 @Bot 后粘贴回复。','Over 1,000 characters: a Feishu form may reject it. Mention @Bot and paste it in a reply instead.'],files:['预期工作区变化','Expected workspace changes'],navigate:['目录与文章','Directories and articles'],pendingNew:['未审批的新增文章','Pending new pages'],pendingModify:['未审批的修改文章','Pending modified pages'],processed:['已经审核和修订的文章','Reviewed or revision requested'],noSelection:['请先选择审核结果或填写修订意见','Select a decision or enter revision instructions first'],baseline:['无 Git 导航基线,目录沿用当前工作区;未推断历史目录变化。','No Git navigation baseline. Current navigation is shown without inferred historic changes.'],placement:['待落位文章不是新的站点栏目;请先按既有分类完成导航规划。','Unplaced articles are not a new site category. Finish their placement in the existing navigation first.']};
|
|
104424
|
+
const t=k=>words[k]?.[language==='zh'?0:1]||k;
|
|
104425
|
+
const escape=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
|
104426
|
+
const decisions=new Map(),notes=new Map(),expanded=new Set();let selected=null,active=null;
|
|
104427
|
+
const candidates=DATA.pages.filter(p=>p.candidate_id),ordered=[...candidates].sort((a,b)=>a.candidate_id<b.candidate_id?-1:1);
|
|
104428
|
+
for(const p of candidates)if(p.revisionInstruction){decisions.set(p.candidate_id,'revised');notes.set(p.candidate_id,p.revisionInstruction)}
|
|
104429
|
+
const badge=change=>change==='unchanged'?'':'<span class="badge '+change+'">'+(change==='new'?'New':change==='modify'?'Modify':t(change))+'</span>';
|
|
104430
|
+
const state=p=>decisions.get(p.candidate_id)||'pending';
|
|
104431
|
+
function descendants(key,seen=new Set()){if(seen.has(key))return[];seen.add(key);return DATA.nodes.filter(n=>n.parent===key).flatMap(n=>[n,...descendants(n.key,seen)])}
|
|
104432
|
+
function nodeChange(n){const changes=[n.change,...[n,...descendants(n.key)].flatMap(v=>{const p=DATA.pages.find(p=>p.id===v.page);return p?[p.change]:[]}),...descendants(n.key).map(v=>v.change)];return changes.includes('modify')?'modify':changes.includes('new')?'new':'unchanged'}
|
|
104433
|
+
function nodeTitle(n){return n.key==='review-unplaced'?t('unplaced'):n.title}
|
|
104434
|
+
function nodeLabel(n){return (n.removed?'<del>':'')+escape(nodeTitle(n))+(n.removed?'</del>':'')+(n.oldTitle?'<small class="old-title"> ← '+escape(n.oldTitle)+'</small>':'')}
|
|
104435
|
+
function totals(){const result={new:0,modify:0,approved:0,rejected:0,revised:0,pending:0};for(const p of candidates){const s=state(p);result[s]++;if(s==='pending')result[p.change==='new'?'new':'modify']++}return result}
|
|
104436
|
+
function updateCounts(){const c=totals();$('counts').textContent=c.new+' New / '+c.modify+' Modify / '+(c.approved+c.rejected+c.revised)+' Confirm';$('counter-pop').innerHTML='<div class="counter-grid"><div><b>'+c.new+'</b>'+t('pendingNew')+'</div><div><b>'+c.modify+'</b>'+t('pendingModify')+'</div></div><p>'+t('processed')+': '+(c.approved+c.rejected+c.revised)+'</p><small>'+t('approved')+' '+c.approved+' · '+t('rejected')+' '+c.rejected+' · '+t('revised')+' '+c.revised+'</small>'}
|
|
104437
|
+
function button(n,depth){const change=nodeChange(n),page=DATA.pages.find(p=>p.id===n.page);return '<button data-node="'+escape(n.key)+'" class="node '+change+(selected===n.page?' selected':'')+'" style="padding-left:'+(16+14*depth)+'px">'+nodeLabel(n)+badge(change)+(page&&page.candidate_id&&state(page)!=='pending'?badge(state(page)):'')+(DATA.nodes.some(c=>c.parent===n.key)?'<span class="caret">›</span>':'')+'</button>'}
|
|
104438
|
+
function renderNav(){const roots=DATA.nodes.filter(n=>!n.parent).sort((a,b)=>a.order-b.order);$('top').innerHTML=roots.map(n=>'<button data-root="'+escape(n.key)+'" class="'+nodeChange(n)+(active===n.key?' active':'')+'">'+nodeLabel(n)+badge(nodeChange(n))+'</button>').join('');let html='';function walk(key,depth,seen=new Set()){if(seen.has(key))return;seen.add(key);for(const n of DATA.nodes.filter(n=>n.parent===key).sort((a,b)=>a.order-b.order)){html+=button(n,depth);if(expanded.has(n.key)||n.page===selected||descendants(n.key).some(d=>d.page===selected))walk(n.key,depth+1,seen)}}if(active)walk(active,0);else html=roots.map(n=>button(n,0)).join('');$('tree').innerHTML=html}
|
|
104439
|
+
function rootFor(page){let node=DATA.nodes.find(n=>n.page===page);const seen=new Set();while(node?.parent&&!seen.has(node.key)){seen.add(node.key);node=DATA.nodes.find(n=>n.key===node.parent)}return node?.key||null}
|
|
104440
|
+
function showPage(id){selected=id;active=rootFor(id)||active;render()}
|
|
104441
|
+
function localizeBody(){document.querySelectorAll('[data-label]').forEach(el=>{el.textContent=t(el.dataset.label)})}
|
|
104442
|
+
function workspaceTree(){const tree={};for(const p of candidates){let node=tree;for(const part of ('knowledge/'+p.path).split('/'))node=node[part]??=( {} );node.$page=p}function lines(node,level=0){return Object.entries(node).filter(([k])=>k!=='$page').sort(([a],[b])=>a.localeCompare(b)).map(([k,v])=>'<div style="padding-left:'+level*18+'px">'+(v.$page?'<button data-page="'+escape(v.$page.id)+'">'+escape(k)+'</button>'+badge(v.$page.change)+(state(v.$page)!=='pending'?badge(state(v.$page)):''):escape(k)+'/')+'</div>'+lines(v,level+1)).join('')}return '<div class="workspace-tree">'+lines(tree)+'</div>'}
|
|
104443
|
+
function renderHome(){const c=totals();$('article').innerHTML='<h1>'+t('home')+'</h1><p class="stats">'+candidates.length+' '+(language==='zh'?'篇候选正文':'candidate pages')+' · '+c.approved+' '+t('approved')+' '+badge('new')+' '+badge('modify')+'</p>'+(DATA.navigationBaseline==='current'?'<p class="note">'+t('baseline')+'</p>':'')+(DATA.nodes.some(n=>n.key==='review-unplaced')?'<p class="note">'+t('placement')+'</p>':'')+'<h2>'+t('navigate')+'</h2>'+DATA.nodes.filter(n=>!n.parent).map(n=>{const ids=new Set([n,...descendants(n.key)].map(x=>x.page));const pages=candidates.filter(p=>ids.has(p.id));return pages.length?'<section class="home-group"><h3>'+nodeLabel(n)+badge(nodeChange(n))+'</h3><div class="cards">'+pages.map(p=>'<button data-page="'+escape(p.id)+'">'+escape(p.title)+badge(p.change)+(state(p)!=='pending'?badge(state(p)):'')+'</button>').join('')+'</div></section>':''}).join('')+'<h2>'+t('files')+'</h2>'+workspaceTree()}
|
|
104444
|
+
function controls(){const p=DATA.pages.find(p=>p.id===selected),s=p?state(p):'pending';$('footer').hidden=!p?.candidate_id;if(!p?.candidate_id)return;$('revision-note').value=notes.get(p.candidate_id)||'';$('revision-note').placeholder=t('note');$('revision-note').disabled=s==='approved'||s==='rejected';for(const [id,v,label]of[['revise-btn','revised','revise'],['reject-btn','rejected','reject'],['approve-btn','approved','approve']]){const b=$(id);b.disabled=s!=='pending'&&s!==v;b.className='btn '+(s===v?'chosen':v==='approved'?'primary':'');b.innerHTML=s===v?'<span class="normal">'+t(v)+'</span><span class="hover-label">'+t('cancel')+'</span>':t(label);b.title=s===v?t('cancel'):''}}
|
|
104445
|
+
function render(){document.body.classList.toggle('home',selected===null);renderNav();updateCounts();if(selected===null)renderHome();else{const p=DATA.pages.find(p=>p.id===selected);$('article').innerHTML=p?'<h1>'+escape(p.title)+badge(p.change)+(p.candidate_id&&state(p)!=='pending'?badge(state(p)):'')+'</h1>'+(p.previousPath?'<p class="note">'+escape(p.previousPath)+' → '+escape(p.path)+'</p>':'')+(p.candidate_id?p.html:'<div class="unchanged">'+t('unchanged')+'</div>')+(p.sources.length?'<details><summary>'+(language==='zh'?'来源引用':'Sources')+'</summary><ul>'+p.sources.map(s=>'<li>'+escape(s)+'</li>').join('')+'</ul></details>':''):''}controls();localizeBody()}
|
|
104446
|
+
function setDecision(id,value){const current=decisions.get(id);if(current===value){decisions.delete(id);notes.delete(id)}else if(!current){if(value==='revised'){$('revision-note').focus();return}decisions.set(id,value)}render()}
|
|
104447
|
+
function setAllDecision(value){for(const p of candidates)if(!decisions.has(p.candidate_id))decisions.set(p.candidate_id,value);render()}
|
|
104448
|
+
function payloadText(){const statuses=ordered.map(p=>state(p));return feedbackCodec.encode({scope:SCOPE.label,idsHash:SCOPE.ids_sha256,contentHash:SCOPE.candidates_sha256,baselineHash:DATA.baselineHash,statuses,repairs:ordered.flatMap((p,index)=>state(p)==='revised'?[{index,instruction:notes.get(p.candidate_id)}]:[])})}
|
|
104449
|
+
async function copyPayload(){dismissGuide();const c=totals();$('copy-warning').textContent='';$('payload').value='';if(c.pending===candidates.length){$('copy-title').textContent=t('noSelection');$('payload').value=''}else{try{const text=payloadText();$('payload').value=text;await navigator.clipboard.writeText(text);$('copy-title').textContent=t('copied')}catch(e){$('copy-title').textContent=t('failed');if(!$('payload').value)$('payload').value=String(e.message)}}$('copy-summary').textContent=t('approved')+' '+c.approved+' · '+t('rejected')+' '+c.rejected+' · '+t('revised')+' '+c.revised+' · '+t('notReviewed')+' '+c.pending;$('copy-instructions').textContent=t('instructions');$('copy-warning').textContent=Array.from($('payload').value).length>1000?t('long'):'';$('copy-dialog').showModal()}
|
|
104450
|
+
let guideTimer;function dismissGuide(){clearInterval(guideTimer);$('copy-guide').hidden=true}
|
|
104451
|
+
function labels(){$('all-approved').textContent=t('allApprove');$('all-rejected').textContent=t('allReject');$('payload-open').textContent=t('copy');$('guide-text').textContent=t('guide');$('guide-close').textContent=t('known');$('bulk-title').textContent=t('allApprove');$('bulk-message').textContent=t('confirmAll');$('bulk-cancel').textContent=t('cancel');$('bulk-confirm').textContent=t('confirm');$('payload-close').textContent=t('close');$('language').textContent=language==='zh'?'EN':'中文'}
|
|
104452
|
+
document.addEventListener('click',e=>{const b=e.target.closest('button');if(!b)return;if(b.dataset.page){showPage(b.dataset.page);window.scrollTo(0,0)}if(b.dataset.root){active=b.dataset.root;const n=DATA.nodes.find(n=>n.key===active);selected=n?.page||[...descendants(active)].find(d=>d.page)?.page||null;render()}if(b.dataset.node){const n=DATA.nodes.find(n=>n.key===b.dataset.node);if(n.page)showPage(n.page);else{expanded.has(n.key)?expanded.delete(n.key):expanded.add(n.key);renderNav()}}});
|
|
104453
|
+
$('home').onclick=()=>{selected=null;active=null;render();window.scrollTo(0,0)};
|
|
104454
|
+
$('revision-note').oninput=e=>{const p=DATA.pages.find(p=>p.id===selected);if(!p?.candidate_id)return;const value=e.target.value;notes.set(p.candidate_id,value);if(value.trim())decisions.set(p.candidate_id,'revised');else decisions.delete(p.candidate_id);controls();renderNav();updateCounts();$('article').querySelectorAll('h1 > .approved,h1 > .revised,h1 > .rejected').forEach(e=>e.remove());$('article').querySelector('h1')?.insertAdjacentHTML('beforeend',value.trim()?badge('revised'):'')};
|
|
104455
|
+
for(const [id,value]of[['revise-btn','revised'],['reject-btn','rejected'],['approve-btn','approved']])$(id).onclick=()=>{const p=DATA.pages.find(p=>p.id===selected);if(p?.candidate_id)setDecision(p.candidate_id,value)};
|
|
104456
|
+
const addedRoots=DATA.nodes.filter(n=>!n.parent&&n.change==='new'&&!n.removed&&n.key!=='review-unplaced');
|
|
104457
|
+
let bulkTimer,bulkDeadline=0;
|
|
104458
|
+
function updateBulkConfirmation(){const remaining=addedRoots.length?Math.max(0,Math.ceil((bulkDeadline-Date.now())/1000)):0;$('bulk-confirm').disabled=addedRoots.length>0&&(!$('bulk-ack').checked||remaining>0);$('bulk-confirm').textContent=t('confirm')+(remaining?' ('+remaining+'s)':'');return remaining}
|
|
104459
|
+
function openBulkConfirmation(){clearInterval(bulkTimer);$('bulk-ack').checked=false;$('bulk-roots').hidden=!addedRoots.length;$('bulk-roots-title').textContent=t('newRoots');$('bulk-roots-list').innerHTML=addedRoots.map(n=>'<li>'+escape(nodeTitle(n))+'</li>').join('');$('bulk-ack-label').textContent=t('ackRoots');bulkDeadline=Date.now()+8000;updateBulkConfirmation();$('bulk-dialog').showModal();if(addedRoots.length)bulkTimer=setInterval(()=>{if(!updateBulkConfirmation())clearInterval(bulkTimer)},200)}
|
|
104460
|
+
$('bulk-ack').onchange=updateBulkConfirmation;
|
|
104461
|
+
$('bulk-dialog').onclose=()=>clearInterval(bulkTimer);
|
|
104462
|
+
$('all-approved').onclick=openBulkConfirmation;$('bulk-confirm').onclick=()=>{updateBulkConfirmation();if(!$('bulk-dialog').open||$('bulk-confirm').disabled)return;setAllDecision('approved');clearInterval(bulkTimer);$('bulk-dialog').close()};$('bulk-cancel').onclick=()=>{clearInterval(bulkTimer);$('bulk-dialog').close()};$('all-rejected').onclick=()=>setAllDecision('rejected');$('payload-open').onclick=copyPayload;$('payload-close').onclick=()=>$('copy-dialog').close();$('guide-close').onclick=dismissGuide;$('theme').onclick=()=>document.body.classList.toggle('dark');$('language').onclick=()=>{language=language==='zh'?'en':'zh';labels();render()};
|
|
104463
|
+
labels();render();const deadline=Date.now()+10000;guideTimer=setInterval(()=>{const left=Math.max(0,Math.ceil((deadline-Date.now())/1000));$('guide-countdown').textContent=left+'s';if(!left)dismissGuide()},250);
|
|
104464
|
+
`;
|
|
104465
|
+
|
|
104466
|
+
// src/project/reviewSiteStyles.ts
|
|
104467
|
+
var REVIEW_SITE_STYLES = String.raw`
|
|
104468
|
+
:root{--blue:#2563eb;--text:#161e2e;--muted:#646b7c;--line:#e7e9ef;--bg:#fff;--side:#f8f9fc;--red:#c63848;--amber:#a4660a}*{box-sizing:border-box}body{margin:0;color:var(--text);background:var(--bg);font:14px/1.75 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC",sans-serif}button,input,textarea{font:inherit}button{cursor:pointer}button:disabled{opacity:.35;cursor:not-allowed}[hidden]{display:none!important}header{height:64px;padding:0 24px;display:flex;align-items:center;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--bg);z-index:5}#home{border:0;background:none;color:var(--text);width:280px;flex-shrink:0;text-align:left;font-size:15px;font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}nav{display:flex;flex:1;min-width:0;overflow:auto;align-self:stretch}nav button{border:0;background:none;color:var(--text);padding:0 16px;font-size:13px;font-weight:550;white-space:nowrap}nav .active{box-shadow:inset 0 -2px var(--blue)}.new,nav .new{color:var(--red)}.modify,nav .modify{color:var(--amber)}.badge{display:inline-block;margin-left:7px;padding:1px 6px;font-size:10px;line-height:18px;vertical-align:middle;border-radius:4px;font-weight:600;letter-spacing:0}.badge.new{background:#fff0f1}.badge.modify,.badge.revised{background:#fff4dd;color:var(--amber)}.badge.approved{background:#e9f7ef;color:#258451}.badge.rejected{background:#f4edf0;color:#9e4458}.tools{display:flex;align-items:center;gap:8px;position:relative;margin-left:12px}.btn{border:1px solid var(--line);border-radius:6px;background:var(--bg);color:var(--text);padding:5px 10px;font-size:12px;line-height:22px;white-space:nowrap}.primary{color:white;background:var(--blue);border-color:var(--blue)}#theme,#language{margin-left:8px;flex-shrink:0}.counter{position:relative;padding:8px;white-space:nowrap;font-size:12px;font-weight:700;color:var(--blue)}.counter-pop{display:none;position:absolute;top:100%;right:0;min-width:320px;padding:15px;background:var(--bg);border:1px solid var(--line);border-radius:10px;box-shadow:0 8px 30px #17244220;color:var(--text);font-weight:400}.counter:hover .counter-pop,.counter:focus .counter-pop{display:block}.counter-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;font-size:11px}.counter-grid b{display:block;font-size:20px}.counter-pop p{border-top:1px solid var(--line);padding-top:10px}.layout{display:grid;grid-template-columns:280px minmax(0,1fr)}aside{height:calc(100vh - 64px);position:sticky;top:64px;overflow:auto;background:var(--side);border-right:1px solid var(--line);padding:12px 0 80px}.node{display:block;width:100%;text-align:left;border:0;background:none;min-height:32px;padding:6px 16px;font-size:13px;font-weight:450;line-height:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--muted)}.node.new{color:var(--red)}.node.modify{color:var(--amber)}.node.selected{background:#2563eb14}.caret{float:right}.node:hover{background:#8e96aa1a}main{min-width:0;padding:44px 60px 100px 36px}h1{font-size:36px;line-height:1.3;letter-spacing:-.025em;margin:0 0 28px}h2{font-size:25px;line-height:1.4;font-weight:650;margin:46px 0 22px;border-bottom:1px solid var(--line);padding-bottom:14px}h3{font-size:19px;margin:30px 0 14px}article{font-size:16px;line-height:1.8}article>h1>.badge{margin-left:12px}article a{color:inherit}article a:hover{color:var(--blue)}article table{border-collapse:collapse;font-size:14px;display:block;overflow:auto}article th,article td{border:1px solid var(--line);padding:11px 14px}article th{background:var(--side)}pre{overflow:auto;background:var(--side);padding:16px}blockquote{padding:16px 20px;margin:24px 0;border-left:3px solid #00bec8;background:#2563eb14;color:var(--muted)}.home .layout{display:block}.home aside{display:none}.home main{width:1120px;max-width:calc(100% - 280px);margin-left:280px;padding-top:32px}.home article{font-size:14px}.home h1{font-size:28px;margin-bottom:12px}.home h2{font-size:19px;margin:24px 0 12px;padding-bottom:10px}.home h3{font-size:15px;margin:16px 0 10px}.stats{font-size:12px;color:var(--muted)}.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(270px,1fr));gap:8px}.cards button{border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--text);text-align:left;font-size:13px;line-height:20px;padding:10px 14px}.cards button:hover{border-color:var(--blue)}.unchanged{border:1px dashed var(--line);border-radius:8px;padding:24px;text-align:center;color:var(--muted);font-size:13px}.changed{position:relative;border-left:3px solid #e8b051;background:#fffaf0;color:#8f5607;padding:24px 18px 12px;margin:20px 0}.changed>.badge{position:absolute;right:12px;top:6px}.changed details{color:var(--muted)}.workspace-tree{font:12px/1.9 ui-monospace,monospace;border:1px solid var(--line);border-radius:8px;padding:16px;background:var(--side);overflow:auto}.workspace-tree button{border:0;background:none;color:var(--text);padding:0;font:inherit;white-space:nowrap}.note{font-size:12px;color:var(--muted)}footer{position:fixed;bottom:0;left:280px;right:0;background:var(--bg);border-top:1px solid var(--line);padding:12px 35px;display:flex;gap:10px;z-index:4}footer input{flex:1;min-width:0;border:0;outline:0;background:transparent;color:var(--text);font-size:14px;padding:8px 0}footer .btn{min-width:76px}.chosen{color:var(--blue);border-color:var(--blue);background:var(--bg)}.hover-label{display:none}.chosen:hover .normal{display:none}.chosen:hover .hover-label{display:inline}dialog{width:560px;max-width:90vw;background:var(--bg);color:var(--text);border:1px solid var(--line);border-radius:10px;padding:20px;font-size:13px;line-height:1.65}dialog::backdrop{background:#131b3255}dialog h2{font-size:17px;margin:0 0 12px;border:0;padding:0}dialog textarea{width:100%;height:130px;border:1px solid var(--line);border-radius:6px;padding:10px;font:11px/1.6 monospace;background:var(--side);color:var(--text)}#copy-warning{color:var(--amber)}.bulk-ack{display:flex;align-items:center;gap:8px;font-size:13px}.bulk-ack input{accent-color:var(--blue)}#bulk-roots-list{margin:8px 0 12px;padding-left:22px;color:var(--red)}.dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:18px}.guide{position:absolute;right:0;top:calc(100% + 18px);width:300px;background:var(--bg);border:1px solid var(--line);border-radius:10px;padding:15px;box-shadow:0 10px 35px #17244224;font-size:12px}.guide:before{content:'';position:absolute;top:-7px;right:42px;width:12px;height:12px;background:var(--bg);border-top:1px solid var(--line);border-left:1px solid var(--line);transform:rotate(45deg)}#guide-countdown{float:right;color:var(--muted);font-size:11px}.dark{--bg:#171b24;--side:#1b1d24;--text:#e2e4ed;--muted:#a1a6b7;--line:#303440;--blue:#8eb7ff}.dark .changed{background:#302919;color:#f2cf85}@media(min-width:1600px){main,.home main{padding-left:52px}}@media(max-width:1250px){#home{width:190px}nav button{padding:0 8px}.tools{gap:4px}}@media(max-width:1050px){header{height:auto;flex-wrap:wrap;min-height:64px}nav{order:3;flex-basis:100%;height:44px}.tools{margin-left:auto}}@media(max-width:700px){.layout{display:block}aside{position:relative;top:0;height:200px}main,.home main{width:100%;max-width:100%;margin:0;padding:24px 20px 90px}footer{left:0;padding:10px;flex-wrap:wrap}footer input{flex-basis:100%}.tools{flex-wrap:wrap}h1{font-size:28px}}
|
|
104399
104469
|
`;
|
|
104400
104470
|
|
|
104401
104471
|
// src/project/reviewHtml.ts
|
|
104402
|
-
var REVIEW_HTML_ROOT =
|
|
104472
|
+
var REVIEW_HTML_ROOT = join87(".tmp", "context-runtime", "review");
|
|
104403
104473
|
async function collectReviewCandidates(projectRoot, collection) {
|
|
104404
104474
|
const rows = await readCandidateRecords(projectRoot);
|
|
104405
104475
|
const draftRows = rows.filter((row) => row.collection === collection && row.status === "draft");
|
|
@@ -104416,446 +104486,23 @@ async function collectAllReviewCandidates(projectRoot) {
|
|
|
104416
104486
|
snapshot: await readReviewCandidateSnapshot(projectRoot, record4)
|
|
104417
104487
|
})));
|
|
104418
104488
|
}
|
|
104419
|
-
function
|
|
104420
|
-
return value.replace(/&/gu, "&").replace(/</gu, "<").replace(/>/gu, ">").replace(/"/gu, """);
|
|
104421
|
-
}
|
|
104422
|
-
function jsonForScript(value) {
|
|
104423
|
-
return JSON.stringify(value).replace(/</gu, "\\u003c").replace(/>/gu, "\\u003e").replace(/&/gu, "\\u0026").replace(/\u2028/gu, "\\u2028").replace(/\u2029/gu, "\\u2029");
|
|
104424
|
-
}
|
|
104425
|
-
function renderReviewHtml(candidates, reviewScope) {
|
|
104426
|
-
const candidateIds = candidates.map(({ record: record4 }) => record4.candidate_id);
|
|
104427
|
-
const visibleCandidateIds = [...candidateIds].sort();
|
|
104489
|
+
function renderReviewHtml(candidates, reviewScope, model) {
|
|
104428
104490
|
const scope2 = {
|
|
104429
|
-
|
|
104430
|
-
|
|
104431
|
-
|
|
104432
|
-
|
|
104433
|
-
|
|
104434
|
-
|
|
104435
|
-
|
|
104436
|
-
|
|
104437
|
-
|
|
104438
|
-
|
|
104439
|
-
|
|
104440
|
-
article_id: record4.article_id,
|
|
104441
|
-
module: record4.module,
|
|
104442
|
-
status: record4.status,
|
|
104443
|
-
kind: record4.kind,
|
|
104444
|
-
visibility: record4.visibility,
|
|
104445
|
-
source_refs: record4.source_refs,
|
|
104446
|
-
source_paths: record4.indexer_candidate === undefined ? [] : [...new Set(record4.indexer_candidate.sections.flatMap((section) => section.references).map((binding) => binding.locator.path))].sort(),
|
|
104447
|
-
sections: record4.indexer_candidate.sections.map((section) => ({
|
|
104448
|
-
id: section.section_key,
|
|
104449
|
-
kind: record4.kind,
|
|
104450
|
-
summary: section.section_key,
|
|
104451
|
-
body: section.markdown,
|
|
104452
|
-
source_refs: [...new Set(section.references.map((reference2) => `${reference2.source_ref}/${reference2.locator.path}#L${reference2.locator.start_line}-L${reference2.locator.end_line}`))].sort(),
|
|
104453
|
-
content_mode: "authored"
|
|
104454
|
-
})),
|
|
104455
|
-
group_key: reviewScope === "all" ? `${record4.collection} / ${candidateGroupKey({ record: record4, snapshot })}` : candidateGroupKey({ record: record4, snapshot }),
|
|
104456
|
-
group_label: reviewScope === "all" ? `${record4.collection} · ${candidateGroupLabel({ record: record4, snapshot })}` : candidateGroupLabel({ record: record4, snapshot }),
|
|
104457
|
-
review: record4.review,
|
|
104458
|
-
display_summary: record4.review.behavior_summary ?? record4.review.summary,
|
|
104459
|
-
preview: candidatePreview({ record: record4, snapshot }),
|
|
104460
|
-
rendered_markdown: renderReviewMarkdown(record4.indexer_candidate.sections.map((section) => section.markdown).join(`
|
|
104461
|
-
|
|
104462
|
-
`), record4.review.title),
|
|
104463
|
-
snapshot_ready: snapshot !== undefined
|
|
104464
|
-
};
|
|
104465
|
-
});
|
|
104466
|
-
return `<!doctype html>
|
|
104467
|
-
<html lang="en" data-theme="light">
|
|
104468
|
-
<head>
|
|
104469
|
-
<meta charset="utf-8">
|
|
104470
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
104471
|
-
<title>Context Review - ${escapeHtml(reviewScope)}</title>
|
|
104472
|
-
<style>${REVIEW_HTML_STYLES}</style>
|
|
104473
|
-
</head>
|
|
104474
|
-
<body>
|
|
104475
|
-
<main class="shell">
|
|
104476
|
-
<header class="header">
|
|
104477
|
-
<div class="titleline">
|
|
104478
|
-
<h1 id="review-heading">Context Review</h1>
|
|
104479
|
-
<div class="subtle" id="count-state">${candidates.length} draft candidate(s) in ${escapeHtml(reviewScope)} · ${candidates.length} pending 0 approved 0 omitted</div>
|
|
104480
|
-
</div>
|
|
104481
|
-
<div class="toolbar">
|
|
104482
|
-
<span class="bulk-actions">
|
|
104483
|
-
<button class="btn" id="all-approved">All approved</button>
|
|
104484
|
-
<button class="btn" id="all-rejected">Omit all</button>
|
|
104485
|
-
</span>
|
|
104486
|
-
<button class="btn brand" id="payload-open">Copy review results</button>
|
|
104487
|
-
<button class="btn language-btn" id="language" type="button" aria-label="Switch to Chinese">中文</button>
|
|
104488
|
-
<button class="btn icon-btn" id="theme" title="Toggle theme" aria-label="Toggle theme">\uD83C\uDF19</button>
|
|
104489
|
-
</div>
|
|
104490
|
-
</header>
|
|
104491
|
-
<section class="layout">
|
|
104492
|
-
<aside class="panel candidate-panel">
|
|
104493
|
-
<div class="panel-head candidate-head">
|
|
104494
|
-
<span id="pages-label">Pages to review</span>
|
|
104495
|
-
<div class="filters" id="filters" aria-label="candidate filters">
|
|
104496
|
-
<label class="filter"><input id="filter-approved" type="checkbox" checked> <span id="label-approved">approved</span></label>
|
|
104497
|
-
<label class="filter"><input id="filter-rejected" type="checkbox" checked> <span id="label-rejected">omitted</span></label>
|
|
104498
|
-
<label class="filter"><input id="filter-pending" type="checkbox" checked> <span id="label-pending">pending</span></label>
|
|
104499
|
-
</div>
|
|
104500
|
-
</div>
|
|
104501
|
-
<input id="search" type="search" placeholder="Search pages or modules" aria-label="Search pages or modules">
|
|
104502
|
-
<div id="list"></div>
|
|
104503
|
-
</aside>
|
|
104504
|
-
<section class="panel detail-panel">
|
|
104505
|
-
<div class="panel-head reader-navigation"><span id="content-label">Page content</span><div><button class="btn" id="previous-page">Previous</button> <button class="btn" id="next-page">Next</button> <button class="btn" id="next-pending">Next pending</button></div></div>
|
|
104506
|
-
<div class="detail" id="detail"></div>
|
|
104507
|
-
</section>
|
|
104508
|
-
</section>
|
|
104509
|
-
<div class="modal hidden" id="payload-modal" role="dialog" aria-modal="true" aria-labelledby="payload-title">
|
|
104510
|
-
<section class="modal-card">
|
|
104511
|
-
<div>
|
|
104512
|
-
<h2 id="payload-title">Review results</h2>
|
|
104513
|
-
<div class="subtle" id="code-help">These choices take effect only after you send the review code back to the conversation. Each segment is at most 980 characters. Send every segment before applying.</div>
|
|
104514
|
-
</div>
|
|
104515
|
-
<div class="modal-body">
|
|
104516
|
-
<div id="decision-summary"></div>
|
|
104517
|
-
<div class="code-navigation" id="code-navigation" hidden><button class="btn" id="code-previous">Previous segment</button><span id="code-length"></span><button class="btn" id="code-next">Next segment</button></div>
|
|
104518
|
-
<textarea id="payload" aria-label="review code" readonly></textarea>
|
|
104519
|
-
</div>
|
|
104520
|
-
<div class="modal-actions">
|
|
104521
|
-
<span class="subtle" id="modal-copy-state"></span>
|
|
104522
|
-
<button class="btn" id="payload-close">Close</button>
|
|
104523
|
-
<button class="btn primary" id="payload-copy">Copy</button>
|
|
104524
|
-
</div>
|
|
104525
|
-
</section>
|
|
104526
|
-
</div>
|
|
104527
|
-
</main>
|
|
104528
|
-
<script>
|
|
104529
|
-
const translations = ${jsonForScript(REVIEW_UI_ZH)};
|
|
104530
|
-
let language = /^zh(?:-|$)/i.test(navigator.languages?.[0] || navigator.language || "en") ? "zh-CN" : "en";
|
|
104531
|
-
function t(message, values = {}) {
|
|
104532
|
-
const text = language === "zh-CN" ? translations[message] || message : message;
|
|
104533
|
-
return text.replace(/\\{(\\w+)\\}/g, (_, key) => String(values[key] ?? ""));
|
|
104534
|
-
}
|
|
104535
|
-
const candidates = ${jsonForScript(candidateData)};
|
|
104536
|
-
const payloadScope = ${jsonForScript(scope2)};
|
|
104537
|
-
const reviewCode = (${createReviewCodeCodec.toString()})();
|
|
104538
|
-
const payloadScopeLabel = ${jsonForScript(reviewScope)};
|
|
104539
|
-
const decisions = new Map(candidates.map((item) => [item.candidate_id, "pending"]));
|
|
104540
|
-
const list = document.getElementById("list");
|
|
104541
|
-
const detail = document.getElementById("detail");
|
|
104542
|
-
const countState = document.getElementById("count-state");
|
|
104543
|
-
const filterApproved = document.getElementById("filter-approved");
|
|
104544
|
-
const filterRejected = document.getElementById("filter-rejected");
|
|
104545
|
-
const filterPending = document.getElementById("filter-pending");
|
|
104546
|
-
const theme = document.getElementById("theme");
|
|
104547
|
-
const allApproved = document.getElementById("all-approved");
|
|
104548
|
-
const allRejected = document.getElementById("all-rejected");
|
|
104549
|
-
const payloadOpen = document.getElementById("payload-open");
|
|
104550
|
-
const payloadModal = document.getElementById("payload-modal");
|
|
104551
|
-
const payloadClose = document.getElementById("payload-close");
|
|
104552
|
-
const payloadCopy = document.getElementById("payload-copy");
|
|
104553
|
-
const payloadBox = document.getElementById("payload");
|
|
104554
|
-
const modalCopyState = document.getElementById("modal-copy-state");
|
|
104555
|
-
const search = document.getElementById("search");
|
|
104556
|
-
const decisionSummary = document.getElementById("decision-summary");
|
|
104557
|
-
const codeLength = document.getElementById("code-length");
|
|
104558
|
-
let codePart = 0;
|
|
104559
|
-
let selected = candidates[0]?.candidate_id;
|
|
104560
|
-
const collapsedGroups = new Set();
|
|
104561
|
-
|
|
104562
|
-
function html(value) {
|
|
104563
|
-
return String(value).replace(/[&<>"]/g, (char) => ({ "&":"&", "<":"<", ">":">", '"':""" }[char]));
|
|
104564
|
-
}
|
|
104565
|
-
function decisionCounts() {
|
|
104566
|
-
const counts = { pending: 0, approved: 0, rejected: 0 };
|
|
104567
|
-
for (const status of decisions.values()) counts[status] += 1;
|
|
104568
|
-
return counts;
|
|
104569
|
-
}
|
|
104570
|
-
function updateCountState() {
|
|
104571
|
-
const counts = decisionCounts();
|
|
104572
|
-
countState.textContent = t("{count} pages · {scope} · {pending} pending · {approved} approved · {rejected} omitted",
|
|
104573
|
-
{ count: candidates.length, scope: payloadScopeLabel === "all" ? t("All collections") : payloadScopeLabel, ...counts });
|
|
104574
|
-
}
|
|
104575
|
-
function visibleCandidates() {
|
|
104576
|
-
const showApproved = filterApproved.checked;
|
|
104577
|
-
const showRejected = filterRejected.checked;
|
|
104578
|
-
const showPending = filterPending.checked;
|
|
104579
|
-
const query = search.value.trim().toLowerCase();
|
|
104580
|
-
return candidates.filter((item) => {
|
|
104581
|
-
if (query && !(item.review.title + " " + item.module + " " + item.source_paths.join(" ")).toLowerCase().includes(query)) return false;
|
|
104582
|
-
const status = decisions.get(item.candidate_id);
|
|
104583
|
-
return (status === "pending" && showPending) ||
|
|
104584
|
-
(status === "approved" && showApproved) ||
|
|
104585
|
-
(status === "rejected" && showRejected);
|
|
104586
|
-
});
|
|
104587
|
-
}
|
|
104588
|
-
function groupCandidates(items) {
|
|
104589
|
-
const groups = [];
|
|
104590
|
-
const byGroup = new Map();
|
|
104591
|
-
for (const item of items) {
|
|
104592
|
-
const key = item.group_key || item.module || "ungrouped";
|
|
104593
|
-
let group = byGroup.get(key);
|
|
104594
|
-
if (!group) {
|
|
104595
|
-
group = { key, label: item.group_label || key, items: [] };
|
|
104596
|
-
byGroup.set(key, group);
|
|
104597
|
-
groups.push(group);
|
|
104598
|
-
}
|
|
104599
|
-
group.items.push(item);
|
|
104600
|
-
}
|
|
104601
|
-
return groups;
|
|
104602
|
-
}
|
|
104603
|
-
function statusBadge(status) {
|
|
104604
|
-
const label = t(status === "rejected" ? "omitted" : status);
|
|
104605
|
-
return '<span class="badge ' + html(status) + '">' + html(label) + '</span>';
|
|
104606
|
-
}
|
|
104607
|
-
function toggleGroup(groupKey) {
|
|
104608
|
-
if (collapsedGroups.has(groupKey)) collapsedGroups.delete(groupKey);
|
|
104609
|
-
else collapsedGroups.add(groupKey);
|
|
104610
|
-
render();
|
|
104611
|
-
}
|
|
104612
|
-
function setGroupDecision(groupKey, status) {
|
|
104613
|
-
const items = candidates.filter((item) => (item.group_key || item.module || "ungrouped") === groupKey);
|
|
104614
|
-
if (items.length === 0) return;
|
|
104615
|
-
const label = t(status === "rejected" ? "omitted" : status);
|
|
104616
|
-
if (!window.confirm(t("Set all {count} pages in {group} to {status}?", { count: items.length, group: groupKey, status: label }))) return;
|
|
104617
|
-
for (const item of items) {
|
|
104618
|
-
if (status === "approved" && !item.snapshot_ready) continue;
|
|
104619
|
-
decisions.set(item.candidate_id, status);
|
|
104620
|
-
}
|
|
104621
|
-
codePart = 0;
|
|
104622
|
-
modalCopyState.textContent = t("Choices changed. Copy the updated code before applying.");
|
|
104623
|
-
render();
|
|
104624
|
-
updatePayloadBox();
|
|
104625
|
-
}
|
|
104626
|
-
function setAllDecision(status) {
|
|
104627
|
-
if (candidates.length === 0) return;
|
|
104628
|
-
const label = t(status === "rejected" ? "omitted" : status);
|
|
104629
|
-
if (!window.confirm(t("Set all {count} pages to {status}?", { count: candidates.length, status: label }))) return;
|
|
104630
|
-
for (const item of candidates) {
|
|
104631
|
-
if (status === "approved" && !item.snapshot_ready) continue;
|
|
104632
|
-
decisions.set(item.candidate_id, status);
|
|
104633
|
-
}
|
|
104634
|
-
codePart = 0;
|
|
104635
|
-
modalCopyState.textContent = t("Choices changed. Copy the updated code before applying.");
|
|
104636
|
-
render();
|
|
104637
|
-
updatePayloadBox();
|
|
104638
|
-
}
|
|
104639
|
-
function payloadParts() {
|
|
104640
|
-
if (decisionCounts().pending === candidates.length) return [];
|
|
104641
|
-
const ordered = [...candidates].sort((a, b) => a.candidate_id < b.candidate_id ? -1 : a.candidate_id > b.candidate_id ? 1 : 0);
|
|
104642
|
-
return reviewCode.encode(payloadScopeLabel, payloadScope.ids_sha256, payloadScope.candidates_sha256,
|
|
104643
|
-
ordered.map((item) => decisions.get(item.candidate_id)));
|
|
104644
|
-
}
|
|
104645
|
-
function payloadText() { return payloadParts()[codePart] || t("Select at least one page decision; pending pages remain for later review."); }
|
|
104646
|
-
function setDecision(id, status) {
|
|
104647
|
-
const item = candidates.find((candidate) => candidate.candidate_id === id);
|
|
104648
|
-
if (status === "approved" && item && !item.snapshot_ready) return;
|
|
104649
|
-
decisions.set(id, status);
|
|
104650
|
-
codePart = 0;
|
|
104651
|
-
modalCopyState.textContent = t("Choices changed. Copy the updated code before applying.");
|
|
104652
|
-
render();
|
|
104653
|
-
updatePayloadBox();
|
|
104654
|
-
}
|
|
104655
|
-
function updatePayloadBox() {
|
|
104656
|
-
const parts = payloadParts();
|
|
104657
|
-
codePart = Math.min(codePart, Math.max(0, parts.length - 1));
|
|
104658
|
-
payloadBox.value = payloadText();
|
|
104659
|
-
document.getElementById("code-navigation").hidden = parts.length <= 1;
|
|
104660
|
-
codeLength.textContent = parts.length ? t("Segment {part}/{total} · {length}/980 characters", { part: codePart + 1, total: parts.length, length: payloadBox.value.length }) : t("No review code yet");
|
|
104661
|
-
document.getElementById("code-previous").disabled = codePart === 0;
|
|
104662
|
-
document.getElementById("code-next").disabled = codePart + 1 >= parts.length;
|
|
104663
|
-
const counts = decisionCounts();
|
|
104664
|
-
const ready = counts.approved + counts.rejected > 0;
|
|
104665
|
-
decisionSummary.innerHTML = '<p>' + html(t('{approved} approved · {rejected} not included · {pending} pending', counts)) + '</p>' +
|
|
104666
|
-
(counts.rejected ? '<details><summary>' + html(t('Pages not included')) + '</summary><ul>' + candidates.filter((item) => decisions.get(item.candidate_id) === "rejected").map((item) => '<li>' + html(item.review.title) + '</li>').join('') + '</ul></details>' : '');
|
|
104667
|
-
payloadCopy.disabled = !ready;
|
|
104668
|
-
payloadOpen.classList.toggle("ready", ready);
|
|
104669
|
-
payloadOpen.title = ready
|
|
104670
|
-
? t("Open review results")
|
|
104671
|
-
: t("{count} pending pages remain", { count: counts.pending });
|
|
104672
|
-
}
|
|
104673
|
-
function openPayloadModal() {
|
|
104674
|
-
updatePayloadBox();
|
|
104675
|
-
payloadModal.classList.remove("hidden");
|
|
104676
|
-
payloadBox.focus();
|
|
104677
|
-
payloadBox.select();
|
|
104678
|
-
modalCopyState.textContent = "";
|
|
104679
|
-
}
|
|
104680
|
-
function closePayloadModal() {
|
|
104681
|
-
payloadModal.classList.add("hidden");
|
|
104682
|
-
}
|
|
104683
|
-
async function copyPayload() {
|
|
104684
|
-
const counts = decisionCounts();
|
|
104685
|
-
if (counts.approved + counts.rejected === 0) {
|
|
104686
|
-
updatePayloadBox();
|
|
104687
|
-
const message = t("Select at least one page decision; pending pages remain for later review.");
|
|
104688
|
-
modalCopyState.textContent = message;
|
|
104689
|
-
return;
|
|
104690
|
-
}
|
|
104691
|
-
const text = payloadText();
|
|
104692
|
-
payloadBox.value = text;
|
|
104693
|
-
try {
|
|
104694
|
-
if (!navigator.clipboard) throw new Error("clipboard unavailable");
|
|
104695
|
-
await navigator.clipboard.writeText(text);
|
|
104696
|
-
modalCopyState.textContent = t("Copied");
|
|
104697
|
-
} catch {
|
|
104698
|
-
payloadBox.focus();
|
|
104699
|
-
payloadBox.select();
|
|
104700
|
-
modalCopyState.textContent = t("Copy manually from the textarea");
|
|
104701
|
-
}
|
|
104702
|
-
}
|
|
104703
|
-
function render() {
|
|
104704
|
-
updateCountState();
|
|
104705
|
-
if (candidates.length === 0) {
|
|
104706
|
-
list.innerHTML = '<div class="empty">' + html(t('No draft candidates.')) + '</div>';
|
|
104707
|
-
detail.innerHTML = '<div class="empty">' + html(t('Nothing to review.')) + '</div>';
|
|
104708
|
-
return;
|
|
104709
|
-
}
|
|
104710
|
-
const visible = visibleCandidates();
|
|
104711
|
-
if (visible.length === 0) {
|
|
104712
|
-
list.innerHTML = '<div class="empty">' + html(t('No candidates match the current filters.')) + '</div>';
|
|
104713
|
-
detail.innerHTML = '<div class="empty">' + html(t('Adjust the candidate filters to continue reviewing.')) + '</div>';
|
|
104714
|
-
return;
|
|
104715
|
-
}
|
|
104716
|
-
if (!visible.some((item) => item.candidate_id === selected)) selected = visible[0].candidate_id;
|
|
104717
|
-
list.innerHTML = groupCandidates(visible).map((group) =>
|
|
104718
|
-
{
|
|
104719
|
-
const collapsed = collapsedGroups.has(group.key);
|
|
104720
|
-
return '<section class="candidate-group">' +
|
|
104721
|
-
'<div class="candidate-group-title" data-group-toggle="' + html(group.key) + '">' +
|
|
104722
|
-
'<span class="group-label"><span>' + (collapsed ? "▸" : "▾") + '</span><span class="group-key">' + html(group.label) + '</span><span class="group-count">' + group.items.length + ' items</span></span>' +
|
|
104723
|
-
'<span class="group-actions">' +
|
|
104724
|
-
'<button class="group-btn" data-group-status="approved" data-group="' + html(group.key) + '">' + html(t('All approved')) + '</button>' +
|
|
104725
|
-
'<button class="group-btn" data-group-status="rejected" data-group="' + html(group.key) + '">' + html(t('Omit all')) + '</button>' +
|
|
104726
|
-
'</span>' +
|
|
104727
|
-
'</div>' +
|
|
104728
|
-
(collapsed ? "" : group.items.map((item) => {
|
|
104729
|
-
const active = item.candidate_id === selected ? " active" : "";
|
|
104730
|
-
const status = decisions.get(item.candidate_id);
|
|
104731
|
-
return '<button class="candidate' + active + '" data-id="' + html(item.candidate_id) + '">' +
|
|
104732
|
-
'<div class="candidate-title">' +
|
|
104733
|
-
'<span class="candidate-title-text">' + html(item.review.title) + '</span>' +
|
|
104734
|
-
'<span class="candidate-tags"><span class="badge">' + html(item.collection || "unknown") + '</span>' + (!item.snapshot_ready ? '<span class="badge warning">' + html(t('evidence unavailable')) + '</span>' : '') + statusBadge(status) + '</span>' +
|
|
104735
|
-
'</div>' +
|
|
104736
|
-
'<div class="candidate-summary">' + html(item.display_summary || item.review.summary) + '</div>' +
|
|
104737
|
-
'</button>';
|
|
104738
|
-
}).join("")) +
|
|
104739
|
-
'</section>';
|
|
104740
|
-
}
|
|
104741
|
-
).join("");
|
|
104742
|
-
const item = visible.find((candidate) => candidate.candidate_id === selected) ?? visible[0];
|
|
104743
|
-
selected = item.candidate_id;
|
|
104744
|
-
const status = decisions.get(item.candidate_id);
|
|
104745
|
-
const evidenceWarning = item.snapshot_ready ? "" :
|
|
104746
|
-
'<div class="notice warning">' + html(t('Source snapshot unavailable. Restore it before approving this candidate, or omit the page.')) + '</div>';
|
|
104747
|
-
const sectionDetails = '<article class="reader-body">' + item.rendered_markdown + '</article>';
|
|
104748
|
-
const previewBlock = '<details class="technical-details"><summary>' + html(t('Source Markdown')) + '</summary><pre>' +
|
|
104749
|
-
html(item.sections.map((section) => section.body).join("\\n\\n")) + '</pre></details>';
|
|
104750
|
-
const displayedSources = [...new Set([...item.source_paths, ...item.source_refs, ...item.sections.flatMap((section) => section.source_refs)])];
|
|
104751
|
-
const sourceLocationsBlock = displayedSources.length === 0 ? "" :
|
|
104752
|
-
'<details class="technical-details">' +
|
|
104753
|
-
'<summary>' + html(t('Source locations')) + '(' + displayedSources.length + ')</summary>' +
|
|
104754
|
-
'<div class="technical-content"><div class="section-source-refs">' +
|
|
104755
|
-
displayedSources.map((ref) => '<code>' + html(ref) + '</code>').join('') +
|
|
104756
|
-
'</div></div>' +
|
|
104757
|
-
'</details>';
|
|
104758
|
-
detail.innerHTML = '<div class="detail-titlebar">' +
|
|
104759
|
-
'<div class="page-location">' + html(item.path) + '</div>' +
|
|
104760
|
-
'<div class="actions">' +
|
|
104761
|
-
'<button class="btn approve ' + (status === "approved" ? "active" : "") + '" data-action="approved" ' + (!item.snapshot_ready ? "disabled" : "") + '>' + html(t('Approve')) + '</button>' +
|
|
104762
|
-
'<button class="btn reject ' + (status === "rejected" ? "active" : "") + '" data-action="rejected">' + html(t('Omit')) + '</button>' +
|
|
104763
|
-
'</div>' +
|
|
104764
|
-
'</div>' +
|
|
104765
|
-
evidenceWarning +
|
|
104766
|
-
sectionDetails +
|
|
104767
|
-
previewBlock +
|
|
104768
|
-
'<p class="repair-hint">' + html(t('Need changes? Leave this page pending and ask the agent to repair it. Other reviewed pages can be approved.')) + '</p>' +
|
|
104769
|
-
sourceLocationsBlock;
|
|
104770
|
-
document.querySelectorAll("[data-id]").forEach((button) => button.addEventListener("click", () => { selected = button.dataset.id; render(); }));
|
|
104771
|
-
document.querySelectorAll("[data-action]").forEach((button) => button.addEventListener("click", () => setDecision(item.candidate_id, button.dataset.action)));
|
|
104772
|
-
document.querySelectorAll("[data-group-toggle]").forEach((header) => header.addEventListener("click", () => toggleGroup(header.dataset.groupToggle)));
|
|
104773
|
-
document.querySelectorAll("[data-group-status]").forEach((button) => button.addEventListener("click", (event) => {
|
|
104774
|
-
event.stopPropagation();
|
|
104775
|
-
setGroupDecision(button.dataset.group, button.dataset.groupStatus);
|
|
104776
|
-
}));
|
|
104777
|
-
}
|
|
104778
|
-
function navigatePage(direction, pendingOnly = false) {
|
|
104779
|
-
const items = visibleCandidates();
|
|
104780
|
-
const current = items.findIndex((item) => item.candidate_id === selected);
|
|
104781
|
-
for (let step = 1; step <= items.length; step++) {
|
|
104782
|
-
const item = items[(current + direction * step + items.length * 2) % items.length];
|
|
104783
|
-
if (!pendingOnly || decisions.get(item.candidate_id) === "pending") { selected = item.candidate_id; render(); detail.scrollTop = 0; return; }
|
|
104784
|
-
}
|
|
104785
|
-
}
|
|
104786
|
-
search.addEventListener("input", render);
|
|
104787
|
-
document.getElementById("previous-page").addEventListener("click", () => navigatePage(-1));
|
|
104788
|
-
document.getElementById("next-page").addEventListener("click", () => navigatePage(1));
|
|
104789
|
-
document.getElementById("next-pending").addEventListener("click", () => navigatePage(1, true));
|
|
104790
|
-
document.getElementById("code-previous").addEventListener("click", () => { codePart = Math.max(0, codePart - 1); updatePayloadBox(); });
|
|
104791
|
-
document.getElementById("code-next").addEventListener("click", () => { codePart++; updatePayloadBox(); });
|
|
104792
|
-
function applyLanguage() {
|
|
104793
|
-
document.documentElement.lang = language;
|
|
104794
|
-
document.title = t("Context Review") + " - " + payloadScopeLabel;
|
|
104795
|
-
const labels = {
|
|
104796
|
-
"review-heading": "Context Review", "all-approved": "All approved", "all-rejected": "Omit all",
|
|
104797
|
-
"payload-open": "Copy review results", "pages-label": "Pages to review", "label-approved": "approved",
|
|
104798
|
-
"label-rejected": "omitted", "label-pending": "pending", "content-label": "Page content",
|
|
104799
|
-
"previous-page": "Previous", "next-page": "Next", "next-pending": "Next pending",
|
|
104800
|
-
"payload-title": "Review results", "code-previous": "Previous segment", "code-next": "Next segment",
|
|
104801
|
-
"payload-close": "Close", "payload-copy": "Copy",
|
|
104802
|
-
"code-help": "These choices take effect only after you send the review code back to the conversation. Each segment is at most 980 characters. Send every segment before applying.",
|
|
104803
|
-
};
|
|
104804
|
-
for (const [id, message] of Object.entries(labels)) document.getElementById(id).textContent = t(message);
|
|
104805
|
-
search.placeholder = t("Search pages or modules");
|
|
104806
|
-
search.setAttribute("aria-label", t("Search pages or modules"));
|
|
104807
|
-
document.getElementById("filters").setAttribute("aria-label", t("candidate filters"));
|
|
104808
|
-
payloadBox.setAttribute("aria-label", t("review code"));
|
|
104809
|
-
theme.title = t("Toggle theme");
|
|
104810
|
-
theme.setAttribute("aria-label", t("Toggle theme"));
|
|
104811
|
-
const button = document.getElementById("language");
|
|
104812
|
-
button.textContent = language === "zh-CN" ? "English" : "中文";
|
|
104813
|
-
button.setAttribute("aria-label", language === "zh-CN" ? "Switch to English" : "切换为中文");
|
|
104814
|
-
modalCopyState.textContent = "";
|
|
104815
|
-
const scrollTop = detail.scrollTop;
|
|
104816
|
-
render();
|
|
104817
|
-
detail.scrollTop = scrollTop;
|
|
104818
|
-
updatePayloadBox();
|
|
104819
|
-
}
|
|
104820
|
-
function toggleLanguage() {
|
|
104821
|
-
language = language === "zh-CN" ? "en" : "zh-CN";
|
|
104822
|
-
applyLanguage();
|
|
104823
|
-
}
|
|
104824
|
-
document.getElementById("language").addEventListener("click", toggleLanguage);
|
|
104825
|
-
function effectiveTheme() {
|
|
104826
|
-
return document.documentElement.dataset.theme || "light";
|
|
104827
|
-
}
|
|
104828
|
-
function updateThemeIcon() {
|
|
104829
|
-
theme.textContent = effectiveTheme() === "dark" ? "☀️" : "\uD83C\uDF19";
|
|
104830
|
-
}
|
|
104831
|
-
theme.addEventListener("click", () => {
|
|
104832
|
-
document.documentElement.dataset.theme = effectiveTheme() === "dark" ? "light" : "dark";
|
|
104833
|
-
updateThemeIcon();
|
|
104834
|
-
});
|
|
104835
|
-
updateThemeIcon();
|
|
104836
|
-
allApproved.addEventListener("click", () => setAllDecision("approved"));
|
|
104837
|
-
allRejected.addEventListener("click", () => setAllDecision("rejected"));
|
|
104838
|
-
filterApproved.addEventListener("change", render);
|
|
104839
|
-
filterRejected.addEventListener("change", render);
|
|
104840
|
-
filterPending.addEventListener("change", render);
|
|
104841
|
-
payloadOpen.addEventListener("click", openPayloadModal);
|
|
104842
|
-
payloadClose.addEventListener("click", closePayloadModal);
|
|
104843
|
-
payloadCopy.addEventListener("click", copyPayload);
|
|
104844
|
-
payloadModal.addEventListener("click", (event) => {
|
|
104845
|
-
if (event.target === payloadModal) closePayloadModal();
|
|
104846
|
-
});
|
|
104847
|
-
document.addEventListener("keydown", (event) => {
|
|
104848
|
-
if (event.key === "Escape" && !payloadModal.classList.contains("hidden")) closePayloadModal();
|
|
104849
|
-
});
|
|
104850
|
-
applyLanguage();
|
|
104851
|
-
</script>
|
|
104852
|
-
</body>
|
|
104853
|
-
</html>
|
|
104854
|
-
`;
|
|
104491
|
+
label: reviewScope,
|
|
104492
|
+
ids_sha256: candidateIdsHash(candidates.map((c) => c.record.candidate_id).sort()),
|
|
104493
|
+
candidates_sha256: candidateSetHash(candidates.map((c) => c.record))
|
|
104494
|
+
};
|
|
104495
|
+
return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${escapeReviewHtml(model.title)} · Review</title><style>${REVIEW_SITE_STYLES}</style></head><body>
|
|
104496
|
+
<header><button id="home">${escapeReviewHtml(model.title)}</button><nav id="top"></nav><div class="tools"><div class="counter" tabindex="0"><span id="counts"></span><div class="counter-pop" id="counter-pop"></div></div><button class="btn" id="all-approved"></button><button class="btn" id="all-rejected"></button><button class="btn primary" id="payload-open"></button><div class="guide" id="copy-guide"><span id="guide-countdown">10s</span><p id="guide-text"></p><button class="btn" id="guide-close"></button></div></div><button class="btn" id="theme" aria-label="Theme">◐</button><button class="btn" id="language"></button></header>
|
|
104497
|
+
<div class="layout"><aside id="tree"></aside><main><article id="article"></article></main></div>
|
|
104498
|
+
<footer id="footer" hidden><input id="revision-note" aria-label="Revision instructions"><button class="btn" id="revise-btn"></button><button class="btn" id="reject-btn"></button><button class="btn primary" id="approve-btn"></button></footer>
|
|
104499
|
+
<dialog id="bulk-dialog"><h2 id="bulk-title"></h2><p id="bulk-message"></p><div id="bulk-roots" hidden><strong id="bulk-roots-title"></strong><ul id="bulk-roots-list"></ul><label class="bulk-ack"><input type="checkbox" id="bulk-ack"><span id="bulk-ack-label"></span></label></div><div class="dialog-actions"><button class="btn" id="bulk-cancel"></button><button class="btn primary" id="bulk-confirm"></button></div></dialog>
|
|
104500
|
+
<dialog id="copy-dialog"><h2 id="copy-title"></h2><p id="copy-summary"></p><p id="copy-instructions"></p><textarea id="payload" readonly aria-label="Review code"></textarea><p id="copy-warning"></p><button class="btn" id="payload-close"></button></dialog>
|
|
104501
|
+
<script>const DATA=${reviewHtmlJson(model)};const SCOPE=${reviewHtmlJson(scope2)};const feedbackCodec=(${createReviewFeedbackCodec.toString()})();${REVIEW_SITE_CLIENT}</script></body></html>`;
|
|
104855
104502
|
}
|
|
104856
104503
|
function resolveOutputPath(projectRoot, outPath, reviewScope) {
|
|
104857
104504
|
if (outPath === undefined)
|
|
104858
|
-
return
|
|
104505
|
+
return join87(projectRoot, REVIEW_HTML_ROOT, `${reviewScope}.html`);
|
|
104859
104506
|
return isAbsolute15(outPath) ? outPath : resolve28(projectRoot, outPath);
|
|
104860
104507
|
}
|
|
104861
104508
|
async function writeReviewHtml(input) {
|
|
@@ -104865,8 +104512,8 @@ async function writeReviewHtml(input) {
|
|
|
104865
104512
|
}
|
|
104866
104513
|
const candidates = reviewScope === "all" ? await collectAllReviewCandidates(input.projectRoot) : await collectReviewCandidates(input.projectRoot, reviewScope);
|
|
104867
104514
|
const outPath = resolveOutputPath(input.projectRoot, input.out, reviewScope);
|
|
104868
|
-
await mkdir30(
|
|
104869
|
-
await writeFile24(outPath, renderReviewHtml(candidates, reviewScope), "utf8");
|
|
104515
|
+
await mkdir30(dirname37(outPath), { recursive: true });
|
|
104516
|
+
await writeFile24(outPath, renderReviewHtml(candidates, reviewScope, await collectReviewSiteModel(input.projectRoot, candidates)), "utf8");
|
|
104870
104517
|
return {
|
|
104871
104518
|
path: outPath,
|
|
104872
104519
|
candidates: candidates.length,
|
|
@@ -104879,8 +104526,8 @@ async function writeReviewHtml(input) {
|
|
|
104879
104526
|
init_productionRequirements();
|
|
104880
104527
|
init_dist();
|
|
104881
104528
|
init_atomicWrite();
|
|
104882
|
-
import { mkdir as mkdir31, readFile as
|
|
104883
|
-
import { join as
|
|
104529
|
+
import { mkdir as mkdir31, readFile as readFile69 } from "node:fs/promises";
|
|
104530
|
+
import { join as join88 } from "node:path";
|
|
104884
104531
|
var REVIEW_BATCH_MAX_CANDIDATES = 6;
|
|
104885
104532
|
var REVIEW_BATCH_MAX_BYTES = 512 * 1024;
|
|
104886
104533
|
async function readerPurposes(projectRoot, sources) {
|
|
@@ -104956,15 +104603,16 @@ function buildCurrentReviewBatchDocuments(candidates) {
|
|
|
104956
104603
|
});
|
|
104957
104604
|
}
|
|
104958
104605
|
async function materializeCurrentReviewBatchSet(input) {
|
|
104606
|
+
const feedback = await readPendingReviewFeedback(input.projectRoot, input.candidates);
|
|
104959
104607
|
const batches = buildCurrentReviewBatchDocuments(input.candidates);
|
|
104960
104608
|
const setDigest = digestText(batches.map((batch) => `${batch.task_key}:${batch.digest}`).join(`
|
|
104961
104609
|
`));
|
|
104962
|
-
const root2 =
|
|
104610
|
+
const root2 = join88(input.projectRoot, ".tmp", "context-runtime", "review", `current-${setDigest.slice("sha256:".length)}`);
|
|
104963
104611
|
await mkdir31(root2, { recursive: true });
|
|
104964
104612
|
const entries2 = [];
|
|
104965
104613
|
for (const batch of batches) {
|
|
104966
|
-
const path4 =
|
|
104967
|
-
const existing = await
|
|
104614
|
+
const path4 = join88(input.projectRoot, ".tmp", "context-runtime", "review", `${batch.task_key}-${batch.digest.slice("sha256:".length)}.md`);
|
|
104615
|
+
const existing = await readFile69(path4, "utf8").catch((error) => {
|
|
104968
104616
|
if (error.code === "ENOENT")
|
|
104969
104617
|
return;
|
|
104970
104618
|
throw error;
|
|
@@ -104976,6 +104624,7 @@ async function materializeCurrentReviewBatchSet(input) {
|
|
|
104976
104624
|
}
|
|
104977
104625
|
const content3 = [
|
|
104978
104626
|
"# Current knowledge Review",
|
|
104627
|
+
...feedback.length ? ["", "## Pending user revision instructions", ...feedback.map((item) => JSON.stringify(item)), "Apply these through their repair commands, then review the new candidates. Do not approve unchanged drafts to bypass feedback."] : [],
|
|
104979
104628
|
"",
|
|
104980
104629
|
"## Reader purposes",
|
|
104981
104630
|
"",
|
|
@@ -105016,7 +104665,7 @@ async function materializeCurrentReviewBatchSet(input) {
|
|
|
105016
104665
|
].join(`
|
|
105017
104666
|
`);
|
|
105018
104667
|
const digest6 = digestText(content3);
|
|
105019
|
-
const path3 =
|
|
104668
|
+
const path3 = join88(root2, "index.md");
|
|
105020
104669
|
await atomicWriteFile(path3, `${content3}
|
|
105021
104670
|
`);
|
|
105022
104671
|
return {
|
|
@@ -105049,11 +104698,11 @@ function shellQuote6(value) {
|
|
|
105049
104698
|
}
|
|
105050
104699
|
function receiptSetPath(receipts) {
|
|
105051
104700
|
const token = digestText(JSON.stringify(receipts)).slice("sha256:".length);
|
|
105052
|
-
return
|
|
104701
|
+
return join89(".tmp", "context-runtime", "workflow", "read-receipts", `${token}.json`);
|
|
105053
104702
|
}
|
|
105054
104703
|
async function writeReceiptContinuation(input) {
|
|
105055
104704
|
const path3 = receiptSetPath(input.receipts);
|
|
105056
|
-
const absolutePath =
|
|
104705
|
+
const absolutePath = join89(input.projectRoot, path3);
|
|
105057
104706
|
await writeJsonAtomic(absolutePath, input.receipts);
|
|
105058
104707
|
const contextCommand = input.managed ? [
|
|
105059
104708
|
"context",
|
|
@@ -105148,7 +104797,7 @@ async function materializeContextWorkflowResource(input) {
|
|
|
105148
104797
|
const resourceId = workflowResourceId(input.resourceId);
|
|
105149
104798
|
const content3 = renderContextWorkflowResource(resourceId, status);
|
|
105150
104799
|
const location = await materializeResource(await loadContextWorkflowProvider(), resourceId, {
|
|
105151
|
-
cache:
|
|
104800
|
+
cache: join89(found.projectRoot, ".tmp", "context-runtime", "workflow", "resources"),
|
|
105152
104801
|
workspace: found.projectRoot,
|
|
105153
104802
|
revision: input.revision,
|
|
105154
104803
|
input: {
|
|
@@ -105180,7 +104829,7 @@ async function materializeContextWorkflowResource(input) {
|
|
|
105180
104829
|
receipts: afterReadReceipts
|
|
105181
104830
|
});
|
|
105182
104831
|
const directResources = (status.workflow.current?.resources.required ?? []).filter((resource) => resource.read_state === "read-required" && resource.path !== undefined && resource.digest !== undefined);
|
|
105183
|
-
const afterReadCommand = directResources.length === 0 ? continuation.command : `context resource acknowledge-current --revision ${shellQuote6(input.revision)}${authorityCommandOptions(authorities, "resource")} --resource-receipts ${shellQuote6(`@${
|
|
104832
|
+
const afterReadCommand = directResources.length === 0 ? continuation.command : `context resource acknowledge-current --revision ${shellQuote6(input.revision)}${authorityCommandOptions(authorities, "resource")} --resource-receipts ${shellQuote6(`@${join89(found.projectRoot, continuation.path)}`)} --format json`;
|
|
105184
104833
|
return {
|
|
105185
104834
|
protocol: "context.workflow.resource.v1",
|
|
105186
104835
|
id: resourceId,
|
|
@@ -105241,14 +104890,14 @@ async function acknowledgeCurrentWorkflowResources(input) {
|
|
|
105241
104890
|
const reevaluated = await reevaluateProjectStatusWorkflow({
|
|
105242
104891
|
snapshot,
|
|
105243
104892
|
resourceReceipts: normalizedReceipts,
|
|
105244
|
-
resourceReceiptsReference: `@${
|
|
104893
|
+
resourceReceiptsReference: `@${join89(found.projectRoot, continuation.path)}`
|
|
105245
104894
|
});
|
|
105246
104895
|
return {
|
|
105247
104896
|
...reevaluated,
|
|
105248
104897
|
resourceAcknowledgement: {
|
|
105249
104898
|
protocol: "context.workflow.resource-receipts.v1",
|
|
105250
104899
|
acknowledged: directResources.length,
|
|
105251
|
-
receiptReference: `@${
|
|
104900
|
+
receiptReference: `@${join89(found.projectRoot, continuation.path)}`
|
|
105252
104901
|
}
|
|
105253
104902
|
};
|
|
105254
104903
|
}
|
|
@@ -105294,9 +104943,9 @@ init_cliFeedback();
|
|
|
105294
104943
|
init_errors3();
|
|
105295
104944
|
init_exitCode();
|
|
105296
104945
|
init_workspace();
|
|
105297
|
-
import { readFile as
|
|
105298
|
-
import { isAbsolute as isAbsolute16, join as
|
|
105299
|
-
var RECEIPT_DIRECTORY =
|
|
104946
|
+
import { readFile as readFile70 } from "node:fs/promises";
|
|
104947
|
+
import { isAbsolute as isAbsolute16, join as join90, sep as sep6, resolve as resolve29 } from "node:path";
|
|
104948
|
+
var RECEIPT_DIRECTORY = join90(".tmp", "context-runtime", "workflow", "read-receipts");
|
|
105300
104949
|
function workflowResourceReceiptCwd(value, cwd) {
|
|
105301
104950
|
if (value === undefined || !value.startsWith("@"))
|
|
105302
104951
|
return cwd;
|
|
@@ -105314,7 +104963,7 @@ async function receiptDocument(value, cwd) {
|
|
|
105314
104963
|
let source2 = value;
|
|
105315
104964
|
if (value.startsWith("@")) {
|
|
105316
104965
|
try {
|
|
105317
|
-
source2 = await
|
|
104966
|
+
source2 = await readFile70(resolve29(cwd, value.slice(1)), "utf8");
|
|
105318
104967
|
} catch (error) {
|
|
105319
104968
|
const ioCode = error !== null && typeof error === "object" && "code" in error && typeof error.code === "string" ? error.code : undefined;
|
|
105320
104969
|
throw new ContextError(ExitCode.UserError, "resource read receipt file is unavailable", {
|
|
@@ -105475,33 +105124,33 @@ function runSuccessBaseBody(input) {
|
|
|
105475
105124
|
`log: ${input.logPath}`
|
|
105476
105125
|
];
|
|
105477
105126
|
}
|
|
105478
|
-
function appendCaptureFileRunBody(
|
|
105127
|
+
function appendCaptureFileRunBody(body2, result) {
|
|
105479
105128
|
const nextAction = nextActionCommand(result.next_action);
|
|
105480
|
-
|
|
105129
|
+
body2.push(`source: file:${result.source.name}`, `include: ${result.source.include.join(", ")}`, `documents: ${result.documents.length}`, `snapshot: ${result.snapshot.manifest}`, `snapshot hash: ${result.snapshot.snapshot_hash}`, `changed: ${result.snapshot.changed ? "yes" : "no"}`);
|
|
105481
105130
|
if (nextAction !== undefined)
|
|
105482
|
-
|
|
105131
|
+
body2.push(`next action: ${nextAction}`);
|
|
105483
105132
|
for (const document4 of result.documents.slice(0, 8)) {
|
|
105484
|
-
|
|
105133
|
+
body2.push(`document ${document4.path}: ${document4.title} (${document4.line_count} line(s))`);
|
|
105485
105134
|
}
|
|
105486
105135
|
}
|
|
105487
|
-
function appendCaptureLarkRunBody(
|
|
105136
|
+
function appendCaptureLarkRunBody(body2, result) {
|
|
105488
105137
|
const nextAction = nextActionCommand(result.next_action);
|
|
105489
|
-
|
|
105138
|
+
body2.push(`source: lark:${result.source.name}`, `identity: ${result.source.identity}`, `documents: ${result.documents.length}`, `assets: ${result.assets.length}`, `snapshot: ${result.snapshot.manifest}`, `snapshot hash: ${result.snapshot.snapshot_hash}`, `changed: ${result.snapshot.changed ? "yes" : "no"}`);
|
|
105490
105139
|
if (nextAction !== undefined)
|
|
105491
|
-
|
|
105140
|
+
body2.push(`next action: ${nextAction}`);
|
|
105492
105141
|
for (const document4 of result.documents.slice(0, 8)) {
|
|
105493
|
-
|
|
105142
|
+
body2.push(`document ${document4.path}: ${document4.title} (${document4.line_count} line(s))`);
|
|
105494
105143
|
}
|
|
105495
105144
|
}
|
|
105496
|
-
function appendRunResultBody(
|
|
105145
|
+
function appendRunResultBody(body2, result) {
|
|
105497
105146
|
if (isCaptureFileRunResult(result))
|
|
105498
|
-
appendCaptureFileRunBody(
|
|
105147
|
+
appendCaptureFileRunBody(body2, result);
|
|
105499
105148
|
if (isCaptureLarkRunResult(result))
|
|
105500
|
-
appendCaptureLarkRunBody(
|
|
105149
|
+
appendCaptureLarkRunBody(body2, result);
|
|
105501
105150
|
if (result !== null && typeof result === "object" && !Array.isArray(result) && "kind" in result && (result.kind === "semantic.rules.view.result" || result.kind === "diagnostics.view.result") && "next_action" in result && result.next_action !== null && typeof result.next_action === "object" && !Array.isArray(result.next_action)) {
|
|
105502
105151
|
const nextCommand = nextActionCommand(result.next_action);
|
|
105503
105152
|
if (nextCommand !== undefined)
|
|
105504
|
-
|
|
105153
|
+
body2.push(`next action: ${nextCommand}`);
|
|
105505
105154
|
}
|
|
105506
105155
|
}
|
|
105507
105156
|
function writeRunSuccess(input) {
|
|
@@ -105522,14 +105171,14 @@ function writeRunSuccess(input) {
|
|
|
105522
105171
|
`);
|
|
105523
105172
|
return;
|
|
105524
105173
|
}
|
|
105525
|
-
const
|
|
105526
|
-
appendRunResultBody(
|
|
105174
|
+
const body2 = runSuccessBaseBody(input);
|
|
105175
|
+
appendRunResultBody(body2, input.result);
|
|
105527
105176
|
process.stdout.write(formatFeedback({
|
|
105528
105177
|
symbol: "✓",
|
|
105529
105178
|
action: "ran",
|
|
105530
105179
|
subject: input.plan.phase.id,
|
|
105531
105180
|
headline: input.plan.phase.kind,
|
|
105532
|
-
body
|
|
105181
|
+
body: body2
|
|
105533
105182
|
}));
|
|
105534
105183
|
}
|
|
105535
105184
|
function compactDiagnostics(record4) {
|
|
@@ -105561,15 +105210,15 @@ function compactJsonResult(result, verbose) {
|
|
|
105561
105210
|
// src/project/runLog.ts
|
|
105562
105211
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
105563
105212
|
import { mkdir as mkdir32, writeFile as writeFile25 } from "node:fs/promises";
|
|
105564
|
-
import { dirname as
|
|
105213
|
+
import { dirname as dirname38, join as join93 } from "node:path";
|
|
105565
105214
|
var createPhaseRunId = () => {
|
|
105566
105215
|
const timestamp = new Date().toISOString().replace(/[-:.TZ]/gu, "");
|
|
105567
105216
|
return `run_${timestamp}_${randomUUID5().slice(0, 8)}`;
|
|
105568
105217
|
};
|
|
105569
105218
|
async function writePhaseRunLog(input) {
|
|
105570
|
-
const relPath =
|
|
105571
|
-
const absPath =
|
|
105572
|
-
await mkdir32(
|
|
105219
|
+
const relPath = join93(".tmp", "context-runtime", "runs", `${input.runId}.json`);
|
|
105220
|
+
const absPath = join93(input.projectRoot, relPath);
|
|
105221
|
+
await mkdir32(dirname38(absPath), { recursive: true });
|
|
105573
105222
|
await writeFile25(absPath, `${JSON.stringify({
|
|
105574
105223
|
run_id: input.runId,
|
|
105575
105224
|
phase_id: input.phase.id,
|
|
@@ -105774,7 +105423,7 @@ function writeRunPlan(plan, format2, preview, previewError) {
|
|
|
105774
105423
|
`);
|
|
105775
105424
|
return;
|
|
105776
105425
|
}
|
|
105777
|
-
const
|
|
105426
|
+
const body2 = [
|
|
105778
105427
|
`dry-run: ${plan.dryRun ? "yes" : "no"}`,
|
|
105779
105428
|
`reads: ${plan.phase.reads.length > 0 ? plan.phase.reads.join(", ") : "none"}`,
|
|
105780
105429
|
`writes: ${plan.phase.writes.length > 0 ? plan.phase.writes.join(", ") : "none"}`,
|
|
@@ -105786,7 +105435,7 @@ function writeRunPlan(plan, format2, preview, previewError) {
|
|
|
105786
105435
|
action: "planned",
|
|
105787
105436
|
subject: plan.phase.id,
|
|
105788
105437
|
headline: plan.phase.kind,
|
|
105789
|
-
body
|
|
105438
|
+
body: body2
|
|
105790
105439
|
}));
|
|
105791
105440
|
}
|
|
105792
105441
|
function customPhaseContext(input) {
|
|
@@ -106308,7 +105957,7 @@ init_debugTrace();
|
|
|
106308
105957
|
// src/project/workflow/workflowExecutionRuntime.ts
|
|
106309
105958
|
init_src();
|
|
106310
105959
|
init_debugTrace();
|
|
106311
|
-
import { createHash as
|
|
105960
|
+
import { createHash as createHash28 } from "node:crypto";
|
|
106312
105961
|
import { spawn as spawn5 } from "node:child_process";
|
|
106313
105962
|
function digestText2(value, includeTail) {
|
|
106314
105963
|
const filtered = redactIndexerOutputText({
|
|
@@ -106318,7 +105967,7 @@ function digestText2(value, includeTail) {
|
|
|
106318
105967
|
const bytes = Buffer.byteLength(filtered);
|
|
106319
105968
|
return {
|
|
106320
105969
|
bytes,
|
|
106321
|
-
sha256:
|
|
105970
|
+
sha256: createHash28("sha256").update(filtered).digest("hex"),
|
|
106322
105971
|
...includeTail && filtered.length > 0 ? { tail: filtered.slice(-8192) } : {}
|
|
106323
105972
|
};
|
|
106324
105973
|
}
|
|
@@ -106511,11 +106160,150 @@ execution scope cleanup failed`, true)
|
|
|
106511
106160
|
// src/project/workflow/workflowInProcessActions.ts
|
|
106512
106161
|
init_workflowFacts();
|
|
106513
106162
|
|
|
106163
|
+
// src/project/reviewCode.ts
|
|
106164
|
+
function createReviewCodeCodec() {
|
|
106165
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
106166
|
+
function checksum(text9) {
|
|
106167
|
+
let crc = 4294967295;
|
|
106168
|
+
for (let i2 = 0;i2 < text9.length; i2++) {
|
|
106169
|
+
crc ^= text9.charCodeAt(i2);
|
|
106170
|
+
for (let bit = 0;bit < 8; bit++)
|
|
106171
|
+
crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
|
|
106172
|
+
}
|
|
106173
|
+
return ((crc ^ 4294967295) >>> 0).toString(16).padStart(8, "0");
|
|
106174
|
+
}
|
|
106175
|
+
function pack(bytes) {
|
|
106176
|
+
let value = 0, bits = 0, result = "";
|
|
106177
|
+
for (const byte of bytes) {
|
|
106178
|
+
value = value << 8 | byte;
|
|
106179
|
+
bits += 8;
|
|
106180
|
+
while (bits >= 6) {
|
|
106181
|
+
bits -= 6;
|
|
106182
|
+
result += alphabet[value >>> bits & 63];
|
|
106183
|
+
}
|
|
106184
|
+
}
|
|
106185
|
+
if (bits)
|
|
106186
|
+
result += alphabet[value << 6 - bits & 63];
|
|
106187
|
+
return result;
|
|
106188
|
+
}
|
|
106189
|
+
function unpack(text9) {
|
|
106190
|
+
if (!/^[A-Za-z0-9_-]*$/.test(text9))
|
|
106191
|
+
throw new Error("Invalid review code encoding");
|
|
106192
|
+
let value = 0, bits = 0;
|
|
106193
|
+
const bytes = [];
|
|
106194
|
+
for (const char of text9) {
|
|
106195
|
+
value = value << 6 | alphabet.indexOf(char);
|
|
106196
|
+
bits += 6;
|
|
106197
|
+
if (bits >= 8) {
|
|
106198
|
+
bits -= 8;
|
|
106199
|
+
bytes.push(value >>> bits & 255);
|
|
106200
|
+
}
|
|
106201
|
+
}
|
|
106202
|
+
if (pack(bytes) !== text9)
|
|
106203
|
+
throw new Error("Noncanonical review code encoding");
|
|
106204
|
+
return bytes;
|
|
106205
|
+
}
|
|
106206
|
+
function hash4(text9) {
|
|
106207
|
+
if (!/^[a-f0-9]{64}$/.test(text9))
|
|
106208
|
+
throw new Error("Review requires a complete candidate digest");
|
|
106209
|
+
return pack(Array.from({ length: 32 }, (_, i2) => Number.parseInt(text9.slice(i2 * 2, i2 * 2 + 2), 16)));
|
|
106210
|
+
}
|
|
106211
|
+
function unhash(text9) {
|
|
106212
|
+
const bytes = unpack(text9);
|
|
106213
|
+
if (bytes.length !== 32)
|
|
106214
|
+
throw new Error("Invalid candidate digest");
|
|
106215
|
+
return bytes.map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
106216
|
+
}
|
|
106217
|
+
function encode(scope2, idsHash, contentHash2, statuses) {
|
|
106218
|
+
if (!/^[a-z][a-z0-9-]*$/.test(scope2) || !statuses.length || statuses.length > 1e6 || statuses.some((status) => status !== "approved" && status !== "rejected" && status !== "pending")) {
|
|
106219
|
+
throw new Error("Select at least one review decision and keep undecided pages pending");
|
|
106220
|
+
}
|
|
106221
|
+
if (statuses.every((s) => s === "pending"))
|
|
106222
|
+
throw new Error("Select at least one review decision");
|
|
106223
|
+
const mode = statuses.every((s) => s === "approved") ? "a" : statuses.every((s) => s === "rejected") ? "r" : statuses.includes("pending") ? "p" : "b";
|
|
106224
|
+
const bytes = Array(Math.ceil(statuses.length / (mode === "p" ? 4 : 8))).fill(0);
|
|
106225
|
+
if (mode === "p")
|
|
106226
|
+
statuses.forEach((s, i2) => {
|
|
106227
|
+
bytes[i2 >> 2] |= (s === "approved" ? 1 : s === "rejected" ? 2 : 0) << i2 % 4 * 2;
|
|
106228
|
+
});
|
|
106229
|
+
if (mode === "b")
|
|
106230
|
+
statuses.forEach((s, i2) => {
|
|
106231
|
+
if (s === "rejected")
|
|
106232
|
+
bytes[i2 >> 3] |= 1 << i2 % 8;
|
|
106233
|
+
});
|
|
106234
|
+
const body2 = ["CR1", scope2, statuses.length, hash4(idsHash), hash4(contentHash2), mode, mode === "b" || mode === "p" ? pack(bytes) : ""].join(".");
|
|
106235
|
+
const code3 = `${body2}.${checksum(body2)}`;
|
|
106236
|
+
if (code3.length <= 980)
|
|
106237
|
+
return [code3];
|
|
106238
|
+
const total = Math.ceil(code3.length / 900);
|
|
106239
|
+
if (total > 200)
|
|
106240
|
+
throw new Error("Review decisions exceed 200 segments; use a smaller collection scope");
|
|
106241
|
+
const identity = checksum(code3);
|
|
106242
|
+
return Array.from({ length: total }, (_, i2) => `CRP1.${identity}.${i2 + 1}.${total}.${code3.slice(i2 * 900, (i2 + 1) * 900)}`);
|
|
106243
|
+
}
|
|
106244
|
+
function decode2(input) {
|
|
106245
|
+
if (input.length > 250000)
|
|
106246
|
+
throw new Error("Review code exceeds the supported size");
|
|
106247
|
+
const lines = input.trim().split(/\s+/);
|
|
106248
|
+
let code3 = lines[0];
|
|
106249
|
+
if (code3.startsWith("CRP1.")) {
|
|
106250
|
+
const parts = new Map;
|
|
106251
|
+
let identity = "", total = 0;
|
|
106252
|
+
for (const line of lines) {
|
|
106253
|
+
const match = /^CRP1\.([a-f0-9]{8})\.([1-9][0-9]*)\.([1-9][0-9]*)\.(.+)$/.exec(line);
|
|
106254
|
+
if (!match || line.length > 980)
|
|
106255
|
+
throw new Error("Invalid review code segment");
|
|
106256
|
+
const index2 = Number(match[2]), count2 = Number(match[3]);
|
|
106257
|
+
if (count2 > 200 || index2 > count2 || parts.has(index2) || total && (total !== count2 || identity !== match[1])) {
|
|
106258
|
+
throw new Error("Duplicate or mixed review code segments");
|
|
106259
|
+
}
|
|
106260
|
+
identity = match[1];
|
|
106261
|
+
total = count2;
|
|
106262
|
+
parts.set(index2, match[4]);
|
|
106263
|
+
}
|
|
106264
|
+
if (parts.size !== total)
|
|
106265
|
+
throw new Error(`Missing review code segments: received ${parts.size} of ${total}; collect all segments before applying`);
|
|
106266
|
+
code3 = Array.from({ length: total }, (_, i2) => parts.get(i2 + 1)).join("");
|
|
106267
|
+
if (checksum(code3) !== identity)
|
|
106268
|
+
throw new Error("Review code segment checksum mismatch");
|
|
106269
|
+
} else if (lines.length !== 1 || code3.length > 980)
|
|
106270
|
+
throw new Error("Copy each complete review code segment unchanged");
|
|
106271
|
+
const fields = code3.split(".");
|
|
106272
|
+
if (fields.length !== 8 || fields[0] !== "CR1" || checksum(fields.slice(0, 7).join(".")) !== fields[7]) {
|
|
106273
|
+
throw new Error("Review code is damaged or unsupported; copy it again from the report");
|
|
106274
|
+
}
|
|
106275
|
+
const [, scope2, countText, ids, content3, mode, data2] = fields;
|
|
106276
|
+
if (!/^[a-z][a-z0-9-]*$/.test(scope2) || !/^[1-9][0-9]*$/.test(countText))
|
|
106277
|
+
throw new Error("Invalid review scope");
|
|
106278
|
+
const count = Number(countText);
|
|
106279
|
+
if (count > 1e6 || !["a", "r", "b", "p"].includes(mode))
|
|
106280
|
+
throw new Error("Invalid review decisions");
|
|
106281
|
+
const bytes = unpack(data2);
|
|
106282
|
+
const perByte = mode === "p" ? 4 : 8;
|
|
106283
|
+
if (mode === "b" || mode === "p" ? bytes.length !== Math.ceil(count / perByte) || count % perByte !== 0 && bytes.at(-1) >>> count % perByte * (mode === "p" ? 2 : 1) !== 0 : data2 !== "") {
|
|
106284
|
+
throw new Error("Invalid review decision bitmap");
|
|
106285
|
+
}
|
|
106286
|
+
const statuses = Array.from({ length: count }, (_, i2) => {
|
|
106287
|
+
if (mode === "p") {
|
|
106288
|
+
const value = bytes[i2 >> 2] >>> i2 % 4 * 2 & 3;
|
|
106289
|
+
if (value === 3)
|
|
106290
|
+
throw new Error("Invalid pending review bitmap");
|
|
106291
|
+
return value === 1 ? "approved" : value === 2 ? "rejected" : "pending";
|
|
106292
|
+
}
|
|
106293
|
+
return mode === "r" || mode === "b" && bytes[i2 >> 3] & 1 << i2 % 8 ? "rejected" : "approved";
|
|
106294
|
+
});
|
|
106295
|
+
if (statuses.every((s) => s === "pending"))
|
|
106296
|
+
throw new Error("Review contains no decisions");
|
|
106297
|
+
return { scope: scope2, count, idsHash: unhash(ids), contentHash: unhash(content3), statuses };
|
|
106298
|
+
}
|
|
106299
|
+
return { encode, decode: decode2 };
|
|
106300
|
+
}
|
|
106301
|
+
|
|
106514
106302
|
// src/project/review.ts
|
|
106515
106303
|
init_cliFeedback();
|
|
106516
106304
|
init_errors3();
|
|
106517
106305
|
init_exitCode();
|
|
106518
|
-
import { readFile as
|
|
106306
|
+
import { readFile as readFile75 } from "node:fs/promises";
|
|
106519
106307
|
import { isAbsolute as isAbsolute18, resolve as resolve30 } from "node:path";
|
|
106520
106308
|
|
|
106521
106309
|
// src/project/reviewApply.ts
|
|
@@ -106528,8 +106316,8 @@ init_writeLock();
|
|
|
106528
106316
|
init_reviewApplyIndexer();
|
|
106529
106317
|
init_approvedKnowledgeSnapshots();
|
|
106530
106318
|
import { existsSync as existsSync25 } from "node:fs";
|
|
106531
|
-
import { readFile as
|
|
106532
|
-
import { join as
|
|
106319
|
+
import { readFile as readFile73 } from "node:fs/promises";
|
|
106320
|
+
import { join as join94 } from "node:path";
|
|
106533
106321
|
|
|
106534
106322
|
// src/project/reviewCandidateAuthority.ts
|
|
106535
106323
|
init_src2();
|
|
@@ -106626,7 +106414,7 @@ async function prepareApprovedPage(input) {
|
|
|
106626
106414
|
next: "Refresh the current production or article revision, then reopen Review before approval."
|
|
106627
106415
|
});
|
|
106628
106416
|
}
|
|
106629
|
-
const relPath =
|
|
106417
|
+
const relPath = join94("knowledge", input.record.path);
|
|
106630
106418
|
const existingView = findApprovedPageForArticleId(input.record.indexer_candidate.artifact_ref, input.approvedPageIndex);
|
|
106631
106419
|
const previousPath = input.record.approved_revision?.previous_path;
|
|
106632
106420
|
if (previousPath !== undefined && (!isSafeKnowledgeTargetPath(previousPath.split("/")[0], previousPath) || previousPath.includes("\\")))
|
|
@@ -106641,13 +106429,13 @@ async function prepareApprovedPage(input) {
|
|
|
106641
106429
|
next: "Resolve the approved page path migration explicitly before approving this candidate."
|
|
106642
106430
|
});
|
|
106643
106431
|
}
|
|
106644
|
-
const absPath =
|
|
106645
|
-
const existing = existsSync25(absPath) ? await
|
|
106432
|
+
const absPath = join94(input.projectRoot, relPath);
|
|
106433
|
+
const existing = existsSync25(absPath) ? await readFile73(absPath, "utf8") : undefined;
|
|
106646
106434
|
let previous3;
|
|
106647
106435
|
if (previousPath !== undefined) {
|
|
106648
106436
|
if (existing !== undefined || existingView?.relPath !== `knowledge/${previousPath}`)
|
|
106649
106437
|
throw new TypeError("Page move destination or original identity changed; refresh its revision.");
|
|
106650
|
-
previous3 = { path: `knowledge/${previousPath}`, content: await
|
|
106438
|
+
previous3 = { path: `knowledge/${previousPath}`, content: await readFile73(join94(input.projectRoot, "knowledge", previousPath), "utf8") };
|
|
106651
106439
|
}
|
|
106652
106440
|
if (input.record.approved_revision !== undefined) {
|
|
106653
106441
|
const base = previous3?.content ?? existing;
|
|
@@ -106694,7 +106482,7 @@ async function prepareApprovedPage(input) {
|
|
|
106694
106482
|
}
|
|
106695
106483
|
async function readProjectFileMaybe(projectRoot, relPath) {
|
|
106696
106484
|
try {
|
|
106697
|
-
return await
|
|
106485
|
+
return await readFile73(join94(projectRoot, relPath), "utf8");
|
|
106698
106486
|
} catch (error) {
|
|
106699
106487
|
if (error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
106700
106488
|
return;
|
|
@@ -106824,6 +106612,36 @@ async function applyReviewDecisions(input) {
|
|
|
106824
106612
|
const rows = await readCandidateRecords(input.projectRoot);
|
|
106825
106613
|
const nextRows = [...rows];
|
|
106826
106614
|
const decisions = expandReviewPayload(input.payload, rows);
|
|
106615
|
+
const scoped = rows.filter((r) => r.status === "draft" && (input.payload.scope?.kind === "all" || r.collection === input.payload.collection)).sort((a, b) => a.candidate_id < b.candidate_id ? -1 : 1);
|
|
106616
|
+
if (input.payload.baseline_hash && input.payload.baseline_hash !== await reviewSiteBaselineHash(input.projectRoot, scoped.map((r) => r.approved_revision?.previous_path ?? r.path))) {
|
|
106617
|
+
throw new ContextError(ExitCode.WorkspaceStateError, "Review navigation or approved baseline changed; generate a fresh report", {
|
|
106618
|
+
category: ErrorCategory.WorkspaceStateInvalid,
|
|
106619
|
+
code: "review-baseline-stale",
|
|
106620
|
+
next: "context review html --all --format json"
|
|
106621
|
+
});
|
|
106622
|
+
}
|
|
106623
|
+
const repairIds = new Set;
|
|
106624
|
+
const repairs = (input.payload.feedback_repairs ?? []).map((repair) => {
|
|
106625
|
+
const row = scoped[repair.index];
|
|
106626
|
+
if (!Number.isInteger(repair.index) || !row || repairIds.has(repair.index) || !repair.instruction.trim() || input.payload.encoded_statuses?.[repair.index] !== "pending")
|
|
106627
|
+
throw new Error("Invalid review revision request");
|
|
106628
|
+
repairIds.add(repair.index);
|
|
106629
|
+
const quote = (value) => "'" + value.replace(/'/gu, "'\\''") + "'";
|
|
106630
|
+
return {
|
|
106631
|
+
candidate_id: row.candidate_id,
|
|
106632
|
+
path: row.path,
|
|
106633
|
+
fingerprint: row.fingerprint,
|
|
106634
|
+
instruction: repair.instruction,
|
|
106635
|
+
command: `context revise ${quote(row.candidate_id)} --instruction ${quote(repair.instruction)} --format json`
|
|
106636
|
+
};
|
|
106637
|
+
});
|
|
106638
|
+
const feedbackPath = repairs.length ? `.tmp/context-runtime/review-feedback/${indexerProtocolDigest(input.payload).replace("sha256:", "")}.json` : undefined;
|
|
106639
|
+
const feedbackTarget = feedbackPath === undefined ? undefined : reviewFileTarget({
|
|
106640
|
+
path: feedbackPath,
|
|
106641
|
+
baseContent: await readProjectFileMaybe(input.projectRoot, feedbackPath),
|
|
106642
|
+
targetContent: JSON.stringify({ created_at: now, repairs }, null, 2) + `
|
|
106643
|
+
`
|
|
106644
|
+
});
|
|
106827
106645
|
const approvesAnyCandidate = decisions.some((decision) => decision.status === "approved");
|
|
106828
106646
|
const candidateAuthority = approvesAnyCandidate ? await loadReviewCandidateAuthority(input.projectRoot) : undefined;
|
|
106829
106647
|
const approvedPageIndex = approvesAnyCandidate ? await buildApprovedArticleIndex(input.projectRoot) : {
|
|
@@ -106887,7 +106705,7 @@ async function applyReviewDecisions(input) {
|
|
|
106887
106705
|
});
|
|
106888
106706
|
}
|
|
106889
106707
|
seenApprovedIds.set(approvedRef, row.candidate_id);
|
|
106890
|
-
const approvedPath =
|
|
106708
|
+
const approvedPath = join94("knowledge", row.path);
|
|
106891
106709
|
const previousPathCandidate = seenApprovedPaths.get(knowledgeTargetPathKey(approvedPath));
|
|
106892
106710
|
if (previousPathCandidate !== undefined) {
|
|
106893
106711
|
throw new ContextError(ExitCode.UserError, `multiple approved review decisions target the same knowledge path: ${approvedPath}`, {
|
|
@@ -106937,13 +106755,14 @@ async function applyReviewDecisions(input) {
|
|
|
106937
106755
|
for (const path3 of approvedPageIndex.byRelPath.keys()) {
|
|
106938
106756
|
if (pagesToWrite.some((page) => page.relPath === path3 || page.previous?.path === path3))
|
|
106939
106757
|
continue;
|
|
106940
|
-
const before = await
|
|
106758
|
+
const before = await readFile73(join94(input.projectRoot, path3), "utf8");
|
|
106941
106759
|
const local = path3.replace(/^knowledge\//u, "");
|
|
106942
106760
|
const after = moveKnowledgeLinkTargets2(before, local, local, moved);
|
|
106943
106761
|
navigationTargets.push(reviewFileTarget({ path: path3, baseContent: before, targetContent: after }));
|
|
106944
106762
|
}
|
|
106945
106763
|
}
|
|
106946
106764
|
const targets = [
|
|
106765
|
+
feedbackTarget,
|
|
106947
106766
|
await prepareApprovedKnowledgeSnapshotTarget({ projectRoot: input.projectRoot, pages: pagesToWrite, candidates: rows }),
|
|
106948
106767
|
...navigationTargets,
|
|
106949
106768
|
...pagesToWrite.flatMap((page) => page.previous === undefined ? [] : [reviewFileTarget({
|
|
@@ -106979,6 +106798,7 @@ async function applyReviewDecisions(input) {
|
|
|
106979
106798
|
}
|
|
106980
106799
|
return {
|
|
106981
106800
|
applied: decisions.length,
|
|
106801
|
+
...feedbackPath ? { repairs, feedback_path: feedbackPath } : {},
|
|
106982
106802
|
approved,
|
|
106983
106803
|
rejected,
|
|
106984
106804
|
unchanged,
|
|
@@ -106991,13 +106811,13 @@ async function applyReviewDecisions(input) {
|
|
|
106991
106811
|
}
|
|
106992
106812
|
|
|
106993
106813
|
// src/project/reviewMaintenance.ts
|
|
106994
|
-
import { readFile as
|
|
106814
|
+
import { readFile as readFile74, writeFile as writeFile26 } from "node:fs/promises";
|
|
106995
106815
|
init_writeLock();
|
|
106996
106816
|
init_verifyFrontmatter();
|
|
106997
106817
|
function deprecateApprovedPage(input) {
|
|
106998
106818
|
return withProjectWriteLock(input.projectRoot, "deprecate-article", async () => {
|
|
106999
106819
|
const page = await approvedPageForArticleId(input.projectRoot, input.viewRef);
|
|
107000
|
-
const original = await
|
|
106820
|
+
const original = await readFile74(page.path, "utf8");
|
|
107001
106821
|
const content3 = parseFrontmatterLoose(original).deprecated === true ? original : updateFrontmatter(original, (metadata) => ({ ...metadata, deprecated: true, timestamp: new Date().toISOString() }));
|
|
107002
106822
|
const changed = content3 !== original;
|
|
107003
106823
|
if (changed)
|
|
@@ -107016,13 +106836,13 @@ function deprecateApprovedPage(input) {
|
|
|
107016
106836
|
init_candidateLedger();
|
|
107017
106837
|
|
|
107018
106838
|
// src/project/localHtmlReport.ts
|
|
107019
|
-
import { execFile as
|
|
107020
|
-
import { isAbsolute as isAbsolute17, join as
|
|
106839
|
+
import { execFile as execFile11 } from "node:child_process";
|
|
106840
|
+
import { isAbsolute as isAbsolute17, join as join95 } from "node:path";
|
|
107021
106841
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
107022
|
-
import { promisify as
|
|
107023
|
-
var execFileAsync5 =
|
|
106842
|
+
import { promisify as promisify11 } from "node:util";
|
|
106843
|
+
var execFileAsync5 = promisify11(execFile11);
|
|
107024
106844
|
function htmlReportReference(input) {
|
|
107025
|
-
const absolutePath = isAbsolute17(input.path) ? input.path :
|
|
106845
|
+
const absolutePath = isAbsolute17(input.path) ? input.path : join95(input.projectRoot, input.path);
|
|
107026
106846
|
return {
|
|
107027
106847
|
format: "html",
|
|
107028
106848
|
path: input.path,
|
|
@@ -107194,7 +107014,7 @@ function parseReviewPayloadText(raw) {
|
|
|
107194
107014
|
async function readReviewPayloadFile(filePath2) {
|
|
107195
107015
|
let raw;
|
|
107196
107016
|
try {
|
|
107197
|
-
raw = await
|
|
107017
|
+
raw = await readFile75(filePath2, "utf8");
|
|
107198
107018
|
} catch (error) {
|
|
107199
107019
|
const message = error instanceof Error ? error.message : String(error);
|
|
107200
107020
|
throw new ContextError(ExitCode.UserError, `review payload file cannot be read: ${filePath2}`, {
|
|
@@ -107206,6 +107026,24 @@ async function readReviewPayloadFile(filePath2) {
|
|
|
107206
107026
|
}
|
|
107207
107027
|
if (raw.trim().startsWith("CR")) {
|
|
107208
107028
|
try {
|
|
107029
|
+
if (raw.trim().startsWith("CR2.")) {
|
|
107030
|
+
const feedback = createReviewFeedbackCodec().decode(raw);
|
|
107031
|
+
const collection2 = feedback.scope === "all" ? undefined : assertCollection(feedback.scope);
|
|
107032
|
+
return {
|
|
107033
|
+
decisions: [],
|
|
107034
|
+
encoded_statuses: feedback.statuses.map((s) => s === "revised" ? "pending" : s),
|
|
107035
|
+
feedback_repairs: feedback.repairs,
|
|
107036
|
+
baseline_hash: feedback.baselineHash,
|
|
107037
|
+
...collection2 === undefined ? {} : { collection: collection2 },
|
|
107038
|
+
scope: {
|
|
107039
|
+
kind: collection2 === undefined ? "all" : "collection",
|
|
107040
|
+
...collection2 === undefined ? {} : { collection: collection2 },
|
|
107041
|
+
count: feedback.statuses.length,
|
|
107042
|
+
ids_sha256: feedback.idsHash,
|
|
107043
|
+
candidates_sha256: feedback.contentHash
|
|
107044
|
+
}
|
|
107045
|
+
};
|
|
107046
|
+
}
|
|
107209
107047
|
const decoded = createReviewCodeCodec().decode(raw);
|
|
107210
107048
|
const collection = decoded.scope === "all" ? undefined : assertCollection(decoded.scope);
|
|
107211
107049
|
return {
|
|
@@ -107223,7 +107061,7 @@ async function readReviewPayloadFile(filePath2) {
|
|
|
107223
107061
|
} catch (error) {
|
|
107224
107062
|
throw new ContextError(ExitCode.UserError, error instanceof Error ? error.message : String(error), {
|
|
107225
107063
|
category: ErrorCategory.UserInputInvalid,
|
|
107226
|
-
next: "Copy
|
|
107064
|
+
next: "Copy the complete review code and all following revision instruction lines unchanged from the current report into one input file. Older segmented codes require every segment."
|
|
107227
107065
|
});
|
|
107228
107066
|
}
|
|
107229
107067
|
}
|
|
@@ -107243,6 +107081,7 @@ function formatApplyResult(result, format2) {
|
|
|
107243
107081
|
`rejected: ${result.rejected}`,
|
|
107244
107082
|
`materialized: ${result.materialized}`,
|
|
107245
107083
|
`removed: ${result.removed}`,
|
|
107084
|
+
...(result.repairs ?? []).map((r) => `repair ${r.path}: ${r.command}`),
|
|
107246
107085
|
`unchanged: ${result.unchanged}`,
|
|
107247
107086
|
`candidate file: ${result.candidateFileUpdated ? "updated" : "unchanged"}`,
|
|
107248
107087
|
...result.pages.map((page) => `page: ${page}`),
|
|
@@ -107489,6 +107328,7 @@ async function runReviewApproveAllCommand(input) {
|
|
|
107489
107328
|
`materialized: ${result.materialized}`,
|
|
107490
107329
|
`unchanged: ${result.unchanged}`,
|
|
107491
107330
|
`removed: ${result.removed}`,
|
|
107331
|
+
...(result.repairs ?? []).map((r) => `repair ${r.path}: ${r.command}`),
|
|
107492
107332
|
...continuation === undefined ? [] : [`workflow: ${continuation.state}`, continuation.stop.message]
|
|
107493
107333
|
]
|
|
107494
107334
|
}));
|
|
@@ -108025,8 +107865,8 @@ init_exitCode();
|
|
|
108025
107865
|
init_maintenanceStorage();
|
|
108026
107866
|
init_productionFeedback();
|
|
108027
107867
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
108028
|
-
import { readFile as
|
|
108029
|
-
import { join as
|
|
107868
|
+
import { readFile as readFile76 } from "node:fs/promises";
|
|
107869
|
+
import { join as join96 } from "node:path";
|
|
108030
107870
|
async function beginProductionRevision(input) {
|
|
108031
107871
|
return withProductionFeedback({ operation: "revision" }, () => withProjectWriteLock(input.projectRoot, "production-revision", async () => {
|
|
108032
107872
|
await recoverDurableMultiFileTransactions(input.projectRoot);
|
|
@@ -108065,7 +107905,7 @@ async function beginProductionRevision(input) {
|
|
|
108065
107905
|
const formal = approved.byPath.get(path3);
|
|
108066
107906
|
if (!prior && !formal)
|
|
108067
107907
|
throw invalid2("Write the current task first; there is no article draft to revise yet.");
|
|
108068
|
-
const markdown = prior?.body ?? await
|
|
107908
|
+
const markdown = prior?.body ?? await readFile76(await safeProjectTarget(input.projectRoot, join96("knowledge", path3)), "utf8");
|
|
108069
107909
|
const sections = prior?.indexer_candidate.sections.map((section) => ({ id: section.section_key, references: section.references })) ?? formal?.sections;
|
|
108070
107910
|
const sources = [];
|
|
108071
107911
|
for (const source2 of owner.sources) {
|
|
@@ -108241,8 +108081,8 @@ init_actionInputWorkspace();
|
|
|
108241
108081
|
init_cliFeedback();
|
|
108242
108082
|
init_errors3();
|
|
108243
108083
|
init_exitCode();
|
|
108244
|
-
var
|
|
108245
|
-
import { readFile as
|
|
108084
|
+
var import_yaml41 = __toESM(require_dist(), 1);
|
|
108085
|
+
import { readFile as readFile77 } from "node:fs/promises";
|
|
108246
108086
|
function userInputError2(message, detail = {}) {
|
|
108247
108087
|
return new ContextError(ExitCode.UserError, message, {
|
|
108248
108088
|
category: ErrorCategory.UserInputInvalid,
|
|
@@ -108253,7 +108093,7 @@ function parsePayloadText(raw) {
|
|
|
108253
108093
|
const trimmed = raw.trimStart();
|
|
108254
108094
|
if (trimmed.startsWith("{") || trimmed.startsWith("["))
|
|
108255
108095
|
return JSON.parse(raw);
|
|
108256
|
-
return
|
|
108096
|
+
return import_yaml41.default.parse(raw);
|
|
108257
108097
|
}
|
|
108258
108098
|
function isCompleteJsonLine(raw) {
|
|
108259
108099
|
if (!raw.endsWith(`
|
|
@@ -108311,7 +108151,7 @@ async function readPayloadTextFromStdin(stdin) {
|
|
|
108311
108151
|
}
|
|
108312
108152
|
async function readPayloadText(path3) {
|
|
108313
108153
|
if (path3 !== "-")
|
|
108314
|
-
return
|
|
108154
|
+
return readFile77(path3, "utf8");
|
|
108315
108155
|
return readPayloadTextFromStdin(process.stdin);
|
|
108316
108156
|
}
|
|
108317
108157
|
async function readYamlOrJsonInput(input) {
|
|
@@ -108673,7 +108513,7 @@ function registerRuntimeEventLogCommands(program2) {
|
|
|
108673
108513
|
if (result.status === "pending") {
|
|
108674
108514
|
const reason = result.last_result?.reason;
|
|
108675
108515
|
const requiresNetworkAccess = isNetworkFailure(reason);
|
|
108676
|
-
throw new ContextError(ExitCode.ExternalToolError, requiresNetworkAccess ? "runtime event delivery could not reach the configured sink" : "runtime event delivery was rejected by the configured sink", {
|
|
108516
|
+
throw new ContextError(ExitCode.ExternalToolError, requiresNetworkAccess ? "runtime event delivery could not reach the configured sink" : reason === "invalid_batch" ? "local telemetry bridge rejected the batch before network delivery; update the sink CLI for protocol compatibility" : "runtime event delivery was rejected by the configured sink", {
|
|
108677
108517
|
category: ErrorCategory.ExternalToolFailed,
|
|
108678
108518
|
reason_code: requiresNetworkAccess ? "runtime-events-network-unavailable" : "runtime-events-delivery-failed",
|
|
108679
108519
|
pending_count: result.pending_count,
|
|
@@ -108702,10 +108542,10 @@ init_actionInputWorkspace();
|
|
|
108702
108542
|
|
|
108703
108543
|
// src/project/actionCompletionOutput.ts
|
|
108704
108544
|
init_atomicWrite();
|
|
108705
|
-
var
|
|
108545
|
+
var import_yaml44 = __toESM(require_dist(), 1);
|
|
108706
108546
|
import { Buffer as Buffer4 } from "node:buffer";
|
|
108707
|
-
import { createHash as
|
|
108708
|
-
import { join as
|
|
108547
|
+
import { createHash as createHash30 } from "node:crypto";
|
|
108548
|
+
import { join as join100 } from "node:path";
|
|
108709
108549
|
var INLINE_LIMIT = 16 * 1024;
|
|
108710
108550
|
function record4(value) {
|
|
108711
108551
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
@@ -108718,7 +108558,7 @@ function shortText(value, limit = 400) {
|
|
|
108718
108558
|
}
|
|
108719
108559
|
function serializeActionCompletion(value, format2) {
|
|
108720
108560
|
return format2 === "json" ? `${JSON.stringify(value, null, 2)}
|
|
108721
|
-
` :
|
|
108561
|
+
` : import_yaml44.default.stringify(value);
|
|
108722
108562
|
}
|
|
108723
108563
|
async function prepareActionCompletionOutput(input) {
|
|
108724
108564
|
if (input.verbose)
|
|
@@ -108730,9 +108570,9 @@ async function prepareActionCompletionOutput(input) {
|
|
|
108730
108570
|
const production = typeof result.stage_state === "string";
|
|
108731
108571
|
if (production && Buffer4.byteLength(full) <= INLINE_LIMIT)
|
|
108732
108572
|
return input.result;
|
|
108733
|
-
const digest6 =
|
|
108734
|
-
const root2 =
|
|
108735
|
-
const resultFile =
|
|
108573
|
+
const digest6 = createHash30("sha256").update(full).digest("hex");
|
|
108574
|
+
const root2 = join100(input.projectRoot, ".tmp/context-runtime/action-results");
|
|
108575
|
+
const resultFile = join100(root2, `${digest6}.json`);
|
|
108736
108576
|
await atomicWriteFile(resultFile, full);
|
|
108737
108577
|
if (production)
|
|
108738
108578
|
return {
|
|
@@ -108743,7 +108583,7 @@ async function prepareActionCompletionOutput(input) {
|
|
|
108743
108583
|
...pick(result, ["next", "next_preparation"])
|
|
108744
108584
|
};
|
|
108745
108585
|
const next2 = record4(result.next) ?? record4(record4(result.workflow)?.current) ?? record4(record4(result.continuation)?.next);
|
|
108746
|
-
const nextFile = next2 === undefined ? undefined :
|
|
108586
|
+
const nextFile = next2 === undefined ? undefined : join100(root2, `${digest6}.next.json`);
|
|
108747
108587
|
if (nextFile !== undefined)
|
|
108748
108588
|
await atomicWriteFile(nextFile, serializeActionCompletion(next2, "json"));
|
|
108749
108589
|
const outcomes = (Array.isArray(result.outcomes) ? result.outcomes : []).map(record4).filter((item) => item !== undefined);
|
|
@@ -108784,7 +108624,7 @@ async function prepareActionCompletionOutput(input) {
|
|
|
108784
108624
|
]),
|
|
108785
108625
|
next_route: next2 === undefined ? null : {
|
|
108786
108626
|
file: nextFile,
|
|
108787
|
-
digest: `sha256:${
|
|
108627
|
+
digest: `sha256:${createHash30("sha256").update(serializeActionCompletion(next2, "json")).digest("hex")}`,
|
|
108788
108628
|
...pick(next2, ["revision", "node", "availability"]),
|
|
108789
108629
|
commands: next2.commands,
|
|
108790
108630
|
gate: next2.gate === undefined ? undefined : pick(record4(next2.gate), ["id", "resolution", "delegatable"])
|
|
@@ -108831,7 +108671,7 @@ init_errors3();
|
|
|
108831
108671
|
init_cliFeedback();
|
|
108832
108672
|
init_exitCode();
|
|
108833
108673
|
init_productionFeedback();
|
|
108834
|
-
var
|
|
108674
|
+
var import_yaml45 = __toESM(require_dist(), 1);
|
|
108835
108675
|
import { relative as relative24, resolve as resolve32 } from "node:path";
|
|
108836
108676
|
async function completeCurrentProductionAction(input) {
|
|
108837
108677
|
return withProductionFeedback({ operation: "action", file: input.submissionPath }, () => withProjectWriteLock(input.projectRoot, "production-action", async () => {
|
|
@@ -108850,7 +108690,7 @@ async function completeCurrentProductionAction(input) {
|
|
|
108850
108690
|
throw invalid3("Production requires a stage-local manifest file, not stdin.");
|
|
108851
108691
|
const path3 = relative24(resolve32(input.projectRoot, productionAgentDirectory(stage.id)), resolve32(input.cwd, input.submissionPath));
|
|
108852
108692
|
const manifest = await readProductionFile({ projectRoot: input.projectRoot, stage: stage.id, path: path3 });
|
|
108853
|
-
const value =
|
|
108693
|
+
const value = import_yaml45.default.parse(manifest.text, { uniqueKeys: true });
|
|
108854
108694
|
if (!value || typeof value !== "object" || !("stage" in value) || value.stage !== stage.id) {
|
|
108855
108695
|
throw invalid3("The manifest must identify the current production stage. No tasks were saved.");
|
|
108856
108696
|
}
|
|
@@ -108997,7 +108837,7 @@ init_errors3();
|
|
|
108997
108837
|
init_cliFeedback();
|
|
108998
108838
|
init_exitCode();
|
|
108999
108839
|
init_productionFeedback();
|
|
109000
|
-
var
|
|
108840
|
+
var import_yaml46 = __toESM(require_dist(), 1);
|
|
109001
108841
|
import { relative as relative25, resolve as resolve33 } from "node:path";
|
|
109002
108842
|
var productionKnownTasksSchema = productionPlanInputSchema.omit({ stage: true });
|
|
109003
108843
|
async function prepareKnownProductionTasks(input) {
|
|
@@ -109012,7 +108852,7 @@ async function prepareKnownProductionTasks(input) {
|
|
|
109012
108852
|
input_schema: zodToJsonSchema(productionKnownTasksSchema, { $refStrategy: "none" }),
|
|
109013
108853
|
next_action: { command: "context status --format json" }
|
|
109014
108854
|
});
|
|
109015
|
-
const plan = productionKnownTasksSchema.parse(
|
|
108855
|
+
const plan = productionKnownTasksSchema.parse(import_yaml46.default.parse(fixed2.text, { uniqueKeys: true }));
|
|
109016
108856
|
const request = await productionPlanningRequest(input.projectRoot);
|
|
109017
108857
|
let stage = await readProductionStage(input.projectRoot);
|
|
109018
108858
|
if (!request || stage?.report_approved || (stage ? ![stage.id, request.revision].includes(input.revision) : request.revision !== input.revision)) {
|
|
@@ -109022,7 +108862,7 @@ async function prepareKnownProductionTasks(input) {
|
|
|
109022
108862
|
await prepareCurrentProductionStage({ projectRoot: input.projectRoot, revision: input.revision });
|
|
109023
108863
|
stage = await readProductionStage(input.projectRoot);
|
|
109024
108864
|
}
|
|
109025
|
-
const text10 =
|
|
108865
|
+
const text10 = import_yaml46.default.stringify({ ...plan, stage: stage.id });
|
|
109026
108866
|
const result = await submitProductionPlan({
|
|
109027
108867
|
projectRoot: input.projectRoot,
|
|
109028
108868
|
stage: stage.id,
|
|
@@ -109144,9 +108984,9 @@ function registerProjectActionCommands(program2) {
|
|
|
109144
108984
|
// src/commands/cleanClaudePluginCache.ts
|
|
109145
108985
|
init_cliFeedback();
|
|
109146
108986
|
import { existsSync as existsSync27 } from "node:fs";
|
|
109147
|
-
import { readdir as
|
|
108987
|
+
import { readdir as readdir26, rm as rm20 } from "node:fs/promises";
|
|
109148
108988
|
import { homedir } from "node:os";
|
|
109149
|
-
import { join as
|
|
108989
|
+
import { join as join101 } from "node:path";
|
|
109150
108990
|
var ORPHAN_MARKER = ".orphaned_at";
|
|
109151
108991
|
var CLAUDE_PLUGIN_CACHE_ROOT_ENV = "C4A_CLAUDE_PLUGIN_CACHE_ROOT";
|
|
109152
108992
|
var CLAUDE_PLUGIN_CACHE_HOME_ENV = "C4A_CLAUDE_PLUGIN_CACHE_HOME";
|
|
@@ -109159,23 +108999,23 @@ async function cleanClaudePluginCache(opts = {}) {
|
|
|
109159
108999
|
lines.push("· claude plugin cache: missing — nothing to clean");
|
|
109160
109000
|
return { lines, removed, scanned };
|
|
109161
109001
|
}
|
|
109162
|
-
const marketplaces = await
|
|
109002
|
+
const marketplaces = await readdir26(cacheRoot, { withFileTypes: true });
|
|
109163
109003
|
for (const mp of marketplaces) {
|
|
109164
109004
|
if (!mp.isDirectory())
|
|
109165
109005
|
continue;
|
|
109166
|
-
const mpDir =
|
|
109167
|
-
const plugins = await
|
|
109006
|
+
const mpDir = join101(cacheRoot, mp.name);
|
|
109007
|
+
const plugins = await readdir26(mpDir, { withFileTypes: true });
|
|
109168
109008
|
for (const pl of plugins) {
|
|
109169
109009
|
if (!pl.isDirectory())
|
|
109170
109010
|
continue;
|
|
109171
|
-
const plDir =
|
|
109172
|
-
const versions = await
|
|
109011
|
+
const plDir = join101(mpDir, pl.name);
|
|
109012
|
+
const versions = await readdir26(plDir, { withFileTypes: true });
|
|
109173
109013
|
for (const ver of versions) {
|
|
109174
109014
|
if (!ver.isDirectory())
|
|
109175
109015
|
continue;
|
|
109176
109016
|
scanned += 1;
|
|
109177
|
-
const verDir =
|
|
109178
|
-
const markerPath =
|
|
109017
|
+
const verDir = join101(plDir, ver.name);
|
|
109018
|
+
const markerPath = join101(verDir, ORPHAN_MARKER);
|
|
109179
109019
|
if (!existsSync27(markerPath))
|
|
109180
109020
|
continue;
|
|
109181
109021
|
const label2 = `${mp.name}/${pl.name}/${ver.name}`;
|
|
@@ -109207,11 +109047,11 @@ function resolveClaudePluginCacheRoot(opts) {
|
|
|
109207
109047
|
if (explicitRoot)
|
|
109208
109048
|
return explicitRoot;
|
|
109209
109049
|
const home = opts.home ?? process.env[CLAUDE_PLUGIN_CACHE_HOME_ENV] ?? homedir();
|
|
109210
|
-
return
|
|
109050
|
+
return join101(home, ".claude", "plugins", "cache");
|
|
109211
109051
|
}
|
|
109212
109052
|
async function isEmptyDir(dir) {
|
|
109213
109053
|
try {
|
|
109214
|
-
const entries2 = await
|
|
109054
|
+
const entries2 = await readdir26(dir);
|
|
109215
109055
|
return entries2.length === 0;
|
|
109216
109056
|
} catch {
|
|
109217
109057
|
return false;
|
|
@@ -109239,13 +109079,13 @@ init_exitCode();
|
|
|
109239
109079
|
|
|
109240
109080
|
// src/lib/packageVersion.ts
|
|
109241
109081
|
import { existsSync as existsSync28, readFileSync as readFileSync9 } from "node:fs";
|
|
109242
|
-
import { dirname as
|
|
109082
|
+
import { dirname as dirname41, join as join102 } from "node:path";
|
|
109243
109083
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
109244
109084
|
function readPackageVersion() {
|
|
109245
109085
|
try {
|
|
109246
|
-
let dir =
|
|
109086
|
+
let dir = dirname41(fileURLToPath7(import.meta.url));
|
|
109247
109087
|
for (let depth = 0;depth < 8; depth += 1) {
|
|
109248
|
-
const packagePath =
|
|
109088
|
+
const packagePath = join102(dir, "package.json");
|
|
109249
109089
|
if (existsSync28(packagePath)) {
|
|
109250
109090
|
const parsed = JSON.parse(readFileSync9(packagePath, "utf8"));
|
|
109251
109091
|
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
@@ -109253,7 +109093,7 @@ function readPackageVersion() {
|
|
|
109253
109093
|
}
|
|
109254
109094
|
break;
|
|
109255
109095
|
}
|
|
109256
|
-
const parent =
|
|
109096
|
+
const parent = dirname41(dir);
|
|
109257
109097
|
if (parent === dir)
|
|
109258
109098
|
break;
|
|
109259
109099
|
dir = parent;
|
|
@@ -109268,24 +109108,24 @@ function readPackageVersion() {
|
|
|
109268
109108
|
|
|
109269
109109
|
// src/project/sourceCommands.ts
|
|
109270
109110
|
init_src2();
|
|
109271
|
-
import { readFile as
|
|
109111
|
+
import { readFile as readFile90 } from "node:fs/promises";
|
|
109272
109112
|
import { isAbsolute as isAbsolute22, resolve as resolve37 } from "node:path";
|
|
109273
109113
|
init_cliFeedback();
|
|
109274
109114
|
init_errors3();
|
|
109275
109115
|
init_exitCode();
|
|
109276
|
-
var
|
|
109116
|
+
var import_yaml50 = __toESM(require_dist(), 1);
|
|
109277
109117
|
|
|
109278
109118
|
// src/project/repoSourceRecovery.ts
|
|
109279
109119
|
init_cliFeedback();
|
|
109280
109120
|
init_errors3();
|
|
109281
109121
|
init_exitCode();
|
|
109282
|
-
import { execFile as
|
|
109122
|
+
import { execFile as execFile12 } from "node:child_process";
|
|
109283
109123
|
import { existsSync as existsSync29 } from "node:fs";
|
|
109284
109124
|
import { lstat as lstat12, mkdir as mkdir34, readlink as readlink2, realpath as realpath10, rm as rm21, symlink as symlink4 } from "node:fs/promises";
|
|
109285
|
-
import { basename as basename11, dirname as
|
|
109286
|
-
import { promisify as
|
|
109125
|
+
import { basename as basename11, dirname as dirname42, isAbsolute as isAbsolute19, relative as relative26, resolve as resolve34 } from "node:path";
|
|
109126
|
+
import { promisify as promisify12 } from "node:util";
|
|
109287
109127
|
init_writeLock();
|
|
109288
|
-
var execFileAsync6 =
|
|
109128
|
+
var execFileAsync6 = promisify12(execFile12);
|
|
109289
109129
|
var RECOVERY_SCHEMA = "context.repository-source-recovery.v1";
|
|
109290
109130
|
function userInputError3(message, detail = {}) {
|
|
109291
109131
|
return new ContextError(ExitCode.UserError, message, {
|
|
@@ -109467,7 +109307,7 @@ async function cloneCheckout(input) {
|
|
|
109467
109307
|
next: `Use local mode with path ${JSON.stringify(target)} after inspecting the existing checkout.`
|
|
109468
109308
|
});
|
|
109469
109309
|
}
|
|
109470
|
-
await mkdir34(
|
|
109310
|
+
await mkdir34(dirname42(target), { recursive: true });
|
|
109471
109311
|
const cloneArgs = ["clone", "--no-checkout", "--depth=1", "--filter=blob:none", input.remote, target];
|
|
109472
109312
|
try {
|
|
109473
109313
|
await execFileAsync6("git", cloneArgs, { encoding: "utf8", maxBuffer: 4 * 1024 * 1024 });
|
|
@@ -109516,7 +109356,7 @@ async function bindLocalAlias(input) {
|
|
|
109516
109356
|
const stats = await lstat12(alias).catch(() => null);
|
|
109517
109357
|
if (stats !== null) {
|
|
109518
109358
|
if (stats.isSymbolicLink()) {
|
|
109519
|
-
const actual = resolve34(
|
|
109359
|
+
const actual = resolve34(dirname42(alias), await readlink2(alias));
|
|
109520
109360
|
const actualReal = await realpath10(actual).catch(() => null);
|
|
109521
109361
|
if (actualReal !== null && actualReal === await realpath10(input.checkout))
|
|
109522
109362
|
return;
|
|
@@ -109532,8 +109372,8 @@ async function bindLocalAlias(input) {
|
|
|
109532
109372
|
});
|
|
109533
109373
|
}
|
|
109534
109374
|
}
|
|
109535
|
-
await mkdir34(
|
|
109536
|
-
await symlink4(relative26(
|
|
109375
|
+
await mkdir34(dirname42(alias), { recursive: true });
|
|
109376
|
+
await symlink4(relative26(dirname42(alias), input.checkout) || ".", alias);
|
|
109537
109377
|
}
|
|
109538
109378
|
function selectPhysicalGroup(sources, selector) {
|
|
109539
109379
|
const direct = selectRepoSources(sources, selector);
|
|
@@ -109627,11 +109467,11 @@ async function restoreRepositorySources(input) {
|
|
|
109627
109467
|
// src/project/sourceDocumentStatus.ts
|
|
109628
109468
|
init_src2();
|
|
109629
109469
|
import { existsSync as existsSync30 } from "node:fs";
|
|
109630
|
-
import { readFile as
|
|
109631
|
-
import { join as
|
|
109470
|
+
import { readFile as readFile83 } from "node:fs/promises";
|
|
109471
|
+
import { join as join104 } from "node:path";
|
|
109632
109472
|
// src/project/sourceCommandViews.ts
|
|
109633
|
-
import { readFile as
|
|
109634
|
-
import { join as
|
|
109473
|
+
import { readFile as readFile82 } from "node:fs/promises";
|
|
109474
|
+
import { join as join103 } from "node:path";
|
|
109635
109475
|
init_workspace();
|
|
109636
109476
|
init_documentBatchManifest();
|
|
109637
109477
|
function repoSourceAgentView(source2) {
|
|
@@ -109694,7 +109534,7 @@ async function fileSourceAgentViewWithNextAction(input) {
|
|
|
109694
109534
|
};
|
|
109695
109535
|
}
|
|
109696
109536
|
function documentSourceManifestPath(source2) {
|
|
109697
|
-
return source2.snapshot?.manifest ??
|
|
109537
|
+
return source2.snapshot?.manifest ?? join103(source2.materializedAt, "manifest.json");
|
|
109698
109538
|
}
|
|
109699
109539
|
async function fileSourceDocumentSiteHint(input) {
|
|
109700
109540
|
const detection = await detectDocumentSiteFiles({
|
|
@@ -109704,7 +109544,7 @@ async function fileSourceDocumentSiteHint(input) {
|
|
|
109704
109544
|
let snapshotConfigured = false;
|
|
109705
109545
|
const manifest = documentSourceManifestPath(input.source);
|
|
109706
109546
|
try {
|
|
109707
|
-
const parsed = parseDocumentSnapshotForSource(JSON.parse(await
|
|
109547
|
+
const parsed = parseDocumentSnapshotForSource(JSON.parse(await readFile82(join103(input.projectRoot, manifest), "utf8")), input.source.name);
|
|
109708
109548
|
snapshotConfigured = manifestUsesMdxJsonDocs(parsed);
|
|
109709
109549
|
} catch {
|
|
109710
109550
|
snapshotConfigured = false;
|
|
@@ -109736,11 +109576,11 @@ async function larkSourceAgentViewWithNextAction(input) {
|
|
|
109736
109576
|
// src/project/sourceDocumentStatus.ts
|
|
109737
109577
|
init_documentBatchManifest();
|
|
109738
109578
|
function documentSourceManifestPath2(source2) {
|
|
109739
|
-
return source2.snapshot?.manifest ??
|
|
109579
|
+
return source2.snapshot?.manifest ?? join104(source2.materializedAt, "manifest.json");
|
|
109740
109580
|
}
|
|
109741
109581
|
async function documentSnapshotState(input) {
|
|
109742
109582
|
const manifest = documentSourceManifestPath2(input.source);
|
|
109743
|
-
const manifestPath =
|
|
109583
|
+
const manifestPath = join104(input.projectRoot, manifest);
|
|
109744
109584
|
if (!existsSync30(manifestPath)) {
|
|
109745
109585
|
return {
|
|
109746
109586
|
snapshotReady: false,
|
|
@@ -109751,7 +109591,7 @@ async function documentSnapshotState(input) {
|
|
|
109751
109591
|
};
|
|
109752
109592
|
}
|
|
109753
109593
|
try {
|
|
109754
|
-
const parsed = findDocumentSnapshotForSource(JSON.parse(await
|
|
109594
|
+
const parsed = findDocumentSnapshotForSource(JSON.parse(await readFile83(manifestPath, "utf8")), input.source.name);
|
|
109755
109595
|
if (parsed === null) {
|
|
109756
109596
|
return {
|
|
109757
109597
|
snapshotReady: false,
|
|
@@ -109819,7 +109659,7 @@ async function documentSnapshotState(input) {
|
|
|
109819
109659
|
const missing = [
|
|
109820
109660
|
...parsed.files.map((file) => file.path),
|
|
109821
109661
|
...(parsed.assets ?? []).filter((asset) => asset.content_hash !== undefined).map((asset) => asset.path)
|
|
109822
|
-
].find((path3) => !existsSync30(
|
|
109662
|
+
].find((path3) => !existsSync30(join104(input.projectRoot, input.source.materializedAt, path3)));
|
|
109823
109663
|
if (missing !== undefined) {
|
|
109824
109664
|
return {
|
|
109825
109665
|
snapshotReady: false,
|
|
@@ -109918,10 +109758,10 @@ init_atomicWrite();
|
|
|
109918
109758
|
init_cliFeedback();
|
|
109919
109759
|
init_errors3();
|
|
109920
109760
|
init_exitCode();
|
|
109921
|
-
var
|
|
109922
|
-
import { createHash as
|
|
109923
|
-
import { readFile as
|
|
109924
|
-
import { basename as basename12, extname as extname15, isAbsolute as isAbsolute20, join as
|
|
109761
|
+
var import_yaml47 = __toESM(require_dist(), 1);
|
|
109762
|
+
import { createHash as createHash31 } from "node:crypto";
|
|
109763
|
+
import { readFile as readFile84, realpath as realpath11 } from "node:fs/promises";
|
|
109764
|
+
import { basename as basename12, extname as extname15, isAbsolute as isAbsolute20, join as join105, relative as relative27, resolve as resolve35 } from "node:path";
|
|
109925
109765
|
init_writeLock();
|
|
109926
109766
|
var SOURCE_NAME_PATTERN3 = /^[a-z0-9][a-z0-9._-]*$/u;
|
|
109927
109767
|
function isDateSourceNamespace(value) {
|
|
@@ -109950,7 +109790,7 @@ function defaultLarkModule(input) {
|
|
|
109950
109790
|
if (titleSlug.length > 0)
|
|
109951
109791
|
return titleSlug;
|
|
109952
109792
|
}
|
|
109953
|
-
const opaqueSlug = (kind, identity) => `${kind}-${
|
|
109793
|
+
const opaqueSlug = (kind, identity) => `${kind}-${createHash31("sha256").update(identity).digest("hex").slice(0, 12)}`;
|
|
109954
109794
|
if (input.url !== undefined) {
|
|
109955
109795
|
try {
|
|
109956
109796
|
const parsed = new URL(input.url);
|
|
@@ -110017,8 +109857,8 @@ function assertSafeFileInclude(value) {
|
|
|
110017
109857
|
}
|
|
110018
109858
|
async function readRegistryDocument(projectRoot, registryPath2) {
|
|
110019
109859
|
try {
|
|
110020
|
-
const content3 = await
|
|
110021
|
-
return content3.trim().length === 0 ? { sources: [] } :
|
|
109860
|
+
const content3 = await readFile84(join105(projectRoot, registryPath2), "utf8");
|
|
109861
|
+
return content3.trim().length === 0 ? { sources: [] } : import_yaml47.default.parse(content3);
|
|
110022
109862
|
} catch (error) {
|
|
110023
109863
|
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
110024
109864
|
return { sources: [] };
|
|
@@ -110135,7 +109975,7 @@ async function addFileSourceUnlocked(input) {
|
|
|
110135
109975
|
const record6 = entry2;
|
|
110136
109976
|
return record6.name !== input.name && record6.id !== input.name;
|
|
110137
109977
|
}), nextEntry];
|
|
110138
|
-
await atomicWriteFile(
|
|
109978
|
+
await atomicWriteFile(join105(input.projectRoot, DEFAULT_FILE_SOURCES_REGISTRY_PATH), import_yaml47.default.stringify({ sources: nextSources }));
|
|
110139
109979
|
const updated = await loadSourcesRegistry({ rootDir: input.projectRoot });
|
|
110140
109980
|
const entry = updated.files.find((source2) => source2.name === input.name || source2.id === input.name);
|
|
110141
109981
|
if (entry === undefined) {
|
|
@@ -110191,7 +110031,7 @@ async function addLarkSourceUnlocked(input) {
|
|
|
110191
110031
|
const record6 = entry2;
|
|
110192
110032
|
return record6.name !== input.name && record6.id !== input.name;
|
|
110193
110033
|
}), nextEntry];
|
|
110194
|
-
await atomicWriteFile(
|
|
110034
|
+
await atomicWriteFile(join105(input.projectRoot, DEFAULT_LARK_SOURCES_REGISTRY_PATH), import_yaml47.default.stringify({ sources: nextSources }));
|
|
110195
110035
|
const updated = await loadSourcesRegistry({ rootDir: input.projectRoot });
|
|
110196
110036
|
const entry = updated.larks.find((source2) => source2.name === input.name || source2.id === input.name);
|
|
110197
110037
|
if (entry === undefined) {
|
|
@@ -110297,12 +110137,12 @@ function parseBatchItem(value, index2) {
|
|
|
110297
110137
|
const url = optionalString(record6, "url", path3);
|
|
110298
110138
|
const docToken = optionalString(record6, "docToken", path3);
|
|
110299
110139
|
const wikiToken = optionalString(record6, "wikiToken", path3);
|
|
110300
|
-
const
|
|
110140
|
+
const title2 = optionalString(record6, "title", path3);
|
|
110301
110141
|
const module = optionalString(record6, "module", path3) ?? defaultLarkModule({
|
|
110302
110142
|
...url !== undefined ? { url } : {},
|
|
110303
110143
|
...docToken !== undefined ? { docToken } : {},
|
|
110304
110144
|
...wikiToken !== undefined ? { wikiToken } : {},
|
|
110305
|
-
...
|
|
110145
|
+
...title2 !== undefined ? { title: title2 } : {}
|
|
110306
110146
|
});
|
|
110307
110147
|
return {
|
|
110308
110148
|
type,
|
|
@@ -110310,7 +110150,7 @@ function parseBatchItem(value, index2) {
|
|
|
110310
110150
|
...url !== undefined ? { url } : {},
|
|
110311
110151
|
...docToken !== undefined ? { docToken } : {},
|
|
110312
110152
|
...wikiToken !== undefined ? { wikiToken } : {},
|
|
110313
|
-
...
|
|
110153
|
+
...title2 !== undefined ? { title: title2 } : {}
|
|
110314
110154
|
};
|
|
110315
110155
|
}
|
|
110316
110156
|
function parseBatchPayload(payload) {
|
|
@@ -110391,11 +110231,11 @@ init_candidateLedger();
|
|
|
110391
110231
|
init_documentBatchManifest();
|
|
110392
110232
|
init_workspace();
|
|
110393
110233
|
init_writeLock();
|
|
110394
|
-
var
|
|
110234
|
+
var import_yaml48 = __toESM(require_dist(), 1);
|
|
110395
110235
|
import { existsSync as existsSync31 } from "node:fs";
|
|
110396
|
-
import { createHash as
|
|
110397
|
-
import { readFile as
|
|
110398
|
-
import { isAbsolute as isAbsolute21, join as
|
|
110236
|
+
import { createHash as createHash32 } from "node:crypto";
|
|
110237
|
+
import { readFile as readFile85, readdir as readdir27, rm as rm22 } from "node:fs/promises";
|
|
110238
|
+
import { isAbsolute as isAbsolute21, join as join106, relative as relative28, resolve as resolve36, sep as sep8 } from "node:path";
|
|
110399
110239
|
function sourceIdentity(source2) {
|
|
110400
110240
|
if (source2.kind === "source.collection")
|
|
110401
110241
|
return;
|
|
@@ -110427,10 +110267,10 @@ function collectStrings(value, output) {
|
|
|
110427
110267
|
}
|
|
110428
110268
|
}
|
|
110429
110269
|
async function yamlReferences(input) {
|
|
110430
|
-
const absolutePath =
|
|
110270
|
+
const absolutePath = join106(input.projectRoot, input.path);
|
|
110431
110271
|
if (!existsSync31(absolutePath))
|
|
110432
110272
|
return false;
|
|
110433
|
-
const parsed =
|
|
110273
|
+
const parsed = import_yaml48.default.parse(await readFile85(absolutePath, "utf8"));
|
|
110434
110274
|
const strings = [];
|
|
110435
110275
|
collectStrings(parsed, strings);
|
|
110436
110276
|
return strings.some((value) => stringReferencesSource(value, input.source));
|
|
@@ -110556,11 +110396,11 @@ async function registryRemovalWrite(projectRoot, source2) {
|
|
|
110556
110396
|
const path3 = registryPath2(source2.type);
|
|
110557
110397
|
if (path3 === null)
|
|
110558
110398
|
return;
|
|
110559
|
-
const absolutePath =
|
|
110560
|
-
const document4 = existsSync31(absolutePath) ?
|
|
110399
|
+
const absolutePath = join106(projectRoot, path3);
|
|
110400
|
+
const document4 = existsSync31(absolutePath) ? import_yaml48.default.parse(await readFile85(absolutePath, "utf8")) : { sources: [] };
|
|
110561
110401
|
return {
|
|
110562
110402
|
path: absolutePath,
|
|
110563
|
-
bytes:
|
|
110403
|
+
bytes: import_yaml48.default.stringify(removeDocumentEntry(document4, source2))
|
|
110564
110404
|
};
|
|
110565
110405
|
}
|
|
110566
110406
|
function safeManagedMaterializedPath(projectRoot, source2) {
|
|
@@ -110575,7 +110415,7 @@ function safeManagedMaterializedPath(projectRoot, source2) {
|
|
|
110575
110415
|
return absolute;
|
|
110576
110416
|
}
|
|
110577
110417
|
function safeManagedManifestPath(projectRoot, source2) {
|
|
110578
|
-
const manifest = source2.manifest ??
|
|
110418
|
+
const manifest = source2.manifest ?? join106(source2.materializedAt, "manifest.json");
|
|
110579
110419
|
if (isAbsolute21(manifest))
|
|
110580
110420
|
throw unsafeOwnership(source2, manifest);
|
|
110581
110421
|
const absolute = resolve36(projectRoot, manifest);
|
|
@@ -110610,7 +110450,7 @@ function projectRelative(projectRoot, path3) {
|
|
|
110610
110450
|
return relative28(projectRoot, path3).split(sep8).join("/");
|
|
110611
110451
|
}
|
|
110612
110452
|
function digest6(value) {
|
|
110613
|
-
return `sha256:${
|
|
110453
|
+
return `sha256:${createHash32("sha256").update(JSON.stringify(value)).digest("hex")}`;
|
|
110614
110454
|
}
|
|
110615
110455
|
async function sharedMaterializedOwners(projectRoot, source2) {
|
|
110616
110456
|
const target = safeManagedMaterializedPath(projectRoot, source2);
|
|
@@ -110704,7 +110544,7 @@ async function createRemovalPlan(projectRoot, selector) {
|
|
|
110704
110544
|
source: source2,
|
|
110705
110545
|
registry: registryPath2(source2.type),
|
|
110706
110546
|
registryBytes: registryWrite?.bytes ?? null,
|
|
110707
|
-
managedBytes: source2.type === "note" || source2.type === "sessions" ? await
|
|
110547
|
+
managedBytes: source2.type === "note" || source2.type === "sessions" ? await readFile85(absoluteRemovals[0], "utf8") : null,
|
|
110708
110548
|
references,
|
|
110709
110549
|
cleanup,
|
|
110710
110550
|
manifestBytes: manifestWrite?.bytes ?? null
|
|
@@ -110737,10 +110577,10 @@ function publicRemovalResult(plan, action) {
|
|
|
110737
110577
|
};
|
|
110738
110578
|
}
|
|
110739
110579
|
async function pruneExtractRuntime(projectRoot, source2) {
|
|
110740
|
-
const fingerprintPath =
|
|
110580
|
+
const fingerprintPath = join106(projectRoot, ".tmp/context-runtime/extract/source-fingerprints.json");
|
|
110741
110581
|
const removedPhaseIds = new Set;
|
|
110742
110582
|
if (existsSync31(fingerprintPath)) {
|
|
110743
|
-
const parsed = JSON.parse(await
|
|
110583
|
+
const parsed = JSON.parse(await readFile85(fingerprintPath, "utf8"));
|
|
110744
110584
|
const phases = parsed.phases ?? {};
|
|
110745
110585
|
const next2 = Object.fromEntries(Object.entries(phases).filter(([phaseId, raw]) => {
|
|
110746
110586
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
@@ -110754,27 +110594,27 @@ async function pruneExtractRuntime(projectRoot, source2) {
|
|
|
110754
110594
|
await atomicWriteFile(fingerprintPath, `${JSON.stringify({ ...parsed, phases: next2 }, null, 2)}
|
|
110755
110595
|
`);
|
|
110756
110596
|
}
|
|
110757
|
-
const phaseOwnershipPath =
|
|
110597
|
+
const phaseOwnershipPath = join106(projectRoot, ".tmp/context-runtime/extract/custom-phase-candidates.json");
|
|
110758
110598
|
if (existsSync31(phaseOwnershipPath) && removedPhaseIds.size > 0) {
|
|
110759
|
-
const parsed = JSON.parse(await
|
|
110599
|
+
const parsed = JSON.parse(await readFile85(phaseOwnershipPath, "utf8"));
|
|
110760
110600
|
const phases = Object.fromEntries(Object.entries(parsed.phases ?? {}).filter(([phaseId]) => !removedPhaseIds.has(phaseId)));
|
|
110761
110601
|
await atomicWriteFile(phaseOwnershipPath, `${JSON.stringify({ ...parsed, phases }, null, 2)}
|
|
110762
110602
|
`);
|
|
110763
110603
|
}
|
|
110764
|
-
const symbolPath =
|
|
110604
|
+
const symbolPath = join106(projectRoot, ".tmp/context-runtime/extract/source-symbols.json");
|
|
110765
110605
|
if (existsSync31(symbolPath)) {
|
|
110766
|
-
const parsed = JSON.parse(await
|
|
110606
|
+
const parsed = JSON.parse(await readFile85(symbolPath, "utf8"));
|
|
110767
110607
|
const symbols = Array.isArray(parsed.symbols) ? parsed.symbols.filter((entry) => entry === null || typeof entry !== "object" || Array.isArray(entry) || entry.source !== source2.name) : [];
|
|
110768
110608
|
const phaseFingerprints = Object.fromEntries(Object.entries(parsed.phaseFingerprints ?? {}).filter(([phaseId]) => !removedPhaseIds.has(phaseId)));
|
|
110769
110609
|
await atomicWriteFile(symbolPath, `${JSON.stringify({ ...parsed, phaseFingerprints, symbols }, null, 2)}
|
|
110770
110610
|
`);
|
|
110771
110611
|
}
|
|
110772
|
-
const snapshotRoot =
|
|
110612
|
+
const snapshotRoot = join106(projectRoot, ".tmp/context-runtime/extract/candidates");
|
|
110773
110613
|
const visit4 = async (directory) => {
|
|
110774
110614
|
if (!existsSync31(directory))
|
|
110775
110615
|
return;
|
|
110776
|
-
for (const entry of await
|
|
110777
|
-
const path3 =
|
|
110616
|
+
for (const entry of await readdir27(directory, { withFileTypes: true })) {
|
|
110617
|
+
const path3 = join106(directory, entry.name);
|
|
110778
110618
|
if (entry.isDirectory()) {
|
|
110779
110619
|
await visit4(path3);
|
|
110780
110620
|
continue;
|
|
@@ -110782,7 +110622,7 @@ async function pruneExtractRuntime(projectRoot, source2) {
|
|
|
110782
110622
|
if (!entry.isFile() || !entry.name.endsWith(".json"))
|
|
110783
110623
|
continue;
|
|
110784
110624
|
try {
|
|
110785
|
-
const parsed = JSON.parse(await
|
|
110625
|
+
const parsed = JSON.parse(await readFile85(path3, "utf8"));
|
|
110786
110626
|
const refs = Array.isArray(parsed.source_refs) ? parsed.source_refs : [];
|
|
110787
110627
|
if (parsed.source === source2.name || refs.some((ref2) => typeof ref2 === "string" && stringReferencesSource(ref2, source2))) {
|
|
110788
110628
|
await rm22(path3, { force: true });
|
|
@@ -110824,7 +110664,7 @@ async function removeProjectSource(input) {
|
|
|
110824
110664
|
});
|
|
110825
110665
|
}
|
|
110826
110666
|
await applyAtomicFileBatch({
|
|
110827
|
-
transactionRoot:
|
|
110667
|
+
transactionRoot: join106(input.projectRoot, ".tmp", "context-runtime", "source-remove-transactions"),
|
|
110828
110668
|
writes: [...plan.registryWrite === undefined ? [] : [plan.registryWrite], ...plan.manifestWrite !== undefined ? [plan.manifestWrite] : []],
|
|
110829
110669
|
removals: plan.absoluteRemovals
|
|
110830
110670
|
});
|
|
@@ -110840,7 +110680,7 @@ init_workspace();
|
|
|
110840
110680
|
init_writeLock();
|
|
110841
110681
|
init_durableMultiFileTransaction();
|
|
110842
110682
|
init_durableSingleFileTransaction();
|
|
110843
|
-
import { readFile as
|
|
110683
|
+
import { readFile as readFile86 } from "node:fs/promises";
|
|
110844
110684
|
import ts from "typescript";
|
|
110845
110685
|
function generateSourceConfiguration(text10, selected) {
|
|
110846
110686
|
const file = ts.createSourceFile("index.ts", text10, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
@@ -110980,7 +110820,7 @@ function generateSourceConfiguration(text10, selected) {
|
|
|
110980
110820
|
async function configureRegisteredSources(projectRoot, selected) {
|
|
110981
110821
|
return withProjectWriteLock(projectRoot, "source-project-configuration", async () => {
|
|
110982
110822
|
const path3 = "src/index.ts";
|
|
110983
|
-
const config = JSON.parse(await
|
|
110823
|
+
const config = JSON.parse(await readFile86(await safeProjectTarget(projectRoot, "package.json"), "utf8"));
|
|
110984
110824
|
if (config.context?.entry !== path3)
|
|
110985
110825
|
return {
|
|
110986
110826
|
status: "manual",
|
|
@@ -110989,7 +110829,7 @@ async function configureRegisteredSources(projectRoot, selected) {
|
|
|
110989
110829
|
sources: selected
|
|
110990
110830
|
};
|
|
110991
110831
|
const target = await safeProjectTarget(projectRoot, path3);
|
|
110992
|
-
const text10 = await
|
|
110832
|
+
const text10 = await readFile86(target, "utf8");
|
|
110993
110833
|
const updated = generateSourceConfiguration(text10, selected);
|
|
110994
110834
|
if (updated === undefined)
|
|
110995
110835
|
return {
|
|
@@ -111069,7 +110909,7 @@ async function readIncludeList(projectRoot, path3) {
|
|
|
111069
110909
|
}
|
|
111070
110910
|
let content3;
|
|
111071
110911
|
try {
|
|
111072
|
-
content3 = await
|
|
110912
|
+
content3 = await readFile90(isAbsolute22(trimmed) ? resolve37(trimmed) : resolve37(projectRoot, trimmed), "utf8");
|
|
111073
110913
|
} catch (error) {
|
|
111074
110914
|
throw new ContextError(ExitCode.UserError, `cannot read include list: ${trimmed}`, {
|
|
111075
110915
|
category: ErrorCategory.UserInputInvalid,
|
|
@@ -111137,7 +110977,7 @@ function writeFormatted(value, format2) {
|
|
|
111137
110977
|
return;
|
|
111138
110978
|
}
|
|
111139
110979
|
if (format2 === "yaml") {
|
|
111140
|
-
process.stdout.write(
|
|
110980
|
+
process.stdout.write(import_yaml50.default.stringify(value));
|
|
111141
110981
|
return;
|
|
111142
110982
|
}
|
|
111143
110983
|
process.stdout.write(renderTable2(value));
|
|
@@ -111327,14 +111167,14 @@ report shown above. A direct maintenance call outside that Route may omit it.
|
|
|
111327
111167
|
const url = optionalString2(options.url);
|
|
111328
111168
|
const docToken = optionalString2(options.docToken);
|
|
111329
111169
|
const wikiToken = optionalString2(options.wikiToken);
|
|
111330
|
-
const
|
|
111170
|
+
const title2 = optionalString2(options.title);
|
|
111331
111171
|
const requestedModule = optionalString2(options.module);
|
|
111332
111172
|
const batchMode = sourceNamespace.generated || isDateSourceNamespace(sourceNamespace.name) || requestedModule !== undefined;
|
|
111333
111173
|
const module = batchMode ? requestedModule ?? defaultLarkModule({
|
|
111334
111174
|
...url !== undefined ? { url } : {},
|
|
111335
111175
|
...docToken !== undefined ? { docToken } : {},
|
|
111336
111176
|
...wikiToken !== undefined ? { wikiToken } : {},
|
|
111337
|
-
...
|
|
111177
|
+
...title2 !== undefined ? { title: title2 } : {}
|
|
111338
111178
|
}) : undefined;
|
|
111339
111179
|
const sourceName = module === undefined ? sourceNamespace.name : `${sourceNamespace.name}/${module}`;
|
|
111340
111180
|
const result = await addLarkSource({
|
|
@@ -111344,7 +111184,7 @@ report shown above. A direct maintenance call outside that Route may omit it.
|
|
|
111344
111184
|
...url !== undefined ? { url } : {},
|
|
111345
111185
|
...docToken !== undefined ? { docToken } : {},
|
|
111346
111186
|
...wikiToken !== undefined ? { wikiToken } : {},
|
|
111347
|
-
...
|
|
111187
|
+
...title2 !== undefined ? { title: title2 } : {}
|
|
111348
111188
|
});
|
|
111349
111189
|
writeFormatted(options.configure ? { ...result, configuration: await configureRegisteredSources(projectRoot, [{ type: "lark", name: sourceName }]) } : result, format2);
|
|
111350
111190
|
});
|
|
@@ -111458,19 +111298,19 @@ report shown above. A direct maintenance call outside that Route may omit it.
|
|
|
111458
111298
|
init_cliFeedback();
|
|
111459
111299
|
init_errors3();
|
|
111460
111300
|
init_exitCode();
|
|
111461
|
-
var
|
|
111301
|
+
var import_yaml52 = __toESM(require_dist(), 1);
|
|
111462
111302
|
|
|
111463
111303
|
// src/project/productionSkillCatalog.ts
|
|
111464
111304
|
init_errors3();
|
|
111465
111305
|
init_cliFeedback();
|
|
111466
111306
|
init_exitCode();
|
|
111467
|
-
var
|
|
111307
|
+
var import_yaml51 = __toESM(require_dist(), 1);
|
|
111468
111308
|
import { constants as constants6, existsSync as existsSync32 } from "node:fs";
|
|
111469
|
-
import { lstat as lstat13, open as open5, readdir as
|
|
111470
|
-
import { dirname as
|
|
111309
|
+
import { lstat as lstat13, open as open5, readdir as readdir28 } from "node:fs/promises";
|
|
111310
|
+
import { dirname as dirname43, join as join108, resolve as resolve38 } from "node:path";
|
|
111471
111311
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
111472
111312
|
function bundledSkillRoot() {
|
|
111473
|
-
const directory =
|
|
111313
|
+
const directory = dirname43(fileURLToPath8(import.meta.url));
|
|
111474
111314
|
const root2 = [resolve38(directory, "indexers/bundles"), resolve38(directory, "../../dist/indexers/bundles")].find((path3) => existsSync32(path3));
|
|
111475
111315
|
if (!root2)
|
|
111476
111316
|
throw new TypeError("Bundled skill files are unavailable; build or reinstall the Context CLI.");
|
|
@@ -111494,10 +111334,10 @@ async function listProductionSkills(root2) {
|
|
|
111494
111334
|
}
|
|
111495
111335
|
}
|
|
111496
111336
|
async function readProductionSkills(root2) {
|
|
111497
|
-
const entries2 = (await
|
|
111337
|
+
const entries2 = (await readdir28(root2, { withFileTypes: true })).filter((entry) => entry.isDirectory()).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
111498
111338
|
const skills = [];
|
|
111499
111339
|
for (const directory of entries2) {
|
|
111500
|
-
const entry =
|
|
111340
|
+
const entry = join108(root2, directory.name, "SKILL.md");
|
|
111501
111341
|
if (!(await lstat13(entry)).isFile())
|
|
111502
111342
|
throw new TypeError(`Skill entry must be a regular file: ${entry}`);
|
|
111503
111343
|
const handle2 = await open5(entry, constants6.O_RDONLY | constants6.O_NOFOLLOW | constants6.O_NONBLOCK);
|
|
@@ -111514,7 +111354,7 @@ async function readProductionSkills(root2) {
|
|
|
111514
111354
|
const frontmatter2 = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/u.exec(header);
|
|
111515
111355
|
if (!frontmatter2)
|
|
111516
111356
|
throw new TypeError(`Skill entry needs complete frontmatter within 64 KiB: ${entry}`);
|
|
111517
|
-
const value =
|
|
111357
|
+
const value = import_yaml51.default.parse(frontmatter2[1], { uniqueKeys: true });
|
|
111518
111358
|
if (!value || typeof value !== "object" || !("name" in value) || typeof value.name !== "string" || !value.name.trim() || !("description" in value) || typeof value.description !== "string" || !value.description.trim()) {
|
|
111519
111359
|
throw new TypeError(`Skill entry needs a name and description: ${entry}`);
|
|
111520
111360
|
}
|
|
@@ -111577,7 +111417,7 @@ function outputFormat2(options) {
|
|
|
111577
111417
|
}
|
|
111578
111418
|
function writeOutput(value, format2) {
|
|
111579
111419
|
process.stdout.write(format2 === "json" ? `${JSON.stringify(value, null, 2)}
|
|
111580
|
-
` :
|
|
111420
|
+
` : import_yaml52.default.stringify(value));
|
|
111581
111421
|
}
|
|
111582
111422
|
function registerProjectIndexerCommands(program2) {
|
|
111583
111423
|
const indexer = program2.command("indexer").description("Discover bundled skills or inspect a benchmark result");
|
|
@@ -111632,7 +111472,7 @@ init_exitCode();
|
|
|
111632
111472
|
init_workflowProvider();
|
|
111633
111473
|
init_workspace();
|
|
111634
111474
|
import { existsSync as existsSync33 } from "node:fs";
|
|
111635
|
-
import { dirname as
|
|
111475
|
+
import { dirname as dirname44, resolve as resolve39 } from "node:path";
|
|
111636
111476
|
function shellQuote7(value) {
|
|
111637
111477
|
return /^[A-Za-z0-9._/=-]+$/u.test(value) ? value : `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
111638
111478
|
}
|
|
@@ -111665,10 +111505,10 @@ function readyResult(input, projectRoot, relocation) {
|
|
|
111665
111505
|
return {
|
|
111666
111506
|
schema: "context.entry.v1",
|
|
111667
111507
|
guidance: {
|
|
111668
|
-
knowledge_updates: { path: resolve39(
|
|
111669
|
-
workspace_prepare: { path: resolve39(
|
|
111670
|
-
workspace_commit: { path: resolve39(
|
|
111671
|
-
workspace_restore: { path: resolve39(
|
|
111508
|
+
knowledge_updates: { path: resolve39(dirname44(contextWorkflowProviderPath()), "resources/procedures/knowledge-updates.md") },
|
|
111509
|
+
workspace_prepare: { path: resolve39(dirname44(contextWorkflowProviderPath()), "resources/procedures/workspace-prepare.md") },
|
|
111510
|
+
workspace_commit: { path: resolve39(dirname44(contextWorkflowProviderPath()), "resources/procedures/workspace-commit.md") },
|
|
111511
|
+
workspace_restore: { path: resolve39(dirname44(contextWorkflowProviderPath()), "resources/procedures/workspace-restore.md") }
|
|
111672
111512
|
},
|
|
111673
111513
|
state: relocation ? "workspace-relocation-required" : "workspace-ready",
|
|
111674
111514
|
cwd: resolve39(input.cwd),
|
|
@@ -111905,20 +111745,20 @@ init_cliFeedback();
|
|
|
111905
111745
|
init_errors3();
|
|
111906
111746
|
init_exitCode();
|
|
111907
111747
|
import { existsSync as existsSync35 } from "node:fs";
|
|
111908
|
-
import { dirname as
|
|
111748
|
+
import { dirname as dirname46, join as join110, resolve as resolve40 } from "node:path";
|
|
111909
111749
|
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
111910
111750
|
|
|
111911
111751
|
// src/project/pluginInstallTargets.ts
|
|
111912
111752
|
init_cliFeedback();
|
|
111913
111753
|
init_errors3();
|
|
111914
111754
|
init_exitCode();
|
|
111915
|
-
import { execFile as
|
|
111755
|
+
import { execFile as execFile13 } from "node:child_process";
|
|
111916
111756
|
import { existsSync as existsSync34 } from "node:fs";
|
|
111917
|
-
import { cp as cp2, mkdir as mkdir35, readdir as
|
|
111757
|
+
import { cp as cp2, mkdir as mkdir35, readdir as readdir29, readFile as readFile91, rename as rename9, rm as rm23, writeFile as writeFile27 } from "node:fs/promises";
|
|
111918
111758
|
import { homedir as homedir2 } from "node:os";
|
|
111919
|
-
import { dirname as
|
|
111920
|
-
import { promisify as
|
|
111921
|
-
var execFileAsync7 =
|
|
111759
|
+
import { dirname as dirname45, join as join109 } from "node:path";
|
|
111760
|
+
import { promisify as promisify13 } from "node:util";
|
|
111761
|
+
var execFileAsync7 = promisify13(execFile13);
|
|
111922
111762
|
var MARKETPLACE_NAME = "c4a";
|
|
111923
111763
|
var PLUGIN_ID = "c4a@c4a";
|
|
111924
111764
|
var PLUGIN_NAME = "c4a";
|
|
@@ -111970,23 +111810,23 @@ async function claudePluginInstalled(pluginId) {
|
|
|
111970
111810
|
}
|
|
111971
111811
|
}
|
|
111972
111812
|
function codexHome() {
|
|
111973
|
-
return process.env.CODEX_HOME?.trim() ||
|
|
111813
|
+
return process.env.CODEX_HOME?.trim() || join109(homedir2(), ".codex");
|
|
111974
111814
|
}
|
|
111975
111815
|
function claudePluginCacheRoot() {
|
|
111976
111816
|
const explicitRoot = process.env[CLAUDE_PLUGIN_CACHE_ROOT_ENV2]?.trim();
|
|
111977
111817
|
if (explicitRoot)
|
|
111978
111818
|
return explicitRoot;
|
|
111979
111819
|
const home = process.env[CLAUDE_PLUGIN_CACHE_HOME_ENV2]?.trim() || homedir2();
|
|
111980
|
-
return
|
|
111820
|
+
return join109(home, ".claude", "plugins", "cache");
|
|
111981
111821
|
}
|
|
111982
111822
|
function sharedSkillsRoot() {
|
|
111983
|
-
return process.env[SHARED_SKILLS_ROOT_ENV]?.trim() ||
|
|
111823
|
+
return process.env[SHARED_SKILLS_ROOT_ENV]?.trim() || join109(homedir2(), ".agents", "skills");
|
|
111984
111824
|
}
|
|
111985
111825
|
function claudeSkillsRoot() {
|
|
111986
|
-
return process.env[CLAUDE_SKILLS_ROOT_ENV]?.trim() ||
|
|
111826
|
+
return process.env[CLAUDE_SKILLS_ROOT_ENV]?.trim() || join109(homedir2(), ".claude", "skills");
|
|
111987
111827
|
}
|
|
111988
111828
|
function cursorPluginRoot() {
|
|
111989
|
-
return process.env[CURSOR_PLUGIN_ROOT_ENV]?.trim() ||
|
|
111829
|
+
return process.env[CURSOR_PLUGIN_ROOT_ENV]?.trim() || join109(homedir2(), ".cursor", "plugins", "local", PLUGIN_NAME);
|
|
111990
111830
|
}
|
|
111991
111831
|
function blockHeader(line) {
|
|
111992
111832
|
const match = line.match(/^\s*\[([^\]]+)\]\s*$/u);
|
|
@@ -112037,8 +111877,8 @@ function pruneLegacyCodexConfigContent(content3) {
|
|
|
112037
111877
|
`), removed };
|
|
112038
111878
|
}
|
|
112039
111879
|
async function pruneLegacyCodexConfig(dryRun, steps) {
|
|
112040
|
-
const configPath =
|
|
112041
|
-
const current2 = await
|
|
111880
|
+
const configPath = join109(codexHome(), "config.toml");
|
|
111881
|
+
const current2 = await readFile91(configPath, "utf8").catch(() => "");
|
|
112042
111882
|
if (!current2)
|
|
112043
111883
|
return;
|
|
112044
111884
|
const next2 = pruneLegacyCodexConfigContent(current2);
|
|
@@ -112054,10 +111894,10 @@ async function pruneLegacyCodexConfig(dryRun, steps) {
|
|
|
112054
111894
|
}
|
|
112055
111895
|
}
|
|
112056
111896
|
async function pruneCodexPluginCacheForName(pluginName, keepVersion, dryRun, steps) {
|
|
112057
|
-
const cacheRoot =
|
|
111897
|
+
const cacheRoot = join109(codexHome(), "plugins", "cache", MARKETPLACE_NAME, pluginName);
|
|
112058
111898
|
if (!existsSync34(cacheRoot))
|
|
112059
111899
|
return;
|
|
112060
|
-
const versions = (await
|
|
111900
|
+
const versions = (await readdir29(cacheRoot, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory() && entry.name !== keepVersion).map((entry) => entry.name).sort();
|
|
112061
111901
|
if (versions.length === 0)
|
|
112062
111902
|
return;
|
|
112063
111903
|
steps.push({
|
|
@@ -112066,7 +111906,7 @@ async function pruneCodexPluginCacheForName(pluginName, keepVersion, dryRun, ste
|
|
|
112066
111906
|
status: dryRun ? "planned" : "ran"
|
|
112067
111907
|
});
|
|
112068
111908
|
if (!dryRun)
|
|
112069
|
-
await Promise.all(versions.map((version3) => rm23(
|
|
111909
|
+
await Promise.all(versions.map((version3) => rm23(join109(cacheRoot, version3), { recursive: true, force: true })));
|
|
112070
111910
|
}
|
|
112071
111911
|
async function pruneCodexPluginCache(currentVersion, dryRun, steps) {
|
|
112072
111912
|
await pruneCodexPluginCacheForName(PLUGIN_NAME, currentVersion, dryRun, steps);
|
|
@@ -112076,7 +111916,7 @@ async function pruneCodexPluginCache(currentVersion, dryRun, steps) {
|
|
|
112076
111916
|
}
|
|
112077
111917
|
async function isEmptyDir2(dir) {
|
|
112078
111918
|
try {
|
|
112079
|
-
return (await
|
|
111919
|
+
return (await readdir29(dir)).length === 0;
|
|
112080
111920
|
} catch {
|
|
112081
111921
|
return false;
|
|
112082
111922
|
}
|
|
@@ -112086,19 +111926,19 @@ async function pruneClaudeOrphanContextCache(dryRun, steps) {
|
|
|
112086
111926
|
if (!existsSync34(cacheRoot))
|
|
112087
111927
|
return;
|
|
112088
111928
|
const removed = [];
|
|
112089
|
-
const marketplaces = await
|
|
111929
|
+
const marketplaces = await readdir29(cacheRoot, { withFileTypes: true }).catch(() => []);
|
|
112090
111930
|
for (const marketplace of marketplaces) {
|
|
112091
111931
|
if (!marketplace.isDirectory())
|
|
112092
111932
|
continue;
|
|
112093
|
-
const pluginDir =
|
|
111933
|
+
const pluginDir = join109(cacheRoot, marketplace.name, PLUGIN_NAME);
|
|
112094
111934
|
if (!existsSync34(pluginDir))
|
|
112095
111935
|
continue;
|
|
112096
|
-
const versions = await
|
|
111936
|
+
const versions = await readdir29(pluginDir, { withFileTypes: true }).catch(() => []);
|
|
112097
111937
|
for (const version3 of versions) {
|
|
112098
111938
|
if (!version3.isDirectory())
|
|
112099
111939
|
continue;
|
|
112100
|
-
const versionDir =
|
|
112101
|
-
if (!existsSync34(
|
|
111940
|
+
const versionDir = join109(pluginDir, version3.name);
|
|
111941
|
+
if (!existsSync34(join109(versionDir, ORPHAN_MARKER2)))
|
|
112102
111942
|
continue;
|
|
112103
111943
|
removed.push(`${marketplace.name}/${PLUGIN_NAME}/${version3.name}`);
|
|
112104
111944
|
if (!dryRun) {
|
|
@@ -112108,7 +111948,7 @@ async function pruneClaudeOrphanContextCache(dryRun, steps) {
|
|
|
112108
111948
|
if (!dryRun && await isEmptyDir2(pluginDir)) {
|
|
112109
111949
|
await rm23(pluginDir, { recursive: true, force: true });
|
|
112110
111950
|
}
|
|
112111
|
-
const marketplaceDir =
|
|
111951
|
+
const marketplaceDir = join109(cacheRoot, marketplace.name);
|
|
112112
111952
|
if (!dryRun && await isEmptyDir2(marketplaceDir)) {
|
|
112113
111953
|
await rm23(marketplaceDir, { recursive: true, force: true });
|
|
112114
111954
|
}
|
|
@@ -112129,7 +111969,7 @@ async function pruneClaudeLegacyPluginCache(dryRun, steps) {
|
|
|
112129
111969
|
return;
|
|
112130
111970
|
const removed = [];
|
|
112131
111971
|
for (const pluginName of LEGACY_PLUGIN_NAMES) {
|
|
112132
|
-
const pluginDir =
|
|
111972
|
+
const pluginDir = join109(cacheRoot, MARKETPLACE_NAME, pluginName);
|
|
112133
111973
|
if (!existsSync34(pluginDir))
|
|
112134
111974
|
continue;
|
|
112135
111975
|
removed.push(`${MARKETPLACE_NAME}/${pluginName}`);
|
|
@@ -112146,12 +111986,12 @@ async function pruneClaudeLegacyPluginCache(dryRun, steps) {
|
|
|
112146
111986
|
}
|
|
112147
111987
|
}
|
|
112148
111988
|
async function pruneClaudeSupersededContextCache(root2, dryRun, steps) {
|
|
112149
|
-
const manifest = await
|
|
111989
|
+
const manifest = await readFile91(join109(root2, "claude", ".claude-plugin", "plugin.json"), "utf8").then((content3) => JSON.parse(content3)).catch(() => null);
|
|
112150
111990
|
const currentVersion = typeof manifest?.version === "string" ? manifest.version : null;
|
|
112151
111991
|
if (!currentVersion)
|
|
112152
111992
|
return;
|
|
112153
|
-
const pluginDir =
|
|
112154
|
-
const staleVersions = (await
|
|
111993
|
+
const pluginDir = join109(claudePluginCacheRoot(), MARKETPLACE_NAME, PLUGIN_NAME);
|
|
111994
|
+
const staleVersions = (await readdir29(pluginDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory() && entry.name !== currentVersion).map((entry) => entry.name);
|
|
112155
111995
|
if (staleVersions.length === 0)
|
|
112156
111996
|
return;
|
|
112157
111997
|
steps.push({
|
|
@@ -112160,7 +112000,7 @@ async function pruneClaudeSupersededContextCache(root2, dryRun, steps) {
|
|
|
112160
112000
|
status: dryRun ? "planned" : "ran"
|
|
112161
112001
|
});
|
|
112162
112002
|
if (!dryRun) {
|
|
112163
|
-
await Promise.all(staleVersions.map((version3) => rm23(
|
|
112003
|
+
await Promise.all(staleVersions.map((version3) => rm23(join109(pluginDir, version3), { recursive: true, force: true })));
|
|
112164
112004
|
}
|
|
112165
112005
|
}
|
|
112166
112006
|
function enableCodexPluginConfig(content3) {
|
|
@@ -112224,36 +112064,36 @@ source = ${JSON.stringify(root2)}
|
|
|
112224
112064
|
`;
|
|
112225
112065
|
}
|
|
112226
112066
|
async function ensureCodexPluginEnabled() {
|
|
112227
|
-
const configPath =
|
|
112228
|
-
await mkdir35(
|
|
112229
|
-
const current2 = await
|
|
112067
|
+
const configPath = join109(codexHome(), "config.toml");
|
|
112068
|
+
await mkdir35(dirname45(configPath), { recursive: true });
|
|
112069
|
+
const current2 = await readFile91(configPath, "utf8").catch(() => "");
|
|
112230
112070
|
const next2 = enableCodexPluginConfig(current2);
|
|
112231
112071
|
if (next2 !== current2) {
|
|
112232
112072
|
await writeFile27(configPath, next2, "utf8");
|
|
112233
112073
|
}
|
|
112234
112074
|
}
|
|
112235
112075
|
async function ensureCodexLocalMarketplace(root2) {
|
|
112236
|
-
const configPath =
|
|
112237
|
-
await mkdir35(
|
|
112238
|
-
const current2 = await
|
|
112076
|
+
const configPath = join109(codexHome(), "config.toml");
|
|
112077
|
+
await mkdir35(dirname45(configPath), { recursive: true });
|
|
112078
|
+
const current2 = await readFile91(configPath, "utf8").catch(() => "");
|
|
112239
112079
|
const next2 = upsertCodexLocalMarketplaceConfig(current2, root2);
|
|
112240
112080
|
if (next2 !== current2) {
|
|
112241
112081
|
await writeFile27(configPath, next2, "utf8");
|
|
112242
112082
|
}
|
|
112243
112083
|
}
|
|
112244
112084
|
async function codexPluginVersion(root2) {
|
|
112245
|
-
const manifestPath =
|
|
112246
|
-
const manifest = JSON.parse(await
|
|
112085
|
+
const manifestPath = join109(root2, "codex", ".codex-plugin", "plugin.json");
|
|
112086
|
+
const manifest = JSON.parse(await readFile91(manifestPath, "utf8"));
|
|
112247
112087
|
if (typeof manifest.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(manifest.version)) {
|
|
112248
112088
|
throw new Error(`Codex plugin manifest has an invalid version: ${manifestPath}`);
|
|
112249
112089
|
}
|
|
112250
112090
|
return manifest.version;
|
|
112251
112091
|
}
|
|
112252
112092
|
function codexPluginCacheDir(version3) {
|
|
112253
|
-
return
|
|
112093
|
+
return join109(codexHome(), "plugins", "cache", MARKETPLACE_NAME, PLUGIN_NAME, version3);
|
|
112254
112094
|
}
|
|
112255
112095
|
async function replaceDirectoryFromSource(source2, target) {
|
|
112256
|
-
await mkdir35(
|
|
112096
|
+
await mkdir35(dirname45(target), { recursive: true });
|
|
112257
112097
|
const temporary = `${target}.tmp-${process.pid}-${Date.now()}`;
|
|
112258
112098
|
const previous3 = `${target}.previous-${process.pid}-${Date.now()}`;
|
|
112259
112099
|
await rm23(temporary, { recursive: true, force: true });
|
|
@@ -112273,7 +112113,7 @@ async function replaceDirectoryFromSource(source2, target) {
|
|
|
112273
112113
|
}
|
|
112274
112114
|
}
|
|
112275
112115
|
async function materializeCodexPluginCache(root2, version3, dryRun, steps) {
|
|
112276
|
-
const source2 =
|
|
112116
|
+
const source2 = join109(root2, "codex");
|
|
112277
112117
|
const target = codexPluginCacheDir(version3);
|
|
112278
112118
|
steps.push({
|
|
112279
112119
|
agent: "codex",
|
|
@@ -112285,16 +112125,16 @@ async function materializeCodexPluginCache(root2, version3, dryRun, steps) {
|
|
|
112285
112125
|
await replaceDirectoryFromSource(source2, target);
|
|
112286
112126
|
}
|
|
112287
112127
|
async function bundledProviderSkillNames(root2) {
|
|
112288
|
-
const skillsRoot =
|
|
112289
|
-
const entries2 = await
|
|
112128
|
+
const skillsRoot = join109(root2, "skills");
|
|
112129
|
+
const entries2 = await readdir29(skillsRoot, { withFileTypes: true });
|
|
112290
112130
|
const names = [];
|
|
112291
112131
|
for (const entry of entries2) {
|
|
112292
112132
|
if (!entry.isDirectory() || entry.name === "context")
|
|
112293
112133
|
continue;
|
|
112294
|
-
const skillPath =
|
|
112134
|
+
const skillPath = join109(skillsRoot, entry.name, "SKILL.md");
|
|
112295
112135
|
if (!existsSync34(skillPath))
|
|
112296
112136
|
continue;
|
|
112297
|
-
const skill = await
|
|
112137
|
+
const skill = await readFile91(skillPath, "utf8");
|
|
112298
112138
|
if (!/^\s*context-role:\s*["']?indexer-provider["']?\s*$/mu.test(skill))
|
|
112299
112139
|
continue;
|
|
112300
112140
|
names.push(entry.name);
|
|
@@ -112306,10 +112146,10 @@ async function bundledProviderSkillNames(root2) {
|
|
|
112306
112146
|
return names;
|
|
112307
112147
|
}
|
|
112308
112148
|
async function materializeProviderSkills(root2, targetRoot, agent, dryRun, steps) {
|
|
112309
|
-
const sourceRoot2 =
|
|
112149
|
+
const sourceRoot2 = join109(root2, "skills");
|
|
112310
112150
|
for (const name3 of await bundledProviderSkillNames(root2)) {
|
|
112311
|
-
const source2 =
|
|
112312
|
-
const target =
|
|
112151
|
+
const source2 = join109(sourceRoot2, name3);
|
|
112152
|
+
const target = join109(targetRoot, name3);
|
|
112313
112153
|
steps.push({
|
|
112314
112154
|
agent,
|
|
112315
112155
|
command: `materialize lifecycle Provider skill: ${shellQuote8(source2)} -> ${shellQuote8(target)}`,
|
|
@@ -112320,7 +112160,7 @@ async function materializeProviderSkills(root2, targetRoot, agent, dryRun, steps
|
|
|
112320
112160
|
}
|
|
112321
112161
|
}
|
|
112322
112162
|
async function installCursor(root2, dryRun, steps) {
|
|
112323
|
-
const source2 =
|
|
112163
|
+
const source2 = join109(root2, "cursor");
|
|
112324
112164
|
const target = cursorPluginRoot();
|
|
112325
112165
|
steps.push({
|
|
112326
112166
|
agent: "cursor",
|
|
@@ -112375,12 +112215,12 @@ async function installCodex(root2, dryRun, steps) {
|
|
|
112375
112215
|
steps.push({ agent: "codex", command: commandLine("codex", addArgs), status: dryRun ? "planned" : "ran" });
|
|
112376
112216
|
steps.push({
|
|
112377
112217
|
agent: "codex",
|
|
112378
|
-
command: `ensure ${shellQuote8(
|
|
112218
|
+
command: `ensure ${shellQuote8(join109(codexHome(), "config.toml"))} registers local marketplace ${shellQuote8(MARKETPLACE_NAME)}`,
|
|
112379
112219
|
status: dryRun ? "planned" : "ran"
|
|
112380
112220
|
});
|
|
112381
112221
|
steps.push({
|
|
112382
112222
|
agent: "codex",
|
|
112383
|
-
command: `ensure ${shellQuote8(
|
|
112223
|
+
command: `ensure ${shellQuote8(join109(codexHome(), "config.toml"))} enables ${shellQuote8(PLUGIN_ID)}`,
|
|
112384
112224
|
status: dryRun ? "planned" : "ran"
|
|
112385
112225
|
});
|
|
112386
112226
|
if (dryRun) {
|
|
@@ -112419,10 +112259,10 @@ function pluginAgentOption(value) {
|
|
|
112419
112259
|
}
|
|
112420
112260
|
function packageCandidateDirs() {
|
|
112421
112261
|
const dirs = [];
|
|
112422
|
-
let dir =
|
|
112262
|
+
let dir = dirname46(fileURLToPath9(import.meta.url));
|
|
112423
112263
|
for (let index2 = 0;index2 < 8; index2++) {
|
|
112424
112264
|
dirs.push(dir);
|
|
112425
|
-
const parent =
|
|
112265
|
+
const parent = dirname46(dir);
|
|
112426
112266
|
if (parent === dir)
|
|
112427
112267
|
break;
|
|
112428
112268
|
dir = parent;
|
|
@@ -112435,13 +112275,13 @@ function pluginRootCandidates() {
|
|
|
112435
112275
|
return [resolve40(envRoot)];
|
|
112436
112276
|
const candidates = [];
|
|
112437
112277
|
for (const dir of packageCandidateDirs()) {
|
|
112438
|
-
candidates.push(
|
|
112439
|
-
candidates.push(
|
|
112278
|
+
candidates.push(join110(dir, "plugins"));
|
|
112279
|
+
candidates.push(join110(dir, "dist", "plugins"));
|
|
112440
112280
|
}
|
|
112441
112281
|
return [...new Set(candidates)];
|
|
112442
112282
|
}
|
|
112443
112283
|
function isInstallablePluginRoot(root2) {
|
|
112444
|
-
return existsSync35(
|
|
112284
|
+
return existsSync35(join110(root2, ".claude-plugin", "marketplace.json")) && existsSync35(join110(root2, ".agents", "plugins", "marketplace.json")) && existsSync35(join110(root2, "claude", ".claude-plugin", "plugin.json")) && existsSync35(join110(root2, "codex", ".codex-plugin", "plugin.json")) && existsSync35(join110(root2, "cursor", ".cursor-plugin", "plugin.json")) && existsSync35(join110(root2, "skills"));
|
|
112445
112285
|
}
|
|
112446
112286
|
function resolveBundledPluginsRoot() {
|
|
112447
112287
|
const candidates = pluginRootCandidates();
|
|
@@ -112552,32 +112392,32 @@ function formatPluginStatusResult(result) {
|
|
|
112552
112392
|
function formatPluginInstallResult(result) {
|
|
112553
112393
|
const degraded = result.results.some((item) => item.status === "skipped" || item.status === "failed");
|
|
112554
112394
|
const ready = result.results.filter((item) => item.status === "installed" || item.status === "planned").map((item) => item.agent);
|
|
112555
|
-
const
|
|
112395
|
+
const body2 = [
|
|
112556
112396
|
`marketplace: ${dim(result.pluginsRoot)}`,
|
|
112557
112397
|
"manual install: use the marketplace path above as the plugin marketplace root."
|
|
112558
112398
|
];
|
|
112559
112399
|
for (const item of result.results) {
|
|
112560
112400
|
if (item.status === "installed" || item.status === "planned") {
|
|
112561
|
-
|
|
112401
|
+
body2.push(`✅ ${item.agent}: ${item.status}`);
|
|
112562
112402
|
continue;
|
|
112563
112403
|
}
|
|
112564
112404
|
const icon = item.status === "failed" ? "✗" : "⚠";
|
|
112565
112405
|
const detail = item.message ? ` — ${item.message}` : "";
|
|
112566
|
-
|
|
112406
|
+
body2.push(yellow(`${icon} ${item.agent}: ${item.status}${detail}`));
|
|
112567
112407
|
if (item.next)
|
|
112568
|
-
|
|
112408
|
+
body2.push(yellow(` next: ${item.next}`));
|
|
112569
112409
|
}
|
|
112570
112410
|
const detailSteps = result.steps.map((step) => ` ${step.agent}: ${step.status} ${step.command}`);
|
|
112571
112411
|
if (detailSteps.length > 0) {
|
|
112572
|
-
|
|
112573
|
-
|
|
112412
|
+
body2.push("details:");
|
|
112413
|
+
body2.push(...detailSteps.map(dim));
|
|
112574
112414
|
}
|
|
112575
112415
|
return formatFeedback({
|
|
112576
112416
|
symbol: result.dryRun ? "·" : degraded ? "⚠" : "✓",
|
|
112577
112417
|
action: result.dryRun ? "planned" : "installed",
|
|
112578
112418
|
subject: "context plugin",
|
|
112579
112419
|
headline: `${ready.length}/${result.agents.length} target(s) ready`,
|
|
112580
|
-
body
|
|
112420
|
+
body: body2
|
|
112581
112421
|
});
|
|
112582
112422
|
}
|
|
112583
112423
|
|
|
@@ -112677,29 +112517,29 @@ function inferErrorCategory(message) {
|
|
|
112677
112517
|
}
|
|
112678
112518
|
function readQuickstartPath() {
|
|
112679
112519
|
try {
|
|
112680
|
-
let dir =
|
|
112520
|
+
let dir = dirname47(fileURLToPath10(import.meta.url));
|
|
112681
112521
|
for (let i2 = 0;i2 < 8; i2++) {
|
|
112682
|
-
const candidate =
|
|
112522
|
+
const candidate = join111(dir, "docs", "quickstart.md");
|
|
112683
112523
|
if (existsSync36(candidate))
|
|
112684
112524
|
return candidate;
|
|
112685
|
-
const pkg =
|
|
112525
|
+
const pkg = join111(dir, "package.json");
|
|
112686
112526
|
if (existsSync36(pkg))
|
|
112687
112527
|
return candidate;
|
|
112688
|
-
const parent =
|
|
112528
|
+
const parent = dirname47(dir);
|
|
112689
112529
|
if (parent === dir)
|
|
112690
112530
|
break;
|
|
112691
112531
|
dir = parent;
|
|
112692
112532
|
}
|
|
112693
112533
|
} catch {}
|
|
112694
|
-
return
|
|
112534
|
+
return join111(dirname47(fileURLToPath10(import.meta.url)), "docs", "quickstart.md");
|
|
112695
112535
|
}
|
|
112696
112536
|
var GREEN = "\x1B[32m";
|
|
112697
112537
|
var RESET = "\x1B[0m";
|
|
112698
112538
|
function greenBox(lines) {
|
|
112699
112539
|
const width = Math.max(...lines.map((line) => line.length));
|
|
112700
112540
|
const border = `+${"-".repeat(width + 2)}+`;
|
|
112701
|
-
const
|
|
112702
|
-
return `${GREEN}${[border, ...
|
|
112541
|
+
const body2 = lines.map((line) => `| ${line.padEnd(width)} |`);
|
|
112542
|
+
return `${GREEN}${[border, ...body2, border].join(`
|
|
112703
112543
|
`)}${RESET}`;
|
|
112704
112544
|
}
|
|
112705
112545
|
function headerHelpText() {
|