@c4a/context-cli 0.5.35-alpha.3 → 0.5.35-beta.1

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 CHANGED
@@ -45002,6 +45002,17 @@ function hasCitedFencedDetail(detail, basisTexts) {
45002
45002
  return false;
45003
45003
  return fencedBlocksIn(detail).some((block) => cited.includes(block));
45004
45004
  }
45005
+ function fencedPayloadLines(block) {
45006
+ return block.split(/\r?\n/u).filter((line) => !/^\s*(?:```|~~~)/u.test(line)).map((line) => line.trim()).filter((line) => line.length > 0);
45007
+ }
45008
+ function citedFencedDetailSupportsExcerpt(detail, basisTexts) {
45009
+ const citedBlocks = fencedBlocksIn(basisText(basisTexts));
45010
+ const detailBlocks = fencedBlocksIn(detail);
45011
+ return detailBlocks.length > 0 && detailBlocks.every((block) => citedBlocks.some((cited) => cited.includes(block) || fencedPayloadLines(block).every((line) => cited.includes(line))));
45012
+ }
45013
+ function stripFencedBlocks(value) {
45014
+ return value.replace(FENCED_DETAIL_BLOCK_RE, " ");
45015
+ }
45005
45016
  var FENCED_DETAIL_BLOCK_RE;
45006
45017
  var init_sectionDetail = __esm(() => {
45007
45018
  init_normalize();
@@ -45235,13 +45246,18 @@ function sourceTextSupportDiagnostic(content3, citedText, thresholds = DEFAULT_T
45235
45246
  function sourceSectionSupportDiagnostic(input) {
45236
45247
  const detail = typeof input.detail === "string" ? input.detail.trim() : "";
45237
45248
  const content3 = input.content;
45249
+ const thresholds = thresholdsForKind(input.kind);
45238
45250
  if (detail.length === 0) {
45239
- return {
45240
- ...sourceTextSupportDiagnostic(content3, input.citedText, thresholdsForKind(input.kind)),
45241
- checkedFields: ["content"]
45242
- };
45251
+ const diagnostic = sourceTextSupportDiagnostic(content3, input.citedText, thresholds);
45252
+ if (input.kind === "example" && diagnostic.verdict === "unsupported" && citedFencedDetailSupportsExcerpt(content3, input.citedText)) {
45253
+ const prose = stripFencedBlocks(content3).trim();
45254
+ const proseDiagnostic = prose.length === 0 ? { ...diagnostic, verdict: "supported", missingContentTerms: [], missingHardTerms: [] } : sourceTextSupportDiagnostic(prose, stripFencedBlocks(input.citedText), thresholds);
45255
+ if (prose.length === 0 || proseDiagnostic.verdict !== "unsupported" && proseDiagnostic.missingHardTerms.length === 0) {
45256
+ return { ...proseDiagnostic, checkedFields: ["content"] };
45257
+ }
45258
+ }
45259
+ return { ...diagnostic, checkedFields: ["content"] };
45243
45260
  }
45244
- const thresholds = thresholdsForKind(input.kind);
45245
45261
  const contentDiagnostic = sourceTextSupportDiagnostic(content3, input.citedText, thresholds);
45246
45262
  const detailDiagnostic = sourceTextSupportDiagnostic(detail, input.citedText, thresholds);
45247
45263
  const combined = sourceTextSupportDiagnostic(`${content3}
@@ -46456,10 +46472,13 @@ function createCompileChangesOutput(input) {
46456
46472
  let nodesChanged = 0;
46457
46473
  let nonBlockingBlocksChanged = 0;
46458
46474
  let nonBlockingNodesChanged = 0;
46475
+ const firstCompileSlugs = [];
46459
46476
  for (const node3 of input.nodes) {
46460
46477
  const actionableBlocks = blockingChangedBlocksForNode(node3);
46461
46478
  const nonBlockingBlocks = nonBlockingChangedBlocksForNode(node3);
46462
46479
  const locatorOnlyChanges = node3.locator_only_changes ?? [];
46480
+ if (node3.reason === "first-compile")
46481
+ firstCompileSlugs.push(node3.slug);
46463
46482
  if (node3.reason === "first-compile" || actionableBlocks.length > 0 || locatorOnlyChanges.length > 0)
46464
46483
  nodesChanged += 1;
46465
46484
  if (nonBlockingBlocks.length > 0)
@@ -46475,6 +46494,7 @@ function createCompileChangesOutput(input) {
46475
46494
  sectionsChanged.add(`${node3.slug}:${section}`);
46476
46495
  }
46477
46496
  }
46497
+ firstCompileSlugs.sort((left, right) => left.localeCompare(right));
46478
46498
  const status = input.unknownInputs.length > 0 ? "unknown-input" : "ready";
46479
46499
  return {
46480
46500
  schema_version: INCREMENTAL_SCHEMA_VERSION,
@@ -46496,6 +46516,14 @@ function createCompileChangesOutput(input) {
46496
46516
  unknown_inputs: input.unknownInputs.length,
46497
46517
  partial: input.unknownInputs.length > 0
46498
46518
  },
46519
+ ...firstCompileSlugs.length > 0 ? {
46520
+ first_compile_pending: {
46521
+ remaining: firstCompileSlugs.length,
46522
+ remaining_slugs: firstCompileSlugs,
46523
+ commands: firstCompileSlugs.map((slug) => `context compile --context ${slug}`),
46524
+ next_action: "Compile each remaining first-compile Node before running context compile --close."
46525
+ }
46526
+ } : {},
46499
46527
  nodes: input.nodes,
46500
46528
  ...input.ignoredLocatorChanges !== undefined && input.ignoredLocatorChanges.length > 0 ? { ignored_locator_changes: input.ignoredLocatorChanges } : {},
46501
46529
  unknown_inputs: input.unknownInputs
@@ -46561,35 +46589,35 @@ var init_compileIgnoredSources = __esm(() => {
46561
46589
  });
46562
46590
 
46563
46591
  // src/workflow/compileChangesInputs.ts
46592
+ function alignNodeFromOwnership(node3) {
46593
+ return {
46594
+ slug: node3.slug,
46595
+ type: node3.type,
46596
+ sources: [...node3.sources],
46597
+ contextSources: [...node3.context_sources ?? []],
46598
+ hasExplicitSources: true,
46599
+ plannedSections: [...node3.planned_sections ?? []]
46600
+ };
46601
+ }
46564
46602
  async function readAlignNodes(ctxDir) {
46565
46603
  try {
46566
- const nodes = await scanWorkspaceNodes(ctxDir);
46567
- if (nodes.length === 0) {
46568
- const ownership = await readCurrentSourceOwnership(ctxDir);
46569
- if (ownership?.nodes !== undefined && ownership.nodes.length > 0) {
46570
- return {
46571
- status: "ready",
46572
- nodes: ownership.nodes.map((node3) => ({
46573
- slug: node3.slug,
46574
- type: node3.type,
46575
- sources: [...node3.sources],
46576
- contextSources: [...node3.context_sources ?? []],
46577
- hasExplicitSources: true,
46578
- plannedSections: [...node3.planned_sections ?? []]
46579
- }))
46580
- };
46581
- }
46604
+ const locatedNodes = await scanWorkspaceNodes(ctxDir);
46605
+ const nodes = new Map(locatedNodes.map((located) => [located.parsed.node.id, {
46606
+ slug: located.parsed.node.id,
46607
+ type: located.parsed.node.type,
46608
+ sources: [...located.parsed.node.sources],
46609
+ contextSources: [...located.parsed.node.context_sources ?? []],
46610
+ hasExplicitSources: true,
46611
+ plannedSections: [...located.parsed.node.planned_sections ?? []]
46612
+ }]));
46613
+ const ownership = await readCurrentSourceOwnership(ctxDir);
46614
+ for (const node3 of ownership?.nodes ?? []) {
46615
+ if (!nodes.has(node3.slug))
46616
+ nodes.set(node3.slug, alignNodeFromOwnership(node3));
46582
46617
  }
46583
46618
  return {
46584
46619
  status: "ready",
46585
- nodes: nodes.map((located) => ({
46586
- slug: located.parsed.node.id,
46587
- type: located.parsed.node.type,
46588
- sources: [...located.parsed.node.sources],
46589
- contextSources: [...located.parsed.node.context_sources ?? []],
46590
- hasExplicitSources: true,
46591
- plannedSections: [...located.parsed.node.planned_sections ?? []]
46592
- }))
46620
+ nodes: [...nodes.values()]
46593
46621
  };
46594
46622
  } catch {
46595
46623
  return { status: "unknown-input", reason: "knowledge-scan-failed" };
@@ -49453,7 +49481,7 @@ function lineRangeText(range) {
49453
49481
  return `${range.start}:${range.end}`;
49454
49482
  }
49455
49483
  function requestFullTextPageCommand(slug, blockId, range) {
49456
- return `context compile --context ${slug} --request-full-text ${blockId} --request-full-text-range ${range} --format json`;
49484
+ return `context compile --context ${slug} --request-full-text ${blockId} --request-full-text-range ${range} --view text --format json`;
49457
49485
  }
49458
49486
  function isFenceLine(line) {
49459
49487
  return /^\s*(```|~~~)/u.test(line);
@@ -49828,7 +49856,7 @@ function requestableFullTextBlockIds(ownership, slug) {
49828
49856
  }
49829
49857
  function requestFullTextCommand(slug, blockIds) {
49830
49858
  const flags2 = blockIds.length > 0 ? blockIds.map((blockId) => ` --request-full-text ${blockId}`).join("") : " --request-full-text <block_id>";
49831
- return `context compile --context ${slug}${flags2}`;
49859
+ return `context compile --context ${slug}${flags2} --view text --format json`;
49832
49860
  }
49833
49861
  function requestFullTextError(input) {
49834
49862
  const hint = {
@@ -50033,7 +50061,7 @@ async function appendFinalizedBlock(input) {
50033
50061
  request_full_text: {
50034
50062
  available: true,
50035
50063
  block_id: blockId,
50036
- command_hint: `context compile --context ${input.nodeSlug} --request-full-text ${blockId}`
50064
+ command_hint: `context compile --context ${input.nodeSlug} --request-full-text ${blockId} --view text --format json`
50037
50065
  }
50038
50066
  } : {}
50039
50067
  };
@@ -50254,7 +50282,7 @@ function compileContextRuntimeHints(context) {
50254
50282
  message: `${expandableSecondaryBlockIds.length} secondary shared block(s) are compacted and not citation-eligible.`,
50255
50283
  target_node: context.node.slug,
50256
50284
  next_action: "Request full text only when you need to inspect background. Expanded secondary text still cannot support Section source_refs.",
50257
- command: `context compile --context ${context.node.slug}${flags2}`,
50285
+ command: `context compile --context ${context.node.slug}${flags2} --view text --format json`,
50258
50286
  available_block_ids: expandableSecondaryBlockIds
50259
50287
  });
50260
50288
  }
@@ -52747,6 +52775,23 @@ function nearbyCitationEligibleSourceRefs(input) {
52747
52775
  })).filter((item) => item.distance <= 3).sort((left, right) => left.distance - right.distance || left.range.start - right.range.start);
52748
52776
  return refs.slice(0, 8).map((item) => item.sourceRef);
52749
52777
  }
52778
+ function suggestedSplitDraftActions(diagnostics) {
52779
+ const selectedBlocks = [...diagnostics.selected_blocks ?? []].sort((left, right) => left.manifest_index - right.manifest_index || left.source_ref.localeCompare(right.source_ref));
52780
+ if (selectedBlocks.length === 0) {
52781
+ return diagnostics.source_refs.map((sourceRef) => ({ op: "add", source_refs: [sourceRef] }));
52782
+ }
52783
+ const runs = [];
52784
+ for (const block of selectedBlocks) {
52785
+ const current = runs.at(-1);
52786
+ const previous3 = current?.at(-1);
52787
+ if (current === undefined || previous3 === undefined || block.manifest_index !== previous3.manifest_index + 1) {
52788
+ runs.push([block]);
52789
+ } else {
52790
+ current.push(block);
52791
+ }
52792
+ }
52793
+ return runs.map((run2) => ({ op: "add", source_refs: run2.map((block) => block.source_ref) }));
52794
+ }
52750
52795
  function normalizedForRawCompare2(value) {
52751
52796
  return normalizeMarkdown(value).trim();
52752
52797
  }
@@ -52783,11 +52828,17 @@ async function assertResolvedSourceRefs(ctxDir, sourceRefs2, slug, context, labe
52783
52828
  }
52784
52829
  });
52785
52830
  }
52831
+ const suggestedSplitActions = suggestedSplitDraftActions(diagnostics);
52786
52832
  throw draftError(slug, context, `${label} could not be resolved to one contiguous source_ref; split the action or include every intervening citation-eligible source_ref`, {
52787
52833
  path: label,
52788
52834
  reasonCode: "source-refs-not-contiguous",
52789
52835
  currentValue: sourceRefs2,
52790
- diagnostics
52836
+ diagnostics: {
52837
+ ...diagnostics,
52838
+ repair_options: ["split_by_contiguous_runs", "include_missing_intervening_source_refs"],
52839
+ ...suggestedSplitActions.length > 1 ? { suggested_split_actions: suggestedSplitActions } : {},
52840
+ next_action: "Use suggested_split_actions as separate draft actions, or include every missing_intervening_blocks[].source_ref if one Section truly covers that intervening evidence."
52841
+ }
52791
52842
  });
52792
52843
  }
52793
52844
  }
@@ -57303,7 +57354,7 @@ async function pathExistsNoFollow(path3) {
57303
57354
  }
57304
57355
  function displayLanguage(language) {
57305
57356
  if (!language || language.trim().length === 0)
57306
- return "not set";
57357
+ return "English (default)";
57307
57358
  const normalized = language.trim().toLowerCase();
57308
57359
  if (["zh", "zh-cn", "chinese", "中文", "汉语"].includes(normalized))
57309
57360
  return "Chinese";
@@ -57316,56 +57367,47 @@ function renderAgentsGuide(input) {
57316
57367
  const projectRoot = location.layout === "embedded" ? ".." : ".";
57317
57368
  return `# C4A Workspace Instructions
57318
57369
 
57319
- This directory is the C4A data root for a knowledge workspace.
57320
-
57321
- - Workspace name: ${input.workspaceName}
57322
- - Workspace layout: ${location.layout}
57323
- - Project root relative to this file: \`${projectRoot}\`
57324
- - C4A data root relative to this file: \`.\`
57325
- - Default user-facing language: ${displayLanguage(input.language)}
57326
-
57327
- ## Agent Rules
57328
-
57329
- - Treat this data root as CLI-owned by default. Agents and users should not directly edit managed files unless a command or skill explicitly asks for a structured input file.
57330
- - Generated knowledge titles, summaries, and user-facing workflow reports follow this workspace language unless a CLI payload returns a more specific \`generation_policy\`. Keep \`node.summary\` concise: target under 15 tokens and never over 30 tokens. Source-bound Section content should stay close to the cited source language when it differs from the workspace language; add \`section.summary\` for long content as one plain paragraph. Preserve product names, code identifiers, command flags, slugs, \`block_id\` / \`source_ref\` tokens, and exact quoted evidence as printed when it is active user-facing knowledge.
57331
- - Keep Agent prompts and handoff payloads stable: fixed protocol/schema rules first, existing knowledge lookup second, source-shared context third, and the current task-specific candidate/Node/question last. Do not add current timestamps, random ids, scratch paths, host absolute paths, or reordered JSON fields to Agent-authored payloads or reports unless a \`context\` command explicitly returned them as semantic workspace facts.
57332
- - Existing \`knowledge/\` Nodes are the lookup registry. Use \`context mdrive glossary list|match\`, \`context mdrive node list\`, \`context mdrive query\`, or \`context query\` to reuse term, service, system, action, and domain handles. Do not create a separate dynamic registry file, and do not read \`knowledge/**\` directly just to decide whether a name already exists. When \`context mdrive glossary match <name>\` returns \`match.kind\`, \`match.matched\`, and \`match.rank\`, treat those as stable lookup hints; exact title/slug/alias hits should usually reuse the existing Node.
57333
- - Naming surfaces are separate: \`/context:*\` names user slash commands, \`context ...\` names CLI primitives, and \`context:skill-*\` names packaged skills/procedures invoked by slash workflows. Do not invent \`/context:compile-draft\`, \`/context:skill-*\`, or call a packaged skill a user command.
57334
- - Do not directly read, search, or list workspace files with generic file tools or shell commands such as Read, Glob, Grep, \`ls\`, \`find\`, \`tree\`, \`rg\`, \`cat\`, or \`sed\`. Use higher-level \`context\` commands instead — \`context status\`, \`context source list|get\`, \`context workflow show|status\`, \`context compile --context\`, \`context mdrive node|section|edge|query\`, and \`context query\` cover normal production reads. \`context workspace read/list/search\` is a developer/debug surface only; do not use it as a production agent next step, including ad-hoc browsing of \`raw/\`, \`knowledge/\`, \`archive/\`, \`decisions/\`, or cache internals.
57335
- - Do not directly write workspace files with generic file tools, shell redirection, patch tools, or scripts. Use higher-level \`context\` commands for managed data and workflow artifacts. Agent workflow handoffs should go through stdin and CLI-owned workflow payloads, not ad-hoc scratch files; \`output/\` root must not contain direct files.
57336
- - Do not run state-changing \`context\` commands in parallel against the same workspace. Commands such as \`context reconcile apply\`, \`context compile --close\`, \`context align --finalize\`, \`context capture\`, \`context drop\`, \`context purge\`, and \`context mdrive ... add|update|delete|supersede|deprecate\` must run sequentially so rollback, ledger, cache, and index updates stay coherent.
57337
- - Do not write or run ad-hoc Python, Node.js, shell, or other scripts to inspect, transform, filter, summarize, or bulk-read \`.context/**\`, this data root, \`output/**\`, \`decisions/**\`, raw snapshots, knowledge markdown, cache files, archives, or \`/tmp\` files produced by C4A commands. If a step needs structured data, use the relevant \`context ... --format json\` command or skill output and pass that output directly to the next \`context\` command or workflow.
57338
- - Do not pipe or wrap \`context ... --format json\` output through \`python3\`, \`node\`, \`jq\`, \`sed\`, \`cat\`, \`2>&1\`, \`|| echo\`, or similar shell helpers. Run the \`context\` command directly and consume its complete stdout.
57339
- - Workflow payload exploration follows one query protocol: \`--view\` selects the semantic view, \`--token-budget <n>\` controls the context window size, and \`--source\`, \`--heading\`, \`--node\`, \`--type\`, \`--status\`, \`--candidate-id\`, or \`--item-id\` only narrow the view. Start with purpose-built views such as \`context compile --context <slug> --view source-refs --token-budget 2000 --format json\`, \`context workflow show --payload node-context --view source-refs --token-budget 2000 --unwrap --format json\`, \`context workflow show --payload coverage-candidates --view coverage-summary --token-budget 2000 --unwrap --format json\`, \`context workflow show --payload coverage-candidates --view coverage --type <issue-type> --token-budget 2000 --unwrap --format json\`, and \`context workflow show --payload align-segments --view blocks --source <source-id> --token-budget 2000 --unwrap --format json\`.
57340
- - Budgeted views return \`token_budget\`, \`token_used\`, \`shown_count\`, \`total\`, \`omitted_count\`, \`truncated\`, \`selection_policy\`, \`item_id_field\`, and \`how_to_explore[]\`. If \`truncated: true\`, follow \`how_to_explore[]\` rather than slicing stdout yourself; prefer narrowing the view before increasing the budget. \`context_items[]\` are background-only entries, and \`context_omitted_count\` means additional background exists outside the current budget.
57341
- - Schema discovery must also go through direct \`context\` commands. Schema commands support \`--format text|json|yaml\`; use readable YAML/text while drafting and JSON when a caller needs machine parsing. Inspect the schema \`enums\` field for allowed tags/actions instead of inventing labels such as \`platform\` or \`tool\`.
57342
- - When passing large JSON / YAML / Markdown bodies to the CLI, feed stdin directly into the \`context\` command with a heredoc, for example \`context align --finalize - <<'JSON'\`. Do not pipe a heredoc through another command, and never redirect heredocs to workspace files.
57343
- - For large \`context align --finalize\` payloads, use \`block_ownership_defaults[]\` as a compact source-level ownership rule instead of generating temporary JSON files or enumerating hundreds of block rows. Defaults must still declare an explicit \`source_id\`, \`ownership_role\`, owner/visibility fields, and reason when needed; use \`block_ownership[]\` for block-level exceptions. Defaults are a compression mechanism, not permission to assign mixed-source evidence to one owner without review.
57344
- - In Claude Code, direct heredoc commands such as \`context compile --draft <slug> --input - --plan <<'JSON'\` may still prompt for permission when only subcommand-specific allow rules are configured. If that happens, ask the user before adding the scoped allow rule \`Bash(context:*)\` to the project \`.claude/settings.local.json\`; do not enable \`bypassPermissions\`.
57345
- - Never hand-edit \`raw/\`, \`raw/_sources.yaml\`, \`archive/\`, \`knowledge/\`, \`knowledge/_index.md\`, or \`knowledge/changelog.md\`. Maintain them through \`context\` CLI commands and \`/context:*\` workflows.
57346
- - \`decisions/\` stores CLI-managed semantic reconciliation decisions. Do not directly read, search, or write \`decisions/semantic.yaml\`; use \`context reconcile prepare|review|apply|record\`.
57347
- - \`archive/\` stores dropped-but-restorable source and knowledge artifacts. Do not edit it directly; use \`context drop\`, \`context capture\` restore flow, or \`context purge\`.
57348
- - Use \`context source list|get\` for source registry metadata; do not inspect \`raw/_sources.yaml\` directly.
57349
- - Use \`/context:capture\` or \`context capture\` for documents, and \`/context:capture --code\` / \`context capture --code\` for code snapshots. Do not write files directly under \`raw/\`. If code capture fails because a runner or plugin dependency is missing, surface the CLI's structured agent hint list exactly as returned; do not run hand-written \`node -e\`, \`require.resolve\`, or package-probing preflight snippets.
57350
- - Code snapshots are raw inputs until projected. After \`context capture --code\`, run \`context compile --code <source-slug>\` to materialize package/category/symbol knowledge Nodes, then run \`context compile --close\` when the CLI asks for close. Do not use prose \`/context:align\` or draft/reconcile compile to hand-build code knowledge.
57351
- - Code projection uses package/category/symbol layout: package roots such as \`pkg\` and category Nodes such as \`pkg/components\` are navigation surfaces, while concrete symbol Nodes such as \`pkg/symbol/button\` carry API Sections and code frontmatter. Use Node slugs, \`code_package\`, \`symbol_kind\`, and \`code_symbols[]\` as semantic handles; do not infer ownership, category, or identity from source file paths, directory names, line numbers, or \`module_path\`.
57352
- - Mixed code/doc knowledge is code-first. Materialize code symbol Nodes before aligning manuals, examples, notes, or on-call prose that mention APIs. When prose mentions an existing symbol through text, JSX tags, imports, or code spans, reuse the concrete symbol Node; if the same API name exists in multiple packages, ask for selection or leave it unresolved instead of creating a duplicate API Node.
57353
- - Use \`/context:align\` for structural planning. Read the current align workflow schemas, inspect \`align-segments\` through \`context workflow show --payload align-segments --view blocks --token-budget <n> --unwrap --format json\` plus semantic filters, build candidate ledger outputs, and finalize with \`context align --finalize -\`. Align workflow payload submissions must go through stdin; do not write scratch YAML/JSON files for coarse-read, candidate ops, or structure decision. Finalize consumes only the structure-decision payload and finalized block ownership; it does not read raw directly or accept old patch inputs.
57354
- - Use \`/context:compile\` for synthesis into \`knowledge/\`. Prepare NodeContext with \`context compile --context <slug> --format json\`, then inspect citation handles with \`context compile --context <slug> --view source-refs --token-budget 2000 --format json\` or \`context workflow show --payload node-context --view source-refs --token-budget 2000 --unwrap --format json\`; source-refs is a projection of \`NodeContext.raw_snippets[]\`, not a separate source. Use \`how_to_explore[]\` and semantic filters before expanding the durable NodeContext for the draft procedure. Pass drafts to \`context compile --draft <slug> --input - --plan --prepare\`; the CLI stores workflow payloads for review/apply. Use \`--save-input\` only when an explicit draft debug copy is needed. For precise manual knowledge edits, use \`context mdrive ...\`; do not bypass the CLI for article markdown, indexes, or changelog entries.
57355
- - \`/context:compile\` may process a workset, but each Node must complete its own context → draft → prepare → review → apply loop before the agent applies another Node. Do not run multiple Node reconcile/apply chains in parallel, hide several Node failures inside one batch command, or move to the next Node while the current Node has unresolved review questions, schema errors, unsupported evidence, or coverage warnings.
57356
- - Use \`context query\` for mixed code/doc consumption. \`context query --intent node_lookup --query "<name>"\` disambiguates same-name symbols with full slug and \`code_package\`; \`context query --intent node_view --scope <slug>\` opens a known Node; \`context query --intent section_search --scope <slug> --query "<terms>" --evidence code|prose|all\` filters code-owned versus doc-owned Sections. Query metadata such as \`source_id\`, \`source_type\`, \`evidence_kind\`, \`source_ref\`, \`version_applicability\`, and \`code_projection_status\` is authoritative; do not guess evidence kind from \`code_package\` or inspect rendered markdown to recover provenance.
57357
- - Dropping or refreshing code sources is ownership-aware. Use \`context drop <source-id> --plan\` / \`context drop --apply-plan\` and \`context capture --code\` refresh rather than deleting files. Code-owned Sections, code-owned graph edges, and external deps can be removed while doc-owned Sections on the same symbol Node are preserved with \`code_projection_status=dropped\` or \`detached\`; use the query/status repair hints to reattach.
57358
- - Treat compile \`source_support\` as a lexical diagnostic, not a target to game. Do not copy raw text merely to raise matched-term counts or show evidence; \`source_ref\` already provides traceability. Keep Section \`content\` as the user-facing wording, preserving cited raw prose when it is already clear and only cleaning formatting, typos, casing, entity names, aliases, or grammar without changing meaning.
57359
- - When \`context compile --draft <slug> --plan\` or \`context reconcile prepare --mode compile\` returns \`compact-source-low-coverage\` or \`dense-source-low-coverage\`, treat it as a required coverage progress warning. Check \`coverage is <covered>/<total>\` and \`remaining <N> snippet(s)\`, then return to the same draft and add actions for uncovered, source-backed, orthogonal facts before review/apply. If \`context verify\` or \`context doctor\` reports \`compact-source-evidence-undercovered\` or \`dense-source-evidence-undercovered\`, review the reported uncovered raw blocks before declaring the source complete. CLI success for one action only proves that action is supported; it does not prove the Node is complete.
57360
- - Users may intentionally edit \`config.yaml\` and \`aspects/*/prompt.md\`; after doing so, run \`context doctor\` or \`context status\`.
57361
- - Use \`context doctor\`, \`context verify\`, and \`context status\` to inspect workspace health instead of inferring state from partial files.
57362
- - \`context status\` is local-only and must be run from the workspace root. When an agent starts in a child directory, run \`context status\` from the project root or have the user reposition the shell; \`context workspace locate\` is a developer/debug fallback, not a normal production handoff.
57363
- - Aspect prompts live under \`aspects/\` and are long-lived workspace files.
57364
-
57365
- ## Layout Notes
57366
-
57367
- - \`embedded\` layout keeps this data root under the project \`.context/\` directory and is intended for normal code repositories.
57368
- - \`root\` layout uses the current directory as the C4A data root. It is intended only for dedicated knowledge repositories; keep product source code outside this root unless you intentionally want C4A commands to treat it as part of the knowledge workspace.
57370
+ This directory is the C4A data root.
57371
+
57372
+ - Workspace: ${input.workspaceName}
57373
+ - Layout: ${location.layout}
57374
+ - Project root: \`${projectRoot}\` (relative to this file)
57375
+ - Language: ${displayLanguage(input.language)} (used for generated titles, summaries, and workflow reports)
57376
+
57377
+ ## TL;DR — Non-negotiables
57378
+
57379
+ - **The CLI owns this data root.** All reads and writes go through \`context ...\` commands. Never use generic tools (\`Read\`, \`Glob\`, \`Grep\`, \`ls\`, \`find\`, \`cat\`, \`rg\`, \`head\`, \`tail\`, \`jq\`, \`sed\`, \`python3\`, \`node\`, shell pipes) against \`.context/**\`, \`output/**\`, \`archive/**\`, \`decisions/**\`, or \`/tmp\` files produced by C4A.
57380
+ - **Consume \`context ... --format json\` stdout directly.** Don't pipe it through \`jq\`, \`sed\`, \`cat\`, \`2>&1\`, or any wrapper.
57381
+ - **State-changing commands run sequentially.** Never run \`reconcile apply\`, \`compile --close\`, \`align --finalize\`, \`capture\`, \`drop\`, \`purge\`, or \`mdrive ... add|update|delete|supersede|deprecate\` in parallel against this workspace.
57382
+ - **Names are distinct.** \`/context:*\` = user slash command, \`context ...\` = CLI primitive, \`context:skill-*\` = packaged skill. Don't invent \`/context:skill-*\` or \`/context:compile-draft\`.
57383
+
57384
+ ## Managed directories
57385
+
57386
+ These paths are CLI-owned. Hand-editing breaks the ledger, index, or cache.
57387
+
57388
+ | Path | Use this instead |
57389
+ |---|---|
57390
+ | \`raw/\`, \`raw/_sources.yaml\` | \`context capture\`, \`context capture --code\`, \`context source list|get\` |
57391
+ | \`knowledge/\`, \`knowledge/_index.md\`, \`knowledge/changelog.md\` | \`/context:compile\`, \`context mdrive ...\` |
57392
+ | \`decisions/semantic.yaml\` | \`context reconcile prepare|review|apply\` |
57393
+ | \`archive/\` | \`context drop\`, \`context purge\`, \`context capture\` restore |
57394
+ | \`config.yaml\`, \`aspects/*/prompt.md\` | hand-editing OK; run \`context doctor\` or \`context status\` afterward |
57395
+
57396
+ Workflow input goes through stdin (e.g. \`context align --finalize - <<'JSON'\`). Do not write scratch files under \`output/\` root or pipe heredocs through other commands.
57397
+
57398
+ ## Discovery trust the CLI, not this file
57399
+
57400
+ This file describes the workspace, not the workflows. For align / compile / drop procedures, follow the relevant \`/context:*\` slash command or \`context:skill-*\` skill. Command flags, view names, and schemas may evolve; ask the CLI:
57401
+
57402
+ - \`context status\` current workspace state (run from project root)
57403
+ - \`context doctor\`, \`context verify\` health checks
57404
+ - \`context schema <name>\` input schemas with allowed enums and examples
57405
+ - \`context <cmd> --help\` command flags and view options
57406
+
57407
+ ## Layout
57408
+
57409
+ - **embedded** data root lives at \`.context/\` under the project root. Normal code repositories.
57410
+ - **root** data root is the current directory itself. Use only for dedicated knowledge repositories; keep product source code outside this root.
57369
57411
  `;
57370
57412
  }
57371
57413
  async function writeAgentsGuide(input) {
@@ -57382,8 +57424,19 @@ async function writeAgentsGuide(input) {
57382
57424
  }
57383
57425
  let claudeLinkCreated = false;
57384
57426
  if (!await pathExistsNoFollow(claudePath)) {
57385
- await symlink("AGENTS.md", claudePath);
57386
- claudeLinkCreated = true;
57427
+ try {
57428
+ await symlink("AGENTS.md", claudePath);
57429
+ claudeLinkCreated = true;
57430
+ } catch (err2) {
57431
+ const code3 = err2.code;
57432
+ if (code3 === "EPERM" || code3 === "EINVAL" || code3 === "ENOSYS") {
57433
+ await writeFile4(claudePath, `# See [AGENTS.md](./AGENTS.md)
57434
+ `, "utf8");
57435
+ claudeLinkCreated = true;
57436
+ } else {
57437
+ throw err2;
57438
+ }
57439
+ }
57387
57440
  }
57388
57441
  return { agentsPath, agentsWritten, claudePath, claudeLinkCreated };
57389
57442
  }
@@ -66579,13 +66632,87 @@ function removeDroppedContextSources2(input) {
66579
66632
  function shouldArchiveNode2(node3) {
66580
66633
  return node3.sections.length === 0 && node3.children.length === 0 && node3.containsList.length === 0 && node3.body.trim().length === 0;
66581
66634
  }
66635
+ function shouldArchiveDroppedCodeNavigationNode(node3, sourceSlug3) {
66636
+ return node3.node.code_package === sourceSlug3 && (node3.node.code_symbols?.length ?? 0) === 0 && node3.sections.length === 0 && node3.children.length === 0 && node3.body.trim().length === 0;
66637
+ }
66638
+ function archiveLocatedNode(input) {
66639
+ if (input.archivedSlugs.has(input.located.parsed.node.id))
66640
+ return;
66641
+ if (input.located.path.length === 0) {
66642
+ input.deletedFiles.add(input.located.filePath);
66643
+ } else {
66644
+ detachNodeAtPath(input.located.root, input.located.path);
66645
+ input.touchedFiles.add(input.located.filePath);
66646
+ }
66647
+ input.archivedSlugs.add(input.located.parsed.node.id);
66648
+ }
66649
+ function archiveEmptyAffectedNodes(input) {
66650
+ let changed = false;
66651
+ for (const located of input.nodes) {
66652
+ if (!input.affectedSlugs.has(located.parsed.node.id))
66653
+ continue;
66654
+ if (input.archivedSlugs.has(located.parsed.node.id))
66655
+ continue;
66656
+ if (!shouldArchiveNode2(located.parsed))
66657
+ continue;
66658
+ archiveLocatedNode({
66659
+ located,
66660
+ deletedFiles: input.deletedFiles,
66661
+ touchedFiles: input.touchedFiles,
66662
+ archivedSlugs: input.archivedSlugs
66663
+ });
66664
+ changed = true;
66665
+ }
66666
+ return changed;
66667
+ }
66668
+ function archiveDroppedCodeNavigationNodes(input) {
66669
+ let changed = false;
66670
+ for (const located of input.nodes) {
66671
+ if (input.archivedSlugs.has(located.parsed.node.id))
66672
+ continue;
66673
+ if (!shouldArchiveDroppedCodeNavigationNode(located.parsed, input.sourceSlug))
66674
+ continue;
66675
+ archiveLocatedNode({
66676
+ located,
66677
+ deletedFiles: input.deletedFiles,
66678
+ touchedFiles: input.touchedFiles,
66679
+ archivedSlugs: input.archivedSlugs
66680
+ });
66681
+ changed = true;
66682
+ }
66683
+ return changed;
66684
+ }
66685
+ function containsEntryTargetSlug(parentSlug, entry) {
66686
+ const href = entry.href.split(/[?#]/u)[0]?.replace(/^\.\//u, "").replace(/\.md$/u, "") ?? "";
66687
+ if (href.length === 0 || /^[a-z][a-z0-9+.-]*:/iu.test(href))
66688
+ return entry.slug.length > 0 ? entry.slug : null;
66689
+ const parentParts = parentSlug.split("/").filter((part) => part.length > 0);
66690
+ const rootSlug = parentParts[0] ?? "";
66691
+ if (href === rootSlug || href.startsWith(`${rootSlug}/`))
66692
+ return href;
66693
+ if (rootSlug.length > 0 && href.startsWith("symbol/"))
66694
+ return `${rootSlug}/${href}`;
66695
+ if (parentParts.length > 1)
66696
+ return `${parentParts.slice(0, -1).join("/")}/${href}`;
66697
+ return rootSlug.length > 0 ? `${rootSlug}/${href}` : href;
66698
+ }
66699
+ function containsEntryMatchesArchivedSlug(parentSlug, entry, archivedSlug) {
66700
+ if (entry.slug === archivedSlug)
66701
+ return true;
66702
+ return containsEntryTargetSlug(parentSlug, entry) === archivedSlug;
66703
+ }
66582
66704
  function removeReferencesToArchivedNodes2(input) {
66583
66705
  if (input.archivedSlugs.size === 0)
66584
66706
  return;
66585
66707
  const visit2 = (filePath, node3) => {
66586
66708
  const beforeContains = node3.containsList.length;
66587
- for (const slug of input.archivedSlugs)
66588
- node3.containsList = removeContainsEntry(node3.containsList, slug);
66709
+ node3.containsList = node3.containsList.filter((entry) => {
66710
+ for (const slug of input.archivedSlugs) {
66711
+ if (containsEntryMatchesArchivedSlug(node3.node.id, entry, slug))
66712
+ return false;
66713
+ }
66714
+ return true;
66715
+ });
66589
66716
  if (node3.containsList.length !== beforeContains)
66590
66717
  input.touchedFiles.add(filePath);
66591
66718
  for (const section of node3.sections) {
@@ -66717,6 +66844,8 @@ async function applyCodeSourceDropPlan(input) {
66717
66844
  const touchedFiles = new Set;
66718
66845
  const deletedFiles = new Set;
66719
66846
  const archivedSlugs = new Set;
66847
+ const affectedSlugs = new Set(plan.affected_nodes.map((node3) => node3.slug));
66848
+ const sourceSlug3 = codeSourceSlug(source2);
66720
66849
  let removedSections = 0;
66721
66850
  let reindexedSections = 0;
66722
66851
  for (const planNode of plan.affected_nodes) {
@@ -66727,17 +66856,32 @@ async function applyCodeSourceDropPlan(input) {
66727
66856
  removedSections += result.removedSections;
66728
66857
  reindexedSections += result.reindexedSections;
66729
66858
  if (result.archiveNode && located.path.length === 0) {
66730
- deletedFiles.add(located.filePath);
66731
- archivedSlugs.add(located.parsed.node.id);
66859
+ archiveLocatedNode({ located, deletedFiles, touchedFiles, archivedSlugs });
66732
66860
  } else if (result.archiveNode) {
66733
- detachNodeAtPath(located.root, located.path);
66734
- touchedFiles.add(located.filePath);
66735
- archivedSlugs.add(located.parsed.node.id);
66861
+ archiveLocatedNode({ located, deletedFiles, touchedFiles, archivedSlugs });
66736
66862
  } else if (result.removedSections > 0 || result.reindexedSections > 0) {
66737
66863
  touchedFiles.add(located.filePath);
66738
66864
  }
66739
66865
  }
66740
- removeReferencesToArchivedNodes2({ files, archivedSlugs, touchedFiles });
66866
+ while (true) {
66867
+ removeReferencesToArchivedNodes2({ files, archivedSlugs, touchedFiles });
66868
+ const changed = archiveEmptyAffectedNodes({
66869
+ nodes,
66870
+ affectedSlugs,
66871
+ deletedFiles,
66872
+ touchedFiles,
66873
+ archivedSlugs
66874
+ });
66875
+ const changedNavigation = archiveDroppedCodeNavigationNodes({
66876
+ nodes,
66877
+ sourceSlug: sourceSlug3,
66878
+ deletedFiles,
66879
+ touchedFiles,
66880
+ archivedSlugs
66881
+ });
66882
+ if (!changed && !changedNavigation)
66883
+ break;
66884
+ }
66741
66885
  removeDroppedContextSources2({ files, sourceId: source2.id, deletedFiles, touchedFiles });
66742
66886
  const owners = new Set;
66743
66887
  for (const node3 of plan.affected_nodes)
@@ -66758,7 +66902,7 @@ async function applyCodeSourceDropPlan(input) {
66758
66902
  touchedKnowledgeFiles,
66759
66903
  plan
66760
66904
  });
66761
- await replaceCodeGraphEdges(input.ctxDir, `code:${codeSourceSlug(source2)}`, []);
66905
+ await replaceCodeGraphEdges(input.ctxDir, `code:${sourceSlug3}`, []);
66762
66906
  await replaceExternalDepsForOwners(input.ctxDir, [], owners);
66763
66907
  const renderContext = touchedFiles.size > 0 ? await buildRenderWorkspaceContext(input.ctxDir, files.filter((file) => !deletedFiles.has(file.filePath))) : undefined;
66764
66908
  for (const filePath of touchedFiles) {
@@ -77458,7 +77602,7 @@ function nodeContextSourceRefRows(value) {
77458
77602
  citation_eligible: snippet.citation_eligible === true,
77459
77603
  context_only: snippet.context_only === true,
77460
77604
  ...quotePreview !== undefined ? { quote_preview: quotePreview } : {},
77461
- ...node3 !== undefined && blockId !== undefined && canRequestFullTextForSnippet(snippet) ? { request_full_text_command: `context compile --context ${shellQuote(node3)} --request-full-text ${shellQuote(blockId)} --format json` } : {}
77605
+ ...node3 !== undefined && blockId !== undefined && canRequestFullTextForSnippet(snippet) ? { request_full_text_command: `context compile --context ${shellQuote(node3)} --request-full-text ${shellQuote(blockId)} --view text --format json` } : {}
77462
77606
  };
77463
77607
  });
77464
77608
  }
@@ -84551,10 +84695,115 @@ function plannedSectionSourceRefGroups(context, rows) {
84551
84695
  source_refs: run2.map((row) => row.source_ref)
84552
84696
  })),
84553
84697
  draft_action_templates: templates,
84554
- draft_action_template: templates[0] ?? draftActionTemplate(plan.section_kind, [])
84698
+ ...status === "ready_with_splits" ? {} : { draft_action_template: templates[0] ?? draftActionTemplate(plan.section_kind, []) }
84699
+ };
84700
+ });
84701
+ }
84702
+
84703
+ // src/commands/compileSourceRefsCodeText.ts
84704
+ function recordArray(value) {
84705
+ return Array.isArray(value) ? value.filter((item) => typeof item === "object" && item !== null && !Array.isArray(item)) : [];
84706
+ }
84707
+ function exampleSourceRefs(sectionGroups) {
84708
+ const refs = new Set;
84709
+ for (const group of sectionGroups) {
84710
+ if (group.section_kind !== "example")
84711
+ continue;
84712
+ for (const sourceRef of Array.isArray(group.source_refs) ? group.source_refs : []) {
84713
+ if (typeof sourceRef === "string")
84714
+ refs.add(sourceRef);
84715
+ }
84716
+ }
84717
+ return refs;
84718
+ }
84719
+ function fencedCodeTextBySourceRef(context) {
84720
+ const out2 = new Map;
84721
+ for (const snippet of context.raw_snippets) {
84722
+ if (typeof snippet.source_ref !== "string" || typeof snippet.quote !== "string")
84723
+ continue;
84724
+ if (!/```[\s\S]*```/u.test(snippet.quote))
84725
+ continue;
84726
+ out2.set(snippet.source_ref, snippet.quote);
84727
+ }
84728
+ return out2;
84729
+ }
84730
+ function enrichRows(rows, exampleRefs, fullTextBySourceRef) {
84731
+ return recordArray(rows).map((row) => {
84732
+ const sourceRef = typeof row.source_ref === "string" ? row.source_ref : undefined;
84733
+ const fullText = sourceRef !== undefined && exampleRefs.has(sourceRef) ? fullTextBySourceRef.get(sourceRef) : undefined;
84734
+ if (fullText === undefined)
84735
+ return row;
84736
+ return {
84737
+ ...row,
84738
+ quote_full_text: fullText,
84739
+ quote_full_text_reason: "example-code-block",
84740
+ quote_full_text_field: "items[].quote_full_text"
84555
84741
  };
84556
84742
  });
84557
84743
  }
84744
+ function withExampleCodeFullText(input) {
84745
+ const exampleRefs = exampleSourceRefs(input.sectionGroups);
84746
+ if (exampleRefs.size === 0)
84747
+ return input.projected;
84748
+ const fullTextBySourceRef = fencedCodeTextBySourceRef(input.context);
84749
+ if (fullTextBySourceRef.size === 0)
84750
+ return input.projected;
84751
+ return {
84752
+ ...input.projected,
84753
+ items: enrichRows(input.projected.items, exampleRefs, fullTextBySourceRef),
84754
+ context_items: enrichRows(input.projected.context_items, exampleRefs, fullTextBySourceRef)
84755
+ };
84756
+ }
84757
+
84758
+ // src/commands/compileSourceRefsScaffold.ts
84759
+ init_compile();
84760
+ function recordArray2(value) {
84761
+ return Array.isArray(value) ? value.filter((item) => typeof item === "object" && item !== null && !Array.isArray(item)) : [];
84762
+ }
84763
+ function stringArray7(value) {
84764
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
84765
+ }
84766
+ function defaultScaffoldSectionKind(context) {
84767
+ return context.node.planned_sections?.[0] ?? "description";
84768
+ }
84769
+ function plannedTemplateActions(sectionGroups) {
84770
+ return sectionGroups.filter((group) => group.status === "ready" || group.status === "ready_with_splits").flatMap((group) => recordArray2(group.draft_action_templates));
84771
+ }
84772
+ function sourceRefsDraftScaffold(input) {
84773
+ const requested = new Set(input.requestedBlockIds ?? []);
84774
+ const plannedActions = requested.size === 0 ? plannedTemplateActions(input.sectionGroups) : [];
84775
+ if (plannedActions.length > 0) {
84776
+ return {
84777
+ schema_version: COMPILE_DRAFT_SCHEMA_VERSION,
84778
+ target_node: input.context.node.slug,
84779
+ selection: {
84780
+ mode: "planned_section_groups",
84781
+ selected_action_count: plannedActions.length,
84782
+ planned_section_groups: input.sectionGroups.filter((group) => group.status === "ready" || group.status === "ready_with_splits").map((group) => group.section_id).filter((sectionId) => typeof sectionId === "string")
84783
+ },
84784
+ usage: "Fill each action.content, adjust kind when needed, then submit the actions with context compile --draft <node> --input - --plan.",
84785
+ actions: plannedActions.map((action) => ({ ...action }))
84786
+ };
84787
+ }
84788
+ const selectedRows = input.rows.filter((row) => row.citation_eligible === true && typeof row.source_ref === "string" && (requested.size === 0 || typeof row.block_id === "string" && requested.has(row.block_id)));
84789
+ const kind = defaultScaffoldSectionKind(input.context);
84790
+ return {
84791
+ schema_version: COMPILE_DRAFT_SCHEMA_VERSION,
84792
+ target_node: input.context.node.slug,
84793
+ selection: {
84794
+ mode: requested.size > 0 ? "requested_block_ids" : "visible_citation_eligible_source_refs",
84795
+ selected_action_count: selectedRows.length,
84796
+ ...requested.size > 0 ? { requested_block_ids: [...requested] } : {}
84797
+ },
84798
+ usage: "Fill each action.content, adjust kind when needed, then submit the actions with context compile --draft <node> --input - --plan.",
84799
+ actions: selectedRows.map((row) => ({
84800
+ op: "add",
84801
+ kind,
84802
+ content: "",
84803
+ source_refs: stringArray7([row.source_ref])
84804
+ }))
84805
+ };
84806
+ }
84558
84807
 
84559
84808
  // src/commands/compileWorkflowViews.ts
84560
84809
  function compileChangesFormat(value) {
@@ -84576,12 +84825,17 @@ function compileOutputView(value) {
84576
84825
  return "source-refs";
84577
84826
  if (value === "issues")
84578
84827
  return "issues";
84579
- throw new ContextError(ExitCode.UserError, "--view must be full, summary, source-refs, or issues", {
84828
+ if (value === "text")
84829
+ return "text";
84830
+ throw new ContextError(ExitCode.UserError, "--view must be full, summary, source-refs, issues, or text", {
84580
84831
  category: ErrorCategory.UserInputInvalid,
84581
84832
  flag: "--view"
84582
84833
  });
84583
84834
  }
84584
84835
  function compileChangesNextHint(changes) {
84836
+ if (changes.first_compile_pending !== undefined && changes.first_compile_pending.remaining > 0) {
84837
+ return `${changes.first_compile_pending.remaining} first-compile node(s) remain; run first_compile_pending.commands[] before context compile --close`;
84838
+ }
84585
84839
  if (changes.reason === "no-changed-nodes") {
84586
84840
  if ((changes.stats.non_blocking_blocks_changed ?? 0) > 0) {
84587
84841
  return "only non-blocking context/source changes were detected; compile draft can stop";
@@ -84610,6 +84864,11 @@ function compileChangesNextHint(changes) {
84610
84864
  }
84611
84865
  function compileChangesNext(changes) {
84612
84866
  const reason = compileChangesNextHint(changes);
84867
+ if (changes.first_compile_pending !== undefined && changes.first_compile_pending.commands.length > 0) {
84868
+ const command = changes.first_compile_pending.commands[0];
84869
+ if (command !== undefined)
84870
+ return { command, reason };
84871
+ }
84613
84872
  if (changes.reason === "no-changed-nodes")
84614
84873
  return { reason };
84615
84874
  if (changes.status === "unknown-input") {
@@ -84659,6 +84918,7 @@ function compactCompileChanges(changes) {
84659
84918
  unknown_input_count: node3.unknown_inputs.length,
84660
84919
  source_ids: [...new Set(node3.changed_blocks.map((block) => block.source_id))]
84661
84920
  })),
84921
+ ...changes.first_compile_pending !== undefined ? { first_compile_pending: changes.first_compile_pending } : {},
84662
84922
  ignored_locator_change_count: changes.ignored_locator_changes?.length ?? 0,
84663
84923
  unknown_inputs: changes.unknown_inputs
84664
84924
  };
@@ -84826,14 +85086,14 @@ function writeCompileContextSummary(input) {
84826
85086
  }
84827
85087
  writeCompileContextTextSummary(input, coverageRepairRound, coverageScopeHint);
84828
85088
  }
84829
- function recordArray(value) {
85089
+ function recordArray3(value) {
84830
85090
  return Array.isArray(value) ? value.filter((item) => typeof item === "object" && item !== null && !Array.isArray(item)) : [];
84831
85091
  }
84832
85092
  function objectRecord(value) {
84833
85093
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
84834
85094
  }
84835
85095
  function projectedSourceRefRows(value) {
84836
- return recordArray(value.items).concat(recordArray(value.context_items)).filter((row) => typeof row.source_ref === "string" && typeof row.block_id === "string" && typeof row.citation_eligible === "boolean");
85096
+ return recordArray3(value.items).concat(recordArray3(value.context_items)).filter((row) => typeof row.source_ref === "string" && typeof row.block_id === "string" && typeof row.citation_eligible === "boolean");
84837
85097
  }
84838
85098
  function compileSourceRefsBaseCommand(slug) {
84839
85099
  return `context compile --context ${shellQuote(slug)} --view source-refs --format json`;
@@ -84849,30 +85109,6 @@ function sourceRefsDraftScaffoldCommand(slug, options = {}) {
84849
85109
  "--format json"
84850
85110
  ].join(" ");
84851
85111
  }
84852
- function defaultScaffoldSectionKind(context) {
84853
- return context.node.planned_sections?.[0] ?? "description";
84854
- }
84855
- function sourceRefsDraftScaffold(input) {
84856
- const requested = new Set(input.requestedBlockIds ?? []);
84857
- const selectedRows = input.rows.filter((row) => row.citation_eligible === true && typeof row.source_ref === "string" && (requested.size === 0 || typeof row.block_id === "string" && requested.has(row.block_id)));
84858
- const kind = defaultScaffoldSectionKind(input.context);
84859
- return {
84860
- schema_version: COMPILE_DRAFT_SCHEMA_VERSION,
84861
- target_node: input.context.node.slug,
84862
- selection: {
84863
- mode: requested.size > 0 ? "requested_block_ids" : "visible_citation_eligible_source_refs",
84864
- selected_action_count: selectedRows.length,
84865
- ...requested.size > 0 ? { requested_block_ids: [...requested] } : {}
84866
- },
84867
- usage: "Fill each action.content, adjust kind when needed, then submit the actions with context compile --draft <node> --input - --plan.",
84868
- actions: selectedRows.map((row) => ({
84869
- op: "add",
84870
- kind,
84871
- content: "",
84872
- source_refs: [row.source_ref]
84873
- }))
84874
- };
84875
- }
84876
85112
  function sourceRefsSectionGroupHint(sectionGroups) {
84877
85113
  if (sectionGroups.length === 0)
84878
85114
  return [];
@@ -84918,16 +85154,18 @@ function sourceRefsViewHints(input) {
84918
85154
  }
84919
85155
  function writeCompileSourceRefs(context, format, options = {}) {
84920
85156
  const projected = projectNodeContextSourceRefs(context, options, compileSourceRefsBaseCommand(context.node.slug));
84921
- const projectedHints = recordArray(projected.agent_hints);
85157
+ const projectedHints = recordArray3(projected.agent_hints);
84922
85158
  const projectedCounts = objectRecord(projected.counts);
84923
- const windowRows = recordArray(projected.items).concat(recordArray(projected.context_items));
85159
+ const windowRows = recordArray3(projected.items).concat(recordArray3(projected.context_items));
84924
85160
  const sectionGroups = plannedSectionSourceRefGroups(context, projectedSourceRefRows(projected));
85161
+ const projectedForOutput = withExampleCodeFullText({ projected, context, sectionGroups });
84925
85162
  const draftScaffoldCommand = sourceRefsDraftScaffoldCommand(context.node.slug, options);
84926
85163
  const requestedScaffoldBlockIds = options.draftScaffoldBlockIds ?? [];
84927
- const scaffoldRows = requestedScaffoldBlockIds.length > 0 ? recordArray(context.raw_snippets) : windowRows;
85164
+ const scaffoldRows = requestedScaffoldBlockIds.length > 0 ? recordArray3(context.raw_snippets) : windowRows;
84928
85165
  const draftScaffold = options.draftScaffold === true ? sourceRefsDraftScaffold({
84929
85166
  context,
84930
85167
  rows: scaffoldRows,
85168
+ sectionGroups,
84931
85169
  ...requestedScaffoldBlockIds.length > 0 ? { requestedBlockIds: requestedScaffoldBlockIds } : {}
84932
85170
  }) : undefined;
84933
85171
  const scaffoldHint = {
@@ -84940,7 +85178,7 @@ function writeCompileSourceRefs(context, format, options = {}) {
84940
85178
  const viewHints = sourceRefsViewHints({ projectedHints, sectionGroups, scaffoldHint });
84941
85179
  if (format === "json") {
84942
85180
  writeJson3({
84943
- ...projected,
85181
+ ...projectedForOutput,
84944
85182
  ...sectionGroups.length > 0 ? { planned_section_groups: sectionGroups } : {},
84945
85183
  draft_scaffold_command: draftScaffoldCommand,
84946
85184
  ...draftScaffold !== undefined ? { draft_scaffold: draftScaffold } : {},
@@ -84949,7 +85187,7 @@ function writeCompileSourceRefs(context, format, options = {}) {
84949
85187
  planned_section_groups: sectionGroups.length,
84950
85188
  planned_section_groups_ready: sectionGroups.filter((group) => group.status === "ready" || group.status === "ready_with_splits").length,
84951
85189
  planned_section_groups_split: sectionGroups.filter((group) => group.status === "ready_with_splits").length,
84952
- ...draftScaffold !== undefined ? { draft_scaffold_actions: recordArray(draftScaffold.actions).length } : {}
85190
+ ...draftScaffold !== undefined ? { draft_scaffold_actions: recordArray3(draftScaffold.actions).length } : {}
84953
85191
  },
84954
85192
  agent_hints: viewHints
84955
85193
  });
@@ -85041,7 +85279,7 @@ function writeCompileDraftStatus(input) {
85041
85279
  function claimIdForActionIndex(index2) {
85042
85280
  return `claim-${String(index2 + 1).padStart(3, "0")}`;
85043
85281
  }
85044
- function stringArray7(value) {
85282
+ function stringArray8(value) {
85045
85283
  return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
85046
85284
  }
85047
85285
  function sourceRefNarrowingByActionIndex(hints) {
@@ -85050,9 +85288,9 @@ function sourceRefNarrowingByActionIndex(hints) {
85050
85288
  if (hint.code !== "compile-source-refs-narrowing-suggested" || typeof hint.op_index !== "number")
85051
85289
  continue;
85052
85290
  const diagnostics = hint.diagnostics ?? {};
85053
- const original = stringArray7(diagnostics.original_source_refs);
85054
- const narrowed = stringArray7(diagnostics.narrowed_source_refs);
85055
- const removed = stringArray7(diagnostics.removed_source_refs);
85291
+ const original = stringArray8(diagnostics.original_source_refs);
85292
+ const narrowed = stringArray8(diagnostics.narrowed_source_refs);
85293
+ const removed = stringArray8(diagnostics.removed_source_refs);
85056
85294
  if (original.length === 0 || narrowed.length === 0 || removed.length === 0)
85057
85295
  continue;
85058
85296
  byIndex.set(hint.op_index, {
@@ -85070,8 +85308,8 @@ function sourceRefNarrowingByActionIndex(hints) {
85070
85308
  function sourceSupportDiagnostics(support) {
85071
85309
  if (support?.verdict === undefined || support.verdict === "supported")
85072
85310
  return;
85073
- const missingHardTerms = stringArray7(support.missing_hard_terms);
85074
- const missingContentTerms = stringArray7(support.missing_content_terms);
85311
+ const missingHardTerms = stringArray8(support.missing_hard_terms);
85312
+ const missingContentTerms = stringArray8(support.missing_content_terms);
85075
85313
  const contextOnlyMatches = Array.isArray(support.context_only_hard_term_matches) ? support.context_only_hard_term_matches.slice(0, 12).map((match) => ({
85076
85314
  ...typeof match.term === "string" ? { term: match.term } : {},
85077
85315
  ...typeof match.line_range === "string" ? { line_range: match.line_range } : {},
@@ -85163,11 +85401,11 @@ function withPayloadMeta2(value, payload) {
85163
85401
  })
85164
85402
  };
85165
85403
  }
85166
- function stringArray8(value) {
85404
+ function stringArray9(value) {
85167
85405
  return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
85168
85406
  }
85169
85407
  function narrowingSuggestionRemovedCount(context) {
85170
- return (context.agent_hints ?? []).filter((hint) => hint.code === "compile-source-refs-narrowing-suggested").reduce((sum, hint) => sum + stringArray8(hint.diagnostics?.removed_source_refs).length, 0);
85408
+ return (context.agent_hints ?? []).filter((hint) => hint.code === "compile-source-refs-narrowing-suggested").reduce((sum, hint) => sum + stringArray9(hint.diagnostics?.removed_source_refs).length, 0);
85171
85409
  }
85172
85410
  function prepareViewFromCompileView(view, viewWasExplicit) {
85173
85411
  if (view === "source-refs") {
@@ -85342,7 +85580,7 @@ async function writeCompileDraftChallengePayloads2(input) {
85342
85580
  });
85343
85581
  }
85344
85582
  }
85345
- function stringArray9(value) {
85583
+ function stringArray10(value) {
85346
85584
  return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
85347
85585
  }
85348
85586
  function preparedSourceSupportStatus(value) {
@@ -85350,8 +85588,8 @@ function preparedSourceSupportStatus(value) {
85350
85588
  return;
85351
85589
  if (value.verdict === "supported")
85352
85590
  return { verdict: value.verdict };
85353
- const missingHardTerms = stringArray9(value.missing_hard_terms);
85354
- const missingContentTerms = stringArray9(value.missing_content_terms);
85591
+ const missingHardTerms = stringArray10(value.missing_hard_terms);
85592
+ const missingContentTerms = stringArray10(value.missing_content_terms);
85355
85593
  return {
85356
85594
  verdict: value.verdict,
85357
85595
  ...missingHardTerms.length > 0 ? { missing_hard_terms: missingHardTerms } : {},
@@ -85666,6 +85904,78 @@ async function runCompileDraftCommand(input) {
85666
85904
  return false;
85667
85905
  }
85668
85906
 
85907
+ // src/commands/compileRequestFullTextView.ts
85908
+ function textViewNextCommand(command) {
85909
+ if (command === undefined)
85910
+ return;
85911
+ if (command.includes("--view text"))
85912
+ return command;
85913
+ if (command.includes("--format json"))
85914
+ return command.replace("--format json", "--view text --format json");
85915
+ return `${command} --view text`;
85916
+ }
85917
+ function pageItem(context, page) {
85918
+ const snippet = context.raw_snippets[page.raw_snippet_index];
85919
+ const nextCommand2 = textViewNextCommand(page.next_command);
85920
+ return {
85921
+ block_id: snippet?.block_id ?? context.request_full_text?.requested_block_ids[0] ?? "<unknown>",
85922
+ raw_snippet_index: page.raw_snippet_index,
85923
+ ...page.source_ref !== undefined || snippet?.source_ref !== undefined ? { source_ref: page.source_ref ?? snippet?.source_ref } : {},
85924
+ line_range: page.line_range,
85925
+ line_start: page.line_start,
85926
+ line_end: page.line_end,
85927
+ text: page.text,
85928
+ text_field: "items[].text",
85929
+ ...page.next_range !== undefined ? { next_range: page.next_range } : {},
85930
+ ...nextCommand2 !== undefined ? { next_command: nextCommand2 } : {}
85931
+ };
85932
+ }
85933
+ function tokenEstimate4(items) {
85934
+ const chars = items.reduce((sum, item) => sum + (typeof item.text === "string" ? item.text.length : 0), 0);
85935
+ return Math.ceil(chars / 4);
85936
+ }
85937
+ function writeCompileRequestFullTextView(context, format) {
85938
+ const pages = context.request_full_text?.pages ?? [];
85939
+ const items = pages.map((page) => pageItem(context, page));
85940
+ if (format === "json") {
85941
+ writeJson3({
85942
+ schema_version: "compile.request_full_text.view.v1",
85943
+ view: "text",
85944
+ view_of: "NodeContext.request_full_text.pages",
85945
+ node: context.node.slug,
85946
+ text_field: "items[].text",
85947
+ requested_block_ids: context.request_full_text?.requested_block_ids ?? [],
85948
+ item_id_field: "block_id",
85949
+ token_budget: "request-full-text-page",
85950
+ token_used: tokenEstimate4(items),
85951
+ shown_count: items.length,
85952
+ total: items.length,
85953
+ omitted_count: 0,
85954
+ truncated: items.some((item) => item.next_range !== undefined),
85955
+ selection_policy: {
85956
+ id: "request-full-text-v1",
85957
+ order: [{ field: "requested_block_ids", direction: "input" }]
85958
+ },
85959
+ items,
85960
+ how_to_explore: items.filter((item) => typeof item.next_command === "string").map((item) => ({
85961
+ level: "next_page",
85962
+ block_id: item.block_id,
85963
+ command: item.next_command,
85964
+ reason: "continue reading this full-text block page"
85965
+ }))
85966
+ });
85967
+ return;
85968
+ }
85969
+ const lines = [`Request full text for ${context.node.slug}`];
85970
+ for (const item of items) {
85971
+ lines.push("", `### ${String(item.block_id)} ${String(item.line_range)}`, String(item.text ?? ""));
85972
+ if (typeof item.next_command === "string")
85973
+ lines.push(`next: ${item.next_command}`);
85974
+ }
85975
+ process.stdout.write(lines.join(`
85976
+ `));
85977
+ }
85978
+
85669
85979
  // src/commands/workflowCompileHelpers.ts
85670
85980
  init_cliFeedback();
85671
85981
  init_errors();
@@ -85837,7 +86147,7 @@ function compileScopeForOptions(options) {
85837
86147
 
85838
86148
  // src/commands/workflowCompileCommand.ts
85839
86149
  function registerCompileWorkflowCommand(program2) {
85840
- program2.command("compile").description("Workflow helpers for /context:compile").option("--code [slug]", "compile deterministic code projection for an active aspect:code source").option("--scan-changes", "print incremental compile workset without creating a workflow lock").addOption(new Option("--scan", "removed; use --scan-changes").hideHelp()).option("--format <format>", "with --scan-changes/--context, output format: json | table", "table").option("--view <view>", "compact output view: full | summary | source-refs | issues").option("--ignore-source <source-id>", "with --scan-changes or --context, ignore refresh-applied source ids", collectIgnoredSource, []).option("--context <slug>", "prepare NodeContext payload for one align node and print summary/digests").option("--source <source-id>", "with --context --view source-refs, narrow source refs to one source id").option("--heading <heading-prefix>", "with --context --view source-refs, narrow source refs to a heading path prefix or heading text").option("--token-budget <n>", "with --context --view source-refs, limit output by approximate token budget").option("--draft-scaffold", "with --context --view source-refs, include a compile-draft skeleton for visible citation refs").option("--cover-uncovered-only", "with --context, return only unresolved coverage candidates for a targeted repair round").option("--request-full-text <block-id>", "with --context, expand one visible finalized evidence block", collectRequestedBlock, []).option("--request-full-text-range <start:end>", "with --request-full-text, read a block-relative line page").option("--node-cycle <slug>", "validate one node draft, prepare reconcile, review safe defaults, and apply in one command").option("--delegated", "with a new compile workflow, record user-authorized delegated mode for low-risk review decisions").option("--draft <slug>", "validate a compile draft JSON/YAML document from stdin, or with --prepare reuse the saved draft").option("--prepare", "with --draft --plan, also prepare the semantic reconcile payload").option("--draft-status <slug>", "print the current compile draft session summary").option("--draft-patch <slug>", "apply a compile draft patch for one node").option("--input <stdin>", "compile draft JSON/YAML document used with --draft; use '-' for stdin").option("--accept-safe-defaults", "with --node-cycle, accept mechanically safe defaults and stop if anything needs judgment").option("--coverage-disposition <stdin>", "coverage disposition patch JSON/YAML document; use '-' for stdin").option("--coverage-skip <candidate-id>", "skip one coverage candidate without writing a disposition file").option("--coverage-skip-unresolved", "skip every unresolved coverage candidate in the node payload").option("--coverage-disposition-node <slug>", "node slug for --coverage-disposition").option("--coverage-disposition-replace", "replace existing dispositions for the patched coverage candidates").option("--payload-digest <digest>", "optional workflow payload digest stale guard for write commands").option("--reason <text>", "reason for coverage skip shortcuts").option("--plan", "validate the draft without writing knowledge").option("--close", "run compile close: compact, rebuild index, append changelog, verify").option("--save-input", "with --draft, save the consumed draft under output/compile/compile.<slug>.draft.yaml").action(async (options) => {
86150
+ program2.command("compile").description("Workflow helpers for /context:compile").option("--code [slug]", "compile deterministic code projection for an active aspect:code source").option("--scan-changes", "print incremental compile workset without creating a workflow lock").addOption(new Option("--scan", "removed; use --scan-changes").hideHelp()).option("--format <format>", "with --scan-changes/--context, output format: json | table", "table").option("--view <view>", "compact output view: full | summary | source-refs | issues | text").option("--ignore-source <source-id>", "with --scan-changes or --context, ignore refresh-applied source ids", collectIgnoredSource, []).option("--context <slug>", "prepare NodeContext payload for one align node and print summary/digests").option("--source <source-id>", "with --context --view source-refs, narrow source refs to one source id").option("--heading <heading-prefix>", "with --context --view source-refs, narrow source refs to a heading path prefix or heading text").option("--token-budget <n>", "with --context --view source-refs, limit output by approximate token budget").option("--draft-scaffold", "with --context --view source-refs, include a compile-draft skeleton for visible citation refs").option("--cover-uncovered-only", "with --context, return only unresolved coverage candidates for a targeted repair round").option("--request-full-text <block-id>", "with --context, expand one visible finalized evidence block", collectRequestedBlock, []).option("--request-full-text-range <start:end>", "with --request-full-text, read a block-relative line page").option("--node-cycle <slug>", "validate one node draft, prepare reconcile, review safe defaults, and apply in one command").option("--delegated", "with a new compile workflow, record user-authorized delegated mode for low-risk review decisions").option("--draft <slug>", "validate a compile draft JSON/YAML document from stdin, or with --prepare reuse the saved draft").option("--prepare", "with --draft --plan, also prepare the semantic reconcile payload").option("--draft-status <slug>", "print the current compile draft session summary").option("--draft-patch <slug>", "apply a compile draft patch for one node").option("--input <stdin>", "compile draft JSON/YAML document used with --draft; use '-' for stdin").option("--accept-safe-defaults", "with --node-cycle, accept mechanically safe defaults and stop if anything needs judgment").option("--coverage-disposition <stdin>", "coverage disposition patch JSON/YAML document; use '-' for stdin").option("--coverage-skip <candidate-id>", "skip one coverage candidate without writing a disposition file").option("--coverage-skip-unresolved", "skip every unresolved coverage candidate in the node payload").option("--coverage-disposition-node <slug>", "node slug for --coverage-disposition").option("--coverage-disposition-replace", "replace existing dispositions for the patched coverage candidates").option("--payload-digest <digest>", "optional workflow payload digest stale guard for write commands").option("--reason <text>", "reason for coverage skip shortcuts").option("--plan", "validate the draft without writing knowledge").option("--close", "run compile close: compact, rebuild index, append changelog, verify").option("--save-input", "with --draft, save the consumed draft under output/compile/compile.<slug>.draft.yaml").action(async (options) => {
85841
86151
  if (options.scan === true) {
85842
86152
  throw new ContextError(ExitCode.UserError, "context compile --scan was removed; use context compile --scan-changes", {
85843
86153
  category: ErrorCategory.UserInputInvalid,
@@ -85854,7 +86164,7 @@ function registerCompileWorkflowCommand(program2) {
85854
86164
  const wantsChanges = options.scanChanges === true;
85855
86165
  const actionCount = countActions(wantsCode, wantsChanges, typeof options.context === "string", typeof options.nodeCycle === "string", typeof options.draft === "string", typeof options.draftStatus === "string", typeof options.draftPatch === "string", typeof options.coverageDisposition === "string", typeof options.coverageSkip === "string", options.coverageSkipUnresolved === true, options.close === true);
85856
86166
  if (actionCount !== 1) {
85857
- throw new ContextError(ExitCode.UserError, "usage: context compile --code [slug] | --scan-changes [--delegated] | --context <slug> [--view source-refs] [--source <source-id>] [--heading <heading-prefix>] [--token-budget <n>] [--draft-scaffold] [--request-full-text <block-id> [--request-full-text-range <start:end>]] [--delegated] | --node-cycle <slug> --input - --accept-safe-defaults [--delegated] | --draft <slug> --input - --plan [--prepare] [--delegated] | --draft <slug> --plan --prepare | --draft-status <slug> | --draft-patch <slug> --input - --plan [--payload-digest <digest>] | --coverage-disposition - --coverage-disposition-node <slug> [--payload-digest <digest>] | --coverage-skip <candidate-id> --coverage-disposition-node <slug> --reason <text> | --coverage-skip-unresolved --coverage-disposition-node <slug> --reason <text> | --close. Code sources are not processed by bare context compile; use context compile --code.", {
86167
+ throw new ContextError(ExitCode.UserError, "usage: context compile --code [slug] | --scan-changes [--delegated] | --context <slug> [--view source-refs|text] [--source <source-id>] [--heading <heading-prefix>] [--token-budget <n>] [--draft-scaffold] [--request-full-text <block-id> [--request-full-text-range <start:end>]] [--delegated] | --node-cycle <slug> --input - --accept-safe-defaults [--delegated] | --draft <slug> --input - --plan [--prepare] [--delegated] | --draft <slug> --plan --prepare | --draft-status <slug> | --draft-patch <slug> --input - --plan [--payload-digest <digest>] | --coverage-disposition - --coverage-disposition-node <slug> [--payload-digest <digest>] | --coverage-skip <candidate-id> --coverage-disposition-node <slug> --reason <text> | --coverage-skip-unresolved --coverage-disposition-node <slug> --reason <text> | --close. Code sources are not processed by bare context compile; use context compile --code.", {
85858
86168
  category: ErrorCategory.UserInputInvalid,
85859
86169
  agent_hints: [{
85860
86170
  code: "compile-code-explicit-required",
@@ -85868,14 +86178,15 @@ function registerCompileWorkflowCommand(program2) {
85868
86178
  const viewWasExplicit = typeof options.view === "string";
85869
86179
  const view = compileOutputView(options.view);
85870
86180
  const wantsDraftPrepare = typeof options.draft === "string" && options.prepare === true;
86181
+ const requestFullTextBlockIds = ignoredSourceIds(options.requestFullText);
85871
86182
  if (view !== "full") {
85872
86183
  if (!wantsChanges && typeof options.context !== "string" && !wantsDraftPrepare) {
85873
86184
  throw new ContextError(ExitCode.UserError, "--view is only valid with --scan-changes, --context, or --draft --prepare", {
85874
86185
  category: ErrorCategory.UserInputInvalid
85875
86186
  });
85876
86187
  }
85877
- if (wantsChanges && view === "source-refs") {
85878
- throw new ContextError(ExitCode.UserError, "--view source-refs is only valid with --context", {
86188
+ if (wantsChanges && (view === "source-refs" || view === "text")) {
86189
+ throw new ContextError(ExitCode.UserError, `--view ${view} is only valid with --context`, {
85879
86190
  category: ErrorCategory.UserInputInvalid
85880
86191
  });
85881
86192
  }
@@ -85889,8 +86200,12 @@ function registerCompileWorkflowCommand(program2) {
85889
86200
  category: ErrorCategory.UserInputInvalid
85890
86201
  });
85891
86202
  }
86203
+ if (view === "text" && (typeof options.context !== "string" || requestFullTextBlockIds.length === 0)) {
86204
+ throw new ContextError(ExitCode.UserError, "--view text requires --context <slug> --request-full-text <block-id>", {
86205
+ category: ErrorCategory.UserInputInvalid
86206
+ });
86207
+ }
85892
86208
  }
85893
- const requestFullTextBlockIds = ignoredSourceIds(options.requestFullText);
85894
86209
  const requestFullTextRange = parseRequestFullTextRange(options.requestFullTextRange);
85895
86210
  const coverUncoveredOnly = options.coverUncoveredOnly === true;
85896
86211
  const draftScaffold = options.draftScaffold === true;
@@ -86098,11 +86413,15 @@ function registerCompileWorkflowCommand(program2) {
86098
86413
  }
86099
86414
  if (typeof options.context === "string") {
86100
86415
  let context = await getNodeContext(ctx.ctxDir, options.context, {
86101
- ...view !== "source-refs" && !coverUncoveredOnly ? { mode: "changed-only" } : {},
86416
+ ...view !== "source-refs" && view !== "text" && requestFullTextBlockIds.length === 0 && !coverUncoveredOnly ? { mode: "changed-only" } : {},
86102
86417
  ...ignoreSourceIds.length > 0 ? { ignoreSourceIds } : {},
86103
86418
  ...requestFullTextBlockIds.length > 0 ? { requestFullTextBlockIds } : {},
86104
86419
  ...requestFullTextRange !== undefined ? { requestFullTextRange } : {}
86105
86420
  });
86421
+ if (view === "text") {
86422
+ writeCompileRequestFullTextView(context, compileChangesFormat(options.format));
86423
+ return;
86424
+ }
86106
86425
  const sourceFinalize = await readCurrentSourceOwnershipRecord(ctx.ctxDir).then((record) => record === null ? undefined : publishedSourceOwnershipSummary(record));
86107
86426
  const selectedCoverageStatus = coverUncoveredOnly ? await readCoverageWorkspaceStatus(ctx.ctxDir) : undefined;
86108
86427
  const selectedCoverageCandidates = selectedCoverageStatus?.candidates.filter((candidate) => candidate.node_slug === context.node.slug && candidate.status === "unresolved");
@@ -86425,6 +86744,20 @@ var AGENT_HINT_EMITTER_WORKFLOW_INVENTORY = [
86425
86744
  handles: ["node_slug", "section_id", "source_ref"],
86426
86745
  notes: "Compile applies semantic draft actions."
86427
86746
  },
86747
+ {
86748
+ source: "src/workflow/compileChangesOutput.ts",
86749
+ family: "compile changes output",
86750
+ policy: "semantic",
86751
+ handles: ["node_slug", "first_compile_pending", "next_action"],
86752
+ notes: "Incremental compile output routes agents through remaining Node slugs and compile commands instead of cache probing."
86753
+ },
86754
+ {
86755
+ source: "src/workflow/compileChangesTypes.ts",
86756
+ family: "compile changes contract",
86757
+ policy: "semantic",
86758
+ handles: ["node_slug", "first_compile_pending", "next_action"],
86759
+ notes: "Compile changes types define semantic pending-work handles and next actions for Agent views."
86760
+ },
86428
86761
  {
86429
86762
  source: "src/workflow/compileClose.ts",
86430
86763
  family: "compile close",
@@ -86453,6 +86786,13 @@ var AGENT_HINT_EMITTER_WORKFLOW_INVENTORY = [
86453
86786
  handles: ["draft_digest", "action_id", "node_slug"],
86454
86787
  notes: "Draft patch hints use session digest and action ids."
86455
86788
  },
86789
+ {
86790
+ source: "src/workflow/compileDraftSourceRefs.ts",
86791
+ family: "compile draft source refs",
86792
+ policy: "semantic",
86793
+ handles: ["source_ref", "block_id", "node_slug"],
86794
+ notes: "Source_ref diagnostics provide nearby refs, split actions, and citation handles rather than storage inspection steps."
86795
+ },
86456
86796
  {
86457
86797
  source: "src/workflow/compileDraftTypes.ts",
86458
86798
  family: "compile draft types",
@@ -86743,6 +87083,20 @@ var AGENT_HINT_EMITTER_INVENTORY = [
86743
87083
  handles: ["project_id", "cache_status", "pending_compile"],
86744
87084
  notes: "Next commands are cache/status commands, not physical cache inspection."
86745
87085
  },
87086
+ {
87087
+ source: "src/commands/captureCodeInputHints.ts",
87088
+ family: "code capture input hints",
87089
+ policy: "semantic",
87090
+ handles: ["target_path", "module_path", "received_targets"],
87091
+ notes: "Code capture input errors route agents through plan and module capture commands rather than package probing."
87092
+ },
87093
+ {
87094
+ source: "src/commands/captureCommand.ts",
87095
+ family: "capture command output",
87096
+ policy: "semantic",
87097
+ handles: ["source_id", "capture_mode", "agent_hints"],
87098
+ notes: "Capture output serializes structured hints from mode-specific handlers."
87099
+ },
86746
87100
  {
86747
87101
  source: "src/commands/captureNote.ts",
86748
87102
  family: "note capture code anchors",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c4a/context-cli",
3
- "version": "0.5.35-alpha.3",
3
+ "version": "0.5.35-beta.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "context": "./cli.js"
@@ -30,6 +30,7 @@ Keep the prompt shape stable: read fixed schema/protocol first, then existing kn
30
30
  - `--unwrap` only removes the workflow metadata envelope. It does not turn a summary view into detail output.
31
31
  3. Reuse existing knowledge before inventing candidates. For named terms or entities, prefer `context mdrive glossary match <name>` and `context mdrive node list --format json` over direct file reads. Treat `match.kind`, `match.matched`, and `match.rank` as stable lookup hints: exact title/slug/alias hits should usually reuse the existing Node instead of creating another one.
32
32
  - Apply packaged `context:skill-align-workflow` Node classification gates before candidate ops and again before finalize: Action requires scale plus process evidence; Entity requires a concrete A/B tag or pure `term`; Domain requires child Nodes; fake Entities need at least two suspicious signals before downgrade. Scope/process words in a source title, such as "方案", "架构", "流程", "策略", or "演练", are review signals for the title/type choice, not proof that the Node is an Entity.
33
+ - Code projection Nodes are reusable knowledge handles. When prose evidence should attach to an existing code symbol Node, reuse the code slug (for example `pkg/symbol/button`) instead of creating a parallel document Node. If that code slug is not already in finalized prose ownership, declare it in the current `nodes[]` with the same node type and compatible tags/title, add the desired `planned_sections`, and point `sections[].owner` plus `block_ownership[].owners` at that slug. Do not cite `aspect:code:*` source refs from prose Sections; prose Sections cite only the current document evidence.
33
34
  4. Submit generated workflow payloads directly through stdin, preferably as JSON. Use YAML schemas only for reading examples when helpful; generated artifacts should avoid YAML quoting/indentation failure loops. Do not create `/tmp` or workspace scratch files for align payloads. The CLI owns ids, reducer validation, workflow payload storage, and mechanical aggregate. You own semantic discovery, Node type/tag decisions, structure decisions, and user-facing questions.
34
35
  - Save coarse-read with `context align --coarse-read - --format json`.
35
36
  The latest `align-coarse-read` payload is only the most recent checkpoint; durable multi-source reading notes are stored under `align-candidate-ledger.source_readings`.
@@ -80,6 +81,8 @@ Submit it with `context align --ownership-patch - --format json`. Keep `base_dig
80
81
 
81
82
  If `align-segments.incremental.mode` is `incremental`, the finalize step is a delta merge: submit only the Nodes and block ownership supported by the current scanned sources, and reference previous finalized Nodes when they are parents, dependencies, domain children, owners, or visibility targets. Absence of an old Node or edge is not a delete signal. Do not redeclare an old parent/domain just to attach a new child. `sections[].owner` must be a Node declared in the current payload; previous finalized Nodes can be referenced structurally but do not receive new section plans from this incremental payload. Existing or previously removed Node slugs cannot change `node_type`; `context align --scan --full` does not bypass that guard. Use a new slug for a different type, or retire the old slug through `context drop` or explicit structure correction before re-aligning.
82
83
 
84
+ For code-projection Nodes, distinguish "reuse the existing knowledge file" from "previous finalized prose ownership". A freshly projected code Node may exist in `knowledge/` but not yet appear in finalized ownership. To attach current prose evidence to it, declare that same slug in the current finalize payload and plan only prose-owned Sections for the current evidence; the compile step will merge the prose source and Sections into the existing code Node while preserving code metadata and code-owned Sections.
85
+
83
86
  `nodes[].planned_sections` is the distinct set of Section kinds planned for that Node. List each kind at most once; do not copy `sections[].section_kind` one-for-one when a Node has multiple Sections of the same kind.
84
87
 
85
88
  For large finalize decisions, use `block_ownership_defaults[]` instead of enumerating every block. Each default names a `source_id` plus the same ownership fields as a block-level entry except `block_id`; the CLI expands it across that source's coverable blocks. Put only exceptions in `block_ownership[]`, which override defaults for their `block_id`. Keep the payload on stdin; do not generate temp JSON files just to list hundreds of ownership rows.
@@ -18,6 +18,7 @@ Run the align workflow through the CLI-owned beta.8 payload chain: scan segments
18
18
  - Finalized structure is represented by `align-structure-decision`.
19
19
  - Retired payloads include candidate tables, decision patches, and full-tree finalize documents.
20
20
  - Existing knowledge is the lookup registry. Use `context mdrive glossary match <name>` / `context mdrive node list --format json` for term/entity reuse; do not read `knowledge/**` and do not create a separate registry file.
21
+ - Code projection Nodes are existing knowledge handles. When document evidence belongs on a code symbol, reuse the code slug instead of creating a parallel document Node. If the slug exists only as projected code knowledge and is not yet in finalized prose ownership, declare that same slug in the current `nodes[]`, add the prose `planned_sections`, and route `sections[].owner` plus `block_ownership[].owners` to it.
21
22
  - Keep cache-friendly prompt order: fixed protocol and schemas first, existing knowledge lookup second, source-shared payload views third, current candidate batch last. Preserve CLI JSON order and do not add timestamps, random ids, scratch paths, or host paths to generated payloads.
22
23
  - `align-segments.generation_policy` is the workspace language contract for generated Node titles, summaries, rationale prose, and planned Section wording. Source-bound Section wording should stay close to the cited source language when it differs from the workspace language. Preserve product names, code identifiers, slugs, flags, `block_id` handles, and `source_ref` tokens exactly when needed.
23
24
  - Source titles and headings are ordinary evidence, not structural authority. Do not automatically copy them into Node titles or aliases; classify the evidence referent first, then generate a title that fits the final Node type.
@@ -43,6 +44,8 @@ Use this only inside `/context:align`.
43
44
 
44
45
  If `align-segments.incremental.mode` is `incremental`, finalize is a delta merge. Submit only the Nodes and ownership supported by the current scanned sources; reference previous finalized Nodes when they are parents, dependencies, domain children, owners, or visibility targets. Absence of an old Node or edge is not a delete signal. Do not redeclare an old parent/domain just to attach a new child. `sections[].owner` must be a Node declared in the current payload; previous finalized Nodes can be referenced structurally but do not receive new section plans from this incremental payload. Existing or previously removed Node slugs cannot change `node_type`; `context align --scan --full` does not bypass that guard. Use a new slug for a different type, or retire the old slug through `context drop` or explicit structure correction before re-aligning.
45
46
 
47
+ For code-projection Nodes, "existing knowledge file" and "previous finalized prose ownership" are different states. A freshly projected code Node can be reused by declaring the same slug in the current finalize payload and planning only prose-owned Sections for the current evidence. Compile merges the prose source and Sections into the existing code Node while preserving code metadata and code-owned Sections. Prose Sections must cite document evidence, not `aspect:code:*` source refs.
48
+
46
49
  `nodes[].planned_sections` is the distinct set of Section kinds planned for that Node. List each kind at most once; do not copy `sections[].section_kind` one-for-one when a Node has multiple Sections of the same kind.
47
50
 
48
51
  For large finalize decisions, use `block_ownership_defaults[]` instead of enumerating every block. Each default names a `source_id` plus the same ownership fields as a block-level entry except `block_id`; the CLI expands it across that source's coverable blocks. Put only exceptions in `block_ownership[]`, which override defaults for their `block_id`. Keep the payload on stdin; do not generate temp JSON files just to list hundreds of ownership rows.