@c4a/context-cli 0.6.1-beta.5 → 0.6.1-beta.6
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/cli.js +552 -94
- package/package.json +2 -2
- package/plugins/VERSION +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +2 -2
- package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
- package/providers/context/graphs/workspace.yaml +1 -1
- package/providers/context/manifest.json +13 -13
- package/providers/context/provider.yaml +1 -1
- package/providers/context/resources/manuals/reference/project-api.md +6 -3
- package/providers/context/resources/procedures/prose-compile.md +6 -3
- package/providers/context/resources/procedures/source-boundary.md +6 -1
- package/providers/context/resources/procedures/source-capture-detailed.md +9 -6
package/README.md
CHANGED
|
@@ -150,7 +150,7 @@ context source add file [YYYYMMDD] --module <module> --local <file-or-folder>
|
|
|
150
150
|
context source add lark [YYYYMMDD] --module <module> --url <lark-url>
|
|
151
151
|
context source add batch [YYYYMMDD] --input <yaml-or-json>
|
|
152
152
|
context source remove <source-id> --format json # preview
|
|
153
|
-
context source remove <source-id> --yes --format json
|
|
153
|
+
context source remove <source-id> --yes --plan-digest <preview-digest> --format json
|
|
154
154
|
context source ensure [source]
|
|
155
155
|
context source inspect [source]
|
|
156
156
|
|
package/README.zh-CN.md
CHANGED
|
@@ -116,7 +116,7 @@ context source add file [YYYYMMDD] --module <module> --local <file-or-folder>
|
|
|
116
116
|
context source add lark [YYYYMMDD] --module <module> --url <lark-url>
|
|
117
117
|
context source add batch [YYYYMMDD] --input <yaml-or-json>
|
|
118
118
|
context source remove <source-id> --format json # 预览
|
|
119
|
-
context source remove <source-id> --yes --format json
|
|
119
|
+
context source remove <source-id> --yes --plan-digest <预览摘要> --format json
|
|
120
120
|
context source ensure [source]
|
|
121
121
|
context source inspect [source]
|
|
122
122
|
|
package/cli.js
CHANGED
|
@@ -17936,6 +17936,7 @@ function renderAgents(projectName, language) {
|
|
|
17936
17936
|
"- 来源边界、正文读取、提取范围、语义分类、结构确认、Review 和包形态均是显式门禁,除非当前 Route 已证明可以继续。",
|
|
17937
17937
|
"- 不得根据目录、文件名、URL、示例或旧会话推断用户决定;没有明确授权时不得对来源仓库执行 clone、fetch、checkout、install、build、test 或脚本。",
|
|
17938
17938
|
"- 生命周期写入必须使用 Context CLI,不得用临时脚本修改 `sources/`、`knowledge/`、`dist/` 或 `.tmp/context-runtime/`。",
|
|
17939
|
+
"- 飞书采集只在 `evidence_status: error` 时停止;`projection_status: generic|warning` 表示原始 XML 已保留且可继续。不得在用户工作区修补 CLI 或手改快照来增加 renderer。",
|
|
17939
17940
|
"- Agent 编写的临时输入优先放在 `.tmp/agent-payloads/`;这是推荐而非强制。不要自行创建 `inputs/` 等顶层临时目录。",
|
|
17940
17941
|
"- 普通 Review 使用用户原样提供的 Payload;托管批准只能使用托管 status 返回的 revision-bound 原子命令。",
|
|
17941
17942
|
"- Route 允许时,只读证据可以并行;注册、stage、confirm、Review apply、close 和 build 必须串行。",
|
|
@@ -17972,6 +17973,7 @@ function renderAgents(projectName, language) {
|
|
|
17972
17973
|
"- Source boundaries, source-body reads, extraction scope, semantic classification, structure confirmation, Review, and package shape remain explicit gates unless the current route proves otherwise.",
|
|
17973
17974
|
"- Never infer source or review decisions from repository layout, filenames, URLs, examples, or prior conversations. Never clone, fetch, checkout, install, build, test, or run source-repository scripts without explicit authority.",
|
|
17974
17975
|
"- Use Context CLI for lifecycle writes. Do not inspect or repair `sources/`, `knowledge/`, `dist/`, or `.tmp/context-runtime/` with ad hoc scripts.",
|
|
17976
|
+
"- Stop Lark capture only for `evidence_status: error`. A `projection_status` of `generic` or `warning` means the original XML is preserved and the route may continue. Never patch the CLI or edit snapshots in a user workspace to add a renderer.",
|
|
17975
17977
|
"- Prefer `.tmp/agent-payloads/` for Agent-authored transient command inputs. This is a recommendation, not a CLI requirement; explicit custom paths remain valid. Avoid inventing top-level scratch directories such as `inputs/`.",
|
|
17976
17978
|
"- Review uses the user's exact Payload in ordinary mode. Managed approval uses only the revision-bound atomic command returned by managed status.",
|
|
17977
17979
|
"- Evidence reads may be parallel when the route says so; registry, stage, confirm, Review apply, close, and build mutations are serial.",
|
|
@@ -45952,6 +45954,7 @@ function fidelityIssues(value, field) {
|
|
|
45952
45954
|
}
|
|
45953
45955
|
return {
|
|
45954
45956
|
severity: item.severity,
|
|
45957
|
+
impact: item.impact === "evidence" || item.impact === "projection" ? item.impact : item.severity === "error" ? "evidence" : "projection",
|
|
45955
45958
|
code: requiredString(item.code, `${field}[${index}].code`),
|
|
45956
45959
|
block_type: requiredString(item.block_type, `${field}[${index}].block_type`),
|
|
45957
45960
|
count: item.count,
|
|
@@ -45983,11 +45986,29 @@ function parseDocumentCaptureFidelity(value, field) {
|
|
|
45983
45986
|
throw new TypeError(`${field} does not close for ${blockType}: discovered ${discoveredCount}, converted ${converted[blockType] ?? 0}, skipped ${skippedCount}`);
|
|
45984
45987
|
}
|
|
45985
45988
|
}
|
|
45986
|
-
const
|
|
45989
|
+
const evidenceStatus = issues.some((issue) => issue.impact === "evidence" && issue.severity === "error") ? "error" : "complete";
|
|
45990
|
+
const projectionIssues = issues.filter((issue) => issue.impact === "projection");
|
|
45991
|
+
const inferredProjectionStatus = projectionIssues.some((issue) => issue.severity === "error") ? "error" : projectionIssues.some((issue) => issue.code === "lark.capture.generic-projection") ? "generic" : projectionIssues.length > 0 ? "warning" : "complete";
|
|
45992
|
+
const projectionStatus = value.projection_status === "complete" || value.projection_status === "generic" || value.projection_status === "warning" || value.projection_status === "error" ? value.projection_status : inferredProjectionStatus;
|
|
45993
|
+
const status = evidenceStatus === "error" || projectionStatus === "error" ? "error" : issues.length > 0 ? "warning" : "complete";
|
|
45987
45994
|
if (value.status !== status) {
|
|
45988
45995
|
throw new TypeError(`${field}.status must be ${status} for its issues`);
|
|
45989
45996
|
}
|
|
45990
|
-
|
|
45997
|
+
if (value.evidence_status !== undefined && value.evidence_status !== evidenceStatus) {
|
|
45998
|
+
throw new TypeError(`${field}.evidence_status must be ${evidenceStatus} for its issues`);
|
|
45999
|
+
}
|
|
46000
|
+
if (value.projection_status !== undefined && value.projection_status !== inferredProjectionStatus) {
|
|
46001
|
+
throw new TypeError(`${field}.projection_status must be ${inferredProjectionStatus} for its issues`);
|
|
46002
|
+
}
|
|
46003
|
+
return {
|
|
46004
|
+
status,
|
|
46005
|
+
evidence_status: evidenceStatus,
|
|
46006
|
+
projection_status: projectionStatus,
|
|
46007
|
+
discovered,
|
|
46008
|
+
converted,
|
|
46009
|
+
skipped,
|
|
46010
|
+
issues
|
|
46011
|
+
};
|
|
45991
46012
|
}
|
|
45992
46013
|
|
|
45993
46014
|
// ../extract/src/documentEvidence.ts
|
|
@@ -62934,7 +62955,7 @@ function batchIdentity(sourceName) {
|
|
|
62934
62955
|
return;
|
|
62935
62956
|
return { batch, module };
|
|
62936
62957
|
}
|
|
62937
|
-
function
|
|
62958
|
+
function parseDocumentSnapshotBatchManifest(value) {
|
|
62938
62959
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
62939
62960
|
throw new TypeError("document snapshot batch manifest must be an object");
|
|
62940
62961
|
}
|
|
@@ -62958,7 +62979,7 @@ function findDocumentSnapshotForSource(value, sourceName) {
|
|
|
62958
62979
|
const identity = batchIdentity(sourceName);
|
|
62959
62980
|
if (identity === undefined)
|
|
62960
62981
|
throw new TypeError(`batch manifest cannot resolve non-batch source: ${sourceName}`);
|
|
62961
|
-
const batch =
|
|
62982
|
+
const batch = parseDocumentSnapshotBatchManifest(value);
|
|
62962
62983
|
const snapshot2 = batch.sources[identity.module];
|
|
62963
62984
|
if (snapshot2 === undefined)
|
|
62964
62985
|
return null;
|
|
@@ -62991,7 +63012,7 @@ function updateDocumentManifestFile(input) {
|
|
|
62991
63012
|
const identity = batchIdentity(input.snapshot.source_name);
|
|
62992
63013
|
if (identity === undefined)
|
|
62993
63014
|
return input.snapshot;
|
|
62994
|
-
const existing = input.current === null ? undefined :
|
|
63015
|
+
const existing = input.current === null ? undefined : parseDocumentSnapshotBatchManifest(input.current);
|
|
62995
63016
|
if (existing !== undefined && (existing.source_type !== input.snapshot.source_type || existing.batch !== identity.batch)) {
|
|
62996
63017
|
throw new TypeError(`document snapshot batch identity mismatch for ${input.snapshot.source_name}`);
|
|
62997
63018
|
}
|
|
@@ -63005,6 +63026,36 @@ function updateDocumentManifestFile(input) {
|
|
|
63005
63026
|
}).sort(([left], [right]) => left.localeCompare(right)))
|
|
63006
63027
|
};
|
|
63007
63028
|
}
|
|
63029
|
+
function removeDocumentSnapshotFromManifestFile(input) {
|
|
63030
|
+
if (input.current === null) {
|
|
63031
|
+
return { snapshot: null, next: null, remainingSources: 0 };
|
|
63032
|
+
}
|
|
63033
|
+
if (input.current !== null && typeof input.current === "object" && !Array.isArray(input.current) && input.current.schema_version === DOCUMENT_SNAPSHOT_BATCH_SCHEMA_VERSION) {
|
|
63034
|
+
const identity = batchIdentity(input.sourceName);
|
|
63035
|
+
if (identity === undefined)
|
|
63036
|
+
throw new TypeError(`batch manifest cannot resolve non-batch source: ${input.sourceName}`);
|
|
63037
|
+
const batch = parseDocumentSnapshotBatchManifest(input.current);
|
|
63038
|
+
if (batch.batch !== identity.batch) {
|
|
63039
|
+
throw new TypeError(`document snapshot batch identity mismatch for ${input.sourceName}`);
|
|
63040
|
+
}
|
|
63041
|
+
const snapshot2 = batch.sources[identity.module] ?? null;
|
|
63042
|
+
if (snapshot2 !== null && snapshot2.source_name !== input.sourceName) {
|
|
63043
|
+
throw new TypeError(`document snapshot batch source entry does not match ${input.sourceName}`);
|
|
63044
|
+
}
|
|
63045
|
+
const sources = Object.fromEntries(Object.entries(batch.sources).filter(([module]) => module !== identity.module).sort(([left], [right]) => left.localeCompare(right)));
|
|
63046
|
+
const remainingSources = Object.keys(sources).length;
|
|
63047
|
+
return {
|
|
63048
|
+
snapshot: snapshot2,
|
|
63049
|
+
next: remainingSources === 0 ? null : { ...batch, sources },
|
|
63050
|
+
remainingSources
|
|
63051
|
+
};
|
|
63052
|
+
}
|
|
63053
|
+
const snapshot = parseDocumentSnapshotManifest(input.current);
|
|
63054
|
+
if (snapshot.source_name !== input.sourceName) {
|
|
63055
|
+
throw new TypeError(`document snapshot is for ${snapshot.source_name}, expected ${input.sourceName}`);
|
|
63056
|
+
}
|
|
63057
|
+
return { snapshot, next: null, remainingSources: 0 };
|
|
63058
|
+
}
|
|
63008
63059
|
function renderDocumentManifestFile(value) {
|
|
63009
63060
|
return `${JSON.stringify(value, null, 2)}
|
|
63010
63061
|
`;
|
|
@@ -63520,11 +63571,11 @@ function documentSnapshotFidelityState(manifest) {
|
|
|
63520
63571
|
const report = manifest.metadata?.capture?.fidelity;
|
|
63521
63572
|
if (report === undefined)
|
|
63522
63573
|
return { blocking: [], warnings: [] };
|
|
63523
|
-
const render = (
|
|
63574
|
+
const render = (predicate) => report.issues.filter(predicate).map((issue) => `${issue.code}: ${issue.block_type} × ${issue.count}: ${issue.reason}`);
|
|
63524
63575
|
return {
|
|
63525
63576
|
report,
|
|
63526
|
-
blocking: render("error"),
|
|
63527
|
-
warnings: render("warning")
|
|
63577
|
+
blocking: render((issue) => issue.impact === "evidence" && issue.severity === "error"),
|
|
63578
|
+
warnings: render((issue) => issue.impact === "projection" || issue.severity === "warning")
|
|
63528
63579
|
};
|
|
63529
63580
|
}
|
|
63530
63581
|
|
|
@@ -84000,7 +84051,7 @@ async function documentSourceStatus(projectRoot, type, source3) {
|
|
|
84000
84051
|
source: source3,
|
|
84001
84052
|
snapshotConfigured: readiness.snapshotConfigured
|
|
84002
84053
|
}) : null;
|
|
84003
|
-
const agentHints = readiness.ready ? [] : [readiness.captureFidelity?.
|
|
84054
|
+
const agentHints = readiness.ready ? [] : [readiness.captureFidelity?.evidence_status === "error" ? "document-capture-fidelity-recapture-required" : "document-source-not-captured"];
|
|
84004
84055
|
if (documentSiteHint !== null)
|
|
84005
84056
|
agentHints.push(documentSiteHint);
|
|
84006
84057
|
return {
|
|
@@ -84097,7 +84148,7 @@ function documentSnapshotReadiness(input) {
|
|
|
84097
84148
|
return {
|
|
84098
84149
|
ready: false,
|
|
84099
84150
|
diagnostics: fidelity.blocking,
|
|
84100
|
-
workspaceDiagnostics:
|
|
84151
|
+
workspaceDiagnostics: [],
|
|
84101
84152
|
...fidelity.report !== undefined ? { captureFidelity: fidelity.report } : {}
|
|
84102
84153
|
};
|
|
84103
84154
|
}
|
|
@@ -84112,7 +84163,7 @@ function documentSnapshotReadiness(input) {
|
|
|
84112
84163
|
}
|
|
84113
84164
|
return {
|
|
84114
84165
|
ready: true,
|
|
84115
|
-
diagnostics:
|
|
84166
|
+
diagnostics: fidelity.warnings,
|
|
84116
84167
|
workspaceDiagnostics: [],
|
|
84117
84168
|
snapshotHash: manifest.snapshot_hash,
|
|
84118
84169
|
...fidelity.report !== undefined ? { captureFidelity: fidelity.report } : {},
|
|
@@ -84318,6 +84369,8 @@ function workspaceStateValid(observation) {
|
|
|
84318
84369
|
return true;
|
|
84319
84370
|
}
|
|
84320
84371
|
function blockingVerificationClear(observation) {
|
|
84372
|
+
if (observation.capturedDocumentSources < observation.documentSources.length)
|
|
84373
|
+
return true;
|
|
84321
84374
|
if (observation.verifyErrors === 0)
|
|
84322
84375
|
return true;
|
|
84323
84376
|
if (verifyErrorsAreCloseRepairable(observation.verifyIssues))
|
|
@@ -84325,6 +84378,8 @@ function blockingVerificationClear(observation) {
|
|
|
84325
84378
|
return onlySourceDriftErrors(observation.verifyIssues);
|
|
84326
84379
|
}
|
|
84327
84380
|
function evidenceMaintenanceClear(observation) {
|
|
84381
|
+
if (observation.capturedDocumentSources < observation.documentSources.length)
|
|
84382
|
+
return true;
|
|
84328
84383
|
return observation.evidenceWarnings !== "orphaned" && observation.evidenceWarnings !== "stale";
|
|
84329
84384
|
}
|
|
84330
84385
|
function proseDeclarationsComplete(observation) {
|
|
@@ -84877,7 +84932,7 @@ function rootDiagnostics(observation) {
|
|
|
84877
84932
|
message: "Approved knowledge changed and its deterministic structure projection must be refreshed.",
|
|
84878
84933
|
count: observation.projectionRefreshIssues
|
|
84879
84934
|
});
|
|
84880
|
-
} else if (observation.verifyErrors > 0) {
|
|
84935
|
+
} else if (observation.verifyErrors > 0 && observation.capturedDocumentSources === observation.documentSources.length) {
|
|
84881
84936
|
diagnostics.push({
|
|
84882
84937
|
code: "diagnostic.verify-failed",
|
|
84883
84938
|
severity: "error",
|
|
@@ -85282,8 +85337,8 @@ function renderSources(status) {
|
|
|
85282
85337
|
const repositories = status.sources.map((source3) => `${inline(source3.name)} — ready=${inline(source3.ready)}, ref=${inline(source3.ref)}, scope-match=${inline(source3.scopeMatches)}` + (source3.subpath === undefined ? "" : `, subpath=${inline(source3.subpath)}`));
|
|
85283
85338
|
const documents = status.documentSources.map((source3) => {
|
|
85284
85339
|
const fidelity = source3.captureFidelity;
|
|
85285
|
-
const summary = fidelity === undefined ? "fidelity=unavailable" : `fidelity=${inline(fidelity.status)}, discovered=${Object.values(fidelity.discovered).reduce((sum, count) => sum + count, 0)}, converted=${Object.values(fidelity.converted).reduce((sum, count) => sum + count, 0)}, skipped=${fidelity.skipped.reduce((sum, item) => sum + item.count, 0)}`;
|
|
85286
|
-
const issues = fidelity?.issues.map((issue) => `${inline(issue.severity)} ${inline(issue.code)} ${inline(issue.block_type)} (${issue.count}) — ${issue.reason}`) ?? [];
|
|
85340
|
+
const summary = fidelity === undefined ? "fidelity=unavailable" : `fidelity=${inline(fidelity.status)}, evidence=${inline(fidelity.evidence_status)}, projection=${inline(fidelity.projection_status)}, discovered=${Object.values(fidelity.discovered).reduce((sum, count) => sum + count, 0)}, converted=${Object.values(fidelity.converted).reduce((sum, count) => sum + count, 0)}, skipped=${fidelity.skipped.reduce((sum, item) => sum + item.count, 0)}`;
|
|
85341
|
+
const issues = fidelity?.issues.map((issue) => `${inline(issue.severity)} ${inline(issue.impact)} ${inline(issue.code)} ${inline(issue.block_type)} (${issue.count}) — ${issue.reason}`) ?? [];
|
|
85287
85342
|
return [
|
|
85288
85343
|
`${inline(`${source3.type}:${source3.name}`)} — captured=${inline(source3.snapshotReady)}, manifest=${inline(source3.manifest)}, ${summary}`,
|
|
85289
85344
|
...issues.map((issue) => ` - ${issue}`)
|
|
@@ -85717,7 +85772,7 @@ function compileStatusRouting(input) {
|
|
|
85717
85772
|
};
|
|
85718
85773
|
}
|
|
85719
85774
|
function pendingDocumentCaptureCommands(input) {
|
|
85720
|
-
const pendingSources = input.documentSources.filter((source3) => !source3.snapshotReady
|
|
85775
|
+
const pendingSources = input.documentSources.filter((source3) => !source3.snapshotReady);
|
|
85721
85776
|
const phaseIds = [];
|
|
85722
85777
|
const missingSources = [];
|
|
85723
85778
|
for (const source3 of pendingSources) {
|
|
@@ -86187,7 +86242,15 @@ function receiptSetPath(receipts) {
|
|
|
86187
86242
|
async function writeReceiptContinuation(input) {
|
|
86188
86243
|
const path4 = receiptSetPath(input.receipts);
|
|
86189
86244
|
await writeJsonAtomic(join46(input.projectRoot, path4), input.receipts);
|
|
86190
|
-
const command2 = [
|
|
86245
|
+
const command2 = input.managed ? [
|
|
86246
|
+
"context",
|
|
86247
|
+
"--workflow-resource-receipts",
|
|
86248
|
+
shellQuote6(`@${path4}`),
|
|
86249
|
+
"run",
|
|
86250
|
+
authorityCommandOptions(input.authorities, "resource").trim(),
|
|
86251
|
+
"--until blocked-or-complete",
|
|
86252
|
+
"--format json"
|
|
86253
|
+
].filter((item) => item.length > 0).join(" ") : [
|
|
86191
86254
|
"context status",
|
|
86192
86255
|
authorityCommandOptions(input.authorities, "resource").trim(),
|
|
86193
86256
|
"--resource-receipts",
|
|
@@ -86261,6 +86324,7 @@ async function materializeContextWorkflowResource(input) {
|
|
|
86261
86324
|
const afterReadReceipts = mergedReceipts(input.resourceReceipts, receiptCandidate);
|
|
86262
86325
|
const continuation = await writeReceiptContinuation({
|
|
86263
86326
|
projectRoot: found.projectRoot,
|
|
86327
|
+
managed: input.managed === true,
|
|
86264
86328
|
authorities,
|
|
86265
86329
|
receipts: afterReadReceipts
|
|
86266
86330
|
});
|
|
@@ -86313,6 +86377,7 @@ async function acknowledgeCurrentWorkflowResources(input) {
|
|
|
86313
86377
|
};
|
|
86314
86378
|
const continuation = await writeReceiptContinuation({
|
|
86315
86379
|
projectRoot: found.projectRoot,
|
|
86380
|
+
managed: input.managed === true,
|
|
86316
86381
|
authorities,
|
|
86317
86382
|
receipts: normalizedReceipts
|
|
86318
86383
|
});
|
|
@@ -86467,9 +86532,11 @@ async function existingFileKind(path4) {
|
|
|
86467
86532
|
const stats = await lstat2(path4);
|
|
86468
86533
|
if (stats.isFile())
|
|
86469
86534
|
return "file";
|
|
86535
|
+
if (stats.isDirectory())
|
|
86536
|
+
return "directory";
|
|
86470
86537
|
if (stats.isSymbolicLink())
|
|
86471
86538
|
return "symlink";
|
|
86472
|
-
throw new TypeError(`atomic file batch target
|
|
86539
|
+
throw new TypeError(`atomic file batch target has an unsupported filesystem kind: ${path4}`);
|
|
86473
86540
|
} catch (error) {
|
|
86474
86541
|
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
86475
86542
|
return "missing";
|
|
@@ -90046,6 +90113,7 @@ class XMLParser {
|
|
|
90046
90113
|
}
|
|
90047
90114
|
|
|
90048
90115
|
// src/lib/larkDocxXml.ts
|
|
90116
|
+
var LARK_EMPTY_SUB_PAGE_LIST_CODE = "lark.capture.sub-page-list-empty";
|
|
90049
90117
|
var parser = new XMLParser({
|
|
90050
90118
|
preserveOrder: true,
|
|
90051
90119
|
ignoreAttributes: false,
|
|
@@ -90090,22 +90158,25 @@ class FidelityTracker {
|
|
|
90090
90158
|
reason,
|
|
90091
90159
|
count: (current2?.count ?? 0) + 1
|
|
90092
90160
|
});
|
|
90093
|
-
const
|
|
90161
|
+
const code3 = severity === "error" ? "lark.capture.fidelity-loss" : "lark.capture.unsupported-empty-block";
|
|
90162
|
+
const issueKey = `${severity}\x00${code3}\x00${blockType}\x00${reason}`;
|
|
90094
90163
|
const issue = this.issues.get(issueKey);
|
|
90095
90164
|
this.issues.set(issueKey, {
|
|
90096
90165
|
severity,
|
|
90097
|
-
|
|
90166
|
+
impact: "projection",
|
|
90167
|
+
code: code3,
|
|
90098
90168
|
block_type: blockType,
|
|
90099
90169
|
count: (issue?.count ?? 0) + 1,
|
|
90100
90170
|
reason
|
|
90101
90171
|
});
|
|
90102
90172
|
}
|
|
90103
|
-
flag(blockType, reason, severity) {
|
|
90104
|
-
const issueKey = `${severity}\x00${blockType}\x00${reason}`;
|
|
90173
|
+
flag(blockType, reason, severity, code3 = severity === "error" ? "lark.capture.fidelity-loss" : "lark.capture.fidelity-warning", impact = severity === "error" ? "evidence" : "projection") {
|
|
90174
|
+
const issueKey = `${severity}\x00${impact}\x00${code3}\x00${blockType}\x00${reason}`;
|
|
90105
90175
|
const issue = this.issues.get(issueKey);
|
|
90106
90176
|
this.issues.set(issueKey, {
|
|
90107
90177
|
severity,
|
|
90108
|
-
|
|
90178
|
+
impact,
|
|
90179
|
+
code: code3,
|
|
90109
90180
|
block_type: blockType,
|
|
90110
90181
|
count: (issue?.count ?? 0) + 1,
|
|
90111
90182
|
reason
|
|
@@ -90130,8 +90201,19 @@ class FidelityTracker {
|
|
|
90130
90201
|
}
|
|
90131
90202
|
}
|
|
90132
90203
|
const issues = [...this.issues.values()].sort((left, right) => left.severity.localeCompare(right.severity) || left.block_type.localeCompare(right.block_type));
|
|
90133
|
-
const
|
|
90134
|
-
|
|
90204
|
+
const evidenceStatus = issues.some((issue) => issue.impact === "evidence" && issue.severity === "error") ? "error" : "complete";
|
|
90205
|
+
const projectionIssues = issues.filter((issue) => issue.impact === "projection");
|
|
90206
|
+
const projectionStatus = projectionIssues.some((issue) => issue.severity === "error") ? "error" : projectionIssues.some((issue) => issue.code === "lark.capture.generic-projection") ? "generic" : projectionIssues.length > 0 ? "warning" : "complete";
|
|
90207
|
+
const status = evidenceStatus === "error" || projectionStatus === "error" ? "error" : issues.length > 0 ? "warning" : "complete";
|
|
90208
|
+
return {
|
|
90209
|
+
status,
|
|
90210
|
+
evidence_status: evidenceStatus,
|
|
90211
|
+
projection_status: projectionStatus,
|
|
90212
|
+
discovered,
|
|
90213
|
+
converted,
|
|
90214
|
+
skipped,
|
|
90215
|
+
issues
|
|
90216
|
+
};
|
|
90135
90217
|
}
|
|
90136
90218
|
}
|
|
90137
90219
|
function sortedCounts(counts2) {
|
|
@@ -90190,7 +90272,16 @@ function stableDocumentUrl(sourceUrl, fileType, docId) {
|
|
|
90190
90272
|
return `lark:${fileType ?? "document"}:${docId}`;
|
|
90191
90273
|
}
|
|
90192
90274
|
function resourceIdentity(kind, attrs) {
|
|
90193
|
-
|
|
90275
|
+
if (kind === "bookmark")
|
|
90276
|
+
return;
|
|
90277
|
+
if (kind === "poll")
|
|
90278
|
+
return attrs.id;
|
|
90279
|
+
if (kind === "synced-reference") {
|
|
90280
|
+
const sourceToken = attrs["src-token"];
|
|
90281
|
+
const sourceBlockId = attrs["src-block-id"];
|
|
90282
|
+
return sourceToken !== undefined && sourceBlockId !== undefined ? `${sourceToken}#${sourceBlockId}` : undefined;
|
|
90283
|
+
}
|
|
90284
|
+
const kindCandidates = kind === "cite" || kind === "document" ? [attrs["doc-id"]] : kind === "base" || kind === "sheet" ? [attrs["table-id"], attrs["sheet-id"]] : [];
|
|
90194
90285
|
const candidates = [
|
|
90195
90286
|
attrs.token,
|
|
90196
90287
|
attrs["file-token"],
|
|
@@ -90199,7 +90290,7 @@ function resourceIdentity(kind, attrs) {
|
|
|
90199
90290
|
attrs["obj-token"],
|
|
90200
90291
|
attrs["source-id"],
|
|
90201
90292
|
attrs["chat-id"],
|
|
90202
|
-
attrs.id,
|
|
90293
|
+
...kind === "document" ? [] : [attrs.id],
|
|
90203
90294
|
...kindCandidates,
|
|
90204
90295
|
attrs.src
|
|
90205
90296
|
];
|
|
@@ -90366,21 +90457,157 @@ ${safeFence(source3, attrs.type === "mermaid" ? "mermaid" : "text")}
|
|
|
90366
90457
|
|
|
90367
90458
|
> ${title}${details.length > 0 ? ` — ${details}` : ""} (${resource.locator})
|
|
90368
90459
|
|
|
90460
|
+
`;
|
|
90461
|
+
}
|
|
90462
|
+
function renderSubPage(nodes, attrs, ctx) {
|
|
90463
|
+
const title = attrs.title ?? (normalizeInline(textContent(nodes)) || "Untitled subpage");
|
|
90464
|
+
const docId = attrs["doc-id"] ?? attrs.token;
|
|
90465
|
+
const resource = addResource(ctx, "sub-page", "document", attrs, title);
|
|
90466
|
+
if (docId === undefined) {
|
|
90467
|
+
ctx.tracker.flag("sub-page", "sub-page has no doc-id or token", "error");
|
|
90468
|
+
return `${escapeMarkdownLabel(title)} <!-- ${resource.locator} -->`;
|
|
90469
|
+
}
|
|
90470
|
+
const target = stableDocumentUrl(ctx.sourceUrl, attrs["file-type"], docId);
|
|
90471
|
+
return `[${escapeMarkdownLabel(title)}](${target}) <!-- ${resource.locator} -->`;
|
|
90472
|
+
}
|
|
90473
|
+
function renderSubPageList(nodes, ctx) {
|
|
90474
|
+
if (!nodes.some((node3) => elementName(node3) === "sub-page")) {
|
|
90475
|
+
ctx.tracker.flag("sub-page-list", "sub-page-list returned no sub-page entries; child navigation completeness cannot be verified", "error", LARK_EMPTY_SUB_PAGE_LIST_CODE);
|
|
90476
|
+
}
|
|
90477
|
+
const lines = [];
|
|
90478
|
+
for (const node3 of nodes) {
|
|
90479
|
+
const name2 = elementName(node3);
|
|
90480
|
+
if (name2 === undefined)
|
|
90481
|
+
continue;
|
|
90482
|
+
const rendered = normalizeMarkdown2(renderNode(node3, { ...ctx, mode: "inline" }));
|
|
90483
|
+
if (rendered.length === 0)
|
|
90484
|
+
continue;
|
|
90485
|
+
lines.push(name2 === "sub-page" ? `- ${rendered}` : rendered);
|
|
90486
|
+
}
|
|
90487
|
+
return lines.length === 0 ? "" : `
|
|
90488
|
+
|
|
90489
|
+
${lines.join(`
|
|
90490
|
+
`)}
|
|
90491
|
+
|
|
90492
|
+
`;
|
|
90493
|
+
}
|
|
90494
|
+
function renderBookmark(nodes, attrs, ctx) {
|
|
90495
|
+
const href = attrs.href ?? attrs.url;
|
|
90496
|
+
const title = attrs.name ?? attrs.title ?? (normalizeInline(textContent(nodes)) || href) ?? "Bookmark";
|
|
90497
|
+
const resource = addResource(ctx, "bookmark", "bookmark", attrs, title);
|
|
90498
|
+
if (href === undefined || isTransientMediaUrl(href)) {
|
|
90499
|
+
ctx.tracker.flag("bookmark", "bookmark has no stable non-transient URL", "error");
|
|
90500
|
+
return `
|
|
90501
|
+
|
|
90502
|
+
> Bookmark: ${escapeMarkdownLabel(title)} <!-- ${resource.locator} -->
|
|
90503
|
+
|
|
90504
|
+
`;
|
|
90505
|
+
}
|
|
90506
|
+
return `
|
|
90507
|
+
|
|
90508
|
+
> Bookmark: [${escapeMarkdownLabel(title)}](${href}) <!-- ${resource.locator} -->
|
|
90509
|
+
|
|
90510
|
+
`;
|
|
90511
|
+
}
|
|
90512
|
+
function renderSyncedReference(attrs, ctx) {
|
|
90513
|
+
const sourceToken = attrs["src-token"];
|
|
90514
|
+
const sourceBlockId = attrs["src-block-id"];
|
|
90515
|
+
const title = attrs.title ?? attrs.name ?? "Synced reference";
|
|
90516
|
+
const resource = addResource(ctx, "synced_reference", "synced-reference", attrs, title);
|
|
90517
|
+
if (sourceToken === undefined || sourceBlockId === undefined) {
|
|
90518
|
+
ctx.tracker.flag("synced_reference", "synced_reference requires both src-token and src-block-id", "error");
|
|
90519
|
+
return `
|
|
90520
|
+
|
|
90521
|
+
> ${escapeMarkdownLabel(title)} <!-- ${resource.locator} -->
|
|
90522
|
+
|
|
90523
|
+
`;
|
|
90524
|
+
}
|
|
90525
|
+
const target = `${stableDocumentUrl(ctx.sourceUrl, "docx", sourceToken)}#${encodeURIComponent(sourceBlockId)}`;
|
|
90526
|
+
return `
|
|
90527
|
+
|
|
90528
|
+
> [${escapeMarkdownLabel(title)}](${target}) <!-- ${resource.locator} -->
|
|
90529
|
+
|
|
90530
|
+
`;
|
|
90531
|
+
}
|
|
90532
|
+
function renderChecklistItem(blockType, nodes, attrs, ctx) {
|
|
90533
|
+
const done = attrs.done;
|
|
90534
|
+
const checked = attrs.checked;
|
|
90535
|
+
const state = done ?? checked;
|
|
90536
|
+
const stateIsValid = (state === "true" || state === "false") && (done === undefined || checked === undefined || done === checked);
|
|
90537
|
+
const body = normalizeInline(renderChildren(nodes, { ...ctx, mode: "inline" }));
|
|
90538
|
+
if (!stateIsValid) {
|
|
90539
|
+
ctx.tracker.flag(blockType, `${blockType} requires one unambiguous boolean done or checked attribute`, "warning", "lark.capture.checkbox-state-invalid", "projection");
|
|
90540
|
+
return `
|
|
90541
|
+
- [?] ${body}
|
|
90542
|
+
`;
|
|
90543
|
+
}
|
|
90544
|
+
return `
|
|
90545
|
+
- [${state === "true" ? "x" : " "}] ${body}
|
|
90546
|
+
`;
|
|
90547
|
+
}
|
|
90548
|
+
function meaningfulAttributes(attrs, excluded) {
|
|
90549
|
+
return Object.entries(attrs).filter(([key, value]) => value.length > 0 && !PRESENTATION_ONLY_ATTRIBUTES.has(key) && !excluded.has(key)).sort(([left], [right]) => left.localeCompare(right));
|
|
90550
|
+
}
|
|
90551
|
+
function auditableAttributes(attrs) {
|
|
90552
|
+
return Object.entries(attrs).filter(([, value]) => value.length > 0).map(([key, value]) => [key, isTransientMediaUrl(value) ? "[redacted-transient-url]" : value]).sort(([left], [right]) => left.localeCompare(right));
|
|
90553
|
+
}
|
|
90554
|
+
function renderPollOption(blockType, nodes, attrs, ctx) {
|
|
90555
|
+
const body = normalizeInline(renderChildren(nodes, { ...ctx, mode: "inline" }));
|
|
90556
|
+
const label3 = body || attrs.name || attrs.title || attrs.label || attrs.value;
|
|
90557
|
+
const details = meaningfulAttributes(attrs, new Set(["name", "title", "label", "value"])).map(([key, value]) => `${key}=${value}`);
|
|
90558
|
+
if (label3 === undefined && details.length === 0) {
|
|
90559
|
+
ctx.tracker.flag(blockType, `${blockType} has no visible label or metadata`, "warning", "lark.capture.poll-option-empty", "projection");
|
|
90560
|
+
}
|
|
90561
|
+
const rendered = [label3 ?? "Unnamed option", ...details.length > 0 ? [`(${details.join(", ")})`] : []].join(" ");
|
|
90562
|
+
return `
|
|
90563
|
+
- ${rendered}
|
|
90564
|
+
`;
|
|
90565
|
+
}
|
|
90566
|
+
function renderPoll(nodes, attrs, ctx) {
|
|
90567
|
+
const title = attrs.name ?? attrs.title ?? "Untitled poll";
|
|
90568
|
+
const resource = addResource(ctx, "poll", "poll", attrs, title);
|
|
90569
|
+
const href = attrs.href ?? attrs.url;
|
|
90570
|
+
const label3 = href !== undefined && !isTransientMediaUrl(href) ? `[${escapeMarkdownLabel(title)}](${href})` : escapeMarkdownLabel(title);
|
|
90571
|
+
const details = meaningfulAttributes(attrs, new Set(["name", "title", "href", "url"])).map(([key, value]) => `${key}=${value}`);
|
|
90572
|
+
const children = normalizeMarkdown2(renderChildren(nodes, { ...ctx, mode: "block" }));
|
|
90573
|
+
const lines = [
|
|
90574
|
+
`> Lark poll (non-interactive): ${label3} <!-- ${resource.locator} -->`,
|
|
90575
|
+
...details.length > 0 ? [`> Exported attributes: ${details.join(", ")}`] : [],
|
|
90576
|
+
...children.length === 0 ? ["> Options and results are not present in the exported XML."] : [children]
|
|
90577
|
+
];
|
|
90578
|
+
return `
|
|
90579
|
+
|
|
90580
|
+
${lines.join(`
|
|
90581
|
+
`)}
|
|
90582
|
+
|
|
90369
90583
|
`;
|
|
90370
90584
|
}
|
|
90371
90585
|
function renderUnknown(name2, nodes, attrs, ctx) {
|
|
90372
90586
|
const body = normalizeMarkdown2(renderChildren(nodes, ctx));
|
|
90373
|
-
const
|
|
90374
|
-
if (body.length === 0 &&
|
|
90587
|
+
const exportedAttrs = auditableAttributes(attrs);
|
|
90588
|
+
if (body.length === 0 && exportedAttrs.length === 0) {
|
|
90375
90589
|
ctx.tracker.skip(name2, "unknown empty block omitted", "warning");
|
|
90376
90590
|
return "";
|
|
90377
90591
|
}
|
|
90378
|
-
ctx.tracker.
|
|
90592
|
+
ctx.tracker.convert(name2);
|
|
90593
|
+
ctx.tracker.flag(name2, "block was preserved through the generic non-interactive projection; inspect source.xml for the original structure", "warning", "lark.capture.generic-projection", "projection");
|
|
90594
|
+
const digest4 = createHash20("sha256").update(JSON.stringify({ name: name2, attributes: exportedAttrs, text: normalizeInline(textContent(nodes)) }), "utf8").digest("hex").slice(0, 12);
|
|
90595
|
+
const locator = `lark:block:${name2}:${digest4}`;
|
|
90596
|
+
ctx.resources.push({
|
|
90597
|
+
kind: "embed",
|
|
90598
|
+
locator,
|
|
90599
|
+
title: name2,
|
|
90600
|
+
attributes: Object.fromEntries(exportedAttrs)
|
|
90601
|
+
});
|
|
90602
|
+
const lines = [
|
|
90603
|
+
`> Lark block (generic projection): \`${name2}\` <!-- ${locator} -->`,
|
|
90604
|
+
...exportedAttrs.length > 0 ? [`> Exported attributes: ${JSON.stringify(Object.fromEntries(exportedAttrs))}`] : [],
|
|
90605
|
+
...body.length > 0 ? [body] : []
|
|
90606
|
+
];
|
|
90379
90607
|
return `
|
|
90380
90608
|
|
|
90381
|
-
|
|
90382
|
-
|
|
90383
|
-
> `)}` : ""}
|
|
90609
|
+
${lines.join(`
|
|
90610
|
+
`)}
|
|
90384
90611
|
|
|
90385
90612
|
`;
|
|
90386
90613
|
}
|
|
@@ -90397,6 +90624,22 @@ function renderNode(node3, ctx) {
|
|
|
90397
90624
|
ctx.tracker.convert(name2);
|
|
90398
90625
|
return renderResource(name2, nodes, attrs, ctx);
|
|
90399
90626
|
}
|
|
90627
|
+
if (name2 === "sub-page") {
|
|
90628
|
+
ctx.tracker.convert(name2);
|
|
90629
|
+
return renderSubPage(nodes, attrs, ctx);
|
|
90630
|
+
}
|
|
90631
|
+
if (name2 === "sub-page-list") {
|
|
90632
|
+
ctx.tracker.convert(name2);
|
|
90633
|
+
return renderSubPageList(nodes, ctx);
|
|
90634
|
+
}
|
|
90635
|
+
if (name2 === "bookmark") {
|
|
90636
|
+
ctx.tracker.convert(name2);
|
|
90637
|
+
return renderBookmark(nodes, attrs, ctx);
|
|
90638
|
+
}
|
|
90639
|
+
if (name2 === "synced_reference") {
|
|
90640
|
+
ctx.tracker.convert(name2);
|
|
90641
|
+
return renderSyncedReference(attrs, ctx);
|
|
90642
|
+
}
|
|
90400
90643
|
if (name2 === "title" || /^h[1-9]$/u.test(name2) || name2 === "heading") {
|
|
90401
90644
|
ctx.tracker.convert(name2);
|
|
90402
90645
|
const level = name2 === "title" ? 1 : name2 === "heading" ? Number(attrs.level ?? 2) : Number(name2.slice(1));
|
|
@@ -90506,12 +90749,21 @@ ${body.split(`
|
|
|
90506
90749
|
ctx.tracker.convert(name2);
|
|
90507
90750
|
return "";
|
|
90508
90751
|
}
|
|
90509
|
-
if (name2 === "todo") {
|
|
90752
|
+
if (name2 === "checkbox" || name2 === "todo") {
|
|
90510
90753
|
ctx.tracker.convert(name2);
|
|
90511
|
-
|
|
90512
|
-
|
|
90513
|
-
|
|
90514
|
-
|
|
90754
|
+
return renderChecklistItem(name2, nodes, attrs, ctx);
|
|
90755
|
+
}
|
|
90756
|
+
if (name2 === "poll") {
|
|
90757
|
+
if (nodes.length === 0 && Object.keys(attrs).length === 0) {
|
|
90758
|
+
ctx.tracker.skip(name2, "empty poll omitted because the export contains no identity or content", "warning");
|
|
90759
|
+
return "";
|
|
90760
|
+
}
|
|
90761
|
+
ctx.tracker.convert(name2);
|
|
90762
|
+
return renderPoll(nodes, attrs, ctx);
|
|
90763
|
+
}
|
|
90764
|
+
if (["option", "poll-option", "poll_option", "choice"].includes(name2)) {
|
|
90765
|
+
ctx.tracker.convert(name2);
|
|
90766
|
+
return renderPollOption(name2, nodes, attrs, ctx);
|
|
90515
90767
|
}
|
|
90516
90768
|
if (["mention", "person", "emoji"].includes(name2)) {
|
|
90517
90769
|
ctx.tracker.convert(name2);
|
|
@@ -90553,6 +90805,7 @@ function projectLarkDocxXml(input) {
|
|
|
90553
90805
|
// src/lib/feishu.ts
|
|
90554
90806
|
var LARK_BIN = "lark-cli";
|
|
90555
90807
|
var MAX_FETCH_PAGES = 50;
|
|
90808
|
+
var MAX_STRUCTURAL_FETCH_ATTEMPTS = 2;
|
|
90556
90809
|
|
|
90557
90810
|
class LarkCliNotInstalledError extends Error {
|
|
90558
90811
|
constructor() {
|
|
@@ -90754,6 +91007,8 @@ function extractDocsFetchAssets(payload) {
|
|
|
90754
91007
|
function emptyFidelityReport() {
|
|
90755
91008
|
return {
|
|
90756
91009
|
status: "complete",
|
|
91010
|
+
evidence_status: "complete",
|
|
91011
|
+
projection_status: "complete",
|
|
90757
91012
|
discovered: {},
|
|
90758
91013
|
converted: {},
|
|
90759
91014
|
skipped: [],
|
|
@@ -90785,37 +91040,35 @@ function fidelityReportAsset(report) {
|
|
|
90785
91040
|
return {
|
|
90786
91041
|
path: "capture-fidelity.json",
|
|
90787
91042
|
bytes: Buffer.from(`${JSON.stringify({
|
|
90788
|
-
schema_version: "context.lark-capture-fidelity.
|
|
91043
|
+
schema_version: "context.lark-capture-fidelity.v2",
|
|
90789
91044
|
...report
|
|
90790
91045
|
}, null, 2)}
|
|
90791
91046
|
`, "utf8"),
|
|
90792
91047
|
mediaType: "application/vnd.context.lark-capture-fidelity+json",
|
|
90793
91048
|
source: {
|
|
90794
91049
|
kind: "capture-fidelity",
|
|
90795
|
-
status: report.status
|
|
91050
|
+
status: report.status,
|
|
91051
|
+
evidence_status: report.evidence_status,
|
|
91052
|
+
projection_status: report.projection_status
|
|
90796
91053
|
}
|
|
90797
91054
|
};
|
|
90798
91055
|
}
|
|
90799
|
-
async function
|
|
91056
|
+
async function fetchDocsResponse(input, docsFetchPlan, runner2) {
|
|
90800
91057
|
const chunks = [];
|
|
90801
91058
|
let contentFormat;
|
|
90802
91059
|
let title;
|
|
90803
91060
|
let revisionId;
|
|
90804
91061
|
let unsupportedShape;
|
|
90805
91062
|
const assets = [];
|
|
90806
|
-
const docsFetchPlan = await resolveDocsFetchPlan(input.docsApiVersion ?? "auto", runner2);
|
|
90807
91063
|
let nextOffset;
|
|
90808
91064
|
for (let page = 0;page < MAX_FETCH_PAGES; page++) {
|
|
90809
91065
|
const args = ["docs", "+fetch", "--as", "user", "--doc", input.url, "--detail", "full", "--format", "json"];
|
|
90810
|
-
if (docsFetchPlan.apiVersion === "v2")
|
|
91066
|
+
if (docsFetchPlan.apiVersion === "v2")
|
|
90811
91067
|
args.push("--api-version", "v2");
|
|
90812
|
-
|
|
90813
|
-
if (docsFetchPlan.docFormat === "xml") {
|
|
91068
|
+
if (docsFetchPlan.docFormat === "xml")
|
|
90814
91069
|
args.push("--doc-format", "xml");
|
|
90815
|
-
|
|
90816
|
-
if (nextOffset !== undefined) {
|
|
91070
|
+
if (nextOffset !== undefined)
|
|
90817
91071
|
args.push("--offset", String(nextOffset));
|
|
90818
|
-
}
|
|
90819
91072
|
const result = await runner2(args);
|
|
90820
91073
|
if (result.exitCode !== 0) {
|
|
90821
91074
|
throw new LarkCliError(docsFetchFailureMessage(result.stderr, docsFetchPlan.apiVersion), result.exitCode, result.stderr);
|
|
@@ -90826,9 +91079,8 @@ async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
|
90826
91079
|
throw new LarkCliError("lark-cli docs +fetch changed content format between pages", 0, "");
|
|
90827
91080
|
}
|
|
90828
91081
|
contentFormat = extracted.format;
|
|
90829
|
-
if (page === 0 && extracted.title !== undefined)
|
|
91082
|
+
if (page === 0 && extracted.title !== undefined)
|
|
90830
91083
|
title = extracted.title;
|
|
90831
|
-
}
|
|
90832
91084
|
revisionId ??= extractDocsFetchRevisionId(payload);
|
|
90833
91085
|
assets.push(...extractDocsFetchAssets(payload));
|
|
90834
91086
|
if (extracted.body !== undefined && extracted.body.length > 0) {
|
|
@@ -90846,19 +91098,42 @@ async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
|
90846
91098
|
throw new LarkCliError(`${LARK_BIN} docs +fetch exceeded ${MAX_FETCH_PAGES} pagination calls; likely a server-side issue`, 0, "");
|
|
90847
91099
|
}
|
|
90848
91100
|
}
|
|
90849
|
-
const
|
|
91101
|
+
const body = chunks.join(`
|
|
90850
91102
|
|
|
90851
91103
|
`);
|
|
90852
|
-
if (
|
|
91104
|
+
if (body.trim().length === 0 && (title === undefined || title.length === 0)) {
|
|
90853
91105
|
if (unsupportedShape !== undefined) {
|
|
90854
91106
|
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, "");
|
|
90855
91107
|
}
|
|
90856
91108
|
throw new LarkCliError("document is empty — it may not exist or you lack permission", 0, "");
|
|
90857
91109
|
}
|
|
90858
|
-
|
|
91110
|
+
return {
|
|
91111
|
+
body,
|
|
91112
|
+
contentFormat,
|
|
91113
|
+
...title !== undefined ? { title } : {},
|
|
91114
|
+
...revisionId !== undefined ? { revisionId } : {},
|
|
91115
|
+
assets
|
|
91116
|
+
};
|
|
91117
|
+
}
|
|
91118
|
+
function hasEmptySubPageList(projection) {
|
|
91119
|
+
return projection.fidelity.issues.some((issue) => issue.code === LARK_EMPTY_SUB_PAGE_LIST_CODE);
|
|
91120
|
+
}
|
|
91121
|
+
async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
91122
|
+
const docsFetchPlan = await resolveDocsFetchPlan(input.docsApiVersion ?? "auto", runner2);
|
|
91123
|
+
let fetched = await fetchDocsResponse(input, docsFetchPlan, runner2);
|
|
91124
|
+
let projection;
|
|
91125
|
+
for (let attempt = 0;attempt < MAX_STRUCTURAL_FETCH_ATTEMPTS && fetched.contentFormat === "xml"; attempt++) {
|
|
91126
|
+
projection = projectLarkDocxXml({ xml: fetched.body, sourceUrl: input.url });
|
|
91127
|
+
if (!hasEmptySubPageList(projection) || attempt === MAX_STRUCTURAL_FETCH_ATTEMPTS - 1)
|
|
91128
|
+
break;
|
|
91129
|
+
fetched = await fetchDocsResponse(input, docsFetchPlan, runner2);
|
|
91130
|
+
}
|
|
91131
|
+
let body = fetched.body;
|
|
91132
|
+
let title = fetched.title;
|
|
91133
|
+
const revisionId = fetched.revisionId;
|
|
91134
|
+
const assets = [...fetched.assets];
|
|
90859
91135
|
let fidelity = emptyFidelityReport();
|
|
90860
|
-
if (
|
|
90861
|
-
const projection = projectLarkDocxXml({ xml: fetchedBody, sourceUrl: input.url });
|
|
91136
|
+
if (projection !== undefined) {
|
|
90862
91137
|
body = projection.markdown;
|
|
90863
91138
|
title ??= projection.title;
|
|
90864
91139
|
fidelity = projection.fidelity;
|
|
@@ -91206,6 +91481,21 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
91206
91481
|
next: `fix write permissions or restore ${materializedAt}, then rerun context run capture:lark:${resolved.sourceName}`
|
|
91207
91482
|
});
|
|
91208
91483
|
}
|
|
91484
|
+
if (fetched.fidelity.evidence_status === "error") {
|
|
91485
|
+
throw new ContextError(ExitCode.WorkspaceStateError, `lark source ${resolved.sourceName} audit snapshot was preserved, but capture fidelity validation failed`, {
|
|
91486
|
+
category: ErrorCategory.PartialFailure,
|
|
91487
|
+
code: "lark.capture.fidelity-loss",
|
|
91488
|
+
sourceName: resolved.sourceName,
|
|
91489
|
+
snapshot: {
|
|
91490
|
+
manifest: manifestPath,
|
|
91491
|
+
materializedAt,
|
|
91492
|
+
snapshot_hash: manifest.snapshot_hash,
|
|
91493
|
+
changed
|
|
91494
|
+
},
|
|
91495
|
+
issues: fetched.fidelity.issues.filter((issue) => issue.impact === "evidence"),
|
|
91496
|
+
next: "context status --format json"
|
|
91497
|
+
});
|
|
91498
|
+
}
|
|
91209
91499
|
return {
|
|
91210
91500
|
kind: "document.capture.lark.result",
|
|
91211
91501
|
source: {
|
|
@@ -97480,6 +97770,13 @@ function selectAutomaticWorkflowCommand(status) {
|
|
|
97480
97770
|
stop: blockedStop(status, "workflow.until.route-not-immediate", "The current route requires a decision or authority that is not resolved.")
|
|
97481
97771
|
};
|
|
97482
97772
|
}
|
|
97773
|
+
const unreadRequired = route.resources.required.filter((resource) => resource.read_state === "read-required");
|
|
97774
|
+
if (unreadRequired.length > 0) {
|
|
97775
|
+
return {
|
|
97776
|
+
state: "blocked",
|
|
97777
|
+
stop: blockedStop(status, "workflow.until.agent-context-required", `Read the current route's required resources before automatic execution: ${unreadRequired.map((resource) => resource.id).join(", ")}.`)
|
|
97778
|
+
};
|
|
97779
|
+
}
|
|
97483
97780
|
const commands = route.commands.filter((item) => item.availability === "immediate");
|
|
97484
97781
|
if (commands.length !== 1) {
|
|
97485
97782
|
return {
|
|
@@ -98375,10 +98672,10 @@ async function documentSnapshotState(input) {
|
|
|
98375
98672
|
if (fidelity.blocking.length > 0) {
|
|
98376
98673
|
return {
|
|
98377
98674
|
snapshotReady: false,
|
|
98378
|
-
state: "
|
|
98675
|
+
state: "needs-capture",
|
|
98379
98676
|
manifest,
|
|
98380
98677
|
diagnostics: fidelity.blocking,
|
|
98381
|
-
next: `context
|
|
98678
|
+
next: `context run capture:${input.sourceType}:${input.source.name}`,
|
|
98382
98679
|
...fidelity.report !== undefined ? { captureFidelity: fidelity.report } : {}
|
|
98383
98680
|
};
|
|
98384
98681
|
}
|
|
@@ -98913,6 +99210,7 @@ async function registerSourceBatch(input) {
|
|
|
98913
99210
|
|
|
98914
99211
|
// src/project/sourceRemoval.ts
|
|
98915
99212
|
import { existsSync as existsSync38 } from "node:fs";
|
|
99213
|
+
import { createHash as createHash25 } from "node:crypto";
|
|
98916
99214
|
import { readFile as readFile42, readdir as readdir18, rm as rm15 } from "node:fs/promises";
|
|
98917
99215
|
import { isAbsolute as isAbsolute10, join as join61, relative as relative15, resolve as resolve25, sep as sep3 } from "node:path";
|
|
98918
99216
|
var import_yaml34 = __toESM(require_dist3(), 1);
|
|
@@ -99009,7 +99307,8 @@ async function resolveRemovableSource(projectRoot, selector) {
|
|
|
99009
99307
|
name: source3.name,
|
|
99010
99308
|
...source3.namespace !== undefined ? { namespace: source3.namespace } : {},
|
|
99011
99309
|
...source3.module !== undefined ? { module: source3.module } : {},
|
|
99012
|
-
materializedAt: source3.materializedAt
|
|
99310
|
+
materializedAt: source3.materializedAt,
|
|
99311
|
+
...source3.snapshot?.manifest !== undefined ? { manifest: source3.snapshot.manifest } : {}
|
|
99013
99312
|
})),
|
|
99014
99313
|
...registry2.larks.map((source3) => ({
|
|
99015
99314
|
type: "lark",
|
|
@@ -99017,7 +99316,8 @@ async function resolveRemovableSource(projectRoot, selector) {
|
|
|
99017
99316
|
name: source3.name,
|
|
99018
99317
|
...source3.namespace !== undefined ? { namespace: source3.namespace } : {},
|
|
99019
99318
|
...source3.module !== undefined ? { module: source3.module } : {},
|
|
99020
|
-
materializedAt: source3.materializedAt
|
|
99319
|
+
materializedAt: source3.materializedAt,
|
|
99320
|
+
...source3.snapshot?.manifest !== undefined ? { manifest: source3.snapshot.manifest } : {}
|
|
99021
99321
|
}))
|
|
99022
99322
|
].filter((source3) => source3.id === selector || source3.name === selector);
|
|
99023
99323
|
if (matches.length === 0) {
|
|
@@ -99056,18 +99356,14 @@ function removeDocumentEntry(document4, source3) {
|
|
|
99056
99356
|
});
|
|
99057
99357
|
return { ...record, sources: nextSources };
|
|
99058
99358
|
}
|
|
99059
|
-
async function
|
|
99060
|
-
if (source3.type === "repo") {
|
|
99061
|
-
const registry2 = await readRepoRegistry(projectRoot);
|
|
99062
|
-
await writeRepoRegistry(projectRoot, {
|
|
99063
|
-
repos: registry2.repos.filter((entry) => entry.name !== source3.name && entry.id !== source3.id)
|
|
99064
|
-
});
|
|
99065
|
-
return;
|
|
99066
|
-
}
|
|
99359
|
+
async function registryRemovalWrite(projectRoot, source3) {
|
|
99067
99360
|
const path4 = registryPath2(source3.type);
|
|
99068
99361
|
const absolutePath = join61(projectRoot, path4);
|
|
99069
99362
|
const document4 = existsSync38(absolutePath) ? import_yaml34.default.parse(await readFile42(absolutePath, "utf8")) : { sources: [] };
|
|
99070
|
-
|
|
99363
|
+
return {
|
|
99364
|
+
path: absolutePath,
|
|
99365
|
+
bytes: import_yaml34.default.stringify(removeDocumentEntry(document4, source3))
|
|
99366
|
+
};
|
|
99071
99367
|
}
|
|
99072
99368
|
function safeManagedMaterializedPath(projectRoot, source3) {
|
|
99073
99369
|
if (isAbsolute10(source3.materializedAt))
|
|
@@ -99080,6 +99376,158 @@ function safeManagedMaterializedPath(projectRoot, source3) {
|
|
|
99080
99376
|
}
|
|
99081
99377
|
return absolute;
|
|
99082
99378
|
}
|
|
99379
|
+
function safeManagedManifestPath(projectRoot, source3) {
|
|
99380
|
+
const manifest = source3.manifest ?? join61(source3.materializedAt, "manifest.json");
|
|
99381
|
+
if (isAbsolute10(manifest))
|
|
99382
|
+
throw unsafeOwnership(source3, manifest);
|
|
99383
|
+
const absolute = resolve25(projectRoot, manifest);
|
|
99384
|
+
const expectedRoot = resolve25(projectRoot, "sources", source3.type);
|
|
99385
|
+
const rel = relative15(expectedRoot, absolute);
|
|
99386
|
+
if (rel.length === 0 || rel === ".." || rel.startsWith(`..${sep3}`) || isAbsolute10(rel)) {
|
|
99387
|
+
throw unsafeOwnership(source3, manifest);
|
|
99388
|
+
}
|
|
99389
|
+
return absolute;
|
|
99390
|
+
}
|
|
99391
|
+
function safeManagedSnapshotChild(projectRoot, source3, path4) {
|
|
99392
|
+
const materializedRoot = safeManagedMaterializedPath(projectRoot, source3);
|
|
99393
|
+
if (materializedRoot === undefined || isAbsolute10(path4))
|
|
99394
|
+
throw unsafeOwnership(source3, path4);
|
|
99395
|
+
const absolute = resolve25(materializedRoot, path4);
|
|
99396
|
+
const rel = relative15(materializedRoot, absolute);
|
|
99397
|
+
if (rel.length === 0 || rel === ".." || rel.startsWith(`..${sep3}`) || isAbsolute10(rel)) {
|
|
99398
|
+
throw unsafeOwnership(source3, path4);
|
|
99399
|
+
}
|
|
99400
|
+
return absolute;
|
|
99401
|
+
}
|
|
99402
|
+
function unsafeOwnership(source3, path4) {
|
|
99403
|
+
return new ContextError(ExitCode.WorkspaceStateError, `source '${source3.name}' has an unsafe managed path`, {
|
|
99404
|
+
category: ErrorCategory.WorkspaceStateInvalid,
|
|
99405
|
+
code: "source-remove-ownership-invalid",
|
|
99406
|
+
source: `${source3.type}:${source3.name}`,
|
|
99407
|
+
path: path4,
|
|
99408
|
+
next: "Repair the source registry or snapshot manifest, then preview source removal again."
|
|
99409
|
+
});
|
|
99410
|
+
}
|
|
99411
|
+
function projectRelative(projectRoot, path4) {
|
|
99412
|
+
return relative15(projectRoot, path4).split(sep3).join("/");
|
|
99413
|
+
}
|
|
99414
|
+
function digest4(value) {
|
|
99415
|
+
return `sha256:${createHash25("sha256").update(JSON.stringify(value)).digest("hex")}`;
|
|
99416
|
+
}
|
|
99417
|
+
async function sharedMaterializedOwners(projectRoot, source3) {
|
|
99418
|
+
const target = safeManagedMaterializedPath(projectRoot, source3);
|
|
99419
|
+
if (target === undefined)
|
|
99420
|
+
return [];
|
|
99421
|
+
const registry2 = await loadSourcesRegistry({ rootDir: projectRoot });
|
|
99422
|
+
const entries = source3.type === "repo" ? registry2.repos : source3.type === "file" ? registry2.files : registry2.larks;
|
|
99423
|
+
return entries.filter((entry) => entry.name !== source3.name && safeManagedMaterializedPath(projectRoot, {
|
|
99424
|
+
type: source3.type,
|
|
99425
|
+
id: entry.id,
|
|
99426
|
+
name: entry.name,
|
|
99427
|
+
materializedAt: entry.materializedAt
|
|
99428
|
+
}) === target).map((entry) => `${source3.type}:${entry.name}`).sort();
|
|
99429
|
+
}
|
|
99430
|
+
function documentOwnedPaths(projectRoot, source3, snapshot) {
|
|
99431
|
+
return [...new Set([
|
|
99432
|
+
...snapshot.files.map((entry) => entry.path),
|
|
99433
|
+
...snapshot.assets?.map((entry) => entry.path) ?? [],
|
|
99434
|
+
...snapshot.metadata?.capture?.routeFiles?.map((entry) => entry.path) ?? []
|
|
99435
|
+
].map((path4) => safeManagedSnapshotChild(projectRoot, source3, path4)))].sort();
|
|
99436
|
+
}
|
|
99437
|
+
async function createRemovalPlan(projectRoot, selector) {
|
|
99438
|
+
const source3 = await resolveRemovableSource(projectRoot, selector);
|
|
99439
|
+
const references = await collectSourceReferences(projectRoot, source3);
|
|
99440
|
+
const registryWrite = await registryRemovalWrite(projectRoot, source3);
|
|
99441
|
+
const sharedMaterializedBy = await sharedMaterializedOwners(projectRoot, source3);
|
|
99442
|
+
const absoluteRemovals = [];
|
|
99443
|
+
let manifestWrite;
|
|
99444
|
+
let cleanup = {
|
|
99445
|
+
mode: "registry-only",
|
|
99446
|
+
sharedMaterializedBy,
|
|
99447
|
+
filesToRemove: [],
|
|
99448
|
+
directoriesToRemove: []
|
|
99449
|
+
};
|
|
99450
|
+
if (source3.type === "file" || source3.type === "lark") {
|
|
99451
|
+
const manifestPath = safeManagedManifestPath(projectRoot, source3);
|
|
99452
|
+
let removal;
|
|
99453
|
+
try {
|
|
99454
|
+
removal = removeDocumentSnapshotFromManifestFile({
|
|
99455
|
+
current: await readDocumentManifestFile(manifestPath),
|
|
99456
|
+
sourceName: source3.name
|
|
99457
|
+
});
|
|
99458
|
+
} catch (error) {
|
|
99459
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
99460
|
+
throw new ContextError(ExitCode.WorkspaceStateError, `cannot prove snapshot ownership for '${source3.name}': ${message}`, {
|
|
99461
|
+
category: ErrorCategory.WorkspaceStateInvalid,
|
|
99462
|
+
code: "source-remove-ownership-invalid",
|
|
99463
|
+
source: `${source3.type}:${source3.name}`,
|
|
99464
|
+
manifest: projectRelative(projectRoot, manifestPath),
|
|
99465
|
+
next: "Repair or recapture the source snapshot, then preview source removal again."
|
|
99466
|
+
});
|
|
99467
|
+
}
|
|
99468
|
+
if (removal.snapshot !== null) {
|
|
99469
|
+
absoluteRemovals.push(...documentOwnedPaths(projectRoot, source3, removal.snapshot));
|
|
99470
|
+
if (removal.next === null) {
|
|
99471
|
+
absoluteRemovals.push(manifestPath);
|
|
99472
|
+
} else {
|
|
99473
|
+
manifestWrite = { path: manifestPath, bytes: renderDocumentManifestFile(removal.next) };
|
|
99474
|
+
}
|
|
99475
|
+
cleanup = {
|
|
99476
|
+
mode: "document-snapshot",
|
|
99477
|
+
sharedMaterializedBy,
|
|
99478
|
+
manifest: projectRelative(projectRoot, manifestPath),
|
|
99479
|
+
manifestEntry: source3.name,
|
|
99480
|
+
filesToRemove: absoluteRemovals.filter((path4) => path4 !== manifestPath).map((path4) => projectRelative(projectRoot, path4)),
|
|
99481
|
+
directoriesToRemove: []
|
|
99482
|
+
};
|
|
99483
|
+
}
|
|
99484
|
+
} else {
|
|
99485
|
+
const materializedPath = safeManagedMaterializedPath(projectRoot, source3);
|
|
99486
|
+
if (materializedPath !== undefined && sharedMaterializedBy.length === 0 && existsSync38(materializedPath)) {
|
|
99487
|
+
absoluteRemovals.push(materializedPath);
|
|
99488
|
+
cleanup = {
|
|
99489
|
+
mode: "exclusive-materialization",
|
|
99490
|
+
sharedMaterializedBy,
|
|
99491
|
+
filesToRemove: [],
|
|
99492
|
+
directoriesToRemove: [projectRelative(projectRoot, materializedPath)]
|
|
99493
|
+
};
|
|
99494
|
+
}
|
|
99495
|
+
}
|
|
99496
|
+
const planDigest = digest4({
|
|
99497
|
+
source: source3,
|
|
99498
|
+
registry: registryPath2(source3.type),
|
|
99499
|
+
registryBytes: registryWrite.bytes,
|
|
99500
|
+
references,
|
|
99501
|
+
cleanup,
|
|
99502
|
+
manifestBytes: manifestWrite?.bytes ?? null
|
|
99503
|
+
});
|
|
99504
|
+
const next = references.length > 0 ? `Remove all listed references, then preview context source remove '${source3.id}' --format json again.` : `context source remove '${source3.id}' --yes --plan-digest '${planDigest}' --format json`;
|
|
99505
|
+
return {
|
|
99506
|
+
action: "preview",
|
|
99507
|
+
source: source3,
|
|
99508
|
+
registry: registryPath2(source3.type),
|
|
99509
|
+
materialized: source3.materializedAt,
|
|
99510
|
+
references,
|
|
99511
|
+
plan_digest: planDigest,
|
|
99512
|
+
cleanup,
|
|
99513
|
+
next,
|
|
99514
|
+
registryWrite,
|
|
99515
|
+
...manifestWrite !== undefined ? { manifestWrite } : {},
|
|
99516
|
+
absoluteRemovals: [...new Set(absoluteRemovals)].sort()
|
|
99517
|
+
};
|
|
99518
|
+
}
|
|
99519
|
+
function publicRemovalResult(plan, action) {
|
|
99520
|
+
return {
|
|
99521
|
+
action,
|
|
99522
|
+
source: plan.source,
|
|
99523
|
+
registry: plan.registry,
|
|
99524
|
+
materialized: plan.materialized,
|
|
99525
|
+
references: plan.references,
|
|
99526
|
+
plan_digest: plan.plan_digest,
|
|
99527
|
+
cleanup: plan.cleanup,
|
|
99528
|
+
...action === "preview" && plan.next !== undefined ? { next: plan.next } : {}
|
|
99529
|
+
};
|
|
99530
|
+
}
|
|
99083
99531
|
async function pruneExtractRuntime(projectRoot, source3) {
|
|
99084
99532
|
const fingerprintPath = join61(projectRoot, ".tmp/context-runtime/extract/source-fingerprints.json");
|
|
99085
99533
|
const removedPhaseIds = new Set;
|
|
@@ -99130,34 +99578,43 @@ async function pruneExtractRuntime(projectRoot, source3) {
|
|
|
99130
99578
|
await visit3(snapshotRoot);
|
|
99131
99579
|
}
|
|
99132
99580
|
async function removeProjectSource(input) {
|
|
99133
|
-
|
|
99134
|
-
|
|
99135
|
-
|
|
99136
|
-
|
|
99137
|
-
source
|
|
99138
|
-
|
|
99139
|
-
|
|
99140
|
-
|
|
99141
|
-
...!input.apply ? { next: `Remove all listed references, then run context source remove '${source3.id}' --yes --format json.` } : {}
|
|
99142
|
-
};
|
|
99143
|
-
if (!input.apply)
|
|
99144
|
-
return result;
|
|
99145
|
-
if (references.length > 0) {
|
|
99146
|
-
throw new ContextError(ExitCode.WorkspaceStateError, `source '${source3.name}' is still referenced`, {
|
|
99147
|
-
category: ErrorCategory.WorkspaceStateInvalid,
|
|
99148
|
-
code: "source-remove-referenced",
|
|
99149
|
-
source: `${source3.type}:${source3.name}`,
|
|
99150
|
-
references,
|
|
99151
|
-
next: `Remove the listed project/candidate/knowledge references, then rerun context source remove '${source3.id}' --yes --format json.`
|
|
99581
|
+
if (!input.apply) {
|
|
99582
|
+
return publicRemovalResult(await createRemovalPlan(input.projectRoot, input.selector), "preview");
|
|
99583
|
+
}
|
|
99584
|
+
if (input.planDigest === undefined) {
|
|
99585
|
+
throw new ContextError(ExitCode.UserError, "source removal requires a digest-bound preview", {
|
|
99586
|
+
category: ErrorCategory.UserInputInvalid,
|
|
99587
|
+
code: "source-remove-plan-required",
|
|
99588
|
+
next: `Run context source remove '${input.selector}' --format json, inspect cleanup, then execute its next command.`
|
|
99152
99589
|
});
|
|
99153
99590
|
}
|
|
99154
99591
|
return withProjectWriteLock(input.projectRoot, "source-remove", async () => {
|
|
99155
|
-
await
|
|
99156
|
-
|
|
99157
|
-
|
|
99158
|
-
|
|
99159
|
-
|
|
99160
|
-
|
|
99592
|
+
const plan = await createRemovalPlan(input.projectRoot, input.selector);
|
|
99593
|
+
if (plan.plan_digest !== input.planDigest) {
|
|
99594
|
+
throw new ContextError(ExitCode.WorkspaceStateError, "source removal preview is stale", {
|
|
99595
|
+
category: ErrorCategory.WorkspaceStateInvalid,
|
|
99596
|
+
code: "source-remove-plan-stale",
|
|
99597
|
+
expected: input.planDigest,
|
|
99598
|
+
actual: plan.plan_digest,
|
|
99599
|
+
next: `Run context source remove '${plan.source.id}' --format json again and inspect the new cleanup plan.`
|
|
99600
|
+
});
|
|
99601
|
+
}
|
|
99602
|
+
if (plan.references.length > 0) {
|
|
99603
|
+
throw new ContextError(ExitCode.WorkspaceStateError, `source '${plan.source.name}' is still referenced`, {
|
|
99604
|
+
category: ErrorCategory.WorkspaceStateInvalid,
|
|
99605
|
+
code: "source-remove-referenced",
|
|
99606
|
+
source: `${plan.source.type}:${plan.source.name}`,
|
|
99607
|
+
references: plan.references,
|
|
99608
|
+
next: `Remove the listed project/candidate/knowledge references, then preview context source remove '${plan.source.id}' --format json again.`
|
|
99609
|
+
});
|
|
99610
|
+
}
|
|
99611
|
+
await applyAtomicFileBatch({
|
|
99612
|
+
transactionRoot: join61(input.projectRoot, ".tmp", "context-runtime", "source-remove-transactions"),
|
|
99613
|
+
writes: [plan.registryWrite, ...plan.manifestWrite !== undefined ? [plan.manifestWrite] : []],
|
|
99614
|
+
removals: plan.absoluteRemovals
|
|
99615
|
+
});
|
|
99616
|
+
await pruneExtractRuntime(input.projectRoot, plan.source);
|
|
99617
|
+
return publicRemovalResult(plan, "removed");
|
|
99161
99618
|
});
|
|
99162
99619
|
}
|
|
99163
99620
|
|
|
@@ -99490,14 +99947,15 @@ the CLI derives a lowercase path-safe module and rejects duplicate batch identit
|
|
|
99490
99947
|
const format = assertChoice(options.format, ["json", "yaml"], "--format");
|
|
99491
99948
|
writeFormatted(projectSource, format);
|
|
99492
99949
|
});
|
|
99493
|
-
source3.command("remove <id>").description("Preview or remove one unreferenced source and its managed snapshot").option("--yes", "apply the removal after reference checks").option("--format <format>", "output format: json | yaml | table", "json").action(async (id2, ...args) => {
|
|
99950
|
+
source3.command("remove <id>").description("Preview or remove one unreferenced source and its managed snapshot").option("--yes", "apply the removal after reference checks").option("--plan-digest <digest>", "bind --yes to the exact previewed cleanup plan").option("--format <format>", "output format: json | yaml | table", "json").action(async (id2, ...args) => {
|
|
99494
99951
|
const options = actionOptions(...args);
|
|
99495
99952
|
const projectRoot = requireProjectRoot(process.cwd(), "source remove");
|
|
99496
99953
|
const format = assertChoice(options.format, DATA_FORMATS, "--format");
|
|
99497
99954
|
const result = await removeProjectSource({
|
|
99498
99955
|
projectRoot,
|
|
99499
99956
|
selector: id2,
|
|
99500
|
-
apply: options.yes === true
|
|
99957
|
+
apply: options.yes === true,
|
|
99958
|
+
...typeof options.planDigest === "string" ? { planDigest: options.planDigest } : {}
|
|
99501
99959
|
});
|
|
99502
99960
|
writeFormatted(result, format);
|
|
99503
99961
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c4a/context-cli",
|
|
3
|
-
"version": "0.6.1-beta.
|
|
3
|
+
"version": "0.6.1-beta.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local CLI for capturing, compiling, and governing knowledge workspaces",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@c4a/agent-graph": "0.2.3",
|
|
24
|
-
"@c4a/context": "0.6.1-beta.
|
|
24
|
+
"@c4a/context": "0.6.1-beta.6",
|
|
25
25
|
"commander": "^11.0.0",
|
|
26
26
|
"fast-xml-parser": "^5.10.1",
|
|
27
27
|
"handlebars": "^4.7.8",
|
package/plugins/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.6.1-beta.
|
|
1
|
+
0.6.1-beta.6
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context",
|
|
3
|
-
"version": "0.6.1-beta.
|
|
3
|
+
"version": "0.6.1-beta.6",
|
|
4
4
|
"description": "Maintain a project-local knowledge workspace through init and next-step agent guidance.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "c4a"
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"skills": "./skills/",
|
|
19
19
|
"interface": {
|
|
20
20
|
"displayName": "C4A Context",
|
|
21
|
-
"shortDescription": "Initialize and advance a local, source-linked project knowledge workspace.\nv0.6.1-beta.
|
|
21
|
+
"shortDescription": "Initialize and advance a local, source-linked project knowledge workspace.\nv0.6.1-beta.6",
|
|
22
22
|
"longDescription": "Create a Context workspace and use agent-guided next steps to register sources, run extraction, review candidates, build package outputs, and verify health without silently mutating source repositories.",
|
|
23
23
|
"developerName": "c4a",
|
|
24
24
|
"category": "Productivity",
|
|
@@ -129,8 +129,8 @@ nodes:
|
|
|
129
129
|
resources:
|
|
130
130
|
required:
|
|
131
131
|
- resources/procedures/prose-compile.md
|
|
132
|
-
- resources/views/structure-current.yaml
|
|
133
132
|
recommended:
|
|
133
|
+
- resources/views/structure-current.yaml
|
|
134
134
|
- resources/semantic/compile/index.md
|
|
135
135
|
|
|
136
136
|
- id: resume-review-current-batch
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
"schema": "agent-graph.bundle.v1",
|
|
3
3
|
"provider": {
|
|
4
4
|
"id": "c4a/context",
|
|
5
|
-
"version": "0.6.1-beta.
|
|
5
|
+
"version": "0.6.1-beta.6"
|
|
6
6
|
},
|
|
7
7
|
"providerManifest": "provider.yaml",
|
|
8
8
|
"graphs": [
|
|
9
9
|
{
|
|
10
10
|
"id": "workspace",
|
|
11
11
|
"path": "graphs/workspace.yaml",
|
|
12
|
-
"digest": "sha256:
|
|
12
|
+
"digest": "sha256:4b344cb9ecc03990f2a2f35aca372949917510f5957c4760fe69a651368eddbc"
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"actions": [
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
{
|
|
154
154
|
"id": "context.sdk.project-api",
|
|
155
155
|
"path": "resources/manuals/reference/project-api.md",
|
|
156
|
-
"digest": "sha256:
|
|
156
|
+
"digest": "sha256:9ab0be26aeccea79f561c7ecd01636beb97b7088ea1ddb141c3cbc96af758508"
|
|
157
157
|
},
|
|
158
158
|
{
|
|
159
159
|
"id": "context.sdk.template-variables",
|
|
@@ -313,17 +313,17 @@
|
|
|
313
313
|
{
|
|
314
314
|
"id": "procedure.prose-compile",
|
|
315
315
|
"path": "resources/procedures/prose-compile.md",
|
|
316
|
-
"digest": "sha256:
|
|
316
|
+
"digest": "sha256:c36ba3dcf9aa11a8759afaab2c9a99859c31acaf5f06456c4e0e6793130b7d6e"
|
|
317
317
|
},
|
|
318
318
|
{
|
|
319
319
|
"id": "procedure.source-boundary",
|
|
320
320
|
"path": "resources/procedures/source-boundary.md",
|
|
321
|
-
"digest": "sha256:
|
|
321
|
+
"digest": "sha256:99097947961d7fa5b17f9e6b9c9471aac38cec1943bd7feb414b21d6a1e26a49"
|
|
322
322
|
},
|
|
323
323
|
{
|
|
324
324
|
"id": "procedure.source-capture-detailed",
|
|
325
325
|
"path": "resources/procedures/source-capture-detailed.md",
|
|
326
|
-
"digest": "sha256:
|
|
326
|
+
"digest": "sha256:0cf764a43c33fbcf86b32a3731fec5905ebf0d312b7939059aae99a6ab0532e2"
|
|
327
327
|
},
|
|
328
328
|
{
|
|
329
329
|
"id": "procedure.verify-and-repair",
|
|
@@ -447,11 +447,11 @@
|
|
|
447
447
|
},
|
|
448
448
|
{
|
|
449
449
|
"path": "graphs/workspace.yaml",
|
|
450
|
-
"digest": "sha256:
|
|
450
|
+
"digest": "sha256:4b344cb9ecc03990f2a2f35aca372949917510f5957c4760fe69a651368eddbc"
|
|
451
451
|
},
|
|
452
452
|
{
|
|
453
453
|
"path": "provider.yaml",
|
|
454
|
-
"digest": "sha256:
|
|
454
|
+
"digest": "sha256:ad1b825734418814f1ac3673d329070228b6b8004cd821c39a3224c6d30935dd"
|
|
455
455
|
},
|
|
456
456
|
{
|
|
457
457
|
"path": "resources/diagnostics/projection-stale.md",
|
|
@@ -511,7 +511,7 @@
|
|
|
511
511
|
},
|
|
512
512
|
{
|
|
513
513
|
"path": "resources/manuals/reference/project-api.md",
|
|
514
|
-
"digest": "sha256:
|
|
514
|
+
"digest": "sha256:9ab0be26aeccea79f561c7ecd01636beb97b7088ea1ddb141c3cbc96af758508"
|
|
515
515
|
},
|
|
516
516
|
{
|
|
517
517
|
"path": "resources/manuals/reference/template-variables.md",
|
|
@@ -555,15 +555,15 @@
|
|
|
555
555
|
},
|
|
556
556
|
{
|
|
557
557
|
"path": "resources/procedures/prose-compile.md",
|
|
558
|
-
"digest": "sha256:
|
|
558
|
+
"digest": "sha256:c36ba3dcf9aa11a8759afaab2c9a99859c31acaf5f06456c4e0e6793130b7d6e"
|
|
559
559
|
},
|
|
560
560
|
{
|
|
561
561
|
"path": "resources/procedures/source-boundary.md",
|
|
562
|
-
"digest": "sha256:
|
|
562
|
+
"digest": "sha256:99097947961d7fa5b17f9e6b9c9471aac38cec1943bd7feb414b21d6a1e26a49"
|
|
563
563
|
},
|
|
564
564
|
{
|
|
565
565
|
"path": "resources/procedures/source-capture-detailed.md",
|
|
566
|
-
"digest": "sha256:
|
|
566
|
+
"digest": "sha256:0cf764a43c33fbcf86b32a3731fec5905ebf0d312b7939059aae99a6ab0532e2"
|
|
567
567
|
},
|
|
568
568
|
{
|
|
569
569
|
"path": "resources/procedures/verify-and-repair.md",
|
|
@@ -633,5 +633,5 @@
|
|
|
633
633
|
"graphDependencies": {
|
|
634
634
|
"workspace": []
|
|
635
635
|
},
|
|
636
|
-
"digest": "sha256:
|
|
636
|
+
"digest": "sha256:9496e5137e372e4711605465046811bf322c53603bc7b8c5fbfa07df0862d0eb"
|
|
637
637
|
}
|
|
@@ -343,9 +343,12 @@ redacted XML audit asset, projects supported blocks deterministically into
|
|
|
343
343
|
readable Markdown, and registers external resources such as document citations,
|
|
344
344
|
images, video, whiteboards, and Base references in the snapshot manifest even
|
|
345
345
|
when their binary content is not downloaded. The projection does not infer or
|
|
346
|
-
summarize document meaning. Its fidelity report closes discovered blocks
|
|
347
|
-
converted and intentionally skipped blocks
|
|
348
|
-
|
|
346
|
+
summarize document meaning. Its fidelity report closes discovered blocks
|
|
347
|
+
against converted and intentionally skipped blocks and reports evidence
|
|
348
|
+
completeness separately from Markdown projection quality. Unknown non-empty XML
|
|
349
|
+
blocks receive a generic, auditable, non-interactive projection and do not block
|
|
350
|
+
downstream work. Missing source content or unresolved external-resource identity
|
|
351
|
+
remains an evidence error and prevents downstream Review.
|
|
349
352
|
Snapshot files live under `sources/lark/<date>/` as sibling document files
|
|
350
353
|
tracked by one date-level `manifest.json`. Access credentials and transient
|
|
351
354
|
signed media URLs are not written into the workspace.
|
|
@@ -15,6 +15,9 @@ by the current Route. Do not create compile-action payloads or rewrite source
|
|
|
15
15
|
content.
|
|
16
16
|
|
|
17
17
|
One compile command validates every owned view first, then atomically
|
|
18
|
-
materializes the source/collection candidate batch.
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
materializes the source/collection candidate batch. In an explicitly managed
|
|
19
|
+
conversation, the host loop may continue across the remaining deterministic
|
|
20
|
+
compile slots after this procedure has been read; it re-evaluates revision and
|
|
21
|
+
validation state after every write. The current structure view is optional
|
|
22
|
+
inspection context because the CLI consumes the confirmed structure directly.
|
|
23
|
+
Do not open a partial Review while planned views remain.
|
|
@@ -34,7 +34,12 @@ To retire a registered source, first run `context source remove <source-id>
|
|
|
34
34
|
--format json`. This is a read-only preview that lists every project, candidate,
|
|
35
35
|
or approved-knowledge reference. Only after those references are intentionally
|
|
36
36
|
resolved may the route use `--yes`; the CLI never silently deletes referenced
|
|
37
|
-
knowledge or another source's materialized files.
|
|
37
|
+
knowledge or another source's materialized files. Execute the exact
|
|
38
|
+
digest-bound command returned by the preview. For a shared document batch, the
|
|
39
|
+
manifest entry is the ownership boundary: removal deletes only that entry and
|
|
40
|
+
its explicitly listed files/assets. An uncaptured module has no manifest entry,
|
|
41
|
+
so removal is registry-only; the shared date directory is never inferred as
|
|
42
|
+
module-owned.
|
|
38
43
|
|
|
39
44
|
Repository readiness checks are mechanical and may run after the boundary is
|
|
40
45
|
registered. Clone, fetch, checkout, install, build, test, and other external
|
|
@@ -54,12 +54,15 @@ contract, so any manual edit breaks idempotency.
|
|
|
54
54
|
Lark capture obtains the structured XML representation and produces two
|
|
55
55
|
separate artifacts: XML audit evidence and a deterministic readable Markdown
|
|
56
56
|
projection. Do not treat raw XML as Markdown and do not rewrite it yourself.
|
|
57
|
-
Inspect
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
Inspect `evidence_status` and `projection_status` separately. An evidence error
|
|
58
|
+
means the source body, external content, or a stable resource locator could not
|
|
59
|
+
be preserved, so the workflow must not proceed to Align, Compile, or Review.
|
|
60
|
+
A projection warning or `generic` status means the original XML is preserved
|
|
61
|
+
and a deterministic non-interactive Markdown fallback was emitted; report the
|
|
62
|
+
diagnostic, but continue through the Route. The CLI, not the plugin, parses the
|
|
63
|
+
document. The plugin only displays the structured counts and diagnostics
|
|
64
|
+
returned by the current source resource. Do not patch the CLI or edit snapshots
|
|
65
|
+
to add a renderer from a user workspace.
|
|
63
66
|
|
|
64
67
|
### Code capture diagnostics
|
|
65
68
|
|