@astrosheep/square 0.3.34 → 0.3.35
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 +10 -10
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/activity-feed.js +1 -1
- package/dist/activity.d.ts +1 -0
- package/dist/activity.js +6 -3
- package/dist/artifact.js +2 -1
- package/dist/automatic-session.js +17 -1
- package/dist/boundary-presentation.d.ts +1 -0
- package/dist/boundary-presentation.js +12 -1
- package/dist/catch-decisions.d.ts +4 -0
- package/dist/catch-decisions.js +23 -7
- package/dist/cli/context.d.ts +1 -0
- package/dist/cli/context.js +26 -1
- package/dist/cli/observation-commands.d.ts +7 -1
- package/dist/cli/observation-commands.js +149 -63
- package/dist/cli/program.js +8 -2
- package/dist/cli/square-commands.d.ts +3 -1
- package/dist/cli/square-commands.js +42 -16
- package/dist/codex-queue.d.ts +1 -0
- package/dist/codex-queue.js +1 -1
- package/dist/decisions.d.ts +1 -0
- package/dist/decisions.js +29 -14
- package/dist/delivery-health.d.ts +1 -0
- package/dist/delivery-health.js +19 -6
- package/dist/delivery.d.ts +2 -0
- package/dist/delivery.js +1 -0
- package/dist/harness-links.js +15 -3
- package/dist/harness.js +3 -1
- package/dist/help.js +25 -16
- package/dist/inbox.d.ts +2 -0
- package/dist/inbox.js +4 -2
- package/dist/list.js +77 -24
- package/dist/model.d.ts +1 -3
- package/dist/paseo-connection.js +9 -3
- package/dist/paseo-state.d.ts +4 -1
- package/dist/paseo-state.js +2 -2
- package/dist/presentation.d.ts +4 -0
- package/dist/presentation.js +41 -12
- package/dist/runtime.d.ts +1 -0
- package/dist/square-actions.d.ts +5 -0
- package/dist/square-actions.js +31 -7
- package/dist/square-core.d.ts +11 -1
- package/dist/square-core.js +12 -9
- package/dist/square-facade.d.ts +5 -2
- package/dist/square-file-adapter.d.ts +1 -1
- package/dist/square-file-adapter.js +11 -4
- package/dist/square-wiring.d.ts +1 -0
- package/dist/square-wiring.js +5 -1
- package/dist/stream.d.ts +8 -1
- package/dist/stream.js +17 -6
- package/dist/views.d.ts +11 -5
- package/dist/views.js +42 -16
- package/dist/wake-sink.d.ts +2 -0
- package/dist/wake-sink.js +10 -1
- package/dist/watch.js +26 -9
- package/extensions/square-pi.js +41 -7
- package/package.json +1 -1
- package/skills/brainstorm/SKILL.md +8 -6
- package/skills/square/SKILL.md +10 -10
|
@@ -21,7 +21,7 @@ leave for good : done (permanent — not the end of a round)
|
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
23
|
square --location .square/PUBLIC.square --as <name> catch --now
|
|
24
|
-
square --location .square/PUBLIC.square --as <name> express
|
|
24
|
+
square --location .square/PUBLIC.square --as <name> express --mention alice "your thought"
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
For any other square, find it, then `join` it once (`ls` is short for `list`; `--depth N` widens the search):
|
|
@@ -46,22 +46,22 @@ square --location <square> --as <name> status
|
|
|
46
46
|
Everything you land is one activity — pure speech, pure action, or both. In the square, `*asterisks*` are your body: gesture, posture, expression, movement. **Always give speech a body.** Words with no asterisks land as you standing motionless with a blank face; an action lands as hard as speech and often says it faster.
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
square --location <square> --as <name> express
|
|
50
|
-
square --location <square> --as <name> express "*nods slowly
|
|
51
|
-
square --location <square> --as <name> express "*stands*
|
|
49
|
+
square --location <square> --as <name> express --mention alice "I disagree — the cache is the wrong layer for this."
|
|
50
|
+
square --location <square> --as <name> express --mention bob "*nods slowly*"
|
|
51
|
+
square --location <square> --as <name> express --mention alice "*stands* Fine. I'll take the migration."
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
For a longer activity, use stdin:
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
square --location <square> --as <name> express - <<'EOF'
|
|
57
|
+
square --location <square> --as <name> express --mention bob - <<'EOF'
|
|
58
58
|
*drops a rough sketch onto the table*
|
|
59
59
|
|
|
60
|
-
The ownership boundary belongs here.
|
|
60
|
+
The ownership boundary belongs here. Does this match your read?
|
|
61
61
|
EOF
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
**Addressing.**
|
|
64
|
+
**Addressing.** Use `--mention <name>` (repeatable) to address participants; they hear the full body even when they are not listening, and everyone else sees you walk over to them. Use `--no-mention` to land a bare activity; `listen` opts a participant into future bare delivery. Use `--bell` only when every participant needs it. An `@name` in the body is ordinary Markdown and does not address anyone. Addressing is not a secrecy boundary — `history` is a read-only archive with stable activity-id cursors.
|
|
65
65
|
|
|
66
66
|
**Discipline.** Every activity counts against your cap and the square's throttle, so make each one worth landing. Keep private progress and tool chatter out — express only when another participant needs the thought, question, or decision.
|
|
67
67
|
|
|
@@ -78,7 +78,7 @@ square --location <square> --as <name> catch --now --mention # take in pen
|
|
|
78
78
|
square --location <square> --as <name> catch --now --from <names> # take in pending activity from named participants
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
Every catch needs exactly one mode: `--now` or `--idle <duration>`. `--mention` and `--from` filter either mode; they do not replace it. Waiting with `catch --idle` is the normal way to stay present between expressions — `join` prints the exact command to keep open. Do not build a polling loop.
|
|
81
|
+
Every catch needs exactly one mode: `--now` or `--idle <duration>`. `--mention` and `--from` filter either mode; they do not replace it. Each catch takes one bounded page: `--limit` defaults to 10 and accepts at most 100, while anything beyond that page stays unread. Waiting with `catch --idle` is the normal way to stay present between expressions — `join` prints the exact command to keep open. Do not build a polling loop.
|
|
82
82
|
|
|
83
83
|
## Listen
|
|
84
84
|
|
|
@@ -101,11 +101,11 @@ square history --limit 5 # most recent 5, oldest to newest
|
|
|
101
101
|
square history --before act/12 --limit 5 # the page before act/12
|
|
102
102
|
square history --after act/12 --limit 5 # the page after act/12
|
|
103
103
|
square history --limit 5 --order desc # newest first
|
|
104
|
-
square history --no-truncate #
|
|
104
|
+
square history --no-truncate # show every original body
|
|
105
105
|
square history --grep 'term' # search
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
See `square history --help` for advanced usage. Never read or parse the binary Square artifact directly.
|
|
108
|
+
See `square history --help` for advanced usage. Never read or parse the binary Square artifact directly. One result shows its full body; multiple results use previews. `--no-truncate` shows every original body. Follow the printed activity-id command to continue page by page.
|
|
109
109
|
|
|
110
110
|
## Hold
|
|
111
111
|
|
package/dist/activity-feed.js
CHANGED
|
@@ -21,7 +21,7 @@ function matchesParticipants(act, participants) {
|
|
|
21
21
|
}
|
|
22
22
|
export function matchesFeedFilter(act, filter, recipients) {
|
|
23
23
|
if (act.kind === 'say') {
|
|
24
|
-
return matchesCatchFilter({ actor: act.actor, body: act.body, reach: act.reach, ...(recipients === undefined ? {} : { recipients }) }, filter);
|
|
24
|
+
return matchesCatchFilter({ actor: act.actor, body: act.body, mentions: act.mentions, reach: act.reach, ...(recipients === undefined ? {} : { recipients }) }, filter);
|
|
25
25
|
}
|
|
26
26
|
return filter.mention === undefined && matchesParticipants(act, filter.participants);
|
|
27
27
|
}
|
package/dist/activity.d.ts
CHANGED
package/dist/activity.js
CHANGED
|
@@ -79,11 +79,14 @@ export async function cmdActivity(squarePath, name, activity, resolveBody, opts)
|
|
|
79
79
|
const pendingPublic = before.pendingPublic;
|
|
80
80
|
const pendingRoomChanges = before.pendingRoomChanges;
|
|
81
81
|
try {
|
|
82
|
-
await participant.express(body, {
|
|
82
|
+
const result = await participant.express(body, {
|
|
83
83
|
force,
|
|
84
|
+
...(opts.mentions === undefined ? {} : { mentions: opts.mentions }),
|
|
84
85
|
...(reach === undefined ? {} : { reach }),
|
|
85
86
|
...(opts.reply === undefined ? {} : { reply: formatActivityId(opts.reply) }),
|
|
86
87
|
});
|
|
88
|
+
if (result.delivery?.notCapable)
|
|
89
|
+
process.stderr.write(`! wake not-capable: ${result.delivery.notCapable}\n`);
|
|
87
90
|
const freshSquare = await openSquare(squarePath, { clock: nowMs });
|
|
88
91
|
const fresh = await activityPresentation(freshSquare, knownName).finally(() => closeOpenSquare(freshSquare));
|
|
89
92
|
const headerCount = fresh.participantCount;
|
|
@@ -136,7 +139,7 @@ export async function cmdActivity(squarePath, name, activity, resolveBody, opts)
|
|
|
136
139
|
const delayMs = error.facts?.retryAfterMs ?? SLEEP_MS;
|
|
137
140
|
if (noWait) {
|
|
138
141
|
const draftPath = saveActivityDraft(squarePath, name, rawInput);
|
|
139
|
-
process.stdout.write(renderExpressNoWait({ squarePath, name: knownName, reason: 'throttled', delayMs, draftPath, participantCount: headerCount, held }));
|
|
142
|
+
process.stdout.write(renderExpressNoWait({ squarePath, name: knownName, reason: 'throttled', delayMs, draftPath, participantCount: headerCount, held, forceCommand: opts.forceCommand }));
|
|
140
143
|
process.exit(1);
|
|
141
144
|
}
|
|
142
145
|
if (announcedWait !== 'throttled') {
|
|
@@ -150,7 +153,7 @@ export async function cmdActivity(squarePath, name, activity, resolveBody, opts)
|
|
|
150
153
|
const holdReason = fresh.holdReason;
|
|
151
154
|
if (noWait) {
|
|
152
155
|
const draftPath = saveActivityDraft(squarePath, name, rawInput);
|
|
153
|
-
process.stdout.write(renderExpressNoWait({ squarePath, name: knownName, reason: 'held', holdReason, draftPath, participantCount: headerCount, held }));
|
|
156
|
+
process.stdout.write(renderExpressNoWait({ squarePath, name: knownName, reason: 'held', holdReason, draftPath, participantCount: headerCount, held, forceCommand: opts.forceCommand }));
|
|
154
157
|
process.exit(1);
|
|
155
158
|
}
|
|
156
159
|
if (announcedWait !== 'held') {
|
package/dist/artifact.js
CHANGED
|
@@ -99,9 +99,10 @@ function validateStoredAct(value) {
|
|
|
99
99
|
&& validateActor(value.actor, true)
|
|
100
100
|
&& (value.body === undefined || typeof value.body === 'string');
|
|
101
101
|
case 'say':
|
|
102
|
-
return hasExactKeys(value, ['kind', 'actor', 'at', 'body', 'index'], ['reach', 'reply'])
|
|
102
|
+
return hasExactKeys(value, ['kind', 'actor', 'at', 'body', 'index'], ['mentions', 'reach', 'reply'])
|
|
103
103
|
&& validateActor(value.actor, true)
|
|
104
104
|
&& typeof value.body === 'string'
|
|
105
|
+
&& (value.mentions === undefined || isStringArray(value.mentions))
|
|
105
106
|
&& (value.reach === undefined || value.reach === 'bell')
|
|
106
107
|
&& (value.reply === undefined || isNonNegativeInteger(value.reply));
|
|
107
108
|
case 'hold':
|
|
@@ -5,6 +5,7 @@ import { closeOpenSquare } from './open-square.js';
|
|
|
5
5
|
import { Square } from './square-wiring.js';
|
|
6
6
|
import { entryPresentation } from './views.js';
|
|
7
7
|
import { automaticParticipant } from './participant-identity.js';
|
|
8
|
+
import { SquareError } from './model.js';
|
|
8
9
|
import { createHostLedgerPort } from './host-ledger-file-adapter.js';
|
|
9
10
|
import { projectSessionBindings } from './square-projections.js';
|
|
10
11
|
import { publishWakeRoute, retireWakeRouteFromArtifact, resolvePrimaryWakeRoute, defaultWakeRouteCapabilities } from './routes.js';
|
|
@@ -50,7 +51,22 @@ export async function automaticSessionStart(provider, sessionId, cwd, env = proc
|
|
|
50
51
|
return undefined;
|
|
51
52
|
}
|
|
52
53
|
const name = automaticParticipant(provider, sessionId, env);
|
|
53
|
-
|
|
54
|
+
const hostLedger = hostLedgerForEnv(env);
|
|
55
|
+
let bindings;
|
|
56
|
+
let entry;
|
|
57
|
+
try {
|
|
58
|
+
bindings = await hostLedger.listPresence({ location: squarePath, participant: name, scopes: ['user', 'local'] });
|
|
59
|
+
entry = await entryPresentation(reader, name);
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await closeOpenSquare(reader);
|
|
63
|
+
}
|
|
64
|
+
if (bindings.some((binding) => binding.session !== sessionId)) {
|
|
65
|
+
throw new SquareError('already_joined', `${name} is already bound to another session`);
|
|
66
|
+
}
|
|
67
|
+
if (entry.joined && !bindings.some((binding) => binding.session === sessionId)) {
|
|
68
|
+
throw new SquareError('already_joined', `${name} is already joined by another session`);
|
|
69
|
+
}
|
|
54
70
|
const scopedEnv = operationEnv(provider, sessionId, env);
|
|
55
71
|
const square = await Square.at({ path: squarePath, hostLedger: hostLedgerForEnv(scopedEnv), env: scopedEnv });
|
|
56
72
|
try {
|
|
@@ -2,4 +2,5 @@ import type { InboxMembership } from './model.js';
|
|
|
2
2
|
/** A fresh blocking catch owns only the notifications admitted by its filter. */
|
|
3
3
|
export declare function pendingAtBoundary(inbox: InboxMembership[]): InboxMembership[];
|
|
4
4
|
export declare function renderPendingAtBoundary(inbox: InboxMembership[]): string;
|
|
5
|
+
/** Serialize a boundary across hook processes so one stale inbox cannot render twice. */
|
|
5
6
|
export declare function presentPendingAtBoundary<T>(sessionId: string, present: (context: string) => T | Promise<T>, lookup?: (sessionId: string, env?: NodeJS.ProcessEnv) => Promise<InboxMembership[]> | InboxMembership[], env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<T | undefined>;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
1
4
|
import { leaseOwnsNotification } from './delivery.js';
|
|
5
|
+
import { withFileLock } from './file-lock.js';
|
|
2
6
|
import { openSquare } from './square-file-adapter.js';
|
|
3
7
|
import { closeOpenSquare } from './open-square.js';
|
|
4
8
|
import { presentPending } from './presentation-operations.js';
|
|
@@ -80,7 +84,7 @@ function renderBoundary(inbox) {
|
|
|
80
84
|
complete,
|
|
81
85
|
};
|
|
82
86
|
}
|
|
83
|
-
|
|
87
|
+
async function presentPendingAtBoundaryUnlocked(sessionId, present, lookup = sessionInbox, env = process.env, signal) {
|
|
84
88
|
const inbox = await lookup(sessionId, env);
|
|
85
89
|
if (signal?.aborted)
|
|
86
90
|
return undefined;
|
|
@@ -138,3 +142,10 @@ export async function presentPendingAtBoundary(sessionId, present, lookup = sess
|
|
|
138
142
|
}
|
|
139
143
|
return result;
|
|
140
144
|
}
|
|
145
|
+
/** Serialize a boundary across hook processes so one stale inbox cannot render twice. */
|
|
146
|
+
export async function presentPendingAtBoundary(sessionId, present, lookup = sessionInbox, env = process.env, signal) {
|
|
147
|
+
const ledgerRoot = env.SQUARE_HOST_LEDGER_USER
|
|
148
|
+
?? (env.SQUARE_REGISTRY === undefined ? path.join(os.homedir(), '.square', 'host-ledger') : path.dirname(env.SQUARE_REGISTRY));
|
|
149
|
+
const ownerKey = createHash('sha256').update(sessionId).digest('hex');
|
|
150
|
+
return withFileLock(path.join(ledgerRoot, `presentation-boundary-${ownerKey}.lock`), { retryMs: 10, staleMs: 300_000, signal }, () => presentPendingAtBoundaryUnlocked(sessionId, present, lookup, env, signal));
|
|
151
|
+
}
|
|
@@ -7,11 +7,15 @@ export interface CatchDecision {
|
|
|
7
7
|
readonly perceptions: ReadonlyMap<number, Perception>;
|
|
8
8
|
readonly consumedThrough: string | null;
|
|
9
9
|
readonly changed: boolean;
|
|
10
|
+
readonly remaining: number;
|
|
10
11
|
}
|
|
12
|
+
export declare const CATCH_DEFAULT_LIMIT = 10;
|
|
13
|
+
export declare const CATCH_MAX_LIMIT = 100;
|
|
11
14
|
export interface CatchProjection {
|
|
12
15
|
cursorFor(name: string): number;
|
|
13
16
|
directedTo(activity: StoredAct, name: string): boolean;
|
|
14
17
|
perceive(activity: StoredAct, name: string): Perception;
|
|
18
|
+
isSeen(name: string, index: number): boolean;
|
|
15
19
|
}
|
|
16
20
|
/** Pure catch selection and acknowledgement over one SquareState snapshot. */
|
|
17
21
|
export declare function decideCatch(state: SquareState, requestedName: string, options: CatchOptions, at: number, project?: (state: SquareState) => CatchProjection): CatchDecision;
|
package/dist/catch-decisions.js
CHANGED
|
@@ -1,43 +1,58 @@
|
|
|
1
1
|
import { audienceOf, formatActivityId, replayLandedAudiences } from './square-core.js';
|
|
2
|
-
import { matchesMentionTarget, readCursor, recordObservation } from './runtime.js';
|
|
2
|
+
import { matchesMentionTarget, observationFor, readCursor, recordObservation } from './runtime.js';
|
|
3
3
|
import { participantIdentity } from './participant-identity.js';
|
|
4
4
|
import { SquareError, sameName, validateName } from './model.js';
|
|
5
|
-
import { resolveRosterName
|
|
5
|
+
import { resolveRosterName } from './runtime.js';
|
|
6
|
+
export const CATCH_DEFAULT_LIMIT = 10;
|
|
7
|
+
export const CATCH_MAX_LIMIT = 100;
|
|
8
|
+
function catchLimit(value) {
|
|
9
|
+
const limit = value ?? CATCH_DEFAULT_LIMIT;
|
|
10
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > CATCH_MAX_LIMIT) {
|
|
11
|
+
throw new SquareError('invalid_args', `Catch limit must be a positive integer no greater than ${CATCH_MAX_LIMIT}.`);
|
|
12
|
+
}
|
|
13
|
+
return limit;
|
|
14
|
+
}
|
|
6
15
|
function defaultProjection(state) {
|
|
7
16
|
const landed = replayLandedAudiences(state.acts);
|
|
8
17
|
return {
|
|
9
18
|
cursorFor: (name) => readCursor(state, name, landed),
|
|
10
19
|
directedTo: (activity, name) => activity.kind === 'say' && landed.includes(activity, name),
|
|
11
20
|
perceive: (activity, name) => activity.kind !== 'say' || sameName(activity.actor, name) || landed.includes(activity, name) ? 'full' : 'presence',
|
|
21
|
+
isSeen: (name, index) => observationFor(state, name, index)?.state === 'seen',
|
|
12
22
|
};
|
|
13
23
|
}
|
|
14
24
|
function resolveCatchName(state, requestedName) {
|
|
15
25
|
validateName(requestedName);
|
|
16
26
|
const known = resolveRosterName(state, requestedName);
|
|
17
27
|
if (known === undefined) {
|
|
18
|
-
throw new SquareError('invalid_args', `Unknown participant "${participantIdentity(requestedName)}"
|
|
28
|
+
throw new SquareError('invalid_args', `Unknown participant "${participantIdentity(requestedName)}".`);
|
|
19
29
|
}
|
|
20
30
|
return known;
|
|
21
31
|
}
|
|
22
32
|
/** Pure catch selection and acknowledgement over one SquareState snapshot. */
|
|
23
33
|
export function decideCatch(state, requestedName, options, at, project = defaultProjection) {
|
|
24
34
|
const viewer = resolveCatchName(state, requestedName);
|
|
35
|
+
const limit = catchLimit(options.limit);
|
|
25
36
|
const delivery = project(state);
|
|
26
|
-
const cursor = delivery.cursorFor(viewer);
|
|
27
37
|
const from = options.from;
|
|
28
38
|
const mentionOnly = options.mention === true;
|
|
29
39
|
const delivered = [];
|
|
30
40
|
const perceptions = new Map();
|
|
41
|
+
const matching = [];
|
|
31
42
|
for (const activity of state.acts) {
|
|
32
|
-
if (activity.
|
|
43
|
+
if (activity.kind !== 'say' || !delivery.directedTo(activity, viewer))
|
|
33
44
|
continue;
|
|
34
45
|
if (from !== undefined && !from.some((participant) => sameName(participant, activity.actor)))
|
|
35
46
|
continue;
|
|
36
47
|
if (mentionOnly && audienceOf(activity).kind !== 'bell' && !matchesMentionTarget(activity, viewer))
|
|
37
48
|
continue;
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
if (delivery.isSeen(viewer, activity.index))
|
|
50
|
+
continue;
|
|
51
|
+
matching.push(activity);
|
|
40
52
|
}
|
|
53
|
+
delivered.push(...matching.slice(0, limit));
|
|
54
|
+
for (const activity of delivered)
|
|
55
|
+
perceptions.set(activity.index, delivery.perceive(activity, viewer));
|
|
41
56
|
let changed = false;
|
|
42
57
|
for (const activity of delivered) {
|
|
43
58
|
changed = recordObservation(state, viewer, activity.index, 'seen', at) || changed;
|
|
@@ -49,5 +64,6 @@ export function decideCatch(state, requestedName, options, at, project = default
|
|
|
49
64
|
perceptions,
|
|
50
65
|
consumedThrough: consumed < 0 ? null : formatActivityId(consumed),
|
|
51
66
|
changed,
|
|
67
|
+
remaining: Math.max(0, matching.length - delivered.length),
|
|
52
68
|
};
|
|
53
69
|
}
|
package/dist/cli/context.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export declare function usage(command: string): never;
|
|
|
18
18
|
export declare function requireValue(args: string[], index: number, flag: string): string;
|
|
19
19
|
export declare function parseDurationMs(value: string, flag: string): number;
|
|
20
20
|
export declare function parsePositiveInteger(value: string, flag: string): number;
|
|
21
|
+
export declare function parseBoundedLimit(value: string | undefined, flag: string, max: number, retry: string): number;
|
|
21
22
|
export declare function parseNonNegativeInteger(value: string, flag: string): number;
|
|
22
23
|
export declare function parseHardCap(value: string): HardCap;
|
|
23
24
|
export declare function parseNameList(value: string, flag: string): string[];
|
package/dist/cli/context.js
CHANGED
|
@@ -57,6 +57,15 @@ export function parsePositiveInteger(value, flag) {
|
|
|
57
57
|
fail(`Invalid ${flag}: value is too large.`);
|
|
58
58
|
return parsed;
|
|
59
59
|
}
|
|
60
|
+
export function parseBoundedLimit(value, flag, max, retry) {
|
|
61
|
+
if (value === undefined || value.startsWith('--') || !/^[1-9]\d*$/.test(value) || !Number.isSafeInteger(Number(value))) {
|
|
62
|
+
fail(`✕ ${flag} needs a positive integer\n» ${retry}`);
|
|
63
|
+
}
|
|
64
|
+
const parsed = Number(value);
|
|
65
|
+
if (parsed > max)
|
|
66
|
+
fail(`✕ ${flag} is capped at ${max}\n» ${retry}`);
|
|
67
|
+
return parsed;
|
|
68
|
+
}
|
|
60
69
|
export function parseNonNegativeInteger(value, flag) {
|
|
61
70
|
if (!/^\d+$/.test(value))
|
|
62
71
|
fail(`Invalid ${flag}: expected a non-negative integer.`);
|
|
@@ -117,10 +126,22 @@ function configuredName() {
|
|
|
117
126
|
validateName(value);
|
|
118
127
|
return value;
|
|
119
128
|
}
|
|
129
|
+
function withoutAsCommand(rawArgs) {
|
|
130
|
+
const args = [];
|
|
131
|
+
for (let index = 0; index < rawArgs.length; index++) {
|
|
132
|
+
if (rawArgs[index] === '--as') {
|
|
133
|
+
index += 1;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
args.push(rawArgs[index]);
|
|
137
|
+
}
|
|
138
|
+
return `square ${args.map((arg) => /^[A-Za-z0-9_./:=@+,-]+$/.test(arg) ? arg : `'${arg.replace(/'/g, "'\\''")}'`).join(' ')}`;
|
|
139
|
+
}
|
|
120
140
|
export async function parseGlobalArgs(rawArgs) {
|
|
121
141
|
const args = [...rawArgs];
|
|
122
142
|
let requestedPath;
|
|
123
143
|
let name;
|
|
144
|
+
let hasExplicitName = false;
|
|
124
145
|
for (let index = 0; index < args.length; index++) {
|
|
125
146
|
if (args[index] === '--location') {
|
|
126
147
|
requestedPath = requireValue(args, index, args[index]);
|
|
@@ -129,16 +150,20 @@ export async function parseGlobalArgs(rawArgs) {
|
|
|
129
150
|
}
|
|
130
151
|
else if (args[index] === '--as') {
|
|
131
152
|
name = requireValue(args, index, args[index]);
|
|
153
|
+
hasExplicitName = true;
|
|
132
154
|
args.splice(index, 2);
|
|
133
155
|
index -= 1;
|
|
134
156
|
}
|
|
135
157
|
}
|
|
158
|
+
const command = args[0];
|
|
159
|
+
if (command === 'history' && hasExplicitName) {
|
|
160
|
+
fail(`✕ history is an archive and does not use --as\n» ${withoutAsCommand(rawArgs)}`);
|
|
161
|
+
}
|
|
136
162
|
if (name === undefined)
|
|
137
163
|
name = configuredName();
|
|
138
164
|
if (name !== undefined)
|
|
139
165
|
validateName(name);
|
|
140
166
|
const explicitSquarePath = requestedPath !== undefined;
|
|
141
|
-
const command = args[0];
|
|
142
167
|
const configured = configuredLocation();
|
|
143
168
|
if (command !== undefined && !['--help', '-h'].includes(command) && locationIsRequired(command) && requestedPath === undefined && configured === undefined) {
|
|
144
169
|
fail(`✕ ${command} needs a square location\n» square ls`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ActivitiesOptions, type WatchOptions } from '../model.js';
|
|
2
|
+
import { type StreamStart } from '../stream.js';
|
|
2
3
|
import { type CommandSpec } from './context.js';
|
|
3
4
|
interface HistoryCommandOptions extends ActivitiesOptions {
|
|
4
5
|
noTruncate: boolean;
|
|
@@ -8,15 +9,20 @@ export declare const listCommand: CommandSpec<string[]>;
|
|
|
8
9
|
interface StreamIntent {
|
|
9
10
|
ndjson: boolean;
|
|
10
11
|
forName?: string;
|
|
12
|
+
start: StreamStart;
|
|
11
13
|
}
|
|
12
14
|
export declare const streamCommand: CommandSpec<StreamIntent>;
|
|
13
15
|
export declare const catchCommand: CommandSpec<WatchOptions>;
|
|
14
16
|
export declare const historyCommand: CommandSpec<HistoryCommandOptions, string>;
|
|
15
|
-
|
|
17
|
+
interface ParticipantsCommandOptions {
|
|
18
|
+
limit: number;
|
|
19
|
+
}
|
|
20
|
+
export declare const participantsCommand: CommandSpec<ParticipantsCommandOptions, string>;
|
|
16
21
|
export declare const statusCommand: CommandSpec<undefined, string>;
|
|
17
22
|
interface InboxIntent {
|
|
18
23
|
sessionId: string;
|
|
19
24
|
json: boolean;
|
|
25
|
+
limit: number;
|
|
20
26
|
}
|
|
21
27
|
export declare const inboxCommand: CommandSpec<InboxIntent, string>;
|
|
22
28
|
export declare const claudeHookCommand: CommandSpec<undefined, string>;
|