pi-midcompact 0.5.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/plan.ts CHANGED
@@ -22,14 +22,14 @@ export function addDraftRange(
22
22
  ): DraftPlan {
23
23
  const start = atoms.find((atom) => atom.ref === input.start);
24
24
  const end = atoms.find((atom) => atom.ref === input.end);
25
- if (!start || !end) throw new Error("Unknown atom ref; run locate/inspect again against the current anchor snapshot.");
25
+ if (!start || !end) throw new Error("Unknown atom ref; run inspect or locate again against the current anchor snapshot.");
26
26
  if (start.index > end.index) throw new Error("start must not occur after end.");
27
27
  const selected = atoms.slice(start.index, end.index + 1);
28
28
  if (selected.length === 0) throw new Error("Empty range.");
29
29
  const unsafe = selected.find((atom) => isProtectedAtom(atom));
30
30
  if (unsafe) throw new Error(`Range crosses protected atom ${unsafe.ref} (${unsafe.kind}). Split the plan around it.`);
31
31
  const overlaps = draft.ranges.some((range) => !(end.index < range.startIndex || start.index > range.endIndex));
32
- if (overlaps) throw new Error("Range overlaps an existing draft range.");
32
+ if (overlaps) throw new Error("Range overlaps an existing plan range; remove or replace it first.");
33
33
 
34
34
  const metrics = rangeMetricsForAtoms(selected);
35
35
  const summary = input.summary ?? "";
@@ -76,7 +76,7 @@ export function updateDraftRange(
76
76
  patch: { summary?: string; topic?: string },
77
77
  ): DraftPlan {
78
78
  const target = draft.ranges.find((range) => range.id === draftId);
79
- if (!target) throw new Error(`Unknown draft range ${draftId}.`);
79
+ if (!target) throw new Error(`Unknown plan range ${draftId}.`);
80
80
  const summary = patch.summary ?? target.summary;
81
81
  const topic = patch.topic ?? target.topic;
82
82
  const replacement = replacementContentChars(summary, topic);
@@ -90,7 +90,7 @@ export function updateDraftRange(
90
90
  }
91
91
 
92
92
  export function removeDraftRange(draft: DraftPlan, draftId: string): DraftPlan {
93
- if (!draft.ranges.some((range) => range.id === draftId)) throw new Error(`Unknown draft range ${draftId}.`);
93
+ if (!draft.ranges.some((range) => range.id === draftId)) throw new Error(`Unknown plan range ${draftId}.`);
94
94
  return { ...draft, revision: draft.revision + 1, ranges: draft.ranges.filter((range) => range.id !== draftId) };
95
95
  }
96
96
 
@@ -153,18 +153,18 @@ export interface DraftFormatOptions {
153
153
  /** Agent-facing plan output with bounded semantic landmarks and summaries. */
154
154
  export function formatDraft(draft: DraftPlan, telemetry?: DraftTelemetry, options: DraftFormatOptions = {}): string {
155
155
  if (options.detail === "full" && !options.draftId) {
156
- throw new Error("plan show detail=full requires draft_id.");
156
+ throw new Error("Full plan output requires a known range id.");
157
157
  }
158
158
  const selected = options.draftId
159
159
  ? draft.ranges.filter((range) => range.id === options.draftId)
160
160
  : draft.ranges;
161
161
  const atomsByRef = options.atoms ? new Map(options.atoms.map((atom) => [atom.ref, atom])) : undefined;
162
- if (options.draftId && selected.length === 0) throw new Error(`Unknown draft range ${options.draftId}.`);
162
+ if (options.draftId && selected.length === 0) throw new Error(`Unknown plan range ${options.draftId}.`);
163
163
 
164
164
  const lines: string[] = [];
165
165
  if (telemetry) lines.push(formatTelemetry(telemetry));
166
166
  if (draft.ranges.length === 0) {
167
- lines.push(`Draft v${draft.revision}: no compression ranges.`);
167
+ lines.push(`Plan v${draft.revision}: no compression ranges.`);
168
168
  return lines.join("\n\n");
169
169
  }
170
170
  lines.push(draftHeader(draft));
@@ -180,7 +180,7 @@ export function formatDraft(draft: DraftPlan, telemetry?: DraftTelemetry, option
180
180
  const block = formatRangeBrief(range, atomsByRef);
181
181
  const currentLength = lines.join("\n\n").length;
182
182
  if (currentLength + 2 + block.length > DRAFT_OUTPUT_LIMIT) {
183
- const notice = `Output budget reached: showed ${shown} of ${selected.length} range(s). Use draft_id to inspect one range.`;
183
+ const notice = `Output budget reached: showed ${shown} of ${selected.length} range(s). Use plan_read to inspect one range.`;
184
184
  if (currentLength + 2 + notice.length <= DRAFT_OUTPUT_LIMIT) lines.push(notice);
185
185
  break;
186
186
  }
@@ -199,7 +199,7 @@ export function formatPlanMutation(
199
199
  ): string {
200
200
  const pendingCount = draft.ranges.filter((range) => range.summary.trim().length === 0).length;
201
201
  const verb = op === "add" ? "added" : op === "update" ? "updated" : "removed";
202
- const lines = [`Draft v${draft.revision}: ${verb} ${changedId} · ${draft.ranges.length} range(s) · ${pendingCount} pending summary.`];
202
+ const lines = [`Plan v${draft.revision}: ${verb} ${changedId} · ${draft.ranges.length} range(s) · ${pendingCount} pending summary.`];
203
203
  if (op !== "remove") {
204
204
  const changed = draft.ranges.find((range) => range.id === changedId);
205
205
  const atomsByRef = atoms ? new Map(atoms.map((atom) => [atom.ref, atom])) : undefined;
@@ -211,7 +211,7 @@ export function formatPlanMutation(
211
211
  function draftHeader(draft: DraftPlan): string {
212
212
  const pendingCount = draft.ranges.filter((range) => range.summary.trim().length === 0).length;
213
213
  const reviewState = pendingCount === 0 ? "ready for review" : `${pendingCount} pending summary`;
214
- return `Draft v${draft.revision}: ${draft.ranges.length} range(s) (${reviewState}).`;
214
+ return `Plan v${draft.revision}: ${draft.ranges.length} range(s) (${reviewState}).`;
215
215
  }
216
216
 
217
217
  function formatRangeBrief(range: DraftRange, atomsByRef?: ReadonlyMap<string, Atom>): string {
package/src/projection.ts CHANGED
@@ -74,8 +74,8 @@ export function replacementMetrics(block: CompressionBlock): ContentMetrics {
74
74
  * Factual replacement content chars for a range, given its topic and summary.
75
75
  * Computed from the actual summary message wrapper text that projection emits.
76
76
  */
77
- export function replacementContentChars(summary: string, topic?: string): number {
78
- const block: CompressionBlock = {
77
+ function draftReplacementBlock(summary: string, topic?: string): CompressionBlock {
78
+ return {
79
79
  id: "draft",
80
80
  summary,
81
81
  topic,
@@ -89,7 +89,16 @@ export function replacementContentChars(summary: string, topic?: string): number
89
89
  originalApproxTokens: 0,
90
90
  compressedApproxTokens: 0,
91
91
  };
92
- return replacementMetrics(block).contentChars;
92
+ }
93
+
94
+ /** Render the exact replacement content used for draft display estimates. */
95
+ export function replacementContentText(summary: string, topic?: string): string {
96
+ const content = summaryMessage(draftReplacementBlock(summary, topic)).content;
97
+ return typeof content === "string" ? content : "";
98
+ }
99
+
100
+ export function replacementContentChars(summary: string, topic?: string): number {
101
+ return replacementMetrics(draftReplacementBlock(summary, topic)).contentChars;
93
102
  }
94
103
 
95
104
  /** @deprecated legacy heuristic; kept only for old approximate-token field compat. */
package/src/review-ui.ts CHANGED
@@ -46,7 +46,7 @@ export async function showReviewUi(
46
46
 
47
47
  const header: string[] = [
48
48
  h("╭"),
49
- framed(accent(theme.bold(`Midcompact Review · Draft v${draft.revision} · ${draft.ranges.length} range(s)`))),
49
+ framed(accent(theme.bold(`Midcompact Review · Plan v${draft.revision} · ${draft.ranges.length} range(s)`))),
50
50
  framed(usageLine(telemetry, theme)),
51
51
  h("├"),
52
52
  ];
@@ -195,7 +195,7 @@ export async function showReviewUi(
195
195
 
196
196
  export function buildReviewText(atoms: Atom[], draft: DraftPlan, telemetry: DraftTelemetry): string {
197
197
  const lines = [
198
- `Midcompact Review · Draft v${draft.revision}`,
198
+ `Midcompact Review · Plan v${draft.revision}`,
199
199
  plainUsageLine(telemetry),
200
200
  "This is awareness, not a target.",
201
201
  "",