@astrosheep/square 0.3.58 → 0.3.62
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/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/claude-plugin/skills/square/SKILL.md +5 -7
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/activity.d.ts +6 -2
- package/dist/activity.js +170 -133
- package/dist/artifact.d.ts +4 -0
- package/dist/artifact.js +43 -14
- package/dist/attention-presentation.d.ts +1 -0
- package/dist/attention-presentation.js +21 -19
- package/dist/automatic-session.js +16 -26
- package/dist/boundary-presentation.d.ts +1 -2
- package/dist/boundary-presentation.js +4 -19
- package/dist/catch-decisions.js +1 -1
- package/dist/cli/context.d.ts +2 -0
- package/dist/cli/context.js +17 -33
- package/dist/cli/harness-command.js +1 -1
- package/dist/cli/mcp-server-command.d.ts +2 -0
- package/dist/cli/mcp-server-command.js +13 -0
- package/dist/cli/observation-commands.js +132 -64
- package/dist/cli/program.js +36 -9
- package/dist/cli/registry.js +2 -0
- package/dist/cli/square-commands.d.ts +1 -1
- package/dist/cli/square-commands.js +103 -133
- package/dist/decisions.d.ts +4 -0
- package/dist/decisions.js +82 -34
- package/dist/delivery-operations.d.ts +1 -2
- package/dist/delivery-operations.js +1 -4
- package/dist/file-changes.d.ts +12 -0
- package/dist/file-changes.js +235 -0
- package/dist/help.js +4 -8
- package/dist/host-ledger-file-adapter.d.ts +15 -13
- package/dist/host-ledger-file-adapter.js +131 -79
- package/dist/host-ledger-root.d.ts +2 -0
- package/dist/host-ledger-root.js +9 -0
- package/dist/host-ledger.d.ts +4 -24
- package/dist/inbox.d.ts +10 -5
- package/dist/inbox.js +152 -67
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/list.js +12 -9
- package/dist/mcp.d.ts +7 -0
- package/dist/mcp.js +122 -0
- package/dist/mention-parse.d.ts +8 -0
- package/dist/mention-parse.js +107 -0
- package/dist/model.d.ts +5 -0
- package/dist/model.js +4 -1
- package/dist/notifications.d.ts +1 -0
- package/dist/notifications.js +25 -20
- package/dist/participant-host.js +2 -2
- package/dist/ports.d.ts +2 -9
- package/dist/presentation.d.ts +19 -1
- package/dist/presentation.js +111 -73
- package/dist/presented.js +2 -1
- package/dist/registry.d.ts +8 -4
- package/dist/registry.js +43 -76
- package/dist/routes.d.ts +19 -2
- package/dist/routes.js +48 -5
- package/dist/runtime.d.ts +10 -0
- package/dist/runtime.js +39 -0
- package/dist/square-actions.d.ts +26 -13
- package/dist/square-actions.js +203 -87
- package/dist/square-application.d.ts +58 -0
- package/dist/square-application.js +183 -0
- package/dist/square-facade.d.ts +24 -9
- package/dist/square-file-adapter.d.ts +5 -12
- package/dist/square-file-adapter.js +12 -64
- package/dist/square-projections.d.ts +3 -2
- package/dist/square-projections.js +12 -3
- package/dist/square-storage.js +27 -34
- package/dist/square-wiring.d.ts +12 -7
- package/dist/square-wiring.js +36 -32
- package/dist/state-cell.d.ts +1 -1
- package/dist/stream.js +31 -27
- package/dist/tty-style.d.ts +7 -0
- package/dist/tty-style.js +13 -0
- package/dist/views.d.ts +3 -3
- package/dist/views.js +10 -9
- package/dist/wake-attempts.js +2 -1
- package/dist/wake-evidence.js +2 -8
- package/dist/wakes.d.ts +1 -0
- package/dist/wakes.js +5 -5
- package/dist/watch.d.ts +6 -1
- package/dist/watch.js +43 -28
- package/extensions/square-pi.js +107 -109
- package/guides/application-boundary.md +32 -0
- package/guides/change-waits.md +28 -0
- package/guides/host-ledger.md +9 -0
- package/guides/mcp.md +9 -0
- package/guides/operation-cancellation.md +17 -0
- package/package.json +10 -4
- package/skills/square/SKILL.md +5 -7
|
@@ -6,15 +6,20 @@ import { cmdListSquares } from '../list.js';
|
|
|
6
6
|
import { nameKey, parseActivityId, sameName } from '../model.js';
|
|
7
7
|
import { commandPrefix, participantIdentity, renderGrepActivitiesView, renderEventCli, renderAmbientEvent, renderPresenceAnchor, truncateChars, withPathOutput, quoteShell, } from '../presentation.js';
|
|
8
8
|
import { actId, nowMs } from '../runtime.js';
|
|
9
|
+
import { style } from '../tty-style.js';
|
|
9
10
|
import { cmdStream, cmdStreamNdjson } from '../stream.js';
|
|
10
11
|
import { formatRelativeTime, formatTimestamp, parseTimeOrRelative } from '../time.js';
|
|
11
12
|
import { cmdWatch } from '../watch.js';
|
|
12
13
|
import { openSquare } from '../square-file-adapter.js';
|
|
13
14
|
import { closeOpenSquare } from '../open-square.js';
|
|
14
|
-
import {
|
|
15
|
+
import { createSquareApplication } from '../square-application.js';
|
|
16
|
+
import { historyPresentation } from '../views.js';
|
|
17
|
+
import { hostLedgerForEnv } from '../registry.js';
|
|
15
18
|
import { fail, parseBoundedLimit, parseDurationMs, parseNameList, parseNonNegativeInteger, readStdin, requireParticipant, requireSquarePath, requireValue, usage, } from './context.js';
|
|
16
19
|
import { CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT } from '../catch-decisions.js';
|
|
17
20
|
const STATUS_PARTICIPANT_PREVIEW_LIMIT = 10;
|
|
21
|
+
/** A participant counts as here when their last sign of life sits inside this window. */
|
|
22
|
+
const STATUS_HERE_WINDOW_MS = 30 * 60_000;
|
|
18
23
|
const HISTORY_DEFAULT_LIMIT = 10;
|
|
19
24
|
const HISTORY_MAX_LIMIT = 100;
|
|
20
25
|
const PARTICIPANTS_DEFAULT_LIMIT = 20;
|
|
@@ -94,10 +99,10 @@ export const catchCommand = {
|
|
|
94
99
|
for (let index = 0; index < argv.length; index++) {
|
|
95
100
|
if (argv[index] === '--id') {
|
|
96
101
|
if (id !== undefined)
|
|
97
|
-
fail('✕ catch accepts one --id\
|
|
102
|
+
fail('✕ catch accepts one --id\nsquare catch --help');
|
|
98
103
|
const value = requireValue(argv, index, '--id');
|
|
99
104
|
if (parseActivityId(value) === undefined)
|
|
100
|
-
fail('✕ invalid --id: expected an activity id like act/12\
|
|
105
|
+
fail('✕ invalid --id: expected an activity id like act/12\nsquare catch --help');
|
|
101
106
|
id = value;
|
|
102
107
|
index += 1;
|
|
103
108
|
}
|
|
@@ -121,11 +126,11 @@ export const catchCommand = {
|
|
|
121
126
|
else if (argv[index] === '--now')
|
|
122
127
|
now = true;
|
|
123
128
|
else
|
|
124
|
-
fail(`✕ catch does not know ${argv[index]}\
|
|
129
|
+
fail(`✕ catch does not know ${argv[index]}\nsquare catch --help`);
|
|
125
130
|
}
|
|
126
131
|
if (id !== undefined) {
|
|
127
132
|
if (argv.some((flag) => ['--idle', '--from', '--mention', '--limit', '--replace'].includes(flag))) {
|
|
128
|
-
fail('✕ --id cannot be combined with --idle, --from, --mention, --limit or --replace\
|
|
133
|
+
fail('✕ --id cannot be combined with --idle, --from, --mention, --limit or --replace\nsquare catch --help');
|
|
129
134
|
}
|
|
130
135
|
return { id, now: true };
|
|
131
136
|
}
|
|
@@ -144,7 +149,7 @@ export const catchCommand = {
|
|
|
144
149
|
},
|
|
145
150
|
async execute(intent, context) {
|
|
146
151
|
const squarePath = requireSquarePath(context);
|
|
147
|
-
const caught = await cmdWatch(squarePath, requireParticipant(context.name), intent);
|
|
152
|
+
const caught = await cmdWatch(squarePath, requireParticipant(context.name), intent, { cwd: context.cwd, env: context.env });
|
|
148
153
|
if (caught !== false)
|
|
149
154
|
await sweepPendingNotifications(squarePath);
|
|
150
155
|
},
|
|
@@ -271,7 +276,7 @@ function parseHistory(argv, context) {
|
|
|
271
276
|
else if (flag === '--json')
|
|
272
277
|
json = true;
|
|
273
278
|
else
|
|
274
|
-
fail(`✕ history does not know ${flag}\
|
|
279
|
+
fail(`✕ history does not know ${flag}\nsquare history --help`);
|
|
275
280
|
}
|
|
276
281
|
if (grep !== undefined && fixed !== undefined)
|
|
277
282
|
fail('--grep and --fixed cannot be combined.');
|
|
@@ -300,6 +305,30 @@ function parseHistory(argv, context) {
|
|
|
300
305
|
continuationArgs: historyContinuationArgs(argv),
|
|
301
306
|
};
|
|
302
307
|
}
|
|
308
|
+
function applicationHistoryQuery(options) {
|
|
309
|
+
return {
|
|
310
|
+
...(options.participants === undefined || options.participants.length === 0 ? {} : { from: options.participants }),
|
|
311
|
+
...(options.mention === undefined ? {} : { mention: options.mention }),
|
|
312
|
+
...(options.afterIndex === undefined ? {} : { after: actId(options.afterIndex) }),
|
|
313
|
+
...(options.beforeIndex === undefined ? {} : { before: actId(options.beforeIndex) }),
|
|
314
|
+
...(options.grep === undefined ? {} : { grep: options.grep }), ...(options.fixed === undefined ? {} : { fixed: options.fixed }),
|
|
315
|
+
order: 'asc',
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function activityAsStored(activity) {
|
|
319
|
+
const index = parseActivityId(activity.id);
|
|
320
|
+
if (index === undefined)
|
|
321
|
+
throw new Error('Application returned a non-canonical activity id');
|
|
322
|
+
switch (activity.kind) {
|
|
323
|
+
case 'say': return { index, kind: 'say', actor: activity.actor, at: activity.at, body: activity.body ?? '', mentions: activity.mentions, ...(activity.reach === undefined ? {} : { reach: activity.reach }), ...(activity.reply === undefined ? {} : { reply: parseActivityId(activity.reply) ?? undefined }) };
|
|
324
|
+
case 'done': return { index, kind: 'done', actor: activity.actor, at: activity.at, body: activity.body ?? '' };
|
|
325
|
+
case 'hold': return { index, kind: 'hold', actor: activity.actor, at: activity.at, ...(activity.body === undefined ? {} : { body: activity.body }) };
|
|
326
|
+
case 'resume': return { index, kind: 'resume', actor: activity.actor, at: activity.at };
|
|
327
|
+
case 'join': return { index, kind: 'join', actor: activity.actor, at: activity.at };
|
|
328
|
+
case 'listen': return { index, kind: 'listen', actor: activity.actor, target: activity.target ?? '', at: activity.at };
|
|
329
|
+
case 'ignore': return { index, kind: 'ignore', actor: activity.actor, target: activity.target ?? '', at: activity.at };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
303
332
|
function historyContinuationCommand(options, squarePath, direction, index) {
|
|
304
333
|
const args = [...(options.continuationArgs ?? []), direction, actId(index), '--limit', String(options.lastN ?? HISTORY_DEFAULT_LIMIT)];
|
|
305
334
|
return `${commandPrefix(squarePath)} history ${args.map((arg) => arg.startsWith('-') || /^act\/\d+$/.test(arg) || /^\d+$/.test(arg) ? arg : quoteShell(arg)).join(' ')}`;
|
|
@@ -308,6 +337,10 @@ function boundedHistoryCommand(options, squarePath) {
|
|
|
308
337
|
const args = [...(options.continuationArgs ?? []), '--limit', String(HISTORY_MAX_LIMIT)];
|
|
309
338
|
return `${commandPrefix(squarePath)} history ${args.map((arg) => arg.startsWith('-') || /^act\/\d+$/.test(arg) || /^\d+$/.test(arg) ? arg : quoteShell(arg)).join(' ')}`;
|
|
310
339
|
}
|
|
340
|
+
/** The archive-visible activity model: content plus the lifecycle moves that keep its story honest — a raised hand pairs with its lowering, and a departure pairs with the return. */
|
|
341
|
+
function isArchiveActivity(item) {
|
|
342
|
+
return item.kind === 'say' || item.kind === 'done' || item.kind === 'hold' || item.kind === 'resume' || item.kind === 'join';
|
|
343
|
+
}
|
|
311
344
|
function renderFields(sayNumbers, item, fields) {
|
|
312
345
|
return fields.map((field) => {
|
|
313
346
|
switch (field) {
|
|
@@ -341,7 +374,7 @@ function jsonLine(sayNumbers, item) {
|
|
|
341
374
|
});
|
|
342
375
|
}
|
|
343
376
|
function renderHistoryProjection(projection, visible, noTruncate, squarePath) {
|
|
344
|
-
const shown = visible.filter(
|
|
377
|
+
const shown = visible.filter(isArchiveActivity);
|
|
345
378
|
const preview = noTruncate || shown.length <= 1 ? undefined : 200;
|
|
346
379
|
const chunks = [];
|
|
347
380
|
for (const activity of shown) {
|
|
@@ -359,7 +392,7 @@ function renderHistoryProjection(projection, visible, noTruncate, squarePath) {
|
|
|
359
392
|
if (chunks.length === 0)
|
|
360
393
|
return 'latest\n ○ no public activity in this view';
|
|
361
394
|
if (preview !== undefined && shown.some((activity) => activity.kind === 'say' && activity.body.length > preview)) {
|
|
362
|
-
chunks.push(
|
|
395
|
+
chunks.push(`${commandPrefix(squarePath)} history --no-truncate`);
|
|
363
396
|
}
|
|
364
397
|
return chunks.join('\n\n');
|
|
365
398
|
}
|
|
@@ -367,14 +400,18 @@ export const historyCommand = {
|
|
|
367
400
|
parse(argv, context) { return parseHistory(argv, context); },
|
|
368
401
|
async execute(options, context) {
|
|
369
402
|
const squarePath = requireSquarePath(context);
|
|
370
|
-
const square = await openSquare(squarePath, { clock: nowMs });
|
|
403
|
+
const square = await openSquare(squarePath, { clock: nowMs, env: context.env, hostLedger: hostLedgerForEnv(context.env) });
|
|
371
404
|
try {
|
|
372
405
|
// Keep the projection chronological; pagination chooses a stable edge,
|
|
373
406
|
// then --order only changes how the selected page is displayed.
|
|
374
407
|
const projection = await historyPresentation(square, { ...options, order: 'asc' });
|
|
375
|
-
|
|
408
|
+
const useApplicationHistory = options.atIndexes === undefined && options.beforeContext === undefined && options.afterContext === undefined && options.after === undefined;
|
|
409
|
+
const applicationHistory = useApplicationHistory
|
|
410
|
+
? await createSquareApplication({ cwd: context.cwd, env: { ...context.env, SQUARE_PARTICIPANT_NAME: undefined }, squarePath, clock: nowMs }).history(applicationHistoryQuery(options))
|
|
411
|
+
: undefined;
|
|
412
|
+
let events = applicationHistory === undefined ? [...projection.activities] : applicationHistory.map(activityAsStored);
|
|
376
413
|
if (options.lastN === null && events.length > HISTORY_MAX_LIMIT) {
|
|
377
|
-
fail(`✕ history is capped at ${HISTORY_MAX_LIMIT} activities\n
|
|
414
|
+
fail(`✕ history is capped at ${HISTORY_MAX_LIMIT} activities\n${boundedHistoryCommand(options, squarePath)}`);
|
|
378
415
|
}
|
|
379
416
|
const searching = options.grep !== undefined || options.fixed !== undefined;
|
|
380
417
|
const totalMatches = searching ? events.length : 0;
|
|
@@ -394,8 +431,8 @@ export const historyCommand = {
|
|
|
394
431
|
const output = pattern === undefined || pattern === ''
|
|
395
432
|
? renderHistoryProjection(projection, events, options.noTruncate === true, squarePath)
|
|
396
433
|
: renderGrepActivitiesView(events, totalMatches, options.noTruncate, squarePath, pattern, options.fixed !== undefined, () => 'full');
|
|
397
|
-
const publicEvents = events.filter(
|
|
398
|
-
const allPublic = projection.activities.filter(
|
|
434
|
+
const publicEvents = events.filter(isArchiveActivity);
|
|
435
|
+
const allPublic = projection.activities.filter(isArchiveActivity);
|
|
399
436
|
const pageMin = publicEvents.length === 0 ? undefined : Math.min(...publicEvents.map((item) => item.index));
|
|
400
437
|
const pageMax = publicEvents.length === 0 ? undefined : Math.max(...publicEvents.map((item) => item.index));
|
|
401
438
|
const hasMore = options.lastN != null && publicEvents.length > 0 && (options.afterIndex !== undefined
|
|
@@ -403,7 +440,7 @@ export const historyCommand = {
|
|
|
403
440
|
: allPublic.some((item) => item.index < (pageMin ?? Infinity)));
|
|
404
441
|
const cursorDirection = options.afterIndex !== undefined ? '--after' : '--before';
|
|
405
442
|
const cursorIndex = cursorDirection === '--after' ? Math.max(...publicEvents.map((item) => item.index)) : Math.min(...publicEvents.map((item) => item.index));
|
|
406
|
-
const continuation = hasMore ? `\n\n
|
|
443
|
+
const continuation = hasMore ? `\n\n${historyContinuationCommand(options, squarePath, cursorDirection, cursorIndex)}` : '';
|
|
407
444
|
return withPathOutput(squarePath, output + continuation, { participantCount: projection.participantCount });
|
|
408
445
|
}
|
|
409
446
|
finally {
|
|
@@ -429,32 +466,30 @@ export const participantsCommand = {
|
|
|
429
466
|
parse(argv, context) { return parseParticipants(argv, context); },
|
|
430
467
|
async execute(intent, context) {
|
|
431
468
|
const squarePath = requireSquarePath(context);
|
|
432
|
-
const
|
|
433
|
-
|
|
469
|
+
const application = createSquareApplication({ cwd: context.cwd, env: context.env, squarePath, clock: nowMs });
|
|
470
|
+
{
|
|
434
471
|
const now = nowMs();
|
|
435
|
-
const participants = await
|
|
472
|
+
const participants = await application.participants();
|
|
436
473
|
const lines = participants.slice(0, intent.limit).map((participant) => {
|
|
437
|
-
const
|
|
474
|
+
const recent = participant.lastActiveAt !== undefined && now - participant.lastActiveAt <= STATUS_HERE_WINDOW_MS;
|
|
475
|
+
const glyph = participant.state === 'done' ? '○' : participant.presence === 'watching' ? '◎' : participant.activityCount > 0 && recent ? '●' : '○';
|
|
438
476
|
const state = participant.state === 'done' ? 'done' : participant.presence === 'watching' ? 'catching' : participant.state;
|
|
439
477
|
const last = participant.lastActiveAt === undefined ? '—' : formatRelativeTime(participant.lastActiveAt, now);
|
|
440
|
-
return ` ${glyph} ${participant.name} · ${state} · ${participant.activityCount} ${participant.activityCount === 1 ? 'activity' : 'activities'} · ${last}`;
|
|
478
|
+
return ` ${glyph} ${participant.name}${style('dim', ` · ${state} · ${participant.activityCount} ${participant.activityCount === 1 ? 'activity' : 'activities'} · ${last}`)}`;
|
|
441
479
|
});
|
|
442
480
|
const participantCount = participants.filter((participant) => participant.state === 'active').length;
|
|
443
481
|
const tail = participants.length <= intent.limit
|
|
444
482
|
? []
|
|
445
483
|
: [
|
|
446
|
-
` ○ ${lines.length} of ${participants.length} participants shown
|
|
484
|
+
style('dim', ` ○ ${lines.length} of ${participants.length} participants shown`),
|
|
447
485
|
...(participants.length <= PARTICIPANTS_MAX_LIMIT ? [
|
|
448
|
-
|
|
486
|
+
`${participantsLimitCommand(squarePath, participants.length)}`,
|
|
449
487
|
] : []),
|
|
450
488
|
];
|
|
451
489
|
return withPathOutput(squarePath, ['participants', ...lines, ...tail].join('\n'), {
|
|
452
490
|
participantCount,
|
|
453
491
|
});
|
|
454
492
|
}
|
|
455
|
-
finally {
|
|
456
|
-
await closeOpenSquare(square);
|
|
457
|
-
}
|
|
458
493
|
},
|
|
459
494
|
present: (result) => process.stdout.write(result),
|
|
460
495
|
};
|
|
@@ -463,41 +498,60 @@ export const statusCommand = {
|
|
|
463
498
|
usage(context.command); return undefined; },
|
|
464
499
|
async execute(_intent, context) {
|
|
465
500
|
const squarePath = requireSquarePath(context);
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
const presentation = await
|
|
501
|
+
const application = createSquareApplication({ cwd: context.cwd, env: context.env, squarePath, clock: nowMs });
|
|
502
|
+
{
|
|
503
|
+
const presentation = await application.status();
|
|
469
504
|
const result = presentation.status;
|
|
470
505
|
const active = result.participants.filter((participant) => participant.state === 'active');
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
506
|
+
const here = active.filter((participant) => participant.presence === 'watching'
|
|
507
|
+
|| (participant.lastActiveAt !== undefined && result.now - participant.lastActiveAt <= STATUS_HERE_WINDOW_MS));
|
|
508
|
+
const lingering = active.length - here.length;
|
|
509
|
+
const orderedHere = [
|
|
510
|
+
...here.filter((participant) => participant.presence === 'watching'),
|
|
511
|
+
...here.filter((participant) => participant.presence !== 'watching'),
|
|
512
|
+
];
|
|
513
|
+
const people = [];
|
|
514
|
+
if (active.length === 0) {
|
|
515
|
+
people.push(' ○ nobody in the square');
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
if (orderedHere.length === 0)
|
|
519
|
+
people.push(' ○ nobody here right now');
|
|
520
|
+
people.push(...orderedHere.slice(0, STATUS_PARTICIPANT_PREVIEW_LIMIT).map((participant) => {
|
|
521
|
+
const glyph = participant.presence === 'watching'
|
|
522
|
+
? '◎'
|
|
523
|
+
: participant.activityCount > 0 ? '●' : '○';
|
|
524
|
+
const summary = participant.presence === 'watching'
|
|
525
|
+
? 'catching'
|
|
526
|
+
: participant.activityCount > 0
|
|
527
|
+
? formatRelativeTime(participant.lastActiveAt ?? result.now, result.now)
|
|
528
|
+
: `quiet · ${participant.lastActiveAt === undefined ? 'just now' : formatRelativeTime(participant.lastActiveAt, result.now)}`;
|
|
529
|
+
const showAttention = context.name === undefined || sameName(participant.name, context.name);
|
|
530
|
+
const attention = !showAttention
|
|
531
|
+
? ''
|
|
532
|
+
: participant.pendingMentionCount > 0
|
|
533
|
+
? `${participant.pendingMentionCount} ${participant.pendingMentionCount === 1 ? 'mention' : 'mentions'} waiting`
|
|
534
|
+
: participant.unreadActivityCount > 0
|
|
535
|
+
? `${participant.unreadActivityCount} change${participant.unreadActivityCount === 1 ? '' : 's'} waiting`
|
|
536
|
+
: 'caught up';
|
|
537
|
+
return ` ${glyph} ${participantIdentity(participant.name)}${style('dim', ` · ${summary}${attention === '' ? '' : ` · ${attention}`}`)}`;
|
|
538
|
+
}));
|
|
539
|
+
const unshown = Math.max(0, orderedHere.length - STATUS_PARTICIPANT_PREVIEW_LIMIT) + lingering;
|
|
540
|
+
if (orderedHere.length > STATUS_PARTICIPANT_PREVIEW_LIMIT) {
|
|
541
|
+
people.push(` ○ … ${orderedHere.length - STATUS_PARTICIPANT_PREVIEW_LIMIT} more here`);
|
|
542
|
+
}
|
|
543
|
+
if (lingering > 0)
|
|
544
|
+
people.push(` ○ … ${lingering} lingering`);
|
|
545
|
+
if (unshown > 0) {
|
|
546
|
+
people.push(`${result.participants.length <= PARTICIPANTS_MAX_LIMIT
|
|
547
|
+
? participantsLimitCommand(squarePath, result.participants.length)
|
|
548
|
+
: `${commandPrefix(squarePath)} participants`}`);
|
|
549
|
+
}
|
|
495
550
|
}
|
|
496
|
-
const cap = result.hardCap === null ? 'unlimited' :
|
|
551
|
+
const cap = result.hardCap === null ? 'unlimited' : `${result.hardCap} each`;
|
|
552
|
+
const holdTime = result.holdAt === undefined ? 'just now' : formatRelativeTime(result.holdAt, result.now);
|
|
497
553
|
const hold = result.holdActive
|
|
498
|
-
? `· ${result.holdActor === undefined ? 'someone' : participantIdentity(result.holdActor)} raised a hand${result.holdReason ? ` — ${result.holdReason}` : ''} · ${
|
|
499
|
-
? 'just now'
|
|
500
|
-
: formatRelativeTime(result.holdAt, result.now)}`
|
|
554
|
+
? `· ${result.holdActor === undefined ? 'someone' : participantIdentity(result.holdActor)} raised a hand${result.holdReason ? ` — ${result.holdReason}` : ''}${style('dim', ` · ${holdTime}`)}`
|
|
501
555
|
: undefined;
|
|
502
556
|
const visible = result.latestAct === undefined
|
|
503
557
|
? ''
|
|
@@ -510,20 +564,34 @@ export const statusCommand = {
|
|
|
510
564
|
const latest = visible === ''
|
|
511
565
|
? [result.latestAct === undefined
|
|
512
566
|
? ' ○ no public activity yet'
|
|
513
|
-
: ' · latest
|
|
567
|
+
: ' · the latest words were meant for other ears']
|
|
514
568
|
: [` ${visible.replace(/\n/g, '\n ')}`];
|
|
569
|
+
// Presence events after the latest public act: otherwise the roster can say someone is
|
|
570
|
+
// here while the latest line shows them leaving, with nothing to reconcile the two.
|
|
571
|
+
const latestIndex = result.latestAct?.index ?? -1;
|
|
572
|
+
const stepsIn = presentation.state.acts.filter((act) => act.kind === 'join' && act.index > latestIndex);
|
|
573
|
+
for (const act of stepsIn.slice(-3)) {
|
|
574
|
+
const rejoined = presentation.state.acts.some((other) => other.kind === 'done' && other.actor === act.actor && other.index < act.index);
|
|
575
|
+
latest.push(style('dim', ` → ${participantIdentity(act.actor)} ${rejoined ? 'stepped back in' : 'stepped in'} · ${formatRelativeTime(act.at, result.now)}`));
|
|
576
|
+
}
|
|
577
|
+
if (stepsIn.length > 3)
|
|
578
|
+
latest.push(style('dim', ` → … ${stepsIn.length - 3} more stepped in`));
|
|
515
579
|
if (visible.includes('more chars') && result.latestAct !== undefined) {
|
|
516
|
-
latest.push(
|
|
580
|
+
latest.push(`${commandPrefix(squarePath)} history --at ${actId(result.latestAct)} -C 2 --no-truncate`);
|
|
517
581
|
}
|
|
582
|
+
const counts = [
|
|
583
|
+
`${here.length} here`,
|
|
584
|
+
...(lingering > 0 ? [`${lingering} lingering`] : []),
|
|
585
|
+
`${result.doneCount} done`,
|
|
586
|
+
`cap ${cap}`,
|
|
587
|
+
`throttle ${result.throttlePerMinute === undefined ? 'none' : `${result.throttlePerMinute}/min`}`,
|
|
588
|
+
].join(' · ');
|
|
518
589
|
const output = [
|
|
519
|
-
|
|
520
|
-
...(hold === undefined ? [] : ['', hold]), '', 'around the square', ...people, '', 'latest', ...latest,
|
|
590
|
+
style('dim', counts),
|
|
591
|
+
...(hold === undefined ? [] : ['', hold]), '', style('dim', 'around the square'), ...people, '', style('dim', 'latest'), ...latest,
|
|
521
592
|
].join('\n');
|
|
522
593
|
return withPathOutput(squarePath, output, { participantCount: result.activeCount, held: result.holdActive });
|
|
523
594
|
}
|
|
524
|
-
finally {
|
|
525
|
-
await closeOpenSquare(square);
|
|
526
|
-
}
|
|
527
595
|
},
|
|
528
596
|
present: (result) => process.stdout.write(result),
|
|
529
597
|
};
|
|
@@ -558,7 +626,7 @@ export const inboxCommand = {
|
|
|
558
626
|
fail('inbox requires --for-session <session-id>.');
|
|
559
627
|
const retry = inboxLimitCommand(sessionId, json);
|
|
560
628
|
if (duplicateLimit)
|
|
561
|
-
fail(`✕ inbox accepts one --limit\n
|
|
629
|
+
fail(`✕ inbox accepts one --limit\n${retry}`);
|
|
562
630
|
const limit = hasLimit
|
|
563
631
|
? parseBoundedLimit(limitValue, '--limit', INBOX_MAX_LIMIT, retry)
|
|
564
632
|
: INBOX_DEFAULT_LIMIT;
|
package/dist/cli/program.js
CHANGED
|
@@ -1,20 +1,47 @@
|
|
|
1
1
|
import { helpRequest } from '../help.js';
|
|
2
2
|
import { isSquareError } from '../model.js';
|
|
3
|
-
import { joinRecoveryCommand, participantsRecoveryCommand } from '../presentation.js';
|
|
3
|
+
import { formatRefusal, joinRecoveryCommand, participantsRecoveryCommand, takeoverRecoveryLines } from '../presentation.js';
|
|
4
|
+
import { inSquareCount, nowMs } from '../runtime.js';
|
|
5
|
+
import { openSquare } from '../square-file-adapter.js';
|
|
6
|
+
import { closeOpenSquare } from '../open-square.js';
|
|
4
7
|
import { defaultContext, parseGlobalArgs } from './context.js';
|
|
5
8
|
import { executeRegisteredCommand, findCommand } from './registry.js';
|
|
6
|
-
function
|
|
9
|
+
async function participantCountFor(squarePath) {
|
|
10
|
+
try {
|
|
11
|
+
const square = await openSquare(squarePath, { clock: nowMs });
|
|
12
|
+
try {
|
|
13
|
+
return inSquareCount((await square.artifact.read()).state);
|
|
14
|
+
}
|
|
15
|
+
finally {
|
|
16
|
+
await closeOpenSquare(square);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function handleSquareError(error, squarePath, name) {
|
|
7
24
|
if (isSquareError(error)) {
|
|
8
|
-
|
|
9
|
-
if (squarePath !== undefined &&
|
|
25
|
+
const existing = [error.message];
|
|
26
|
+
if (squarePath !== undefined && name !== undefined && error.code === 'already_joined') {
|
|
27
|
+
existing.push(...takeoverRecoveryLines(squarePath, name));
|
|
28
|
+
}
|
|
29
|
+
else if (squarePath !== undefined && /^✕ .* has never stepped into/.test(error.message)) {
|
|
10
30
|
// The caller's name has never joined this square; only a join admits it.
|
|
11
31
|
if (name !== undefined)
|
|
12
|
-
|
|
32
|
+
existing.push(joinRecoveryCommand(squarePath, name));
|
|
13
33
|
else
|
|
14
|
-
|
|
34
|
+
existing.push(participantsRecoveryCommand(squarePath));
|
|
35
|
+
}
|
|
36
|
+
else if (squarePath !== undefined && /^✕ .* is not standing in this square/.test(error.message)) {
|
|
37
|
+
existing.push(participantsRecoveryCommand(squarePath));
|
|
38
|
+
}
|
|
39
|
+
if (squarePath === undefined) {
|
|
40
|
+
process.stderr.write(`${existing.join('\n')}\n`);
|
|
15
41
|
}
|
|
16
|
-
else
|
|
17
|
-
|
|
42
|
+
else {
|
|
43
|
+
const participantCount = await participantCountFor(squarePath);
|
|
44
|
+
process.stderr.write(formatRefusal(squarePath, existing, participantCount === undefined ? {} : { participantCount }));
|
|
18
45
|
}
|
|
19
46
|
process.exit(error.code === 'not_found' ? 1 : 2);
|
|
20
47
|
}
|
|
@@ -45,6 +72,6 @@ export async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
45
72
|
await executeRegisteredCommand(command, parsed.args.slice(1), defaultContext(command, parsed.squarePath, parsed.name));
|
|
46
73
|
}
|
|
47
74
|
catch (error) {
|
|
48
|
-
handleSquareError(error, squarePath, requestedName);
|
|
75
|
+
await handleSquareError(error, squarePath, requestedName);
|
|
49
76
|
}
|
|
50
77
|
}
|
package/dist/cli/registry.js
CHANGED
|
@@ -2,6 +2,7 @@ import { buildCommand, doneCommand, expressCommand, holdCommand, ignoreCommand,
|
|
|
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';
|
|
5
|
+
import { mcpServerCommand } from './mcp-server-command.js';
|
|
5
6
|
import { catchCommand, claudeHookCommand, codexHookCommand, historyCommand, inboxCommand, listCommand, participantsCommand, statusCommand, streamCommand, } from './observation-commands.js';
|
|
6
7
|
/** Every public command is an executable adapter, including aliases and utility commands. */
|
|
7
8
|
export const commandRegistry = [
|
|
@@ -29,6 +30,7 @@ export const commandRegistry = [
|
|
|
29
30
|
{ names: ['participants'], spec: participantsCommand },
|
|
30
31
|
{ names: ['help'], spec: helpCommand },
|
|
31
32
|
{ names: ['version', '--version', '-v'], spec: versionCommand },
|
|
33
|
+
{ names: ['mcp-server'], spec: mcpServerCommand },
|
|
32
34
|
];
|
|
33
35
|
export function findCommand(name) {
|
|
34
36
|
return commandRegistry.find((command) => command.names.includes(name));
|