agentcohort 0.1.1 → 0.2.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/README.md +88 -12
- package/dist/args.d.ts +1 -0
- package/dist/args.js +9 -0
- package/dist/cli.js +45 -2
- package/dist/config.d.ts +32 -0
- package/dist/config.js +100 -0
- package/dist/configCmd.d.ts +27 -0
- package/dist/configCmd.js +54 -0
- package/dist/defaults.d.ts +21 -0
- package/dist/defaults.js +22 -0
- package/dist/diff.d.ts +24 -0
- package/dist/diff.js +64 -0
- package/dist/installer.d.ts +2 -0
- package/dist/installer.js +5 -1
- package/dist/promptModels.d.ts +13 -0
- package/dist/promptModels.js +66 -0
- package/dist/render.d.ts +11 -0
- package/dist/render.js +35 -0
- package/dist/templates/CLAUDE.section.md +19 -0
- package/dist/templates/agents/bug-fixer.md +14 -0
- package/dist/templates/agents/bug-hunter.md +14 -0
- package/dist/templates/agents/expert-council.md +14 -0
- package/dist/templates/agents/feature-implementer.md +14 -0
- package/dist/templates/agents/feature-planner.md +14 -0
- package/dist/templates/agents/final-reviewer.md +14 -0
- package/dist/templates/agents/perf-optimizer.md +14 -0
- package/dist/templates/agents/perf-reviewer.md +14 -0
- package/dist/templates/agents/performance-hunter.md +14 -0
- package/dist/templates/agents/regression-guard.md +14 -0
- package/dist/templates/agents/repo-scout.md +14 -0
- package/dist/templates/agents/reproduction-engineer.md +14 -0
- package/dist/templates/agents/root-cause-analyst.md +14 -0
- package/dist/templates/agents/solution-architect.md +14 -0
- package/dist/templates/agents/test-verifier.md +14 -0
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -93,6 +93,20 @@ production-grade correctness, no shallow fixes, no fixing without evidence, and
|
|
|
93
93
|
**a bug audit never fixes** — it produces a recommendation and stops at a human
|
|
94
94
|
approval gate.
|
|
95
95
|
|
|
96
|
+
### How agents respect your CLAUDE.md and skills
|
|
97
|
+
|
|
98
|
+
Every installed agent boots by reading your project's `CLAUDE.md`
|
|
99
|
+
content **outside** the `# Agentcohort Routing Rules` section and by
|
|
100
|
+
checking for installed skills that match the current task. The rules:
|
|
101
|
+
|
|
102
|
+
- Your project rules take precedence over an agent's default prompt.
|
|
103
|
+
- An agent invokes a matching skill instead of re-implementing it.
|
|
104
|
+
- Agentcohort's defaults apply only where your project is silent.
|
|
105
|
+
|
|
106
|
+
This means `agentcohort` slots into a project that already has its own
|
|
107
|
+
CLAUDE.md and skills (e.g. `superpowers`) — it does not override what
|
|
108
|
+
you've already set up.
|
|
109
|
+
|
|
96
110
|
## Using the workflow commands (inside Claude Code)
|
|
97
111
|
|
|
98
112
|
| Command | Pipeline | Use it for |
|
|
@@ -113,6 +127,58 @@ approval gate.
|
|
|
113
127
|
- **Sonnet** — implementation, testing, bug & performance hunting.
|
|
114
128
|
- **Opus** — architecture, root-cause analysis, expert council, final review.
|
|
115
129
|
|
|
130
|
+
## Customizing model strategy
|
|
131
|
+
|
|
132
|
+
`agentcohort init` will (interactively) prompt you to either use the
|
|
133
|
+
default Claude model IDs or pick your own for each tier. Your choice is
|
|
134
|
+
saved to `.agentcohort.json` at the project root and reused on later
|
|
135
|
+
runs.
|
|
136
|
+
|
|
137
|
+
To revisit your choice without re-installing everything, run:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
agentcohort config
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
This re-prompts for the three tier model IDs, shows a diff of which
|
|
144
|
+
installed agents would change, and applies the changes with your
|
|
145
|
+
confirmation.
|
|
146
|
+
|
|
147
|
+
To force a re-prompt during install (instead of using the existing
|
|
148
|
+
`.agentcohort.json`), run:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
agentcohort init --reconfigure
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### `.agentcohort.json` schema (v1)
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"$schema": "https://raw.githubusercontent.com/Thiendekaco/agentcohort/main/schema/agentcohort-config-v1.json",
|
|
159
|
+
"version": 1,
|
|
160
|
+
"models": {
|
|
161
|
+
"premium": "claude-opus-4-7",
|
|
162
|
+
"mid": "claude-sonnet-4-6",
|
|
163
|
+
"cheap": "claude-haiku-4-5-20251001"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
- **premium** — architecture, root-cause analysis, expert council, final
|
|
169
|
+
review.
|
|
170
|
+
- **mid** — implementation, testing, bug & performance hunting.
|
|
171
|
+
- **cheap** — fast read-only repo scout.
|
|
172
|
+
|
|
173
|
+
Hand-editing one of the installed `.claude/agents/*.md` files to use a
|
|
174
|
+
specific model ID is respected: subsequent `agentcohort init` and
|
|
175
|
+
`agentcohort config` runs leave that hand-edit alone (the tool only
|
|
176
|
+
rewrites lines that are still tier aliases or match the previous
|
|
177
|
+
config's IDs).
|
|
178
|
+
|
|
179
|
+
Model IDs are not validated by the tool — if the ID is invalid, Claude
|
|
180
|
+
Code will fail at agent spawn time.
|
|
181
|
+
|
|
116
182
|
## Customizing agents
|
|
117
183
|
|
|
118
184
|
The installed files are plain Markdown and **yours to edit**:
|
|
@@ -146,7 +212,8 @@ before changing them (or back them up with `--backup`).
|
|
|
146
212
|
- **`--dry-run`** performs zero writes and zero backups.
|
|
147
213
|
- Backups are written next to the original as
|
|
148
214
|
`<file>.backup-YYYYMMDD-HHMMSS` and never overwrite an existing backup.
|
|
149
|
-
- Cross-platform (Windows/macOS/Linux)
|
|
215
|
+
- Cross-platform (Windows/macOS/Linux); a single runtime dependency
|
|
216
|
+
(`@inquirer/prompts` for the interactive model-tier prompt), no
|
|
150
217
|
shell-specific behavior.
|
|
151
218
|
|
|
152
219
|
## Development
|
|
@@ -157,27 +224,36 @@ npm run build # tsc -> dist/, then copies templates
|
|
|
157
224
|
npm test # vitest
|
|
158
225
|
```
|
|
159
226
|
|
|
160
|
-
##
|
|
227
|
+
## Branching & releases
|
|
228
|
+
|
|
229
|
+
Two long-lived branches:
|
|
230
|
+
|
|
231
|
+
- **`dev`** — integration / staging. All feature PRs target `dev`. Nothing
|
|
232
|
+
here publishes to npm; this is the place to bundle PRs together, run
|
|
233
|
+
manual smoke tests, and verify the release as a whole.
|
|
234
|
+
- **`main`** — production. Only ever updated by merging `dev` → `main`.
|
|
235
|
+
Every push to `main` triggers the [`Release`](.github/workflows/release.yml)
|
|
236
|
+
workflow.
|
|
161
237
|
|
|
162
|
-
|
|
163
|
-
gets published.** Every push to `main` runs the
|
|
164
|
-
[`Release`](.github/workflows/release.yml) workflow, which:
|
|
238
|
+
The workflow does:
|
|
165
239
|
|
|
166
240
|
1. installs, builds and runs the full test suite;
|
|
167
241
|
2. publishes the **current** `package.json` version to npm —
|
|
168
242
|
https://www.npmjs.com/package/agentcohort (so the very first
|
|
169
|
-
release
|
|
243
|
+
release was exactly `0.1.0`, nothing skipped);
|
|
170
244
|
3. creates the annotated git tag `vX.Y.Z` on the published commit;
|
|
171
245
|
4. bumps to the next dev version (`patch` by default) and pushes a
|
|
172
246
|
`chore(release): published vX.Y.Z, open vX.Y.(Z+1) [skip ci]` commit back
|
|
173
247
|
to `main`.
|
|
174
248
|
|
|
175
|
-
So
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
249
|
+
So the normal release cycle is: open PR → `dev` → review & merge → smoke
|
|
250
|
+
test on `dev` → open PR `dev` → `main` → merge → workflow publishes. To
|
|
251
|
+
ship a `minor`/`major` instead of `patch`, bump `package.json` yourself
|
|
252
|
+
in the PR before merging (or use the *Run workflow* button to control
|
|
253
|
+
how the **next** pending version is opened). If the pending version is
|
|
254
|
+
already on npm, publish is skipped and the job still succeeds (safe
|
|
255
|
+
re-runs). The `[skip ci]` marker stops the release commit from
|
|
256
|
+
re-triggering the workflow (no publish loop).
|
|
181
257
|
|
|
182
258
|
**One-time setup:** add an npm **Automation** access token as the repository
|
|
183
259
|
secret `NPM_TOKEN` (GitHub → Settings → Secrets and variables → Actions →
|
package/dist/args.d.ts
CHANGED
package/dist/args.js
CHANGED
|
@@ -9,6 +9,7 @@ const FLAGS = {
|
|
|
9
9
|
'--dry-run': 'dryRun',
|
|
10
10
|
'--force': 'force',
|
|
11
11
|
'--backup': 'backup',
|
|
12
|
+
'--reconfigure': 'reconfigure',
|
|
12
13
|
'--help': 'help',
|
|
13
14
|
'-h': 'help',
|
|
14
15
|
'--version': 'version',
|
|
@@ -22,6 +23,7 @@ function parseArgs(argv) {
|
|
|
22
23
|
dryRun: false,
|
|
23
24
|
force: false,
|
|
24
25
|
backup: false,
|
|
26
|
+
reconfigure: false,
|
|
25
27
|
help: false,
|
|
26
28
|
version: false,
|
|
27
29
|
unknown: [],
|
|
@@ -56,6 +58,9 @@ ${b('USAGE')}
|
|
|
56
58
|
${b('COMMANDS')}
|
|
57
59
|
init Install agents, workflow commands and routing rules
|
|
58
60
|
into ./.claude and ./CLAUDE.md of the current project.
|
|
61
|
+
config Re-prompt the model-tier strategy, show a diff of
|
|
62
|
+
any pending changes to installed agents, and apply
|
|
63
|
+
them with confirmation.
|
|
59
64
|
|
|
60
65
|
${b('OPTIONS')}
|
|
61
66
|
--yes, -y Non-interactive. Safe defaults: new files created;
|
|
@@ -67,6 +72,9 @@ ${b('OPTIONS')}
|
|
|
67
72
|
section without prompting (no backup unless --backup).
|
|
68
73
|
--backup Always back up a file before overwriting it.
|
|
69
74
|
Backup name: <file>.backup-YYYYMMDD-HHMMSS
|
|
75
|
+
--reconfigure (init only) Re-prompt model-tier strategy even if a
|
|
76
|
+
.agentcohort.json already exists. Requires a TTY;
|
|
77
|
+
not compatible with --yes.
|
|
70
78
|
--help, -h Show this help.
|
|
71
79
|
--version, -v Print the version.
|
|
72
80
|
|
|
@@ -75,6 +83,7 @@ ${b('WHAT GETS INSTALLED')}
|
|
|
75
83
|
reviewer, bug-hunter, root-cause-analyst, ...).
|
|
76
84
|
.claude/commands/ 7 workflow commands.
|
|
77
85
|
CLAUDE.md Appends a "# Agentcohort Routing Rules" section.
|
|
86
|
+
.agentcohort.json (Only when user customizes model-tier strategy.)
|
|
78
87
|
|
|
79
88
|
${b('WORKFLOW COMMANDS (run inside Claude Code)')}
|
|
80
89
|
/auto-flow Classify the task and pick the right workflow.
|
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.main = main;
|
|
5
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
6
|
+
const core_1 = require("@inquirer/core");
|
|
5
7
|
const installer_1 = require("./installer");
|
|
6
8
|
const prompt_1 = require("./prompt");
|
|
9
|
+
const promptModels_1 = require("./promptModels");
|
|
10
|
+
const configCmd_1 = require("./configCmd");
|
|
11
|
+
const config_1 = require("./config");
|
|
7
12
|
const logger_1 = require("./logger");
|
|
8
13
|
const paths_1 = require("./paths");
|
|
9
14
|
const args_1 = require("./args");
|
|
@@ -55,11 +60,15 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
55
60
|
process.stdout.write((0, args_1.helpText)() + '\n');
|
|
56
61
|
return 0;
|
|
57
62
|
}
|
|
58
|
-
if (args.command !== 'init') {
|
|
63
|
+
if (args.command !== 'init' && args.command !== 'config') {
|
|
59
64
|
process.stderr.write((0, logger_1.paint)(`✗ Unknown command: ${args.command}\n`, 'red'));
|
|
60
65
|
process.stdout.write((0, args_1.helpText)() + '\n');
|
|
61
66
|
return 1;
|
|
62
67
|
}
|
|
68
|
+
if (args.reconfigure && (args.yes || args.force)) {
|
|
69
|
+
process.stderr.write((0, logger_1.paint)('✗ --reconfigure requires interactive mode (cannot combine with --yes or --force).\n', 'red'));
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
63
72
|
const stdinTTY = Boolean(process.stdin.isTTY);
|
|
64
73
|
const stdoutTTY = Boolean(process.stdout.isTTY);
|
|
65
74
|
const interactive = !args.yes && !args.force && !args.dryRun && stdinTTY && stdoutTTY;
|
|
@@ -73,10 +82,39 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
73
82
|
logger.info('Non-interactive environment detected — using safe defaults (like --yes).');
|
|
74
83
|
}
|
|
75
84
|
try {
|
|
85
|
+
const cwd = process.cwd();
|
|
86
|
+
if (args.command === 'config') {
|
|
87
|
+
if (!interactive) {
|
|
88
|
+
process.stderr.write((0, logger_1.paint)('✗ `agentcohort config` requires interactive mode (TTY). Edit .agentcohort.json directly to set models non-interactively.\n', 'red'));
|
|
89
|
+
return 1;
|
|
90
|
+
}
|
|
91
|
+
const result = await (0, configCmd_1.runConfigCmd)({
|
|
92
|
+
cwd,
|
|
93
|
+
promptModelStrategy: promptModels_1.promptModelStrategy,
|
|
94
|
+
confirm: (message) => (0, prompts_1.confirm)({ message, default: true }),
|
|
95
|
+
});
|
|
96
|
+
const msg = {
|
|
97
|
+
'no-changes': 'No changes. Configuration is up to date.',
|
|
98
|
+
'no-agents': 'Config saved. No installed agents found — run `agentcohort init` to install.',
|
|
99
|
+
'cancelled': 'Cancelled. No changes made.',
|
|
100
|
+
'applied': `Applied ${result.changes.length} change(s) to installed agents.`,
|
|
101
|
+
}[result.status];
|
|
102
|
+
process.stdout.write(`${(0, logger_1.paint)('•', 'cyan')} ${msg}\n`);
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
// command === 'init'
|
|
106
|
+
let existingConfig = (0, config_1.loadConfig)(cwd);
|
|
107
|
+
let models = (0, config_1.resolveModels)(existingConfig);
|
|
108
|
+
if (interactive && (existingConfig === null || args.reconfigure)) {
|
|
109
|
+
const newModels = await (0, promptModels_1.promptModelStrategy)(existingConfig?.models);
|
|
110
|
+
// Persist config BEFORE install so a partial install can be re-run idempotently.
|
|
111
|
+
(0, config_1.writeConfig)(cwd, { version: 1, models: newModels });
|
|
112
|
+
models = newModels;
|
|
113
|
+
}
|
|
76
114
|
if (interactive)
|
|
77
115
|
resolverHandle = (0, prompt_1.createInteractiveResolver)();
|
|
78
116
|
const result = await (0, installer_1.runInit)({
|
|
79
|
-
cwd
|
|
117
|
+
cwd,
|
|
80
118
|
yes: args.yes,
|
|
81
119
|
dryRun: args.dryRun,
|
|
82
120
|
force: args.force,
|
|
@@ -84,12 +122,17 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
84
122
|
interactive,
|
|
85
123
|
resolver: resolverHandle?.resolve,
|
|
86
124
|
logger,
|
|
125
|
+
models,
|
|
87
126
|
});
|
|
88
127
|
printSummary(result);
|
|
89
128
|
return 0;
|
|
90
129
|
}
|
|
91
130
|
catch (err) {
|
|
92
131
|
const message = err instanceof Error ? err.message : String(err);
|
|
132
|
+
if (err instanceof core_1.ExitPromptError) {
|
|
133
|
+
process.stderr.write((0, logger_1.paint)('\nCancelled. No changes made.\n', 'yellow'));
|
|
134
|
+
return 130;
|
|
135
|
+
}
|
|
93
136
|
process.stderr.write((0, logger_1.paint)(`\n✗ ${message}\n`, 'red'));
|
|
94
137
|
return 1;
|
|
95
138
|
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare const CONFIG_FILENAME = ".agentcohort.json";
|
|
2
|
+
export interface ModelsConfig {
|
|
3
|
+
premium: string;
|
|
4
|
+
mid: string;
|
|
5
|
+
cheap: string;
|
|
6
|
+
}
|
|
7
|
+
export interface AgentcohortConfig {
|
|
8
|
+
version: 1;
|
|
9
|
+
models: ModelsConfig;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Validate that `raw` is a well-formed AgentcohortConfig. Returns the
|
|
13
|
+
* parsed config (with unknown top-level keys stripped). Throws on any
|
|
14
|
+
* violation.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateConfig(raw: unknown): AgentcohortConfig;
|
|
17
|
+
/**
|
|
18
|
+
* Load `.agentcohort.json` from `projectRoot`. Returns null if absent.
|
|
19
|
+
* Throws a structured error if the file exists but is malformed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadConfig(projectRoot: string): AgentcohortConfig | null;
|
|
22
|
+
/**
|
|
23
|
+
* Write `cfg` to `.agentcohort.json` in `projectRoot`. Includes a
|
|
24
|
+
* `$schema` field for editor autocomplete. Idempotent at the byte
|
|
25
|
+
* level: writing the same config twice produces identical bytes.
|
|
26
|
+
*/
|
|
27
|
+
export declare function writeConfig(projectRoot: string, cfg: AgentcohortConfig): void;
|
|
28
|
+
/**
|
|
29
|
+
* Resolve a ModelsConfig from a possibly-null config. When null,
|
|
30
|
+
* returns DEFAULT_MODELS.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveModels(cfg: AgentcohortConfig | null): ModelsConfig;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CONFIG_FILENAME = void 0;
|
|
4
|
+
exports.validateConfig = validateConfig;
|
|
5
|
+
exports.loadConfig = loadConfig;
|
|
6
|
+
exports.writeConfig = writeConfig;
|
|
7
|
+
exports.resolveModels = resolveModels;
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const defaults_1 = require("./defaults");
|
|
11
|
+
exports.CONFIG_FILENAME = '.agentcohort.json';
|
|
12
|
+
const TIERS = ['premium', 'mid', 'cheap'];
|
|
13
|
+
function fail(reason) {
|
|
14
|
+
throw new Error(`Invalid .agentcohort.json: ${reason}. Expected schema: { version: 1, models: { premium: string, mid: string, cheap: string } }`);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validate that `raw` is a well-formed AgentcohortConfig. Returns the
|
|
18
|
+
* parsed config (with unknown top-level keys stripped). Throws on any
|
|
19
|
+
* violation.
|
|
20
|
+
*/
|
|
21
|
+
function validateConfig(raw) {
|
|
22
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
23
|
+
fail('not an object');
|
|
24
|
+
}
|
|
25
|
+
const obj = raw;
|
|
26
|
+
if (obj.version !== 1) {
|
|
27
|
+
fail(`unsupported version ${JSON.stringify(obj.version)}; expected 1`);
|
|
28
|
+
}
|
|
29
|
+
const models = obj.models;
|
|
30
|
+
if (models === null || typeof models !== 'object' || Array.isArray(models)) {
|
|
31
|
+
fail('models must be an object');
|
|
32
|
+
}
|
|
33
|
+
const m = models;
|
|
34
|
+
const out = {};
|
|
35
|
+
for (const tier of TIERS) {
|
|
36
|
+
const v = m[tier];
|
|
37
|
+
if (typeof v !== 'string') {
|
|
38
|
+
fail(`models.${tier} must be a string`);
|
|
39
|
+
}
|
|
40
|
+
if (v.trim().length === 0) {
|
|
41
|
+
fail(`models.${tier} must be a non-empty, non-whitespace string`);
|
|
42
|
+
}
|
|
43
|
+
out[tier] = v;
|
|
44
|
+
}
|
|
45
|
+
return { version: 1, models: out };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Load `.agentcohort.json` from `projectRoot`. Returns null if absent.
|
|
49
|
+
* Throws a structured error if the file exists but is malformed.
|
|
50
|
+
*/
|
|
51
|
+
function loadConfig(projectRoot) {
|
|
52
|
+
const path = (0, node_path_1.join)(projectRoot, exports.CONFIG_FILENAME);
|
|
53
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
54
|
+
return null;
|
|
55
|
+
let parsed;
|
|
56
|
+
try {
|
|
57
|
+
parsed = JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
61
|
+
fail(`malformed JSON (${msg})`);
|
|
62
|
+
}
|
|
63
|
+
return validateConfig(parsed);
|
|
64
|
+
}
|
|
65
|
+
const SCHEMA_URL = 'https://raw.githubusercontent.com/Thiendekaco/agentcohort/main/schema/agentcohort-config-v1.json';
|
|
66
|
+
function serializeConfig(cfg) {
|
|
67
|
+
const ordered = {
|
|
68
|
+
$schema: SCHEMA_URL,
|
|
69
|
+
version: cfg.version,
|
|
70
|
+
models: {
|
|
71
|
+
premium: cfg.models.premium,
|
|
72
|
+
mid: cfg.models.mid,
|
|
73
|
+
cheap: cfg.models.cheap,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
return JSON.stringify(ordered, null, 2) + '\n';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Write `cfg` to `.agentcohort.json` in `projectRoot`. Includes a
|
|
80
|
+
* `$schema` field for editor autocomplete. Idempotent at the byte
|
|
81
|
+
* level: writing the same config twice produces identical bytes.
|
|
82
|
+
*/
|
|
83
|
+
function writeConfig(projectRoot, cfg) {
|
|
84
|
+
const path = (0, node_path_1.join)(projectRoot, exports.CONFIG_FILENAME);
|
|
85
|
+
(0, node_fs_1.writeFileSync)(path, serializeConfig(cfg), 'utf8');
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Resolve a ModelsConfig from a possibly-null config. When null,
|
|
89
|
+
* returns DEFAULT_MODELS.
|
|
90
|
+
*/
|
|
91
|
+
function resolveModels(cfg) {
|
|
92
|
+
if (cfg === null) {
|
|
93
|
+
return {
|
|
94
|
+
premium: defaults_1.DEFAULT_MODELS.premium,
|
|
95
|
+
mid: defaults_1.DEFAULT_MODELS.mid,
|
|
96
|
+
cheap: defaults_1.DEFAULT_MODELS.cheap,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return cfg.models;
|
|
100
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ModelsConfig } from './config';
|
|
2
|
+
import { ModelChange } from './diff';
|
|
3
|
+
export type ConfigCmdStatus = 'no-changes' | 'no-agents' | 'cancelled' | 'applied';
|
|
4
|
+
export interface ConfigCmdResult {
|
|
5
|
+
status: ConfigCmdStatus;
|
|
6
|
+
changes: ModelChange[];
|
|
7
|
+
}
|
|
8
|
+
export interface ConfigCmdOptions {
|
|
9
|
+
cwd: string;
|
|
10
|
+
/** Inject the prompt function — production passes the real TUI, tests pass a mock. */
|
|
11
|
+
promptModelStrategy: (current?: ModelsConfig) => Promise<ModelsConfig>;
|
|
12
|
+
/** Inject the diff-confirm function — same reason. */
|
|
13
|
+
confirm: (message: string) => Promise<boolean>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Run the `agentcohort config` subcommand.
|
|
17
|
+
*
|
|
18
|
+
* 1. Load existing config (or null → defaults).
|
|
19
|
+
* 2. Prompt user for a new ModelsConfig (pre-filled with current).
|
|
20
|
+
* 3. If no models changed: write config (idempotent) → 'no-changes'.
|
|
21
|
+
* 4. Compute the diff of installed agent files.
|
|
22
|
+
* 5. If diff is empty (e.g. no .claude/agents dir): write config → 'no-agents'.
|
|
23
|
+
* 6. Otherwise: confirm with user. Decline → 'cancelled'. Accept →
|
|
24
|
+
* write config + rewrite each affected file's `model:` line in
|
|
25
|
+
* place (preserves the rest byte-for-byte) → 'applied'.
|
|
26
|
+
*/
|
|
27
|
+
export declare function runConfigCmd(opts: ConfigCmdOptions): Promise<ConfigCmdResult>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runConfigCmd = runConfigCmd;
|
|
4
|
+
const node_path_1 = require("node:path");
|
|
5
|
+
const config_1 = require("./config");
|
|
6
|
+
const diff_1 = require("./diff");
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
/**
|
|
9
|
+
* Run the `agentcohort config` subcommand.
|
|
10
|
+
*
|
|
11
|
+
* 1. Load existing config (or null → defaults).
|
|
12
|
+
* 2. Prompt user for a new ModelsConfig (pre-filled with current).
|
|
13
|
+
* 3. If no models changed: write config (idempotent) → 'no-changes'.
|
|
14
|
+
* 4. Compute the diff of installed agent files.
|
|
15
|
+
* 5. If diff is empty (e.g. no .claude/agents dir): write config → 'no-agents'.
|
|
16
|
+
* 6. Otherwise: confirm with user. Decline → 'cancelled'. Accept →
|
|
17
|
+
* write config + rewrite each affected file's `model:` line in
|
|
18
|
+
* place (preserves the rest byte-for-byte) → 'applied'.
|
|
19
|
+
*/
|
|
20
|
+
async function runConfigCmd(opts) {
|
|
21
|
+
const existing = (0, config_1.loadConfig)(opts.cwd);
|
|
22
|
+
const oldModels = (0, config_1.resolveModels)(existing);
|
|
23
|
+
const newModels = await opts.promptModelStrategy(existing?.models);
|
|
24
|
+
const noChange = newModels.premium === oldModels.premium &&
|
|
25
|
+
newModels.mid === oldModels.mid &&
|
|
26
|
+
newModels.cheap === oldModels.cheap;
|
|
27
|
+
const newConfig = { version: 1, models: newModels };
|
|
28
|
+
const agentDir = (0, node_path_1.join)(opts.cwd, '.claude', 'agents');
|
|
29
|
+
if (noChange) {
|
|
30
|
+
(0, config_1.writeConfig)(opts.cwd, newConfig);
|
|
31
|
+
if (!(0, node_fs_1.existsSync)(agentDir)) {
|
|
32
|
+
return { status: 'no-agents', changes: [] };
|
|
33
|
+
}
|
|
34
|
+
return { status: 'no-changes', changes: [] };
|
|
35
|
+
}
|
|
36
|
+
const changes = (0, diff_1.computeFrontmatterModelDiff)(agentDir, oldModels, newModels);
|
|
37
|
+
if (changes.length === 0) {
|
|
38
|
+
(0, config_1.writeConfig)(opts.cwd, newConfig);
|
|
39
|
+
return { status: 'no-agents', changes: [] };
|
|
40
|
+
}
|
|
41
|
+
const message = `Apply ${changes.length} model change${changes.length === 1 ? '' : 's'}?`;
|
|
42
|
+
const accepted = await opts.confirm(message);
|
|
43
|
+
if (!accepted) {
|
|
44
|
+
return { status: 'cancelled', changes };
|
|
45
|
+
}
|
|
46
|
+
(0, config_1.writeConfig)(opts.cwd, newConfig);
|
|
47
|
+
for (const c of changes) {
|
|
48
|
+
const path = (0, node_path_1.join)(agentDir, c.file);
|
|
49
|
+
const current = (0, node_fs_1.readFileSync)(path, 'utf8');
|
|
50
|
+
const rewritten = current.replace(/^model:[ \t]+\S+[ \t]*$/m, `model: ${c.to}`);
|
|
51
|
+
(0, node_fs_1.writeFileSync)(path, rewritten, 'utf8');
|
|
52
|
+
}
|
|
53
|
+
return { status: 'applied', changes };
|
|
54
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for default Claude model IDs and the
|
|
3
|
+
* alias-to-tier mapping the installer uses when rewriting agent
|
|
4
|
+
* templates.
|
|
5
|
+
*
|
|
6
|
+
* When a new Claude model ships, update DEFAULT_MODELS here and the
|
|
7
|
+
* change flows through both the default install path and the prompt
|
|
8
|
+
* defaults.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_MODELS: {
|
|
11
|
+
readonly premium: "claude-opus-4-7";
|
|
12
|
+
readonly mid: "claude-sonnet-4-6";
|
|
13
|
+
readonly cheap: "claude-haiku-4-5-20251001";
|
|
14
|
+
};
|
|
15
|
+
export declare const TIER_ALIASES: {
|
|
16
|
+
readonly opus: "premium";
|
|
17
|
+
readonly sonnet: "mid";
|
|
18
|
+
readonly haiku: "cheap";
|
|
19
|
+
};
|
|
20
|
+
export type Tier = keyof typeof DEFAULT_MODELS;
|
|
21
|
+
export type TierAlias = keyof typeof TIER_ALIASES;
|
package/dist/defaults.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Single source of truth for default Claude model IDs and the
|
|
4
|
+
* alias-to-tier mapping the installer uses when rewriting agent
|
|
5
|
+
* templates.
|
|
6
|
+
*
|
|
7
|
+
* When a new Claude model ships, update DEFAULT_MODELS here and the
|
|
8
|
+
* change flows through both the default install path and the prompt
|
|
9
|
+
* defaults.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TIER_ALIASES = exports.DEFAULT_MODELS = void 0;
|
|
13
|
+
exports.DEFAULT_MODELS = {
|
|
14
|
+
premium: 'claude-opus-4-7',
|
|
15
|
+
mid: 'claude-sonnet-4-6',
|
|
16
|
+
cheap: 'claude-haiku-4-5-20251001',
|
|
17
|
+
};
|
|
18
|
+
exports.TIER_ALIASES = {
|
|
19
|
+
opus: 'premium',
|
|
20
|
+
sonnet: 'mid',
|
|
21
|
+
haiku: 'cheap',
|
|
22
|
+
};
|
package/dist/diff.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ModelsConfig } from './config';
|
|
2
|
+
export interface ModelChange {
|
|
3
|
+
file: string;
|
|
4
|
+
from: string;
|
|
5
|
+
to: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* For each `.md` file in `installedAgentDir`, decide whether changing
|
|
9
|
+
* the model config from `oldModels` to `newModels` would alter the
|
|
10
|
+
* file's frontmatter `model:` line. Returns the list of changes (file
|
|
11
|
+
* name + from/to model IDs).
|
|
12
|
+
*
|
|
13
|
+
* A file is treated three ways:
|
|
14
|
+
* 1. Tier alias in frontmatter (`model: opus|sonnet|haiku`): treat
|
|
15
|
+
* as `oldModels[tier(alias)]` and check whether
|
|
16
|
+
* `newModels[tier(alias)]` differs.
|
|
17
|
+
* 2. Concrete ID matching one of oldModels: that's a previously
|
|
18
|
+
* rendered file; we know its tier, so check whether the new
|
|
19
|
+
* config differs.
|
|
20
|
+
* 3. Concrete ID NOT in oldModels: a hand-edit. Skipped.
|
|
21
|
+
*
|
|
22
|
+
* Returns [] if the dir does not exist.
|
|
23
|
+
*/
|
|
24
|
+
export declare function computeFrontmatterModelDiff(installedAgentDir: string, oldModels: ModelsConfig, newModels: ModelsConfig): ModelChange[];
|
package/dist/diff.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeFrontmatterModelDiff = computeFrontmatterModelDiff;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const defaults_1 = require("./defaults");
|
|
7
|
+
/**
|
|
8
|
+
* For each `.md` file in `installedAgentDir`, decide whether changing
|
|
9
|
+
* the model config from `oldModels` to `newModels` would alter the
|
|
10
|
+
* file's frontmatter `model:` line. Returns the list of changes (file
|
|
11
|
+
* name + from/to model IDs).
|
|
12
|
+
*
|
|
13
|
+
* A file is treated three ways:
|
|
14
|
+
* 1. Tier alias in frontmatter (`model: opus|sonnet|haiku`): treat
|
|
15
|
+
* as `oldModels[tier(alias)]` and check whether
|
|
16
|
+
* `newModels[tier(alias)]` differs.
|
|
17
|
+
* 2. Concrete ID matching one of oldModels: that's a previously
|
|
18
|
+
* rendered file; we know its tier, so check whether the new
|
|
19
|
+
* config differs.
|
|
20
|
+
* 3. Concrete ID NOT in oldModels: a hand-edit. Skipped.
|
|
21
|
+
*
|
|
22
|
+
* Returns [] if the dir does not exist.
|
|
23
|
+
*/
|
|
24
|
+
function computeFrontmatterModelDiff(installedAgentDir, oldModels, newModels) {
|
|
25
|
+
if (!(0, node_fs_1.existsSync)(installedAgentDir))
|
|
26
|
+
return [];
|
|
27
|
+
const oldById = {
|
|
28
|
+
[oldModels.premium]: 'premium',
|
|
29
|
+
[oldModels.mid]: 'mid',
|
|
30
|
+
[oldModels.cheap]: 'cheap',
|
|
31
|
+
};
|
|
32
|
+
const changes = [];
|
|
33
|
+
for (const file of (0, node_fs_1.readdirSync)(installedAgentDir).sort()) {
|
|
34
|
+
if (!file.endsWith('.md'))
|
|
35
|
+
continue;
|
|
36
|
+
const text = (0, node_fs_1.readFileSync)((0, node_path_1.join)(installedAgentDir, file), 'utf8');
|
|
37
|
+
const m = text.match(/^model:[ \t]+(\S+)[ \t]*$/m);
|
|
38
|
+
if (!m?.[1])
|
|
39
|
+
continue;
|
|
40
|
+
const value = m[1];
|
|
41
|
+
let tier;
|
|
42
|
+
let from = '';
|
|
43
|
+
if (value === 'opus' || value === 'sonnet' || value === 'haiku') {
|
|
44
|
+
tier = defaults_1.TIER_ALIASES[value];
|
|
45
|
+
if (!tier)
|
|
46
|
+
continue; // satisfy strict TS
|
|
47
|
+
from = oldModels[tier];
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const tierFromId = oldById[value];
|
|
51
|
+
if (!tierFromId) {
|
|
52
|
+
// hand-edited specific ID → skip
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
tier = tierFromId;
|
|
56
|
+
from = value;
|
|
57
|
+
}
|
|
58
|
+
const to = newModels[tier];
|
|
59
|
+
if (from !== to) {
|
|
60
|
+
changes.push({ file, from, to });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return changes;
|
|
64
|
+
}
|
package/dist/installer.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EntryKind } from './manifest';
|
|
2
2
|
import type { ConflictResolver } from './prompt';
|
|
3
3
|
import type { Logger } from './logger';
|
|
4
|
+
import type { ModelsConfig } from './config';
|
|
4
5
|
export type Disposition = 'created' | 'overwritten' | 'appended-section' | 'replaced-section' | 'skipped' | 'unchanged';
|
|
5
6
|
export interface ActionRecord {
|
|
6
7
|
targetRelPath: string;
|
|
@@ -19,6 +20,7 @@ export interface InitOptions {
|
|
|
19
20
|
backup: boolean;
|
|
20
21
|
/** When false, conflicts are resolved by safe automatic defaults. */
|
|
21
22
|
interactive: boolean;
|
|
23
|
+
models: ModelsConfig;
|
|
22
24
|
resolver?: ConflictResolver;
|
|
23
25
|
now?: () => Date;
|
|
24
26
|
logger?: Logger;
|
package/dist/installer.js
CHANGED
|
@@ -7,6 +7,7 @@ const fileOps_1 = require("./fileOps");
|
|
|
7
7
|
const claudeMd_1 = require("./claudeMd");
|
|
8
8
|
const manifest_1 = require("./manifest");
|
|
9
9
|
const paths_1 = require("./paths");
|
|
10
|
+
const render_1 = require("./render");
|
|
10
11
|
/** Pick a non-clobbering backup path (never overwrite an existing backup). */
|
|
11
12
|
function uniqueBackupPath(target, date) {
|
|
12
13
|
const base = (0, fileOps_1.backupPathFor)(target, date);
|
|
@@ -100,7 +101,10 @@ async function runInit(options) {
|
|
|
100
101
|
return { projectRoot, actions, dryRun: options.dryRun };
|
|
101
102
|
// ---- per-entry handlers (closures over decide/record/doBackup) ----
|
|
102
103
|
async function handleRegular(entry) {
|
|
103
|
-
const
|
|
104
|
+
const rawTemplate = (0, node_fs_1.readFileSync)(entry.templateAbsPath, 'utf8');
|
|
105
|
+
const template = entry.targetRelPath.startsWith('.claude/agents/')
|
|
106
|
+
? (0, render_1.renderAgentTemplate)(rawTemplate, options.models)
|
|
107
|
+
: rawTemplate;
|
|
104
108
|
const existing = (0, fileOps_1.readIfExists)(entry.targetAbsPath);
|
|
105
109
|
if (existing === null) {
|
|
106
110
|
if (!options.dryRun)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ModelsConfig } from './config';
|
|
2
|
+
/**
|
|
3
|
+
* Interactive 1-step or 2-step prompt that returns a ModelsConfig.
|
|
4
|
+
*
|
|
5
|
+
* Step 1: select "Use defaults (auto)" or "Customize each tier".
|
|
6
|
+
* Step 2 (custom only): three input prompts for premium / mid / cheap.
|
|
7
|
+
*
|
|
8
|
+
* When `current` is provided, prompts pre-fill with it. Otherwise the
|
|
9
|
+
* defaults are pre-filled.
|
|
10
|
+
*
|
|
11
|
+
* Throws ExitPromptError on Ctrl+C — the caller catches and exits 130.
|
|
12
|
+
*/
|
|
13
|
+
export declare function promptModelStrategy(current?: ModelsConfig): Promise<ModelsConfig>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.promptModelStrategy = promptModelStrategy;
|
|
4
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
5
|
+
const defaults_1 = require("./defaults");
|
|
6
|
+
/**
|
|
7
|
+
* Interactive 1-step or 2-step prompt that returns a ModelsConfig.
|
|
8
|
+
*
|
|
9
|
+
* Step 1: select "Use defaults (auto)" or "Customize each tier".
|
|
10
|
+
* Step 2 (custom only): three input prompts for premium / mid / cheap.
|
|
11
|
+
*
|
|
12
|
+
* When `current` is provided, prompts pre-fill with it. Otherwise the
|
|
13
|
+
* defaults are pre-filled.
|
|
14
|
+
*
|
|
15
|
+
* Throws ExitPromptError on Ctrl+C — the caller catches and exits 130.
|
|
16
|
+
*/
|
|
17
|
+
async function promptModelStrategy(current) {
|
|
18
|
+
const base = current ?? {
|
|
19
|
+
premium: defaults_1.DEFAULT_MODELS.premium,
|
|
20
|
+
mid: defaults_1.DEFAULT_MODELS.mid,
|
|
21
|
+
cheap: defaults_1.DEFAULT_MODELS.cheap,
|
|
22
|
+
};
|
|
23
|
+
const mode = await (0, prompts_1.select)({
|
|
24
|
+
message: 'Model strategy:',
|
|
25
|
+
choices: [
|
|
26
|
+
{
|
|
27
|
+
name: 'Use defaults (recommended)',
|
|
28
|
+
value: 'auto',
|
|
29
|
+
description: `premium=${defaults_1.DEFAULT_MODELS.premium}, mid=${defaults_1.DEFAULT_MODELS.mid}, cheap=${defaults_1.DEFAULT_MODELS.cheap}`,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'Customize each tier',
|
|
33
|
+
value: 'custom',
|
|
34
|
+
description: 'Enter a specific model ID for premium / mid / cheap',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
default: current ? 'custom' : 'auto',
|
|
38
|
+
});
|
|
39
|
+
if (mode === 'auto') {
|
|
40
|
+
return {
|
|
41
|
+
premium: defaults_1.DEFAULT_MODELS.premium,
|
|
42
|
+
mid: defaults_1.DEFAULT_MODELS.mid,
|
|
43
|
+
cheap: defaults_1.DEFAULT_MODELS.cheap,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const premium = await (0, prompts_1.input)({
|
|
47
|
+
message: 'Premium tier model ID (architecture, root cause, review):',
|
|
48
|
+
default: base.premium,
|
|
49
|
+
validate: (v) => (v.trim().length > 0 ? true : 'must not be empty'),
|
|
50
|
+
});
|
|
51
|
+
const mid = await (0, prompts_1.input)({
|
|
52
|
+
message: 'Mid tier model ID (implementation, tests, hunting):',
|
|
53
|
+
default: base.mid,
|
|
54
|
+
validate: (v) => (v.trim().length > 0 ? true : 'must not be empty'),
|
|
55
|
+
});
|
|
56
|
+
const cheap = await (0, prompts_1.input)({
|
|
57
|
+
message: 'Cheap tier model ID (repo scout):',
|
|
58
|
+
default: base.cheap,
|
|
59
|
+
validate: (v) => (v.trim().length > 0 ? true : 'must not be empty'),
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
premium: premium.trim(),
|
|
63
|
+
mid: mid.trim(),
|
|
64
|
+
cheap: cheap.trim(),
|
|
65
|
+
};
|
|
66
|
+
}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ModelsConfig } from './config';
|
|
2
|
+
/**
|
|
3
|
+
* Rewrite the `model:` line inside the YAML frontmatter from a tier
|
|
4
|
+
* alias (haiku/sonnet/opus) to the user's concrete model ID. Leaves
|
|
5
|
+
* everything else (including hand-edited specific IDs in the
|
|
6
|
+
* frontmatter, and any `model:` text in the body) unchanged.
|
|
7
|
+
*
|
|
8
|
+
* Pure and idempotent: rendering an already-rendered file returns it
|
|
9
|
+
* unchanged.
|
|
10
|
+
*/
|
|
11
|
+
export declare function renderAgentTemplate(content: string, models: ModelsConfig): string;
|
package/dist/render.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderAgentTemplate = renderAgentTemplate;
|
|
4
|
+
const defaults_1 = require("./defaults");
|
|
5
|
+
/**
|
|
6
|
+
* Rewrite the `model:` line inside the YAML frontmatter from a tier
|
|
7
|
+
* alias (haiku/sonnet/opus) to the user's concrete model ID. Leaves
|
|
8
|
+
* everything else (including hand-edited specific IDs in the
|
|
9
|
+
* frontmatter, and any `model:` text in the body) unchanged.
|
|
10
|
+
*
|
|
11
|
+
* Pure and idempotent: rendering an already-rendered file returns it
|
|
12
|
+
* unchanged.
|
|
13
|
+
*/
|
|
14
|
+
function renderAgentTemplate(content, models) {
|
|
15
|
+
if (!content.startsWith('---'))
|
|
16
|
+
return content;
|
|
17
|
+
// Find the end of the YAML frontmatter (the second `---` on its own line).
|
|
18
|
+
const fmEndRe = /^---[ \t]*\r?\n([\s\S]*?\r?\n)---[ \t]*\r?\n/;
|
|
19
|
+
const fmMatch = content.match(fmEndRe);
|
|
20
|
+
if (!fmMatch)
|
|
21
|
+
return content;
|
|
22
|
+
const fmEnd = fmMatch[0].length;
|
|
23
|
+
const frontmatter = content.slice(0, fmEnd);
|
|
24
|
+
const body = content.slice(fmEnd);
|
|
25
|
+
const aliasRe = /^model:[ \t]+(haiku|sonnet|opus)[ \t]*$/m;
|
|
26
|
+
const aliasMatch = frontmatter.match(aliasRe);
|
|
27
|
+
if (!aliasMatch)
|
|
28
|
+
return content;
|
|
29
|
+
const alias = aliasMatch[1];
|
|
30
|
+
const tier = defaults_1.TIER_ALIASES[alias];
|
|
31
|
+
const newModelId = models[tier];
|
|
32
|
+
const newLine = `model: ${newModelId}`;
|
|
33
|
+
const newFrontmatter = frontmatter.replace(aliasRe, newLine);
|
|
34
|
+
return newFrontmatter + body;
|
|
35
|
+
}
|
|
@@ -8,6 +8,25 @@
|
|
|
8
8
|
This project runs as an **AI software-engineering organization**. Default to
|
|
9
9
|
routing work through the workflow commands instead of ad-hoc editing.
|
|
10
10
|
|
|
11
|
+
## Interoperability & precedence
|
|
12
|
+
|
|
13
|
+
These rules govern how the installed agents interact with the rest of
|
|
14
|
+
your project's setup. They apply to every agent and every workflow.
|
|
15
|
+
|
|
16
|
+
- **Your project rules win.** Anything you write in this CLAUDE.md
|
|
17
|
+
*outside* this `# Agentcohort Routing Rules` section takes precedence
|
|
18
|
+
over an installed agent's prompt. On conflict, agents follow your
|
|
19
|
+
rules.
|
|
20
|
+
- **Installed skills must be invoked when they match.** If you have a
|
|
21
|
+
skill (e.g. `superpowers:*`, `gstack`, etc.) that fits the current
|
|
22
|
+
task, the agent invokes it instead of re-implementing the same logic.
|
|
23
|
+
- **Agent prompts are a baseline, not ground truth.** When your
|
|
24
|
+
CLAUDE.md specifies a tool, framework, commit style, or workflow, the
|
|
25
|
+
agent uses your choice — not the default in its prompt.
|
|
26
|
+
- **Pipeline commands remain the default routing.** `/dev-flow`,
|
|
27
|
+
`/bug-audit`, and the others are the default. A user-defined flow in
|
|
28
|
+
your CLAUDE.md takes precedence when present.
|
|
29
|
+
|
|
11
30
|
## Operating standard (all agents)
|
|
12
31
|
|
|
13
32
|
- Operate at **top 1% principal/staff software-engineer** level.
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep, Edit, Write, Bash
|
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Bug Fixer**. You correct the proven root cause of an approved
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep, Bash
|
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Bug Hunter**. You find what is broken or fragile before users
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep
|
|
|
5
5
|
model: opus
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Expert Council** — a single agent that deliberates as four
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep, Edit, Write, Bash
|
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Feature Implementer**. You execute the plan exactly, making the
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep
|
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Feature Planner**. You convert intent into an unambiguous,
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep, Bash
|
|
|
5
5
|
model: opus
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Final Reviewer**. You are the last line of defense before code
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep, Edit, Bash
|
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Performance Optimizer**. You make it faster without making it
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep, Bash
|
|
|
5
5
|
model: opus
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Performance Reviewer**. You make sure the speedup did not buy
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep, Bash
|
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Performance Hunter**. You locate where time and resources
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep, Edit, Bash
|
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Regression Guard**. Your tests are the bug's permanent tombstone:
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep
|
|
|
5
5
|
model: haiku
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Repo Scout**. You go in first, map the terrain, and come back
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep, Bash, Edit
|
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Reproduction Engineer**. A bug that cannot be reproduced cannot
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep, Bash
|
|
|
5
5
|
model: opus
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Root Cause Analyst**. You refuse to stop at the symptom. You
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep
|
|
|
5
5
|
model: opus
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Solution Architect**. You decide *how* the system should change
|
|
@@ -5,6 +5,20 @@ tools: Read, Glob, Grep, Edit, Bash
|
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- boot-directive-start -->
|
|
9
|
+
|
|
10
|
+
# Boot directive — read before acting
|
|
11
|
+
|
|
12
|
+
1. Read project CLAUDE.md (especially content OUTSIDE the
|
|
13
|
+
`# Agentcohort Routing Rules` section). User project rules take
|
|
14
|
+
precedence over this agent prompt where they conflict.
|
|
15
|
+
2. Check available skills. If any skill matches what you're about to do,
|
|
16
|
+
invoke it first — don't re-implement what a skill provides.
|
|
17
|
+
3. Your role below is the default playbook. User CLAUDE.md and skills
|
|
18
|
+
override this playbook on conflict.
|
|
19
|
+
|
|
20
|
+
<!-- boot-directive-end -->
|
|
21
|
+
|
|
8
22
|
# Role
|
|
9
23
|
|
|
10
24
|
You are the **Test Verifier**. You are the evidence gate: after a change, you
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentcohort",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Install a principal/staff-level Claude Code AI software engineering organization (agents + workflows + routing rules) into any project with one command.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -43,7 +43,8 @@
|
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
46
|
-
"
|
|
46
|
+
"sync-boot": "node scripts/sync-boot-directive.mjs",
|
|
47
|
+
"build": "npm run clean && npm run sync-boot && tsc -p tsconfig.json && node scripts/copy-templates.mjs",
|
|
47
48
|
"test": "vitest run",
|
|
48
49
|
"test:watch": "vitest",
|
|
49
50
|
"prepublishOnly": "npm run build && npm test"
|
|
@@ -52,5 +53,8 @@
|
|
|
52
53
|
"@types/node": "^20.14.0",
|
|
53
54
|
"typescript": "^5.5.0",
|
|
54
55
|
"vitest": "^2.0.0"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@inquirer/prompts": "^7.10.1"
|
|
55
59
|
}
|
|
56
|
-
}
|
|
60
|
+
}
|