@astrosheep/square 0.3.5 → 0.3.6

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 (51) hide show
  1. package/codex-plugin/.codex-plugin/plugin.json +3 -2
  2. package/dist/activity-feed.js +26 -18
  3. package/dist/activity.js +9 -10
  4. package/dist/artifact.js +126 -202
  5. package/dist/claude-hook.js +45 -21
  6. package/dist/cli/context.js +7 -7
  7. package/dist/cli/maintenance-commands.js +10 -26
  8. package/dist/cli/meta-commands.js +3 -6
  9. package/dist/cli/observation-commands.js +44 -52
  10. package/dist/cli/program.js +4 -4
  11. package/dist/cli/registry.js +5 -5
  12. package/dist/cli/square-commands.js +16 -18
  13. package/dist/cmd/notify-once.js +23 -21
  14. package/dist/compact.js +1 -1
  15. package/dist/decisions.js +53 -86
  16. package/dist/delivery-health.js +104 -210
  17. package/dist/delivery.js +68 -18
  18. package/dist/doctor.js +9 -8
  19. package/dist/harness-claude.js +38 -245
  20. package/dist/harness-codex.js +82 -616
  21. package/dist/harness-stage.js +36 -0
  22. package/dist/harness.js +3 -5
  23. package/dist/help.js +43 -35
  24. package/dist/inbox.js +12 -11
  25. package/dist/index.js +9 -121
  26. package/dist/list.js +1 -1
  27. package/dist/model.js +0 -6
  28. package/dist/notification-failures.js +54 -0
  29. package/dist/notifications.js +47 -62
  30. package/dist/paseo-timeline.js +58 -188
  31. package/dist/presentation.js +55 -63
  32. package/dist/presented.js +9 -8
  33. package/dist/registry.js +55 -45
  34. package/dist/runtime.js +27 -84
  35. package/dist/square-application.js +135 -130
  36. package/dist/square-core.js +3 -11
  37. package/dist/stream.js +27 -126
  38. package/dist/wake-sink.js +134 -188
  39. package/dist/watch.js +65 -122
  40. package/extensions/square-opencode.js +1 -1
  41. package/extensions/square-pi.js +8 -130
  42. package/guides/architect.md +3 -3
  43. package/guides/participant.md +25 -16
  44. package/package.json +2 -2
  45. package/skills/brainstorm/SKILL.md +25 -32
  46. package/skills/square/.claude-plugin/plugin.json +1 -1
  47. package/skills/square/SKILL.md +39 -107
  48. package/skills/square-feedback/SKILL.md +4 -4
  49. package/dist/harness-lifecycle.js +0 -102
  50. package/dist/square-store.js +0 -111
  51. package/dist/terminal.js +0 -125
@@ -3,273 +3,66 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { SQUARE_IDENTITY } from './identity.js';
6
- import { reconcileInstall, reconcileUninstall, staleManagedRegistrations, } from './harness-lifecycle.js';
6
+ import { stageReplacement } from './harness-stage.js';
7
7
  export const CLAUDE_PLUGIN_ID = SQUARE_IDENTITY.pluginId;
8
8
  export const CLAUDE_MARKETPLACE_NAME = SQUARE_IDENTITY.marketplaceName;
9
9
  export function claudeMarketplaceRoot(homeDir) {
10
10
  return path.join(homeDir, '.square', 'claude', 'marketplaces', CLAUDE_MARKETPLACE_NAME);
11
11
  }
12
- function packageAssets(relative) {
13
- return fileURLToPath(new URL(relative, import.meta.url));
14
- }
15
- function runClaudeCommand(homeDir, args) {
12
+ function runClaude(homeDir, args) {
16
13
  const result = spawnSync(process.env.SQUARE_CLAUDE_BIN || 'claude', args, {
17
- encoding: 'utf8',
18
- env: { ...process.env, HOME: homeDir, CLAUDE_CONFIG_DIR: path.join(homeDir, '.claude') },
19
- maxBuffer: 4 * 1024 * 1024,
20
- timeout: 30_000,
14
+ encoding: 'utf8', env: { ...process.env, HOME: homeDir, CLAUDE_CONFIG_DIR: path.join(homeDir, '.claude') }, timeout: 30_000,
21
15
  });
22
16
  if (result.error)
23
17
  throw result.error;
24
18
  return { status: result.status ?? 1, stdout: result.stdout || '', stderr: result.stderr || '' };
25
19
  }
26
- function requireSuccess(result, operation) {
27
- if (result.status === 0)
20
+ function requireSuccess(result, operation, allowMissing = false) {
21
+ if (result.status === 0 || (allowMissing && /not configured|not installed|not found/i.test(result.stderr)))
28
22
  return;
29
23
  throw new Error(`Claude ${operation} failed: ${result.stderr.trim() || result.stdout.trim() || `exit ${result.status}`}`);
30
24
  }
31
- function writeJsonAtomic(filePath, value) {
32
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
33
- const temporary = `${filePath}.${process.pid}.${Date.now()}.tmp`;
34
- fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
35
- fs.renameSync(temporary, filePath);
25
+ function writeJson(file, value) {
26
+ fs.mkdirSync(path.dirname(file), { recursive: true });
27
+ fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
36
28
  }
37
- function stageClaudeBundle(homeDir, marketplaceRoot) {
38
- const token = `${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
39
- const stage = `${marketplaceRoot}.${token}.stage`;
40
- const backup = `${marketplaceRoot}.${token}.previous`;
41
- const pluginRoot = path.join(stage, 'plugins', SQUARE_IDENTITY.pluginName);
42
- fs.mkdirSync(path.dirname(marketplaceRoot), { recursive: true });
43
- try {
44
- fs.cpSync(packageAssets('../skills/square/'), pluginRoot, { recursive: true });
45
- writeJsonAtomic(path.join(stage, '.claude-plugin', 'marketplace.json'), {
29
+ export async function installClaudePlugin(homeDir, run = runClaude) {
30
+ const marketplaceRoot = claudeMarketplaceRoot(homeDir);
31
+ const staged = stageReplacement(marketplaceRoot, (stage) => {
32
+ const plugin = path.join(stage, 'plugins', SQUARE_IDENTITY.pluginName);
33
+ fs.cpSync(fileURLToPath(new URL('../skills/square/', import.meta.url)), plugin, { recursive: true });
34
+ writeJson(path.join(stage, '.claude-plugin', 'marketplace.json'), {
46
35
  name: CLAUDE_MARKETPLACE_NAME,
47
- description: `${SQUARE_IDENTITY.productName} harness integrations`,
48
- owner: { name: SQUARE_IDENTITY.productName },
49
- plugins: [{ name: SQUARE_IDENTITY.pluginName, description: `Native Claude Code delivery for ${SQUARE_IDENTITY.productName}`, source: './plugins/square' }],
36
+ plugins: [{ name: SQUARE_IDENTITY.pluginName, source: './plugins/square' }],
50
37
  });
51
- if (fs.existsSync(marketplaceRoot))
52
- fs.renameSync(marketplaceRoot, backup);
53
- fs.renameSync(stage, marketplaceRoot);
54
- }
55
- catch (error) {
56
- try {
57
- fs.rmSync(stage, { recursive: true, force: true });
58
- }
59
- catch { }
60
- if (fs.existsSync(backup) && !fs.existsSync(marketplaceRoot))
61
- fs.renameSync(backup, marketplaceRoot);
62
- throw error;
63
- }
64
- return {
65
- desired: { marketplaceName: CLAUDE_MARKETPLACE_NAME, marketplaceRoot, pluginId: CLAUDE_PLUGIN_ID },
66
- rollback() {
67
- fs.rmSync(marketplaceRoot, { recursive: true, force: true });
68
- if (fs.existsSync(backup))
69
- fs.renameSync(backup, marketplaceRoot);
70
- },
71
- finalize() { fs.rmSync(backup, { recursive: true, force: true }); },
72
- };
73
- }
74
- function isRecord(value) {
75
- return value !== null && typeof value === 'object';
76
- }
77
- function isManagedSource(managedRoot, source) {
78
- const relative = path.relative(path.resolve(managedRoot), path.resolve(source));
79
- return relative === '' || (relative !== '..' &&
80
- !relative.startsWith(`..${path.sep}`) &&
81
- !path.isAbsolute(relative));
82
- }
83
- function isCurrentManagedMarketplace(marketplace, managedRoot) {
84
- return marketplace.name === CLAUDE_MARKETPLACE_NAME &&
85
- marketplace.local &&
86
- isManagedSource(managedRoot, marketplace.source);
87
- }
88
- function parseClaudeMarketplaceRegistration(value) {
89
- if (!isRecord(value)) {
90
- throw new Error('Claude marketplace inventory entry is invalid.');
91
- }
92
- const name = value.name;
93
- const source = value.source;
94
- const installLocation = value.installLocation;
95
- if (typeof name !== 'string' ||
96
- typeof source !== 'string' ||
97
- typeof installLocation !== 'string') {
98
- throw new Error('Claude marketplace inventory entry is malformed.');
99
- }
100
- if (source === 'directory') {
101
- const sourcePath = value.path;
102
- if (typeof sourcePath !== 'string') {
103
- throw new Error('Claude directory marketplace entry is malformed.');
104
- }
105
- return { name, source: sourcePath, local: true };
106
- }
107
- const stableSource = typeof value.repo === 'string' ? value.repo : name;
108
- return { name, source: `${source}:${stableSource}`, local: false };
109
- }
110
- function parseInventory(stdout) {
38
+ });
111
39
  try {
112
- const payload = JSON.parse(stdout);
113
- if (!Array.isArray(payload)) {
114
- throw new Error('Claude marketplace inventory must be a JSON array.');
115
- }
116
- const marketplaces = payload.map(parseClaudeMarketplaceRegistration);
117
- return { marketplaces };
40
+ const add = run(homeDir, ['plugin', 'marketplace', 'add', marketplaceRoot]);
41
+ requireSuccess(add, 'marketplace install', true);
42
+ requireSuccess(run(homeDir, ['plugin', 'install', CLAUDE_PLUGIN_ID]), 'plugin install');
43
+ requireSuccess(run(homeDir, ['plugin', 'update', CLAUDE_PLUGIN_ID]), 'plugin update');
44
+ staged.finalize();
118
45
  }
119
46
  catch (error) {
120
- throw error instanceof Error ? error : new Error(String(error));
121
- }
122
- }
123
- function parseClaudePluginInventory(stdout) {
124
- try {
125
- const parsed = JSON.parse(stdout);
126
- return Array.isArray(parsed) ? parsed : [];
127
- }
128
- catch {
129
- throw new Error('Claude plugin inventory returned invalid JSON.');
47
+ staged.rollback();
48
+ throw error;
130
49
  }
131
- }
132
- function isVerifiedClaudePlugin(entry) {
133
- if (!isRecord(entry))
134
- return false;
135
- return entry.id === CLAUDE_PLUGIN_ID &&
136
- entry.enabled === true &&
137
- entry.version === SQUARE_IDENTITY.packageVersion;
138
- }
139
- function claudeProtocol(run) {
140
- return {
141
- host: 'claude',
142
- marketplaceName: CLAUDE_MARKETPLACE_NAME,
143
- pluginId: CLAUDE_PLUGIN_ID,
144
- managedRoot: (homeDir) => path.join(homeDir, '.square', 'claude'),
145
- stageBundle: stageClaudeBundle,
146
- inspectInventory(homeDir) {
147
- const result = run(homeDir, ['plugin', 'marketplace', 'list', '--json']);
148
- requireSuccess(result, 'marketplace inventory');
149
- return parseInventory(result.stdout);
150
- },
151
- registerMarketplace(homeDir, desired) {
152
- const result = run(homeDir, ['plugin', 'marketplace', 'add', desired.marketplaceRoot]);
153
- requireSuccess(result, 'marketplace install');
154
- },
155
- installOrUpdate(homeDir, desired) {
156
- requireSuccess(run(homeDir, ['plugin', 'install', desired.pluginId]), 'plugin install');
157
- requireSuccess(run(homeDir, ['plugin', 'update', desired.pluginId]), 'plugin update');
158
- },
159
- verifyPluginAndHooks(homeDir, desired) {
160
- const hooksPath = path.join(desired.marketplaceRoot, 'plugins', SQUARE_IDENTITY.pluginName, 'hooks', 'hooks.json');
161
- if (!fs.existsSync(hooksPath)) {
162
- throw new Error(`Claude plugin hooks missing from ${desired.marketplaceRoot}`);
163
- }
164
- const listed = run(homeDir, ['plugin', 'list', '--json']);
165
- requireSuccess(listed, 'plugin inventory');
166
- const plugins = parseClaudePluginInventory(listed.stdout);
167
- if (!plugins.some(isVerifiedClaudePlugin)) {
168
- throw new Error(`Claude did not verify ${CLAUDE_PLUGIN_ID} as installed and enabled.`);
169
- }
170
- },
171
- removePlugin(homeDir, pluginId) {
172
- const result = run(homeDir, ['plugin', 'remove', pluginId]);
173
- const isMissing = result.stderr.includes('is not configured or installed');
174
- if (result.status !== 0 && !isMissing) {
175
- requireSuccess(result, 'plugin removal');
176
- }
177
- },
178
- removeMarketplace(homeDir, marketplaceName) {
179
- const result = run(homeDir, ['plugin', 'marketplace', 'remove', marketplaceName]);
180
- const isMissing = result.stderr.includes('is not configured or installed');
181
- if (result.status !== 0 && !isMissing) {
182
- requireSuccess(result, 'marketplace removal');
183
- }
184
- },
185
- removeManagedSource(source) {
186
- fs.rmSync(source, { recursive: true, force: true });
187
- },
188
- retireDirectDelivery(homeDir) {
189
- const legacySkill = path.join(homeDir, '.claude', 'skills', 'square');
190
- try {
191
- if (fs.lstatSync(legacySkill).isSymbolicLink()) {
192
- fs.rmSync(legacySkill, { force: true });
193
- }
194
- }
195
- catch { }
196
- },
197
- removeManagedRoot(homeDir) {
198
- fs.rmSync(path.join(homeDir, '.square', 'claude'), { recursive: true, force: true });
199
- },
200
- };
201
- }
202
- export async function installClaudePlugin(homeDir, run = runClaudeCommand) {
203
- const protocol = claudeProtocol(run);
204
- const inventory = await reconcileInstall(homeDir, protocol);
205
- const managedRoot = protocol.managedRoot(homeDir);
206
- const marketplaceRoot = inventory.marketplaces.find((entry) => isCurrentManagedMarketplace(entry, managedRoot))?.source ?? claudeMarketplaceRoot(homeDir);
207
- const pluginRoot = path.join(marketplaceRoot, 'plugins', SQUARE_IDENTITY.pluginName);
208
- return { marketplaceRoot, pluginRoot };
209
- }
210
- export async function uninstallClaudePlugin(homeDir, run = runClaudeCommand) {
211
- const base = claudeProtocol(run);
212
- const protocol = {
213
- ...base,
214
- async inspectInventory(currentHome) {
215
- const inventory = await base.inspectInventory(currentHome);
216
- const managedRoot = base.managedRoot(currentHome);
217
- const hasManagedMarketplace = inventory.marketplaces.some((entry) => entry.local && isManagedSource(managedRoot, entry.source));
218
- if (hasManagedMarketplace)
219
- return inventory;
220
- const fallback = {
221
- name: CLAUDE_MARKETPLACE_NAME,
222
- source: claudeMarketplaceRoot(currentHome),
223
- local: true,
224
- pluginIds: [CLAUDE_PLUGIN_ID],
225
- };
226
- return { marketplaces: [...inventory.marketplaces, fallback] };
227
- },
228
- };
229
- await reconcileUninstall(homeDir, protocol);
230
- return { paths: [claudeMarketplaceRoot(homeDir)], notes: [] };
231
- }
232
- export async function doctorClaudePlugin(homeDir, run = runClaudeCommand) {
233
- const protocol = claudeProtocol(run);
234
- const inventory = await protocol.inspectInventory(homeDir);
235
- const managedRoot = protocol.managedRoot(homeDir);
236
- const current = inventory.marketplaces.find((entry) => isCurrentManagedMarketplace(entry, managedRoot));
237
- const root = current?.source ?? claudeMarketplaceRoot(homeDir);
238
- const desired = {
239
- marketplaceName: CLAUDE_MARKETPLACE_NAME,
240
- marketplaceRoot: root,
241
- pluginId: CLAUDE_PLUGIN_ID,
242
- };
243
- const stale = staleManagedRegistrations(inventory, managedRoot, desired);
244
- let pluginStatus;
50
+ return { marketplaceRoot, pluginRoot: path.join(marketplaceRoot, 'plugins', SQUARE_IDENTITY.pluginName) };
51
+ }
52
+ export async function uninstallClaudePlugin(homeDir, run = runClaude) {
53
+ const root = claudeMarketplaceRoot(homeDir);
54
+ requireSuccess(run(homeDir, ['plugin', 'remove', CLAUDE_PLUGIN_ID]), 'plugin removal', true);
55
+ requireSuccess(run(homeDir, ['plugin', 'marketplace', 'remove', CLAUDE_MARKETPLACE_NAME]), 'marketplace removal', true);
56
+ fs.rmSync(root, { recursive: true, force: true });
57
+ return { paths: [root], notes: [] };
58
+ }
59
+ export async function doctorClaudePlugin(homeDir, run = runClaude) {
60
+ const root = claudeMarketplaceRoot(homeDir);
61
+ const bundle = path.join(root, 'plugins', SQUARE_IDENTITY.pluginName);
245
62
  const listed = run(homeDir, ['plugin', 'list', '--json']);
246
- if (listed.status !== 0) {
247
- pluginStatus = `○ ${CLAUDE_PLUGIN_ID} plugin inventory unavailable`;
248
- }
249
- else {
250
- try {
251
- const plugins = parseClaudePluginInventory(listed.stdout);
252
- const valid = plugins.some(isVerifiedClaudePlugin);
253
- pluginStatus = valid
254
- ? `✓ ${CLAUDE_PLUGIN_ID} installed, enabled, and version ${SQUARE_IDENTITY.packageVersion}`
255
- : `○ ${CLAUDE_PLUGIN_ID} is not installed, enabled, and version ${SQUARE_IDENTITY.packageVersion}`;
256
- }
257
- catch {
258
- pluginStatus = `○ ${CLAUDE_PLUGIN_ID} plugin inventory unavailable`;
259
- }
260
- }
261
- const bundlePath = path.join(root, 'plugins', SQUARE_IDENTITY.pluginName);
262
- const bundleStatus = fs.existsSync(bundlePath)
263
- ? `✓ Square Claude plugin bundle ${root}`
264
- : `○ Square Claude plugin bundle missing ${root}`;
265
- const marketplaceStatus = current !== undefined
266
- ? `✓ ${CLAUDE_PLUGIN_ID} marketplace registered`
267
- : `○ ${CLAUDE_PLUGIN_ID} marketplace is not registered`;
63
+ const installed = listed.status === 0 && listed.stdout.includes(CLAUDE_PLUGIN_ID);
268
64
  return [
269
- bundleStatus,
270
- marketplaceStatus,
271
- pluginStatus,
272
- ...(stale.length === 0 ? [] : [`✕ ${stale.length} stale Square marketplace registration(s)`]),
65
+ fs.existsSync(bundle) ? `✓ Square Claude plugin bundle ${root}` : `○ Square Claude plugin bundle missing ${root}`,
66
+ installed ? `✓ ${CLAUDE_PLUGIN_ID} installed` : `○ ${CLAUDE_PLUGIN_ID} unavailable`,
273
67
  ];
274
68
  }
275
- export const claudeHarness = Object.freeze({ install: installClaudePlugin, uninstall: uninstallClaudePlugin, doctor: doctorClaudePlugin });