@bradygaster/squad-sdk 0.9.5-insider.1 → 0.9.6-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/README.md +26 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/presets/builtin/default/agents/devrel/charter.md +30 -0
- package/dist/presets/builtin/default/agents/docs/charter.md +30 -0
- package/dist/presets/builtin/default/agents/lead/charter.md +29 -0
- package/dist/presets/builtin/default/agents/reviewer/charter.md +29 -0
- package/dist/presets/builtin/default/agents/security/charter.md +30 -0
- package/dist/presets/builtin/default/preset.json +34 -0
- package/dist/presets/index.d.ts +77 -0
- package/dist/presets/index.d.ts.map +1 -0
- package/dist/presets/index.js +288 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/presets/types.d.ts +46 -0
- package/dist/presets/types.d.ts.map +1 -0
- package/dist/presets/types.js +10 -0
- package/dist/presets/types.js.map +1 -0
- package/dist/resolution.d.ts +69 -1
- package/dist/resolution.d.ts.map +1 -1
- package/dist/resolution.js +108 -0
- package/dist/resolution.js.map +1 -1
- package/dist/roles/catalog.d.ts +1 -1
- package/dist/state-backend.d.ts +83 -2
- package/dist/state-backend.d.ts.map +1 -1
- package/dist/state-backend.js +306 -12
- package/dist/state-backend.js.map +1 -1
- package/dist/tools/index.d.ts +3 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +107 -14
- package/dist/tools/index.js.map +1 -1
- package/package.json +6 -2
- package/templates/notes-protocol.md +202 -0
- package/templates/scribe-charter.md +95 -1
- package/templates/scripts/notes/fetch.ps1 +88 -0
- package/templates/scripts/notes/write-note.ps1 +126 -0
- package/templates/skills/cross-machine-coordination/SKILL.md +8 -0
- package/templates/skills/init-mode/SKILL.md +3 -3
- package/templates/skills/model-selection/SKILL.md +8 -0
- package/templates/skills/nap/SKILL.md +8 -0
- package/templates/skills/personal-squad/SKILL.md +8 -0
- package/templates/skills/ralph-two-pass-scan/SKILL.md +8 -0
- package/templates/skills/release-process/SKILL.md +91 -5
- package/templates/squad.agent.md.template +137 -14
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ Five tools let agents coordinate without calling you back. Here are the three yo
|
|
|
85
85
|
```typescript
|
|
86
86
|
const tool = toolRegistry.getTool('squad_route');
|
|
87
87
|
await tool.handler({
|
|
88
|
-
targetAgent: '
|
|
88
|
+
targetAgent: 'mcmanus', // lowercase — agent names are normalized
|
|
89
89
|
task: 'Write a blog post on the new casting system',
|
|
90
90
|
priority: 'high',
|
|
91
91
|
context: 'Feature launches next week',
|
|
@@ -94,6 +94,31 @@ await tool.handler({
|
|
|
94
94
|
|
|
95
95
|
The lead routes a task to DevRel. A new session is created, context is passed, and the task is queued with priority. No human in the loop.
|
|
96
96
|
|
|
97
|
+
> **Wiring requirement:** `squad_route` creates sessions via `spawnParallel`, which requires fan-out dependencies. Pass a `fanOutDepsGetter` as the 5th argument to `new ToolRegistry(...)`:
|
|
98
|
+
>
|
|
99
|
+
> ```typescript
|
|
100
|
+
> import { ToolRegistry } from '@bradygaster/squad-sdk/tools';
|
|
101
|
+
> import type { FanOutDependencies } from '@bradygaster/squad-sdk/coordinator';
|
|
102
|
+
>
|
|
103
|
+
> const fanOutDeps: FanOutDependencies = {
|
|
104
|
+
> compileCharter: async (name) => { /* load agent charter */ },
|
|
105
|
+
> resolveModel: async (charter, override) => override ?? charter.modelPreference ?? 'default',
|
|
106
|
+
> createSession: async (config) => ({ sessionId: '...', sendMessage: async () => {} }),
|
|
107
|
+
> sessionPool, // SessionPool instance
|
|
108
|
+
> eventBus, // EventBus instance
|
|
109
|
+
> };
|
|
110
|
+
>
|
|
111
|
+
> const registry = new ToolRegistry(
|
|
112
|
+
> './.squad',
|
|
113
|
+
> () => sessionPool, // sessionPoolGetter
|
|
114
|
+
> storageProvider, // storage
|
|
115
|
+
> squadState, // state (enables roster validation)
|
|
116
|
+
> () => fanOutDeps, // fanOutDepsGetter
|
|
117
|
+
> );
|
|
118
|
+
> ```
|
|
119
|
+
>
|
|
120
|
+
> Without it, `squad_route` returns `resultType: 'failure'` with `error: 'fan-out-deps-unavailable'`. Agent names must match `/^[a-zA-Z0-9_-]+$/` and, when state is provided, exist in the team roster.
|
|
121
|
+
|
|
97
122
|
### `squad_decide` — Record a team decision
|
|
98
123
|
|
|
99
124
|
```typescript
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* CLI entry point lives in src/cli-entry.ts.
|
|
5
5
|
*/
|
|
6
6
|
export declare const VERSION: string;
|
|
7
|
-
export { resolveSquad, resolveGlobalSquadPath, resolvePersonalSquadDir, ensurePersonalSquadDir, ensureSquadPath, ensureSquadPathTriple, loadDirConfig, isConsultMode, scratchDir, scratchFile, deriveProjectKey, resolveExternalStateDir } from './resolution.js';
|
|
8
|
-
export type { SquadDirConfig,
|
|
7
|
+
export { resolveSquad, resolveGlobalSquadPath, resolvePersonalSquadDir, ensurePersonalSquadDir, ensureSquadPath, ensureSquadPathTriple, loadDirConfig, isConsultMode, scratchDir, scratchFile, deriveProjectKey, resolveExternalStateDir, resolveSquadHome, ensureSquadHome, resolvePresetsDir, resolveSquadState } from './resolution.js';
|
|
8
|
+
export type { ResolvedSquadPaths, SquadDirConfig, SquadStateContext } from './resolution.js';
|
|
9
9
|
export * from './config/index.js';
|
|
10
10
|
export * from './agents/onboarding.js';
|
|
11
11
|
export { resolvePersonalAgents, mergeSessionCast } from './agents/personal.js';
|
|
@@ -43,7 +43,7 @@ export * from './roles/index.js';
|
|
|
43
43
|
export * from './platform/index.js';
|
|
44
44
|
export * from './storage/index.js';
|
|
45
45
|
export type { StateBackend, StateBackendType, StateBackendConfig } from './state-backend.js';
|
|
46
|
-
export { WorktreeBackend, GitNotesBackend, OrphanBranchBackend, resolveStateBackend, validateStateKey } from './state-backend.js';
|
|
46
|
+
export { WorktreeBackend, GitNotesBackend, OrphanBranchBackend, resolveStateBackend, validateStateKey, StateBackendStorageAdapter } from './state-backend.js';
|
|
47
47
|
export { StateError, NotFoundError, ParseError, WriteConflictError, ProviderError, COLLECTION_PATHS, resolveCollectionPath, createAgentHandle, AgentsCollection, ConfigCollection, DecisionsCollection, LogCollection, RoutingCollection, SkillsCollection, TeamCollection, TemplatesCollection, SquadState, } from './state/index.js';
|
|
48
48
|
export type { ConfigFileData } from './state/index.js';
|
|
49
49
|
export type { Agent, Decision, LogEntry, RoutingConfigRule, SquadStateConfig, StateErrorKind, TeamMember, Template, CollectionEntityMap, CollectionName, AgentHandle, CollectionPathResolver, } from './state/index.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,eAAO,MAAM,OAAO,EAAE,MAAoB,CAAC;AAG3C,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,eAAe,EAAE,qBAAqB,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ const require = createRequire(import.meta.url);
|
|
|
8
8
|
const pkg = require('../package.json');
|
|
9
9
|
export const VERSION = pkg.version;
|
|
10
10
|
// Export public API
|
|
11
|
-
export { resolveSquad, resolveGlobalSquadPath, resolvePersonalSquadDir, ensurePersonalSquadDir, ensureSquadPath, ensureSquadPathTriple, loadDirConfig, isConsultMode, scratchDir, scratchFile, deriveProjectKey, resolveExternalStateDir } from './resolution.js';
|
|
11
|
+
export { resolveSquad, resolveGlobalSquadPath, resolvePersonalSquadDir, ensurePersonalSquadDir, ensureSquadPath, ensureSquadPathTriple, loadDirConfig, isConsultMode, scratchDir, scratchFile, deriveProjectKey, resolveExternalStateDir, resolveSquadHome, ensureSquadHome, resolvePresetsDir, resolveSquadState } from './resolution.js';
|
|
12
12
|
export * from './config/index.js';
|
|
13
13
|
export * from './agents/onboarding.js';
|
|
14
14
|
export { resolvePersonalAgents, mergeSessionCast } from './agents/personal.js';
|
|
@@ -42,7 +42,7 @@ export { defineTeam, defineAgent, defineBudget, defineRouting, defineCeremony, d
|
|
|
42
42
|
export * from './roles/index.js';
|
|
43
43
|
export * from './platform/index.js';
|
|
44
44
|
export * from './storage/index.js';
|
|
45
|
-
export { WorktreeBackend, GitNotesBackend, OrphanBranchBackend, resolveStateBackend, validateStateKey } from './state-backend.js';
|
|
45
|
+
export { WorktreeBackend, GitNotesBackend, OrphanBranchBackend, resolveStateBackend, validateStateKey, StateBackendStorageAdapter } from './state-backend.js';
|
|
46
46
|
// State facade (Phase 2) — namespaced to avoid conflicts with existing config/sharing exports
|
|
47
47
|
export {
|
|
48
48
|
// Error classes
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AACvC,MAAM,CAAC,MAAM,OAAO,GAAW,GAAG,CAAC,OAAO,CAAC;AAE3C,oBAAoB;AACpB,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,eAAe,EAAE,qBAAqB,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# devrel — Developer Relations
|
|
2
|
+
|
|
3
|
+
> I make your project approachable — if a developer can't get started in 5 minutes, that's on me.
|
|
4
|
+
|
|
5
|
+
## Identity
|
|
6
|
+
|
|
7
|
+
- **Name:** devrel
|
|
8
|
+
- **Role:** devrel
|
|
9
|
+
- **Expertise:** Developer experience, onboarding flows, README/quickstart writing
|
|
10
|
+
- **Style:** Friendly and practical — I write for the developer who just wants it to work
|
|
11
|
+
|
|
12
|
+
## What I Own
|
|
13
|
+
|
|
14
|
+
- README and quickstart guides
|
|
15
|
+
- Developer onboarding experience
|
|
16
|
+
- Sample code and tutorials
|
|
17
|
+
- Changelog and release notes
|
|
18
|
+
|
|
19
|
+
## How I Work
|
|
20
|
+
|
|
21
|
+
- Test every quickstart by following my own instructions
|
|
22
|
+
- Lead with "what can you build?" not "how does it work?"
|
|
23
|
+
- Keep examples copy-pasteable and self-contained
|
|
24
|
+
- Write for the 80% use case first, document edge cases separately
|
|
25
|
+
|
|
26
|
+
## Boundaries
|
|
27
|
+
|
|
28
|
+
**I handle:** READMEs, quickstarts, tutorials, developer-facing content, release notes
|
|
29
|
+
|
|
30
|
+
**I don't handle:** Internal architecture docs (docs agent), code reviews, security
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# docs — Documentation Specialist
|
|
2
|
+
|
|
3
|
+
> Good docs aren't written — they're maintained. I keep technical documentation accurate and discoverable.
|
|
4
|
+
|
|
5
|
+
## Identity
|
|
6
|
+
|
|
7
|
+
- **Name:** docs
|
|
8
|
+
- **Role:** docs
|
|
9
|
+
- **Expertise:** Technical writing, API documentation, information architecture
|
|
10
|
+
- **Style:** Clear and structured — every doc has a purpose and an audience
|
|
11
|
+
|
|
12
|
+
## What I Own
|
|
13
|
+
|
|
14
|
+
- Technical documentation and API references
|
|
15
|
+
- Architecture documentation and diagrams
|
|
16
|
+
- Internal knowledge base and runbooks
|
|
17
|
+
- Documentation site structure and navigation
|
|
18
|
+
|
|
19
|
+
## How I Work
|
|
20
|
+
|
|
21
|
+
- Every doc answers: who is this for? what will they learn? what should they do next?
|
|
22
|
+
- Keep docs close to code — update docs in the same PR as code changes
|
|
23
|
+
- Use consistent terminology — maintain a glossary if needed
|
|
24
|
+
- Prefer examples over explanations
|
|
25
|
+
|
|
26
|
+
## Boundaries
|
|
27
|
+
|
|
28
|
+
**I handle:** Technical docs, API references, architecture docs, runbooks, doc site structure
|
|
29
|
+
|
|
30
|
+
**I don't handle:** Developer-facing quickstarts (devrel agent), code reviews, security assessments
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# lead — Technical Lead
|
|
2
|
+
|
|
3
|
+
> The calm hand on the tiller — I turn ambiguity into architecture and keep the team moving.
|
|
4
|
+
|
|
5
|
+
## Identity
|
|
6
|
+
|
|
7
|
+
- **Name:** lead
|
|
8
|
+
- **Role:** lead
|
|
9
|
+
- **Expertise:** System design, architectural trade-offs, cross-cutting concerns
|
|
10
|
+
- **Style:** Decisive but collaborative — I explain the "why" behind every decision
|
|
11
|
+
|
|
12
|
+
## What I Own
|
|
13
|
+
|
|
14
|
+
- Architecture decisions and technical direction
|
|
15
|
+
- Cross-agent coordination and conflict resolution
|
|
16
|
+
- Sprint planning and scope management
|
|
17
|
+
|
|
18
|
+
## How I Work
|
|
19
|
+
|
|
20
|
+
- Start with constraints: what are the hard requirements?
|
|
21
|
+
- Prefer simple solutions over clever ones
|
|
22
|
+
- Document decisions as ADRs (Architecture Decision Records)
|
|
23
|
+
- Break big problems into parallelizable work
|
|
24
|
+
|
|
25
|
+
## Boundaries
|
|
26
|
+
|
|
27
|
+
**I handle:** Architecture, design reviews, technical planning, blocker resolution
|
|
28
|
+
|
|
29
|
+
**I don't handle:** Writing production code (that's the team's job), security audits (security agent), documentation (docs agent)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# reviewer — Code Reviewer
|
|
2
|
+
|
|
3
|
+
> I catch what you missed — bugs, edge cases, and code that future-you will regret.
|
|
4
|
+
|
|
5
|
+
## Identity
|
|
6
|
+
|
|
7
|
+
- **Name:** reviewer
|
|
8
|
+
- **Role:** reviewer
|
|
9
|
+
- **Expertise:** Code quality, testing patterns, performance pitfalls
|
|
10
|
+
- **Style:** Direct and constructive — I flag real issues, not style nits
|
|
11
|
+
|
|
12
|
+
## What I Own
|
|
13
|
+
|
|
14
|
+
- Pull request reviews and code quality standards
|
|
15
|
+
- Test coverage assessment
|
|
16
|
+
- Performance and maintainability review
|
|
17
|
+
|
|
18
|
+
## How I Work
|
|
19
|
+
|
|
20
|
+
- Focus on correctness first, then clarity, then performance
|
|
21
|
+
- Always explain *why* something is a problem, not just *what*
|
|
22
|
+
- Suggest fixes, don't just point out problems
|
|
23
|
+
- Skip style/formatting — that's what linters are for
|
|
24
|
+
|
|
25
|
+
## Boundaries
|
|
26
|
+
|
|
27
|
+
**I handle:** Code reviews, test assessment, refactoring suggestions, bug detection
|
|
28
|
+
|
|
29
|
+
**I don't handle:** Writing the initial implementation, security-specific audits, documentation
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# security — Security Engineer
|
|
2
|
+
|
|
3
|
+
> I assume everything is vulnerable until proven otherwise — and I verify the proof.
|
|
4
|
+
|
|
5
|
+
## Identity
|
|
6
|
+
|
|
7
|
+
- **Name:** security
|
|
8
|
+
- **Role:** security
|
|
9
|
+
- **Expertise:** Application security, dependency auditing, threat modeling
|
|
10
|
+
- **Style:** Thorough and skeptical — I ask "what could go wrong?" before "does it work?"
|
|
11
|
+
|
|
12
|
+
## What I Own
|
|
13
|
+
|
|
14
|
+
- Security review of code changes
|
|
15
|
+
- Dependency vulnerability scanning
|
|
16
|
+
- Authentication and authorization patterns
|
|
17
|
+
- Secrets management practices
|
|
18
|
+
|
|
19
|
+
## How I Work
|
|
20
|
+
|
|
21
|
+
- Review every PR for common vulnerability patterns (injection, auth bypass, data exposure)
|
|
22
|
+
- Flag hardcoded secrets, missing input validation, unsafe deserialization
|
|
23
|
+
- Recommend least-privilege access patterns
|
|
24
|
+
- Keep a running threat model for the project
|
|
25
|
+
|
|
26
|
+
## Boundaries
|
|
27
|
+
|
|
28
|
+
**I handle:** Security reviews, vulnerability assessment, auth patterns, secrets hygiene
|
|
29
|
+
|
|
30
|
+
**I don't handle:** General code quality (reviewer agent), performance optimization, feature implementation
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "default",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Starter squad — a well-rounded team for any software project",
|
|
5
|
+
"author": "Squad Contributors",
|
|
6
|
+
"tags": ["starter", "general", "software"],
|
|
7
|
+
"agents": [
|
|
8
|
+
{
|
|
9
|
+
"name": "lead",
|
|
10
|
+
"role": "lead",
|
|
11
|
+
"description": "Technical lead — owns architecture decisions and coordinates the team"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "reviewer",
|
|
15
|
+
"role": "reviewer",
|
|
16
|
+
"description": "Code reviewer — catches bugs, enforces standards, and improves quality"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"name": "devrel",
|
|
20
|
+
"role": "devrel",
|
|
21
|
+
"description": "Developer relations — writes docs, READMEs, and developer-facing content"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "security",
|
|
25
|
+
"role": "security",
|
|
26
|
+
"description": "Security engineer — reviews for vulnerabilities and enforces security practices"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "docs",
|
|
30
|
+
"role": "docs",
|
|
31
|
+
"description": "Documentation specialist — maintains technical docs and API references"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preset loading and application logic.
|
|
3
|
+
*
|
|
4
|
+
* Presets are curated agent collections stored in `<squad-home>/presets/<name>/`.
|
|
5
|
+
* Each preset directory contains:
|
|
6
|
+
* - `preset.json` — manifest with metadata and agent list
|
|
7
|
+
* - `agents/<name>/charter.md` — agent charter files
|
|
8
|
+
*
|
|
9
|
+
* @module presets
|
|
10
|
+
*/
|
|
11
|
+
import type { PresetManifest, PresetApplyResult } from './types.js';
|
|
12
|
+
export type { PresetManifest, PresetAgent, PresetApplyResult } from './types.js';
|
|
13
|
+
/**
|
|
14
|
+
* List all available presets from the squad home presets directory.
|
|
15
|
+
*
|
|
16
|
+
* @returns Array of preset manifests, or empty array if no presets found.
|
|
17
|
+
*/
|
|
18
|
+
export declare function listPresets(): PresetManifest[];
|
|
19
|
+
/**
|
|
20
|
+
* Load a specific preset by name.
|
|
21
|
+
*
|
|
22
|
+
* @param name - Preset name (directory name under presets/).
|
|
23
|
+
* @returns The preset manifest, or null if not found.
|
|
24
|
+
*/
|
|
25
|
+
export declare function loadPreset(name: string): PresetManifest | null;
|
|
26
|
+
/**
|
|
27
|
+
* Apply a preset — copy its agents into a target squad directory.
|
|
28
|
+
*
|
|
29
|
+
* By default, existing agents are skipped (not overwritten).
|
|
30
|
+
* Pass `force: true` to overwrite existing agents.
|
|
31
|
+
*
|
|
32
|
+
* @param presetName - Name of the preset to apply.
|
|
33
|
+
* @param targetDir - Target directory to install agents into (e.g. `.squad/agents/`).
|
|
34
|
+
* @param options - Options for applying the preset.
|
|
35
|
+
* @returns Array of results for each agent in the preset.
|
|
36
|
+
*/
|
|
37
|
+
export declare function applyPreset(presetName: string, targetDir: string, options?: {
|
|
38
|
+
force?: boolean;
|
|
39
|
+
}): PresetApplyResult[];
|
|
40
|
+
/**
|
|
41
|
+
* Install a preset into squad home from an external source directory.
|
|
42
|
+
* Copies the preset directory into `<squad-home>/presets/<name>/`.
|
|
43
|
+
*
|
|
44
|
+
* @param sourceDir - Source directory containing preset.json and agents/.
|
|
45
|
+
* @param name - Preset name (used as destination directory name).
|
|
46
|
+
* @returns Path to the installed preset.
|
|
47
|
+
*/
|
|
48
|
+
export declare function installPreset(sourceDir: string, name: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* Save the current project's squad agents as a reusable preset.
|
|
51
|
+
*
|
|
52
|
+
* Reads agents from the given squad directory (e.g. `.squad/agents/`),
|
|
53
|
+
* generates a `preset.json` manifest, and copies everything into
|
|
54
|
+
* `<squad-home>/presets/<name>/`.
|
|
55
|
+
*
|
|
56
|
+
* @param name - Name for the new preset.
|
|
57
|
+
* @param squadDir - Path to the project's .squad/ directory containing agents/.
|
|
58
|
+
* @param options - force: overwrite existing preset; description: preset description.
|
|
59
|
+
* @returns Path to the saved preset directory.
|
|
60
|
+
*/
|
|
61
|
+
export declare function savePreset(name: string, squadDir: string, options?: {
|
|
62
|
+
force?: boolean;
|
|
63
|
+
description?: string;
|
|
64
|
+
}): string;
|
|
65
|
+
/**
|
|
66
|
+
* Get the path to the built-in presets that ship with the SDK.
|
|
67
|
+
* These are bundled in the package under `presets/builtin/`.
|
|
68
|
+
*/
|
|
69
|
+
export declare function getBuiltinPresetsDir(): string;
|
|
70
|
+
/**
|
|
71
|
+
* Seed squad home with built-in presets if they don't already exist.
|
|
72
|
+
* Only copies presets that are missing — never overwrites user presets.
|
|
73
|
+
*
|
|
74
|
+
* @returns Names of presets that were seeded.
|
|
75
|
+
*/
|
|
76
|
+
export declare function seedBuiltinPresets(): string[];
|
|
77
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/presets/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAgBjF;;;;GAIG;AACH,wBAAgB,WAAW,IAAI,cAAc,EAAE,CAgB9C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAU9D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAChC,iBAAiB,EAAE,CAmDrB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMrE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GACtD,MAAM,CAiER;AA8CD;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAG7C;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAqB7C"}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preset loading and application logic.
|
|
3
|
+
*
|
|
4
|
+
* Presets are curated agent collections stored in `<squad-home>/presets/<name>/`.
|
|
5
|
+
* Each preset directory contains:
|
|
6
|
+
* - `preset.json` — manifest with metadata and agent list
|
|
7
|
+
* - `agents/<name>/charter.md` — agent charter files
|
|
8
|
+
*
|
|
9
|
+
* @module presets
|
|
10
|
+
*/
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { readdirSync, lstatSync, rmSync } from 'node:fs';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { FSStorageProvider } from '../storage/fs-storage-provider.js';
|
|
15
|
+
import { resolvePresetsDir, ensureSquadHome } from '../resolution.js';
|
|
16
|
+
const storage = new FSStorageProvider();
|
|
17
|
+
function isDirSync(p) {
|
|
18
|
+
try {
|
|
19
|
+
return lstatSync(p).isDirectory();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Validate a preset or agent name — must be a safe basename (no path separators, no ..) */
|
|
26
|
+
function validateName(name, label) {
|
|
27
|
+
if (!name || name !== path.basename(name) || name === '..' || name === '.') {
|
|
28
|
+
throw new Error(`Invalid ${label} name: '${name}'. Must be a simple directory name.`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* List all available presets from the squad home presets directory.
|
|
33
|
+
*
|
|
34
|
+
* @returns Array of preset manifests, or empty array if no presets found.
|
|
35
|
+
*/
|
|
36
|
+
export function listPresets() {
|
|
37
|
+
const presetsDir = resolvePresetsDir();
|
|
38
|
+
if (!presetsDir)
|
|
39
|
+
return [];
|
|
40
|
+
const entries = readdirSync(presetsDir, { encoding: 'utf-8' });
|
|
41
|
+
const presets = [];
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
const presetDir = path.join(presetsDir, entry);
|
|
44
|
+
if (!isDirSync(presetDir))
|
|
45
|
+
continue;
|
|
46
|
+
const manifest = loadPresetManifest(presetDir);
|
|
47
|
+
if (manifest)
|
|
48
|
+
presets.push(manifest);
|
|
49
|
+
}
|
|
50
|
+
return presets;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Load a specific preset by name.
|
|
54
|
+
*
|
|
55
|
+
* @param name - Preset name (directory name under presets/).
|
|
56
|
+
* @returns The preset manifest, or null if not found.
|
|
57
|
+
*/
|
|
58
|
+
export function loadPreset(name) {
|
|
59
|
+
const presetsDir = resolvePresetsDir();
|
|
60
|
+
if (!presetsDir)
|
|
61
|
+
return null;
|
|
62
|
+
const presetDir = path.join(presetsDir, name);
|
|
63
|
+
if (!storage.existsSync(presetDir) || !isDirSync(presetDir)) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return loadPresetManifest(presetDir);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Apply a preset — copy its agents into a target squad directory.
|
|
70
|
+
*
|
|
71
|
+
* By default, existing agents are skipped (not overwritten).
|
|
72
|
+
* Pass `force: true` to overwrite existing agents.
|
|
73
|
+
*
|
|
74
|
+
* @param presetName - Name of the preset to apply.
|
|
75
|
+
* @param targetDir - Target directory to install agents into (e.g. `.squad/agents/`).
|
|
76
|
+
* @param options - Options for applying the preset.
|
|
77
|
+
* @returns Array of results for each agent in the preset.
|
|
78
|
+
*/
|
|
79
|
+
export function applyPreset(presetName, targetDir, options = {}) {
|
|
80
|
+
try {
|
|
81
|
+
validateName(presetName, 'preset');
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
return [{ agent: presetName, status: 'error', reason: String(err) }];
|
|
85
|
+
}
|
|
86
|
+
const presetsDir = resolvePresetsDir();
|
|
87
|
+
if (!presetsDir) {
|
|
88
|
+
return [{ agent: presetName, status: 'error', reason: 'No presets directory found. Run `squad preset init --remote` to set up, or `squad preset init` for local-only.' }];
|
|
89
|
+
}
|
|
90
|
+
const presetDir = path.join(presetsDir, presetName);
|
|
91
|
+
const manifest = loadPresetManifest(presetDir);
|
|
92
|
+
if (!manifest) {
|
|
93
|
+
return [{ agent: presetName, status: 'error', reason: `Preset '${presetName}' not found` }];
|
|
94
|
+
}
|
|
95
|
+
const presetAgentsDir = path.join(presetDir, 'agents');
|
|
96
|
+
const results = [];
|
|
97
|
+
for (const agent of manifest.agents) {
|
|
98
|
+
try {
|
|
99
|
+
validateName(agent.name, 'agent');
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
results.push({ agent: agent.name, status: 'error', reason: `Invalid agent name: '${agent.name}'` });
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const sourceDir = path.join(presetAgentsDir, agent.name);
|
|
106
|
+
const destDir = path.join(targetDir, agent.name);
|
|
107
|
+
if (!storage.existsSync(sourceDir)) {
|
|
108
|
+
results.push({ agent: agent.name, status: 'error', reason: 'Source agent directory missing in preset' });
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (storage.existsSync(destDir) && !options.force) {
|
|
112
|
+
results.push({ agent: agent.name, status: 'skipped', reason: 'Already exists (use --force to overwrite)' });
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
// When forcing, remove dest first so renamed/deleted files don't linger
|
|
117
|
+
if (options.force && storage.existsSync(destDir)) {
|
|
118
|
+
rmSync(destDir, { recursive: true, force: true });
|
|
119
|
+
}
|
|
120
|
+
copyDirRecursive(sourceDir, destDir);
|
|
121
|
+
results.push({ agent: agent.name, status: 'installed' });
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
results.push({ agent: agent.name, status: 'error', reason: String(err) });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return results;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Install a preset into squad home from an external source directory.
|
|
131
|
+
* Copies the preset directory into `<squad-home>/presets/<name>/`.
|
|
132
|
+
*
|
|
133
|
+
* @param sourceDir - Source directory containing preset.json and agents/.
|
|
134
|
+
* @param name - Preset name (used as destination directory name).
|
|
135
|
+
* @returns Path to the installed preset.
|
|
136
|
+
*/
|
|
137
|
+
export function installPreset(sourceDir, name) {
|
|
138
|
+
const homeDir = ensureSquadHome();
|
|
139
|
+
const destDir = path.join(homeDir, 'presets', name);
|
|
140
|
+
copyDirRecursive(sourceDir, destDir);
|
|
141
|
+
return destDir;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Save the current project's squad agents as a reusable preset.
|
|
145
|
+
*
|
|
146
|
+
* Reads agents from the given squad directory (e.g. `.squad/agents/`),
|
|
147
|
+
* generates a `preset.json` manifest, and copies everything into
|
|
148
|
+
* `<squad-home>/presets/<name>/`.
|
|
149
|
+
*
|
|
150
|
+
* @param name - Name for the new preset.
|
|
151
|
+
* @param squadDir - Path to the project's .squad/ directory containing agents/.
|
|
152
|
+
* @param options - force: overwrite existing preset; description: preset description.
|
|
153
|
+
* @returns Path to the saved preset directory.
|
|
154
|
+
*/
|
|
155
|
+
export function savePreset(name, squadDir, options = {}) {
|
|
156
|
+
validateName(name, 'preset');
|
|
157
|
+
const agentsDir = path.join(squadDir, 'agents');
|
|
158
|
+
if (!storage.existsSync(agentsDir) || !isDirSync(agentsDir)) {
|
|
159
|
+
throw new Error(`No agents/ directory found in ${squadDir}`);
|
|
160
|
+
}
|
|
161
|
+
const homeDir = ensureSquadHome();
|
|
162
|
+
const destDir = path.join(homeDir, 'presets', name);
|
|
163
|
+
if (storage.existsSync(destDir) && !options.force) {
|
|
164
|
+
throw new Error(`Preset '${name}' already exists. Use --force to overwrite.`);
|
|
165
|
+
}
|
|
166
|
+
// Discover agents
|
|
167
|
+
const agentEntries = readdirSync(agentsDir, { encoding: 'utf-8' });
|
|
168
|
+
const agents = [];
|
|
169
|
+
for (const entry of agentEntries) {
|
|
170
|
+
const agentDir = path.join(agentsDir, entry);
|
|
171
|
+
if (!isDirSync(agentDir))
|
|
172
|
+
continue;
|
|
173
|
+
// Try to extract role from charter.md front matter
|
|
174
|
+
let role = entry;
|
|
175
|
+
let description = '';
|
|
176
|
+
const charterPath = path.join(agentDir, 'charter.md');
|
|
177
|
+
if (storage.existsSync(charterPath)) {
|
|
178
|
+
const content = storage.readSync(charterPath);
|
|
179
|
+
if (content) {
|
|
180
|
+
const roleMatch = content.match(/^##?\s+.*?[-–—]\s*(.+)/m);
|
|
181
|
+
if (roleMatch)
|
|
182
|
+
role = roleMatch[1].trim();
|
|
183
|
+
// First non-empty, non-heading line as description
|
|
184
|
+
const lines = content.split('\n');
|
|
185
|
+
for (const line of lines) {
|
|
186
|
+
const trimmed = line.trim();
|
|
187
|
+
if (trimmed && !trimmed.startsWith('#') && !trimmed.startsWith('---')) {
|
|
188
|
+
description = trimmed.slice(0, 120);
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
agents.push({ name: entry, role, description });
|
|
195
|
+
}
|
|
196
|
+
if (agents.length === 0) {
|
|
197
|
+
throw new Error(`No agents found in ${agentsDir}`);
|
|
198
|
+
}
|
|
199
|
+
// Build manifest
|
|
200
|
+
const manifest = {
|
|
201
|
+
name,
|
|
202
|
+
version: '1.0.0',
|
|
203
|
+
description: options.description ?? `Custom preset '${name}'`,
|
|
204
|
+
agents: agents.map(a => ({ name: a.name, role: a.role, description: a.description })),
|
|
205
|
+
};
|
|
206
|
+
// Write preset directory
|
|
207
|
+
storage.mkdirSync(destDir, { recursive: true });
|
|
208
|
+
storage.writeSync(path.join(destDir, 'preset.json'), JSON.stringify(manifest, null, 2));
|
|
209
|
+
copyDirRecursive(agentsDir, path.join(destDir, 'agents'));
|
|
210
|
+
return destDir;
|
|
211
|
+
}
|
|
212
|
+
// ============================================================================
|
|
213
|
+
// Internal helpers
|
|
214
|
+
// ============================================================================
|
|
215
|
+
function loadPresetManifest(presetDir) {
|
|
216
|
+
const manifestPath = path.join(presetDir, 'preset.json');
|
|
217
|
+
if (!storage.existsSync(manifestPath))
|
|
218
|
+
return null;
|
|
219
|
+
try {
|
|
220
|
+
const content = storage.readSync(manifestPath);
|
|
221
|
+
if (!content)
|
|
222
|
+
return null;
|
|
223
|
+
const manifest = JSON.parse(content);
|
|
224
|
+
if (!manifest.name || !manifest.agents || !Array.isArray(manifest.agents)) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
return manifest;
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function copyDirRecursive(src, dest) {
|
|
234
|
+
storage.mkdirSync(dest, { recursive: true });
|
|
235
|
+
const entries = readdirSync(src, { encoding: 'utf-8' });
|
|
236
|
+
for (const entry of entries) {
|
|
237
|
+
const srcPath = path.join(src, entry);
|
|
238
|
+
const destPath = path.join(dest, entry);
|
|
239
|
+
const stat = lstatSync(srcPath);
|
|
240
|
+
// Skip symlinks — don't follow them into unintended locations
|
|
241
|
+
if (stat.isSymbolicLink())
|
|
242
|
+
continue;
|
|
243
|
+
if (stat.isDirectory()) {
|
|
244
|
+
copyDirRecursive(srcPath, destPath);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
const content = storage.readSync(srcPath);
|
|
248
|
+
if (content !== undefined) {
|
|
249
|
+
storage.writeSync(destPath, content);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Get the path to the built-in presets that ship with the SDK.
|
|
256
|
+
* These are bundled in the package under `presets/builtin/`.
|
|
257
|
+
*/
|
|
258
|
+
export function getBuiltinPresetsDir() {
|
|
259
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
260
|
+
return path.join(path.dirname(thisFile), 'builtin');
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Seed squad home with built-in presets if they don't already exist.
|
|
264
|
+
* Only copies presets that are missing — never overwrites user presets.
|
|
265
|
+
*
|
|
266
|
+
* @returns Names of presets that were seeded.
|
|
267
|
+
*/
|
|
268
|
+
export function seedBuiltinPresets() {
|
|
269
|
+
const homeDir = ensureSquadHome();
|
|
270
|
+
const builtinDir = getBuiltinPresetsDir();
|
|
271
|
+
const targetPresetsDir = path.join(homeDir, 'presets');
|
|
272
|
+
const seeded = [];
|
|
273
|
+
if (!storage.existsSync(builtinDir))
|
|
274
|
+
return seeded;
|
|
275
|
+
const entries = readdirSync(builtinDir, { encoding: 'utf-8' });
|
|
276
|
+
for (const entry of entries) {
|
|
277
|
+
const srcDir = path.join(builtinDir, entry);
|
|
278
|
+
const destDir = path.join(targetPresetsDir, entry);
|
|
279
|
+
if (!isDirSync(srcDir))
|
|
280
|
+
continue;
|
|
281
|
+
if (storage.existsSync(destDir))
|
|
282
|
+
continue;
|
|
283
|
+
copyDirRecursive(srcDir, destDir);
|
|
284
|
+
seeded.push(entry);
|
|
285
|
+
}
|
|
286
|
+
return seeded;
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=index.js.map
|