@astrosheep/square 0.3.7 → 0.3.9
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/cli/context.js +1 -1
- package/dist/cli/harness-command.js +56 -13
- package/dist/cli/registry.js +3 -1
- package/dist/harness-claude.js +1 -0
- package/dist/harness-codex.js +36 -8
- package/dist/harness-links.js +8 -4
- package/dist/help.js +14 -4
- package/package.json +1 -1
- package/skills/square/.claude-plugin/plugin.json +1 -1
package/dist/cli/context.js
CHANGED
|
@@ -133,7 +133,7 @@ export function parseGlobalArgs(rawArgs) {
|
|
|
133
133
|
validateName(name);
|
|
134
134
|
const explicitSquarePath = requestedPath !== undefined;
|
|
135
135
|
const command = args[0];
|
|
136
|
-
const resolved = !explicitSquarePath && !['ls', 'list', 'version'].includes(command ?? '')
|
|
136
|
+
const resolved = !explicitSquarePath && !['ls', 'list', 'version', 'install', 'uninstall'].includes(command ?? '')
|
|
137
137
|
? resolveDefaultSquarePath()
|
|
138
138
|
: { path: requestedPath ?? DEFAULT_SQUARE_PATH, multiple: false };
|
|
139
139
|
return { squarePath: resolved.path, explicitSquarePath, multipleSquares: resolved.multiple, name, args };
|
|
@@ -1,34 +1,77 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import { executeHarnessTarget, harnessTargets, } from '../harness.js';
|
|
3
|
+
function installTargets(intent, action) {
|
|
4
|
+
const available = harnessTargets().filter((target) => target.capabilities.includes(action));
|
|
5
|
+
const names = intent.all ? available.map((target) => target.name) : [...new Set(intent.targets)];
|
|
6
|
+
if (names.length === 0)
|
|
7
|
+
throw new Error(`${action} requires one or more targets, or --all`);
|
|
8
|
+
for (const name of names) {
|
|
9
|
+
if (!available.some((target) => target.name === name)) {
|
|
10
|
+
throw new Error(`Unknown ${action} target: ${name}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return names;
|
|
14
|
+
}
|
|
15
|
+
function parseInstall(argv, action) {
|
|
16
|
+
const all = argv.includes('--all');
|
|
17
|
+
const force = argv.includes('-f') || argv.includes('--force');
|
|
18
|
+
const allowedOptions = action === 'install' ? ['--all', '-f', '--force'] : ['--all'];
|
|
19
|
+
const option = argv.find((argument) => argument.startsWith('-') && !allowedOptions.includes(argument));
|
|
20
|
+
if (option !== undefined)
|
|
21
|
+
throw new Error(`Unknown ${action} option: ${option}`);
|
|
22
|
+
if (action === 'uninstall' && force)
|
|
23
|
+
throw new Error(`Unknown uninstall option: ${argv.find((item) => item === '-f' || item === '--force')}`);
|
|
24
|
+
const targets = argv.filter((argument) => !argument.startsWith('-'));
|
|
25
|
+
if (all && targets.length > 0)
|
|
26
|
+
throw new Error(`Cannot combine ${action} --all with named targets`);
|
|
27
|
+
return { targets, all, force };
|
|
28
|
+
}
|
|
29
|
+
function targetCommand(action) {
|
|
30
|
+
return {
|
|
31
|
+
parse(argv) { return parseInstall(argv, action); },
|
|
32
|
+
async execute(intent, context) {
|
|
33
|
+
const results = [];
|
|
34
|
+
for (const target of installTargets(intent, action)) {
|
|
35
|
+
results.push(await executeHarnessTarget(target, action, {
|
|
36
|
+
homeDir: context.homeDir,
|
|
37
|
+
squarePath: context.squarePath,
|
|
38
|
+
force: intent.force,
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
notes: results.flatMap((result) => result.notes),
|
|
43
|
+
lines: results.flatMap((result) => result.lines),
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
present(result) { process.stdout.write(formatHarnessResult(result)); },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export const installCommand = targetCommand('install');
|
|
50
|
+
export const uninstallCommand = targetCommand('uninstall');
|
|
3
51
|
export const harnessCommand = {
|
|
4
52
|
parse(argv) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
throw new Error('harness requires install, uninstall, or doctor');
|
|
8
|
-
}
|
|
53
|
+
if (argv[0] !== 'doctor')
|
|
54
|
+
throw new Error('harness only supports doctor');
|
|
9
55
|
const rest = argv.slice(1);
|
|
10
|
-
const option = rest.find((argument) => argument.startsWith('-')
|
|
56
|
+
const option = rest.find((argument) => argument.startsWith('-'));
|
|
11
57
|
if (option !== undefined)
|
|
12
58
|
throw new Error(`Unknown harness option: ${option}`);
|
|
59
|
+
if (rest.length > 1)
|
|
60
|
+
throw new Error('harness doctor accepts at most one target');
|
|
13
61
|
return {
|
|
14
|
-
|
|
15
|
-
target: rest.find((argument) => !argument.startsWith('-')),
|
|
16
|
-
force: rest.includes('-f') || rest.includes('--force'),
|
|
62
|
+
target: rest[0],
|
|
17
63
|
};
|
|
18
64
|
},
|
|
19
65
|
async execute(intent, context) {
|
|
20
|
-
if (intent.target === undefined && intent.action !== 'doctor') {
|
|
21
|
-
throw new Error('harness install/uninstall requires an explicit target: skills | claude | codex | opencode | pi');
|
|
22
|
-
}
|
|
23
66
|
const targetNames = intent.target === undefined
|
|
24
67
|
? harnessTargets().map((target) => target.name)
|
|
25
68
|
: [intent.target];
|
|
26
69
|
const results = [];
|
|
27
70
|
for (const target of targetNames) {
|
|
28
|
-
results.push(await executeHarnessTarget(target,
|
|
71
|
+
results.push(await executeHarnessTarget(target, 'doctor', {
|
|
29
72
|
homeDir: context.homeDir,
|
|
30
73
|
squarePath: context.squarePath,
|
|
31
|
-
force:
|
|
74
|
+
force: false,
|
|
32
75
|
}));
|
|
33
76
|
}
|
|
34
77
|
return {
|
package/dist/cli/registry.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { buildCommand, compactCommand, doneCommand, expressCommand, holdCommand, joinCommand, resumeCommand } from './square-commands.js';
|
|
2
2
|
import { doctorCommand } from './maintenance-commands.js';
|
|
3
|
-
import { harnessCommand } from './harness-command.js';
|
|
3
|
+
import { harnessCommand, installCommand, uninstallCommand } from './harness-command.js';
|
|
4
4
|
import { helpCommand, versionCommand } from './meta-commands.js';
|
|
5
5
|
import { catchCommand, claudeHookCommand, codexHookCommand, historyCommand, inboxCommand, listCommand, participantsCommand, statusCommand, streamCommand, warmupCommand, } from './observation-commands.js';
|
|
6
6
|
/** Every public command is an executable adapter, including aliases and utility commands. */
|
|
@@ -17,6 +17,8 @@ export const commandRegistry = [
|
|
|
17
17
|
{ names: ['done'], spec: doneCommand },
|
|
18
18
|
{ names: ['hold'], spec: holdCommand },
|
|
19
19
|
{ names: ['resume'], spec: resumeCommand },
|
|
20
|
+
{ names: ['install'], spec: installCommand },
|
|
21
|
+
{ names: ['uninstall'], spec: uninstallCommand },
|
|
20
22
|
{ names: ['harness'], spec: harnessCommand },
|
|
21
23
|
{ names: ['compact'], spec: compactCommand },
|
|
22
24
|
{ names: ['doctor'], spec: doctorCommand },
|
package/dist/harness-claude.js
CHANGED
|
@@ -33,6 +33,7 @@ export async function installClaudePlugin(homeDir, run = runClaude) {
|
|
|
33
33
|
fs.cpSync(fileURLToPath(new URL('../skills/square/', import.meta.url)), plugin, { recursive: true });
|
|
34
34
|
writeJson(path.join(stage, '.claude-plugin', 'marketplace.json'), {
|
|
35
35
|
name: CLAUDE_MARKETPLACE_NAME,
|
|
36
|
+
owner: { name: 'Square' },
|
|
36
37
|
plugins: [{ name: SQUARE_IDENTITY.pluginName, source: './plugins/square' }],
|
|
37
38
|
});
|
|
38
39
|
});
|
package/dist/harness-codex.js
CHANGED
|
@@ -12,8 +12,33 @@ const LEGACY_MARKETPLACES = ['astrosheep-square'];
|
|
|
12
12
|
export function codexMarketplaceRoot(homeDir) {
|
|
13
13
|
return path.join(homeDir, '.square', 'codex', 'marketplaces', CODEX_MARKETPLACE_NAME);
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
function configuredMarketplaceRoot(homeDir, configText) {
|
|
16
|
+
const lines = configText.split('\n');
|
|
17
|
+
const header = `[marketplaces.${CODEX_MARKETPLACE_NAME}]`;
|
|
18
|
+
const start = lines.findIndex((line) => line.trim() === header);
|
|
19
|
+
if (start < 0)
|
|
20
|
+
return undefined;
|
|
21
|
+
const end = lines.findIndex((line, index) => index > start && /^\s*\[[^\]]+\]/.test(line));
|
|
22
|
+
const section = lines.slice(start + 1, end < 0 ? undefined : end);
|
|
23
|
+
const sourceType = section.find((line) => /^\s*source_type\s*=/.test(line));
|
|
24
|
+
if (sourceType && !/=\s*["']local["']\s*$/.test(sourceType))
|
|
25
|
+
return undefined;
|
|
26
|
+
const source = section.find((line) => /^\s*source\s*=/.test(line));
|
|
27
|
+
const match = source?.match(/^\s*source\s*=\s*("(?:\\.|[^"])*"|'[^']*')\s*$/);
|
|
28
|
+
if (!match)
|
|
29
|
+
return undefined;
|
|
30
|
+
const value = match[1].startsWith('"') ? JSON.parse(match[1]) : match[1].slice(1, -1);
|
|
31
|
+
return path.isAbsolute(value) ? value : path.resolve(codexHome(homeDir), value);
|
|
32
|
+
}
|
|
33
|
+
function activeMarketplaceRoot(homeDir, configText) {
|
|
34
|
+
return configuredMarketplaceRoot(homeDir, configText) ?? codexMarketplaceRoot(homeDir);
|
|
35
|
+
}
|
|
36
|
+
export function codexPluginRoot(homeDir, marketplaceRoot = codexMarketplaceRoot(homeDir)) {
|
|
37
|
+
return path.join(marketplaceRoot, 'plugins', SQUARE_IDENTITY.pluginName);
|
|
38
|
+
}
|
|
39
|
+
export function codexPluginHooksPath(homeDir, marketplaceRoot = codexMarketplaceRoot(homeDir)) {
|
|
40
|
+
return path.join(codexPluginRoot(homeDir, marketplaceRoot), 'hooks', 'hooks.json');
|
|
41
|
+
}
|
|
17
42
|
function codexHome(homeDir) { return path.join(homeDir, '.codex'); }
|
|
18
43
|
export function codexHomeHooksPath(homeDir) { return path.join(codexHome(homeDir), 'hooks.json'); }
|
|
19
44
|
export function codexConfigPath(homeDir) { return path.join(codexHome(homeDir), 'config.toml'); }
|
|
@@ -53,9 +78,9 @@ function writeAtomic(file, text) {
|
|
|
53
78
|
fs.renameSync(temp, file);
|
|
54
79
|
}
|
|
55
80
|
export async function installCodexPlugin(homeDir, run = runCodex) {
|
|
56
|
-
const root = codexMarketplaceRoot(homeDir);
|
|
57
81
|
const config = codexConfigPath(homeDir);
|
|
58
82
|
const current = fs.existsSync(config) ? fs.readFileSync(config, 'utf8') : '';
|
|
83
|
+
const root = activeMarketplaceRoot(homeDir, current);
|
|
59
84
|
const staged = stageReplacement(root, (stage) => {
|
|
60
85
|
const plugin = path.join(stage, 'plugins', SQUARE_IDENTITY.pluginName);
|
|
61
86
|
fs.cpSync(fileURLToPath(new URL('../codex-plugin/', import.meta.url)), plugin, { recursive: true });
|
|
@@ -88,7 +113,7 @@ export async function installCodexPlugin(homeDir, run = runCodex) {
|
|
|
88
113
|
notes.push(`retired ${pluginId}`);
|
|
89
114
|
}
|
|
90
115
|
staged.finalize();
|
|
91
|
-
return { configPath: config, marketplaceRoot: root, pluginRoot: codexPluginRoot(homeDir), ...(installedPath ? { installedPath } : {}), notes };
|
|
116
|
+
return { configPath: config, marketplaceRoot: root, pluginRoot: codexPluginRoot(homeDir, root), ...(installedPath ? { installedPath } : {}), notes };
|
|
92
117
|
}
|
|
93
118
|
catch (error) {
|
|
94
119
|
staged.rollback();
|
|
@@ -96,24 +121,27 @@ export async function installCodexPlugin(homeDir, run = runCodex) {
|
|
|
96
121
|
}
|
|
97
122
|
}
|
|
98
123
|
export async function uninstallCodexPlugin(homeDir, run = runCodex) {
|
|
124
|
+
const config = codexConfigPath(homeDir);
|
|
125
|
+
const current = fs.existsSync(config) ? fs.readFileSync(config, 'utf8') : '';
|
|
126
|
+
const root = activeMarketplaceRoot(homeDir, current);
|
|
99
127
|
requireSuccess(run(homeDir, ['plugin', 'remove', CODEX_PLUGIN_ID, '--json']), 'plugin removal', true);
|
|
100
128
|
requireSuccess(run(homeDir, ['plugin', 'marketplace', 'remove', CODEX_MARKETPLACE_NAME, '--json']), 'marketplace removal', true);
|
|
101
129
|
for (const marketplace of LEGACY_MARKETPLACES) {
|
|
102
130
|
requireSuccess(run(homeDir, ['plugin', 'remove', `${SQUARE_IDENTITY.pluginName}@${marketplace}`, '--json']), 'legacy plugin removal', true);
|
|
103
131
|
requireSuccess(run(homeDir, ['plugin', 'marketplace', 'remove', marketplace, '--json']), 'legacy marketplace removal', true);
|
|
104
132
|
}
|
|
105
|
-
const root = codexMarketplaceRoot(homeDir);
|
|
106
133
|
fs.rmSync(root, { recursive: true, force: true });
|
|
107
134
|
fs.rmSync(codexHomeHooksPath(homeDir), { force: true });
|
|
108
135
|
return { paths: [root, codexConfigPath(homeDir), codexHomeHooksPath(homeDir)], notes: [] };
|
|
109
136
|
}
|
|
110
137
|
export async function doctorCodexPlugin(homeDir, run = runCodex) {
|
|
111
|
-
const root = codexMarketplaceRoot(homeDir);
|
|
112
138
|
const config = codexConfigPath(homeDir);
|
|
139
|
+
const current = fs.existsSync(config) ? fs.readFileSync(config, 'utf8') : '';
|
|
140
|
+
const root = activeMarketplaceRoot(homeDir, current);
|
|
113
141
|
const listed = run(homeDir, ['plugin', 'list', '--json']);
|
|
114
142
|
return [
|
|
115
|
-
/^hooks\s*=\s*true$/m.test(
|
|
116
|
-
fs.existsSync(codexPluginHooksPath(homeDir)) ? `✓ Square plugin hooks ${codexPluginHooksPath(homeDir)}` : `○ Square plugin bundle missing ${root}`,
|
|
143
|
+
/^hooks\s*=\s*true$/m.test(current) ? `✓ features.hooks=true in ${config}` : `○ features.hooks missing in ${config}`,
|
|
144
|
+
fs.existsSync(codexPluginHooksPath(homeDir, root)) ? `✓ Square plugin hooks ${codexPluginHooksPath(homeDir, root)}` : `○ Square plugin bundle missing ${root}`,
|
|
117
145
|
listed.status === 0 && listed.stdout.includes(CODEX_PLUGIN_ID) ? `✓ ${CODEX_PLUGIN_ID} installed` : `○ ${CODEX_PLUGIN_ID} unavailable`,
|
|
118
146
|
];
|
|
119
147
|
}
|
package/dist/harness-links.js
CHANGED
|
@@ -22,17 +22,21 @@ export function installHarnessLinks(links, force = false) {
|
|
|
22
22
|
if (!fs.existsSync(link.source)) {
|
|
23
23
|
throw new Error(`Harness link source is missing: ${link.source}`);
|
|
24
24
|
}
|
|
25
|
+
const existing = lstatMaybe(link.target);
|
|
25
26
|
return {
|
|
26
27
|
...link,
|
|
27
|
-
existing
|
|
28
|
+
existing,
|
|
28
29
|
sourceIsDirectory: fs.statSync(link.source).isDirectory(),
|
|
30
|
+
alreadyManaged: existing !== undefined && sameLink(link.source, link.target),
|
|
29
31
|
};
|
|
30
32
|
});
|
|
31
|
-
for (const { target, existing } of prepared) {
|
|
32
|
-
if (existing !== undefined && !force)
|
|
33
|
+
for (const { target, existing, alreadyManaged } of prepared) {
|
|
34
|
+
if (existing !== undefined && !alreadyManaged && !force)
|
|
33
35
|
throw new Error(`Refusing to overwrite existing link: ${target}\nPass -f to replace it.`);
|
|
34
36
|
}
|
|
35
|
-
for (const { source, target, existing, sourceIsDirectory } of prepared) {
|
|
37
|
+
for (const { source, target, existing, sourceIsDirectory, alreadyManaged } of prepared) {
|
|
38
|
+
if (alreadyManaged)
|
|
39
|
+
continue;
|
|
36
40
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
37
41
|
if (existing !== undefined)
|
|
38
42
|
fs.rmSync(target, { recursive: true, force: true });
|
package/dist/help.js
CHANGED
|
@@ -48,9 +48,19 @@ const COMMANDS = [
|
|
|
48
48
|
{ names: ['hold'], usage: '--as <name> hold [reason | -]', usesSquare: true, group: 'participant', summary: 'Raise a hand and pause participant activity.' },
|
|
49
49
|
{ names: ['resume'], usage: '--as <name> resume', usesSquare: true, group: 'participant', summary: 'Lower the raised hand and resume activity.' },
|
|
50
50
|
{
|
|
51
|
-
names: ['
|
|
52
|
-
summary: 'Install
|
|
53
|
-
details: ['Targets:', ' skills, claude, codex, opencode, pi
|
|
51
|
+
names: ['install'], usage: 'install (--all | <target>...) [-f]', group: 'maintenance',
|
|
52
|
+
summary: 'Install Square support for one or more agent hosts.',
|
|
53
|
+
details: ['Targets:', ' skills, claude, codex, opencode, pi', '', 'Options:', ' --all Install every supported target.', ' -f, --force Replace existing managed links.'],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
names: ['uninstall'], usage: 'uninstall (--all | <target>...)', group: 'maintenance',
|
|
57
|
+
summary: 'Remove Square support from one or more agent hosts.',
|
|
58
|
+
details: ['Targets:', ' skills, claude, codex, opencode, pi', '', 'Options:', ' --all Remove every supported target.'],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
names: ['harness'], usage: 'harness doctor [skills|claude|codex|opencode|pi|delivery]', usesSquare: true, group: 'maintenance', hiddenFromIndex: true,
|
|
62
|
+
summary: 'Diagnose installed agent-host support.',
|
|
63
|
+
details: ['Targets:', ' skills, claude, codex, opencode, pi Diagnose one installed adapter.', ' delivery Diagnose delivery for the selected square.'],
|
|
54
64
|
},
|
|
55
65
|
{ names: ['compact'], usage: 'compact [--keep N]', usesSquare: true, group: 'host', summary: 'Move older activity out of the working artifact while keeping the latest N.' },
|
|
56
66
|
{
|
|
@@ -69,7 +79,7 @@ export function renderGlobalHelp() {
|
|
|
69
79
|
const groups = [
|
|
70
80
|
{ key: 'participant', title: 'In the square:', order: ['join', 'express', 'catch', 'history', 'status', 'hold', 'resume', 'done'] },
|
|
71
81
|
{ key: 'host', title: 'Prepare and manage:', order: ['build', 'list', 'participants', 'warmup', 'compact'] },
|
|
72
|
-
{ key: 'maintenance', title: 'Setup and repair:', order: ['
|
|
82
|
+
{ key: 'maintenance', title: 'Setup and repair:', order: ['install', 'uninstall', 'doctor'] },
|
|
73
83
|
];
|
|
74
84
|
const commandLines = groups.flatMap(({ key, title, order }) => [
|
|
75
85
|
title,
|
package/package.json
CHANGED