@astrosheep/square 0.3.14 → 0.3.15
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/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/activity-feed.d.ts +2 -3
- package/dist/activity-feed.js +6 -15
- package/dist/activity.js +1 -1
- package/dist/artifact.js +17 -26
- package/dist/attention-presentation.d.ts +13 -0
- package/dist/attention-presentation.js +22 -0
- package/dist/automatic-session.js +6 -8
- package/dist/boundary-presentation.js +35 -14
- package/dist/claude-hook.js +5 -3
- package/dist/cli/context.d.ts +4 -4
- package/dist/cli/context.js +25 -4
- package/dist/cli/harness-command.js +1 -1
- package/dist/cli/maintenance-commands.js +5 -4
- package/dist/cli/observation-commands.js +24 -16
- package/dist/cli/program.js +1 -1
- package/dist/cli/registry.js +4 -1
- package/dist/cli/square-commands.d.ts +7 -0
- package/dist/cli/square-commands.js +123 -27
- package/dist/codex-hook.js +5 -3
- package/dist/decisions.d.ts +19 -0
- package/dist/decisions.js +45 -12
- package/dist/delivery.d.ts +14 -21
- package/dist/delivery.js +40 -74
- package/dist/help.js +8 -5
- package/dist/inbox.js +1 -0
- package/dist/index.d.ts +1 -10
- package/dist/index.js +0 -32
- package/dist/landing.d.ts +12 -0
- package/dist/landing.js +35 -4
- package/dist/model.d.ts +8 -11
- package/dist/model.js +2 -2
- package/dist/notifications.js +35 -11
- package/dist/paseo-delivery.js +7 -4
- package/dist/paseo.d.ts +6 -0
- package/dist/paseo.js +5 -0
- package/dist/presence.d.ts +3 -0
- package/dist/presence.js +40 -10
- package/dist/presentation.d.ts +6 -2
- package/dist/presentation.js +22 -9
- package/dist/presented.d.ts +2 -0
- package/dist/presented.js +20 -0
- package/dist/registry.js +4 -1
- package/dist/routes.d.ts +5 -0
- package/dist/routes.js +17 -0
- package/dist/runtime.d.ts +4 -5
- package/dist/runtime.js +35 -18
- package/dist/square-core.d.ts +17 -0
- package/dist/square-core.js +51 -1
- package/dist/square-facade.d.ts +9 -1
- package/dist/square-wiring.d.ts +8 -0
- package/dist/square-wiring.js +32 -3
- package/dist/views.d.ts +9 -3
- package/dist/views.js +16 -14
- package/dist/wake-port.d.ts +4 -1
- package/dist/wake-port.js +5 -0
- package/dist/wakes.js +3 -5
- package/dist/watch.js +1 -0
- package/package.json +19 -3
- package/skills/brainstorm/SKILL.md +1 -1
- package/skills/square/.claude-plugin/plugin.json +1 -1
- package/skills/square/SKILL.md +1 -1
package/dist/activity-feed.d.ts
CHANGED
|
@@ -4,11 +4,10 @@ export interface ActivityFeedFilter {
|
|
|
4
4
|
mention?: string;
|
|
5
5
|
}
|
|
6
6
|
export declare function actDelta(acts: StoredAct[], cursor: number): StoredAct[];
|
|
7
|
-
/**
|
|
7
|
+
/** Visible activities after the participant's derived continuous-seen prefix. */
|
|
8
8
|
export declare function deliveryDelta(squareState: SquareState, name: string): StoredAct[];
|
|
9
9
|
export declare function peerRoomChanges(delta: StoredAct[], name: string): RoomChangeAct[];
|
|
10
10
|
export declare function peerPublicActs(delta: StoredAct[], name: string): PublicAct[];
|
|
11
|
-
export declare function matchesFeedFilter(act: StoredAct, filter: ActivityFeedFilter): boolean;
|
|
11
|
+
export declare function matchesFeedFilter(act: StoredAct, filter: ActivityFeedFilter, recipients?: readonly string[]): boolean;
|
|
12
12
|
export declare function filteredPeerActivities(delta: StoredAct[], name: string, filter: ActivityFeedFilter): PublicAct[];
|
|
13
13
|
export declare function filteredRoomChanges(delta: StoredAct[], name: string, filter: ActivityFeedFilter): RoomChangeAct[];
|
|
14
|
-
export declare function ackPeerDelta(squareState: SquareState, name: string, delta: StoredAct[], at?: number): boolean;
|
package/dist/activity-feed.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import { sameName } from './model.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { readCursor } from './runtime.js';
|
|
3
|
+
import { matchesCatchFilter } from './delivery.js';
|
|
4
4
|
export function actDelta(acts, cursor) {
|
|
5
5
|
return acts.filter((act) => act.index > cursor);
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
7
|
+
/** Visible activities after the participant's derived continuous-seen prefix. */
|
|
8
8
|
export function deliveryDelta(squareState, name) {
|
|
9
|
-
|
|
10
|
-
const seen = new Set(items.map((act) => act.index));
|
|
11
|
-
for (const notification of deriveDeliveryModel(squareState).pendingFor(name)) {
|
|
12
|
-
if (!seen.has(notification.item.index))
|
|
13
|
-
items.push(notification.item);
|
|
14
|
-
}
|
|
15
|
-
return items.sort((a, b) => a.index - b.index);
|
|
9
|
+
return actDelta(squareState.acts, readCursor(squareState, name));
|
|
16
10
|
}
|
|
17
11
|
export function peerRoomChanges(delta, name) {
|
|
18
12
|
return delta.filter((act) => act.actor !== undefined && !sameName(act.actor, name) && act.kind !== 'say' && act.kind !== 'read');
|
|
@@ -23,9 +17,9 @@ export function peerPublicActs(delta, name) {
|
|
|
23
17
|
function matchesParticipants(act, participants) {
|
|
24
18
|
return participants === undefined || (act.actor !== undefined && participants.some((participant) => sameName(participant, act.actor)));
|
|
25
19
|
}
|
|
26
|
-
export function matchesFeedFilter(act, filter) {
|
|
20
|
+
export function matchesFeedFilter(act, filter, recipients) {
|
|
27
21
|
if (act.kind === 'say') {
|
|
28
|
-
return matchesCatchFilter({ actor: act.actor, body: act.body, reach: act.reach }, filter);
|
|
22
|
+
return matchesCatchFilter({ actor: act.actor, body: act.body, reach: act.reach, ...(recipients === undefined ? {} : { recipients }) }, filter);
|
|
29
23
|
}
|
|
30
24
|
return filter.mention === undefined && matchesParticipants(act, filter.participants);
|
|
31
25
|
}
|
|
@@ -39,6 +33,3 @@ export function filteredRoomChanges(delta, name, filter) {
|
|
|
39
33
|
return [];
|
|
40
34
|
return peerRoomChanges(delta, name).filter((act) => matchesParticipants(act, filter.participants));
|
|
41
35
|
}
|
|
42
|
-
export function ackPeerDelta(squareState, name, delta, at) {
|
|
43
|
-
return advanceCursor(squareState, name, latestActIndex([...peerPublicActs(delta, name), ...peerRoomChanges(delta, name)]), at);
|
|
44
|
-
}
|
package/dist/activity.js
CHANGED
|
@@ -87,7 +87,7 @@ export async function cmdActivity(squarePath, name, activity, resolveBody, opts)
|
|
|
87
87
|
const held = fresh.held;
|
|
88
88
|
const ownActCount = fresh.ownActivityCount;
|
|
89
89
|
const hasPending = pendingPublic.length > 0 || pendingRoomChanges.length > 0;
|
|
90
|
-
const pending = hasPending ? `\n\n${renderPendingFeed([...fresh.activities], [...pendingPublic], [...pendingRoomChanges], knownName)}` : '';
|
|
90
|
+
const pending = hasPending ? `\n\n${renderPendingFeed([...fresh.activities], [...pendingPublic], [...pendingRoomChanges], knownName, fresh.state)}` : '';
|
|
91
91
|
const hint = expressHintLine(ownActCount);
|
|
92
92
|
const confirmation = `● heads turn your way — #${ownActCount}`;
|
|
93
93
|
const withHint = hint ? `${confirmation}\n${hint}` : confirmation;
|
package/dist/artifact.js
CHANGED
|
@@ -33,18 +33,12 @@ function isNonblankString(value) {
|
|
|
33
33
|
function isStringArray(value) {
|
|
34
34
|
return Array.isArray(value) && value.every((item) => typeof item === 'string');
|
|
35
35
|
}
|
|
36
|
-
function
|
|
36
|
+
function validateObservation(value) {
|
|
37
37
|
return isObject(value)
|
|
38
|
-
&& hasExactKeys(value, ['
|
|
39
|
-
&&
|
|
40
|
-
&& value.
|
|
41
|
-
&&
|
|
42
|
-
}
|
|
43
|
-
function validateDeliveryReceipt(value) {
|
|
44
|
-
return isObject(value)
|
|
45
|
-
&& hasExactKeys(value, ['status', 'at'])
|
|
46
|
-
&& value.status === 'delivered'
|
|
47
|
-
&& isFiniteNumber(value.at);
|
|
38
|
+
&& hasExactKeys(value, ['state', 'at'], ['ownerId'])
|
|
39
|
+
&& (value.state === 'notified' || value.state === 'seen')
|
|
40
|
+
&& isFiniteNumber(value.at)
|
|
41
|
+
&& (value.ownerId === undefined || isNonblankString(value.ownerId));
|
|
48
42
|
}
|
|
49
43
|
function validateWatchLease(value) {
|
|
50
44
|
if (!isObject(value)
|
|
@@ -100,25 +94,18 @@ function parseNotifyLeaseKey(key) {
|
|
|
100
94
|
}
|
|
101
95
|
function validateRuntime(value) {
|
|
102
96
|
if (!isObject(value)
|
|
103
|
-
|| !hasExactKeys(value, ['nextActIndex', '
|
|
97
|
+
|| !hasExactKeys(value, ['nextActIndex', 'observations', 'leases', 'notifyLeases'])
|
|
104
98
|
|| !isNonNegativeInteger(value.nextActIndex)
|
|
105
|
-
|| !validateRecord(value.
|
|
99
|
+
|| !validateRecord(value.observations, (candidate) => isObject(candidate) && Object.entries(candidate).every(([id, observation]) => parseActivityId(id) !== undefined && validateObservation(observation)))
|
|
106
100
|
|| !validateRecord(value.leases, validateWatchLease)
|
|
107
|
-
|| !validateRecord(value.notifyLeases, validateNotifyLease)
|
|
108
|
-
|| !isObject(value.deliveryReceipts))
|
|
101
|
+
|| !validateRecord(value.notifyLeases, validateNotifyLease))
|
|
109
102
|
return false;
|
|
110
|
-
return
|
|
111
|
-
&& isObject(receipts)
|
|
112
|
-
&& Object.entries(receipts).every(([id, receipt]) => parseActivityId(id) !== undefined && validateDeliveryReceipt(receipt)));
|
|
103
|
+
return true;
|
|
113
104
|
}
|
|
114
105
|
function validateAssignedRuntimeReferences(runtime) {
|
|
115
106
|
const bound = runtime.nextActIndex;
|
|
116
|
-
for (const
|
|
117
|
-
|
|
118
|
-
return 'future';
|
|
119
|
-
}
|
|
120
|
-
for (const receipts of Object.values(runtime.deliveryReceipts)) {
|
|
121
|
-
for (const id of Object.keys(receipts)) {
|
|
107
|
+
for (const observations of Object.values(runtime.observations)) {
|
|
108
|
+
for (const id of Object.keys(observations)) {
|
|
122
109
|
const index = parseActivityId(id);
|
|
123
110
|
if (index === undefined)
|
|
124
111
|
return 'malformed';
|
|
@@ -167,6 +154,11 @@ function validateStoredAct(value) {
|
|
|
167
154
|
return hasExactKeys(value, ['kind', 'actor', 'at', 'through', 'index'])
|
|
168
155
|
&& validateActor(value.actor, true)
|
|
169
156
|
&& isNonNegativeInteger(value.through);
|
|
157
|
+
case 'listen':
|
|
158
|
+
case 'ignore':
|
|
159
|
+
return hasExactKeys(value, ['kind', 'actor', 'target', 'at', 'index'])
|
|
160
|
+
&& validateActor(value.actor, true)
|
|
161
|
+
&& validateActor(value.target, true);
|
|
170
162
|
default:
|
|
171
163
|
return false;
|
|
172
164
|
}
|
|
@@ -257,8 +249,7 @@ function decodeEnvelope(bytes, magic) {
|
|
|
257
249
|
export function emptyRuntimeState(nextActIndex = 0) {
|
|
258
250
|
return {
|
|
259
251
|
nextActIndex,
|
|
260
|
-
|
|
261
|
-
deliveryReceipts: {},
|
|
252
|
+
observations: {},
|
|
262
253
|
leases: {},
|
|
263
254
|
notifyLeases: {},
|
|
264
255
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DirectedNotificationRoute } from './model.js';
|
|
2
|
+
export declare const ATTENTION_BODY_MAX = 120;
|
|
3
|
+
export interface AttentionPreview {
|
|
4
|
+
squarePath: string;
|
|
5
|
+
actIndex: number;
|
|
6
|
+
recipient: string;
|
|
7
|
+
actor: string;
|
|
8
|
+
route: DirectedNotificationRoute;
|
|
9
|
+
body: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function previewAttentionBody(body: string): string;
|
|
12
|
+
export declare function displayAttentionPath(squarePath: string): string;
|
|
13
|
+
export declare function renderAttentionPreview(attention: AttentionPreview): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { notificationMessageId } from './delivery.js';
|
|
3
|
+
import { participantIdentity } from './presentation.js';
|
|
4
|
+
export const ATTENTION_BODY_MAX = 120;
|
|
5
|
+
export function previewAttentionBody(body) {
|
|
6
|
+
const compact = body.replace(/\r\n/g, '\n');
|
|
7
|
+
if (compact.length <= ATTENTION_BODY_MAX)
|
|
8
|
+
return compact;
|
|
9
|
+
return `${compact.slice(0, ATTENTION_BODY_MAX).trimEnd()}\n… [truncated; run catch --now]`;
|
|
10
|
+
}
|
|
11
|
+
export function displayAttentionPath(squarePath) {
|
|
12
|
+
return squarePath.startsWith(homedir())
|
|
13
|
+
? `~${squarePath.slice(homedir().length)}`
|
|
14
|
+
: squarePath;
|
|
15
|
+
}
|
|
16
|
+
export function renderAttentionPreview(attention) {
|
|
17
|
+
const attentionKind = attention.route === 'bell' ? 'bell' : 'attention';
|
|
18
|
+
return [
|
|
19
|
+
`${notificationMessageId(attention.squarePath, attention.actIndex)} · ${displayAttentionPath(attention.squarePath)}: ${participantIdentity(attention.recipient)} from ${participantIdentity(attention.actor)} (${attentionKind})`,
|
|
20
|
+
previewAttentionBody(attention.body),
|
|
21
|
+
].join('\n');
|
|
22
|
+
}
|
|
@@ -5,7 +5,7 @@ import { openSquare } from './square-file-adapter.js';
|
|
|
5
5
|
import { closeOpenSquare } from './open-square.js';
|
|
6
6
|
import { Square } from './square-wiring.js';
|
|
7
7
|
import { entryPresentation } from './views.js';
|
|
8
|
-
import { canonicalSquarePath,
|
|
8
|
+
import { canonicalSquarePath, lookupSessionBindings, recordSessionDone, recordSessionJoin } from './registry.js';
|
|
9
9
|
import { participantIdentity, renderAmbientEvent } from './presentation.js';
|
|
10
10
|
import { validateName } from './model.js';
|
|
11
11
|
const providerEnv = {
|
|
@@ -39,16 +39,13 @@ export async function automaticSessionStart(provider, sessionId, cwd, env = proc
|
|
|
39
39
|
return undefined;
|
|
40
40
|
}
|
|
41
41
|
const name = automaticParticipant(provider, sessionId, env);
|
|
42
|
-
const
|
|
43
|
-
const before = await entryPresentation(reader, name);
|
|
44
|
-
if (bindings.some((binding) => canonicalSquarePath(binding.squarePath) === canonicalSquarePath(squarePath) && binding.name === name) && before.joined) {
|
|
45
|
-
await closeOpenSquare(reader);
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
42
|
+
const alreadyBound = lookupSessionBindings(sessionId).some((binding) => canonicalSquarePath(binding.squarePath) === canonicalSquarePath(squarePath) && binding.name === name);
|
|
48
43
|
await closeOpenSquare(reader);
|
|
49
44
|
const square = await Square.at({ path: squarePath });
|
|
50
45
|
try {
|
|
51
|
-
await square.
|
|
46
|
+
const implicit = await square.implicitJoin(name);
|
|
47
|
+
if (implicit.state === 'done' || (implicit.state === 'active' && alreadyBound))
|
|
48
|
+
return undefined;
|
|
52
49
|
const channel = provider === 'claude' ? 'claude-code' : provider;
|
|
53
50
|
recordSessionJoin(sessionId, name, squarePath, channel, { ...env, [providerEnv[provider]]: sessionId });
|
|
54
51
|
const afterSquare = await openSquare(squarePath);
|
|
@@ -57,6 +54,7 @@ export async function automaticSessionStart(provider, sessionId, cwd, env = proc
|
|
|
57
54
|
now: Date.now(),
|
|
58
55
|
preview: 200,
|
|
59
56
|
actNumber: event.kind === 'say' ? after.sayNumbers[event.index] : undefined,
|
|
57
|
+
squareState: after.state,
|
|
60
58
|
})).filter(Boolean).join('\n\n');
|
|
61
59
|
return [`You joined the public square as ${participantIdentity(name)}.`, after.scene, after.context ? `context\n${after.context}` : '', activity ? `recent activity\n${activity}` : ''].filter(Boolean).join('\n\n');
|
|
62
60
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { leaseOwnsNotification
|
|
1
|
+
import { leaseOwnsNotification } from './delivery.js';
|
|
2
2
|
import { sessionInbox } from './inbox.js';
|
|
3
|
-
import {
|
|
3
|
+
import { notificationMessageId } from './delivery.js';
|
|
4
|
+
import { markBoundarySeen } from './square-wiring.js';
|
|
5
|
+
import { renderAttentionPreview } from './attention-presentation.js';
|
|
6
|
+
import { participantCommandPrefix } from './presentation.js';
|
|
4
7
|
import { presentOnce } from './presented.js';
|
|
5
|
-
const BODY_MAX = 200;
|
|
6
8
|
const CONTEXT_MAX = 1200;
|
|
7
9
|
function pendingCount(inbox) {
|
|
8
10
|
return inbox.reduce((total, membership) => total + membership.notifications.length, 0);
|
|
@@ -16,17 +18,11 @@ export function pendingAtBoundary(inbox) {
|
|
|
16
18
|
return membership;
|
|
17
19
|
return {
|
|
18
20
|
...membership,
|
|
19
|
-
notifications: membership.notifications.filter((notification) => !leaseOwnsNotification(lease, notification)),
|
|
21
|
+
notifications: membership.notifications.filter((notification) => !leaseOwnsNotification(lease, { ...notification, recipient: membership.name })),
|
|
20
22
|
};
|
|
21
23
|
})
|
|
22
24
|
.filter((membership) => membership.notifications.length > 0);
|
|
23
25
|
}
|
|
24
|
-
function bodyPreview(body) {
|
|
25
|
-
const compact = body.replace(/\r\n/g, '\n');
|
|
26
|
-
if (compact.length <= BODY_MAX)
|
|
27
|
-
return compact;
|
|
28
|
-
return `${compact.slice(0, BODY_MAX).trimEnd()}\n… [truncated; run catch --now]`;
|
|
29
|
-
}
|
|
30
26
|
export function renderPendingAtBoundary(inbox) {
|
|
31
27
|
const count = pendingCount(inbox);
|
|
32
28
|
const noun = count === 1 ? 'notification' : 'notifications';
|
|
@@ -41,10 +37,15 @@ export function renderPendingAtBoundary(inbox) {
|
|
|
41
37
|
for (const [index, entry] of queued.entries()) {
|
|
42
38
|
const { membership, notification } = entry;
|
|
43
39
|
const command = `${participantCommandPrefix(membership.squarePath, membership.name)} catch --now`;
|
|
44
|
-
const id = notificationMessageId(membership.squarePath, notification.actIndex);
|
|
45
40
|
const block = [
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
renderAttentionPreview({
|
|
42
|
+
squarePath: membership.squarePath,
|
|
43
|
+
actIndex: notification.actIndex,
|
|
44
|
+
recipient: membership.name,
|
|
45
|
+
actor: notification.actor,
|
|
46
|
+
route: notification.route,
|
|
47
|
+
body: notification.body,
|
|
48
|
+
}),
|
|
48
49
|
`Ack with: ${command}`,
|
|
49
50
|
].join('\n');
|
|
50
51
|
const omittedAfter = omitted + queued.length - index - 1;
|
|
@@ -74,5 +75,25 @@ export function renderPendingAtBoundary(inbox) {
|
|
|
74
75
|
}
|
|
75
76
|
export async function presentPendingAtBoundary(sessionId, present, lookup = sessionInbox, env = process.env) {
|
|
76
77
|
const inbox = await lookup(sessionId);
|
|
77
|
-
|
|
78
|
+
let deliveredInbox;
|
|
79
|
+
let deliveredContext;
|
|
80
|
+
const result = presentOnce(sessionId, () => pendingAtBoundary(inbox), (inbox) => {
|
|
81
|
+
const context = renderPendingAtBoundary(inbox);
|
|
82
|
+
deliveredInbox = inbox;
|
|
83
|
+
deliveredContext = context;
|
|
84
|
+
return present(context);
|
|
85
|
+
}, env);
|
|
86
|
+
if (result !== undefined && deliveredInbox !== undefined && deliveredContext !== undefined) {
|
|
87
|
+
await markCompleteBoundaryObservations(deliveredInbox, deliveredContext);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
async function markCompleteBoundaryObservations(inbox, context) {
|
|
92
|
+
for (const membership of inbox) {
|
|
93
|
+
const complete = membership.notifications
|
|
94
|
+
.filter((notification) => context.includes(notificationMessageId(membership.squarePath, notification.actIndex)) && notification.body.length <= 120)
|
|
95
|
+
.map((notification) => notification.actIndex);
|
|
96
|
+
if (complete.length > 0)
|
|
97
|
+
await markBoundarySeen(membership.squarePath, membership.name, membership.ownerId, complete);
|
|
98
|
+
}
|
|
78
99
|
}
|
package/dist/claude-hook.js
CHANGED
|
@@ -12,11 +12,12 @@ export async function runClaudeHookAsync(inputText, env = process.env) {
|
|
|
12
12
|
if (input === null || typeof input !== 'object')
|
|
13
13
|
return '';
|
|
14
14
|
const value = input;
|
|
15
|
-
if (typeof value.session_id !== 'string'
|
|
15
|
+
if (typeof value.session_id !== 'string')
|
|
16
16
|
return runClaudeHook(inputText, env);
|
|
17
17
|
if (value.hook_event_name === 'SessionStart' || value.hook_event_name === 'SessionResume') {
|
|
18
|
+
const cwd = typeof value.cwd === 'string' ? value.cwd : process.cwd();
|
|
18
19
|
try {
|
|
19
|
-
const context = await automaticSessionStart('claude', value.session_id,
|
|
20
|
+
const context = await automaticSessionStart('claude', value.session_id, cwd, env);
|
|
20
21
|
return context === undefined ? '' : `${JSON.stringify({ hookSpecificOutput: { hookEventName: value.hook_event_name, additionalContext: context } })}\n`;
|
|
21
22
|
}
|
|
22
23
|
catch {
|
|
@@ -24,8 +25,9 @@ export async function runClaudeHookAsync(inputText, env = process.env) {
|
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
if (value.hook_event_name === 'SessionEnd') {
|
|
28
|
+
const cwd = typeof value.cwd === 'string' ? value.cwd : process.cwd();
|
|
27
29
|
try {
|
|
28
|
-
await automaticSessionEnd('claude', value.session_id,
|
|
30
|
+
await automaticSessionEnd('claude', value.session_id, cwd, env);
|
|
29
31
|
}
|
|
30
32
|
catch { /* end remains bounded */ }
|
|
31
33
|
return '';
|
package/dist/cli/context.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type HardCap } from '../model.js';
|
|
2
|
-
export declare const DEFAULT_SQUARE_PATH = ".square/SQUARE.square";
|
|
3
2
|
export interface CommandContext {
|
|
4
|
-
squarePath
|
|
3
|
+
squarePath?: string;
|
|
5
4
|
name?: string;
|
|
6
5
|
homeDir: string;
|
|
7
6
|
command: string;
|
|
@@ -24,7 +23,7 @@ export declare function parseHardCap(value: string): HardCap;
|
|
|
24
23
|
export declare function parseNameList(value: string, flag: string): string[];
|
|
25
24
|
export declare function requireParticipant(name: string | undefined): string;
|
|
26
25
|
export interface ParsedGlobalArgs {
|
|
27
|
-
squarePath
|
|
26
|
+
squarePath?: string;
|
|
28
27
|
explicitSquarePath: boolean;
|
|
29
28
|
multipleSquares: boolean;
|
|
30
29
|
name?: string;
|
|
@@ -32,4 +31,5 @@ export interface ParsedGlobalArgs {
|
|
|
32
31
|
}
|
|
33
32
|
export declare function parseGlobalArgs(rawArgs: string[]): ParsedGlobalArgs;
|
|
34
33
|
export declare function locationIsRequired(command: string): boolean;
|
|
35
|
-
export declare function defaultContext(command: string, squarePath
|
|
34
|
+
export declare function defaultContext(command: string, squarePath?: string, name?: string): CommandContext;
|
|
35
|
+
export declare function requireSquarePath(context: CommandContext): string;
|
package/dist/cli/context.js
CHANGED
|
@@ -3,7 +3,6 @@ import os from 'node:os';
|
|
|
3
3
|
import { commandUsageHint } from '../help.js';
|
|
4
4
|
import { parseParticipantList, validateName } from '../model.js';
|
|
5
5
|
import { localParticipantName } from '../registry.js';
|
|
6
|
-
export const DEFAULT_SQUARE_PATH = '.square/SQUARE.square';
|
|
7
6
|
export function readStdinSync() {
|
|
8
7
|
try {
|
|
9
8
|
return fs.readFileSync(0, 'utf8');
|
|
@@ -87,7 +86,23 @@ export function requireParticipant(name) {
|
|
|
87
86
|
validateName(name);
|
|
88
87
|
return name;
|
|
89
88
|
}
|
|
90
|
-
const LOCATION_REQUIRED_COMMANDS = new Set([
|
|
89
|
+
const LOCATION_REQUIRED_COMMANDS = new Set([
|
|
90
|
+
'build',
|
|
91
|
+
'stream',
|
|
92
|
+
'join',
|
|
93
|
+
'catch',
|
|
94
|
+
'express',
|
|
95
|
+
'listen',
|
|
96
|
+
'ignore',
|
|
97
|
+
'listening',
|
|
98
|
+
'done',
|
|
99
|
+
'hold',
|
|
100
|
+
'resume',
|
|
101
|
+
'history',
|
|
102
|
+
'status',
|
|
103
|
+
'participants',
|
|
104
|
+
'doctor',
|
|
105
|
+
]);
|
|
91
106
|
function configuredLocation() {
|
|
92
107
|
const value = process.env.SQUARE_LOCATION?.trim();
|
|
93
108
|
return value === '' ? undefined : value;
|
|
@@ -125,9 +140,10 @@ export function parseGlobalArgs(rawArgs) {
|
|
|
125
140
|
if (command !== undefined && !['--help', '-h'].includes(command) && locationIsRequired(command) && requestedPath === undefined && configured === undefined) {
|
|
126
141
|
fail(`✕ ${command} needs a square location\n» square ls`);
|
|
127
142
|
}
|
|
128
|
-
const squarePath = requestedPath ?? configured
|
|
129
|
-
if (name === undefined && command !== undefined && locationIsRequired(command))
|
|
143
|
+
const squarePath = requestedPath ?? configured;
|
|
144
|
+
if (name === undefined && squarePath !== undefined && command !== undefined && locationIsRequired(command)) {
|
|
130
145
|
name = localParticipantName(squarePath);
|
|
146
|
+
}
|
|
131
147
|
return { squarePath, explicitSquarePath: explicitSquarePath || configured !== undefined, multipleSquares: false, name, args };
|
|
132
148
|
}
|
|
133
149
|
export function locationIsRequired(command) {
|
|
@@ -136,3 +152,8 @@ export function locationIsRequired(command) {
|
|
|
136
152
|
export function defaultContext(command, squarePath, name) {
|
|
137
153
|
return { command, squarePath, name, homeDir: os.homedir() };
|
|
138
154
|
}
|
|
155
|
+
export function requireSquarePath(context) {
|
|
156
|
+
if (context.squarePath === undefined)
|
|
157
|
+
fail(`✕ ${context.command} needs a square location\n» square ls`);
|
|
158
|
+
return context.squarePath;
|
|
159
|
+
}
|
|
@@ -102,7 +102,7 @@ export function formatHarnessResult(result) {
|
|
|
102
102
|
return `${[...result.notes, ...result.lines, ...result.failures].join('\n')}\n`;
|
|
103
103
|
}
|
|
104
104
|
export function runHarnessCommand(argv, squarePath) {
|
|
105
|
-
const context = { homeDir: os.homedir(), squarePath: squarePath
|
|
105
|
+
const context = { homeDir: os.homedir(), ...(squarePath === undefined ? {} : { squarePath }), command: 'harness' };
|
|
106
106
|
const intent = harnessCommand.parse(argv, context);
|
|
107
107
|
return Promise.resolve(harnessCommand.execute(intent, context)).then(formatHarnessResult);
|
|
108
108
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { diagnoseSquareFile } from '../square-storage.js';
|
|
2
2
|
import { renderDoctorClean, renderDoctorUnfixable, withPathOutput } from '../presentation.js';
|
|
3
3
|
import { inSquareCount } from '../runtime.js';
|
|
4
|
-
import { usage } from './context.js';
|
|
4
|
+
import { requireSquarePath, usage } from './context.js';
|
|
5
5
|
export const doctorCommand = {
|
|
6
6
|
parse(argv, context) {
|
|
7
7
|
if (argv.length > 0)
|
|
@@ -9,15 +9,16 @@ export const doctorCommand = {
|
|
|
9
9
|
return undefined;
|
|
10
10
|
},
|
|
11
11
|
execute(_intent, context) {
|
|
12
|
-
const
|
|
12
|
+
const squarePath = requireSquarePath(context);
|
|
13
|
+
const diagnosis = diagnoseSquareFile(squarePath);
|
|
13
14
|
if (diagnosis.unfixable !== undefined || diagnosis.state === undefined) {
|
|
14
15
|
return {
|
|
15
|
-
output: withPathOutput(
|
|
16
|
+
output: withPathOutput(squarePath, renderDoctorUnfixable(diagnosis.unfixable ?? 'the snapshot could not be decoded')),
|
|
16
17
|
exitCode: 2,
|
|
17
18
|
};
|
|
18
19
|
}
|
|
19
20
|
return {
|
|
20
|
-
output: withPathOutput(
|
|
21
|
+
output: withPathOutput(squarePath, renderDoctorClean(), {
|
|
21
22
|
participantCount: inSquareCount(diagnosis.state),
|
|
22
23
|
}),
|
|
23
24
|
};
|
|
@@ -12,7 +12,7 @@ import { cmdWatch } from '../watch.js';
|
|
|
12
12
|
import { openSquare } from '../square-file-adapter.js';
|
|
13
13
|
import { closeOpenSquare } from '../open-square.js';
|
|
14
14
|
import { historyPresentation, participantsPresentation, statusPresentation } from '../views.js';
|
|
15
|
-
import { fail, parseDurationMs, parseNameList, parseNonNegativeInteger, readStdinSync, requireParticipant, requireValue, usage, } from './context.js';
|
|
15
|
+
import { fail, parseDurationMs, parseNameList, parseNonNegativeInteger, readStdinSync, requireParticipant, requireSquarePath, requireValue, usage, } from './context.js';
|
|
16
16
|
export const listCommand = {
|
|
17
17
|
parse: (argv) => argv,
|
|
18
18
|
async execute(argv, context) {
|
|
@@ -39,10 +39,11 @@ export const streamCommand = {
|
|
|
39
39
|
return { ndjson, forName };
|
|
40
40
|
},
|
|
41
41
|
async execute(intent, context) {
|
|
42
|
+
const squarePath = requireSquarePath(context);
|
|
42
43
|
if (intent.ndjson)
|
|
43
|
-
await cmdStreamNdjson(
|
|
44
|
+
await cmdStreamNdjson(squarePath, intent.forName);
|
|
44
45
|
else
|
|
45
|
-
await cmdStream(
|
|
46
|
+
await cmdStream(squarePath);
|
|
46
47
|
},
|
|
47
48
|
present: () => { },
|
|
48
49
|
};
|
|
@@ -93,8 +94,9 @@ export const catchCommand = {
|
|
|
93
94
|
};
|
|
94
95
|
},
|
|
95
96
|
async execute(intent, context) {
|
|
96
|
-
|
|
97
|
-
await
|
|
97
|
+
const squarePath = requireSquarePath(context);
|
|
98
|
+
await cmdWatch(squarePath, requireParticipant(context.name), intent);
|
|
99
|
+
await sweepPendingNotifications(squarePath);
|
|
98
100
|
},
|
|
99
101
|
present: () => { },
|
|
100
102
|
};
|
|
@@ -111,6 +113,7 @@ function parseTimestamp(value, flag) {
|
|
|
111
113
|
return timestamp;
|
|
112
114
|
}
|
|
113
115
|
function parseHistory(argv, context) {
|
|
116
|
+
const squarePath = requireSquarePath(context);
|
|
114
117
|
const viewer = context.name;
|
|
115
118
|
let lastN = 10;
|
|
116
119
|
let lastNExplicit = false;
|
|
@@ -133,7 +136,7 @@ function parseHistory(argv, context) {
|
|
|
133
136
|
const flag = argv[index];
|
|
134
137
|
if (flag === '--limit') {
|
|
135
138
|
const value = argv[index + 1];
|
|
136
|
-
const retry = `${commandPrefix(
|
|
139
|
+
const retry = `${commandPrefix(squarePath)} history --limit 30`;
|
|
137
140
|
if (value === undefined || value.startsWith('--'))
|
|
138
141
|
fail(`✕ --limit needs a positive number\n» ${retry}`);
|
|
139
142
|
if (!/^[1-9]\d*$/.test(value) || !Number.isSafeInteger(Number(value))) {
|
|
@@ -283,6 +286,7 @@ function renderHistoryProjection(projection, visible, full, squarePath, viewer,
|
|
|
283
286
|
const options = {
|
|
284
287
|
preview,
|
|
285
288
|
actNumber: activity.kind === 'say' ? projection.sayNumbers[activity.index] : undefined,
|
|
289
|
+
perception: activity.perception,
|
|
286
290
|
};
|
|
287
291
|
const rendered = mode === 'archive'
|
|
288
292
|
? renderEventCli(activity, options)
|
|
@@ -302,7 +306,8 @@ function renderHistoryProjection(projection, visible, full, squarePath, viewer,
|
|
|
302
306
|
export const historyCommand = {
|
|
303
307
|
parse(argv, context) { return parseHistory(argv, context); },
|
|
304
308
|
async execute(options, context) {
|
|
305
|
-
const
|
|
309
|
+
const squarePath = requireSquarePath(context);
|
|
310
|
+
const square = await openSquare(squarePath, { clock: nowMs });
|
|
306
311
|
try {
|
|
307
312
|
const projection = await historyPresentation(square, options);
|
|
308
313
|
let events = [...projection.activities];
|
|
@@ -324,9 +329,9 @@ export const historyCommand = {
|
|
|
324
329
|
|| (options.lastN == null && options.full === true)
|
|
325
330
|
|| anonymous;
|
|
326
331
|
const output = pattern === undefined || pattern === ''
|
|
327
|
-
? renderHistoryProjection(projection, events, options.full === true || anonymous,
|
|
328
|
-
: renderGrepActivitiesView(events, totalMatches, options.full,
|
|
329
|
-
return withPathOutput(
|
|
332
|
+
? renderHistoryProjection(projection, events, options.full === true || anonymous, squarePath, options.viewer ?? '', archive ? 'archive' : 'ambient')
|
|
333
|
+
: renderGrepActivitiesView(events, totalMatches, options.full, squarePath, pattern, options.fixed !== undefined);
|
|
334
|
+
return withPathOutput(squarePath, output, { participantCount: projection.participantCount });
|
|
330
335
|
}
|
|
331
336
|
finally {
|
|
332
337
|
await closeOpenSquare(square);
|
|
@@ -338,7 +343,8 @@ export const participantsCommand = {
|
|
|
338
343
|
parse(argv, context) { if (argv.length > 0)
|
|
339
344
|
usage(context.command); return undefined; },
|
|
340
345
|
async execute(_intent, context) {
|
|
341
|
-
const
|
|
346
|
+
const squarePath = requireSquarePath(context);
|
|
347
|
+
const square = await openSquare(squarePath, { clock: nowMs });
|
|
342
348
|
try {
|
|
343
349
|
const now = nowMs();
|
|
344
350
|
const participants = await participantsPresentation(square);
|
|
@@ -349,7 +355,7 @@ export const participantsCommand = {
|
|
|
349
355
|
return ` ${glyph} ${participantIdentity(participant.name)} · ${state} · ${participant.activityCount} ${participant.activityCount === 1 ? 'activity' : 'activities'} · ${last}`;
|
|
350
356
|
});
|
|
351
357
|
const participantCount = participants.filter((participant) => participant.state === 'active').length;
|
|
352
|
-
return withPathOutput(
|
|
358
|
+
return withPathOutput(squarePath, ['participants', ...lines].join('\n'), {
|
|
353
359
|
participantCount,
|
|
354
360
|
});
|
|
355
361
|
}
|
|
@@ -363,7 +369,8 @@ export const statusCommand = {
|
|
|
363
369
|
parse(argv, context) { if (argv.length > 0)
|
|
364
370
|
usage(context.command); return undefined; },
|
|
365
371
|
async execute(_intent, context) {
|
|
366
|
-
const
|
|
372
|
+
const squarePath = requireSquarePath(context);
|
|
373
|
+
const square = await openSquare(squarePath, { clock: nowMs });
|
|
367
374
|
try {
|
|
368
375
|
const presentation = await statusPresentation(square);
|
|
369
376
|
const result = presentation.status;
|
|
@@ -387,7 +394,7 @@ export const statusCommand = {
|
|
|
387
394
|
const attention = !showAttention
|
|
388
395
|
? ''
|
|
389
396
|
: participant.pendingMentionCount > 0
|
|
390
|
-
? `${participant.pendingMentionCount}
|
|
397
|
+
? `${participant.pendingMentionCount} attention${participant.pendingMentionCount === 1 ? '' : 's'} waiting`
|
|
391
398
|
: participant.unreadActivityCount > 0
|
|
392
399
|
? `${participant.unreadActivityCount} change${participant.unreadActivityCount === 1 ? '' : 's'} waiting`
|
|
393
400
|
: 'caught up';
|
|
@@ -405,6 +412,7 @@ export const statusCommand = {
|
|
|
405
412
|
now: result.now,
|
|
406
413
|
preview: 200,
|
|
407
414
|
actNumber: presentation.latestActNumber,
|
|
415
|
+
squareState: presentation.state,
|
|
408
416
|
});
|
|
409
417
|
const latest = visible === ''
|
|
410
418
|
? [result.latestAct === undefined
|
|
@@ -412,14 +420,14 @@ export const statusCommand = {
|
|
|
412
420
|
: ' · latest activity is private to another participant']
|
|
413
421
|
: [` ${visible.replace(/\n/g, '\n ')}`];
|
|
414
422
|
if (visible.includes('more chars') && result.latestAct !== undefined) {
|
|
415
|
-
const prefix = context.name === undefined ? commandPrefix(
|
|
423
|
+
const prefix = context.name === undefined ? commandPrefix(squarePath) : participantCommandPrefix(squarePath, context.name);
|
|
416
424
|
latest.push(`» ${prefix} history --at ${actId(result.latestAct)} -C 2 --full`);
|
|
417
425
|
}
|
|
418
426
|
const output = [
|
|
419
427
|
`${result.activeCount} active · ${result.doneCount} done · cap ${cap} · throttle ${result.throttlePerMinute === undefined ? 'none' : `${result.throttlePerMinute}/min`}`,
|
|
420
428
|
...(hold === undefined ? [] : ['', hold]), '', 'around the square', ...people, '', 'latest', ...latest,
|
|
421
429
|
].join('\n');
|
|
422
|
-
return withPathOutput(
|
|
430
|
+
return withPathOutput(squarePath, output, { participantCount: result.activeCount, held: result.holdActive });
|
|
423
431
|
}
|
|
424
432
|
finally {
|
|
425
433
|
await closeOpenSquare(square);
|
package/dist/cli/program.js
CHANGED
|
@@ -14,7 +14,7 @@ export async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
14
14
|
try {
|
|
15
15
|
const requestedHelp = helpRequest(rawArgs);
|
|
16
16
|
if (requestedHelp !== undefined) {
|
|
17
|
-
await executeRegisteredCommand('help', requestedHelp.command === undefined ? [] : [requestedHelp.command], defaultContext('help'
|
|
17
|
+
await executeRegisteredCommand('help', requestedHelp.command === undefined ? [] : [requestedHelp.command], defaultContext('help'));
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
20
|
const parsed = parseGlobalArgs(rawArgs);
|
package/dist/cli/registry.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildCommand, doneCommand, expressCommand, holdCommand, joinCommand, resumeCommand } from './square-commands.js';
|
|
1
|
+
import { buildCommand, doneCommand, expressCommand, holdCommand, ignoreCommand, joinCommand, listenCommand, listeningCommand, 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';
|
|
@@ -14,6 +14,9 @@ export const commandRegistry = [
|
|
|
14
14
|
{ names: ['codex-hook'], spec: codexHookCommand },
|
|
15
15
|
{ names: ['catch'], spec: catchCommand },
|
|
16
16
|
{ names: ['express'], spec: expressCommand },
|
|
17
|
+
{ names: ['listen'], spec: listenCommand },
|
|
18
|
+
{ names: ['ignore'], spec: ignoreCommand },
|
|
19
|
+
{ names: ['listening'], spec: listeningCommand },
|
|
17
20
|
{ names: ['done'], spec: doneCommand },
|
|
18
21
|
{ names: ['hold'], spec: holdCommand },
|
|
19
22
|
{ names: ['resume'], spec: resumeCommand },
|
|
@@ -23,9 +23,16 @@ interface BodyIntent {
|
|
|
23
23
|
name: string;
|
|
24
24
|
body?: string;
|
|
25
25
|
}
|
|
26
|
+
interface ListenerIntent {
|
|
27
|
+
name: string;
|
|
28
|
+
target?: string;
|
|
29
|
+
}
|
|
26
30
|
export declare const buildCommand: CommandSpec<BuildIntent, string>;
|
|
27
31
|
export declare const joinCommand: CommandSpec<JoinIntent, string>;
|
|
28
32
|
export declare const expressCommand: CommandSpec<ActivityIntent>;
|
|
33
|
+
export declare const listenCommand: CommandSpec<ListenerIntent, string>;
|
|
34
|
+
export declare const ignoreCommand: CommandSpec<ListenerIntent, string>;
|
|
35
|
+
export declare const listeningCommand: CommandSpec<ListenerIntent, string>;
|
|
29
36
|
export declare const doneCommand: CommandSpec<BodyIntent, string>;
|
|
30
37
|
export declare const holdCommand: CommandSpec<BodyIntent, string>;
|
|
31
38
|
export declare const resumeCommand: CommandSpec<{
|