@chief-clancy/plan 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 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/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # @chief-clancy/plan
2
+
3
+ **Implementation planner for Claude Code.**
4
+
5
+ [![npm](https://img.shields.io/npm/v/@chief-clancy/plan?style=for-the-badge&color=cb3837)](https://www.npmjs.com/package/@chief-clancy/plan) [![License: MIT](https://img.shields.io/badge/License-MIT-blue?style=for-the-badge)](https://github.com/Pushedskydiver/chief-clancy/blob/main/LICENSE)
6
+
7
+ ```bash
8
+ npx @chief-clancy/plan
9
+ ```
10
+
11
+ Fetch backlog tickets from your board, explore the codebase, and generate structured implementation plans posted as comments for human review. Works with board credentials — no full pipeline required.
12
+
13
+ ## What it does
14
+
15
+ The `/clancy:plan` slash command explores your codebase, runs a feasibility scan, and produces a plan with:
16
+
17
+ - Summary and implementation approach
18
+ - Affected files table (file, change type, description)
19
+ - Test strategy (specific tests to write)
20
+ - Acceptance criteria (testable conditions)
21
+ - Dependencies and blockers
22
+ - Risks and considerations
23
+ - Size estimate (S/M/L)
24
+
25
+ ## How it works
26
+
27
+ 1. **Install:** `npx @chief-clancy/plan` — choose global or local
28
+ 2. **Configure board:** `/clancy:board-setup` — connect to your project board
29
+ 3. **Run:** `/clancy:plan PROJ-123` — plan a specific ticket
30
+
31
+ Plans are posted as comments on the ticket for human review.
32
+
33
+ ## Input modes
34
+
35
+ | Mode | Example | Board needed? |
36
+ | --------------- | ------------------------------------------- | --------------------------- |
37
+ | Specific ticket | `/clancy:plan PROJ-123`, `/clancy:plan #42` | Yes (`/clancy:board-setup`) |
38
+ | Batch | `/clancy:plan 3` | Yes (`/clancy:board-setup`) |
39
+ | Queue (default) | `/clancy:plan` | Yes (`/clancy:board-setup`) |
40
+
41
+ ## Flags
42
+
43
+ | Flag | Description |
44
+ | --------- | ------------------------------------ |
45
+ | `--afk` | Auto-confirm all prompts |
46
+ | `--fresh` | Discard existing plan and start over |
47
+
48
+ ## Board ticket mode
49
+
50
+ To plan from board tickets without installing the full pipeline:
51
+
52
+ 1. Run `/clancy:board-setup` in Claude Code
53
+ 2. Follow the prompts to configure your board credentials
54
+ 3. Run `/clancy:plan PROJ-123` (or your board's ticket format)
55
+
56
+ Credentials are stored in `.clancy/.env` and are per-project (not global).
57
+
58
+ Supported boards: Jira, GitHub Issues, Linear, Shortcut, Notion, Azure DevOps.
59
+
60
+ ## Full pipeline
61
+
62
+ For the complete development pipeline (brief, plan, implement, deliver), install the full Clancy package:
63
+
64
+ ```bash
65
+ npx chief-clancy
66
+ ```
67
+
68
+ ## Part of the Clancy monorepo
69
+
70
+ - [`chief-clancy`](https://www.npmjs.com/package/chief-clancy) — full pipeline (install, configure, implement, autopilot)
71
+ - [`@chief-clancy/brief`](https://www.npmjs.com/package/@chief-clancy/brief) — strategic brief generator
72
+ - [`@chief-clancy/terminal`](https://www.npmjs.com/package/@chief-clancy/terminal) — installer, slash commands, hooks, runners
73
+ - [`@chief-clancy/core`](https://www.npmjs.com/package/@chief-clancy/core) — board integrations, pipeline phases, schemas
74
+
75
+ ## Credits
76
+
77
+ Built on the [Ralph technique](https://ghuntley.com/ralph/) by Geoffrey Huntley. See [CREDITS.md](https://github.com/Pushedskydiver/chief-clancy/blob/main/CREDITS.md).
78
+
79
+ ## License
80
+
81
+ MIT — see [LICENSE](https://github.com/Pushedskydiver/chief-clancy/blob/main/LICENSE).
package/bin/plan.js ADDED
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Plan installer — CLI entry point for `npx @chief-clancy/plan`.
5
+ *
6
+ * Self-contained installer that copies the plan slash command and workflow
7
+ * 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 planRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
42
+
43
+ const sources = {
44
+ commandsDir: join(planRoot, 'src', 'commands'),
45
+ workflowsDir: join(planRoot, 'src', 'workflows'),
46
+ };
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // Environment
50
+ // ---------------------------------------------------------------------------
51
+
52
+ const homeDir = process.env.HOME ?? process.env.USERPROFILE;
53
+
54
+ if (!homeDir) {
55
+ console.error(red(' Error: Could not determine home directory.'));
56
+ process.exit(1);
57
+ }
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // CLI flag parsing
61
+ // ---------------------------------------------------------------------------
62
+
63
+ /** @param {readonly string[]} args */
64
+ function parseFlag(args) {
65
+ if (args.includes('--global')) return 'global';
66
+ if (args.includes('--local')) return 'local';
67
+ return null;
68
+ }
69
+
70
+ const flag = parseFlag(process.argv.slice(2));
71
+ const nonInteractive = flag !== null;
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // Prompt helpers
75
+ // ---------------------------------------------------------------------------
76
+
77
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
78
+
79
+ /** @param {string} label */
80
+ const ask = (label) => new Promise((resolve) => rl.question(label, resolve));
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // File lists (keep in sync with install.ts)
84
+ // ---------------------------------------------------------------------------
85
+
86
+ const COMMAND_FILES = ['board-setup.md', 'plan.md'];
87
+ const WORKFLOW_FILES = ['board-setup.md', 'plan.md'];
88
+
89
+ // ---------------------------------------------------------------------------
90
+ // Installer
91
+ // ---------------------------------------------------------------------------
92
+
93
+ /** @param {string} path */
94
+ function rejectSymlink(path) {
95
+ try {
96
+ if (lstatSync(path).isSymbolicLink()) {
97
+ throw new Error(`Symlink rejected: ${path}`);
98
+ }
99
+ } catch (err) {
100
+ if (err.code !== 'ENOENT') throw err;
101
+ }
102
+ }
103
+
104
+ /** @param {string} src @param {string} dest */
105
+ function copyChecked(src, dest) {
106
+ rejectSymlink(dest);
107
+ copyFileSync(src, dest);
108
+ }
109
+
110
+ /** Inline workflow @-references into command files (global mode only). */
111
+ function inlineWorkflows(commandsDest, workflowsDest) {
112
+ const WORKFLOW_REF = /^@\.claude\/clancy\/workflows\/([^/\\]+\.md)\r?$/gm;
113
+
114
+ COMMAND_FILES.forEach((file) => {
115
+ const cmdPath = join(commandsDest, file);
116
+ const content = readFileSync(cmdPath, 'utf8');
117
+ const resolved = content.replace(WORKFLOW_REF, (match, fileName) => {
118
+ const wfPath = join(workflowsDest, fileName);
119
+ if (!existsSync(wfPath)) return match;
120
+ return readFileSync(wfPath, 'utf8');
121
+ });
122
+
123
+ if (resolved !== content) {
124
+ rejectSymlink(cmdPath);
125
+ writeFileSync(cmdPath, resolved);
126
+ }
127
+ });
128
+ }
129
+
130
+ // ---------------------------------------------------------------------------
131
+ // Mode selection
132
+ // ---------------------------------------------------------------------------
133
+
134
+ const MODE_MAP = { 1: 'global', 2: 'local', global: 'global', local: 'local' };
135
+
136
+ /** Prompt the user for global/local and return the mode (or exit on bad input). */
137
+ async function chooseMode() {
138
+ console.log(blue(' Where would you like to install?'));
139
+ console.log('');
140
+ console.log(
141
+ ` 1) Global ${dim('(~/.claude)')} — available in all projects`,
142
+ );
143
+ console.log(` 2) Local ${dim('(./.claude)')} — this project only`);
144
+ console.log('');
145
+ const raw = await ask(cyan(' Choice [1]: '));
146
+ const key = (raw || '1').trim().toLowerCase();
147
+ const resolved = MODE_MAP[key];
148
+
149
+ if (resolved) return resolved;
150
+
151
+ console.log(red('\n Invalid choice. Run npx @chief-clancy/plan again.'));
152
+ rl.close();
153
+ process.exit(1);
154
+ }
155
+
156
+ // ---------------------------------------------------------------------------
157
+ // Main
158
+ // ---------------------------------------------------------------------------
159
+
160
+ async function main() {
161
+ console.log('');
162
+ console.log(blue(' Clancy Plan'));
163
+ console.log(
164
+ ` ${bold(`v${pkg.version}`)}${dim(' Implementation planner for Claude Code.')}`,
165
+ );
166
+ console.log('');
167
+
168
+ const mode = flag ?? (await chooseMode());
169
+
170
+ if (flag) {
171
+ console.log(dim(` Mode: ${flag} (--${flag} flag)`));
172
+ }
173
+
174
+ const cwd = process.cwd();
175
+ const baseDir =
176
+ mode === 'global' ? join(homeDir, '.claude') : join(cwd, '.claude');
177
+
178
+ const commandsDest = join(baseDir, 'commands', 'clancy');
179
+ const workflowsDest = join(baseDir, 'clancy', 'workflows');
180
+
181
+ console.log(dim(` Installing to: ${commandsDest}`));
182
+
183
+ // Create directories
184
+ mkdirSync(commandsDest, { recursive: true });
185
+ mkdirSync(workflowsDest, { recursive: true });
186
+
187
+ // Copy files
188
+ COMMAND_FILES.forEach((f) =>
189
+ copyChecked(join(sources.commandsDir, f), join(commandsDest, f)),
190
+ );
191
+ WORKFLOW_FILES.forEach((f) =>
192
+ copyChecked(join(sources.workflowsDir, f), join(workflowsDest, f)),
193
+ );
194
+
195
+ // Inline workflows for global mode
196
+ if (mode === 'global') {
197
+ inlineWorkflows(commandsDest, workflowsDest);
198
+ }
199
+
200
+ // Write version marker
201
+ const versionPath = join(commandsDest, 'VERSION.plan');
202
+ rejectSymlink(versionPath);
203
+ writeFileSync(versionPath, pkg.version);
204
+
205
+ // Success
206
+ console.log('');
207
+ console.log(green(' ✓ Clancy Plan installed successfully.'));
208
+ console.log('');
209
+ console.log(' Commands available:');
210
+ console.log(
211
+ ` ${cyan('/clancy:plan')} ${dim('Generate an implementation plan')}`,
212
+ );
213
+ console.log(
214
+ ` ${cyan('/clancy:board-setup')} ${dim('Configure board credentials (optional)')}`,
215
+ );
216
+ console.log('');
217
+ console.log(' Next steps:');
218
+ console.log(` 1. Open a project in Claude Code`);
219
+ console.log(` 2. Run: ${cyan('/clancy:plan TICKET-123')}`);
220
+ console.log('');
221
+ console.log(dim(' Want to plan board tickets?'));
222
+ console.log(dim(` Run: ${cyan('/clancy:board-setup')}`));
223
+ console.log('');
224
+ console.log(
225
+ dim(' For the full pipeline (tickets, planning, implementation):'),
226
+ );
227
+ console.log(dim(` npx chief-clancy`));
228
+ console.log('');
229
+
230
+ rl.close();
231
+ }
232
+
233
+ main().catch((err) => {
234
+ console.error(red(`\n Error: ${err.message}`));
235
+ rl.close();
236
+ process.exit(1);
237
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @chief-clancy/plan
3
+ *
4
+ * Implementation planner for Claude Code — decompose briefs into
5
+ * actionable plans. Ships the `/clancy:plan` slash command
6
+ * and a lightweight installer.
7
+ */
8
+ export declare const PACKAGE_NAME: "@chief-clancy/plan";
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,oBAA6B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @chief-clancy/plan
3
+ *
4
+ * Implementation planner for Claude Code — decompose briefs into
5
+ * actionable plans. Ships the `/clancy:plan` slash command
6
+ * and a lightweight installer.
7
+ */
8
+ export const PACKAGE_NAME = '@chief-clancy/plan';
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,oBAA6B,CAAC"}
@@ -0,0 +1,62 @@
1
+ /** The install target — global (`~/.claude`) or local (`./.claude`). */
2
+ export type PlanInstallMode = 'global' | 'local';
3
+ /** All resolved destination paths for a plan installation. */
4
+ export type PlanInstallPaths = {
5
+ readonly commandsDest: string;
6
+ readonly workflowsDest: string;
7
+ };
8
+ /** Source directories within the npm package. */
9
+ type PlanInstallSources = {
10
+ readonly commandsDir: string;
11
+ readonly workflowsDir: string;
12
+ };
13
+ /**
14
+ * File system operations the installer performs.
15
+ *
16
+ * `mkdir` must be idempotent (recursive, ignoring EEXIST).
17
+ */
18
+ type PlanInstallerFs = {
19
+ readonly exists: (path: string) => boolean;
20
+ readonly readFile: (path: string) => string;
21
+ readonly writeFile: (path: string, content: string) => void;
22
+ readonly mkdir: (path: string) => void;
23
+ readonly copyFile: (src: string, dest: string) => void;
24
+ /** Must return `false` (not throw) when the path does not exist. */
25
+ readonly isSymlink: (path: string) => boolean;
26
+ };
27
+ /** Options for {@link runPlanInstall}. */
28
+ export type RunPlanInstallOptions = {
29
+ readonly mode: PlanInstallMode;
30
+ readonly paths: PlanInstallPaths;
31
+ readonly sources: PlanInstallSources;
32
+ readonly version: string;
33
+ readonly fs: PlanInstallerFs;
34
+ };
35
+ /**
36
+ * Parse `--global` / `--local` from CLI arguments.
37
+ *
38
+ * @param args - CLI arguments (typically `process.argv.slice(2)`).
39
+ * @returns The install mode, or `null` if no flag was provided.
40
+ */
41
+ export declare const parsePlanInstallFlag: (args: readonly string[]) => PlanInstallMode | null;
42
+ /**
43
+ * Compute all destination paths for a plan installation.
44
+ *
45
+ * @param mode - Global or local install target.
46
+ * @param homeDir - The user's home directory.
47
+ * @param cwd - The current working directory.
48
+ * @returns All resolved destination paths.
49
+ */
50
+ export declare const resolvePlanInstallPaths: (mode: PlanInstallMode, homeDir: string, cwd: string) => PlanInstallPaths;
51
+ /**
52
+ * Run the plan installation pipeline.
53
+ *
54
+ * Copies command and workflow files to the target directory.
55
+ * For global mode, inlines workflow content into the command file.
56
+ * Writes a VERSION.plan marker with the package version.
57
+ *
58
+ * @param options - All dependencies and configuration.
59
+ */
60
+ export declare const runPlanInstall: (options: RunPlanInstallOptions) => void;
61
+ export {};
62
+ //# 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,eAAe,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEjD,8DAA8D;AAC9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,iDAAiD;AACjD,KAAK,kBAAkB,GAAG;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF;;;;GAIG;AACH,KAAK,eAAe,GAAG;IACrB,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,0CAA0C;AAC1C,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,eAAe,CAAC;CAC9B,CAAC;AAmBF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,MAAM,SAAS,MAAM,EAAE,KACtB,eAAe,GAAG,IAKpB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GAClC,MAAM,eAAe,EACrB,SAAS,MAAM,EACf,KAAK,MAAM,KACV,gBAQF,CAAC;AA4EF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,qBAAqB,KAAG,IAuB/D,CAAC"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Plan installer — self-contained module for `npx @chief-clancy/plan`.
3
+ *
4
+ * Copies the plan slash command and workflow to the Claude Code commands
5
+ * directory. No hooks, bundles, manifests, role filtering, or board
6
+ * configuration.
7
+ */
8
+ import { join } from 'node:path';
9
+ // ---------------------------------------------------------------------------
10
+ // Constants
11
+ // ---------------------------------------------------------------------------
12
+ /** Command files shipped with the plan package. */
13
+ const COMMAND_FILES = ['board-setup.md', 'plan.md'];
14
+ /** Workflow files shipped with the plan package. */
15
+ const WORKFLOW_FILES = ['board-setup.md', 'plan.md'];
16
+ /** Matches `@.claude/clancy/workflows/<filename>.md` on its own line. */
17
+ const WORKFLOW_REF = /^@\.claude\/clancy\/workflows\/([^/\\]+\.md)\r?$/gm;
18
+ // ---------------------------------------------------------------------------
19
+ // Pure functions
20
+ // ---------------------------------------------------------------------------
21
+ /**
22
+ * Parse `--global` / `--local` from CLI arguments.
23
+ *
24
+ * @param args - CLI arguments (typically `process.argv.slice(2)`).
25
+ * @returns The install mode, or `null` if no flag was provided.
26
+ */
27
+ export const parsePlanInstallFlag = (args) => {
28
+ if (args.includes('--global'))
29
+ return 'global';
30
+ if (args.includes('--local'))
31
+ return 'local';
32
+ return null;
33
+ };
34
+ /**
35
+ * Compute all destination paths for a plan installation.
36
+ *
37
+ * @param mode - Global or local install target.
38
+ * @param homeDir - The user's home directory.
39
+ * @param cwd - The current working directory.
40
+ * @returns All resolved destination paths.
41
+ */
42
+ export const resolvePlanInstallPaths = (mode, homeDir, cwd) => {
43
+ const baseDir = mode === 'global' ? join(homeDir, '.claude') : join(cwd, '.claude');
44
+ return {
45
+ commandsDest: join(baseDir, 'commands', 'clancy'),
46
+ workflowsDest: join(baseDir, 'clancy', 'workflows'),
47
+ };
48
+ };
49
+ // ---------------------------------------------------------------------------
50
+ // Internal helpers
51
+ // ---------------------------------------------------------------------------
52
+ /** Throw if the given path is a symlink (ENOENT is swallowed). */
53
+ const rejectSymlink = (path, isSymlink) => {
54
+ if (isSymlink(path)) {
55
+ throw new Error(`Symlink rejected: ${path}`);
56
+ }
57
+ };
58
+ /** Copy a list of files from src dir to dest dir with symlink protection. */
59
+ const copyFiles = (options) => {
60
+ const { files, srcDir, destDir, fs } = options;
61
+ fs.mkdir(destDir);
62
+ files.forEach((file) => {
63
+ const src = join(srcDir, file);
64
+ const dest = join(destDir, file);
65
+ if (!fs.exists(src)) {
66
+ throw new Error(`Source file not found: ${src}`);
67
+ }
68
+ rejectSymlink(dest, fs.isSymlink);
69
+ fs.copyFile(src, dest);
70
+ });
71
+ };
72
+ /**
73
+ * Inline workflow content into command files (global mode only).
74
+ *
75
+ * Replaces `@.claude/clancy/workflows/<name>.md` references with the
76
+ * actual workflow content so global installs work without project-relative
77
+ * @-file resolution.
78
+ */
79
+ const inlineWorkflow = (commandsDest, workflowsDest, fs) => {
80
+ COMMAND_FILES.forEach((file) => {
81
+ const cmdPath = join(commandsDest, file);
82
+ const content = fs.readFile(cmdPath);
83
+ const resolved = content.replace(WORKFLOW_REF, (match, fileName) => {
84
+ const wfPath = join(workflowsDest, fileName);
85
+ return fs.exists(wfPath) ? fs.readFile(wfPath) : match;
86
+ });
87
+ if (resolved !== content) {
88
+ rejectSymlink(cmdPath, fs.isSymlink);
89
+ fs.writeFile(cmdPath, resolved);
90
+ }
91
+ });
92
+ };
93
+ // ---------------------------------------------------------------------------
94
+ // Main pipeline
95
+ // ---------------------------------------------------------------------------
96
+ /**
97
+ * Run the plan installation pipeline.
98
+ *
99
+ * Copies command and workflow files to the target directory.
100
+ * For global mode, inlines workflow content into the command file.
101
+ * Writes a VERSION.plan marker with the package version.
102
+ *
103
+ * @param options - All dependencies and configuration.
104
+ */
105
+ export const runPlanInstall = (options) => {
106
+ const { mode, paths, sources, version, fs } = options;
107
+ copyFiles({
108
+ files: COMMAND_FILES,
109
+ srcDir: sources.commandsDir,
110
+ destDir: paths.commandsDest,
111
+ fs,
112
+ });
113
+ copyFiles({
114
+ files: WORKFLOW_FILES,
115
+ srcDir: sources.workflowsDir,
116
+ destDir: paths.workflowsDest,
117
+ fs,
118
+ });
119
+ if (mode === 'global') {
120
+ inlineWorkflow(paths.commandsDest, paths.workflowsDest, fs);
121
+ }
122
+ const versionPath = join(paths.commandsDest, 'VERSION.plan');
123
+ rejectSymlink(versionPath, fs.isSymlink);
124
+ fs.writeFile(versionPath, version);
125
+ };
126
+ //# 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/plan",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Implementation planner for Claude Code — decompose briefs into actionable plans",
6
+ "author": "Alex Clapperton",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/Pushedskydiver/chief-clancy.git",
11
+ "directory": "packages/plan"
12
+ },
13
+ "homepage": "https://github.com/Pushedskydiver/chief-clancy#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/Pushedskydiver/chief-clancy/issues"
16
+ },
17
+ "keywords": [
18
+ "claude",
19
+ "claude-code",
20
+ "ai",
21
+ "plan",
22
+ "planning",
23
+ "slash-commands",
24
+ "developer-tools"
25
+ ],
26
+ "type": "module",
27
+ "bin": {
28
+ "clancy-plan": "./bin/plan.js"
29
+ },
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js"
34
+ }
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "bin",
39
+ "src/commands",
40
+ "src/workflows"
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,11 @@
1
+ # /clancy:board-setup
2
+
3
+ Configure board credentials for standalone plan usage. Connects your project to a Kanban board so you can plan from board tickets.
4
+
5
+ Not needed if you have the full Clancy pipeline installed (`npx chief-clancy`) — use `/clancy:settings` instead.
6
+
7
+ Supported boards: Jira, GitHub Issues, Linear, Shortcut, Notion, Azure DevOps.
8
+
9
+ @.claude/clancy/workflows/board-setup.md
10
+
11
+ Follow the board setup workflow above. Collect credentials, verify the connection, and write them to `.clancy/.env`.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Structural tests for plan command files.
3
+ *
4
+ * Verifies the commands directory contains the expected markdown files
5
+ * that the plan installer 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 COMMANDS_DIR = fileURLToPath(new URL('.', import.meta.url));
13
+
14
+ const EXPECTED_COMMANDS = ['board-setup.md', 'plan.md'];
15
+
16
+ describe('commands directory structure', () => {
17
+ it('contains exactly the expected command files', () => {
18
+ const commands = readdirSync(COMMANDS_DIR)
19
+ .filter((f) => f.endsWith('.md'))
20
+ .sort();
21
+
22
+ expect(commands).toEqual([...EXPECTED_COMMANDS].sort());
23
+ });
24
+
25
+ it('all command files start with a heading', () => {
26
+ const issues: string[] = [];
27
+
28
+ EXPECTED_COMMANDS.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
+ });