squad-skills 0.1.0
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/LICENSE +21 -0
- package/README.md +184 -0
- package/bin/cli.mjs +13 -0
- package/dist/cli/cli.d.mts +1 -0
- package/dist/cli/cli.mjs +450 -0
- package/package.json +70 -0
- package/skills/squad-backend/SKILL.md +128 -0
- package/skills/squad-backend/references/backend-api-data-and-messaging.md +67 -0
- package/skills/squad-backend/references/backend-performance-reliability-and-observability.md +49 -0
- package/skills/squad-backend/references/backend-security-auth-and-privacy.md +62 -0
- package/skills/squad-backend/references/backend-stack-and-runtime-matrix.md +68 -0
- package/skills/squad-backend/references/backend-system-design-and-distributed-systems.md +61 -0
- package/skills/squad-backend/references/backend-testing-debugging-and-mindset.md +48 -0
- package/skills/squad-backend/references/backend-worked-decisions.md +63 -0
- package/skills/squad-backend/references/official-sources.md +56 -0
- package/skills/squad-backend/references/quality-bar-and-preflight.md +54 -0
- package/skills/squad-backend/references/runtime-capability-fallbacks.md +46 -0
- package/skills/squad-code-review/SKILL.md +112 -0
- package/skills/squad-code-review/references/code-review-worked-decisions.md +58 -0
- package/skills/squad-code-review/references/cross-stack-review-dimensions.md +55 -0
- package/skills/squad-code-review/references/language-runtime-review-signatures.md +37 -0
- package/skills/squad-code-review/references/official-sources.md +24 -0
- package/skills/squad-code-review/references/quality-bar-and-preflight.md +48 -0
- package/skills/squad-code-review/references/review-methodology-debugging-and-mindset.md +53 -0
- package/skills/squad-code-review/references/review-runtime-and-verdict.md +69 -0
- package/skills/squad-code-review/references/security-architecture-data-and-operations-review.md +47 -0
- package/skills/squad-designer/SKILL.md +134 -0
- package/skills/squad-designer/references/anti-slop-quality-review.md +106 -0
- package/skills/squad-designer/references/codebase-first-examples.md +121 -0
- package/skills/squad-designer/references/design-system-ux-accessibility-and-handoff.md +109 -0
- package/skills/squad-designer/references/official-sources.md +85 -0
- package/skills/squad-designer/references/platform-adaptive-layout-and-input.md +40 -0
- package/skills/squad-designer/references/platform-native-apple-android.md +54 -0
- package/skills/squad-designer/references/platform-native-cross-platform.md +59 -0
- package/skills/squad-designer/references/platform-web-foundations-and-motion.md +104 -0
- package/skills/squad-designer/references/task-specific-ui-ux-research.md +91 -0
- package/skills/squad-devops/SKILL.md +131 -0
- package/skills/squad-devops/references/devops-testing-debugging-and-mindset.md +51 -0
- package/skills/squad-devops/references/devops-worked-decisions.md +70 -0
- package/skills/squad-devops/references/official-sources.md +58 -0
- package/skills/squad-devops/references/platform-iac-and-delivery-matrix.md +71 -0
- package/skills/squad-devops/references/quality-bar-and-preflight.md +52 -0
- package/skills/squad-devops/references/runtime-and-safe-delivery-fallbacks.md +49 -0
- package/skills/squad-devops/references/security-networking-secrets-and-supply-chain.md +54 -0
- package/skills/squad-devops/references/self-hosted-vps-and-reverse-proxy.md +124 -0
- package/skills/squad-devops/references/sre-observability-resilience-and-cost.md +52 -0
- package/skills/squad-fix/SKILL.md +143 -0
- package/skills/squad-fix/references/bug-routing-and-ownership.md +57 -0
- package/skills/squad-fix/references/diagnosis-root-cause-and-fix-loop.md +69 -0
- package/skills/squad-fix/references/official-sources.md +57 -0
- package/skills/squad-fix/references/quality-bar-and-preflight.md +47 -0
- package/skills/squad-fix/references/runtime-capability-fallbacks.md +65 -0
- package/skills/squad-fix/references/verification-qa-review-and-reporting.md +61 -0
- package/skills/squad-fix/references/worked-bugfix-examples.md +65 -0
- package/skills/squad-frontend/SKILL.md +134 -0
- package/skills/squad-frontend/references/designer-gate-and-design-intake.md +71 -0
- package/skills/squad-frontend/references/frontend-architecture-state-data-and-forms.md +68 -0
- package/skills/squad-frontend/references/frontend-security-accessibility-and-performance.md +55 -0
- package/skills/squad-frontend/references/frontend-stack-and-motion-selection.md +99 -0
- package/skills/squad-frontend/references/frontend-testing-debugging-and-mindset.md +51 -0
- package/skills/squad-frontend/references/frontend-worked-decisions.md +71 -0
- package/skills/squad-frontend/references/official-sources.md +52 -0
- package/skills/squad-frontend/references/quality-bar-and-preflight.md +49 -0
- package/skills/squad-frontend/references/runtime-capability-fallbacks.md +49 -0
- package/skills/squad-mobile/SKILL.md +120 -0
- package/skills/squad-mobile/references/design-platform-and-lifecycle-gates.md +50 -0
- package/skills/squad-mobile/references/mobile-debugging-and-mindset.md +40 -0
- package/skills/squad-mobile/references/mobile-security-performance-testing-and-release.md +61 -0
- package/skills/squad-mobile/references/mobile-stack-architecture-and-data.md +65 -0
- package/skills/squad-mobile/references/mobile-worked-decisions.md +53 -0
- package/skills/squad-mobile/references/official-sources.md +45 -0
- package/skills/squad-mobile/references/quality-bar-and-preflight.md +50 -0
- package/skills/squad-mobile/references/runtime-capability-fallbacks.md +42 -0
- package/skills/squad-product/SKILL.md +132 -0
- package/skills/squad-product/references/framing-and-acceptance-criteria.md +60 -0
- package/skills/squad-product/references/official-sources.md +42 -0
- package/skills/squad-product/references/plan-document-contract.md +53 -0
- package/skills/squad-product/references/product-worked-decisions.md +66 -0
- package/skills/squad-product/references/quality-bar-and-preflight.md +54 -0
- package/skills/squad-product/references/requirements-and-unknowns.md +68 -0
- package/skills/squad-product/references/scope-phasing-and-sequencing.md +55 -0
- package/skills/squad-qa/SKILL.md +112 -0
- package/skills/squad-qa/references/official-sources.md +42 -0
- package/skills/squad-qa/references/qa-debugging-and-mindset.md +45 -0
- package/skills/squad-qa/references/qa-worked-decisions.md +63 -0
- package/skills/squad-qa/references/quality-bar-and-preflight.md +48 -0
- package/skills/squad-qa/references/security-accessibility-performance-and-release.md +49 -0
- package/skills/squad-qa/references/test-architecture-data-flakiness-and-ci.md +66 -0
- package/skills/squad-qa/references/test-strategy-runtime-and-verdict.md +81 -0
- package/skills/squad-qa/references/testing-domains-and-tooling-matrix.md +57 -0
- package/skills/squads-team/SKILL.md +121 -0
- package/skills/squads-team/references/coordination-contract.md +115 -0
- package/skills/squads-team/references/coordination-worked-decisions.md +60 -0
- package/skills/squads-team/references/delivery-pipeline-and-roster.md +119 -0
- package/skills/squads-team/references/domain-coverage-contracts.md +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Harry Nguyen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Squad Skills
|
|
2
|
+
|
|
3
|
+
[](https://skills.sh/danh121097/squad-skills)
|
|
4
|
+
|
|
5
|
+
Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and
|
|
6
|
+
[**73 more**](https://www.npmjs.com/package/skills#supported-agents).
|
|
7
|
+
|
|
8
|
+
Role-specialized engineering skills for AI coding agents. The collection covers
|
|
9
|
+
product framing, design, frontend, backend, mobile, DevOps, QA, code review, bug
|
|
10
|
+
fixing, and coordinated squad delivery while preserving clear ownership
|
|
11
|
+
boundaries.
|
|
12
|
+
|
|
13
|
+
The repository follows the open Agent Skills format and keeps each installable
|
|
14
|
+
skill under `skills/<skill-name>/SKILL.md`.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
Every path installs the same set of skills. They differ in how the matching
|
|
19
|
+
subagents arrive.
|
|
20
|
+
|
|
21
|
+
| Path | Skills | Subagents | Tools |
|
|
22
|
+
| ------------------ | ------ | ---------------- | ----------------------------------- |
|
|
23
|
+
| Claude Code plugin | ✅ | ✅ same step | Claude Code |
|
|
24
|
+
| npm CLI | ✅ | ✅ same step | Claude Code, Codex |
|
|
25
|
+
| `npx skills add` | ✅ | one more command | every agent the Skills CLI supports |
|
|
26
|
+
|
|
27
|
+
### As a Claude Code plugin
|
|
28
|
+
|
|
29
|
+
The plugin ships the skills and the subagents together, so one command installs
|
|
30
|
+
both:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
/plugin marketplace add danh121097/squad-skills
|
|
34
|
+
/plugin install squad-skills@squad-skills
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Each role then exists twice over: as a skill you invoke, and as a subagent you
|
|
38
|
+
spawn by name.
|
|
39
|
+
|
|
40
|
+
### From the npm package (recommended)
|
|
41
|
+
|
|
42
|
+
Run without a permanent CLI installation:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
npx squad-skills list
|
|
46
|
+
npx squad-skills add --skill squads-team
|
|
47
|
+
npx squad-skills add --skill squad-frontend --global --agent codex
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or install the command globally first:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
npm install --global squad-skills
|
|
54
|
+
squad-skills add --skill squads-team
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The npm command delegates installation to the official `skills` package and
|
|
58
|
+
defaults to copied files, so installed skills do not depend on an ephemeral
|
|
59
|
+
`npx` package-cache path.
|
|
60
|
+
|
|
61
|
+
### Each role as a subagent too
|
|
62
|
+
|
|
63
|
+
`squad-skills add` also writes a subagent definition for every skill it
|
|
64
|
+
installs, so a role can be spawned by name instead of only loaded as a skill.
|
|
65
|
+
Claude Code reads `.claude/agents/<name>.md`; Codex reads
|
|
66
|
+
`.codex/agents/<name>.toml` and loads it only once `config.toml` names the file,
|
|
67
|
+
which the CLI registers after backing that config up. Each definition points at
|
|
68
|
+
the `SKILL.md` just installed rather than copying it, so the skill stays the one
|
|
69
|
+
source of truth.
|
|
70
|
+
|
|
71
|
+
Use `--no-agents` to install skills alone. A catalog installed through
|
|
72
|
+
`npx skills add` never runs this CLI, so generate the definitions afterwards:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
npx squad-skills agents --global
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`--model` and `--effort` write those fields into each Claude Code subagent file,
|
|
79
|
+
so a spawned role runs on the model you picked and survives the next reinstall:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
npx squad-skills agents --global --model opus --effort medium
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
No skill in this catalog names a model. Which one to run is a property of your
|
|
86
|
+
machine, not of the role, so it lives in the command that writes your files.
|
|
87
|
+
Codex takes no per-agent model; set `[agents] default_subagent_model` in
|
|
88
|
+
`~/.codex/config.toml` instead, and the CLI says so rather than dropping the
|
|
89
|
+
flag silently.
|
|
90
|
+
|
|
91
|
+
See [the installation guide](docs/installation.md) for local-checkout commands,
|
|
92
|
+
installation scope, copy versus symlink behavior, and publishing notes.
|
|
93
|
+
|
|
94
|
+
### From GitHub or skills.sh
|
|
95
|
+
|
|
96
|
+
List the available skills directly from the public repository:
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
npx skills add danh121097/squad-skills --list
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Install the squad orchestrator globally for selected agents:
|
|
103
|
+
|
|
104
|
+
```sh
|
|
105
|
+
npx skills add danh121097/squad-skills \
|
|
106
|
+
--skill squads-team --global \
|
|
107
|
+
--agent codex --agent claude-code --agent cursor --agent opencode
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Install a single role:
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
npx skills add danh121097/squad-skills \
|
|
114
|
+
--skill squad-frontend --global --agent codex
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
This route installs skills only. The upstream Skills CLI has no hook a source
|
|
118
|
+
repository can use, so generate the subagent definitions after it:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
npx squad-skills agents --global
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Develop
|
|
125
|
+
|
|
126
|
+
Requires Node.js 22.20 or newer. The repository pins pnpm through `package.json`.
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
pnpm install
|
|
130
|
+
pnpm test
|
|
131
|
+
pnpm release:check
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`pnpm test` runs TypeScript type checking, formatting verification, Vitest,
|
|
135
|
+
skill-contract validation, knowledge-card validation, and catalog discovery
|
|
136
|
+
through the pinned Skills CLI.
|
|
137
|
+
|
|
138
|
+
Read [AGENTS.md](AGENTS.md) before contributing with a coding agent.
|
|
139
|
+
Use [the publishing guide](docs/publishing.md) when the local repository and npm
|
|
140
|
+
package are ready to be made public.
|
|
141
|
+
|
|
142
|
+
## Contribute
|
|
143
|
+
|
|
144
|
+
[CONTRIBUTING.md](CONTRIBUTING.md) is the contract: the contribution types that
|
|
145
|
+
are accepted, the ones that are rejected and why, the provenance a knowledge
|
|
146
|
+
card must carry, and the evidence a skill-content change must carry before it
|
|
147
|
+
ships.
|
|
148
|
+
|
|
149
|
+
Two things are worth knowing before you start. Knowledge enters through a
|
|
150
|
+
reviewed card citing a dated first-party source, never through crawling or a
|
|
151
|
+
pasted page. And a change to anything an agent reads at runtime ships on the
|
|
152
|
+
full deterministic gate plus maintainer review — that gate shows the catalog is
|
|
153
|
+
consistent, contract-bound and within its payload ceiling, which is not the same
|
|
154
|
+
as showing the output got better, so a claim that it did needs its own evidence.
|
|
155
|
+
[AGENTS.md](AGENTS.md) states the rule.
|
|
156
|
+
|
|
157
|
+
If you used one of these skills on real work and its output got something wrong,
|
|
158
|
+
open the [skill output problem](https://github.com/danh121097/squad-skills/issues/new?template=skill-feedback.yml)
|
|
159
|
+
form. You do not need to know the fix.
|
|
160
|
+
[`docs/feedback-and-weekly-improvement.md`](docs/feedback-and-weekly-improvement.md)
|
|
161
|
+
is what happens to a report after that, and
|
|
162
|
+
[`docs/skill-observations.md`](docs/skill-observations.md) is how one becomes a
|
|
163
|
+
rule.
|
|
164
|
+
|
|
165
|
+
## Skill format
|
|
166
|
+
|
|
167
|
+
Each skill directory must contain a `SKILL.md` whose YAML `name` matches the
|
|
168
|
+
directory name and whose `description` explains when the skill applies. Bundle
|
|
169
|
+
supporting material inside that same skill directory so installations remain
|
|
170
|
+
self-contained.
|
|
171
|
+
|
|
172
|
+
The executable contract is owned by the TypeScript validator and tests. Run
|
|
173
|
+
`pnpm validate` for a focused catalog check or `pnpm test` for the full gate.
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT. See [LICENSE](LICENSE).
|
|
178
|
+
|
|
179
|
+
## Discovery
|
|
180
|
+
|
|
181
|
+
After this repository is public, run the documented GitHub smoke install once.
|
|
182
|
+
Public GitHub skills become eligible for skills.sh discovery through anonymous
|
|
183
|
+
Skills CLI installation telemetry; the npm package remains an additional
|
|
184
|
+
distribution path and does not replace the GitHub source.
|
package/bin/cli.mjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import module from 'node:module';
|
|
4
|
+
|
|
5
|
+
if (module.enableCompileCache && !process.env.NODE_DISABLE_COMPILE_CACHE) {
|
|
6
|
+
try {
|
|
7
|
+
module.enableCompileCache();
|
|
8
|
+
} catch {
|
|
9
|
+
// Compile caching is an optional startup optimization.
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
await import('../dist/cli/cli.mjs');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/cli/cli.mjs
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import path, { dirname, resolve } from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { copyFile, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
8
|
+
const agentsTablePattern = /^\s*\[agents\]\s*$/;
|
|
9
|
+
const tableHeaderPattern = /^\s*\[([^\]]+)\]\s*$/;
|
|
10
|
+
function registerCodexAgents(source, names) {
|
|
11
|
+
const added = [];
|
|
12
|
+
const alreadyRegistered = [];
|
|
13
|
+
const missing = [];
|
|
14
|
+
for (const name of names) if (hasAgentEntry(source, name)) alreadyRegistered.push(name);
|
|
15
|
+
else missing.push(name);
|
|
16
|
+
if (missing.length === 0) return {
|
|
17
|
+
added,
|
|
18
|
+
alreadyRegistered,
|
|
19
|
+
source
|
|
20
|
+
};
|
|
21
|
+
const entries = missing.flatMap((name) => [
|
|
22
|
+
` [agents.${name}]`,
|
|
23
|
+
` config_file = "agents/${name}.toml"`,
|
|
24
|
+
` description = ${JSON.stringify(name)}`
|
|
25
|
+
]);
|
|
26
|
+
added.push(...missing);
|
|
27
|
+
return {
|
|
28
|
+
added,
|
|
29
|
+
alreadyRegistered,
|
|
30
|
+
source: insertIntoAgentsTable(source, entries)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function hasAgentEntry(source, name) {
|
|
34
|
+
return source.split(/\r?\n/).some((line) => tableHeaderPattern.exec(line)?.[1] === `agents.${name}`);
|
|
35
|
+
}
|
|
36
|
+
function insertIntoAgentsTable(source, entries) {
|
|
37
|
+
const lines = source.split("\n");
|
|
38
|
+
const tableStart = lines.findIndex((line) => agentsTablePattern.test(line));
|
|
39
|
+
if (tableStart === -1) return `${source}${source.length > 0 && !source.endsWith("\n") ? "\n" : ""}\n[agents]\n${entries.join("\n")}\n`;
|
|
40
|
+
let insertAt = lines.length;
|
|
41
|
+
for (let index = tableStart + 1; index < lines.length; index += 1) {
|
|
42
|
+
const header = tableHeaderPattern.exec(lines[index])?.[1];
|
|
43
|
+
if (header !== void 0 && !header.startsWith("agents.")) {
|
|
44
|
+
insertAt = index;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
while (insertAt > tableStart + 1 && lines[insertAt - 1].trim() === "") insertAt -= 1;
|
|
49
|
+
return [
|
|
50
|
+
...lines.slice(0, insertAt),
|
|
51
|
+
...entries,
|
|
52
|
+
...lines.slice(insertAt)
|
|
53
|
+
].join("\n");
|
|
54
|
+
}
|
|
55
|
+
const generatedMarker = "Generated by squad-skills";
|
|
56
|
+
const frontmatterPattern = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
57
|
+
function readSkillAgentDefinition(source) {
|
|
58
|
+
const frontmatter = source.match(frontmatterPattern)?.[1];
|
|
59
|
+
if (frontmatter === void 0) return null;
|
|
60
|
+
const name = readScalar(frontmatter, "name");
|
|
61
|
+
const description = readScalar(frontmatter, "description");
|
|
62
|
+
if (name === null || description === null) return null;
|
|
63
|
+
return {
|
|
64
|
+
description,
|
|
65
|
+
name,
|
|
66
|
+
whenToUse: readScalar(frontmatter, "when_to_use") ?? ""
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function renderAgentBody(definition, context) {
|
|
70
|
+
const description = [definition.description, definition.whenToUse].filter((part) => part.length > 0).join(" ");
|
|
71
|
+
return `---
|
|
72
|
+
${[
|
|
73
|
+
`name: ${definition.name}`,
|
|
74
|
+
`description: ${JSON.stringify(description)}`,
|
|
75
|
+
...context.frontmatter ?? []
|
|
76
|
+
].join("\n")}
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
<!-- ${generatedMarker} from ${definition.name}/SKILL.md. ${context.origin} -->
|
|
80
|
+
|
|
81
|
+
${context.instruction}
|
|
82
|
+
|
|
83
|
+
That file, together with the references it routes to, is the authoritative
|
|
84
|
+
definition of this role — its scope, its ownership boundaries, its handoff
|
|
85
|
+
contract, and its completion checklist. Read it before acting, and follow its
|
|
86
|
+
own routing rules to decide which of its references to open. Do not act on this
|
|
87
|
+
summary alone, and do not substitute your own process for the one it states.
|
|
88
|
+
|
|
89
|
+
If that file cannot be read, say so and stop rather than improvising the role.
|
|
90
|
+
`;
|
|
91
|
+
}
|
|
92
|
+
function renderClaudeAgentFile(definition, installedSkillPath, preferences = {}) {
|
|
93
|
+
return renderAgentBody(definition, {
|
|
94
|
+
...installedSkillContext(installedSkillPath),
|
|
95
|
+
frontmatter: renderPreferenceLines(preferences)
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function renderPreferenceLines(preferences) {
|
|
99
|
+
const lines = [];
|
|
100
|
+
if (preferences.model !== void 0 && preferences.model !== null) lines.push(`model: ${preferences.model}`);
|
|
101
|
+
if (preferences.effort !== void 0 && preferences.effort !== null) lines.push(`effort: ${preferences.effort}`);
|
|
102
|
+
return lines;
|
|
103
|
+
}
|
|
104
|
+
function installedSkillContext(installedSkillPath) {
|
|
105
|
+
return {
|
|
106
|
+
instruction: `Read \`${installedSkillPath}\` and follow it for this task.`,
|
|
107
|
+
origin: "Reinstalling regenerates it."
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function renderCodexAgentFile(definition, installedSkillPath) {
|
|
111
|
+
const body = renderAgentBody(definition, installedSkillContext(installedSkillPath));
|
|
112
|
+
if (body.includes("'''")) throw new Error(`${definition.name}: agent body contains ''' and cannot be written as a TOML literal string.`);
|
|
113
|
+
return `# ${generatedMarker}. Reinstalling regenerates this file.
|
|
114
|
+
[agents.${definition.name}]
|
|
115
|
+
developer_instructions = '''
|
|
116
|
+
${body}'''
|
|
117
|
+
`;
|
|
118
|
+
}
|
|
119
|
+
function readScalar(frontmatter, key) {
|
|
120
|
+
const line = frontmatter.split(/\r?\n/).find((candidate) => candidate.startsWith(`${key}:`))?.slice(key.length + 1).trim();
|
|
121
|
+
if (line === void 0 || line.length === 0) return null;
|
|
122
|
+
if (line.startsWith("\"")) return JSON.parse(line);
|
|
123
|
+
if (line.startsWith("'")) return line.slice(1, -1).replaceAll("''", "'");
|
|
124
|
+
return line;
|
|
125
|
+
}
|
|
126
|
+
const supportedTools = /* @__PURE__ */ new Set(["claude-code", "codex"]);
|
|
127
|
+
async function installAgentDefinitions(request) {
|
|
128
|
+
const messages = [];
|
|
129
|
+
const written = [];
|
|
130
|
+
const definitions = await readDefinitions(request, messages);
|
|
131
|
+
if (definitions.length === 0) return {
|
|
132
|
+
messages,
|
|
133
|
+
written
|
|
134
|
+
};
|
|
135
|
+
for (const tool of request.agents) {
|
|
136
|
+
if (!supportedTools.has(tool)) continue;
|
|
137
|
+
const target = resolveTarget(tool, request);
|
|
138
|
+
if (target === null) {
|
|
139
|
+
messages.push(`Skipped ${tool} agents: project scope is not a verified location for them; install with --global.`);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
await mkdir(target.agentsDirectory, { recursive: true });
|
|
143
|
+
const preferences = {
|
|
144
|
+
effort: request.effort,
|
|
145
|
+
model: request.model
|
|
146
|
+
};
|
|
147
|
+
if ((preferences.model != null || preferences.effort != null) && !target.carriesPreferences) messages.push(`Ignored --model and --effort for ${tool}: this CLI writes them into Claude Code agent files only. Codex reads a subagent default from [agents] default_subagent_model and default_subagent_reasoning_effort in config.toml.`);
|
|
148
|
+
const registered = [];
|
|
149
|
+
for (const definition of definitions) {
|
|
150
|
+
const installedSkillFile = await findInstalledSkill(target.skillsRoots, definition.name);
|
|
151
|
+
if (installedSkillFile === null) {
|
|
152
|
+
messages.push(`Skipped ${tool}/${definition.name}: the skill is not installed there.`);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const agentFile = path.join(target.agentsDirectory, `${definition.name}${target.extension}`);
|
|
156
|
+
if (!request.force && await isForeignFile(agentFile)) {
|
|
157
|
+
messages.push(`Kept ${agentFile}: not generated by squad-skills. Use --force to replace.`);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
await writeFile(agentFile, target.render(definition, installedSkillFile, target.carriesPreferences ? preferences : {}), "utf8");
|
|
161
|
+
written.push(agentFile);
|
|
162
|
+
registered.push(definition.name);
|
|
163
|
+
}
|
|
164
|
+
if (target.codexConfigFile !== void 0 && registered.length > 0) messages.push(...await registerInCodexConfig(target.codexConfigFile, registered));
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
messages,
|
|
168
|
+
written
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async function readDefinitions(request, messages) {
|
|
172
|
+
const skillsRoot = path.join(request.packageRoot, "skills");
|
|
173
|
+
const names = request.skills.length > 0 ? request.skills : await readSkillDirectories(skillsRoot, messages);
|
|
174
|
+
const definitions = [];
|
|
175
|
+
for (const name of names) {
|
|
176
|
+
let source;
|
|
177
|
+
try {
|
|
178
|
+
source = await readFile(path.join(skillsRoot, name, "SKILL.md"), "utf8");
|
|
179
|
+
} catch {
|
|
180
|
+
messages.push(`Skipped ${name}: no packaged SKILL.md.`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const definition = readSkillAgentDefinition(source);
|
|
184
|
+
if (definition === null) {
|
|
185
|
+
messages.push(`Skipped ${name}: SKILL.md frontmatter is missing a name or description.`);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
definitions.push(definition);
|
|
189
|
+
}
|
|
190
|
+
return definitions;
|
|
191
|
+
}
|
|
192
|
+
async function readSkillDirectories(skillsRoot, messages) {
|
|
193
|
+
try {
|
|
194
|
+
return (await readdir(skillsRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
195
|
+
} catch {
|
|
196
|
+
messages.push(`Skipped every agent: ${skillsRoot} is unreadable.`);
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function resolveTarget(tool, request) {
|
|
201
|
+
const root = request.scope === "global" ? request.homeDirectory : request.projectRoot;
|
|
202
|
+
if (tool === "claude-code") return {
|
|
203
|
+
agentsDirectory: path.join(root, ".claude", "agents"),
|
|
204
|
+
carriesPreferences: true,
|
|
205
|
+
extension: ".md",
|
|
206
|
+
render: renderClaudeAgentFile,
|
|
207
|
+
skillsRoots: [path.join(root, ".claude", "skills"), path.join(root, ".agents", "skills")],
|
|
208
|
+
tool
|
|
209
|
+
};
|
|
210
|
+
if (request.scope !== "global") return null;
|
|
211
|
+
return {
|
|
212
|
+
agentsDirectory: path.join(root, ".codex", "agents"),
|
|
213
|
+
carriesPreferences: false,
|
|
214
|
+
codexConfigFile: path.join(root, ".codex", "config.toml"),
|
|
215
|
+
extension: ".toml",
|
|
216
|
+
render: renderCodexAgentFile,
|
|
217
|
+
skillsRoots: [path.join(root, ".agents", "skills"), path.join(root, ".codex", "skills")],
|
|
218
|
+
tool
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
async function registerInCodexConfig(configFile, names) {
|
|
222
|
+
let source;
|
|
223
|
+
try {
|
|
224
|
+
source = await readFile(configFile, "utf8");
|
|
225
|
+
} catch {
|
|
226
|
+
source = "";
|
|
227
|
+
}
|
|
228
|
+
const result = registerCodexAgents(source, names);
|
|
229
|
+
if (result.added.length === 0) return [];
|
|
230
|
+
if (source.length > 0) await copyFile(configFile, `${configFile}.squad-skills-backup`);
|
|
231
|
+
else await mkdir(path.dirname(configFile), { recursive: true });
|
|
232
|
+
await writeFile(configFile, result.source, "utf8");
|
|
233
|
+
return [`Registered ${result.added.join(", ")} in ${configFile}.`];
|
|
234
|
+
}
|
|
235
|
+
async function findInstalledSkill(roots, name) {
|
|
236
|
+
for (const root of roots) {
|
|
237
|
+
const candidate = path.join(root, name, "SKILL.md");
|
|
238
|
+
try {
|
|
239
|
+
await readFile(candidate, "utf8");
|
|
240
|
+
return candidate;
|
|
241
|
+
} catch {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
async function isForeignFile(file) {
|
|
248
|
+
try {
|
|
249
|
+
return !(await readFile(file, "utf8")).includes(generatedMarker);
|
|
250
|
+
} catch {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const supportedAgentTools = ["claude-code", "codex"];
|
|
255
|
+
const helpFlags = /* @__PURE__ */ new Set(["--help", "-h"]);
|
|
256
|
+
const versionFlags = /* @__PURE__ */ new Set(["--version", "-v"]);
|
|
257
|
+
const addCommands = /* @__PURE__ */ new Set([
|
|
258
|
+
"add",
|
|
259
|
+
"install",
|
|
260
|
+
"i"
|
|
261
|
+
]);
|
|
262
|
+
const noAgentsFlag = "--no-agents";
|
|
263
|
+
const modelFlags = ["--model"];
|
|
264
|
+
const effortFlags = ["--effort"];
|
|
265
|
+
function createCliAction(arguments_, packageRoot, version) {
|
|
266
|
+
const [command, ...forwardedArguments] = arguments_;
|
|
267
|
+
if (command === void 0 || helpFlags.has(command)) return {
|
|
268
|
+
kind: "print",
|
|
269
|
+
message: createHelpText(),
|
|
270
|
+
exitCode: 0
|
|
271
|
+
};
|
|
272
|
+
if (versionFlags.has(command)) return {
|
|
273
|
+
kind: "print",
|
|
274
|
+
message: version,
|
|
275
|
+
exitCode: 0
|
|
276
|
+
};
|
|
277
|
+
const malformed = findMalformedScalarOption(forwardedArguments);
|
|
278
|
+
if (malformed !== null) return {
|
|
279
|
+
kind: "print",
|
|
280
|
+
message: `${malformed} needs a value.\n\n${createHelpText()}`,
|
|
281
|
+
exitCode: 1
|
|
282
|
+
};
|
|
283
|
+
if (addCommands.has(command)) {
|
|
284
|
+
const forwardable = dropPreferenceArguments(forwardedArguments.filter((argument) => argument !== noAgentsFlag));
|
|
285
|
+
return {
|
|
286
|
+
kind: "delegate",
|
|
287
|
+
agentPlan: forwardedArguments.includes(noAgentsFlag) ? null : createAgentPlan(forwardedArguments),
|
|
288
|
+
arguments: [
|
|
289
|
+
"add",
|
|
290
|
+
packageRoot,
|
|
291
|
+
...ensureCopyInstallation(forwardable)
|
|
292
|
+
]
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
if (command === "agents") return {
|
|
296
|
+
kind: "install-agents",
|
|
297
|
+
agentPlan: createAgentPlan(forwardedArguments)
|
|
298
|
+
};
|
|
299
|
+
if (command === "list" || command === "ls") return {
|
|
300
|
+
kind: "delegate",
|
|
301
|
+
agentPlan: null,
|
|
302
|
+
arguments: [
|
|
303
|
+
"add",
|
|
304
|
+
packageRoot,
|
|
305
|
+
"--list",
|
|
306
|
+
...forwardedArguments
|
|
307
|
+
]
|
|
308
|
+
};
|
|
309
|
+
return {
|
|
310
|
+
kind: "print",
|
|
311
|
+
message: `Unknown command: ${command}\n\n${createHelpText()}`,
|
|
312
|
+
exitCode: 1
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function createAgentPlan(arguments_) {
|
|
316
|
+
return {
|
|
317
|
+
agents: readListOption(arguments_, ["--agent", "-a"], supportedAgentTools),
|
|
318
|
+
effort: readScalarOption(arguments_, effortFlags),
|
|
319
|
+
force: arguments_.includes("--force"),
|
|
320
|
+
model: readScalarOption(arguments_, modelFlags),
|
|
321
|
+
scope: arguments_.includes("--global") || arguments_.includes("-g") ? "global" : "project",
|
|
322
|
+
skills: readListOption(arguments_, ["--skill", "-s"], [])
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
function readScalarOption(arguments_, flags) {
|
|
326
|
+
for (const [index, argument] of arguments_.entries()) {
|
|
327
|
+
const inlineFlag = flags.find((flag) => argument.startsWith(`${flag}=`));
|
|
328
|
+
if (inlineFlag !== void 0) {
|
|
329
|
+
const value = argument.slice(inlineFlag.length + 1).trim();
|
|
330
|
+
if (value.length > 0) return value;
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (!flags.includes(argument)) continue;
|
|
334
|
+
const value = arguments_[index + 1]?.trim() ?? "";
|
|
335
|
+
if (value.length > 0 && !value.startsWith("-")) return value;
|
|
336
|
+
}
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
function findMalformedScalarOption(arguments_) {
|
|
340
|
+
for (const flags of [modelFlags, effortFlags]) if (arguments_.some((argument) => flags.includes(argument) || flags.some((flag) => argument.startsWith(`${flag}=`))) && readScalarOption(arguments_, flags) === null) return flags[0];
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
function dropPreferenceArguments(arguments_) {
|
|
344
|
+
const flags = [...modelFlags, ...effortFlags];
|
|
345
|
+
return arguments_.filter((argument, index) => {
|
|
346
|
+
if (flags.some((flag) => argument === flag || argument.startsWith(`${flag}=`))) return false;
|
|
347
|
+
return !flags.includes(arguments_[index - 1]);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function readListOption(arguments_, flags, wildcard) {
|
|
351
|
+
const values = [];
|
|
352
|
+
for (const [index, argument] of arguments_.entries()) {
|
|
353
|
+
const inlineFlag = flags.find((flag) => argument.startsWith(`${flag}=`));
|
|
354
|
+
if (inlineFlag !== void 0) values.push(argument.slice(inlineFlag.length + 1));
|
|
355
|
+
else if (flags.includes(argument)) values.push(arguments_[index + 1] ?? "");
|
|
356
|
+
}
|
|
357
|
+
const parsed = values.flatMap((value) => value.split(",")).map((value) => value.trim()).filter((value) => value.length > 0);
|
|
358
|
+
return parsed.includes("*") || parsed.length === 0 ? wildcard : parsed;
|
|
359
|
+
}
|
|
360
|
+
function ensureCopyInstallation(arguments_) {
|
|
361
|
+
if (arguments_.includes("--copy")) return arguments_;
|
|
362
|
+
return [...arguments_, "--copy"];
|
|
363
|
+
}
|
|
364
|
+
function createHelpText() {
|
|
365
|
+
return `Squad Skills
|
|
366
|
+
|
|
367
|
+
Install role-specialized engineering skills through the Skills CLI.
|
|
368
|
+
|
|
369
|
+
Usage:
|
|
370
|
+
squad-skills add [skills options]
|
|
371
|
+
squad-skills list [skills options]
|
|
372
|
+
squad-skills agents [options]
|
|
373
|
+
|
|
374
|
+
Commands:
|
|
375
|
+
add, install, i Install skills from this package, then generate an agent
|
|
376
|
+
definition for each installed skill
|
|
377
|
+
list, ls List the packaged skills
|
|
378
|
+
agents Generate agent definitions for skills already installed,
|
|
379
|
+
for a catalog installed through \`npx skills add\`
|
|
380
|
+
|
|
381
|
+
Agent options:
|
|
382
|
+
--no-agents Install skills only (add)
|
|
383
|
+
--force Replace an agent file this CLI did not generate
|
|
384
|
+
--global, -g Read and write the user-level location
|
|
385
|
+
--agent, -a Limit to ${supportedAgentTools.join(", ")}
|
|
386
|
+
--skill, -s Limit to named skills
|
|
387
|
+
--model Write this model into each Claude Code agent file
|
|
388
|
+
--effort Write this reasoning effort into each Claude Code agent file
|
|
389
|
+
|
|
390
|
+
Examples:
|
|
391
|
+
squad-skills list
|
|
392
|
+
squad-skills add --skill squads-team
|
|
393
|
+
squad-skills add --skill squad-frontend --global --agent codex
|
|
394
|
+
squad-skills agents --global
|
|
395
|
+
squad-skills agents --global --agent claude-code --model opus --effort medium
|
|
396
|
+
|
|
397
|
+
Claude Code reads an agent from .claude/agents/<name>.md. Codex reads one from
|
|
398
|
+
.codex/agents/<name>.toml and loads it only once config.toml names it, so this
|
|
399
|
+
CLI registers it there and backs the file up first. Codex agents are written at
|
|
400
|
+
global scope only, and carry no model field: Codex takes a subagent default from
|
|
401
|
+
[agents] default_subagent_model in config.toml instead.
|
|
402
|
+
|
|
403
|
+
--model and --effort are machine preferences, not catalog content. They are
|
|
404
|
+
written only where the caller asks for them, so a reinstall keeps them.
|
|
405
|
+
|
|
406
|
+
All other options after add or list are forwarded to the Skills CLI.`;
|
|
407
|
+
}
|
|
408
|
+
const require = createRequire(import.meta.url);
|
|
409
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
410
|
+
const packageMetadata = require(resolve(packageRoot, "package.json"));
|
|
411
|
+
const skillsPackagePath = require.resolve("skills/package.json");
|
|
412
|
+
const skillsCliPath = resolve(dirname(skillsPackagePath), "bin/cli.mjs");
|
|
413
|
+
const action = createCliAction(process.argv.slice(2), packageRoot, packageMetadata.version);
|
|
414
|
+
if (action.kind === "print") {
|
|
415
|
+
console.log(action.message);
|
|
416
|
+
process.exitCode = action.exitCode;
|
|
417
|
+
} else if (action.kind === "install-agents") await writeAgentDefinitions(action.agentPlan);
|
|
418
|
+
else {
|
|
419
|
+
const exitCode = await runSkillsCli(skillsCliPath, action.arguments);
|
|
420
|
+
process.exitCode = exitCode;
|
|
421
|
+
if (exitCode === 0 && action.agentPlan !== null) await writeAgentDefinitions(action.agentPlan);
|
|
422
|
+
}
|
|
423
|
+
async function writeAgentDefinitions(agentPlan) {
|
|
424
|
+
const result = await installAgentDefinitions({
|
|
425
|
+
...agentPlan,
|
|
426
|
+
homeDirectory: homedir(),
|
|
427
|
+
packageRoot,
|
|
428
|
+
projectRoot: process.cwd()
|
|
429
|
+
});
|
|
430
|
+
for (const message of result.messages) console.log(message);
|
|
431
|
+
console.log(result.written.length === 0 ? "No agent definitions written." : `Wrote ${result.written.length} agent definition(s).`);
|
|
432
|
+
}
|
|
433
|
+
function runSkillsCli(cliPath, arguments_) {
|
|
434
|
+
return new Promise((resolveExitCode) => {
|
|
435
|
+
const child = spawn(process.execPath, [cliPath, ...arguments_], { stdio: "inherit" });
|
|
436
|
+
child.once("error", (error) => {
|
|
437
|
+
console.error(`Unable to start the Skills CLI: ${error.message}`);
|
|
438
|
+
resolveExitCode(1);
|
|
439
|
+
});
|
|
440
|
+
child.once("exit", (code, signal) => {
|
|
441
|
+
if (signal !== null) {
|
|
442
|
+
console.error(`Skills CLI stopped by signal ${signal}.`);
|
|
443
|
+
resolveExitCode(1);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
resolveExitCode(code ?? 1);
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
export {};
|