@astrosheep/square 0.3.78 → 0.3.80

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.
Files changed (70) hide show
  1. package/dist/attention-presentation.d.ts +2 -0
  2. package/dist/attention-presentation.js +39 -4
  3. package/dist/automatic-session.js +1 -1
  4. package/dist/boundary-presentation.js +4 -7
  5. package/dist/claude-mod.js +1 -1
  6. package/dist/cli/catch-presentation.d.ts +2 -2
  7. package/dist/cli/catch-presentation.js +3 -3
  8. package/dist/cli/context.d.ts +11 -1
  9. package/dist/cli/context.js +14 -3
  10. package/dist/cli/express-presentation.d.ts +6 -5
  11. package/dist/cli/express-presentation.js +42 -43
  12. package/dist/cli/maintenance-commands.js +6 -6
  13. package/dist/cli/mcp-server-command.js +1 -1
  14. package/dist/cli/observation-commands.d.ts +4 -4
  15. package/dist/cli/observation-commands.js +20 -16
  16. package/dist/cli/program.d.ts +1 -1
  17. package/dist/cli/program.js +5 -5
  18. package/dist/cli/square-commands.js +35 -23
  19. package/dist/decisions.d.ts +3 -5
  20. package/dist/decisions.js +10 -9
  21. package/dist/delivery.d.ts +1 -1
  22. package/dist/help.js +15 -2
  23. package/dist/inbox.js +3 -3
  24. package/dist/index.d.ts +1 -2
  25. package/dist/index.js +1 -2
  26. package/dist/ledger-maintenance.d.ts +1 -1
  27. package/dist/ledger-maintenance.js +1 -1
  28. package/dist/list.d.ts +3 -3
  29. package/dist/list.js +1 -1
  30. package/dist/mcp.d.ts +2 -2
  31. package/dist/mcp.js +12 -7
  32. package/dist/mention-parse.d.ts +2 -8
  33. package/dist/mention-parse.js +6 -9
  34. package/dist/model.d.ts +2 -2
  35. package/dist/notifications.js +24 -8
  36. package/dist/participant-host.js +5 -0
  37. package/dist/plugins/opencode/index.js +1 -1
  38. package/dist/square-actions.d.ts +22 -9
  39. package/dist/square-actions.js +26 -19
  40. package/dist/square-api.d.ts +43 -0
  41. package/dist/square-api.js +471 -0
  42. package/dist/square-facade.d.ts +123 -203
  43. package/dist/square-tools.d.ts +4 -3
  44. package/dist/square-tools.js +21 -15
  45. package/dist/stream.d.ts +2 -2
  46. package/dist/views.d.ts +16 -12
  47. package/dist/views.js +13 -27
  48. package/guides/application-demand-optimization.md +5 -0
  49. package/guides/application-facade-delivery-evidence.md +7 -2
  50. package/guides/mcp-connection-lifetime.md +2 -2
  51. package/guides/mcp.md +1 -1
  52. package/guides/operation-cancellation.md +2 -0
  53. package/guides/primitive-catch-consolidation.md +5 -0
  54. package/guides/primitive-history-consolidation.md +5 -0
  55. package/guides/primitive-status-consolidation.md +5 -0
  56. package/guides/primitive-storage-consolidation.md +5 -0
  57. package/guides/row-storage-design.md +1 -1
  58. package/guides/square-api.md +107 -0
  59. package/package.json +1 -1
  60. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  61. package/plugins/claude/skills/square/SKILL.md +4 -4
  62. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  63. package/plugins/opencode/index.ts +1 -1
  64. package/plugins/paseo/client/markdown.tsx +16 -6
  65. package/plugins/pi/square-pi.js +7 -6
  66. package/skills/square/SKILL.md +4 -4
  67. package/dist/square-application.d.ts +0 -38
  68. package/dist/square-application.js +0 -683
  69. package/guides/application-boundary.md +0 -32
  70. package/guides/application-facade-design.md +0 -98
@@ -14,4 +14,6 @@ export declare function displayAttentionPath(squarePath: string): string;
14
14
  export declare function renderAttentionDescription({ actor, route }: Pick<AttentionPreview, 'actor' | 'route'>): string;
15
15
  /** Full notification text for human-facing expansion, never the bounded model preview. */
16
16
  export declare function renderAttentionFull(attention: AttentionPreview): string;
17
+ /** Shared model-facing envelope; transport correlation and human cards stay outside it. */
18
+ export declare function renderSquareReminder(previews: readonly string[]): string;
17
19
  export declare function renderAttentionPreview(attention: AttentionPreview): string;
@@ -27,6 +27,18 @@ export function renderAttentionDescription({ actor, route }) {
27
27
  export function renderAttentionFull(attention) {
28
28
  return renderAttentionBlock(attention, attention.body);
29
29
  }
30
+ /** Attribute values keep their quotes; the stored body and the human card stay unescaped. */
31
+ function escapeAttribute(value) {
32
+ return value
33
+ .replace(/&/g, '&amp;')
34
+ .replace(/"/g, '&quot;')
35
+ .replace(/</g, '&lt;')
36
+ .replace(/>/g, '&gt;');
37
+ }
38
+ /** A body may quote the envelope's own tags; in the preview they must never read as one. */
39
+ function escapeSquareTags(text) {
40
+ return text.replace(/<(\/?)(square-|system-reminder)/gi, '<\\$1$2');
41
+ }
30
42
  function renderAttentionBlock(attention, body) {
31
43
  return [
32
44
  '<square-activity>',
@@ -37,11 +49,34 @@ function renderAttentionBlock(attention, body) {
37
49
  '</square-activity>',
38
50
  ].join('\n');
39
51
  }
52
+ /** Shared model-facing envelope; transport correlation and human cards stay outside it. */
53
+ export function renderSquareReminder(previews) {
54
+ if (previews.length === 0)
55
+ return '';
56
+ return [
57
+ '',
58
+ '<system-reminder>',
59
+ 'This is an external reminder from Square, not a direct message from the user.',
60
+ '',
61
+ previews.join('\n\n'),
62
+ '</system-reminder>',
63
+ '',
64
+ ].join('\n');
65
+ }
40
66
  export function renderAttentionPreview(attention) {
67
+ const remaining = attention.body.replace(/\r\n/g, '\n').length - ATTENTION_BODY_MAX;
68
+ const attributes = [
69
+ `square="${escapeAttribute(displayAttentionPath(attention.squarePath))}"`,
70
+ `as="${escapeAttribute(attention.recipient)}"`,
71
+ `id="${escapeAttribute(formatActivityId(attention.actIndex))}"`,
72
+ `from="${escapeAttribute(attention.actor)}"`,
73
+ ...(remaining > 0 ? [`truncated="${remaining} more chars"`] : []),
74
+ ];
41
75
  return [
42
- renderAttentionBlock(attention, previewAttentionBody(attention.body)),
43
- ...(attentionBodyIsClipped(attention.body)
44
- ? ['· clipped — catch it to read the rest']
45
- : []),
76
+ `<square-activity ${attributes.join(' ')}>`,
77
+ `${renderAttentionDescription(attention)}.`,
78
+ '',
79
+ escapeSquareTags(previewAttentionBody(attention.body)),
80
+ '</square-activity>',
46
81
  ].join('\n');
47
82
  }
@@ -2,7 +2,7 @@ import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { openSquare } from './open-square.js';
4
4
  import { closeOpenSquare } from './open-square.js';
5
- import { Square } from './square-application.js';
5
+ import { Square } from './square-api.js';
6
6
  import { currentParticipant } from './views.js';
7
7
  import { automaticParticipant, harnessSessionSources } from './participant-identity.js';
8
8
  import { SquareError } from './model.js';
@@ -7,7 +7,7 @@ import { closeOpenSquare } from './open-square.js';
7
7
  import { presentPending } from './presentation-operations.js';
8
8
  import { hostLedgerForEnv } from './registry.js';
9
9
  import { sessionInbox } from './inbox.js';
10
- import { attentionBodyIsClipped, renderAttentionPreview } from './attention-presentation.js';
10
+ import { attentionBodyIsClipped, renderAttentionPreview, renderSquareReminder } from './attention-presentation.js';
11
11
  import { projectBoundaryEligibility } from './square-projections.js';
12
12
  export { pendingAtBoundary } from './square-projections.js';
13
13
  const CONTEXT_MAX = 1200;
@@ -38,15 +38,12 @@ async function awaitWithSignal(promise, signal) {
38
38
  export function renderPendingAtBoundary(inbox) {
39
39
  return renderBoundary(inbox).context;
40
40
  }
41
- function frameBoundary(lines) {
42
- return `\n${lines.join('\n')}\n`;
43
- }
44
41
  function renderBoundary(inbox) {
45
42
  const queued = inbox.flatMap((membership) => membership.notifications.map((notification) => ({ membership, notification })));
46
43
  const blocks = [];
47
44
  const complete = [];
48
45
  let omitted = 0;
49
- const omission = (count) => `… ${count} more — catch up to read them`;
46
+ const omission = (count) => `${count} more ${count === 1 ? 'activity' : 'activities'} not shown.`;
50
47
  for (const [index, entry] of queued.entries()) {
51
48
  const { membership, notification } = entry;
52
49
  const block = [
@@ -60,7 +57,7 @@ function renderBoundary(inbox) {
60
57
  }),
61
58
  ].join('\n');
62
59
  const omittedAfter = omitted + queued.length - index - 1;
63
- const prospective = frameBoundary([...blocks, block, ...(omittedAfter > 0 ? [omission(omittedAfter)] : [])]);
60
+ const prospective = renderSquareReminder([...blocks, block, ...(omittedAfter > 0 ? [omission(omittedAfter)] : [])]);
64
61
  if (prospective.length > CONTEXT_MAX) {
65
62
  omitted += 1;
66
63
  continue;
@@ -73,7 +70,7 @@ function renderBoundary(inbox) {
73
70
  });
74
71
  }
75
72
  return {
76
- context: frameBoundary([...blocks, ...(omitted > 0 ? [omission(omitted)] : [])]),
73
+ context: renderSquareReminder([...blocks, ...(omitted > 0 ? [omission(omitted)] : [])]),
77
74
  complete,
78
75
  };
79
76
  }
@@ -3,7 +3,7 @@ import { claimSessionParticipant, hostLedgerForEnv } from './registry.js';
3
3
  import { openSquare } from './open-square.js';
4
4
  import { closeOpenSquare } from './open-square.js';
5
5
  import { publishWakeRoute, retireWakeRouteFromArtifact } from './routes.js';
6
- import { Square } from './square-application.js';
6
+ import { Square } from './square-api.js';
7
7
  import { observeClaudeDelivery } from './claude-delivery.js';
8
8
  import { processActNotificationsOnce } from './notifications.js';
9
9
  import { sweepPending } from './delivery-operations.js';
@@ -1,7 +1,7 @@
1
- import type { ApplicationOutcome, CatchApplicationData, CatchApplicationOptions, CatchProjection, OperationProgress } from '../square-facade.js';
1
+ import type { CatchResult, CatchOptions, OperationProgress } from '../square-facade.js';
2
2
  export declare function renderCatchProgress(squarePath: string, progress: OperationProgress): string | undefined;
3
3
  /** Render settled catch facts; no selection, terminal decisions, receipts or wait machinery. */
4
- export declare function renderCatchOutcome(squarePath: string, outcome: ApplicationOutcome<CatchApplicationData, CatchProjection>, options?: CatchApplicationOptions): {
4
+ export declare function renderCatchOutcome(squarePath: string, data: CatchResult, options?: CatchOptions): {
5
5
  stdout: string;
6
6
  exitCode: number;
7
7
  };
@@ -1,4 +1,4 @@
1
- import { parseActivityId } from '../square-application.js';
1
+ import { parseActivityId } from '../square-api.js';
2
2
  import { participantCommandPrefix, renderAmbientEvent, renderWatchAlreadyActive, renderWatchForceTakeover, renderWatchReplaceMissing, renderWatchReplaced, renderWatchStatus, withPathOutput, } from '../presentation.js';
3
3
  export function renderCatchProgress(squarePath, progress) {
4
4
  if (progress.kind !== 'catch-started' || !progress.replacementRequested)
@@ -18,8 +18,8 @@ function continuationCommand(squarePath, name, options) {
18
18
  return `${participantCommandPrefix(squarePath, name)} catch ${args.join(' ')}`;
19
19
  }
20
20
  /** Render settled catch facts; no selection, terminal decisions, receipts or wait machinery. */
21
- export function renderCatchOutcome(squarePath, outcome, options = {}) {
22
- const { data, projection } = outcome;
21
+ export function renderCatchOutcome(squarePath, data, options = {}) {
22
+ const projection = data.observation;
23
23
  const location = projection.location ?? squarePath;
24
24
  const name = projection.participant;
25
25
  const stop = projection.stop ?? data.status;
@@ -1,4 +1,6 @@
1
1
  import { type HardCap } from '../model.js';
2
+ import { Square } from '../square-api.js';
3
+ import type { OperationControl } from '../square-facade.js';
2
4
  export interface CommandContext {
3
5
  cwd: string;
4
6
  env: NodeJS.ProcessEnv;
@@ -33,5 +35,13 @@ export interface ParsedGlobalArgs {
33
35
  export declare function parseGlobalArgs(rawArgs: string[]): Promise<ParsedGlobalArgs>;
34
36
  export declare function locationIsRequired(command: string): boolean;
35
37
  export declare function defaultContext(command: string, squarePath?: string, name?: string): CommandContext;
36
- export declare function applicationFor(context: CommandContext): import("../square-facade.js").SquareApplication<true>;
38
+ export declare function squareInput(context: CommandContext): {
39
+ cwd: string;
40
+ env: NodeJS.ProcessEnv;
41
+ path: string | undefined;
42
+ participant: string | undefined;
43
+ };
44
+ /** Adapter lifetime only; each callback uses the actual public resource API. */
45
+ export declare function withSquare<T>(context: CommandContext, run: (square: Square) => Promise<T>, control?: OperationControl): Promise<T>;
46
+ export declare function now(context: CommandContext): number;
37
47
  export declare function requireSquarePath(context: CommandContext): string;
@@ -1,7 +1,7 @@
1
1
  import os from 'node:os';
2
2
  import { commandUsageHint } from '../help.js';
3
3
  import { parseParticipantList, validateName } from '../model.js';
4
- import { createSquareApplication } from '../square-application.js';
4
+ import { Square, squareClock } from '../square-api.js';
5
5
  export async function readStdin() {
6
6
  try {
7
7
  let content = '';
@@ -137,9 +137,20 @@ export function locationIsRequired(command) {
137
137
  export function defaultContext(command, squarePath, name) {
138
138
  return { command, cwd: process.cwd(), env: { ...process.env }, squarePath, name, homeDir: os.homedir() };
139
139
  }
140
- export function applicationFor(context) {
141
- return createSquareApplication({ cwd: context.cwd, env: context.env, squarePath: context.squarePath, participant: context.name });
140
+ export function squareInput(context) {
141
+ return { cwd: context.cwd, env: context.env, path: context.squarePath, participant: context.name };
142
142
  }
143
+ /** Adapter lifetime only; each callback uses the actual public resource API. */
144
+ export async function withSquare(context, run, control) {
145
+ const square = await Square.at(squareInput(context), control);
146
+ try {
147
+ return await run(square);
148
+ }
149
+ finally {
150
+ await square.close();
151
+ }
152
+ }
153
+ export function now(context) { return squareClock(context.env)(); }
143
154
  export function requireSquarePath(context) {
144
155
  if (context.squarePath === undefined)
145
156
  fail(`✕ ${context.command} needs a square location\nsquare ls`);
@@ -1,10 +1,11 @@
1
- import type { ApplicationOutcome, ExpressApplicationData, ExpressApplicationOptions, ExpressProjection, SquareApplication } from '../square-facade.js';
2
- export interface ActivityOptions extends Omit<ExpressApplicationOptions, 'reply'> {
1
+ import { type Square } from '../square-api.js';
2
+ import type { OperationControl, ExpressResult, ExpressOptions } from '../square-facade.js';
3
+ export interface ActivityOptions extends Omit<ExpressOptions, 'reply'> {
3
4
  readonly reply?: string;
4
5
  }
5
6
  export declare function saveActivityDraft(squarePath: string, name: string, body: string, now?: number): string;
6
7
  /** Retry choices are transport syntax, never a second admission decision. */
7
8
  export declare function activityRetryCommand(squarePath: string, name: string, opts: ActivityOptions, force?: boolean): string;
8
- export declare function renderExpression(squarePath: string, outcome: ApplicationOutcome<ExpressApplicationData, ExpressProjection>): string;
9
- /** Body adaptation, terminal IO and drafts only; the application owns every attempt and wait. */
10
- export declare function executeExpression(application: SquareApplication, squarePath: string, requestedName: string | undefined, rawInput: string, opts: ActivityOptions): Promise<void>;
9
+ export declare function renderExpression(squarePath: string, data: ExpressResult): string;
10
+ /** Body adaptation, terminal IO and drafts only; Participant owns every attempt and wait. */
11
+ export declare function executeExpression(open: (control?: OperationControl) => Promise<Square>, squarePath: string, requestedName: string | undefined, rawInput: string, opts: ActivityOptions): Promise<void>;
@@ -2,7 +2,7 @@ import crypto from 'node:crypto';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { isSquareError } from '../model.js';
5
- import { formatActivityId, parseActivityId } from '../square-application.js';
5
+ import { formatActivityId, parseActivityId } from '../square-api.js';
6
6
  import { expressHintLine, formatRefusal, refusalText, renderActivityBlocked, renderActivityLimit, renderActivityUncertain, renderExpressNoWait, renderExpressWaiting, renderPendingFeed, joinRecoveryCommand, participantIdentity, participantsRecoveryCommand, participantCommandPrefix, commandPrefix, quoteShell, takeoverRecoveryLines, withPathOutput, } from '../presentation.js';
7
7
  import { formatDuration } from '../time.js';
8
8
  import { style } from '../tty-style.js';
@@ -33,19 +33,16 @@ function replyArgument(reply) {
33
33
  }
34
34
  /** Retry choices are transport syntax, never a second admission decision. */
35
35
  export function activityRetryCommand(squarePath, name, opts, force = opts.force ?? false) {
36
- const reach = opts.reach === 'bell' ? ' --bell'
37
- : opts.mentions?.length ? opts.mentions.map((target) => ` --mention ${quoteShell(target)}`).join('')
38
- : opts.noMention === true ? ' --no-mention' : '';
36
+ const reach = (opts.reach === 'bell' ? ' --bell' : '')
37
+ + (opts.mentions ?? []).map((target) => ` --mention ${quoteShell(target)}`).join('')
38
+ + (opts.noMention === true ? ' --no-mention' : '');
39
39
  return `${participantCommandPrefix(squarePath, name)} express${force ? ' --force' : ''}${opts.noWait ? ' --no-wait' : ''}${reach}${replyArgument(opts.reply)} -`;
40
40
  }
41
- function mentionRepairCommand(squarePath, name, opts, bell) {
42
- return `${participantCommandPrefix(squarePath, name)} express${opts.force ? ' --force' : ''}${opts.noWait ? ' --no-wait' : ''}${bell ? ' --bell' : ' --no-mention'}${replyArgument(opts.reply)} -`;
43
- }
44
41
  function isGestureOnly(body) {
45
42
  return /\*[^*]*\*/.test(body) && body.replace(/\*[^*]*\*/g, '').trim() === '';
46
43
  }
47
- export function renderExpression(squarePath, outcome) {
48
- const { data, projection } = outcome;
44
+ export function renderExpression(squarePath, data) {
45
+ const projection = data.observation;
49
46
  const name = projection.participant;
50
47
  const pendingSpeaker = projection.pendingPublic[0]?.actor;
51
48
  const pendingLead = pendingSpeaker === undefined ? ''
@@ -63,11 +60,15 @@ export function renderExpression(squarePath, outcome) {
63
60
  const hint = expressHintLine(ownCount);
64
61
  const replyNotice = data.replyAuthorNotHere === undefined ? ''
65
62
  : `\n○ ${participantIdentity(data.replyAuthorNotHere)} isn't in the square right now — the reply landed without reaching them`;
63
+ const unmatchedNotice = data.unmatchedMentions?.length
64
+ ? `\n · ${data.unmatchedMentions.map((token) => `@${token} isn't standing here`).join('; ')} — ${activity.mentions.length === 0 && activity.reply === undefined ? 'activity landed bare' : 'left as literal text'}` : '';
66
65
  return withPathOutput(projection.location ?? squarePath, (projection.reentry === undefined ? '' : '● you stepped back into the square\n')
67
- + confirmation + (hint ? `\n${style('dim', hint)}` : '') + replyNotice + pending, { participantCount: projection.standingCount, held: projection.held });
66
+ + confirmation + (hint ? `\n${style('dim', hint)}` : '') + replyNotice + unmatchedNotice + pending, { participantCount: projection.standingCount, held: projection.held });
68
67
  }
69
- /** Body adaptation, terminal IO and drafts only; the application owns every attempt and wait. */
70
- export async function executeExpression(application, squarePath, requestedName, rawInput, opts) {
68
+ /** Body adaptation, terminal IO and drafts only; Participant owns every attempt and wait. */
69
+ export async function executeExpression(open, squarePath, requestedName, rawInput, opts) {
70
+ let square;
71
+ let attempted = false;
71
72
  let announcedWait;
72
73
  let landedId;
73
74
  let name = requestedName;
@@ -75,8 +76,12 @@ export async function executeExpression(application, squarePath, requestedName,
75
76
  const interrupt = () => controller.abort();
76
77
  process.once('SIGINT', interrupt);
77
78
  try {
78
- const outcome = await application.express(rawInput.replace(/\r\n/g, '\n').trim(), {
79
- ...opts, reply: opts.reply, requireExplicitReach: true,
79
+ square = await open({ signal: controller.signal });
80
+ const participant = await square.participant(requestedName);
81
+ name = participant.name;
82
+ attempted = true;
83
+ const outcome = await participant.express(rawInput.replace(/\r\n/g, '\n').trim(), {
84
+ ...opts, reply: opts.reply, observe: true,
80
85
  }, {
81
86
  signal: controller.signal,
82
87
  onProgress(progress) {
@@ -86,8 +91,8 @@ export async function executeExpression(application, squarePath, requestedName,
86
91
  announcedWait = progress.reason;
87
92
  },
88
93
  });
89
- landedId = outcome.data.activity.id;
90
- name = outcome.projection.participant;
94
+ landedId = outcome.activity.id;
95
+ name = outcome.observation.participant;
91
96
  process.stdout.write(renderExpression(squarePath, outcome));
92
97
  }
93
98
  catch (error) {
@@ -95,13 +100,14 @@ export async function executeExpression(application, squarePath, requestedName,
95
100
  name = detail?.participant ?? name ?? 'participant';
96
101
  // This MUST precede recovery: even an unreadable artifact cannot eat the raw body.
97
102
  const draftPath = saveActivityDraft(squarePath, name, rawInput);
98
- const recovered = await application.recovery().catch(() => undefined);
99
- const fresh = recovered?.data;
103
+ if (square === undefined)
104
+ square = await open().catch(() => undefined);
105
+ const fresh = await square?.recovery(name).catch(() => undefined);
100
106
  name = fresh?.participant ?? name;
101
107
  const output = { squarePath, name, draftPath, participantCount: fresh?.standingCount, held: fresh?.held };
102
108
  const confirmedUnsent = landedId === undefined && detail?.landedId === undefined
103
109
  && detail?.expressEffect !== 'landed' && detail?.expressEffect !== 'unknown'
104
- && (detail?.expressEffect === 'unsent' || (isSquareError(error) && error.facts?.activityUnsent === true));
110
+ && (!attempted || detail?.expressEffect === 'unsent' || (isSquareError(error) && error.facts?.activityUnsent === true));
105
111
  const retryCommand = activityRetryCommand(squarePath, name, opts);
106
112
  const exitCode = (code) => { process.exitCode = controller.signal.aborted ? 130 : code; };
107
113
  if (!confirmedUnsent) {
@@ -144,29 +150,25 @@ export async function executeExpression(application, squarePath, requestedName,
144
150
  }
145
151
  const facts = isSquareError(error) ? error.facts : undefined;
146
152
  const reason = facts?.reason;
147
- const message = reason === 'explicit_reach_required'
148
- ? '✕ express needs --mention <name>, --no-mention, --bell, or an @name in the body\n · --reply <activity-id> also reaches the author it answers'
149
- : reason === 'bell_mention_conflict'
150
- ? '✕ a bell cannot be combined with a mention'
151
- : reason === 'invalid_reply'
152
- ? facts?.activityExists === false
153
- ? `Invalid --reply: ${facts.activityId ?? opts.reply ?? 'that activity'} is not in this square's history`
154
- : 'Invalid --reply: expected an activity id like act/12'
155
- : refusalText(error);
153
+ const message = reason === 'bell_mention_conflict'
154
+ ? '✕ a bell cannot be combined with a mention'
155
+ : reason === 'invalid_reply'
156
+ ? facts?.activityExists === false
157
+ ? `Invalid --reply: ${facts.activityId ?? opts.reply ?? 'that activity'} is not in this square's history`
158
+ : 'Invalid --reply: expected an activity id like act/12'
159
+ : refusalText(error);
156
160
  const lines = [message, `· draft kept: ${draftPath}`];
157
- const recipientFailure = detail?.subject === 'recipient';
158
- const repair = reason === 'bell_mention_conflict' ? { bell: true, fromBody: true }
159
- : reason === 'not_standing' && recipientFailure ? { bell: false, fromBody: false } : undefined;
161
+ const repair = reason === 'bell_mention_conflict' || (reason === 'not_standing' && detail?.subject === 'recipient');
160
162
  if (isSquareError(error)) {
161
163
  if ((reason === 'never_joined' && detail?.subject === 'caller') || error.code === 'unknown_participant' || error.code === 'not_joined') {
162
164
  lines.push(joinRecoveryCommand(squarePath, name));
163
165
  }
164
166
  if (error.code === 'already_joined')
165
167
  lines.push(...takeoverRecoveryLines(squarePath, name));
166
- if (repair !== undefined) {
167
- lines.push(participantsRecoveryCommand(squarePath), repair.fromBody
168
- ? " · edit the draft first — the body's @name must be removed or wrapped in backticks:"
169
- : ' · pick someone standing here, or land it bare:', `${mentionRepairCommand(squarePath, name, opts, repair.bell)} < ${quoteShell(draftPath)}`);
168
+ if (repair) {
169
+ lines.push(participantsRecoveryCommand(squarePath), reason === 'bell_mention_conflict'
170
+ ? ' · ring the bell without explicit mentions:'
171
+ : ' · retry without explicit recipients:', `${activityRetryCommand(squarePath, name, { ...opts, mentions: [] })} < ${quoteShell(draftPath)}`);
170
172
  }
171
173
  else if (error.code === 'invalid_args' || error.code === 'invalid_name') {
172
174
  if (opts.mentions?.length)
@@ -175,15 +177,11 @@ export async function executeExpression(application, squarePath, requestedName,
175
177
  lines.push(`${commandPrefix(squarePath)} history --limit 10`);
176
178
  }
177
179
  }
178
- if (reason === 'explicit_reach_required') {
179
- lines.push(' · choose someone with --mention <name>, or send without addressing anyone:', `${activityRetryCommand(squarePath, name, { ...opts, noMention: true })} < ${quoteShell(draftPath)}`);
180
- }
181
- else if (reason === 'invalid_reply') {
182
- const hasFlagReach = opts.reach !== undefined || (opts.mentions?.length ?? 0) > 0 || opts.noMention === true;
183
- const withoutReply = { ...opts, reply: undefined, ...(hasFlagReach ? {} : { noMention: true }) };
184
- lines.push(`${commandPrefix(squarePath)} history --limit 10`, ' · choose a valid --reply from history, or send without a reply using this repair:', ...(hasFlagReach ? [] : [' · this repair is bare; replace --no-mention with --mention <name> to address someone']), `${activityRetryCommand(squarePath, name, withoutReply)} < ${quoteShell(draftPath)}`);
180
+ if (reason === 'invalid_reply') {
181
+ const withoutReply = { ...opts, reply: undefined };
182
+ lines.push(`${commandPrefix(squarePath)} history --limit 10`, ' · choose a valid --reply from history, or send without a reply using this repair:', `${activityRetryCommand(squarePath, name, withoutReply)} < ${quoteShell(draftPath)}`);
185
183
  }
186
- else if (repair === undefined) {
184
+ else if (!repair) {
187
185
  lines.push(' · correct the problem above before retrying; keep your intended recipients and reply:', `${retryCommand} < ${quoteShell(draftPath)}`);
188
186
  }
189
187
  process.stderr.write(formatRefusal(squarePath, lines, output));
@@ -191,5 +189,6 @@ export async function executeExpression(application, squarePath, requestedName,
191
189
  }
192
190
  finally {
193
191
  process.off('SIGINT', interrupt);
192
+ await square?.close();
194
193
  }
195
194
  }
@@ -1,6 +1,6 @@
1
- import { createSquareApplication } from '../square-application.js';
1
+ import { diagnoseSquare, migrateSquare } from '../square-api.js';
2
2
  import { commandPrefix, renderDoctorClean, renderDoctorUnfixable, withPathOutput } from '../presentation.js';
3
- import { requireSquarePath, usage } from './context.js';
3
+ import { requireSquarePath, squareInput, usage } from './context.js';
4
4
  export const migrateCommand = {
5
5
  parse(argv, context) {
6
6
  if (argv.length > 0)
@@ -9,14 +9,14 @@ export const migrateCommand = {
9
9
  },
10
10
  async execute(_intent, context) {
11
11
  const squarePath = requireSquarePath(context);
12
- const { data: outcome, projection } = await createSquareApplication({ cwd: context.cwd, env: context.env, squarePath }).migrate();
12
+ const outcome = await migrateSquare(squareInput(context));
13
13
  const body = outcome.status === 'migrated' && outcome.counts !== undefined
14
14
  ? [
15
15
  '✓ migrated to row storage',
16
16
  ` · ${outcome.counts.activities} activities · ${outcome.counts.receipts} receipts · ${outcome.counts.leases} leases · ${outcome.counts.routes} routes`,
17
17
  ].join('\n')
18
18
  : '· already format 2 — nothing changed';
19
- return { output: withPathOutput(squarePath, body, { participantCount: projection.standingCount }) };
19
+ return { output: withPathOutput(squarePath, body, { participantCount: outcome.standing }) };
20
20
  },
21
21
  present(result) {
22
22
  process.stdout.write(result.output);
@@ -30,7 +30,7 @@ export const doctorCommand = {
30
30
  },
31
31
  async execute(_intent, context) {
32
32
  const squarePath = requireSquarePath(context);
33
- const { data: diagnosis, projection } = await createSquareApplication({ cwd: context.cwd, env: context.env, squarePath }).diagnose();
33
+ const diagnosis = await diagnoseSquare(squareInput(context));
34
34
  if (diagnosis.migrationRequired !== undefined) {
35
35
  return { output: withPathOutput(squarePath, `${diagnosis.migrationRequired}\n${commandPrefix(squarePath)} migrate\n`), exitCode: 2 };
36
36
  }
@@ -42,7 +42,7 @@ export const doctorCommand = {
42
42
  }
43
43
  return {
44
44
  output: withPathOutput(squarePath, renderDoctorClean(), {
45
- participantCount: projection.standingCount,
45
+ participantCount: diagnosis.standingCount,
46
46
  }),
47
47
  };
48
48
  },
@@ -7,7 +7,7 @@ export const mcpServerCommand = {
7
7
  },
8
8
  async execute(_intent, context) {
9
9
  const { runSquareMcpStdio } = await import('../mcp.js');
10
- await runSquareMcpStdio({ cwd: context.cwd, env: context.env, squarePath: context.squarePath, participant: context.name });
10
+ await runSquareMcpStdio({ cwd: context.cwd, env: context.env, path: context.squarePath, participant: context.name });
11
11
  },
12
12
  present() { },
13
13
  };
@@ -1,20 +1,20 @@
1
- import type { CatchApplicationOptions, DiscoveryApplicationOptions, HistoryApplicationQuery, StreamStart } from '../square-facade.js';
1
+ import type { CatchOptions, DiscoveryOptions, HistoryQuery, StreamStart } from '../square-facade.js';
2
2
  import { type CommandSpec } from './context.js';
3
3
  interface HistoryCommandOptions {
4
- query: HistoryApplicationQuery;
4
+ query: HistoryQuery;
5
5
  noTruncate: boolean;
6
6
  format?: string[];
7
7
  json: boolean;
8
8
  continuationArgs: string[];
9
9
  }
10
- export declare const listCommand: CommandSpec<DiscoveryApplicationOptions, string>;
10
+ export declare const listCommand: CommandSpec<DiscoveryOptions, string>;
11
11
  interface StreamIntent {
12
12
  ndjson: boolean;
13
13
  forName?: string;
14
14
  start: StreamStart;
15
15
  }
16
16
  export declare const streamCommand: CommandSpec<StreamIntent>;
17
- export declare const catchCommand: CommandSpec<CatchApplicationOptions>;
17
+ export declare const catchCommand: CommandSpec<CatchOptions>;
18
18
  export declare const historyCommand: CommandSpec<HistoryCommandOptions, string>;
19
19
  interface ParticipantsCommandOptions {
20
20
  limit: number;
@@ -6,9 +6,9 @@ import { isSquareError, sameName } from '../model.js';
6
6
  import { commandPrefix, participantIdentity, renderGrepActivitiesView, renderEventCli, renderAmbientEventFacts, renderPresenceAnchor, truncateChars, withPathOutput, quoteShell, } from '../presentation.js';
7
7
  import { style } from '../tty-style.js';
8
8
  import { formatRelativeTime, formatTimestamp, parseTimeOrRelative } from '../time.js';
9
- import { formatActivityId, parseActivityId, CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT, HISTORY_MAX_LIMIT, DEFAULT_LIST_DEPTH, MAX_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT } from '../square-application.js';
9
+ import { discover, inbox, formatActivityId, parseActivityId, CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT, HISTORY_MAX_LIMIT, DEFAULT_LIST_DEPTH, MAX_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT } from '../square-api.js';
10
10
  import { renderCatchOutcome, renderCatchProgress } from './catch-presentation.js';
11
- import { applicationFor as application, fail, parseBoundedLimit, parseDurationMs, parseNameList, parseNonNegativeInteger, readStdin, requireSquarePath, requireValue, usage, } from './context.js';
11
+ import { withSquare, squareInput, now, fail, parseBoundedLimit, parseDurationMs, parseNameList, parseNonNegativeInteger, readStdin, requireSquarePath, requireValue, usage, } from './context.js';
12
12
  const STATUS_PARTICIPANT_PREVIEW_LIMIT = 10;
13
13
  const PARTICIPANTS_DEFAULT_LIMIT = 20;
14
14
  const PARTICIPANTS_MAX_LIMIT = 100;
@@ -80,8 +80,8 @@ export const listCommand = {
80
80
  return { depth, limit, after };
81
81
  },
82
82
  async execute(intent, context) {
83
- const outcome = await application(context).discover(intent);
84
- return renderSquareList(outcome.data, outcome.projection);
83
+ const result = await discover(squareInput(context), intent);
84
+ return renderSquareList(result, { now: now(context), depth: intent.depth ?? DEFAULT_LIST_DEPTH, limit: intent.limit ?? DEFAULT_LIST_LIMIT });
85
85
  },
86
86
  present: (result) => process.stdout.write(result),
87
87
  };
@@ -132,10 +132,12 @@ export const streamCommand = {
132
132
  const interrupt = () => controller.abort();
133
133
  process.once('SIGINT', interrupt);
134
134
  try {
135
- for await (const record of application(context).stream({ recipient: intent.forName, start: intent.start }, { signal: controller.signal })) {
136
- if (!process.stdout.write(`${JSON.stringify(record)}\n`))
137
- await once(process.stdout, 'drain', { signal: controller.signal });
138
- }
135
+ await withSquare(context, async (square) => {
136
+ for await (const record of square.stream({ recipient: intent.forName, start: intent.start }, { signal: controller.signal })) {
137
+ if (!process.stdout.write(`${JSON.stringify(record)}\n`))
138
+ await once(process.stdout, 'drain', { signal: controller.signal });
139
+ }
140
+ }, { signal: controller.signal });
139
141
  }
140
142
  catch (error) {
141
143
  if (controller.signal.aborted)
@@ -218,14 +220,14 @@ export const catchCommand = {
218
220
  const interrupt = () => controller.abort();
219
221
  process.once('SIGINT', interrupt);
220
222
  try {
221
- const outcome = await application(context).catch(intent, {
223
+ const outcome = await withSquare(context, async (square) => (await square.participant(context.name)).catch({ ...intent, observe: true }, {
222
224
  signal: controller.signal,
223
225
  onProgress(progress) {
224
226
  const line = renderCatchProgress(squarePath, progress);
225
227
  if (line !== undefined)
226
228
  process.stderr.write(line);
227
229
  },
228
- });
230
+ }), { signal: controller.signal });
229
231
  const rendered = renderCatchOutcome(squarePath, outcome, intent);
230
232
  process.stdout.write(rendered.stdout);
231
233
  process.exitCode = rendered.exitCode;
@@ -301,7 +303,7 @@ function parseHistory(argv, context) {
301
303
  index += 1;
302
304
  }
303
305
  else if (flag === '--since') {
304
- since = parseTimestamp(requireValue(argv, index, flag), flag, application(context).now());
306
+ since = parseTimestamp(requireValue(argv, index, flag), flag, now(context));
305
307
  index += 1;
306
308
  }
307
309
  else if (flag === '--after') {
@@ -446,7 +448,7 @@ export const historyCommand = {
446
448
  const squarePath = requireSquarePath(context);
447
449
  let outcome;
448
450
  try {
449
- outcome = await application(context).history(options.query);
451
+ outcome = await withSquare(context, (square) => square.history({ ...options.query, observe: true }));
450
452
  }
451
453
  catch (error) {
452
454
  const bounds = error?.operation?.historyBounds;
@@ -454,7 +456,7 @@ export const historyCommand = {
454
456
  fail(`✕ history is capped at ${bounds.maxLimit} activities\n${historyContinuationCommand(options, squarePath, ['--limit', String(bounds.maxLimit)])}`);
455
457
  throw error;
456
458
  }
457
- const projection = outcome.projection;
459
+ const projection = outcome.observation;
458
460
  const events = projection.activities;
459
461
  if (options.json)
460
462
  return events.map((item) => jsonLine(projection.sayNumbers, item)).join('\n') + (events.length > 0 ? '\n' : '');
@@ -488,7 +490,8 @@ export const participantsCommand = {
488
490
  parse(argv, context) { return parseParticipants(argv, context); },
489
491
  async execute(intent, context) {
490
492
  const squarePath = requireSquarePath(context);
491
- const { data, projection } = await application(context).participants();
493
+ const data = await withSquare(context, (square) => square.participants({ observe: true }));
494
+ const projection = data.observation;
492
495
  const now = projection.now;
493
496
  const participants = data.participants;
494
497
  const lines = participants.slice(0, intent.limit).map((participant) => {
@@ -516,7 +519,8 @@ export const statusCommand = {
516
519
  usage(context.command); return undefined; },
517
520
  async execute(_intent, context) {
518
521
  const squarePath = requireSquarePath(context);
519
- const { data: result, projection: presentation } = await application(context).status(context.name);
522
+ const result = await withSquare(context, (square) => square.status({ viewer: context.name, observe: true }));
523
+ const presentation = result.observation;
520
524
  const participantsByName = new Map(result.participants.map((participant) => [participant.name, participant]));
521
525
  const orderedHere = presentation.orderedHereNames.map((name) => participantsByName.get(name));
522
526
  const lingering = presentation.lingeringCount;
@@ -639,7 +643,7 @@ export const inboxCommand = {
639
643
  return { sessionId, json, limit };
640
644
  },
641
645
  async execute(intent, context) {
642
- const { data } = await application(context).inbox(intent.sessionId, { limit: intent.limit });
646
+ const data = await inbox(squareInput(context), intent.sessionId, { limit: intent.limit });
643
647
  const rows = data.rows.map((membership) => ({
644
648
  namePreview: inboxDisplay(membership.name),
645
649
  squarePathPreview: inboxDisplay(membership.squarePath),
@@ -1,2 +1,2 @@
1
- /** Parse global flags, select an executable adapter, and leave operations to the application. */
1
+ /** Parse global flags, select an adapter, and execute through Square/Participant. */
2
2
  export declare function runCli(rawArgs?: string[]): Promise<void>;