squad-kit 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/README.md +140 -0
- package/dist/cli.js +607 -0
- package/dist/cli.js.map +1 -0
- package/package.json +60 -0
- package/templates/agents/claude-code/squad-new-story.md +18 -0
- package/templates/agents/claude-code/squad-plan.md +29 -0
- package/templates/agents/copilot/squad-plan.prompt.md +14 -0
- package/templates/agents/cursor/squad-new-story.md +9 -0
- package/templates/agents/cursor/squad-plan.md +26 -0
- package/templates/agents/gemini/squad-plan.toml +14 -0
- package/templates/index.md +7 -0
- package/templates/overview.md +13 -0
- package/templates/prompts/generate-plan.md +89 -0
- package/templates/prompts/intake.md +85 -0
- package/templates/prompts/story-skeleton.md +64 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/init.ts","../src/core/paths.ts","../src/core/config.ts","../src/utils/fs.ts","../src/core/template.ts","../src/commands/new-story.ts","../src/core/tracker.ts","../src/commands/new-plan.ts","../src/utils/clipboard.ts","../src/commands/status.ts","../src/core/sequence.ts","../src/commands/list.ts","../src/commands/tracker-link.ts"],"sourcesContent":["import { Command } from 'commander';\nimport kleur from 'kleur';\nimport { runInit } from './commands/init.js';\nimport { runNewStory } from './commands/new-story.js';\nimport { runNewPlan } from './commands/new-plan.js';\nimport { runStatus } from './commands/status.js';\nimport { runList } from './commands/list.js';\nimport { runTrackerLink } from './commands/tracker-link.js';\n\nconst program = new Command();\n\nprogram\n .name('squad')\n .description('Plan once, execute cheap. A 3-step SDD workflow CLI.')\n .version('0.1.0');\n\nprogram\n .command('init')\n .description('Bootstrap .squad/ in the current directory')\n .option('--agents <list>', 'Comma-separated agent list: claude-code,cursor,copilot,gemini')\n .option('--tracker <type>', 'Tracker type: none|github|linear|jira|azure')\n .option('--name <name>', 'Project name')\n .option('--language <lang>', 'Primary language')\n .option('--force', 'Overwrite existing files', false)\n .option('-y, --yes', 'Accept defaults (non-interactive)', false)\n .action(wrap(runInit));\n\nprogram\n .command('new-story <feature-slug>')\n .description('Scaffold a new story intake under .squad/stories/<feature>/<id>/')\n .option('--id <id>', 'Tracker work-item id (required when tracker is not none and naming.includeTrackerId is true)')\n .option('--title <title>', 'Short title hint (placed at top of intake)')\n .option('-y, --yes', 'Fail fast instead of prompting for missing values', false)\n .action(wrapArgs(runNewStory));\n\nprogram\n .command('new-plan <intake-path>')\n .description('Compose the plan-generation meta-prompt with intake content; prints to stdout and copies to clipboard')\n .option('--no-copy', 'Do not copy the composed prompt to clipboard')\n .action(wrapArgs(runNewPlan));\n\nprogram.command('status').description('Show squad-kit workspace status').action(wrap(runStatus));\n\nprogram\n .command('list')\n .description('List stories and their plan state')\n .option('--feature <slug>', 'Filter by feature slug')\n .action(wrap(runList));\n\nconst tracker = program.command('tracker').description('Tracker id helpers');\ntracker\n .command('link <story-path> <tracker-id>')\n .description('Attach/update a tracker id on an existing story intake')\n .action(wrapArgs(runTrackerLink));\n\nprogram.parseAsync(process.argv).catch((err) => {\n console.error(kleur.red(`error: ${(err as Error).message}`));\n process.exit(1);\n});\n\nfunction wrap<T>(fn: (opts: T) => Promise<void>) {\n return async (opts: T) => {\n try {\n await fn(opts);\n } catch (err) {\n console.error(kleur.red(`error: ${(err as Error).message}`));\n process.exit(1);\n }\n };\n}\n\nfunction wrapArgs<A extends unknown[]>(fn: (...args: A) => Promise<void>) {\n return async (...args: A) => {\n try {\n await fn(...args);\n } catch (err) {\n console.error(kleur.red(`error: ${(err as Error).message}`));\n process.exit(1);\n }\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport kleur from 'kleur';\nimport { input, select, checkbox, confirm } from '@inquirer/prompts';\nimport { buildPaths, SQUAD_DIR } from '../core/paths.js';\nimport { saveConfig, type SquadConfig, type TrackerType } from '../core/config.js';\nimport { copyTree, templatesDir, writeFileSafe, readFile } from '../utils/fs.js';\nimport { render } from '../core/template.js';\n\nexport interface InitOptions {\n agents?: string;\n tracker?: TrackerType;\n name?: string;\n language?: string;\n force?: boolean;\n yes?: boolean;\n}\n\nconst SUPPORTED_AGENTS = ['claude-code', 'cursor', 'copilot', 'gemini'] as const;\ntype AgentName = (typeof SUPPORTED_AGENTS)[number];\n\nconst AGENT_INSTALL: Record<AgentName, { subdir: string; srcDir: string }> = {\n 'claude-code': { subdir: path.join('.claude', 'commands'), srcDir: 'claude-code' },\n cursor: { subdir: path.join('.cursor', 'commands'), srcDir: 'cursor' },\n copilot: { subdir: path.join('.github', 'prompts'), srcDir: 'copilot' },\n gemini: { subdir: path.join('.gemini', 'commands'), srcDir: 'gemini' },\n};\n\nexport async function runInit(opts: InitOptions): Promise<void> {\n const root = process.cwd();\n const paths = buildPaths(root);\n\n if (fs.existsSync(paths.configFile) && !opts.force) {\n console.log(kleur.yellow(`A ${SQUAD_DIR}/config.yaml already exists.`));\n console.log(` To add agents or change settings, re-run with ${kleur.bold('--force')} or edit the file directly.`);\n return;\n }\n\n const defaults = {\n name: opts.name ?? path.basename(root),\n language: opts.language ?? 'typescript',\n tracker: (opts.tracker ?? 'none') as TrackerType,\n agents: parseAgentsFlag(opts.agents),\n };\n\n const answers = opts.yes\n ? defaults\n : {\n name: await input({ message: 'Project name', default: defaults.name }),\n language: await input({ message: 'Primary language', default: defaults.language }),\n tracker: (await select({\n message: 'Issue tracker',\n choices: [\n { name: 'None', value: 'none' as TrackerType },\n { name: 'GitHub Issues', value: 'github' as TrackerType },\n { name: 'Linear', value: 'linear' as TrackerType },\n { name: 'Jira', value: 'jira' as TrackerType },\n { name: 'Azure DevOps', value: 'azure' as TrackerType },\n ],\n default: defaults.tracker,\n })) as TrackerType,\n agents: (await checkbox({\n message: 'Install slash commands for which agents?',\n choices: SUPPORTED_AGENTS.map((a) => ({ name: a, value: a, checked: defaults.agents.includes(a) })),\n })) as AgentName[],\n };\n\n const includeTrackerId =\n answers.tracker !== 'none'\n ? await confirmSafe('Include tracker id in plan filenames (NN-story-<slug>-<id>.md)?', true, !!opts.yes)\n : false;\n\n const config: SquadConfig = {\n version: 1,\n project: { name: answers.name, primaryLanguage: answers.language, projectRoots: ['.'] },\n tracker: { type: answers.tracker },\n naming: { includeTrackerId, globalSequence: true },\n agents: answers.agents,\n };\n\n fs.mkdirSync(paths.squadDir, { recursive: true });\n fs.mkdirSync(paths.promptsDir, { recursive: true });\n fs.mkdirSync(paths.storiesDir, { recursive: true });\n fs.mkdirSync(paths.plansDir, { recursive: true });\n\n copyTree(path.join(templatesDir(), 'prompts'), paths.promptsDir, !!opts.force);\n\n writeFileSafe(\n paths.indexFile,\n readFile(path.join(templatesDir(), 'index.md')),\n !!opts.force,\n );\n\n saveConfig(paths.configFile, config);\n\n writeFileSafe(\n path.join(paths.squadDir, 'README.md'),\n renderSquadReadme(config),\n !!opts.force,\n );\n\n for (const agent of answers.agents) {\n installAgent(root, agent as AgentName, !!opts.force);\n }\n\n console.log(kleur.green(`\\n✓ Initialized ${SQUAD_DIR}/ at ${root}`));\n console.log(` tracker: ${config.tracker.type}`);\n console.log(` agents: ${answers.agents.length ? answers.agents.join(', ') : '(none)'}`);\n console.log(`\\nNext:`);\n console.log(` 1) ${kleur.bold('squad new-story <feature-slug>')}`);\n console.log(` 2) Fill the generated intake.md, then run ${kleur.bold('/squad-plan')} in your agent (or ${kleur.bold('squad new-plan')}).`);\n}\n\nfunction parseAgentsFlag(flag?: string): AgentName[] {\n if (!flag) return [];\n return flag\n .split(',')\n .map((s) => s.trim())\n .filter((s): s is AgentName => (SUPPORTED_AGENTS as readonly string[]).includes(s));\n}\n\nasync function confirmSafe(message: string, defaultValue: boolean, nonInteractive: boolean): Promise<boolean> {\n if (nonInteractive) return defaultValue;\n return confirm({ message, default: defaultValue });\n}\n\nfunction installAgent(root: string, agent: AgentName, overwrite: boolean): void {\n const config = AGENT_INSTALL[agent];\n const src = path.join(templatesDir(), 'agents', config.srcDir);\n const dest = path.join(root, config.subdir);\n if (!fs.existsSync(src)) return;\n copyTree(src, dest, overwrite);\n}\n\nfunction renderSquadReadme(config: SquadConfig): string {\n return render(\n `# squad-kit workspace\n\nThis folder is managed by [squad-kit](https://github.com/AzmSquad/squad-kit).\n\n- **Project:** {{name}}\n- **Language:** {{language}}\n- **Tracker:** {{tracker}}\n\n## Workflow\n\n1. **Intake** — \\`squad new-story <feature-slug>\\` scaffolds \\`stories/<feature>/<id>/intake.md\\`. Paste the tracker title, description, and acceptance criteria.\n2. **Plan** — Run \\`/squad-plan <intake-path>\\` in your agent (or \\`squad new-plan <intake-path>\\` to get the composed prompt on stdout).\n3. **Implement** — Open a new, scoped agent session and attach **only** the generated \\`NN-story-*.md\\` file. Let a cheap model execute it.\n\nSee \\`prompts/generate-plan.md\\` for the plan-generation meta-prompt and \\`prompts/story-skeleton.md\\` for the target plan structure.\n`,\n { name: config.project.name, language: config.project.primaryLanguage ?? '', tracker: config.tracker.type },\n );\n}\n","import path from 'node:path';\nimport fs from 'node:fs';\n\nexport const SQUAD_DIR = '.squad';\n\nexport interface SquadPaths {\n root: string;\n squadDir: string;\n configFile: string;\n promptsDir: string;\n storiesDir: string;\n plansDir: string;\n indexFile: string;\n}\n\nexport function buildPaths(root: string): SquadPaths {\n const squadDir = path.join(root, SQUAD_DIR);\n return {\n root,\n squadDir,\n configFile: path.join(squadDir, 'config.yaml'),\n promptsDir: path.join(squadDir, 'prompts'),\n storiesDir: path.join(squadDir, 'stories'),\n plansDir: path.join(squadDir, 'plans'),\n indexFile: path.join(squadDir, 'plans', '00-index.md'),\n };\n}\n\nexport function findSquadRoot(startDir: string = process.cwd()): string | null {\n let current = path.resolve(startDir);\n const { root } = path.parse(current);\n while (true) {\n if (fs.existsSync(path.join(current, SQUAD_DIR, 'config.yaml'))) {\n return current;\n }\n if (current === root) return null;\n current = path.dirname(current);\n }\n}\n\nexport function requireSquadRoot(startDir: string = process.cwd()): string {\n const root = findSquadRoot(startDir);\n if (!root) {\n throw new Error(\n `No .squad/ directory found (searched upward from ${startDir}). Run \\`squad init\\` first.`,\n );\n }\n return root;\n}\n\nexport function slugify(input: string): string {\n return input\n .toLowerCase()\n .trim()\n .replace(/[^\\p{L}\\p{N}]+/gu, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 80);\n}\n","import fs from 'node:fs';\nimport yaml from 'js-yaml';\n\nexport type TrackerType = 'none' | 'github' | 'linear' | 'jira' | 'azure';\n\nexport interface SquadConfig {\n version: number;\n project: {\n name: string;\n primaryLanguage?: string;\n projectRoots?: string[];\n };\n tracker: {\n type: TrackerType;\n workspace?: string;\n project?: string;\n };\n naming: {\n includeTrackerId: boolean;\n globalSequence: boolean;\n };\n agents: string[];\n}\n\nexport const DEFAULT_CONFIG: SquadConfig = {\n version: 1,\n project: {\n name: 'my-project',\n primaryLanguage: 'typescript',\n projectRoots: ['.'],\n },\n tracker: { type: 'none' },\n naming: { includeTrackerId: false, globalSequence: true },\n agents: [],\n};\n\nexport function loadConfig(configFile: string): SquadConfig {\n const raw = fs.readFileSync(configFile, 'utf8');\n const parsed = yaml.load(raw) as Partial<SquadConfig> | undefined;\n if (!parsed || typeof parsed !== 'object') {\n throw new Error(`Invalid config at ${configFile}: expected a YAML object.`);\n }\n return mergeConfig(DEFAULT_CONFIG, parsed);\n}\n\nexport function saveConfig(configFile: string, config: SquadConfig): void {\n const body = yaml.dump(config, { lineWidth: 100, noRefs: true, sortKeys: false });\n fs.writeFileSync(configFile, body, 'utf8');\n}\n\nfunction mergeConfig(base: SquadConfig, override: Partial<SquadConfig>): SquadConfig {\n return {\n version: override.version ?? base.version,\n project: { ...base.project, ...(override.project ?? {}) },\n tracker: { ...base.tracker, ...(override.tracker ?? {}) },\n naming: { ...base.naming, ...(override.naming ?? {}) },\n agents: override.agents ?? base.agents,\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport function readFile(file: string): string {\n return fs.readFileSync(file, 'utf8');\n}\n\nexport function writeFileSafe(file: string, content: string, overwrite = false): boolean {\n if (!overwrite && fs.existsSync(file)) return false;\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(file, content, 'utf8');\n return true;\n}\n\nexport function copyTree(srcDir: string, destDir: string, overwrite = false): void {\n fs.mkdirSync(destDir, { recursive: true });\n for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {\n const src = path.join(srcDir, entry.name);\n const dest = path.join(destDir, entry.name);\n if (entry.isDirectory()) {\n copyTree(src, dest, overwrite);\n } else if (entry.isFile()) {\n if (!overwrite && fs.existsSync(dest)) continue;\n fs.copyFileSync(src, dest);\n }\n }\n}\n\nexport function packageRoot(): string {\n const here = path.dirname(fileURLToPath(import.meta.url));\n // src/utils → package root when running from source (tsx/vitest)\n // dist → package root when running from bundle\n let dir = here;\n for (let i = 0; i < 6; i++) {\n if (fs.existsSync(path.join(dir, 'package.json')) && fs.existsSync(path.join(dir, 'templates'))) {\n return dir;\n }\n dir = path.dirname(dir);\n }\n throw new Error(`Could not locate squad-kit package root from ${here}`);\n}\n\nexport function templatesDir(): string {\n return path.join(packageRoot(), 'templates');\n}\n","export type TemplateVars = Record<string, string | number | undefined | null>;\n\nconst TOKEN = /\\{\\{\\s*([\\w.-]+)\\s*\\}\\}/g;\n\nexport function render(template: string, vars: TemplateVars): string {\n return template.replace(TOKEN, (_, key: string) => {\n const value = vars[key];\n return value === undefined || value === null ? '' : String(value);\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport kleur from 'kleur';\nimport { input } from '@inquirer/prompts';\nimport { buildPaths, requireSquadRoot, slugify } from '../core/paths.js';\nimport { loadConfig } from '../core/config.js';\nimport { validateTrackerId, trackerIdForFilename } from '../core/tracker.js';\nimport { readFile, templatesDir, writeFileSafe } from '../utils/fs.js';\nimport { render } from '../core/template.js';\n\nexport interface NewStoryOptions {\n id?: string;\n title?: string;\n yes?: boolean;\n}\n\nexport async function runNewStory(featureSlug: string, opts: NewStoryOptions): Promise<void> {\n if (!featureSlug) {\n throw new Error('feature-slug is required. Usage: squad new-story <feature-slug> [--id <tracker-id>] [--title \"...\"]');\n }\n const slug = slugify(featureSlug);\n if (!slug) throw new Error(`Invalid feature slug: \"${featureSlug}\"`);\n\n const root = requireSquadRoot();\n const paths = buildPaths(root);\n const config = loadConfig(paths.configFile);\n\n const interactive = !opts.yes && Boolean(process.stdin.isTTY);\n const trackerNeedsId = config.tracker.type !== 'none' && config.naming.includeTrackerId;\n\n let id = opts.id;\n let title = opts.title;\n\n if (!id && trackerNeedsId) {\n if (!interactive) {\n throw new Error(\n `Tracker \"${config.tracker.type}\" is configured and naming.includeTrackerId is true. Pass --id <tracker-id>.`,\n );\n }\n id = await input({\n message: `${config.tracker.type} work-item id:`,\n validate: (v) => (validateTrackerId(config.tracker.type, v.trim()) ? true : `Invalid ${config.tracker.type} id format.`),\n });\n id = id.trim();\n }\n\n if (id && !validateTrackerId(config.tracker.type, id)) {\n throw new Error(\n `Invalid tracker id \"${id}\" for tracker type \"${config.tracker.type}\". Check .squad/config.yaml or pass --id in the right format.`,\n );\n }\n\n if (!id && !title) {\n if (!interactive) {\n throw new Error('Provide either --id <tracker-id> or --title \"...\" so the story folder has a distinct name.');\n }\n title = (await input({ message: 'Short story title:' })).trim();\n if (!title) throw new Error('Title cannot be empty.');\n }\n\n if (interactive && !title) {\n const maybe = (await input({ message: 'Short title (optional, press Enter to skip):', default: '' })).trim();\n if (maybe) title = maybe;\n }\n\n const storyFolderName = id ? trackerIdForFilename(config.tracker.type, id) : slugify(title!);\n if (!storyFolderName) throw new Error('Could not derive a story folder name from --id or --title.');\n const storyDir = path.join(paths.storiesDir, slug, storyFolderName);\n const intakeFile = path.join(storyDir, 'intake.md');\n const attachmentsDir = path.join(storyDir, 'attachments');\n\n if (fs.existsSync(intakeFile)) {\n throw new Error(`Intake already exists: ${intakeFile}`);\n }\n\n fs.mkdirSync(attachmentsDir, { recursive: true });\n\n const template = readFile(path.join(templatesDir(), 'prompts', 'intake.md'));\n const rendered = render(template, {\n featureSlug: slug,\n storyId: storyFolderName,\n trackerType: config.tracker.type,\n projectRoots: (config.project.projectRoots ?? ['.']).join(', '),\n primaryLanguage: config.project.primaryLanguage ?? '',\n });\n\n const preamble = title ? `> **Title hint (from CLI):** ${title}\\n\\n` : '';\n writeFileSafe(intakeFile, preamble + rendered, false);\n\n ensureFeatureOverview(path.join(paths.plansDir, slug), slug);\n\n console.log(kleur.green(`✓ Created intake:`));\n console.log(` ${path.relative(root, intakeFile)}`);\n console.log(` ${path.relative(root, attachmentsDir)}${path.sep}`);\n console.log(`\\nNext: paste the tracker title, description, and acceptance criteria into the intake, then:`);\n console.log(` ${kleur.bold(`squad new-plan ${path.relative(root, intakeFile)}`)}`);\n console.log(` (or run ${kleur.bold('/squad-plan')} in your agent)`);\n}\n\nfunction ensureFeatureOverview(featurePlanDir: string, slug: string): void {\n fs.mkdirSync(featurePlanDir, { recursive: true });\n const overviewFile = path.join(featurePlanDir, '00-overview.md');\n if (fs.existsSync(overviewFile)) return;\n const template = readFile(path.join(templatesDir(), 'overview.md'));\n writeFileSafe(overviewFile, render(template, { featureSlug: slug }), false);\n}\n","import type { TrackerType } from './config.js';\n\nconst VALIDATORS: Record<Exclude<TrackerType, 'none'>, RegExp> = {\n github: /^(?:[\\w.-]+\\/[\\w.-]+#)?\\d+$/,\n linear: /^[A-Z][A-Z0-9]+-\\d+$/i,\n jira: /^[A-Z][A-Z0-9]+-\\d+$/,\n azure: /^\\d+$/,\n};\n\nexport function validateTrackerId(type: TrackerType, id: string): boolean {\n if (type === 'none') return true;\n const pattern = VALIDATORS[type];\n return pattern.test(id);\n}\n\nexport function trackerIdForFilename(type: TrackerType, id: string): string {\n if (type === 'github') return id.replace(/^[\\w.-]+\\/[\\w.-]+#/, '');\n return id;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport kleur from 'kleur';\nimport { buildPaths, requireSquadRoot } from '../core/paths.js';\nimport { loadConfig } from '../core/config.js';\nimport { readFile } from '../utils/fs.js';\nimport { render } from '../core/template.js';\nimport { copyToClipboard } from '../utils/clipboard.js';\n\nexport interface NewPlanOptions {\n copy?: boolean;\n}\n\nexport async function runNewPlan(intakePath: string, opts: NewPlanOptions): Promise<void> {\n if (!intakePath) {\n throw new Error('intake-path is required. Usage: squad new-plan <path-to-intake.md>');\n }\n const root = requireSquadRoot();\n const paths = buildPaths(root);\n const config = loadConfig(paths.configFile);\n\n const resolved = path.resolve(intakePath);\n if (!fs.existsSync(resolved)) throw new Error(`Intake not found: ${resolved}`);\n const intakeContent = readFile(resolved);\n\n const metaPromptFile = path.join(paths.promptsDir, 'generate-plan.md');\n if (!fs.existsSync(metaPromptFile)) {\n throw new Error(\n `Missing meta-prompt at ${metaPromptFile}. Re-run \\`squad init --force\\` to restore templates.`,\n );\n }\n const metaPrompt = readFile(metaPromptFile);\n\n const composed = render(metaPrompt, {\n projectRoots: (config.project.projectRoots ?? ['.']).join(', '),\n primaryLanguage: config.project.primaryLanguage ?? '',\n trackerType: config.tracker.type,\n intakeContent,\n });\n\n process.stdout.write(composed);\n if (!composed.endsWith('\\n')) process.stdout.write('\\n');\n\n if (opts.copy !== false) {\n const ok = await copyToClipboard(composed);\n if (ok) console.error(kleur.dim('\\n(copied to clipboard)'));\n }\n}\n","import { spawn } from 'node:child_process';\n\nexport async function copyToClipboard(text: string): Promise<boolean> {\n const cmd = clipboardCommand();\n if (!cmd) return false;\n return new Promise((resolve) => {\n const child = spawn(cmd.command, cmd.args, { stdio: ['pipe', 'ignore', 'ignore'] });\n child.on('error', () => resolve(false));\n child.on('close', (code) => resolve(code === 0));\n child.stdin.write(text);\n child.stdin.end();\n });\n}\n\nfunction clipboardCommand(): { command: string; args: string[] } | null {\n if (process.platform === 'darwin') return { command: 'pbcopy', args: [] };\n if (process.platform === 'win32') return { command: 'clip', args: [] };\n if (process.env.WAYLAND_DISPLAY) return { command: 'wl-copy', args: [] };\n if (process.env.DISPLAY) return { command: 'xclip', args: ['-selection', 'clipboard'] };\n return null;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport kleur from 'kleur';\nimport { buildPaths, requireSquadRoot } from '../core/paths.js';\nimport { loadConfig } from '../core/config.js';\nimport { scanPlans, formatSequence } from '../core/sequence.js';\n\nexport async function runStatus(): Promise<void> {\n const root = requireSquadRoot();\n const paths = buildPaths(root);\n const config = loadConfig(paths.configFile);\n const scan = scanPlans(paths.plansDir);\n\n const storyCount = countStories(paths.storiesDir);\n const planCount = scan.usedNumbers.length;\n\n console.log(kleur.bold('squad-kit status'));\n console.log(` project: ${config.project.name}`);\n console.log(` tracker: ${config.tracker.type}`);\n console.log(` agents: ${config.agents.length ? config.agents.join(', ') : '(none)'}`);\n console.log(` stories (drafts + planned): ${storyCount}`);\n console.log(` plan files: ${planCount}`);\n console.log(` next NN: ${formatSequence(scan.nextGlobal)}`);\n if (scan.duplicates.length > 0) {\n console.log(kleur.yellow(` ⚠ duplicate NN numbers: ${scan.duplicates.map(formatSequence).join(', ')}`));\n }\n}\n\nfunction countStories(storiesDir: string): number {\n if (!fs.existsSync(storiesDir)) return 0;\n let total = 0;\n for (const feature of fs.readdirSync(storiesDir, { withFileTypes: true })) {\n if (!feature.isDirectory()) continue;\n const featureDir = path.join(storiesDir, feature.name);\n for (const story of fs.readdirSync(featureDir, { withFileTypes: true })) {\n if (!story.isDirectory()) continue;\n if (fs.existsSync(path.join(featureDir, story.name, 'intake.md'))) total++;\n }\n }\n return total;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nconst STORY_FILE = /^(\\d{2,})-story-.+\\.md$/;\n\nexport interface SequenceScan {\n usedNumbers: number[];\n maxNumber: number;\n duplicates: number[];\n nextGlobal: number;\n}\n\nexport function scanPlans(plansDir: string): SequenceScan {\n const used: number[] = [];\n const seen = new Map<number, number>();\n\n if (fs.existsSync(plansDir)) {\n for (const feature of fs.readdirSync(plansDir, { withFileTypes: true })) {\n if (!feature.isDirectory()) continue;\n const featureDir = path.join(plansDir, feature.name);\n for (const entry of fs.readdirSync(featureDir)) {\n const match = entry.match(STORY_FILE);\n if (!match) continue;\n const n = parseInt(match[1]!, 10);\n used.push(n);\n seen.set(n, (seen.get(n) ?? 0) + 1);\n }\n }\n }\n\n const duplicates = [...seen.entries()].filter(([, c]) => c > 1).map(([n]) => n);\n const maxNumber = used.length ? Math.max(...used) : 0;\n return { usedNumbers: used.sort((a, b) => a - b), maxNumber, duplicates, nextGlobal: maxNumber + 1 };\n}\n\nexport function formatSequence(n: number): string {\n return String(n).padStart(2, '0');\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport kleur from 'kleur';\nimport { buildPaths, requireSquadRoot } from '../core/paths.js';\n\nexport interface ListOptions {\n feature?: string;\n}\n\ninterface Row {\n feature: string;\n id: string;\n hasIntake: boolean;\n planFile?: string;\n}\n\nexport async function runList(opts: ListOptions): Promise<void> {\n const root = requireSquadRoot();\n const paths = buildPaths(root);\n const rows: Row[] = [];\n\n if (fs.existsSync(paths.storiesDir)) {\n for (const feature of fs.readdirSync(paths.storiesDir, { withFileTypes: true })) {\n if (!feature.isDirectory()) continue;\n if (opts.feature && feature.name !== opts.feature) continue;\n const featureDir = path.join(paths.storiesDir, feature.name);\n for (const story of fs.readdirSync(featureDir, { withFileTypes: true })) {\n if (!story.isDirectory()) continue;\n const intake = path.join(featureDir, story.name, 'intake.md');\n rows.push({\n feature: feature.name,\n id: story.name,\n hasIntake: fs.existsSync(intake),\n planFile: findPlan(paths.plansDir, feature.name, story.name),\n });\n }\n }\n }\n\n if (rows.length === 0) {\n console.log(kleur.dim('(no stories yet — try `squad new-story <feature-slug>`)'));\n return;\n }\n\n const header = ['feature', 'story', 'intake', 'plan'];\n const table = rows.map((r) => [\n r.feature,\n r.id,\n r.hasIntake ? 'yes' : '—',\n r.planFile ?? kleur.dim('—'),\n ]);\n printTable([header, ...table]);\n}\n\nfunction findPlan(plansDir: string, feature: string, storyId: string): string | undefined {\n const featureDir = path.join(plansDir, feature);\n if (!fs.existsSync(featureDir)) return undefined;\n for (const entry of fs.readdirSync(featureDir)) {\n if (entry.includes(storyId) && entry.endsWith('.md')) return entry;\n }\n return undefined;\n}\n\nfunction printTable(rows: string[][]): void {\n const widths = rows[0]!.map((_, i) => Math.max(...rows.map((r) => stripAnsi(r[i] ?? '').length)));\n for (const row of rows) {\n console.log(row.map((cell, i) => pad(cell, widths[i]!)).join(' '));\n }\n}\n\nfunction pad(s: string, width: number): string {\n const visible = stripAnsi(s).length;\n return s + ' '.repeat(Math.max(0, width - visible));\n}\n\nfunction stripAnsi(s: string): string {\n return s.replace(/\\x1b\\[[0-9;]*m/g, '');\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport kleur from 'kleur';\nimport { buildPaths, requireSquadRoot } from '../core/paths.js';\nimport { loadConfig } from '../core/config.js';\nimport { validateTrackerId } from '../core/tracker.js';\n\nexport async function runTrackerLink(storyPath: string, trackerId: string): Promise<void> {\n if (!storyPath || !trackerId) {\n throw new Error('Usage: squad tracker link <story-folder-or-intake> <tracker-id>');\n }\n const root = requireSquadRoot();\n const paths = buildPaths(root);\n const config = loadConfig(paths.configFile);\n\n if (!validateTrackerId(config.tracker.type, trackerId)) {\n throw new Error(`Invalid tracker id \"${trackerId}\" for tracker type \"${config.tracker.type}\".`);\n }\n\n const resolved = path.resolve(storyPath);\n const intakeFile = resolved.endsWith('intake.md') ? resolved : path.join(resolved, 'intake.md');\n if (!fs.existsSync(intakeFile)) throw new Error(`intake.md not found at ${intakeFile}`);\n\n const content = fs.readFileSync(intakeFile, 'utf8');\n const updated = upsertTrackerId(content, trackerId);\n fs.writeFileSync(intakeFile, updated, 'utf8');\n\n console.log(kleur.green(`✓ Linked tracker id ${trackerId} → ${path.relative(root, intakeFile)}`));\n}\n\nfunction upsertTrackerId(content: string, id: string): string {\n const pattern = /(-\\s*\\*\\*Work item id:\\*\\*)[^\\n]*/;\n if (pattern.test(content)) {\n return content.replace(pattern, `$1 ${id}`);\n }\n return content + `\\n\\n- **Work item id:** ${id}\\n`;\n}\n"],"mappings":"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","names":["kleur","fs","path","input","fs","fs","path","path","fs","fs","path","kleur","input","input","path","fs","kleur","fs","path","kleur","path","fs","kleur","fs","path","kleur","fs","path","kleur","fs","path","fs","path","kleur","fs","path","kleur","fs","path","kleur","path","fs","kleur","kleur"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "squad-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"author": "Momen Yasser <momenysr@gmail.com>",
|
|
5
|
+
"description": "Plan once, execute cheap. A token-efficient, 3-step SDD workflow CLI (stories → plans → implementation).",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"sdd",
|
|
8
|
+
"spec-driven-development",
|
|
9
|
+
"ai-agents",
|
|
10
|
+
"claude-code",
|
|
11
|
+
"cursor",
|
|
12
|
+
"copilot",
|
|
13
|
+
"planning",
|
|
14
|
+
"llm"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"type": "module",
|
|
18
|
+
"bin": {
|
|
19
|
+
"squad": "./dist/cli.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"templates",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@inquirer/prompts": "^7.2.0",
|
|
29
|
+
"commander": "^12.1.0",
|
|
30
|
+
"fs-extra": "^11.2.0",
|
|
31
|
+
"js-yaml": "^4.1.0",
|
|
32
|
+
"kleur": "^4.1.5"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/fs-extra": "^11.0.4",
|
|
36
|
+
"@types/js-yaml": "^4.0.9",
|
|
37
|
+
"@types/node": "^22.10.0",
|
|
38
|
+
"tsup": "^8.3.5",
|
|
39
|
+
"typescript": "^5.7.2",
|
|
40
|
+
"vitest": "^2.1.8"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/AzmSquad/squad-kit"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/AzmSquad/squad-kit#readme",
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/AzmSquad/squad-kit/issues"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup",
|
|
55
|
+
"dev": "tsup --watch",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:watch": "vitest",
|
|
58
|
+
"lint": "tsc --noEmit"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Scaffold a new squad-kit story intake file.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Scaffold a new story intake folder using the squad-kit CLI.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
`$ARGUMENTS` should be: `<feature-slug> [--id <tracker-id>] [--title "..."]`
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
1. If `$ARGUMENTS` is empty, ask the user for:
|
|
14
|
+
- feature slug (kebab-case, e.g. `checkout`, `auth-rewrite`)
|
|
15
|
+
- optional tracker id (only if `.squad/config.yaml` tracker.type is not `none`)
|
|
16
|
+
- optional title
|
|
17
|
+
2. Run: `squad new-story $ARGUMENTS`
|
|
18
|
+
3. Report the created path. Remind the user to paste the tracker title, description, and acceptance criteria into the generated `intake.md` before running `/squad-plan`.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Generate an implementation plan from a squad-kit story intake file.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
You are generating an agent-executable implementation plan for this project using the squad-kit workflow.
|
|
6
|
+
|
|
7
|
+
## Inputs
|
|
8
|
+
|
|
9
|
+
- **Intake file path:** `$ARGUMENTS`
|
|
10
|
+
- If empty, ask the user for the path to a story intake file (under `.squad/stories/`).
|
|
11
|
+
- **Meta-prompt:** `.squad/prompts/generate-plan.md` — follow its instructions exactly.
|
|
12
|
+
- **Project config:** `.squad/config.yaml` — read `project.projectRoots`, `tracker.type`, `naming.includeTrackerId`, `naming.globalSequence`.
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
|
|
16
|
+
1. Read `.squad/prompts/generate-plan.md` completely. Treat it as your operating instructions for structure, tone, and output rules.
|
|
17
|
+
2. Read the intake file at `$ARGUMENTS`, plus any files in its `attachments/` directory that the intake references.
|
|
18
|
+
3. Read one or two existing plan files under `.squad/plans/` (if any) to match established tone. If none exist, use `.squad/prompts/story-skeleton.md` as the structural reference.
|
|
19
|
+
4. Determine the next sequence number by scanning `.squad/plans/**/NN-story-*.md` for the highest `NN` (global sequence) or the highest within the target feature folder (per-feature sequence), per `config.yaml.naming.globalSequence`.
|
|
20
|
+
5. Write the plan file to `.squad/plans/<feature-slug>/NN-story-<slug>[-<id>].md`.
|
|
21
|
+
6. Update `.squad/plans/<feature-slug>/00-overview.md` with the new story row.
|
|
22
|
+
7. If this is a new feature slug, add it to `.squad/plans/00-index.md`.
|
|
23
|
+
|
|
24
|
+
## Rules
|
|
25
|
+
|
|
26
|
+
- **Planning only.** Do not modify application source code in this session.
|
|
27
|
+
- **Concrete over clever.** File paths, line ranges, type names, function signatures, verification commands. No "consider" or "might".
|
|
28
|
+
- **Respect existing conventions.** Read neighbouring plan files before inventing a new pattern.
|
|
29
|
+
- Report back the path(s) you wrote and a one-line summary per file.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
mode: agent
|
|
3
|
+
description: Generate an implementation plan from a squad-kit story intake file.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Generate an implementation plan using the squad-kit workflow.
|
|
7
|
+
|
|
8
|
+
- Read `.squad/prompts/generate-plan.md` and follow its instructions exactly.
|
|
9
|
+
- Read the intake file provided by the user (under `.squad/stories/`) plus any referenced files in its `attachments/`.
|
|
10
|
+
- Read `.squad/config.yaml` for tracker type and naming rules.
|
|
11
|
+
- Scan `.squad/plans/**/NN-story-*.md` to determine the next global sequence number.
|
|
12
|
+
- Write the plan file to `.squad/plans/<feature-slug>/NN-story-<slug>[-<id>].md` and update `00-overview.md` / `00-index.md` as needed.
|
|
13
|
+
|
|
14
|
+
Planning only. Do not modify application source code in this session.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Scaffold a new squad-kit story intake file.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
`$ARGUMENTS`: `<feature-slug> [--id <tracker-id>] [--title "..."]`
|
|
6
|
+
|
|
7
|
+
1. If arguments are empty, ask the user for feature slug (kebab-case), optional tracker id, optional title.
|
|
8
|
+
2. Run: `squad new-story $ARGUMENTS`
|
|
9
|
+
3. Report the created path. Remind the user to paste the tracker title, description, and acceptance criteria into the generated `intake.md` before running `/squad-plan`.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Generate an implementation plan from a squad-kit story intake file.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
You are generating an agent-executable implementation plan for this project using the squad-kit workflow.
|
|
6
|
+
|
|
7
|
+
## Inputs
|
|
8
|
+
|
|
9
|
+
- **Intake file path:** `$ARGUMENTS` (if empty, ask the user for a path under `.squad/stories/`).
|
|
10
|
+
- **Meta-prompt:** `.squad/prompts/generate-plan.md` — follow its instructions exactly.
|
|
11
|
+
- **Project config:** `.squad/config.yaml` — read `project.projectRoots`, `tracker.type`, `naming.*`.
|
|
12
|
+
|
|
13
|
+
## Steps
|
|
14
|
+
|
|
15
|
+
1. Read `.squad/prompts/generate-plan.md` completely. Treat it as your operating instructions for structure, tone, and output rules.
|
|
16
|
+
2. Read the intake file, plus any files in its `attachments/` directory that the intake references.
|
|
17
|
+
3. Read one or two existing plan files under `.squad/plans/` (if any) to match established tone. If none exist, use `.squad/prompts/story-skeleton.md`.
|
|
18
|
+
4. Determine the next sequence number by scanning `.squad/plans/**/NN-story-*.md`, honouring `config.yaml.naming.globalSequence`.
|
|
19
|
+
5. Write the plan file to `.squad/plans/<feature-slug>/NN-story-<slug>[-<id>].md`.
|
|
20
|
+
6. Update the feature's `00-overview.md` and `.squad/plans/00-index.md` if it's a new feature slug.
|
|
21
|
+
|
|
22
|
+
## Rules
|
|
23
|
+
|
|
24
|
+
- Planning only. Do **not** modify application source code in this session.
|
|
25
|
+
- Concrete over clever. File paths, line ranges, type names, function signatures, verification commands.
|
|
26
|
+
- Respect existing conventions — read neighbouring plan files first.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
description = "Generate an implementation plan from a squad-kit story intake file."
|
|
2
|
+
|
|
3
|
+
prompt = """
|
|
4
|
+
Generate an implementation plan using the squad-kit workflow.
|
|
5
|
+
|
|
6
|
+
Steps:
|
|
7
|
+
1. Read .squad/prompts/generate-plan.md and follow its instructions exactly.
|
|
8
|
+
2. Read the intake file at the argument path (under .squad/stories/) plus any referenced files in its attachments/.
|
|
9
|
+
3. Read .squad/config.yaml for tracker type and naming rules.
|
|
10
|
+
4. Scan .squad/plans/**/NN-story-*.md to determine the next global sequence number.
|
|
11
|
+
5. Write the plan file to .squad/plans/<feature-slug>/NN-story-<slug>[-<id>].md and update 00-overview.md / 00-index.md as needed.
|
|
12
|
+
|
|
13
|
+
Planning only. Do not modify application source code in this session.
|
|
14
|
+
"""
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Plans index
|
|
2
|
+
|
|
3
|
+
One row per feature folder under `.squad/plans/`. `NN` continues as a global execution sequence across all features when `naming.globalSequence` is `true` in `config.yaml`.
|
|
4
|
+
|
|
5
|
+
| Feature | Overview | NN range |
|
|
6
|
+
|---------|----------|----------|
|
|
7
|
+
| _add rows per feature_ |
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# {{featureSlug}} — plan overview
|
|
2
|
+
|
|
3
|
+
Entry point for the **{{featureSlug}}** feature. Stories execute in order by their `NN` prefix.
|
|
4
|
+
|
|
5
|
+
## Stories
|
|
6
|
+
|
|
7
|
+
| NN | File | Title | Tracker id | Depends on |
|
|
8
|
+
|----|------|-------|------------|------------|
|
|
9
|
+
| _add rows as stories are planned_ |
|
|
10
|
+
|
|
11
|
+
## Dependency notes
|
|
12
|
+
|
|
13
|
+
_Describe sequencing, shared contracts, or cross-feature dependencies here._
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Meta-prompt: generate implementation-plan Markdown
|
|
2
|
+
|
|
3
|
+
Use this prompt in **one agent session** together with a filled intake file (`.squad/stories/<feature-slug>/<id>/intake.md`) and any files under its `attachments/`.
|
|
4
|
+
|
|
5
|
+
> **Philosophy: plan once, execute cheap.** Put enough concrete detail into the plan that a cheap executor model can act on it without re-exploring the codebase. File paths, line ranges, type names, function signatures, verification commands — all belong here. Vague advice does not.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
You are maintaining **agent-executable implementation plans** for this project.
|
|
10
|
+
|
|
11
|
+
## Inputs (the user will provide)
|
|
12
|
+
|
|
13
|
+
1. **Feature slug** — Target folder: `.squad/plans/<feature-slug>/` (must already exist, or create it with `00-overview.md`).
|
|
14
|
+
2. **Tracker work item id** (optional) — If the project's tracker type (see `.squad/config.yaml`) is not `none`, the id must appear in the filename and tables. It should already be in the intake **metadata**.
|
|
15
|
+
3. **Intake story** — Read **`.squad/stories/<feature-slug>/<id>/intake.md`** (folder name is the tracker id, or a slug if no tracker).
|
|
16
|
+
4. **Attachments** — If the intake folder has **`attachments/`**, open the files the intake references.
|
|
17
|
+
|
|
18
|
+
## Project context (read as needed)
|
|
19
|
+
|
|
20
|
+
- Project roots: `{{projectRoots}}`
|
|
21
|
+
- Primary language: `{{primaryLanguage}}`
|
|
22
|
+
- Existing plans in `.squad/plans/` — read one or two to match tone and file-oriented style.
|
|
23
|
+
|
|
24
|
+
## Story document pattern
|
|
25
|
+
|
|
26
|
+
Every generated plan Markdown must follow this structure and tone.
|
|
27
|
+
|
|
28
|
+
### H1 title line
|
|
29
|
+
|
|
30
|
+
- Format: `# Story NN — <title> (Story: <tracker-id>)` when a tracker id exists.
|
|
31
|
+
- Format: `# Story NN — <title>` when no tracker.
|
|
32
|
+
- `NN` is the **global** execution sequence across all feature folders (continue after the highest existing number).
|
|
33
|
+
|
|
34
|
+
### Section order (`---` on its own line between major sections)
|
|
35
|
+
|
|
36
|
+
1. **`## Prerequisites`** — Bullet list. Use `None.` only when truly no dependencies. Otherwise reference prior stories as `Story NN completed: …` or **markdown links** to sibling plan files. Mention coordination with owners of shared contracts when those change.
|
|
37
|
+
|
|
38
|
+
2. **`## Story Goal`** — Outcome in clear prose. For business-heavy items a short numbered list of user-visible outcomes is fine.
|
|
39
|
+
|
|
40
|
+
3. **`## Context — Read These Files First`** (use this **exact casing**).
|
|
41
|
+
- Numbered list `1.` … `2.` …
|
|
42
|
+
- Each line: repo-relative path in backticks (or a markdown link to a sibling plan file).
|
|
43
|
+
- After the path, an **em dash** (`—`) and a short instruction: what to read, method names, line ranges (`~lines 513–662`), type names in backticks.
|
|
44
|
+
- Include **grep / search** lines when helpful (`` Grep for `handleRequest` in … ``).
|
|
45
|
+
- Reference intake attachments with workspace-root paths when relevant.
|
|
46
|
+
|
|
47
|
+
4. **Optional: `## Product rules (from story)`** — Use when the intake distinguishes **current vs new behaviour** (**Current:** / **New:**) or notes from business. Skip for purely technical refactors.
|
|
48
|
+
|
|
49
|
+
5. **Implementation body** — Pick the heading style that fits:
|
|
50
|
+
- **`## Backend Tasks`** / **`## Frontend Tasks`** (split by layer), **or**
|
|
51
|
+
- **`## Implementation tasks`** (single stream).
|
|
52
|
+
- Subsections: **`### 1.`** or **`### 1 — Short title`** for ordered work; nest **`####`** only when needed.
|
|
53
|
+
- For each change, prefer **`` File: `path` ``** or **`` Create file: `path` ``** so the executor knows exactly where to edit.
|
|
54
|
+
- Include **language-tagged fenced code** for DTOs, signatures, snippets (large blocks are OK when they match existing repo style).
|
|
55
|
+
- State explicitly **`No backend changes required.`** (or frontend equivalent) when true.
|
|
56
|
+
|
|
57
|
+
6. **`## Verification Steps`** — Numbered list. Bold lead-ins: **`Frontend runs:`**, **`Backend builds:`**, **`Regression:`**. Reference commands concretely (`pnpm dev`, `dotnet build`, `cargo test`, `pytest`).
|
|
58
|
+
|
|
59
|
+
7. **`## Done Criteria`** — `- [ ]` checklist aligned with acceptance criteria. Each item testable.
|
|
60
|
+
|
|
61
|
+
8. **Optional closing line** — For sequential stories:
|
|
62
|
+
`**STOP HERE. Report to the user and wait for confirmation before proceeding to Story NN+1.**`
|
|
63
|
+
|
|
64
|
+
### Tone and formatting conventions
|
|
65
|
+
|
|
66
|
+
- **Direct and imperative** ("Add…", "Find…", "Read lines…"). Tie steps to **concrete paths** and **symbols**.
|
|
67
|
+
- Use **`**bold**`** for file names, field names, stage keys, and critical constraints (**backward compatibility**, **nullable**, **do not** …).
|
|
68
|
+
- Prefer **minimal breaking changes**, **nullable** new fields, and **document** follow-ups in comments when out of scope.
|
|
69
|
+
- User-facing strings (including non-English text) in quotes where they appear in the app.
|
|
70
|
+
|
|
71
|
+
## Output rules
|
|
72
|
+
|
|
73
|
+
1. **Location:** Write new or updated files only under **`.squad/plans/<feature-slug>/`**.
|
|
74
|
+
2. **Filename:**
|
|
75
|
+
- With tracker id: **`NN-story-<slug>-<id>.md`** or **`NN-story-<id>.md`** (project choice in `config.yaml.naming`).
|
|
76
|
+
- Without: **`NN-story-<slug>.md`**.
|
|
77
|
+
3. **Story file body:** Follow the **Story document pattern** above for every new plan file.
|
|
78
|
+
4. **Overview:** Update **`.squad/plans/<feature-slug>/00-overview.md`**:
|
|
79
|
+
- Columns include tracker id (if applicable), file name, title, depends on.
|
|
80
|
+
- Adjust dependency notes if dependencies changed.
|
|
81
|
+
5. **Cross-feature links:** Use relative paths like `../other-feature/NN-story-….md`.
|
|
82
|
+
6. **Index:** If introducing a **new** feature slug, add a row to `.squad/plans/00-index.md`.
|
|
83
|
+
7. **Scope:** Produce planning Markdown only unless the user explicitly asks for code changes.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Intake content (paste below this line, or let the CLI inline it)
|
|
88
|
+
|
|
89
|
+
{{intakeContent}}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Story intake
|
|
2
|
+
|
|
3
|
+
Fill this template for each story you want planned. Keep it copy-paste-friendly: the planner reads **this file and the files in `attachments/`**, nothing else.
|
|
4
|
+
|
|
5
|
+
- Folder: `.squad/stories/{{featureSlug}}/{{storyId}}/intake.md`
|
|
6
|
+
- Binaries (screenshots, PDFs, exports): put them in `attachments/` next to this file and list them below.
|
|
7
|
+
- Do **not** rely on external links (tracker URLs, wiki, chat) — the planner cannot open them. Paste the content you want considered.
|
|
8
|
+
|
|
9
|
+
This is **not** an implementation prompt. It is the input to `.squad/prompts/generate-plan.md`.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Feature
|
|
14
|
+
|
|
15
|
+
- **Feature name (display):**
|
|
16
|
+
- **Feature slug (folder under `plans/`):** `{{featureSlug}}`
|
|
17
|
+
|
|
18
|
+
## Tracker (metadata only)
|
|
19
|
+
|
|
20
|
+
- **Tracker type:** `{{trackerType}}`
|
|
21
|
+
- **Work item id:** (used in filenames and plan tables; leave blank if tracker type is `none`)
|
|
22
|
+
- **Work item type:** Story / Bug / Task / Chore / …
|
|
23
|
+
|
|
24
|
+
External tracker links are **not** followed by the planner. Keep the id for naming and traceability only.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Title
|
|
29
|
+
|
|
30
|
+
*(Paste the work item title verbatim.)*
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Description
|
|
39
|
+
|
|
40
|
+
*(Paste the full work item description, including any formatting you rely on.)*
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Acceptance criteria
|
|
49
|
+
|
|
50
|
+
*(Paste the acceptance criteria — checklist, bullets, Gherkin, whatever the tracker stores.)*
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Attachments
|
|
59
|
+
|
|
60
|
+
Place files in `attachments/` next to this `intake.md`, then list them here so the planner knows what to open.
|
|
61
|
+
|
|
62
|
+
| File (relative to this folder) | What it is |
|
|
63
|
+
| ------------------------------ | ---------- |
|
|
64
|
+
| *(e.g. `attachments/flow.png`)* | *(e.g. UX flow)* |
|
|
65
|
+
|
|
66
|
+
*(Add rows per file. If none, write "None.")*
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Dependencies
|
|
71
|
+
|
|
72
|
+
- **Blocked by / related ids:** (tracker ids only; optional short note)
|
|
73
|
+
- **Depends on code areas or other stories:**
|
|
74
|
+
|
|
75
|
+
## Extra notes (optional)
|
|
76
|
+
|
|
77
|
+
- Anything not captured above (e.g. chat context) — keep short.
|
|
78
|
+
|
|
79
|
+
## Technical hints (optional)
|
|
80
|
+
|
|
81
|
+
- APIs, screens, services already discussed. Repos/roots: `{{projectRoots}}`. Primary language: `{{primaryLanguage}}`.
|
|
82
|
+
|
|
83
|
+
## Out of scope
|
|
84
|
+
|
|
85
|
+
- What this story explicitly does **not** cover:
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Story file anatomy (reference)
|
|
2
|
+
|
|
3
|
+
This file documents the structure of `NN-story-<slug>.md` plans that the planner produces. The planner reads `generate-plan.md`; this file exists so contributors can scan the target shape at a glance.
|
|
4
|
+
|
|
5
|
+
```md
|
|
6
|
+
# Story NN — <title> (Story: <tracker-id?>)
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
- None. # or: - Story NN completed: <short note>
|
|
10
|
+
- See `../other-feature/NN-story-<slug>.md` when dependencies cross feature folders.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Story Goal
|
|
15
|
+
<1–3 paragraphs or a short numbered list of user-visible outcomes>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Context — Read These Files First
|
|
20
|
+
|
|
21
|
+
1. `path/to/file.ts` — read `handleRequest` (~lines 80–140). Note shape of `RequestDTO`.
|
|
22
|
+
2. `path/to/model.ts` — existing `User` entity; new fields must be nullable.
|
|
23
|
+
3. Grep for `FLAG_NAME` across `src/` to find usages.
|
|
24
|
+
4. `.squad/stories/<feature>/<id>/attachments/flow.png` — user-flow diagram.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Implementation tasks
|
|
29
|
+
|
|
30
|
+
### 1 — <short title>
|
|
31
|
+
|
|
32
|
+
**File: `path/to/file.ts`**
|
|
33
|
+
|
|
34
|
+
<concrete instructions; include code blocks with language tags for new signatures/DTOs>
|
|
35
|
+
|
|
36
|
+
### 2 — <short title>
|
|
37
|
+
|
|
38
|
+
**Create file: `path/to/new-file.ts`**
|
|
39
|
+
|
|
40
|
+
<…>
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Verification Steps
|
|
45
|
+
|
|
46
|
+
1. **Backend builds:** `<build command>` — no errors.
|
|
47
|
+
2. **Unit tests pass:** `<test command>` — new tests green.
|
|
48
|
+
3. **Regression:** open `<URL or route>` and confirm existing behaviour is intact.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Done Criteria
|
|
53
|
+
|
|
54
|
+
- [ ] <acceptance criterion 1>
|
|
55
|
+
- [ ] <acceptance criterion 2>
|
|
56
|
+
- [ ] Overview `00-overview.md` updated with this story.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Guidelines:
|
|
60
|
+
|
|
61
|
+
- **Concrete over clever.** Paths, line ranges, type names. No "consider" or "might".
|
|
62
|
+
- **Every task maps to a file edit or a command to run.** If it does not, it belongs in a comment, not a task.
|
|
63
|
+
- **State "no changes required" explicitly** for layers the story does not touch. Silence implies "figure it out", which defeats the point.
|
|
64
|
+
- **Keep Verification small and runnable.** 3–7 steps. If it balloons, the plan is doing too much — split the story.
|