agentcohort 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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +187 -0
  3. package/dist/args.d.ts +13 -0
  4. package/dist/args.js +92 -0
  5. package/dist/claudeMd.d.ts +33 -0
  6. package/dist/claudeMd.js +156 -0
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +108 -0
  9. package/dist/fileOps.d.ts +15 -0
  10. package/dist/fileOps.js +50 -0
  11. package/dist/index.d.ts +17 -0
  12. package/dist/index.js +30 -0
  13. package/dist/installer.d.ts +32 -0
  14. package/dist/installer.js +208 -0
  15. package/dist/logger.d.ts +43 -0
  16. package/dist/logger.js +63 -0
  17. package/dist/manifest.d.ts +18 -0
  18. package/dist/manifest.js +44 -0
  19. package/dist/paths.d.ts +15 -0
  20. package/dist/paths.js +52 -0
  21. package/dist/prompt.d.ts +32 -0
  22. package/dist/prompt.js +57 -0
  23. package/dist/templates/CLAUDE.section.md +62 -0
  24. package/dist/templates/agents/bug-fixer.md +67 -0
  25. package/dist/templates/agents/bug-hunter.md +67 -0
  26. package/dist/templates/agents/expert-council.md +83 -0
  27. package/dist/templates/agents/feature-implementer.md +69 -0
  28. package/dist/templates/agents/feature-planner.md +75 -0
  29. package/dist/templates/agents/final-reviewer.md +71 -0
  30. package/dist/templates/agents/perf-optimizer.md +63 -0
  31. package/dist/templates/agents/perf-reviewer.md +66 -0
  32. package/dist/templates/agents/performance-hunter.md +68 -0
  33. package/dist/templates/agents/regression-guard.md +61 -0
  34. package/dist/templates/agents/repo-scout.md +77 -0
  35. package/dist/templates/agents/reproduction-engineer.md +65 -0
  36. package/dist/templates/agents/root-cause-analyst.md +71 -0
  37. package/dist/templates/agents/solution-architect.md +71 -0
  38. package/dist/templates/agents/test-verifier.md +68 -0
  39. package/dist/templates/commands/auto-flow.md +41 -0
  40. package/dist/templates/commands/bug-audit.md +51 -0
  41. package/dist/templates/commands/bug-fix-approved.md +44 -0
  42. package/dist/templates/commands/dev-flow.md +41 -0
  43. package/dist/templates/commands/fix-blockers.md +36 -0
  44. package/dist/templates/commands/perf-hunt.md +40 -0
  45. package/dist/templates/commands/review-diff.md +39 -0
  46. package/package.json +56 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 agentcohort contributors
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,187 @@
1
+ # agentcohort
2
+
3
+ > Install a principal/staff-level **AI software-engineering organization** for
4
+ > [Claude Code](https://docs.claude.com/en/docs/claude-code) into any project
5
+ > with one command.
6
+
7
+ `agentcohort` is not just a template copier. It installs a coordinated set of
8
+ **15 subagents**, **7 workflow commands**, and **routing rules** that make
9
+ Claude Code work like a disciplined engineering org: explore before changing,
10
+ prove root cause before fixing, measure before optimizing, and review before
11
+ shipping.
12
+
13
+ ---
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm i agentcohort
19
+ ```
20
+
21
+ Or run it without installing:
22
+
23
+ ```bash
24
+ npx agentcohort init
25
+ ```
26
+
27
+ > The npm package is `agentcohort`; the CLI command it installs is
28
+ > just `agentcohort`.
29
+
30
+ ## Quick start
31
+
32
+ From the root of the project you want to equip:
33
+
34
+ ```bash
35
+ agentcohort init
36
+ ```
37
+
38
+ Then open Claude Code in that project and run:
39
+
40
+ ```
41
+ /auto-flow <describe your task, bug, or paste a diff>
42
+ ```
43
+
44
+ `/auto-flow` classifies the work and routes it to the right pipeline.
45
+
46
+ ### Commands
47
+
48
+ | Command | What it does |
49
+ |---|---|
50
+ | `agentcohort init` | Install agents, commands and routing rules into the current project. |
51
+ | `agentcohort init --yes` | Non-interactive. Safe defaults (see below). |
52
+ | `agentcohort init --dry-run` | Print exactly what *would* change. Writes nothing. |
53
+ | `agentcohort init --force` | Overwrite conflicts / replace the routing section without prompting. |
54
+ | `agentcohort init --backup` | Always back up a file before overwriting it. |
55
+ | `agentcohort --version` | Print the version. |
56
+ | `agentcohort --help` | Show help. |
57
+
58
+ Flags compose: `agentcohort init --yes --backup`, `--force --backup`, etc.
59
+
60
+ ## What files are created
61
+
62
+ ```
63
+ .claude/
64
+ agents/
65
+ repo-scout.md solution-architect.md feature-planner.md
66
+ feature-implementer.md test-verifier.md final-reviewer.md
67
+ bug-hunter.md root-cause-analyst.md reproduction-engineer.md
68
+ regression-guard.md bug-fixer.md
69
+ performance-hunter.md perf-optimizer.md perf-reviewer.md
70
+ expert-council.md
71
+ commands/
72
+ auto-flow.md dev-flow.md bug-audit.md bug-fix-approved.md
73
+ perf-hunt.md review-diff.md fix-blockers.md
74
+ CLAUDE.md # a "# Agentcohort Routing Rules" section
75
+ ```
76
+
77
+ A full example tree is in [`examples/generated-claude/`](./examples/generated-claude).
78
+
79
+ ## The philosophy
80
+
81
+ **Core:** Explore → Architect → Plan → Implement → Test → Review
82
+
83
+ **Bugs:** Hunt → Evidence → Root Cause → Expert Council → **Human Approval** →
84
+ Fix → Regression Test → Verify → Review
85
+
86
+ **Performance:** Measure/Evidence → Bottleneck → Safe Optimization → Verify →
87
+ Performance Review
88
+
89
+ Every agent operates at a top-1% principal/staff standard: root-cause first,
90
+ production-grade correctness, no shallow fixes, no fixing without evidence, and
91
+ **a bug audit never fixes** — it produces a recommendation and stops at a human
92
+ approval gate.
93
+
94
+ ## Using the workflow commands (inside Claude Code)
95
+
96
+ | Command | Pipeline | Use it for |
97
+ |---|---|---|
98
+ | `/auto-flow` | classify → route | When unsure — it picks the flow. |
99
+ | `/dev-flow` | scout → architect\* → planner → implementer → test-verifier → final-reviewer | Features & refactors. |
100
+ | `/bug-audit` | bug-hunter → root-cause-analyst → reproduction-engineer → expert-council | Bugs / regressions / bad data / stability. **No fixing.** |
101
+ | `/bug-fix-approved` | bug-fixer → regression-guard → test-verifier → final-reviewer | Implement a fix you already approved. |
102
+ | `/perf-hunt` | performance-hunter → architect\* → perf-optimizer → test-verifier → perf-reviewer | Slowness / bottlenecks. |
103
+ | `/review-diff` | final-reviewer | Review the current diff/PR. |
104
+ | `/fix-blockers` | feature-implementer → test-verifier | Fix only the blockers a review listed. |
105
+
106
+ \* the architect stage runs only when the change is architecture-sensitive.
107
+
108
+ ### Model strategy
109
+
110
+ - **Haiku** — cheap exploration / scouting.
111
+ - **Sonnet** — implementation, testing, bug & performance hunting.
112
+ - **Opus** — architecture, root-cause analysis, expert council, final review.
113
+
114
+ ## Customizing agents
115
+
116
+ The installed files are plain Markdown and **yours to edit**:
117
+
118
+ - Tune any agent in `.claude/agents/*.md` (role, rules, output format, the
119
+ `model:`/`tools:` frontmatter).
120
+ - Adjust a pipeline in `.claude/commands/*.md`.
121
+ - Put **your own** project notes in `CLAUDE.md` *outside* the
122
+ `# Agentcohort Routing Rules` section — that section is owned by the tool and
123
+ may be updated by a future `init`; everything else is never touched.
124
+
125
+ Re-running `agentcohort init` later will detect your edits as conflicts and ask
126
+ before changing them (or back them up with `--backup`).
127
+
128
+ ## Safety notes
129
+
130
+ `agentcohort` is conservative by design:
131
+
132
+ - **Never deletes** your files.
133
+ - **Never silently overwrites.** Existing, differing files trigger a prompt
134
+ (skip / overwrite / backup + overwrite), or an explicit flag.
135
+ - **Idempotent.** Re-running on identical content reports *unchanged* and does
136
+ nothing.
137
+ - **CLAUDE.md is surgical.** A missing file is created; a file without our
138
+ section gets the section *appended* (your content preserved); an existing,
139
+ differing section is **left alone** in non-interactive mode (use `--force`
140
+ to update it). Only the delimited section is ever touched.
141
+ - **`--yes` safe defaults:** new files created; conflicting files
142
+ **backed up then updated**; an existing CLAUDE.md routing section **left
143
+ untouched**.
144
+ - **`--dry-run`** performs zero writes and zero backups.
145
+ - Backups are written next to the original as
146
+ `&lt;file&gt;.backup-YYYYMMDD-HHMMSS` and never overwrite an existing backup.
147
+ - Cross-platform (Windows/macOS/Linux), zero runtime dependencies, no
148
+ shell-specific behavior.
149
+
150
+ ## Development
151
+
152
+ ```bash
153
+ npm install
154
+ npm run build # tsc -> dist/, then copies templates
155
+ npm test # vitest
156
+ ```
157
+
158
+ ## Releases
159
+
160
+ Publishing is automated. **The version in `package.json` is the version that
161
+ gets published.** Every push to `main` runs the
162
+ [`Release`](.github/workflows/release.yml) workflow, which:
163
+
164
+ 1. installs, builds and runs the full test suite;
165
+ 2. publishes the **current** `package.json` version to npm —
166
+ https://www.npmjs.com/package/agentcohort (so the very first
167
+ release is exactly `0.1.0`, nothing skipped);
168
+ 3. creates the annotated git tag `vX.Y.Z` on the published commit;
169
+ 4. bumps to the next dev version (`patch` by default) and pushes a
170
+ `chore(release): published vX.Y.Z, open vX.Y.(Z+1) [skip ci]` commit back
171
+ to `main`.
172
+
173
+ So: to cut a normal release, just push to `main`. To release a `minor`/`major`
174
+ instead, bump `package.json` yourself in a regular commit before pushing (or
175
+ use the *Run workflow* button to control how the **next** pending version is
176
+ opened). If the pending version is already on npm, publish is skipped and the
177
+ job still succeeds (safe re-runs). The `[skip ci]` marker stops the release
178
+ commit from re-triggering the workflow (no publish loop).
179
+
180
+ **One-time setup:** add an npm **Automation** access token as the repository
181
+ secret `NPM_TOKEN` (GitHub → Settings → Secrets and variables → Actions →
182
+ *New repository secret*). Until that secret exists, the workflow's *Publish*
183
+ step will fail while build/test still pass.
184
+
185
+ ## License
186
+
187
+ MIT
package/dist/args.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ export interface ParsedArgs {
2
+ command: string | null;
3
+ yes: boolean;
4
+ dryRun: boolean;
5
+ force: boolean;
6
+ backup: boolean;
7
+ help: boolean;
8
+ version: boolean;
9
+ unknown: string[];
10
+ }
11
+ /** Pure, deterministic argument parser. Unknown tokens are collected, not thrown. */
12
+ export declare function parseArgs(argv: string[]): ParsedArgs;
13
+ export declare function helpText(): string;
package/dist/args.js ADDED
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseArgs = parseArgs;
4
+ exports.helpText = helpText;
5
+ const logger_1 = require("./logger");
6
+ const FLAGS = {
7
+ '--yes': 'yes',
8
+ '-y': 'yes',
9
+ '--dry-run': 'dryRun',
10
+ '--force': 'force',
11
+ '--backup': 'backup',
12
+ '--help': 'help',
13
+ '-h': 'help',
14
+ '--version': 'version',
15
+ '-v': 'version',
16
+ };
17
+ /** Pure, deterministic argument parser. Unknown tokens are collected, not thrown. */
18
+ function parseArgs(argv) {
19
+ const parsed = {
20
+ command: null,
21
+ yes: false,
22
+ dryRun: false,
23
+ force: false,
24
+ backup: false,
25
+ help: false,
26
+ version: false,
27
+ unknown: [],
28
+ };
29
+ for (const arg of argv) {
30
+ if (arg.startsWith('-')) {
31
+ const key = FLAGS[arg];
32
+ if (key) {
33
+ parsed[key] = true;
34
+ }
35
+ else {
36
+ parsed.unknown.push(arg);
37
+ }
38
+ }
39
+ else if (parsed.command === null) {
40
+ parsed.command = arg;
41
+ }
42
+ else {
43
+ parsed.unknown.push(arg);
44
+ }
45
+ }
46
+ return parsed;
47
+ }
48
+ function helpText() {
49
+ const b = (s) => (0, logger_1.paint)(s, 'bold');
50
+ return `
51
+ ${b('agentcohort')} — install a principal/staff-level Claude Code AI engineering org.
52
+
53
+ ${b('USAGE')}
54
+ agentcohort <command> [options]
55
+
56
+ ${b('COMMANDS')}
57
+ init Install agents, workflow commands and routing rules
58
+ into ./.claude and ./CLAUDE.md of the current project.
59
+
60
+ ${b('OPTIONS')}
61
+ --yes, -y Non-interactive. Safe defaults: new files created;
62
+ existing conflicting files backed up then updated;
63
+ an existing CLAUDE.md routing section is left untouched.
64
+ --dry-run Print exactly what would be created/updated. Writes
65
+ nothing. Implies non-interactive (safe defaults).
66
+ --force Overwrite conflicting files / replace the routing
67
+ section without prompting (no backup unless --backup).
68
+ --backup Always back up a file before overwriting it.
69
+ Backup name: <file>.backup-YYYYMMDD-HHMMSS
70
+ --help, -h Show this help.
71
+ --version, -v Print the version.
72
+
73
+ ${b('WHAT GETS INSTALLED')}
74
+ .claude/agents/ 15 subagents (scout, architect, planner, implementer,
75
+ reviewer, bug-hunter, root-cause-analyst, ...).
76
+ .claude/commands/ 7 workflow commands.
77
+ CLAUDE.md Appends a "# Agentcohort Routing Rules" section.
78
+
79
+ ${b('WORKFLOW COMMANDS (run inside Claude Code)')}
80
+ /auto-flow Classify the task and pick the right workflow.
81
+ /dev-flow Feature/refactor: scout -> architect -> plan ->
82
+ implement -> test -> review.
83
+ /bug-audit Investigate ONLY (no fixes): hunt -> evidence ->
84
+ root cause -> expert council -> recommendation.
85
+ /bug-fix-approved Fix an approved bug: fix -> regression -> test -> review.
86
+ /perf-hunt Measure -> bottleneck -> safe optimize -> verify -> review.
87
+ /review-diff Final reviewer on the current diff.
88
+ /fix-blockers Fix only listed blockers, then verify.
89
+
90
+ Existing files are NEVER deleted and NEVER silently overwritten.
91
+ `;
92
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Pure helpers for managing the Agentcohort section inside a project CLAUDE.md.
3
+ *
4
+ * Design goals:
5
+ * - Never destroy the user's existing CLAUDE.md content.
6
+ * - Only ever touch our own clearly-delimited section.
7
+ * - Preserve the rest of the file byte-for-byte (no global EOL rewrites):
8
+ * we splice by string index instead of split/join.
9
+ * - Be fenced-code-block aware so a `# ...` line inside ``` ``` is not
10
+ * mistaken for a real heading boundary.
11
+ */
12
+ export declare const SECTION_TITLE = "# Agentcohort Routing Rules";
13
+ /** Index where our section heading line starts, fenced-code-aware. -1 if absent. */
14
+ export declare function findSectionStart(content: string): number;
15
+ export declare function hasSection(content: string): boolean;
16
+ /** The exact current section block (heading through just before next heading/EOF). */
17
+ export declare function extractSection(content: string): string | null;
18
+ /** True when the existing section already equals the template (ignoring edge whitespace). */
19
+ export declare function sectionMatches(content: string, sectionMarkdown: string): boolean;
20
+ export type UpsertMode = 'append' | 'replace';
21
+ export interface UpsertResult {
22
+ result: string;
23
+ mode: UpsertMode;
24
+ }
25
+ /**
26
+ * Insert or replace the Agentcohort section.
27
+ * - Absent -> appended at the end, separated by a blank line.
28
+ * - Present -> the existing section block is replaced in place; everything
29
+ * before and after is preserved verbatim.
30
+ */
31
+ export declare function upsertSection(content: string, sectionMarkdown: string): UpsertResult;
32
+ /** Minimal CLAUDE.md created when the project has none. */
33
+ export declare function buildInitialClaudeMd(sectionMarkdown: string): string;
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ /**
3
+ * Pure helpers for managing the Agentcohort section inside a project CLAUDE.md.
4
+ *
5
+ * Design goals:
6
+ * - Never destroy the user's existing CLAUDE.md content.
7
+ * - Only ever touch our own clearly-delimited section.
8
+ * - Preserve the rest of the file byte-for-byte (no global EOL rewrites):
9
+ * we splice by string index instead of split/join.
10
+ * - Be fenced-code-block aware so a `# ...` line inside ``` ``` is not
11
+ * mistaken for a real heading boundary.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.SECTION_TITLE = void 0;
15
+ exports.findSectionStart = findSectionStart;
16
+ exports.hasSection = hasSection;
17
+ exports.extractSection = extractSection;
18
+ exports.sectionMatches = sectionMatches;
19
+ exports.upsertSection = upsertSection;
20
+ exports.buildInitialClaudeMd = buildInitialClaudeMd;
21
+ exports.SECTION_TITLE = '# Agentcohort Routing Rules';
22
+ function* iterateLines(content) {
23
+ let i = 0;
24
+ const n = content.length;
25
+ while (i <= n) {
26
+ let nl = content.indexOf('\n', i);
27
+ if (nl === -1)
28
+ nl = n;
29
+ let contentEnd = nl;
30
+ if (contentEnd > i && content[contentEnd - 1] === '\r')
31
+ contentEnd -= 1;
32
+ yield {
33
+ start: i,
34
+ contentEnd,
35
+ next: nl === n ? n : nl + 1,
36
+ text: content.slice(i, contentEnd),
37
+ };
38
+ if (nl === n)
39
+ break;
40
+ i = nl + 1;
41
+ }
42
+ }
43
+ const FENCE_RE = /^\s{0,3}(```+|~~~+)/;
44
+ const TOP_HEADING_RE = /^# (?!#)/; // exactly one '#', then space, not '##'
45
+ /** Index where our section heading line starts, fenced-code-aware. -1 if absent. */
46
+ function findSectionStart(content) {
47
+ let inFence = false;
48
+ let fenceToken = '';
49
+ for (const line of iterateLines(content)) {
50
+ const fence = FENCE_RE.exec(line.text);
51
+ if (fence) {
52
+ const token = fence[1][0]; // '`' or '~'
53
+ if (!inFence) {
54
+ inFence = true;
55
+ fenceToken = token;
56
+ }
57
+ else if (token === fenceToken) {
58
+ inFence = false;
59
+ fenceToken = '';
60
+ }
61
+ continue;
62
+ }
63
+ if (inFence)
64
+ continue;
65
+ if (line.text.trimEnd() === exports.SECTION_TITLE)
66
+ return line.start;
67
+ }
68
+ return -1;
69
+ }
70
+ /** Index where the section ends (start of the next top-level heading) or EOF. */
71
+ function findSectionEnd(content, sectionStart) {
72
+ let inFence = false;
73
+ let fenceToken = '';
74
+ let passedHeading = false;
75
+ for (const line of iterateLines(content)) {
76
+ if (line.start < sectionStart)
77
+ continue;
78
+ if (!passedHeading) {
79
+ // This is our own heading line; skip it then start scanning.
80
+ passedHeading = true;
81
+ continue;
82
+ }
83
+ const fence = FENCE_RE.exec(line.text);
84
+ if (fence) {
85
+ const token = fence[1][0];
86
+ if (!inFence) {
87
+ inFence = true;
88
+ fenceToken = token;
89
+ }
90
+ else if (token === fenceToken) {
91
+ inFence = false;
92
+ fenceToken = '';
93
+ }
94
+ continue;
95
+ }
96
+ if (inFence)
97
+ continue;
98
+ if (TOP_HEADING_RE.test(line.text))
99
+ return line.start;
100
+ }
101
+ return content.length;
102
+ }
103
+ function hasSection(content) {
104
+ return findSectionStart(content) !== -1;
105
+ }
106
+ /** The exact current section block (heading through just before next heading/EOF). */
107
+ function extractSection(content) {
108
+ const start = findSectionStart(content);
109
+ if (start === -1)
110
+ return null;
111
+ const end = findSectionEnd(content, start);
112
+ return content.slice(start, end);
113
+ }
114
+ /** True when the existing section already equals the template (ignoring edge whitespace). */
115
+ function sectionMatches(content, sectionMarkdown) {
116
+ const current = extractSection(content);
117
+ if (current === null)
118
+ return false;
119
+ return current.trim() === sectionMarkdown.trim();
120
+ }
121
+ /**
122
+ * Insert or replace the Agentcohort section.
123
+ * - Absent -> appended at the end, separated by a blank line.
124
+ * - Present -> the existing section block is replaced in place; everything
125
+ * before and after is preserved verbatim.
126
+ */
127
+ function upsertSection(content, sectionMarkdown) {
128
+ const body = sectionMarkdown.replace(/\s+$/, '') + '\n';
129
+ const start = findSectionStart(content);
130
+ if (start === -1) {
131
+ const base = content.length === 0 ? '' : content.replace(/\s+$/, '') + '\n';
132
+ const separator = base === '' ? '' : '\n';
133
+ return { result: base + separator + body, mode: 'append' };
134
+ }
135
+ const end = findSectionEnd(content, start);
136
+ const prefix = content.slice(0, start);
137
+ const suffix = content.slice(end);
138
+ let result = prefix + body;
139
+ if (suffix.length > 0) {
140
+ // Ensure exactly one blank line between our section and the next heading.
141
+ result += '\n' + suffix.replace(/^\n+/, '');
142
+ }
143
+ return { result, mode: 'replace' };
144
+ }
145
+ /** Minimal CLAUDE.md created when the project has none. */
146
+ function buildInitialClaudeMd(sectionMarkdown) {
147
+ const header = [
148
+ '# Project Guidance for Claude Code',
149
+ '',
150
+ 'This file is read by Claude Code at the start of every session.',
151
+ 'The section below was installed by `agentcohort` and wires up the',
152
+ 'AI software-engineering organization (agents + workflows).',
153
+ '',
154
+ ].join('\n');
155
+ return upsertSection(header, sectionMarkdown).result;
156
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(argv?: string[]): Promise<number>;
package/dist/cli.js ADDED
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.main = main;
5
+ const installer_1 = require("./installer");
6
+ const prompt_1 = require("./prompt");
7
+ const logger_1 = require("./logger");
8
+ const paths_1 = require("./paths");
9
+ const args_1 = require("./args");
10
+ function printSummary(result) {
11
+ const counts = new Map();
12
+ let backups = 0;
13
+ for (const a of result.actions) {
14
+ counts.set(a.disposition, (counts.get(a.disposition) ?? 0) + 1);
15
+ if (a.backupPath)
16
+ backups += 1;
17
+ }
18
+ const part = (label, key) => {
19
+ const n = counts.get(key) ?? 0;
20
+ return n > 0 ? `${n} ${label}` : null;
21
+ };
22
+ const segments = [
23
+ part('created', 'created'),
24
+ part('updated', 'overwritten'),
25
+ part('section appended', 'appended-section'),
26
+ part('section updated', 'replaced-section'),
27
+ part('unchanged', 'unchanged'),
28
+ part('skipped', 'skipped'),
29
+ ].filter((x) => x !== null);
30
+ process.stdout.write('\n');
31
+ const head = result.dryRun ? 'Dry run complete' : 'Agentcohort installed';
32
+ process.stdout.write(`${(0, logger_1.paint)(head, 'bold', 'green')} ${segments.join(' · ')}\n`);
33
+ if (backups > 0) {
34
+ process.stdout.write(`${(0, logger_1.paint)('•', 'cyan')} ${backups} backup(s) written alongside the originals.\n`);
35
+ }
36
+ if (!result.dryRun && ((counts.get('created') ?? 0) || (counts.get('overwritten') ?? 0))) {
37
+ process.stdout.write(`${(0, logger_1.paint)('•', 'cyan')} Next: open Claude Code in this project and run ${(0, logger_1.paint)('/auto-flow', 'bold')}.\n`);
38
+ }
39
+ if (result.dryRun) {
40
+ process.stdout.write(`${(0, logger_1.paint)('•', 'cyan')} Re-run without ${(0, logger_1.paint)('--dry-run', 'bold')} to apply.\n`);
41
+ }
42
+ }
43
+ async function main(argv = process.argv.slice(2)) {
44
+ const args = (0, args_1.parseArgs)(argv);
45
+ if (args.unknown.length > 0) {
46
+ process.stderr.write((0, logger_1.paint)(`✗ Unknown argument(s): ${args.unknown.join(', ')}\n`, 'red'));
47
+ process.stdout.write((0, args_1.helpText)() + '\n');
48
+ return 1;
49
+ }
50
+ if (args.version) {
51
+ process.stdout.write((0, paths_1.getVersion)() + '\n');
52
+ return 0;
53
+ }
54
+ if (args.help || args.command === null) {
55
+ process.stdout.write((0, args_1.helpText)() + '\n');
56
+ return 0;
57
+ }
58
+ if (args.command !== 'init') {
59
+ process.stderr.write((0, logger_1.paint)(`✗ Unknown command: ${args.command}\n`, 'red'));
60
+ process.stdout.write((0, args_1.helpText)() + '\n');
61
+ return 1;
62
+ }
63
+ const stdinTTY = Boolean(process.stdin.isTTY);
64
+ const stdoutTTY = Boolean(process.stdout.isTTY);
65
+ const interactive = !args.yes && !args.force && !args.dryRun && stdinTTY && stdoutTTY;
66
+ const logger = (0, logger_1.createLogger)();
67
+ let resolverHandle = null;
68
+ process.stdout.write((0, logger_1.paint)('\nagentcohort', 'bold', 'cyan') + (0, logger_1.paint)(` v${(0, paths_1.getVersion)()}\n`, 'gray'));
69
+ if (args.dryRun) {
70
+ logger.info('Dry run — no files will be written.');
71
+ }
72
+ else if (!interactive && !args.yes && !args.force) {
73
+ logger.info('Non-interactive environment detected — using safe defaults (like --yes).');
74
+ }
75
+ try {
76
+ if (interactive)
77
+ resolverHandle = (0, prompt_1.createInteractiveResolver)();
78
+ const result = await (0, installer_1.runInit)({
79
+ cwd: process.cwd(),
80
+ yes: args.yes,
81
+ dryRun: args.dryRun,
82
+ force: args.force,
83
+ backup: args.backup,
84
+ interactive,
85
+ resolver: resolverHandle?.resolve,
86
+ logger,
87
+ });
88
+ printSummary(result);
89
+ return 0;
90
+ }
91
+ catch (err) {
92
+ const message = err instanceof Error ? err.message : String(err);
93
+ process.stderr.write((0, logger_1.paint)(`\n✗ ${message}\n`, 'red'));
94
+ return 1;
95
+ }
96
+ finally {
97
+ resolverHandle?.close();
98
+ }
99
+ }
100
+ // Auto-run only when executed as the bin (not when imported by tests/tooling).
101
+ if (require.main === module) {
102
+ main()
103
+ .then((code) => process.exit(code))
104
+ .catch((err) => {
105
+ process.stderr.write((0, logger_1.paint)(`\n✗ Fatal: ${String(err)}\n`, 'red'));
106
+ process.exit(1);
107
+ });
108
+ }
@@ -0,0 +1,15 @@
1
+ /** Read a file's UTF-8 content, or null if it does not exist. */
2
+ export declare function readIfExists(path: string): string | null;
3
+ /** Exact byte-for-byte comparison (drives idempotency: identical => "unchanged"). */
4
+ export declare function contentEquals(a: string, b: string): boolean;
5
+ /**
6
+ * Backup filename suffix using LOCAL time: `backup-YYYYMMDD-HHMMSS`.
7
+ * Local time is intentional so the timestamp matches the user's wall clock.
8
+ */
9
+ export declare function formatBackupSuffix(date: Date): string;
10
+ /** Absolute path of the backup file for `target` at `date`. */
11
+ export declare function backupPathFor(target: string, date: Date): string;
12
+ /** Copy an existing file to its backup path. Never deletes the original. */
13
+ export declare function backupFile(target: string, backupPath: string): void;
14
+ /** Create parent directories (recursive) then write the file as UTF-8. */
15
+ export declare function writeFileEnsuringDir(path: string, content: string): void;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readIfExists = readIfExists;
4
+ exports.contentEquals = contentEquals;
5
+ exports.formatBackupSuffix = formatBackupSuffix;
6
+ exports.backupPathFor = backupPathFor;
7
+ exports.backupFile = backupFile;
8
+ exports.writeFileEnsuringDir = writeFileEnsuringDir;
9
+ const node_fs_1 = require("node:fs");
10
+ const node_path_1 = require("node:path");
11
+ /** Read a file's UTF-8 content, or null if it does not exist. */
12
+ function readIfExists(path) {
13
+ if (!(0, node_fs_1.existsSync)(path))
14
+ return null;
15
+ return (0, node_fs_1.readFileSync)(path, 'utf8');
16
+ }
17
+ /** Exact byte-for-byte comparison (drives idempotency: identical => "unchanged"). */
18
+ function contentEquals(a, b) {
19
+ return a === b;
20
+ }
21
+ /** Two-digit / four-digit zero padded. */
22
+ function pad(n, width = 2) {
23
+ return String(n).padStart(width, '0');
24
+ }
25
+ /**
26
+ * Backup filename suffix using LOCAL time: `backup-YYYYMMDD-HHMMSS`.
27
+ * Local time is intentional so the timestamp matches the user's wall clock.
28
+ */
29
+ function formatBackupSuffix(date) {
30
+ const y = pad(date.getFullYear(), 4);
31
+ const mo = pad(date.getMonth() + 1);
32
+ const d = pad(date.getDate());
33
+ const h = pad(date.getHours());
34
+ const mi = pad(date.getMinutes());
35
+ const s = pad(date.getSeconds());
36
+ return `backup-${y}${mo}${d}-${h}${mi}${s}`;
37
+ }
38
+ /** Absolute path of the backup file for `target` at `date`. */
39
+ function backupPathFor(target, date) {
40
+ return `${target}.${formatBackupSuffix(date)}`;
41
+ }
42
+ /** Copy an existing file to its backup path. Never deletes the original. */
43
+ function backupFile(target, backupPath) {
44
+ (0, node_fs_1.copyFileSync)(target, backupPath);
45
+ }
46
+ /** Create parent directories (recursive) then write the file as UTF-8. */
47
+ function writeFileEnsuringDir(path, content) {
48
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
49
+ (0, node_fs_1.writeFileSync)(path, content, 'utf8');
50
+ }