@astrosheep/square 0.3.12 → 0.3.13

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 (121) hide show
  1. package/codex-plugin/.codex-plugin/plugin.json +1 -1
  2. package/codex-plugin/hooks/hooks.json +9 -0
  3. package/dist/activity-feed.d.ts +14 -0
  4. package/dist/activity-feed.js +5 -5
  5. package/dist/activity.d.ts +9 -0
  6. package/dist/activity.js +111 -90
  7. package/dist/artifact.d.ts +20 -0
  8. package/dist/artifact.js +10 -29
  9. package/dist/automatic-session.d.ts +5 -0
  10. package/dist/automatic-session.js +86 -0
  11. package/dist/boundary-presentation.d.ts +5 -0
  12. package/dist/boundary-presentation.js +5 -4
  13. package/dist/claude-hook.d.ts +9 -0
  14. package/dist/claude-hook.js +35 -3
  15. package/dist/cli/context.d.ts +35 -0
  16. package/dist/cli/context.js +25 -30
  17. package/dist/cli/harness-command.d.ts +28 -0
  18. package/dist/cli/harness-command.js +29 -14
  19. package/dist/cli/maintenance-commands.d.ts +7 -0
  20. package/dist/cli/maintenance-commands.js +3 -3
  21. package/dist/cli/meta-commands.d.ts +5 -0
  22. package/dist/cli/observation-commands.d.ts +20 -0
  23. package/dist/cli/observation-commands.js +155 -128
  24. package/dist/cli/program.d.ts +2 -0
  25. package/dist/cli/program.js +2 -12
  26. package/dist/cli/registry.d.ts +9 -0
  27. package/dist/cli/registry.js +1 -2
  28. package/dist/cli/square-commands.d.ts +34 -0
  29. package/dist/cli/square-commands.js +81 -74
  30. package/dist/cmd/notify-once.d.ts +2 -0
  31. package/dist/codex-hook.d.ts +10 -0
  32. package/dist/codex-hook.js +35 -3
  33. package/dist/decisions.d.ts +92 -0
  34. package/dist/decisions.js +83 -77
  35. package/dist/delivery-health.d.ts +21 -0
  36. package/dist/delivery-health.js +40 -34
  37. package/dist/delivery.d.ts +81 -0
  38. package/dist/delivery.js +60 -22
  39. package/dist/file-lock.d.ts +6 -0
  40. package/dist/harness-claude.d.ts +19 -0
  41. package/dist/harness-claude.js +8 -3
  42. package/dist/harness-codex.d.ts +29 -0
  43. package/dist/harness-codex.js +2 -2
  44. package/dist/harness-links.d.ts +17 -0
  45. package/dist/harness-links.js +2 -2
  46. package/dist/harness-pi.d.ts +11 -0
  47. package/dist/harness-pi.js +2 -2
  48. package/dist/harness-stage.d.ts +6 -0
  49. package/dist/harness.d.ts +20 -0
  50. package/dist/harness.js +5 -3
  51. package/dist/help.d.ts +6 -0
  52. package/dist/help.js +6 -5
  53. package/dist/identity.d.ts +13 -0
  54. package/dist/inbox.d.ts +2 -0
  55. package/dist/inbox.js +15 -19
  56. package/dist/index.d.ts +13 -0
  57. package/dist/index.js +31 -69
  58. package/dist/landing.d.ts +10 -0
  59. package/dist/landing.js +95 -0
  60. package/dist/list.d.ts +1 -0
  61. package/dist/list.js +40 -18
  62. package/dist/model.d.ts +147 -0
  63. package/dist/model.js +12 -2
  64. package/dist/notifications.d.ts +30 -0
  65. package/dist/notifications.js +75 -91
  66. package/dist/open-square.d.ts +11 -0
  67. package/dist/open-square.js +4 -0
  68. package/dist/participant-identity.d.ts +1 -0
  69. package/dist/participant-identity.js +3 -0
  70. package/dist/paseo-connection.d.ts +15 -0
  71. package/dist/paseo-delivery.d.ts +14 -0
  72. package/dist/paseo-state.d.ts +11 -0
  73. package/dist/paseo-timeline.d.ts +16 -0
  74. package/dist/presence.d.ts +3 -0
  75. package/dist/presence.js +42 -0
  76. package/dist/presentation.d.ts +91 -0
  77. package/dist/presentation.js +33 -29
  78. package/dist/presented.d.ts +11 -0
  79. package/dist/registry.d.ts +58 -0
  80. package/dist/registry.js +36 -3
  81. package/dist/routes.d.ts +42 -0
  82. package/dist/runtime.d.ts +45 -0
  83. package/dist/runtime.js +42 -34
  84. package/dist/search.d.ts +11 -0
  85. package/dist/square-core.d.ts +111 -0
  86. package/dist/square-core.js +7 -0
  87. package/dist/square-facade.d.ts +86 -0
  88. package/dist/square-facade.js +1 -0
  89. package/dist/square-file-adapter.d.ts +18 -0
  90. package/dist/square-file-adapter.js +70 -0
  91. package/dist/square-storage.d.ts +20 -0
  92. package/dist/square-storage.js +171 -0
  93. package/dist/square-wiring.d.ts +19 -0
  94. package/dist/square-wiring.js +70 -0
  95. package/dist/square.d.ts +2 -0
  96. package/dist/state-cell.d.ts +14 -0
  97. package/dist/state-cell.js +1 -0
  98. package/dist/stream.d.ts +3 -0
  99. package/dist/stream.js +19 -23
  100. package/dist/time.d.ts +6 -0
  101. package/dist/views.d.ts +95 -0
  102. package/dist/views.js +81 -0
  103. package/dist/wake-attempts.d.ts +39 -0
  104. package/dist/wake-evidence.d.ts +12 -0
  105. package/dist/wake-evidence.js +7 -10
  106. package/dist/wake-port.d.ts +20 -0
  107. package/dist/wake-sink.d.ts +12 -0
  108. package/dist/wakes.d.ts +40 -0
  109. package/dist/wakes.js +62 -0
  110. package/dist/watch.d.ts +2 -0
  111. package/dist/watch.js +89 -210
  112. package/extensions/square-opencode.js +27 -1
  113. package/extensions/square-pi.js +13 -0
  114. package/package.json +5 -2
  115. package/skills/brainstorm/SKILL.md +26 -26
  116. package/skills/square/.claude-plugin/plugin.json +1 -1
  117. package/skills/square/SKILL.md +47 -29
  118. package/skills/square/hooks/hooks.json +9 -0
  119. package/skills/square-feedback/SKILL.md +2 -2
  120. package/dist/compact.js +0 -26
  121. package/dist/square-application.js +0 -193
@@ -1,22 +1,22 @@
1
- import { loadSquare } from '../artifact.js';
2
- import { runClaudeHook } from '../claude-hook.js';
3
- import { runCodexHook } from '../codex-hook.js';
4
- import { coreActivities, coreParticipants, coreStatus } from '../decisions.js';
1
+ import { runClaudeHookAsync } from '../claude-hook.js';
2
+ import { runCodexHookAsync } from '../codex-hook.js';
5
3
  import { sessionInbox } from '../inbox.js';
6
4
  import { sweepPendingNotifications } from '../notifications.js';
7
5
  import { cmdListSquares } from '../list.js';
8
- import { sameName } from '../model.js';
9
- import { parseActivityId } from '../square-core.js';
10
- import { commandPrefix, participantCommandPrefix, renderActivitiesView, renderGrepActivitiesView, renderAmbientEvent, withPathOutput, } from '../presentation.js';
11
- import { actId, inSquareCount, nowMs, sayNumberFor } from '../runtime.js';
6
+ import { parseActivityId, sameName } from '../model.js';
7
+ import { commandPrefix, participantCommandPrefix, participantIdentity, renderGrepActivitiesView, renderEventCli, renderAmbientEvent, withPathOutput, } from '../presentation.js';
8
+ import { actId, nowMs } from '../runtime.js';
12
9
  import { cmdStream, cmdStreamNdjson } from '../stream.js';
13
10
  import { formatRelativeTime, formatTimestamp, parseTimeOrRelative } from '../time.js';
14
11
  import { cmdWatch } from '../watch.js';
12
+ import { openSquare } from '../square-file-adapter.js';
13
+ import { closeOpenSquare } from '../open-square.js';
14
+ import { historyPresentation, participantsPresentation, statusPresentation } from '../views.js';
15
15
  import { fail, parseDurationMs, parseNameList, parseNonNegativeInteger, readStdinSync, requireParticipant, requireValue, usage, } from './context.js';
16
16
  export const listCommand = {
17
17
  parse: (argv) => argv,
18
- execute(argv, context) {
19
- cmdListSquares(argv, () => usage(context.command));
18
+ async execute(argv, context) {
19
+ await cmdListSquares(argv, () => usage(context.command));
20
20
  },
21
21
  present: () => { },
22
22
  };
@@ -117,7 +117,7 @@ function parseHistory(argv, context) {
117
117
  let before;
118
118
  let after;
119
119
  let afterIndex;
120
- let atIndex;
120
+ const atIndexes = [];
121
121
  let beforeContext;
122
122
  let afterContext;
123
123
  let mention;
@@ -125,10 +125,8 @@ function parseHistory(argv, context) {
125
125
  let full = false;
126
126
  let grep;
127
127
  let fixed;
128
- let ids;
129
128
  let order;
130
129
  let format;
131
- let countOnly = false;
132
130
  let json = false;
133
131
  const participants = [];
134
132
  for (let index = 0; index < argv.length; index++) {
@@ -166,7 +164,10 @@ function parseHistory(argv, context) {
166
164
  index += 1;
167
165
  }
168
166
  else if (flag === '--at') {
169
- atIndex = parseActRef(requireValue(argv, index, flag), flag);
167
+ const values = requireValue(argv, index, flag).split(',');
168
+ if (values.some((value) => value === ''))
169
+ fail(`Invalid ${flag}: expected an activity id like act/12`);
170
+ atIndexes.push(...values.map((value) => parseActRef(value, flag)));
170
171
  index += 1;
171
172
  }
172
173
  else if (flag === '-B') {
@@ -199,14 +200,6 @@ function parseHistory(argv, context) {
199
200
  fixed = requireValue(argv, index, flag);
200
201
  index += 1;
201
202
  }
202
- else if (flag === '--ids') {
203
- ids = requireValue(argv, index, flag)
204
- .split(',')
205
- .map((item) => item.trim())
206
- .filter(Boolean)
207
- .map((item) => parseActRef(item, flag));
208
- index += 1;
209
- }
210
203
  else if (flag === '--order') {
211
204
  const value = requireValue(argv, index, flag);
212
205
  if (value !== 'asc' && value !== 'desc')
@@ -218,8 +211,6 @@ function parseHistory(argv, context) {
218
211
  format = requireValue(argv, index, flag).split(',').map((item) => item.trim()).filter(Boolean);
219
212
  index += 1;
220
213
  }
221
- else if (flag === '--count')
222
- countOnly = true;
223
214
  else if (flag === '--json')
224
215
  json = true;
225
216
  else
@@ -231,7 +222,7 @@ function parseHistory(argv, context) {
231
222
  fail('--grep and --fixed cannot be combined.');
232
223
  if (grep === '' || fixed === '')
233
224
  fail('--grep and --fixed require non-empty text.');
234
- if (!lastNExplicit && (atIndex !== undefined || ids !== undefined || pending))
225
+ if (!lastNExplicit && (atIndexes.length > 0 || pending))
235
226
  lastN = null;
236
227
  return {
237
228
  lastN,
@@ -239,7 +230,7 @@ function parseHistory(argv, context) {
239
230
  before,
240
231
  after,
241
232
  afterIndex,
242
- atIndex,
233
+ atIndexes: atIndexes.length === 0 ? undefined : atIndexes,
243
234
  beforeContext,
244
235
  afterContext,
245
236
  mention,
@@ -248,14 +239,12 @@ function parseHistory(argv, context) {
248
239
  full,
249
240
  grep,
250
241
  fixed,
251
- ids,
252
242
  order,
253
243
  format,
254
- countOnly,
255
244
  json,
256
245
  };
257
246
  }
258
- function renderFields(doc, item, fields) {
247
+ function renderFields(sayNumbers, item, fields) {
259
248
  return fields.map((field) => {
260
249
  switch (field) {
261
250
  case 'id': return actId(item.index);
@@ -265,13 +254,13 @@ function renderFields(doc, item, fields) {
265
254
  case 'at': return formatTimestamp(item.at);
266
255
  case 'kind': return item.kind;
267
256
  case 'body': return 'body' in item && typeof item.body === 'string' ? item.body.replace(/\s+/g, ' ').trim() : '';
268
- case 'number': return item.kind === 'say' ? String(sayNumberFor(doc.acts, item)) : '';
257
+ case 'number': return item.kind === 'say' ? String(sayNumbers[item.index]) : '';
269
258
  case 'reply': return item.kind === 'say' && item.reply !== undefined ? actId(item.reply) : '';
270
259
  default: return '';
271
260
  }
272
261
  }).join('\t');
273
262
  }
274
- function jsonLine(doc, item) {
263
+ function jsonLine(sayNumbers, item) {
275
264
  const act = item;
276
265
  return JSON.stringify({
277
266
  id: actId(item.index),
@@ -281,122 +270,160 @@ function jsonLine(doc, item) {
281
270
  at: act.at,
282
271
  ts: formatTimestamp(act.at),
283
272
  body: 'body' in act && typeof act.body === 'string' ? act.body : '',
284
- number: act.kind === 'say' ? sayNumberFor(doc.acts, act) : null,
273
+ number: act.kind === 'say' ? sayNumbers[act.index] : null,
285
274
  reach: act.kind === 'say' ? act.reach ?? null : null,
286
275
  reply: act.kind === 'say' && act.reply !== undefined ? actId(act.reply) : null,
287
276
  });
288
277
  }
278
+ function renderHistoryProjection(projection, visible, full, squarePath, viewer, mode) {
279
+ const shown = visible.filter((activity) => activity.kind === 'say' || activity.kind === 'done');
280
+ const preview = full ? undefined : 200;
281
+ const chunks = [];
282
+ for (const activity of shown) {
283
+ const options = {
284
+ preview,
285
+ actNumber: activity.kind === 'say' ? projection.sayNumbers[activity.index] : undefined,
286
+ };
287
+ const rendered = mode === 'archive'
288
+ ? renderEventCli(activity, options)
289
+ : renderAmbientEvent(activity, viewer, options);
290
+ if (rendered !== '')
291
+ chunks.push(rendered);
292
+ for (const name of projection.presenceAnchors[activity.index] ?? [])
293
+ chunks.push(`→ ${participantIdentity(name)} was here`);
294
+ }
295
+ if (chunks.length === 0)
296
+ return 'latest\n ○ no public activity in this view';
297
+ if (preview !== undefined && shown.some((activity) => activity.kind === 'say' && activity.body.length > preview && (mode === 'archive' || activity.perception === 'full'))) {
298
+ chunks.push(`» ${commandPrefix(squarePath)} history --full`);
299
+ }
300
+ return chunks.join('\n\n');
301
+ }
289
302
  export const historyCommand = {
290
303
  parse(argv, context) { return parseHistory(argv, context); },
291
- execute(options, context) {
292
- const doc = loadSquare(context.squarePath);
293
- let events = coreActivities(doc, options);
294
- const searching = options.grep !== undefined || options.fixed !== undefined;
295
- const totalMatches = searching ? events.length : 0;
296
- if (options.lastN != null) {
297
- events = options.order === 'desc'
298
- ? events.slice(0, options.lastN)
299
- : events.slice(-options.lastN);
304
+ async execute(options, context) {
305
+ const square = await openSquare(context.squarePath, { clock: nowMs });
306
+ try {
307
+ const projection = await historyPresentation(square, options);
308
+ let events = [...projection.activities];
309
+ const searching = options.grep !== undefined || options.fixed !== undefined;
310
+ const totalMatches = searching ? events.length : 0;
311
+ if (options.lastN != null) {
312
+ events = options.order === 'desc'
313
+ ? events.slice(0, options.lastN)
314
+ : events.slice(-options.lastN);
315
+ }
316
+ if (options.json)
317
+ return events.map((item) => jsonLine(projection.sayNumbers, item)).join('\n') + (events.length > 0 ? '\n' : '');
318
+ if (options.format !== undefined && options.format.length > 0) {
319
+ return events.map((item) => renderFields(projection.sayNumbers, item, options.format)).join('\n') + (events.length > 0 ? '\n' : '');
320
+ }
321
+ const pattern = options.grep ?? options.fixed;
322
+ const anonymous = options.viewer === undefined;
323
+ const archive = (options.atIndexes !== undefined && options.atIndexes.length > 0)
324
+ || (options.lastN == null && options.full === true)
325
+ || anonymous;
326
+ const output = pattern === undefined || pattern === ''
327
+ ? renderHistoryProjection(projection, events, options.full === true || anonymous, context.squarePath, options.viewer ?? '', archive ? 'archive' : 'ambient')
328
+ : renderGrepActivitiesView(events, totalMatches, options.full, context.squarePath, pattern, options.fixed !== undefined);
329
+ return withPathOutput(context.squarePath, output, { participantCount: projection.participantCount });
300
330
  }
301
- if (options.countOnly)
302
- return `${searching ? totalMatches : events.length}\n`;
303
- if (options.json)
304
- return events.map((item) => jsonLine(doc, item)).join('\n') + (events.length > 0 ? '\n' : '');
305
- if (options.format !== undefined && options.format.length > 0) {
306
- return events.map((item) => renderFields(doc, item, options.format)).join('\n') + (events.length > 0 ? '\n' : '');
331
+ finally {
332
+ await closeOpenSquare(square);
307
333
  }
308
- const pattern = options.grep ?? options.fixed;
309
- const archive = options.atIndex != null
310
- || (options.ids !== undefined && options.ids.length > 0)
311
- || (options.lastN == null && options.full === true);
312
- const output = pattern === undefined || pattern === ''
313
- ? renderActivitiesView(doc, events, null, options.full, context.squarePath, options.viewer ?? '', archive ? 'archive' : 'ambient')
314
- : renderGrepActivitiesView(events, totalMatches, options.full, context.squarePath, pattern, options.fixed !== undefined);
315
- return withPathOutput(context.squarePath, output, { participantCount: inSquareCount(doc) });
316
334
  },
317
335
  present: (result) => process.stdout.write(result),
318
336
  };
319
337
  export const participantsCommand = {
320
338
  parse(argv, context) { if (argv.length > 0)
321
339
  usage(context.command); return undefined; },
322
- execute(_intent, context) {
323
- const doc = loadSquare(context.squarePath);
324
- const now = nowMs();
325
- const participants = coreParticipants(doc, now);
326
- const lines = participants.map((participant) => {
327
- const glyph = participant.state === 'done' ? '×' : participant.presence === 'watching' ? '◎' : participant.activityCount > 0 ? '●' : '○';
328
- const state = participant.state === 'done' ? 'done' : participant.presence === 'watching' ? 'catching' : participant.state;
329
- const last = participant.lastActiveAt === undefined ? '' : formatRelativeTime(participant.lastActiveAt, now);
330
- return ` ${glyph} ${participant.name} · ${state} · ${participant.activityCount} ${participant.activityCount === 1 ? 'activity' : 'activities'} · ${last}`;
331
- });
332
- const participantCount = participants.filter((participant) => participant.state === 'active').length;
333
- return withPathOutput(context.squarePath, ['participants', ...lines].join('\n'), {
334
- participantCount,
335
- });
340
+ async execute(_intent, context) {
341
+ const square = await openSquare(context.squarePath, { clock: nowMs });
342
+ try {
343
+ const now = nowMs();
344
+ const participants = await participantsPresentation(square);
345
+ const lines = participants.map((participant) => {
346
+ const glyph = participant.state === 'done' ? '' : participant.presence === 'watching' ? '' : participant.activityCount > 0 ? '●' : '○';
347
+ const state = participant.state === 'done' ? 'done' : participant.presence === 'watching' ? 'catching' : participant.state;
348
+ const last = participant.lastActiveAt === undefined ? '' : formatRelativeTime(participant.lastActiveAt, now);
349
+ return ` ${glyph} ${participantIdentity(participant.name)} · ${state} · ${participant.activityCount} ${participant.activityCount === 1 ? 'activity' : 'activities'} · ${last}`;
350
+ });
351
+ const participantCount = participants.filter((participant) => participant.state === 'active').length;
352
+ return withPathOutput(context.squarePath, ['participants', ...lines].join('\n'), {
353
+ participantCount,
354
+ });
355
+ }
356
+ finally {
357
+ await closeOpenSquare(square);
358
+ }
336
359
  },
337
360
  present: (result) => process.stdout.write(result),
338
361
  };
339
362
  export const statusCommand = {
340
363
  parse(argv, context) { if (argv.length > 0)
341
364
  usage(context.command); return undefined; },
342
- execute(_intent, context) {
343
- const doc = loadSquare(context.squarePath);
344
- const result = coreStatus(doc, nowMs());
345
- const active = result.participants.filter((participant) => participant.state === 'active').sort((a, b) => {
346
- const aViewer = context.name !== undefined && sameName(a.name, context.name);
347
- const bViewer = context.name !== undefined && sameName(b.name, context.name);
348
- if (aViewer !== bViewer)
349
- return aViewer ? -1 : 1;
350
- return (b.lastActiveAt ?? -Infinity) - (a.lastActiveAt ?? -Infinity) || a.name.localeCompare(b.name);
351
- });
352
- const people = active.length === 0 ? [' ○ nobody in the square'] : active.map((participant) => {
353
- const glyph = participant.presence === 'watching'
354
- ? ''
355
- : participant.activityCount > 0 ? '' : '○';
356
- const summary = participant.activityCount > 0
357
- ? `${participant.activityCount} ${participant.activityCount === 1 ? 'activity' : 'activities'} · ${participant.lastActiveAt === undefined
365
+ async execute(_intent, context) {
366
+ const square = await openSquare(context.squarePath, { clock: nowMs });
367
+ try {
368
+ const presentation = await statusPresentation(square);
369
+ const result = presentation.status;
370
+ const active = result.participants.filter((participant) => participant.state === 'active').sort((a, b) => {
371
+ const aViewer = context.name !== undefined && sameName(a.name, context.name);
372
+ const bViewer = context.name !== undefined && sameName(b.name, context.name);
373
+ if (aViewer !== bViewer)
374
+ return aViewer ? -1 : 1;
375
+ return (b.lastActiveAt ?? -Infinity) - (a.lastActiveAt ?? -Infinity) || a.name.localeCompare(b.name);
376
+ });
377
+ const people = active.length === 0 ? [' ○ nobody in the square'] : active.map((participant) => {
378
+ const glyph = participant.presence === 'watching'
379
+ ? '◎'
380
+ : participant.activityCount > 0 ? '' : '';
381
+ const summary = participant.activityCount > 0
382
+ ? `${participant.activityCount} ${participant.activityCount === 1 ? 'activity' : 'activities'} · ${participant.lastActiveAt === undefined
383
+ ? 'just now'
384
+ : formatRelativeTime(participant.lastActiveAt, result.now)}`
385
+ : `quiet · ${participant.lastActiveAt === undefined ? '—' : formatRelativeTime(participant.lastActiveAt, result.now)}`;
386
+ const showAttention = context.name === undefined || sameName(participant.name, context.name);
387
+ const attention = !showAttention
388
+ ? ''
389
+ : participant.pendingMentionCount > 0
390
+ ? `${participant.pendingMentionCount} mention${participant.pendingMentionCount === 1 ? '' : 's'} waiting`
391
+ : participant.unreadActivityCount > 0
392
+ ? `${participant.unreadActivityCount} change${participant.unreadActivityCount === 1 ? '' : 's'} waiting`
393
+ : 'caught up';
394
+ return ` ${glyph} ${participantIdentity(participant.name)} · ${summary}${attention === '' ? '' : ` · ${attention}`}`;
395
+ });
396
+ const cap = result.hardCap === null ? 'unlimited' : String(result.hardCap);
397
+ const hold = result.holdActive
398
+ ? `· ${result.holdActor === undefined ? 'someone' : participantIdentity(result.holdActor)} raised a hand${result.holdReason ? ` — ${result.holdReason}` : ''} · ${result.holdAt === undefined
358
399
  ? 'just now'
359
- : formatRelativeTime(participant.lastActiveAt, result.now)}`
360
- : `quiet · ${participant.lastActiveAt === undefined ? '—' : formatRelativeTime(participant.lastActiveAt, result.now)}`;
361
- const showAttention = context.name === undefined || sameName(participant.name, context.name);
362
- const attention = !showAttention
400
+ : formatRelativeTime(result.holdAt, result.now)}`
401
+ : undefined;
402
+ const visible = result.latestAct === undefined
363
403
  ? ''
364
- : participant.pendingMentionCount > 0
365
- ? `${participant.pendingMentionCount} mention${participant.pendingMentionCount === 1 ? '' : 's'} waiting`
366
- : participant.unreadActivityCount > 0
367
- ? `${participant.unreadActivityCount} change${participant.unreadActivityCount === 1 ? '' : 's'} waiting`
368
- : 'caught up';
369
- return ` ${glyph} ${participant.name} · ${summary}${attention === '' ? '' : ` · ${attention}`}`;
370
- });
371
- const cap = result.hardCap === null ? 'unlimited' : String(result.hardCap);
372
- const hold = result.holdActive
373
- ? ${result.holdActor ?? 'someone'} raised a hand${result.holdReason ? ` — ${result.holdReason}` : ''} · ${result.holdAt === undefined
374
- ? 'just now'
375
- : formatRelativeTime(result.holdAt, result.now)}`
376
- : undefined;
377
- const visible = result.latestAct === undefined
378
- ? ''
379
- : renderAmbientEvent(result.latestAct, context.name ?? '', {
380
- now: result.now,
381
- preview: 200,
382
- actNumber: result.latestAct.kind === 'say'
383
- ? sayNumberFor(doc.acts, result.latestAct)
384
- : undefined,
385
- });
386
- const latest = visible === ''
387
- ? [result.latestAct === undefined
388
- ? ' ○ no public activity yet'
389
- : ' · latest activity is private to another participant']
390
- : [` ${visible.replace(/\n/g, '\n ')}`];
391
- if (visible.includes('more chars') && result.latestAct !== undefined) {
392
- const prefix = context.name === undefined ? commandPrefix(context.squarePath) : participantCommandPrefix(context.squarePath, context.name);
393
- latest.push(`» ${prefix} history --at ${actId(result.latestAct)} -C 2 --full`);
404
+ : renderAmbientEvent(result.latestAct, context.name ?? '', {
405
+ now: result.now,
406
+ preview: 200,
407
+ actNumber: presentation.latestActNumber,
408
+ });
409
+ const latest = visible === ''
410
+ ? [result.latestAct === undefined
411
+ ? ' ○ no public activity yet'
412
+ : ' · latest activity is private to another participant']
413
+ : [` ${visible.replace(/\n/g, '\n ')}`];
414
+ if (visible.includes('more chars') && result.latestAct !== undefined) {
415
+ const prefix = context.name === undefined ? commandPrefix(context.squarePath) : participantCommandPrefix(context.squarePath, context.name);
416
+ latest.push(`» ${prefix} history --at ${actId(result.latestAct)} -C 2 --full`);
417
+ }
418
+ const output = [
419
+ `${result.activeCount} active · ${result.doneCount} done · cap ${cap} · throttle ${result.throttlePerMinute === undefined ? 'none' : `${result.throttlePerMinute}/min`}`,
420
+ ...(hold === undefined ? [] : ['', hold]), '', 'around the square', ...people, '', 'latest', ...latest,
421
+ ].join('\n');
422
+ return withPathOutput(context.squarePath, output, { participantCount: result.activeCount, held: result.holdActive });
423
+ }
424
+ finally {
425
+ await closeOpenSquare(square);
394
426
  }
395
- const output = [
396
- `${result.activeCount} active · ${result.doneCount} done · cap ${cap} · throttle ${result.throttlePerMinute === undefined ? 'none' : `${result.throttlePerMinute}/min`}`,
397
- ...(hold === undefined ? [] : ['', hold]), '', 'around the square', ...people, '', 'latest', ...latest,
398
- ].join('\n');
399
- return withPathOutput(context.squarePath, output, { participantCount: result.activeCount, held: result.holdActive });
400
427
  },
401
428
  present: (result) => process.stdout.write(result),
402
429
  };
@@ -418,8 +445,8 @@ export const inboxCommand = {
418
445
  fail('inbox requires --for-session <session-id>.');
419
446
  return { sessionId, json };
420
447
  },
421
- execute(intent) {
422
- const inbox = sessionInbox(intent.sessionId);
448
+ async execute(intent) {
449
+ const inbox = await sessionInbox(intent.sessionId);
423
450
  return intent.json ? `${JSON.stringify(inbox)}\n` : inbox.map((membership) => `${membership.name}\t${membership.squarePath}\t${membership.notifications.length}\n`).join('');
424
451
  },
425
452
  present: (result) => process.stdout.write(result),
@@ -432,5 +459,5 @@ function hookCommand(runHook) {
432
459
  present: (result) => process.stdout.write(result),
433
460
  };
434
461
  }
435
- export const claudeHookCommand = hookCommand(runClaudeHook);
436
- export const codexHookCommand = hookCommand(runCodexHook);
462
+ export const claudeHookCommand = hookCommand(runClaudeHookAsync);
463
+ export const codexHookCommand = hookCommand(runCodexHookAsync);
@@ -0,0 +1,2 @@
1
+ /** Parse global flags, select an executable adapter, and leave all command work to the registry. */
2
+ export declare function runCli(rawArgs?: string[]): Promise<void>;
@@ -1,15 +1,9 @@
1
1
  import { helpRequest } from '../help.js';
2
- import { SquareError } from '../model.js';
2
+ import { isSquareError } from '../model.js';
3
3
  import { defaultContext, parseGlobalArgs } from './context.js';
4
4
  import { executeRegisteredCommand, findCommand } from './registry.js';
5
- function isMutatingCommand(command, argv) {
6
- void argv;
7
- if (['build', 'join', 'catch', 'express', 'done', 'hold', 'resume', 'compact'].includes(command))
8
- return true;
9
- return false;
10
- }
11
5
  function handleSquareError(error) {
12
- if (error instanceof SquareError) {
6
+ if (isSquareError(error)) {
13
7
  process.stderr.write(`${error.message}\n`);
14
8
  process.exit(error.code === 'not_found' ? 1 : 2);
15
9
  }
@@ -33,10 +27,6 @@ export async function runCli(rawArgs = process.argv.slice(2)) {
33
27
  process.stderr.write(`unknown command: ${command}\nrun 'square' for usage\n`);
34
28
  process.exit(2);
35
29
  }
36
- if (!parsed.explicitSquarePath && parsed.multipleSquares && isMutatingCommand(command, parsed.args.slice(1))) {
37
- process.stderr.write('✕ more than one square is active here; choose the path before changing or consuming activity.\n» square list\n');
38
- process.exit(2);
39
- }
40
30
  await executeRegisteredCommand(command, parsed.args.slice(1), defaultContext(command, parsed.squarePath, parsed.name));
41
31
  }
42
32
  catch (error) {
@@ -0,0 +1,9 @@
1
+ import { type CommandContext, type CommandSpec } from './context.js';
2
+ export interface RegisteredCommand {
3
+ names: readonly string[];
4
+ spec: CommandSpec<any, any>;
5
+ }
6
+ /** Every public command is an executable adapter, including aliases and utility commands. */
7
+ export declare const commandRegistry: readonly RegisteredCommand[];
8
+ export declare function findCommand(name: string): RegisteredCommand | undefined;
9
+ export declare function executeRegisteredCommand(name: string, argv: string[], context: CommandContext): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { buildCommand, compactCommand, doneCommand, expressCommand, holdCommand, joinCommand, resumeCommand } from './square-commands.js';
1
+ import { buildCommand, doneCommand, expressCommand, holdCommand, joinCommand, resumeCommand } from './square-commands.js';
2
2
  import { doctorCommand } from './maintenance-commands.js';
3
3
  import { harnessCommand, installCommand, uninstallCommand } from './harness-command.js';
4
4
  import { helpCommand, versionCommand } from './meta-commands.js';
@@ -20,7 +20,6 @@ export const commandRegistry = [
20
20
  { names: ['install'], spec: installCommand },
21
21
  { names: ['uninstall'], spec: uninstallCommand },
22
22
  { names: ['harness'], spec: harnessCommand },
23
- { names: ['compact'], spec: compactCommand },
24
23
  { names: ['doctor'], spec: doctorCommand },
25
24
  { names: ['history'], spec: historyCommand },
26
25
  { names: ['status'], spec: statusCommand },
@@ -0,0 +1,34 @@
1
+ import { type BuildOptions, type HardCap, type Reach } from '../model.js';
2
+ import { type CommandSpec } from './context.js';
3
+ interface BuildIntent {
4
+ options: BuildOptions & {
5
+ hardCap: HardCap;
6
+ };
7
+ snippet: string;
8
+ }
9
+ interface JoinIntent {
10
+ name: string;
11
+ lastN: number | null;
12
+ kick: boolean;
13
+ }
14
+ interface ActivityIntent {
15
+ name: string;
16
+ activity: string;
17
+ force: boolean;
18
+ noWait: boolean;
19
+ reach?: Reach;
20
+ reply?: number;
21
+ }
22
+ interface BodyIntent {
23
+ name: string;
24
+ body?: string;
25
+ }
26
+ export declare const buildCommand: CommandSpec<BuildIntent, string>;
27
+ export declare const joinCommand: CommandSpec<JoinIntent, string>;
28
+ export declare const expressCommand: CommandSpec<ActivityIntent>;
29
+ export declare const doneCommand: CommandSpec<BodyIntent, string>;
30
+ export declare const holdCommand: CommandSpec<BodyIntent, string>;
31
+ export declare const resumeCommand: CommandSpec<{
32
+ name: string;
33
+ }, string>;
34
+ export {};