@astrosheep/square 0.3.33 → 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.
Files changed (61) hide show
  1. package/claude-plugin/.claude-plugin/plugin.json +1 -1
  2. package/claude-plugin/skills/square/SKILL.md +10 -10
  3. package/codex-plugin/.codex-plugin/plugin.json +1 -1
  4. package/dist/activity-feed.js +1 -1
  5. package/dist/activity.d.ts +1 -0
  6. package/dist/activity.js +3 -2
  7. package/dist/artifact.js +2 -1
  8. package/dist/automatic-session.js +17 -1
  9. package/dist/boundary-presentation.d.ts +1 -0
  10. package/dist/boundary-presentation.js +16 -1
  11. package/dist/catch-decisions.d.ts +4 -0
  12. package/dist/catch-decisions.js +23 -7
  13. package/dist/cli/context.d.ts +1 -0
  14. package/dist/cli/context.js +26 -1
  15. package/dist/cli/observation-commands.d.ts +7 -1
  16. package/dist/cli/observation-commands.js +149 -63
  17. package/dist/cli/program.js +8 -2
  18. package/dist/cli/square-commands.d.ts +3 -1
  19. package/dist/cli/square-commands.js +42 -16
  20. package/dist/codex-queue.d.ts +1 -0
  21. package/dist/codex-queue.js +1 -1
  22. package/dist/decisions.d.ts +1 -0
  23. package/dist/decisions.js +29 -14
  24. package/dist/delivery-health.d.ts +1 -0
  25. package/dist/delivery-health.js +19 -6
  26. package/dist/delivery-operations.js +5 -5
  27. package/dist/delivery.d.ts +2 -0
  28. package/dist/delivery.js +1 -0
  29. package/dist/harness-links.js +15 -3
  30. package/dist/harness.js +3 -1
  31. package/dist/help.js +25 -16
  32. package/dist/inbox.d.ts +2 -0
  33. package/dist/inbox.js +4 -2
  34. package/dist/list.js +77 -24
  35. package/dist/model.d.ts +1 -3
  36. package/dist/paseo-connection.js +9 -3
  37. package/dist/paseo-state.d.ts +4 -1
  38. package/dist/paseo-state.js +2 -2
  39. package/dist/presentation.d.ts +4 -0
  40. package/dist/presentation.js +41 -12
  41. package/dist/runtime.d.ts +1 -0
  42. package/dist/square-actions.d.ts +5 -0
  43. package/dist/square-actions.js +31 -7
  44. package/dist/square-core.d.ts +11 -1
  45. package/dist/square-core.js +12 -9
  46. package/dist/square-facade.d.ts +5 -2
  47. package/dist/square-file-adapter.d.ts +1 -1
  48. package/dist/square-file-adapter.js +11 -4
  49. package/dist/square-wiring.d.ts +1 -0
  50. package/dist/square-wiring.js +5 -1
  51. package/dist/stream.d.ts +8 -1
  52. package/dist/stream.js +17 -6
  53. package/dist/views.d.ts +11 -5
  54. package/dist/views.js +42 -16
  55. package/dist/wake-sink.d.ts +2 -0
  56. package/dist/wake-sink.js +10 -1
  57. package/dist/watch.js +26 -9
  58. package/extensions/square-pi.js +41 -7
  59. package/package.json +1 -1
  60. package/skills/brainstorm/SKILL.md +8 -6
  61. package/skills/square/SKILL.md +10 -10
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "square",
3
- "version": "0.3.33",
3
+ "version": "0.3.35",
4
4
  "description": "Native Claude Code turn-boundary delivery for Square participants",
5
5
  "author": {
6
6
  "name": "Square"
@@ -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 "@alice your thought"
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 "@alice I disagree — the cache is the wrong layer for this."
50
- square --location <square> --as <name> express "*nods slowly to @bob*"
51
- square --location <square> --as <name> express "*stands* @alice, fine. I'll take the migration."
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. @bob, does this match your read?
60
+ The ownership boundary belongs here. Does this match your read?
61
61
  EOF
62
62
  ```
63
63
 
64
- **Addressing.** Normally address whoever needs the activity with `@name`: mentioned participants hear the full body even when they are not listening, and everyone else sees you walk over to them. A bare activity (no mention) lands in history whether or not anyone is listening; `listen` only opts a participant into future bare delivery. Use `--bell` only when every participant needs it. Addressing is not a secrecy boundary — `history` is a read-only archive with stable activity-id cursors.
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 # expand preview bodies
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. Bodies are previews by default; follow the printed activity-id command to continue page by page.
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
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "square",
3
- "version": "0.3.33",
3
+ "version": "0.3.35",
4
4
  "description": "Shared Square activity with reliable participant attention at Codex boundaries.",
5
5
  "author": {
6
6
  "name": "Square"
@@ -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
  }
@@ -2,6 +2,7 @@ export interface ActivityOptions {
2
2
  force?: boolean;
3
3
  forceCommand: string;
4
4
  noWait?: boolean;
5
+ mentions?: readonly string[];
5
6
  reach?: import('./model.js').Reach;
6
7
  reply?: number;
7
8
  }
package/dist/activity.js CHANGED
@@ -81,6 +81,7 @@ export async function cmdActivity(squarePath, name, activity, resolveBody, opts)
81
81
  try {
82
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
  });
@@ -138,7 +139,7 @@ export async function cmdActivity(squarePath, name, activity, resolveBody, opts)
138
139
  const delayMs = error.facts?.retryAfterMs ?? SLEEP_MS;
139
140
  if (noWait) {
140
141
  const draftPath = saveActivityDraft(squarePath, name, rawInput);
141
- 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 }));
142
143
  process.exit(1);
143
144
  }
144
145
  if (announcedWait !== 'throttled') {
@@ -152,7 +153,7 @@ export async function cmdActivity(squarePath, name, activity, resolveBody, opts)
152
153
  const holdReason = fresh.holdReason;
153
154
  if (noWait) {
154
155
  const draftPath = saveActivityDraft(squarePath, name, rawInput);
155
- 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 }));
156
157
  process.exit(1);
157
158
  }
158
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
- await closeOpenSquare(reader);
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
- export async function presentPendingAtBoundary(sessionId, present, lookup = sessionInbox, env = process.env, signal) {
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;
@@ -107,6 +111,8 @@ export async function presentPendingAtBoundary(sessionId, present, lookup = sess
107
111
  const prior = presentationLocks.get(key);
108
112
  if (prior !== undefined) {
109
113
  await prior;
114
+ if (!rendered)
115
+ return undefined;
110
116
  continue;
111
117
  }
112
118
  const work = presentPending({ artifact: square.artifact, location: entry.membership.squarePath, participant: entry.membership.name, activity: index, hostLedger: square.hostLedger, session: sessionId, sink: { present: renderOnce }, markSeen: entry.markSeen, now: Date.now() }).then(async (outcome) => {
@@ -125,6 +131,8 @@ export async function presentPendingAtBoundary(sessionId, present, lookup = sess
125
131
  finally {
126
132
  presentationLocks.delete(key);
127
133
  }
134
+ if (!rendered)
135
+ return undefined;
128
136
  }
129
137
  }
130
138
  finally {
@@ -134,3 +142,10 @@ export async function presentPendingAtBoundary(sessionId, present, lookup = sess
134
142
  }
135
143
  return result;
136
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;
@@ -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, rosterNames } from './runtime.js';
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)}". Expected one of: ${rosterNames(state).map(participantIdentity).join(', ')}.`);
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.index <= cursor || activity.kind !== 'say' || !delivery.directedTo(activity, viewer))
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
- delivered.push(activity);
39
- perceptions.set(activity.index, delivery.perceive(activity, viewer));
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
  }
@@ -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[];
@@ -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
- export declare const participantsCommand: CommandSpec<undefined, string>;
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>;