@astrosheep/square 0.3.2

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 (47) hide show
  1. package/codex-plugin/.codex-plugin/plugin.json +25 -0
  2. package/codex-plugin/hooks/hooks.json +28 -0
  3. package/dist/activity-feed.js +36 -0
  4. package/dist/activity.js +151 -0
  5. package/dist/artifact.js +739 -0
  6. package/dist/claude-hook.js +112 -0
  7. package/dist/cmd/notify-once.js +37 -0
  8. package/dist/compact.js +39 -0
  9. package/dist/decisions.js +286 -0
  10. package/dist/delivery-health.js +249 -0
  11. package/dist/delivery.js +93 -0
  12. package/dist/doctor.js +34 -0
  13. package/dist/harness.js +584 -0
  14. package/dist/help.js +131 -0
  15. package/dist/inbox.js +33 -0
  16. package/dist/index.js +163 -0
  17. package/dist/list.js +126 -0
  18. package/dist/model.js +44 -0
  19. package/dist/notifications.js +97 -0
  20. package/dist/paseo-timeline.js +206 -0
  21. package/dist/presentation.js +468 -0
  22. package/dist/presented.js +211 -0
  23. package/dist/registry.js +299 -0
  24. package/dist/runtime.js +304 -0
  25. package/dist/search.js +54 -0
  26. package/dist/square-core.js +183 -0
  27. package/dist/square.js +1366 -0
  28. package/dist/stream.js +149 -0
  29. package/dist/terminal.js +125 -0
  30. package/dist/time.js +81 -0
  31. package/dist/wake-sink.js +219 -0
  32. package/dist/watch.js +386 -0
  33. package/extensions/square-opencode.js +87 -0
  34. package/extensions/square-pi.js +167 -0
  35. package/guides/architect.md +165 -0
  36. package/guides/brainstorm.md +404 -0
  37. package/guides/participant.md +171 -0
  38. package/package.json +57 -0
  39. package/skills/brainstorm/SKILL.md +136 -0
  40. package/skills/square/.claude-plugin/plugin.json +8 -0
  41. package/skills/square/SKILL.md +154 -0
  42. package/skills/square/hooks/hooks.json +27 -0
  43. package/skills/square-feedback/SKILL.md +55 -0
  44. package/skills/square-feedback/agents/openai.yaml +4 -0
  45. package/template.md +4 -0
  46. package/templates/architect.md +4 -0
  47. package/templates/brainstorm.md +4 -0
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "square",
3
+ "version": "0.3.2",
4
+ "description": "Shared Square activity with reliable participant attention at Codex boundaries.",
5
+ "author": {
6
+ "name": "Square"
7
+ },
8
+ "license": "MIT",
9
+ "keywords": [
10
+ "square",
11
+ "collaboration",
12
+ "multi-agent"
13
+ ],
14
+ "skills": "./skills/",
15
+ "interface": {
16
+ "displayName": "Square",
17
+ "shortDescription": "Join and coordinate through a shared square",
18
+ "longDescription": "Participate in a shared activity stream and receive addressed Square activity at Codex boundaries.",
19
+ "developerName": "Square",
20
+ "category": "Developer Tools",
21
+ "capabilities": [
22
+ "Interactive"
23
+ ]
24
+ }
25
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "hooks": {
3
+ "UserPromptSubmit": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "square codex-hook",
9
+ "timeout": 5,
10
+ "statusMessage": "square-codex-hook"
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "Stop": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "square codex-hook",
21
+ "timeout": 5,
22
+ "statusMessage": "square-codex-hook"
23
+ }
24
+ ]
25
+ }
26
+ ]
27
+ }
28
+ }
@@ -0,0 +1,36 @@
1
+ import { sameName } from './model.js';
2
+ import { actStableIndex, advanceCursor, latestIndexedActIndex } from './runtime.js';
3
+ import { matchesCatchFilter } from './delivery.js';
4
+ export function indexedDelta(acts, cursor) {
5
+ return acts
6
+ .map((act) => ({ act, index: actStableIndex(act) }))
7
+ .filter((item) => item.index > cursor);
8
+ }
9
+ export function peerRoomChanges(delta, name) {
10
+ return delta.filter((item) => item.act.actor !== undefined && !sameName(item.act.actor, name) && item.act.kind !== 'say' && item.act.kind !== 'read');
11
+ }
12
+ export function peerPublicActs(delta, name) {
13
+ return delta.filter((item) => item.act.actor !== undefined && !sameName(item.act.actor, name) && (item.act.kind === 'say' || item.act.kind === 'done'));
14
+ }
15
+ function matchesParticipants(item, participants) {
16
+ return participants === undefined || (item.act.actor !== undefined && participants.some((participant) => sameName(participant, item.act.actor)));
17
+ }
18
+ export function matchesFeedFilter(item, filter) {
19
+ if (item.act.kind === 'say') {
20
+ return matchesCatchFilter({ actor: item.act.actor, body: item.act.body, bell: item.act.reach === 'bell' }, filter);
21
+ }
22
+ return filter.mention === undefined && matchesParticipants(item, filter.participants);
23
+ }
24
+ export function filteredPeerActivities(delta, name, filter) {
25
+ return peerPublicActs(delta, name)
26
+ .filter((item) => item.act.kind === 'say')
27
+ .filter((item) => matchesFeedFilter(item, filter));
28
+ }
29
+ export function filteredRoomChanges(delta, name, filter) {
30
+ if (filter.mention !== undefined)
31
+ return [];
32
+ return peerRoomChanges(delta, name).filter((item) => matchesParticipants(item, filter.participants));
33
+ }
34
+ export function ackPeerDelta(doc, name, delta) {
35
+ return advanceCursor(doc, name, latestIndexedActIndex([...peerPublicActs(delta, name), ...peerRoomChanges(delta, name)]), 'watch');
36
+ }
@@ -0,0 +1,151 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { setTimeout as sleep } from 'node:timers/promises';
5
+ import { loadSquare } from './artifact.js';
6
+ import { SquareError, validateName } from './model.js';
7
+ import { dispatchActNotifications } from './notifications.js';
8
+ import { actHintLine, renderActivityBlocked, renderActivityLimit, renderActNoWait, renderActWaiting, renderPendingFeed, withActivityNextOutput, } from './presentation.js';
9
+ import { appendAct, currentHold, inSquareCount, nowMs, SLEEP_MS, withSquareLock, resolveRosterName } from './runtime.js';
10
+ import { decideAct, resolveKnownName } from './decisions.js';
11
+ import { formatTimestamp } from './time.js';
12
+ function draftDirFor(squarePath) {
13
+ return path.join(path.dirname(squarePath), 'drafts');
14
+ }
15
+ function draftTimestamp(at) {
16
+ return new Date(at).toISOString().replace(/\.\d{3}Z$/, 'Z').replace(/[:.]/g, '-');
17
+ }
18
+ function draftNamePart(name) {
19
+ return name.replace(/[^\p{L}\p{N}_-]+/gu, '-').replace(/^-+|-+$/g, '') || 'participant';
20
+ }
21
+ export function saveActivityDraft(squarePath, name, body) {
22
+ const draftDir = draftDirFor(squarePath);
23
+ fs.mkdirSync(draftDir, { recursive: true });
24
+ const hash = crypto.createHash('sha256').update(body).digest('hex').slice(0, 8);
25
+ const base = `${draftTimestamp(nowMs())}-${draftNamePart(name)}-${hash}`;
26
+ for (let i = 0;; i++) {
27
+ const filename = `${base}${i === 0 ? '' : `-${i}`}.md`;
28
+ const target = path.join(draftDir, filename);
29
+ try {
30
+ fs.writeFileSync(target, body, { flag: 'wx' });
31
+ return target;
32
+ }
33
+ catch (error) {
34
+ if (error.code !== 'EEXIST')
35
+ throw error;
36
+ }
37
+ }
38
+ }
39
+ export async function cmdActivity(squarePath, name, activity, resolveBody, opts) {
40
+ validateName(name);
41
+ const rawInput = String(resolveBody(activity)).replace(/\r\n/g, '\n');
42
+ let doc;
43
+ try {
44
+ doc = loadSquare(squarePath);
45
+ }
46
+ catch (err) {
47
+ if (err instanceof SquareError) {
48
+ process.stderr.write(err.message + '\n');
49
+ process.exit(err.code === 'not_found' ? 1 : 2);
50
+ }
51
+ throw err;
52
+ }
53
+ const knownName = resolveRosterName(doc, name);
54
+ if (knownName === undefined) {
55
+ const draftPath = saveActivityDraft(squarePath, name, rawInput);
56
+ const expected = doc.acts.filter((act) => act.kind === 'join').map((act) => act.actor);
57
+ process.stderr.write(`Unknown participant "${name}". Expected one of: ${expected.join(', ')}.\n`);
58
+ process.stderr.write(`draft kept: ${draftPath}\n`);
59
+ process.exit(2);
60
+ }
61
+ const body = rawInput.trim();
62
+ const force = opts.force ?? false;
63
+ const noWait = opts.noWait ?? false;
64
+ const reach = opts.reach === undefined
65
+ ? undefined
66
+ : opts.reach === 'bell'
67
+ ? 'bell'
68
+ : { beside: resolveKnownName(doc, opts.reach.beside) };
69
+ let announcedWait;
70
+ while (true) {
71
+ const { decision, sent, headerCount, held } = await withSquareLock(squarePath, () => {
72
+ const freshDoc = loadSquare(squarePath);
73
+ const d = decideAct(freshDoc, { name, body, force, now: nowMs(), reach });
74
+ if (d.type === 'sent') {
75
+ const appended = appendAct(squarePath, freshDoc, d.act);
76
+ return { decision: d, sent: { act: appended, index: appended.index }, headerCount: inSquareCount(freshDoc), held: currentHold(freshDoc.acts).active };
77
+ }
78
+ return { decision: d, sent: undefined, headerCount: inSquareCount(freshDoc), held: currentHold(freshDoc.acts).active };
79
+ });
80
+ switch (decision.type) {
81
+ case 'sent': {
82
+ if (sent)
83
+ await dispatchActNotifications(squarePath, sent);
84
+ const hasPending = decision.pendingPublic.length > 0 || decision.pendingRoomChanges.length > 0;
85
+ const pending = hasPending ? `\n\n${renderPendingFeed(decision.pendingPublic, decision.pendingRoomChanges)}` : '';
86
+ const hint = actHintLine(decision.ownActCount);
87
+ const withHint = hint ? `${decision.confirmation}\n${hint}` : decision.confirmation;
88
+ process.stdout.write(withActivityNextOutput(squarePath, withHint + pending, { participantCount: headerCount, held }));
89
+ return;
90
+ }
91
+ case 'blocked': {
92
+ const draftPath = saveActivityDraft(squarePath, name, rawInput);
93
+ process.stdout.write(renderActivityBlocked({
94
+ squarePath,
95
+ name: knownName,
96
+ forceCommand: opts.forceCommand,
97
+ activitySummaries: decision.activitySummaries,
98
+ unreadRoomChanges: decision.unreadRoomChanges,
99
+ draftPath,
100
+ participantCount: headerCount,
101
+ held,
102
+ }));
103
+ process.exit(1);
104
+ break;
105
+ }
106
+ case 'capped': {
107
+ process.stdout.write(renderActivityLimit({
108
+ squarePath,
109
+ name: knownName,
110
+ count: decision.count,
111
+ hardCap: decision.hardCap,
112
+ draftPath: saveActivityDraft(squarePath, name, rawInput),
113
+ participantCount: headerCount,
114
+ held,
115
+ }));
116
+ process.exit(1);
117
+ break;
118
+ }
119
+ case 'throttled': {
120
+ if (noWait) {
121
+ const draftPath = saveActivityDraft(squarePath, name, rawInput);
122
+ process.stdout.write(renderActNoWait({ squarePath, name: knownName, reason: 'throttled', delayMs: decision.delayMs, draftPath, participantCount: headerCount, held }));
123
+ process.exit(1);
124
+ }
125
+ if (announcedWait !== 'throttled') {
126
+ process.stdout.write(renderActWaiting({ reason: 'throttled', delayMs: decision.delayMs }) + '\n');
127
+ announcedWait = 'throttled';
128
+ }
129
+ await sleep(decision.delayMs);
130
+ break;
131
+ }
132
+ case 'held': {
133
+ if (noWait) {
134
+ const draftPath = saveActivityDraft(squarePath, name, rawInput);
135
+ process.stdout.write(renderActNoWait({ squarePath, name: knownName, reason: 'held', holdReason: decision.reason, draftPath, participantCount: headerCount, held }));
136
+ process.exit(1);
137
+ }
138
+ if (announcedWait !== 'held') {
139
+ process.stdout.write(renderActWaiting({ reason: 'held' }) + '\n');
140
+ announcedWait = 'held';
141
+ }
142
+ await sleep(SLEEP_MS);
143
+ break;
144
+ }
145
+ case 'bell_quota': {
146
+ process.stdout.write(withActivityNextOutput(squarePath, [`✕ the bell stays quiet for now`, ` · you can ring it again at ${formatTimestamp(decision.nextAt)}`].join('\n'), { participantCount: headerCount, held }));
147
+ process.exit(1);
148
+ }
149
+ }
150
+ }
151
+ }