@bradygaster/squad-cli 0.9.6-insider.2 → 0.10.0-insider.1
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/CHANGELOG.md +555 -0
- package/README.md +5 -5
- package/dist/cli/commands/build.js +3 -3
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/copilot-bridge.d.ts.map +1 -1
- package/dist/cli/commands/copilot-bridge.js +5 -1
- package/dist/cli/commands/copilot-bridge.js.map +1 -1
- package/dist/cli/commands/cross-squad.d.ts +15 -2
- package/dist/cli/commands/cross-squad.d.ts.map +1 -1
- package/dist/cli/commands/cross-squad.js +78 -4
- package/dist/cli/commands/cross-squad.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +6 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +120 -5
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/export.d.ts +7 -3
- package/dist/cli/commands/export.d.ts.map +1 -1
- package/dist/cli/commands/export.js +68 -16
- package/dist/cli/commands/export.js.map +1 -1
- package/dist/cli/commands/import.d.ts +7 -3
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/import.js +140 -42
- package/dist/cli/commands/import.js.map +1 -1
- package/dist/cli/commands/install-hooks.d.ts.map +1 -1
- package/dist/cli/commands/install-hooks.js +50 -5
- package/dist/cli/commands/install-hooks.js.map +1 -1
- package/dist/cli/commands/link.d.ts.map +1 -1
- package/dist/cli/commands/link.js +7 -1
- package/dist/cli/commands/link.js.map +1 -1
- package/dist/cli/commands/loop.d.ts.map +1 -1
- package/dist/cli/commands/loop.js +7 -5
- package/dist/cli/commands/loop.js.map +1 -1
- package/dist/cli/commands/memory.d.ts +2 -0
- package/dist/cli/commands/memory.d.ts.map +1 -0
- package/dist/cli/commands/memory.js +304 -0
- package/dist/cli/commands/memory.js.map +1 -0
- package/dist/cli/commands/migrate-backend.d.ts +36 -5
- package/dist/cli/commands/migrate-backend.d.ts.map +1 -1
- package/dist/cli/commands/migrate-backend.js +250 -40
- package/dist/cli/commands/migrate-backend.js.map +1 -1
- package/dist/cli/commands/notes.d.ts +27 -0
- package/dist/cli/commands/notes.d.ts.map +1 -0
- package/dist/cli/commands/notes.js +222 -0
- package/dist/cli/commands/notes.js.map +1 -0
- package/dist/cli/commands/plugin.d.ts.map +1 -1
- package/dist/cli/commands/plugin.js +423 -8
- package/dist/cli/commands/plugin.js.map +1 -1
- package/dist/cli/commands/skill.d.ts +31 -0
- package/dist/cli/commands/skill.d.ts.map +1 -0
- package/dist/cli/commands/skill.js +498 -0
- package/dist/cli/commands/skill.js.map +1 -0
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +9 -1
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/state-mcp.d.ts +25 -0
- package/dist/cli/commands/state-mcp.d.ts.map +1 -0
- package/dist/cli/commands/state-mcp.js +174 -0
- package/dist/cli/commands/state-mcp.js.map +1 -0
- package/dist/cli/commands/watch/agent-spawn.d.ts +62 -0
- package/dist/cli/commands/watch/agent-spawn.d.ts.map +1 -0
- package/dist/cli/commands/watch/agent-spawn.js +127 -0
- package/dist/cli/commands/watch/agent-spawn.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/board.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/board.js +2 -1
- package/dist/cli/commands/watch/capabilities/board.js.map +1 -1
- package/dist/cli/commands/watch/capabilities/decision-hygiene.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/decision-hygiene.js +3 -2
- package/dist/cli/commands/watch/capabilities/decision-hygiene.js.map +1 -1
- package/dist/cli/commands/watch/capabilities/execute.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/execute.js +14 -2
- package/dist/cli/commands/watch/capabilities/execute.js.map +1 -1
- package/dist/cli/commands/watch/capabilities/index.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/index.js +2 -0
- package/dist/cli/commands/watch/capabilities/index.js.map +1 -1
- package/dist/cli/commands/watch/capabilities/monitor-email.d.ts +1 -1
- package/dist/cli/commands/watch/capabilities/monitor-email.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/monitor-email.js +21 -4
- package/dist/cli/commands/watch/capabilities/monitor-email.js.map +1 -1
- package/dist/cli/commands/watch/capabilities/monitor-teams.d.ts +1 -1
- package/dist/cli/commands/watch/capabilities/monitor-teams.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/monitor-teams.js +21 -5
- package/dist/cli/commands/watch/capabilities/monitor-teams.js.map +1 -1
- package/dist/cli/commands/watch/capabilities/notes-promote.d.ts +11 -0
- package/dist/cli/commands/watch/capabilities/notes-promote.d.ts.map +1 -0
- package/dist/cli/commands/watch/capabilities/notes-promote.js +124 -0
- package/dist/cli/commands/watch/capabilities/notes-promote.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/retro.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/retro.js +3 -2
- package/dist/cli/commands/watch/capabilities/retro.js.map +1 -1
- package/dist/cli/commands/watch/capabilities/wave-dispatch.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/wave-dispatch.js +2 -1
- package/dist/cli/commands/watch/capabilities/wave-dispatch.js.map +1 -1
- package/dist/cli/commands/watch/index.d.ts.map +1 -1
- package/dist/cli/commands/watch/index.js +16 -12
- package/dist/cli/commands/watch/index.js.map +1 -1
- package/dist/cli/core/cast.d.ts.map +1 -1
- package/dist/cli/core/cast.js +216 -1
- package/dist/cli/core/cast.js.map +1 -1
- package/dist/cli/core/command-help.d.ts +44 -0
- package/dist/cli/core/command-help.d.ts.map +1 -0
- package/dist/cli/core/command-help.js +401 -0
- package/dist/cli/core/command-help.js.map +1 -0
- package/dist/cli/core/copilot-invocation.d.ts +57 -0
- package/dist/cli/core/copilot-invocation.d.ts.map +1 -0
- package/dist/cli/core/copilot-invocation.js +84 -0
- package/dist/cli/core/copilot-invocation.js.map +1 -0
- package/dist/cli/core/effective-squad-dir.d.ts +33 -0
- package/dist/cli/core/effective-squad-dir.d.ts.map +1 -0
- package/dist/cli/core/effective-squad-dir.js +37 -0
- package/dist/cli/core/effective-squad-dir.js.map +1 -0
- package/dist/cli/core/init.d.ts +2 -0
- package/dist/cli/core/init.d.ts.map +1 -1
- package/dist/cli/core/init.js +130 -1
- package/dist/cli/core/init.js.map +1 -1
- package/dist/cli/core/mcp-root.d.ts +73 -0
- package/dist/cli/core/mcp-root.d.ts.map +1 -0
- package/dist/cli/core/mcp-root.js +195 -0
- package/dist/cli/core/mcp-root.js.map +1 -0
- package/dist/cli/core/mcp-spec.d.ts +48 -0
- package/dist/cli/core/mcp-spec.d.ts.map +1 -0
- package/dist/cli/core/mcp-spec.js +62 -0
- package/dist/cli/core/mcp-spec.js.map +1 -0
- package/dist/cli/core/npm-registry.d.ts +25 -0
- package/dist/cli/core/npm-registry.d.ts.map +1 -0
- package/dist/cli/core/npm-registry.js +65 -0
- package/dist/cli/core/npm-registry.js.map +1 -0
- package/dist/cli/core/templates.d.ts.map +1 -1
- package/dist/cli/core/templates.js +102 -24
- package/dist/cli/core/templates.js.map +1 -1
- package/dist/cli/core/upgrade.d.ts +29 -0
- package/dist/cli/core/upgrade.d.ts.map +1 -1
- package/dist/cli/core/upgrade.js +413 -15
- package/dist/cli/core/upgrade.js.map +1 -1
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/shell/components/App.js +1 -1
- package/dist/cli/shell/components/App.js.map +1 -1
- package/dist/cli/shell/components/MessageStream.js +2 -2
- package/dist/cli/shell/components/MessageStream.js.map +1 -1
- package/dist/cli/shell/coordinator.d.ts.map +1 -1
- package/dist/cli/shell/coordinator.js +11 -5
- package/dist/cli/shell/coordinator.js.map +1 -1
- package/dist/cli/shell/index.d.ts.map +1 -1
- package/dist/cli/shell/index.js +14 -7
- package/dist/cli/shell/index.js.map +1 -1
- package/dist/cli/shell/lifecycle.d.ts.map +1 -1
- package/dist/cli/shell/lifecycle.js +12 -7
- package/dist/cli/shell/lifecycle.js.map +1 -1
- package/dist/cli/shell/session-store.d.ts +4 -4
- package/dist/cli/shell/session-store.d.ts.map +1 -1
- package/dist/cli/shell/session-store.js +11 -11
- package/dist/cli/shell/session-store.js.map +1 -1
- package/dist/cli-entry.js +171 -50
- package/dist/cli-entry.js.map +1 -1
- package/package.json +9 -4
- package/scripts/patch-esm-imports.mjs +3 -1
- package/templates/after-agent-reference.md +64 -0
- package/templates/ceremony-reference.md +82 -0
- package/templates/client-compatibility-reference.md +46 -0
- package/templates/copilot-agent.md +96 -0
- package/templates/copilot-instructions.md +14 -0
- package/templates/fact-checker-policy.md +104 -0
- package/templates/model-selection-reference.md +101 -0
- package/templates/prd-intake.md +105 -0
- package/templates/rai-charter.md +110 -0
- package/templates/rai-policy.md +103 -0
- package/templates/ralph-reference.md +141 -0
- package/templates/routing.md +1 -0
- package/templates/scribe-charter.md +18 -151
- package/templates/session-init-reference.md +199 -0
- package/templates/skills/cross-squad/SKILL.md +66 -6
- package/templates/skills/cross-squad-communication/SKILL.md +399 -0
- package/templates/skills/e2e-template-testing/SKILL.md +557 -0
- package/templates/skills/fact-checking/SKILL.md +61 -0
- package/templates/skills/release-process/SKILL.md +2 -2
- package/templates/skills/squad/SKILL.md +299 -0
- package/templates/skills/squad-help/SKILL.md +97 -0
- package/templates/skills/squad-version-check/SKILL.md +169 -0
- package/templates/skills/tiered-memory/SKILL.md +31 -44
- package/templates/spawn-reference.md +131 -0
- package/templates/squad.agent.md.template +306 -618
- package/templates/workflow-wiring-appendix-a-code-reviewer.md +131 -0
- package/templates/workflow-wiring-appendix-b-documenter.md +140 -0
- package/templates/workflow-wiring-guide.md +276 -0
- package/templates/workflows/squad-heartbeat.yml +167 -167
- package/templates/worktree-reference.md +126 -0
package/dist/cli/core/upgrade.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @module cli/core/upgrade
|
|
5
5
|
*/
|
|
6
6
|
import path from 'node:path';
|
|
7
|
+
import { execFileSync } from 'node:child_process';
|
|
7
8
|
import { FSStorageProvider } from '@bradygaster/squad-sdk';
|
|
8
9
|
import { success, warn, info, dim } from './output.js';
|
|
9
10
|
import { fatal } from './errors.js';
|
|
@@ -11,7 +12,105 @@ import { detectSquadDir } from './detect-squad-dir.js';
|
|
|
11
12
|
import { TEMPLATE_MANIFEST, getTemplatesDir } from './templates.js';
|
|
12
13
|
import { runMigrations } from './migrations.js';
|
|
13
14
|
import { getPackageVersion, stampVersion, readInstalledVersion } from './version.js';
|
|
15
|
+
import { resolveSquadStateMcpSpec } from './mcp-spec.js';
|
|
16
|
+
export { resolveSquadStateMcpSpec } from './mcp-spec.js';
|
|
17
|
+
import { ensureSquadStateMcpInRoot, tombstoneStaleSquadStateInProjectMcp } from './mcp-root.js';
|
|
14
18
|
const storage = new FSStorageProvider();
|
|
19
|
+
/**
|
|
20
|
+
* Returns true if the version looks like a local dev build or unpublished
|
|
21
|
+
* pre-release that cannot be resolved from the public npm registry.
|
|
22
|
+
* Guards against writing unresolvable version strings into MCP config
|
|
23
|
+
* (see #1204).
|
|
24
|
+
*/
|
|
25
|
+
export function isLocalOrUnpublishedVersion(version) {
|
|
26
|
+
if (!version || version === '0.0.0')
|
|
27
|
+
return true;
|
|
28
|
+
// Local linked builds often have 0.0.0-development or similar sentinel
|
|
29
|
+
if (/^0\.0\.0/.test(version))
|
|
30
|
+
return true;
|
|
31
|
+
// Versions with `+` build metadata (e.g. 0.10.0+local.1234) are not
|
|
32
|
+
// publishable to npm — they indicate a local build.
|
|
33
|
+
if (version.includes('+'))
|
|
34
|
+
return true;
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
function buildMcpServerSpecs(isGitHub, cliVersion) {
|
|
38
|
+
// Pin the squad-cli package to the currently-installed CLI version so that
|
|
39
|
+
// `npx -y @bradygaster/squad-cli state-mcp` does NOT silently resolve to the
|
|
40
|
+
// npm `latest` dist-tag (which may predate the `state-mcp` command and thus
|
|
41
|
+
// expose zero tools to Copilot — see MCP-BRIDGE-BROKEN root cause).
|
|
42
|
+
//
|
|
43
|
+
// #1204: When the CLI is a local dev build or unpublished pre-release, fall
|
|
44
|
+
// back to the @insider dist-tag to avoid writing an unresolvable version
|
|
45
|
+
// string that breaks npx resolution at session start.
|
|
46
|
+
let pkgSpec;
|
|
47
|
+
if (!cliVersion || isLocalOrUnpublishedVersion(cliVersion)) {
|
|
48
|
+
pkgSpec = '@bradygaster/squad-cli@insider';
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
pkgSpec = `@bradygaster/squad-cli@${cliVersion}`;
|
|
52
|
+
}
|
|
53
|
+
const servers = [
|
|
54
|
+
{
|
|
55
|
+
name: 'squad_state',
|
|
56
|
+
command: 'npx',
|
|
57
|
+
args: ['-y', pkgSpec, 'state-mcp'],
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
servers.push(isGitHub
|
|
61
|
+
? {
|
|
62
|
+
name: 'EXAMPLE-github',
|
|
63
|
+
command: 'npx',
|
|
64
|
+
args: ['-y', '@anthropic/github-mcp-server'],
|
|
65
|
+
env: { GITHUB_TOKEN: '${GITHUB_TOKEN}' },
|
|
66
|
+
}
|
|
67
|
+
: {
|
|
68
|
+
name: 'EXAMPLE-azure-devops',
|
|
69
|
+
command: 'npx',
|
|
70
|
+
args: ['-y', '@azure/devops-mcp-server'],
|
|
71
|
+
env: {
|
|
72
|
+
AZURE_DEVOPS_ORG: '${AZURE_DEVOPS_ORG}',
|
|
73
|
+
AZURE_DEVOPS_PAT: '${AZURE_DEVOPS_PAT}',
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
return servers;
|
|
77
|
+
}
|
|
78
|
+
function yamlSingleQuoted(value) {
|
|
79
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
80
|
+
}
|
|
81
|
+
function yamlEnvValue(value) {
|
|
82
|
+
if (/^\$\{[A-Z0-9_]+\}$/.test(value)) {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
86
|
+
}
|
|
87
|
+
function buildMcpFrontmatterBlock(isGitHub, cliVersion) {
|
|
88
|
+
const lines = ['mcp-servers:'];
|
|
89
|
+
for (const server of buildMcpServerSpecs(isGitHub, cliVersion)) {
|
|
90
|
+
lines.push(` ${server.name}:`);
|
|
91
|
+
lines.push(' type: local');
|
|
92
|
+
lines.push(` command: ${server.command}`);
|
|
93
|
+
lines.push(` args: [${server.args.map(yamlSingleQuoted).join(', ')}]`);
|
|
94
|
+
lines.push(' tools: ["*"]');
|
|
95
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
96
|
+
lines.push(' env:');
|
|
97
|
+
for (const [key, value] of Object.entries(server.env)) {
|
|
98
|
+
lines.push(` ${key}: ${yamlEnvValue(value)}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return lines.join('\n');
|
|
103
|
+
}
|
|
104
|
+
function injectMcpFrontmatter(content, isGitHub, cliVersion) {
|
|
105
|
+
const closingStart = content.indexOf('\n---', 4);
|
|
106
|
+
if (!content.startsWith('---') || closingStart === -1) {
|
|
107
|
+
return content;
|
|
108
|
+
}
|
|
109
|
+
return content.slice(0, closingStart)
|
|
110
|
+
+ '\n'
|
|
111
|
+
+ buildMcpFrontmatterBlock(isGitHub, cliVersion)
|
|
112
|
+
+ content.slice(closingStart);
|
|
113
|
+
}
|
|
15
114
|
function copyDirRecursive(src, dest, force = true) {
|
|
16
115
|
storage.mkdirSync(dest, { recursive: true });
|
|
17
116
|
for (const entry of storage.listSync(src)) {
|
|
@@ -49,6 +148,64 @@ function compareSemver(a, b) {
|
|
|
49
148
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
50
149
|
return 0;
|
|
51
150
|
}
|
|
151
|
+
function readSquadConfig(squadDir) {
|
|
152
|
+
const configPath = path.join(squadDir, 'config.json');
|
|
153
|
+
if (!storage.existsSync(configPath))
|
|
154
|
+
return {};
|
|
155
|
+
try {
|
|
156
|
+
const raw = storage.readSync(configPath);
|
|
157
|
+
if (!raw)
|
|
158
|
+
return {};
|
|
159
|
+
const parsed = JSON.parse(raw);
|
|
160
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
161
|
+
return parsed;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Ignore malformed config for upgrade compatibility.
|
|
166
|
+
}
|
|
167
|
+
return {};
|
|
168
|
+
}
|
|
169
|
+
function readMcpConfigMode(config) {
|
|
170
|
+
return config['mcpConfigMode'] === 'agent-frontmatter' ? 'agent-frontmatter' : 'copilot-file';
|
|
171
|
+
}
|
|
172
|
+
function detectMcpConfigMode(config, agentDest) {
|
|
173
|
+
const configuredMode = readMcpConfigMode(config);
|
|
174
|
+
if (configuredMode === 'agent-frontmatter')
|
|
175
|
+
return configuredMode;
|
|
176
|
+
if (storage.existsSync(agentDest)) {
|
|
177
|
+
const existingAgent = storage.readSync(agentDest) ?? '';
|
|
178
|
+
const frontmatterEnd = existingAgent.indexOf('\n---', 4);
|
|
179
|
+
const frontmatter = frontmatterEnd === -1 ? '' : existingAgent.slice(0, frontmatterEnd);
|
|
180
|
+
if (frontmatter.includes('mcp-servers:')) {
|
|
181
|
+
return 'agent-frontmatter';
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return configuredMode;
|
|
185
|
+
}
|
|
186
|
+
function detectIsGitHubForMcp(dest, config) {
|
|
187
|
+
if (config['platform'] === 'azure-devops')
|
|
188
|
+
return false;
|
|
189
|
+
try {
|
|
190
|
+
const remoteUrl = execFileSync('git', ['remote', 'get-url', 'origin'], { cwd: dest, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
191
|
+
const remoteUrlLower = remoteUrl.toLowerCase();
|
|
192
|
+
if (remoteUrlLower.includes('dev.azure.com') || remoteUrlLower.includes('visualstudio.com') || remoteUrlLower.includes('ssh.dev.azure.com')) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// No git remote — assume GitHub, matching init behavior.
|
|
198
|
+
}
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
function writeAgentTemplate(agentSrc, agentDest, cliVersion, mcpConfigMode, isGitHub) {
|
|
202
|
+
let agentContent = storage.readSync(agentSrc) ?? '';
|
|
203
|
+
if (mcpConfigMode === 'agent-frontmatter') {
|
|
204
|
+
agentContent = injectMcpFrontmatter(agentContent, isGitHub, cliVersion);
|
|
205
|
+
}
|
|
206
|
+
storage.writeSync(agentDest, agentContent);
|
|
207
|
+
stampVersion(agentDest, cliVersion);
|
|
208
|
+
}
|
|
52
209
|
/**
|
|
53
210
|
* Detect project type by checking marker files
|
|
54
211
|
*/
|
|
@@ -252,6 +409,7 @@ const GITIGNORE_ENTRIES = [
|
|
|
252
409
|
'.squad/log/',
|
|
253
410
|
'.squad/decisions/inbox/',
|
|
254
411
|
'.squad/sessions/',
|
|
412
|
+
'.squad/.cache/',
|
|
255
413
|
'.squad-workstream',
|
|
256
414
|
];
|
|
257
415
|
const ENSURE_DIRECTORIES = [
|
|
@@ -262,7 +420,7 @@ const ENSURE_DIRECTORIES = [
|
|
|
262
420
|
'.squad/decisions/inbox',
|
|
263
421
|
'.squad/casting',
|
|
264
422
|
'.squad/agents',
|
|
265
|
-
'.
|
|
423
|
+
'.github/skills',
|
|
266
424
|
];
|
|
267
425
|
/**
|
|
268
426
|
* Ensure .gitattributes has required merge=union rules (idempotent)
|
|
@@ -417,7 +575,74 @@ function warnIfSkillCustomized(srcPath, destPath, sourceName) {
|
|
|
417
575
|
}
|
|
418
576
|
}
|
|
419
577
|
/**
|
|
420
|
-
*
|
|
578
|
+
* Migrate skills from the legacy `.copilot/skills/<name>/` location to the
|
|
579
|
+
* canonical `.github/skills/<name>/`. Only migrates skills that appear in
|
|
580
|
+
* `TEMPLATE_MANIFEST` (manifest-curated Squad skills) — does NOT touch
|
|
581
|
+
* user-added skills in `.copilot/skills/`. After successful migration,
|
|
582
|
+
* removes the now-empty `.copilot/skills/<name>/` directories.
|
|
583
|
+
*
|
|
584
|
+
* Idempotent: when a skill already exists at the new location the legacy
|
|
585
|
+
* copy is tombstoned without comparing content — this protects any
|
|
586
|
+
* in-place customization the user made at `.github/skills/<name>/` from
|
|
587
|
+
* being clobbered, and means re-running upgrade is a no-op on the new
|
|
588
|
+
* location. If only the legacy copy exists, it is moved over.
|
|
589
|
+
*
|
|
590
|
+
* See bradygaster/squad#1126 (canonical issue) — this is the migration
|
|
591
|
+
* piece of that fix; #1304 is the PR that implements it.
|
|
592
|
+
*/
|
|
593
|
+
function migrateLegacyCopilotSkills(dest) {
|
|
594
|
+
const legacyDir = path.join(dest, '.copilot', 'skills');
|
|
595
|
+
if (!storage.existsSync(legacyDir))
|
|
596
|
+
return { migrated: [], tombstoned: [] };
|
|
597
|
+
const manifestSkills = new Set(TEMPLATE_MANIFEST
|
|
598
|
+
.filter(f => f.source.startsWith('skills/'))
|
|
599
|
+
.map(f => f.source.split('/')[1]) // 'skills/foo/SKILL.md' -> 'foo'
|
|
600
|
+
.filter((s) => Boolean(s)));
|
|
601
|
+
const newDir = path.join(dest, '.github', 'skills');
|
|
602
|
+
const migrated = [];
|
|
603
|
+
const tombstoned = [];
|
|
604
|
+
for (const entry of storage.listSync(legacyDir)) {
|
|
605
|
+
if (!manifestSkills.has(entry))
|
|
606
|
+
continue; // leave user-added skills alone
|
|
607
|
+
const legacySkillDir = path.join(legacyDir, entry);
|
|
608
|
+
if (!storage.isDirectorySync(legacySkillDir))
|
|
609
|
+
continue;
|
|
610
|
+
const newSkillDir = path.join(newDir, entry);
|
|
611
|
+
if (storage.existsSync(newSkillDir)) {
|
|
612
|
+
// New location already has it — drop the legacy copy without overwriting.
|
|
613
|
+
try {
|
|
614
|
+
storage.deleteDirSync(legacySkillDir);
|
|
615
|
+
tombstoned.push(path.posix.join('.copilot/skills', entry));
|
|
616
|
+
}
|
|
617
|
+
catch {
|
|
618
|
+
// best-effort; leave the legacy dir if we can't remove it
|
|
619
|
+
}
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
try {
|
|
623
|
+
storage.mkdirSync(newDir, { recursive: true });
|
|
624
|
+
copyDirRecursive(legacySkillDir, newSkillDir);
|
|
625
|
+
storage.deleteDirSync(legacySkillDir);
|
|
626
|
+
migrated.push(path.posix.join('.github/skills', entry));
|
|
627
|
+
}
|
|
628
|
+
catch {
|
|
629
|
+
// best-effort; leave the legacy dir if anything fails
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
// If the legacy `.copilot/skills/` directory is now empty, remove it too.
|
|
633
|
+
try {
|
|
634
|
+
if (storage.existsSync(legacyDir) && storage.listSync(legacyDir).length === 0) {
|
|
635
|
+
storage.deleteDirSync(legacyDir);
|
|
636
|
+
tombstoned.push('.copilot/skills');
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
catch {
|
|
640
|
+
// best-effort
|
|
641
|
+
}
|
|
642
|
+
return { migrated, tombstoned };
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Sync manifest-declared skills to .github/skills/, respecting overwriteOnUpgrade.
|
|
421
646
|
* Only skills listed in TEMPLATE_MANIFEST are installed — not the entire templates/skills/ dir.
|
|
422
647
|
*/
|
|
423
648
|
function syncAllSkills(dest, templatesDir) {
|
|
@@ -464,7 +689,7 @@ function refreshSquadTemplatesDir(dest, templatesDir) {
|
|
|
464
689
|
/**
|
|
465
690
|
* Run all ensure* checks and skill/template sync — shared by both code paths
|
|
466
691
|
*/
|
|
467
|
-
function runEnsureChecks(dest, templatesDir, filesUpdated) {
|
|
692
|
+
async function runEnsureChecks(dest, templatesDir, filesUpdated) {
|
|
468
693
|
const attrAdded = ensureGitattributes(dest);
|
|
469
694
|
if (attrAdded.length > 0) {
|
|
470
695
|
success(`ensured .gitattributes (${attrAdded.length} rules added)`);
|
|
@@ -485,14 +710,170 @@ function runEnsureChecks(dest, templatesDir, filesUpdated) {
|
|
|
485
710
|
success(`scaffolded ${castingFiles.length} default casting files`);
|
|
486
711
|
filesUpdated.push(...castingFiles);
|
|
487
712
|
}
|
|
713
|
+
const memoryFiles = ensureMemoryGovernanceUpgradeDefaults(dest);
|
|
714
|
+
if (memoryFiles.length > 0) {
|
|
715
|
+
success(`scaffolded memory governance defaults (${memoryFiles.length} files/directories)`);
|
|
716
|
+
filesUpdated.push(...memoryFiles);
|
|
717
|
+
}
|
|
718
|
+
const builtinAgents = ensureBuiltinAgents(dest, templatesDir);
|
|
719
|
+
if (builtinAgents.length > 0) {
|
|
720
|
+
const uniqueAgentNames = Array.from(new Set(builtinAgents.map(p => path.basename(path.dirname(p)))));
|
|
721
|
+
success(`scaffolded ${uniqueAgentNames.length} built-in agent(s): ${uniqueAgentNames.join(', ')}`);
|
|
722
|
+
filesUpdated.push(...builtinAgents);
|
|
723
|
+
}
|
|
724
|
+
const skillMigration = migrateLegacyCopilotSkills(dest);
|
|
725
|
+
if (skillMigration.migrated.length > 0) {
|
|
726
|
+
success(`migrated ${skillMigration.migrated.length} skill(s) from .copilot/skills/ → .github/skills/`);
|
|
727
|
+
filesUpdated.push(...skillMigration.migrated);
|
|
728
|
+
}
|
|
729
|
+
if (skillMigration.tombstoned.length > 0) {
|
|
730
|
+
success(`removed ${skillMigration.tombstoned.length} stale .copilot/skills entries (now live at .github/skills/)`);
|
|
731
|
+
}
|
|
488
732
|
const skillCount = syncAllSkills(dest, templatesDir);
|
|
489
733
|
if (skillCount > 0) {
|
|
490
|
-
success(`synced ${skillCount} skills to .
|
|
734
|
+
success(`synced ${skillCount} skills to .github/skills/`);
|
|
491
735
|
filesUpdated.push(`skills (${skillCount})`);
|
|
492
736
|
}
|
|
493
737
|
refreshSquadTemplatesDir(dest, templatesDir);
|
|
494
738
|
success('refreshed .squad/templates/');
|
|
495
739
|
filesUpdated.push('.squad/templates/');
|
|
740
|
+
// iter-8: write squad_state MCP entry to repo-root `.mcp.json`
|
|
741
|
+
// (auto-loaded by Copilot CLI ≥1.0.59, which walks up from cwd to the
|
|
742
|
+
// git root looking for .mcp.json) and tombstone any stale project-level
|
|
743
|
+
// entry left by older Squad versions in `.copilot/mcp-config.json`.
|
|
744
|
+
// No HOME modifications.
|
|
745
|
+
const pinnedSpec = await resolveSquadStateMcpSpec(getPackageVersion());
|
|
746
|
+
try {
|
|
747
|
+
const rootResult = ensureSquadStateMcpInRoot(dest, getPackageVersion(), pinnedSpec);
|
|
748
|
+
if (rootResult.written) {
|
|
749
|
+
success(`installed squad_state MCP server to .mcp.json (${describeMcpSpec(pinnedSpec)}) — Copilot CLI will auto-load on next invocation`);
|
|
750
|
+
filesUpdated.push('.mcp.json');
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
catch (err) {
|
|
754
|
+
warn(`Could not write .mcp.json: ${err instanceof Error ? err.message : err}`);
|
|
755
|
+
}
|
|
756
|
+
// iter-8: do NOT write to ~/.copilot/mcp-config.json on upgrade. The
|
|
757
|
+
// repo-root .mcp.json write above is sufficient for Copilot CLI ≥1.0.59
|
|
758
|
+
// (walks from cwd up looking for .mcp.json) AND for `copilot -p` from
|
|
759
|
+
// the project root. Out-of-tree `copilot -p` should use
|
|
760
|
+
// `--additional-mcp-config @.mcp.json`. See bradygaster/squad#1296.
|
|
761
|
+
const tomb = tombstoneStaleSquadStateInProjectMcp(dest);
|
|
762
|
+
if (tomb.removed) {
|
|
763
|
+
success(`removed stale squad_state from ${tomb.path} (now lives in .mcp.json)`);
|
|
764
|
+
filesUpdated.push('.copilot/mcp-config.json (tombstoned)');
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
/** Human-readable single-line description of an McpSpec for success() messages. */
|
|
768
|
+
export function describeMcpSpec(spec) {
|
|
769
|
+
// After iter-7 all specs are `npx -y <pkg@version-or-tag> state-mcp`.
|
|
770
|
+
const pkg = spec.args[1] ?? '<unknown>';
|
|
771
|
+
return spec.source === 'insider' ? `${pkg} (@insider fallback)` : pkg;
|
|
772
|
+
}
|
|
773
|
+
export function ensureMemoryGovernanceUpgradeDefaults(dest) {
|
|
774
|
+
const memoryDir = path.join(dest, '.squad', 'memory');
|
|
775
|
+
const created = [];
|
|
776
|
+
for (const dir of ['local', 'policy-inbox', 'semantic-inbox', 'tombstones']) {
|
|
777
|
+
const fullPath = path.join(memoryDir, dir);
|
|
778
|
+
if (!storage.existsSync(fullPath)) {
|
|
779
|
+
storage.mkdirSync(fullPath, { recursive: true });
|
|
780
|
+
created.push(path.join('.squad', 'memory', dir));
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
const defaults = {
|
|
784
|
+
'config.json': JSON.stringify({
|
|
785
|
+
version: 1,
|
|
786
|
+
defaultProvider: 'local',
|
|
787
|
+
promptOnlyFallback: true,
|
|
788
|
+
externalProviders: {
|
|
789
|
+
hostInjectedCopilotAdapter: {
|
|
790
|
+
enabled: false,
|
|
791
|
+
requireApproval: true,
|
|
792
|
+
},
|
|
793
|
+
},
|
|
794
|
+
policy: {
|
|
795
|
+
rejectForbidden: true,
|
|
796
|
+
rejectTransientDurableWrites: true,
|
|
797
|
+
auditContent: false,
|
|
798
|
+
},
|
|
799
|
+
}, null, 2) + '\n',
|
|
800
|
+
'index.json': '[]\n',
|
|
801
|
+
'audit.jsonl': '',
|
|
802
|
+
};
|
|
803
|
+
for (const [file, content] of Object.entries(defaults)) {
|
|
804
|
+
const fullPath = path.join(memoryDir, file);
|
|
805
|
+
if (!storage.existsSync(fullPath)) {
|
|
806
|
+
storage.writeSync(fullPath, content);
|
|
807
|
+
created.push(path.join('.squad', 'memory', file));
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
return created;
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Scaffold always-on built-in agent charters (Rai, Fact Checker) that ship
|
|
814
|
+
* as templates but may be missing from older squads. Idempotent — only writes
|
|
815
|
+
* when the agent directory is absent. Never overwrites existing charters or
|
|
816
|
+
* history. Sources charter content from the shipped `templates/{name}-charter.md`
|
|
817
|
+
* files when available, falling back to a minimal placeholder otherwise.
|
|
818
|
+
*
|
|
819
|
+
* Scribe and Ralph are intentionally NOT scaffolded here — they should already
|
|
820
|
+
* exist in any squad that ran a prior init, and their charters are inlined in
|
|
821
|
+
* cast.ts (no shipped template file). Adding them here would risk overwriting
|
|
822
|
+
* customized versions on legacy squads.
|
|
823
|
+
*
|
|
824
|
+
* @param dest Root directory containing .squad/
|
|
825
|
+
* @param templatesDir Directory containing shipped charter templates
|
|
826
|
+
* @returns Paths (relative to dest) of created agent files
|
|
827
|
+
*/
|
|
828
|
+
export function ensureBuiltinAgents(dest, templatesDir) {
|
|
829
|
+
const agentsDir = path.join(dest, '.squad', 'agents');
|
|
830
|
+
if (!storage.existsSync(agentsDir)) {
|
|
831
|
+
storage.mkdirSync(agentsDir, { recursive: true });
|
|
832
|
+
}
|
|
833
|
+
// Built-in agents that ship as charter templates. Each entry maps to:
|
|
834
|
+
// - dirName: case-preserving directory name under .squad/agents/
|
|
835
|
+
// - templateFile: filename under templatesDir (charter template)
|
|
836
|
+
// - displayName: shown in history.md header
|
|
837
|
+
const builtins = [
|
|
838
|
+
{ dirName: 'Rai', templateFile: 'Rai-charter.md', displayName: 'Rai' },
|
|
839
|
+
{ dirName: 'fact-checker', templateFile: 'fact-checker-charter.md', displayName: 'Fact Checker' },
|
|
840
|
+
];
|
|
841
|
+
const created = [];
|
|
842
|
+
for (const agent of builtins) {
|
|
843
|
+
const agentDir = path.join(agentsDir, agent.dirName);
|
|
844
|
+
const charterPath = path.join(agentDir, 'charter.md');
|
|
845
|
+
const historyPath = path.join(agentDir, 'history.md');
|
|
846
|
+
// Idempotent: skip if agent directory already exists. Never overwrite
|
|
847
|
+
// existing charters or history files (preserves user customization).
|
|
848
|
+
if (storage.existsSync(agentDir))
|
|
849
|
+
continue;
|
|
850
|
+
// Source charter content from the shipped template; fall back to a
|
|
851
|
+
// minimal placeholder if the template file is missing (defensive — should
|
|
852
|
+
// not happen in a well-formed install, but better than crashing upgrade).
|
|
853
|
+
const tplPath = path.join(templatesDir, agent.templateFile);
|
|
854
|
+
let charterContent;
|
|
855
|
+
if (storage.existsSync(tplPath)) {
|
|
856
|
+
charterContent = storage.readSync(tplPath) ?? '';
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
charterContent = `# ${agent.displayName}\n\n> Charter template not found in shipped templates. Run \`squad upgrade\` after reinstalling the CLI to repair.\n`;
|
|
860
|
+
}
|
|
861
|
+
try {
|
|
862
|
+
storage.mkdirSync(agentDir, { recursive: true });
|
|
863
|
+
storage.writeSync(charterPath, charterContent);
|
|
864
|
+
created.push(path.join('.squad', 'agents', agent.dirName, 'charter.md'));
|
|
865
|
+
storage.writeSync(historyPath, `# ${agent.displayName} — History\n\n## Learnings\n\nInitial scaffold via \`squad upgrade\`. Ready for work.\n`);
|
|
866
|
+
created.push(path.join('.squad', 'agents', agent.dirName, 'history.md'));
|
|
867
|
+
}
|
|
868
|
+
catch (err) {
|
|
869
|
+
if (err instanceof Error && 'code' in err && ['EPERM', 'EACCES'].includes(err.code ?? '')) {
|
|
870
|
+
warn(`Could not scaffold built-in agent ${agent.displayName} (read-only). Create .squad/agents/${agent.dirName}/ manually.`);
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
throw err;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
return created;
|
|
496
877
|
}
|
|
497
878
|
/**
|
|
498
879
|
* Run the upgrade command
|
|
@@ -513,6 +894,9 @@ export async function runUpgrade(dest, options = {}) {
|
|
|
513
894
|
}
|
|
514
895
|
const agentDest = path.join(dest, '.github', 'agents', 'squad.agent.md');
|
|
515
896
|
const oldVersion = readInstalledVersion(agentDest) ?? '0.0.0';
|
|
897
|
+
const squadConfig = readSquadConfig(squadDirInfo.path);
|
|
898
|
+
const mcpConfigMode = detectMcpConfigMode(squadConfig, agentDest);
|
|
899
|
+
const isGitHubForMcp = detectIsGitHubForMcp(dest, squadConfig);
|
|
516
900
|
// Check if already current
|
|
517
901
|
const isAlreadyCurrent = !options.force && oldVersion && oldVersion !== '0.0.0' && compareSemver(oldVersion, cliVersion) === 0;
|
|
518
902
|
const projectType = detectProjectType(dest);
|
|
@@ -537,8 +921,7 @@ export async function runUpgrade(dest, options = {}) {
|
|
|
537
921
|
const agentSrc = path.join(templatesDir, 'squad.agent.md.template');
|
|
538
922
|
if (storage.existsSync(agentSrc)) {
|
|
539
923
|
storage.mkdirSync(path.dirname(agentDest), { recursive: true });
|
|
540
|
-
|
|
541
|
-
stampVersion(agentDest, cliVersion);
|
|
924
|
+
writeAgentTemplate(agentSrc, agentDest, cliVersion, mcpConfigMode, isGitHubForMcp);
|
|
542
925
|
success('upgraded squad.agent.md');
|
|
543
926
|
filesUpdated.push('squad.agent.md');
|
|
544
927
|
}
|
|
@@ -546,7 +929,7 @@ export async function runUpgrade(dest, options = {}) {
|
|
|
546
929
|
warn('squad.agent.md.template not found — squad.agent.md was not refreshed. Reinstall or repair the CLI to restore the missing template.');
|
|
547
930
|
}
|
|
548
931
|
// Run infrastructure ensure checks even when already current
|
|
549
|
-
runEnsureChecks(dest, templatesDir, filesUpdated);
|
|
932
|
+
await runEnsureChecks(dest, templatesDir, filesUpdated);
|
|
550
933
|
return {
|
|
551
934
|
fromVersion: oldVersion,
|
|
552
935
|
toVersion: cliVersion,
|
|
@@ -561,8 +944,7 @@ export async function runUpgrade(dest, options = {}) {
|
|
|
561
944
|
fatal('squad.agent.md.template not found in templates — installation may be corrupted');
|
|
562
945
|
}
|
|
563
946
|
storage.mkdirSync(path.dirname(agentDest), { recursive: true });
|
|
564
|
-
|
|
565
|
-
stampVersion(agentDest, cliVersion);
|
|
947
|
+
writeAgentTemplate(agentSrc, agentDest, cliVersion, mcpConfigMode, isGitHubForMcp);
|
|
566
948
|
const fromLabel = oldVersion === '0.0.0' || !oldVersion ? 'unknown' : oldVersion;
|
|
567
949
|
success(`upgraded coordinator from ${fromLabel} to ${cliVersion}`);
|
|
568
950
|
filesUpdated.push('squad.agent.md');
|
|
@@ -613,7 +995,7 @@ export async function runUpgrade(dest, options = {}) {
|
|
|
613
995
|
}
|
|
614
996
|
}
|
|
615
997
|
// Run infrastructure ensure checks
|
|
616
|
-
runEnsureChecks(dest, templatesDir, filesUpdated);
|
|
998
|
+
await runEnsureChecks(dest, templatesDir, filesUpdated);
|
|
617
999
|
console.log();
|
|
618
1000
|
info(`Upgrade complete: v${fromLabel} → v${cliVersion}`);
|
|
619
1001
|
if (migrationsApplied.some(m => m.toLowerCase().includes('scrub email'))) {
|
|
@@ -677,15 +1059,31 @@ export async function selfUpgradeCli(options = {}) {
|
|
|
677
1059
|
execSync(cmd, { stdio: 'inherit' });
|
|
678
1060
|
}
|
|
679
1061
|
catch (err) {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
1062
|
+
// UPGRADE-EPERM-FALSE-SUCCESS fix: do NOT swallow self-upgrade failures.
|
|
1063
|
+
// Previously this only printed a warning and returned, causing the caller
|
|
1064
|
+
// (cli-entry.ts) to then unconditionally print "✅ Upgraded" and exit 0.
|
|
1065
|
+
// Now we surface the failure as a thrown error so the caller can exit non-zero
|
|
1066
|
+
// and avoid the contradictory success message.
|
|
1067
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1068
|
+
const code = err instanceof Error && 'code' in err
|
|
1069
|
+
? (err.code ?? '')
|
|
1070
|
+
: '';
|
|
1071
|
+
const isPermission = code === 'EACCES' || code === 'EPERM' || /EACCES|EPERM|permission denied/i.test(errMsg);
|
|
1072
|
+
const isBusy = code === 'EBUSY' || /EBUSY|in use|cannot access|being used by another process/i.test(errMsg);
|
|
1073
|
+
let hint;
|
|
683
1074
|
if (isPermission) {
|
|
684
|
-
|
|
1075
|
+
hint = `Permission denied. Try: sudo ${cmd}`;
|
|
1076
|
+
}
|
|
1077
|
+
else if (isBusy) {
|
|
1078
|
+
hint = `A file is in use (likely another squad shell is running). Close other squad CLI processes and retry: ${cmd}`;
|
|
685
1079
|
}
|
|
686
1080
|
else {
|
|
687
|
-
|
|
1081
|
+
hint = `Upgrade failed. Try running manually: ${cmd}`;
|
|
688
1082
|
}
|
|
1083
|
+
warn(hint);
|
|
1084
|
+
const failure = new Error(`Self-upgrade failed: ${hint}`);
|
|
1085
|
+
failure.code = code || undefined;
|
|
1086
|
+
throw failure;
|
|
689
1087
|
}
|
|
690
1088
|
}
|
|
691
1089
|
//# sourceMappingURL=upgrade.js.map
|