@chief-clancy/brief 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/bin/brief.js +230 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/installer/install.d.ts +64 -0
- package/dist/installer/install.d.ts.map +1 -0
- package/dist/installer/install.js +135 -0
- package/dist/installer/install.js.map +1 -0
- package/package.json +48 -0
- package/src/agents/agents.test.ts +49 -0
- package/src/agents/devils-advocate.md +54 -0
- package/src/commands/brief.md +29 -0
- package/src/commands/commands.test.ts +39 -0
- package/src/workflows/brief.md +1330 -0
- package/src/workflows/workflows.test.ts +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alex Clapperton
|
|
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/bin/brief.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Brief installer — CLI entry point for `npx @chief-clancy/brief`.
|
|
5
|
+
*
|
|
6
|
+
* Self-contained installer that copies the brief slash command, workflow,
|
|
7
|
+
* and devil's advocate agent prompt to the Claude Code commands directory.
|
|
8
|
+
* No dependencies on @chief-clancy/core or @chief-clancy/terminal.
|
|
9
|
+
*/
|
|
10
|
+
import {
|
|
11
|
+
copyFileSync,
|
|
12
|
+
existsSync,
|
|
13
|
+
lstatSync,
|
|
14
|
+
mkdirSync,
|
|
15
|
+
readFileSync,
|
|
16
|
+
writeFileSync,
|
|
17
|
+
} from 'node:fs';
|
|
18
|
+
import { createRequire } from 'node:module';
|
|
19
|
+
import { dirname, join } from 'node:path';
|
|
20
|
+
import { createInterface } from 'node:readline';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// ANSI helpers (inlined — no external dependencies)
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
28
|
+
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
29
|
+
const blue = (s) => `\x1b[1;34m${s}\x1b[0m`;
|
|
30
|
+
const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
|
|
31
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
32
|
+
const red = (s) => `\x1b[31m${s}\x1b[0m`;
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Resolve package metadata and source directories
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
const require = createRequire(import.meta.url);
|
|
39
|
+
const pkg = require('../package.json');
|
|
40
|
+
|
|
41
|
+
const briefRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
42
|
+
|
|
43
|
+
const sources = {
|
|
44
|
+
commandsDir: join(briefRoot, 'src', 'commands'),
|
|
45
|
+
workflowsDir: join(briefRoot, 'src', 'workflows'),
|
|
46
|
+
agentsDir: join(briefRoot, 'src', 'agents'),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Environment
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
const homeDir = process.env.HOME ?? process.env.USERPROFILE;
|
|
54
|
+
|
|
55
|
+
if (!homeDir) {
|
|
56
|
+
console.error(red(' Error: Could not determine home directory.'));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// CLI flag parsing
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
/** @param {readonly string[]} args */
|
|
65
|
+
function parseFlag(args) {
|
|
66
|
+
if (args.includes('--global')) return 'global';
|
|
67
|
+
if (args.includes('--local')) return 'local';
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const flag = parseFlag(process.argv.slice(2));
|
|
72
|
+
const nonInteractive = flag !== null;
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Prompt helpers
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
79
|
+
|
|
80
|
+
/** @param {string} label */
|
|
81
|
+
const ask = (label) => new Promise((resolve) => rl.question(label, resolve));
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Installer
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
/** @param {string} path */
|
|
88
|
+
function rejectSymlink(path) {
|
|
89
|
+
try {
|
|
90
|
+
if (lstatSync(path).isSymbolicLink()) {
|
|
91
|
+
throw new Error(`Symlink rejected: ${path}`);
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
if (err.code !== 'ENOENT') throw err;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** @param {string} src @param {string} dest */
|
|
99
|
+
function copyChecked(src, dest) {
|
|
100
|
+
rejectSymlink(dest);
|
|
101
|
+
copyFileSync(src, dest);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Inline workflow @-references into command files (global mode only). */
|
|
105
|
+
function inlineWorkflow(commandsDest, workflowsDest) {
|
|
106
|
+
const WORKFLOW_REF = /^@\.claude\/clancy\/workflows\/([^/\\]+\.md)\r?$/gm;
|
|
107
|
+
const cmdPath = join(commandsDest, 'brief.md');
|
|
108
|
+
const content = readFileSync(cmdPath, 'utf8');
|
|
109
|
+
const resolved = content.replace(WORKFLOW_REF, (match, fileName) => {
|
|
110
|
+
const wfPath = join(workflowsDest, fileName);
|
|
111
|
+
if (!existsSync(wfPath)) return match;
|
|
112
|
+
return readFileSync(wfPath, 'utf8');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (resolved !== content) {
|
|
116
|
+
rejectSymlink(cmdPath);
|
|
117
|
+
writeFileSync(cmdPath, resolved);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Mode selection
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
const MODE_MAP = { 1: 'global', 2: 'local', global: 'global', local: 'local' };
|
|
126
|
+
|
|
127
|
+
/** Prompt the user for global/local and return the mode (or exit on bad input). */
|
|
128
|
+
async function chooseMode() {
|
|
129
|
+
console.log(blue(' Where would you like to install?'));
|
|
130
|
+
console.log('');
|
|
131
|
+
console.log(
|
|
132
|
+
` 1) Global ${dim('(~/.claude)')} — available in all projects`,
|
|
133
|
+
);
|
|
134
|
+
console.log(` 2) Local ${dim('(./.claude)')} — this project only`);
|
|
135
|
+
console.log('');
|
|
136
|
+
const raw = await ask(cyan(' Choice [1]: '));
|
|
137
|
+
const key = (raw || '1').trim().toLowerCase();
|
|
138
|
+
const resolved = MODE_MAP[key];
|
|
139
|
+
|
|
140
|
+
if (resolved) return resolved;
|
|
141
|
+
|
|
142
|
+
console.log(red('\n Invalid choice. Run npx @chief-clancy/brief again.'));
|
|
143
|
+
rl.close();
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
// Main
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
async function main() {
|
|
152
|
+
console.log('');
|
|
153
|
+
console.log(blue(' Clancy Brief'));
|
|
154
|
+
console.log(
|
|
155
|
+
` ${bold(`v${pkg.version}`)}${dim(' Strategic brief generator for Claude Code.')}`,
|
|
156
|
+
);
|
|
157
|
+
console.log('');
|
|
158
|
+
|
|
159
|
+
const mode = flag ?? (await chooseMode());
|
|
160
|
+
|
|
161
|
+
if (flag) {
|
|
162
|
+
console.log(dim(` Mode: ${flag} (--${flag} flag)`));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const cwd = process.cwd();
|
|
166
|
+
const baseDir =
|
|
167
|
+
mode === 'global' ? join(homeDir, '.claude') : join(cwd, '.claude');
|
|
168
|
+
|
|
169
|
+
const commandsDest = join(baseDir, 'commands', 'clancy');
|
|
170
|
+
const workflowsDest = join(baseDir, 'clancy', 'workflows');
|
|
171
|
+
const agentsDest = join(baseDir, 'clancy', 'agents');
|
|
172
|
+
|
|
173
|
+
console.log(dim(` Installing to: ${commandsDest}`));
|
|
174
|
+
|
|
175
|
+
// Create directories
|
|
176
|
+
mkdirSync(commandsDest, { recursive: true });
|
|
177
|
+
mkdirSync(workflowsDest, { recursive: true });
|
|
178
|
+
mkdirSync(agentsDest, { recursive: true });
|
|
179
|
+
|
|
180
|
+
// Copy files
|
|
181
|
+
copyChecked(
|
|
182
|
+
join(sources.commandsDir, 'brief.md'),
|
|
183
|
+
join(commandsDest, 'brief.md'),
|
|
184
|
+
);
|
|
185
|
+
copyChecked(
|
|
186
|
+
join(sources.workflowsDir, 'brief.md'),
|
|
187
|
+
join(workflowsDest, 'brief.md'),
|
|
188
|
+
);
|
|
189
|
+
copyChecked(
|
|
190
|
+
join(sources.agentsDir, 'devils-advocate.md'),
|
|
191
|
+
join(agentsDest, 'devils-advocate.md'),
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// Inline workflows for global mode
|
|
195
|
+
if (mode === 'global') {
|
|
196
|
+
inlineWorkflow(commandsDest, workflowsDest);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Write version marker
|
|
200
|
+
const versionPath = join(commandsDest, 'VERSION.brief');
|
|
201
|
+
rejectSymlink(versionPath);
|
|
202
|
+
writeFileSync(versionPath, pkg.version);
|
|
203
|
+
|
|
204
|
+
// Success
|
|
205
|
+
console.log('');
|
|
206
|
+
console.log(green(' ✓ Clancy Brief installed successfully.'));
|
|
207
|
+
console.log('');
|
|
208
|
+
console.log(' Command available:');
|
|
209
|
+
console.log(
|
|
210
|
+
` ${cyan('/clancy:brief')} ${dim('Generate a strategic brief for a feature')}`,
|
|
211
|
+
);
|
|
212
|
+
console.log('');
|
|
213
|
+
console.log(' Next steps:');
|
|
214
|
+
console.log(` 1. Open a project in Claude Code`);
|
|
215
|
+
console.log(` 2. Run: ${cyan('/clancy:brief "Your feature idea"')}`);
|
|
216
|
+
console.log('');
|
|
217
|
+
console.log(
|
|
218
|
+
dim(' For the full pipeline (tickets, planning, implementation):'),
|
|
219
|
+
);
|
|
220
|
+
console.log(dim(` npx chief-clancy`));
|
|
221
|
+
console.log('');
|
|
222
|
+
|
|
223
|
+
rl.close();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
main().catch((err) => {
|
|
227
|
+
console.error(red(`\n Error: ${err.message}`));
|
|
228
|
+
rl.close();
|
|
229
|
+
process.exit(1);
|
|
230
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @chief-clancy/brief
|
|
3
|
+
*
|
|
4
|
+
* Strategic brief generator for Claude Code — grill, decompose, and
|
|
5
|
+
* document feature ideas. Ships the `/clancy:brief` slash command
|
|
6
|
+
* and a lightweight installer.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PACKAGE_NAME: "@chief-clancy/brief";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAG,qBAA8B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @chief-clancy/brief
|
|
3
|
+
*
|
|
4
|
+
* Strategic brief generator for Claude Code — grill, decompose, and
|
|
5
|
+
* document feature ideas. Ships the `/clancy:brief` slash command
|
|
6
|
+
* and a lightweight installer.
|
|
7
|
+
*/
|
|
8
|
+
export const PACKAGE_NAME = '@chief-clancy/brief';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,qBAA8B,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** The install target — global (`~/.claude`) or local (`./.claude`). */
|
|
2
|
+
export type BriefInstallMode = 'global' | 'local';
|
|
3
|
+
/** All resolved destination paths for a brief installation. */
|
|
4
|
+
export type BriefInstallPaths = {
|
|
5
|
+
readonly commandsDest: string;
|
|
6
|
+
readonly workflowsDest: string;
|
|
7
|
+
readonly agentsDest: string;
|
|
8
|
+
};
|
|
9
|
+
/** Source directories within the npm package. */
|
|
10
|
+
type BriefInstallSources = {
|
|
11
|
+
readonly commandsDir: string;
|
|
12
|
+
readonly workflowsDir: string;
|
|
13
|
+
readonly agentsDir: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* File system operations the installer performs.
|
|
17
|
+
*
|
|
18
|
+
* `mkdir` must be idempotent (recursive, ignoring EEXIST).
|
|
19
|
+
*/
|
|
20
|
+
type BriefInstallerFs = {
|
|
21
|
+
readonly exists: (path: string) => boolean;
|
|
22
|
+
readonly readFile: (path: string) => string;
|
|
23
|
+
readonly writeFile: (path: string, content: string) => void;
|
|
24
|
+
readonly mkdir: (path: string) => void;
|
|
25
|
+
readonly copyFile: (src: string, dest: string) => void;
|
|
26
|
+
/** Must return `false` (not throw) when the path does not exist. */
|
|
27
|
+
readonly isSymlink: (path: string) => boolean;
|
|
28
|
+
};
|
|
29
|
+
/** Options for {@link runBriefInstall}. */
|
|
30
|
+
export type RunBriefInstallOptions = {
|
|
31
|
+
readonly mode: BriefInstallMode;
|
|
32
|
+
readonly paths: BriefInstallPaths;
|
|
33
|
+
readonly sources: BriefInstallSources;
|
|
34
|
+
readonly version: string;
|
|
35
|
+
readonly fs: BriefInstallerFs;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Parse `--global` / `--local` from CLI arguments.
|
|
39
|
+
*
|
|
40
|
+
* @param args - CLI arguments (typically `process.argv.slice(2)`).
|
|
41
|
+
* @returns The install mode, or `null` if no flag was provided.
|
|
42
|
+
*/
|
|
43
|
+
export declare const parseBriefInstallFlag: (args: readonly string[]) => BriefInstallMode | null;
|
|
44
|
+
/**
|
|
45
|
+
* Compute all destination paths for a brief installation.
|
|
46
|
+
*
|
|
47
|
+
* @param mode - Global or local install target.
|
|
48
|
+
* @param homeDir - The user's home directory.
|
|
49
|
+
* @param cwd - The current working directory.
|
|
50
|
+
* @returns All resolved destination paths.
|
|
51
|
+
*/
|
|
52
|
+
export declare const resolveBriefInstallPaths: (mode: BriefInstallMode, homeDir: string, cwd: string) => BriefInstallPaths;
|
|
53
|
+
/**
|
|
54
|
+
* Run the brief installation pipeline.
|
|
55
|
+
*
|
|
56
|
+
* Copies command, workflow, and agent files to the target directory.
|
|
57
|
+
* For global mode, inlines workflow content into the command file.
|
|
58
|
+
* Writes a VERSION.brief marker with the package version.
|
|
59
|
+
*
|
|
60
|
+
* @param options - All dependencies and configuration.
|
|
61
|
+
*/
|
|
62
|
+
export declare const runBriefInstall: (options: RunBriefInstallOptions) => void;
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/installer/install.ts"],"names":[],"mappings":"AAaA,wEAAwE;AACxE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElD,+DAA+D;AAC/D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,iDAAiD;AACjD,KAAK,mBAAmB,GAAG;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,KAAK,gBAAgB,GAAG;IACtB,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC/C,CAAC;AAEF,2CAA2C;AAC3C,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,gBAAgB,CAAC;CAC/B,CAAC;AAsBF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAChC,MAAM,SAAS,MAAM,EAAE,KACtB,gBAAgB,GAAG,IAKrB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,GACnC,MAAM,gBAAgB,EACtB,SAAS,MAAM,EACf,KAAK,MAAM,KACV,iBASF,CAAC;AA4EF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,sBAAsB,KAAG,IA6BjE,CAAC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brief installer — self-contained module for `npx @chief-clancy/brief`.
|
|
3
|
+
*
|
|
4
|
+
* Copies the brief slash command, workflow, and devil's advocate agent
|
|
5
|
+
* prompt to the Claude Code commands directory. No hooks, bundles,
|
|
6
|
+
* manifests, role filtering, or board configuration.
|
|
7
|
+
*/
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Constants
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
/** Command files shipped with the brief package. */
|
|
13
|
+
const COMMAND_FILES = ['brief.md'];
|
|
14
|
+
/** Workflow files shipped with the brief package. */
|
|
15
|
+
const WORKFLOW_FILES = ['brief.md'];
|
|
16
|
+
/** Agent files shipped with the brief package. */
|
|
17
|
+
const AGENT_FILES = ['devils-advocate.md'];
|
|
18
|
+
/** Matches `@.claude/clancy/workflows/<filename>.md` on its own line. */
|
|
19
|
+
const WORKFLOW_REF = /^@\.claude\/clancy\/workflows\/([^/\\]+\.md)\r?$/gm;
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Pure functions
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/**
|
|
24
|
+
* Parse `--global` / `--local` from CLI arguments.
|
|
25
|
+
*
|
|
26
|
+
* @param args - CLI arguments (typically `process.argv.slice(2)`).
|
|
27
|
+
* @returns The install mode, or `null` if no flag was provided.
|
|
28
|
+
*/
|
|
29
|
+
export const parseBriefInstallFlag = (args) => {
|
|
30
|
+
if (args.includes('--global'))
|
|
31
|
+
return 'global';
|
|
32
|
+
if (args.includes('--local'))
|
|
33
|
+
return 'local';
|
|
34
|
+
return null;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Compute all destination paths for a brief installation.
|
|
38
|
+
*
|
|
39
|
+
* @param mode - Global or local install target.
|
|
40
|
+
* @param homeDir - The user's home directory.
|
|
41
|
+
* @param cwd - The current working directory.
|
|
42
|
+
* @returns All resolved destination paths.
|
|
43
|
+
*/
|
|
44
|
+
export const resolveBriefInstallPaths = (mode, homeDir, cwd) => {
|
|
45
|
+
const baseDir = mode === 'global' ? join(homeDir, '.claude') : join(cwd, '.claude');
|
|
46
|
+
return {
|
|
47
|
+
commandsDest: join(baseDir, 'commands', 'clancy'),
|
|
48
|
+
workflowsDest: join(baseDir, 'clancy', 'workflows'),
|
|
49
|
+
agentsDest: join(baseDir, 'clancy', 'agents'),
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Internal helpers
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
/** Throw if the given path is a symlink (ENOENT is swallowed). */
|
|
56
|
+
const rejectSymlink = (path, isSymlink) => {
|
|
57
|
+
if (isSymlink(path)) {
|
|
58
|
+
throw new Error(`Symlink rejected: ${path}`);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/** Copy a list of files from src dir to dest dir with symlink protection. */
|
|
62
|
+
const copyFiles = (options) => {
|
|
63
|
+
const { files, srcDir, destDir, fs } = options;
|
|
64
|
+
fs.mkdir(destDir);
|
|
65
|
+
files.forEach((file) => {
|
|
66
|
+
const src = join(srcDir, file);
|
|
67
|
+
const dest = join(destDir, file);
|
|
68
|
+
if (!fs.exists(src)) {
|
|
69
|
+
throw new Error(`Source file not found: ${src}`);
|
|
70
|
+
}
|
|
71
|
+
rejectSymlink(dest, fs.isSymlink);
|
|
72
|
+
fs.copyFile(src, dest);
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Inline workflow content into command files (global mode only).
|
|
77
|
+
*
|
|
78
|
+
* Replaces `@.claude/clancy/workflows/<name>.md` references with the
|
|
79
|
+
* actual workflow content so global installs work without project-relative
|
|
80
|
+
* @-file resolution.
|
|
81
|
+
*/
|
|
82
|
+
const inlineWorkflow = (commandsDest, workflowsDest, fs) => {
|
|
83
|
+
COMMAND_FILES.forEach((file) => {
|
|
84
|
+
const cmdPath = join(commandsDest, file);
|
|
85
|
+
const content = fs.readFile(cmdPath);
|
|
86
|
+
const resolved = content.replace(WORKFLOW_REF, (match, fileName) => {
|
|
87
|
+
const wfPath = join(workflowsDest, fileName);
|
|
88
|
+
return fs.exists(wfPath) ? fs.readFile(wfPath) : match;
|
|
89
|
+
});
|
|
90
|
+
if (resolved !== content) {
|
|
91
|
+
rejectSymlink(cmdPath, fs.isSymlink);
|
|
92
|
+
fs.writeFile(cmdPath, resolved);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Main pipeline
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
/**
|
|
100
|
+
* Run the brief installation pipeline.
|
|
101
|
+
*
|
|
102
|
+
* Copies command, workflow, and agent files to the target directory.
|
|
103
|
+
* For global mode, inlines workflow content into the command file.
|
|
104
|
+
* Writes a VERSION.brief marker with the package version.
|
|
105
|
+
*
|
|
106
|
+
* @param options - All dependencies and configuration.
|
|
107
|
+
*/
|
|
108
|
+
export const runBriefInstall = (options) => {
|
|
109
|
+
const { mode, paths, sources, version, fs } = options;
|
|
110
|
+
copyFiles({
|
|
111
|
+
files: COMMAND_FILES,
|
|
112
|
+
srcDir: sources.commandsDir,
|
|
113
|
+
destDir: paths.commandsDest,
|
|
114
|
+
fs,
|
|
115
|
+
});
|
|
116
|
+
copyFiles({
|
|
117
|
+
files: WORKFLOW_FILES,
|
|
118
|
+
srcDir: sources.workflowsDir,
|
|
119
|
+
destDir: paths.workflowsDest,
|
|
120
|
+
fs,
|
|
121
|
+
});
|
|
122
|
+
copyFiles({
|
|
123
|
+
files: AGENT_FILES,
|
|
124
|
+
srcDir: sources.agentsDir,
|
|
125
|
+
destDir: paths.agentsDest,
|
|
126
|
+
fs,
|
|
127
|
+
});
|
|
128
|
+
if (mode === 'global') {
|
|
129
|
+
inlineWorkflow(paths.commandsDest, paths.workflowsDest, fs);
|
|
130
|
+
}
|
|
131
|
+
const versionPath = join(paths.commandsDest, 'VERSION.brief');
|
|
132
|
+
rejectSymlink(versionPath, fs.isSymlink);
|
|
133
|
+
fs.writeFile(versionPath, version);
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/installer/install.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chief-clancy/brief",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Strategic brief generator for Claude Code — grill, decompose, and document feature ideas",
|
|
5
|
+
"author": "Alex Clapperton",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Pushedskydiver/chief-clancy.git",
|
|
10
|
+
"directory": "packages/brief"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/Pushedskydiver/chief-clancy#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/Pushedskydiver/chief-clancy/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"claude",
|
|
18
|
+
"claude-code",
|
|
19
|
+
"ai",
|
|
20
|
+
"brief",
|
|
21
|
+
"strategy",
|
|
22
|
+
"slash-commands",
|
|
23
|
+
"developer-tools"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"bin": {
|
|
27
|
+
"clancy-brief": "./bin/brief.js"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"bin",
|
|
38
|
+
"src/commands",
|
|
39
|
+
"src/workflows",
|
|
40
|
+
"src/agents"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "rm -rf dist tsconfig.build.tsbuildinfo && tsc --project tsconfig.build.json && tsc-alias --project tsconfig.build.json",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"lint": "eslint .",
|
|
46
|
+
"typecheck": "tsc --noEmit"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural tests for brief agent prompt files.
|
|
3
|
+
*
|
|
4
|
+
* Verifies the agents directory contains the expected prompt files
|
|
5
|
+
* that the brief workflow's AI-grill phase depends on.
|
|
6
|
+
*/
|
|
7
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
import { describe, expect, it } from 'vitest';
|
|
11
|
+
|
|
12
|
+
const AGENTS_DIR = fileURLToPath(new URL('.', import.meta.url));
|
|
13
|
+
|
|
14
|
+
const EXPECTED_AGENTS = ['devils-advocate.md'];
|
|
15
|
+
|
|
16
|
+
describe('agents directory structure', () => {
|
|
17
|
+
it('contains exactly the expected agent files', () => {
|
|
18
|
+
const agents = readdirSync(AGENTS_DIR)
|
|
19
|
+
.filter((f) => f.endsWith('.md'))
|
|
20
|
+
.sort();
|
|
21
|
+
|
|
22
|
+
expect(agents).toEqual([...EXPECTED_AGENTS].sort());
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('all agent files start with a heading', () => {
|
|
26
|
+
const issues: string[] = [];
|
|
27
|
+
|
|
28
|
+
EXPECTED_AGENTS.forEach((file) => {
|
|
29
|
+
const content = readFileSync(new URL(file, import.meta.url), 'utf8');
|
|
30
|
+
const firstLine = content.split('\n')[0]?.trim() ?? '';
|
|
31
|
+
|
|
32
|
+
if (!firstLine.startsWith('#')) {
|
|
33
|
+
issues.push(file);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(issues).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('devils-advocate.md contains confidence classification', () => {
|
|
41
|
+
const content = readFileSync(
|
|
42
|
+
new URL('devils-advocate.md', import.meta.url),
|
|
43
|
+
'utf8',
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
expect(content).toContain('Answerable');
|
|
47
|
+
expect(content).toContain('Open Questions');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Devil's Advocate Agent
|
|
2
|
+
|
|
3
|
+
You are the devil's advocate agent for Clancy's strategist role. Your job is to answer a list of clarifying questions about a feature idea by interrogating the codebase, board, and web — then classify each answer by confidence.
|
|
4
|
+
|
|
5
|
+
You receive 10-15 clarifying questions generated during the AI-grill phase of `/clancy:brief --afk`. You must answer them autonomously. Never ask the human for input — this runs in AFK mode with no human present.
|
|
6
|
+
|
|
7
|
+
## Instructions
|
|
8
|
+
|
|
9
|
+
1. Work through each question one at a time. For every question, investigate before answering — never guess.
|
|
10
|
+
2. Interrogate three sources in order of preference:
|
|
11
|
+
- **Codebase**: use Glob, Grep, and Read to explore affected areas, check `.clancy/docs/`, read existing patterns. Use real file paths and code snippets as evidence.
|
|
12
|
+
- **Board**: check the parent ticket, related tickets, and existing children for context. Look for conflicting requirements.
|
|
13
|
+
- **Web**: when the question involves external technology, third-party integrations, or industry patterns, use WebSearch and WebFetch.
|
|
14
|
+
3. Challenge your own answers. If the codebase says one thing but the ticket description says another, flag the conflict — do not silently pick one.
|
|
15
|
+
4. Follow self-generated follow-ups within the same pass. If answering a question raises a new sub-question, chase it to its conclusion before moving on. Example: "Should this support SSO?" → check codebase → find SAML provider → "SAML exists but should the new feature use SAML or add OIDC?" → check web → resolve or flag.
|
|
16
|
+
5. Be RELENTLESS. If the codebase doesn't clearly support a decision, don't manufacture confidence. Put it in Open Questions.
|
|
17
|
+
6. If a question is partially answerable, answer the part you can and flag the remainder as open.
|
|
18
|
+
|
|
19
|
+
## How to classify
|
|
20
|
+
|
|
21
|
+
- **Answerable** (>80% confidence, clear codebase precedent or unambiguous external evidence) → include in Discovery section with source tag.
|
|
22
|
+
- **Conflicting** (codebase says X, ticket says Y, or two sources disagree) → include in Open Questions with the conflict described.
|
|
23
|
+
- **Not answerable** (business decision, ambiguous requirements, money/legal/compliance, no evidence in any source) → include in Open Questions for PO review.
|
|
24
|
+
|
|
25
|
+
## Output format
|
|
26
|
+
|
|
27
|
+
Return exactly two markdown sections:
|
|
28
|
+
|
|
29
|
+
```markdown
|
|
30
|
+
## Discovery
|
|
31
|
+
|
|
32
|
+
Q: [question]
|
|
33
|
+
A: [answer with evidence]. (Source: codebase|board|web)
|
|
34
|
+
|
|
35
|
+
Q: [question]
|
|
36
|
+
A: [answer]. (Source: codebase)
|
|
37
|
+
|
|
38
|
+
## Open Questions
|
|
39
|
+
|
|
40
|
+
- [ ] [question that couldn't be resolved — with reason]
|
|
41
|
+
- [ ] [question with conflicting evidence — conflict described]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Every answer in Discovery must cite its source: `(Source: codebase)`, `(Source: board)`, `(Source: web)`, or `(Source: codebase, web)` for combined evidence.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Rules
|
|
49
|
+
|
|
50
|
+
- NEVER ask the human questions. You are running autonomously.
|
|
51
|
+
- Single pass — no multi-round conversation loop. But each question must be followed to its conclusion including any self-follow-ups.
|
|
52
|
+
- Every answer must include real file paths, code snippets, ticket references, or URLs as evidence. No hand-waving.
|
|
53
|
+
- When you find no evidence at all, say so explicitly and classify as Open Questions.
|
|
54
|
+
- Prefer specificity over breadth. One concrete file path beats three vague claims.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# /clancy:brief
|
|
2
|
+
|
|
3
|
+
Generate a strategic brief for a feature idea. Researches the codebase, grills you (or itself) on requirements, and produces a decomposition into actionable tickets.
|
|
4
|
+
|
|
5
|
+
Accepts optional arguments:
|
|
6
|
+
|
|
7
|
+
- **Board ticket:** `/clancy:brief PROJ-123`, `/clancy:brief #42`, `/clancy:brief ENG-42` — brief from a board ticket
|
|
8
|
+
- **Inline text:** `/clancy:brief "Add dark mode"` — brief from a description
|
|
9
|
+
- **From file:** `/clancy:brief --from docs/rfc.md` — brief from a local file
|
|
10
|
+
- **Batch mode:** `/clancy:brief 3` — brief up to 3 tickets from the queue
|
|
11
|
+
- **Fresh start:** `--fresh` — discard existing brief and start over
|
|
12
|
+
- **Force web research:** `--research` — include web research in analysis
|
|
13
|
+
- **AFK mode:** `--afk` — use AI-grill instead of human grill
|
|
14
|
+
- **Epic hint:** `--epic PROJ-50 "Add dark mode"` — set parent for approve step
|
|
15
|
+
- **List briefs:** `--list` — show inventory of existing briefs
|
|
16
|
+
|
|
17
|
+
Examples:
|
|
18
|
+
|
|
19
|
+
- `/clancy:brief` — interactive mode (prompt for idea)
|
|
20
|
+
- `/clancy:brief PROJ-200` — brief a Jira ticket
|
|
21
|
+
- `/clancy:brief #42` — brief a GitHub issue
|
|
22
|
+
- `/clancy:brief ENG-42` — brief a Linear issue
|
|
23
|
+
- `/clancy:brief "Add dark mode support"` — brief from inline text
|
|
24
|
+
- `/clancy:brief --afk PROJ-200` — brief with AI-grill (no human questions)
|
|
25
|
+
- `/clancy:brief --list` — show all briefs
|
|
26
|
+
|
|
27
|
+
@.claude/clancy/workflows/brief.md
|
|
28
|
+
|
|
29
|
+
Follow the brief workflow above. Research the codebase, conduct the grill phase, generate the brief, and save it locally. Do not create tickets — briefing only.
|