diff --git a/.gitignore b/.gitignore index fa4fc0e..26b725f 100644 --- a/.gitignore +++ b/.gitignore @@ -140,6 +140,7 @@ vite.config.ts.timestamp-* # AI agent tools .agent-tools/ +.claude/ # Core dump files core diff --git a/.husky/pre-commit b/.husky/pre-commit index 52ae1ff..99593d2 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,3 +1,10 @@ +# Extract commands from skills/ and generate command files +node bin/aidd.js --skills "$(git rev-parse --show-toplevel)" + +# Stage generated skills and command files +git add 'skills/index.md' 2>/dev/null || true +git add 'ai/commands/*.md' 2>/dev/null || true + # Generate index.md files for ai/ folders node bin/aidd.js --index "$(git rev-parse --show-toplevel)" diff --git a/AGENTS.md b/AGENTS.md index 2b2efde..178d316 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -41,3 +41,54 @@ If any conflicts are detected between a requested task and the vision document, 3. Ask the user to clarify how to resolve the conflict before proceeding Never proceed with a task that contradicts the vision without explicit user approval. + +## Agent Skills + +AIDD includes reusable skills in the `skills/` directory following the [agentskills.io](https://agentskills.io) specification. These skills work with any compatible agent (Claude Code, Cursor, etc.). + +### Available Skills + +| Skill | Purpose | +|-------|---------| +| `aidd-discover` | Product discovery, user journeys, personas | +| `aidd-task` | Plan and break down epics into tasks | +| `aidd-execute` | Implement using TDD | +| `aidd-review` | Code review with security focus | +| `aidd-log` | Document changes to activity-log.md | +| `aidd-commit` | Conventional commit formatting | +| `aidd-user-test` | Generate human and AI test scripts | + +### Setup for Claude Code + +Claude Code doesn't natively support `AGENTS.md`. To enable AIDD: + +```bash +# Copy or symlink AGENTS.md to CLAUDE.md +cp AGENTS.md CLAUDE.md +# or: ln -s AGENTS.md CLAUDE.md + +# Symlink skills directory +mkdir -p .claude +ln -s ../skills .claude/skills +``` + +### Setup for Cursor + +```bash +# Symlink skills directory (Cursor also discovers .claude/skills) +mkdir -p .cursor +ln -s ../skills .cursor/skills +``` + +### Manual Invocation + +Primary commands invoke their corresponding skill: +- `/task` - Plan a new epic +- `/execute` - Implement with TDD +- `/review` - Code review +- `/discover` - Product discovery +- `/commit` - Conventional commits +- `/log` - Activity logging +- `/user-test` - Generate test scripts + +For the complete command reference including sub-commands, see `skills/index.md`. diff --git a/ai/commands/commit.md b/ai/commands/commit.md index c4b4fbd..20580f1 100644 --- a/ai/commands/commit.md +++ b/ai/commands/commit.md @@ -1,15 +1,5 @@ -# Commit +# /commit -Act as a senior software engineer to commit changes to the repository in non-interactive modes ONLY, using the following template: +create a conventional commit for staged changes -"$type${[(scope)]}{[!]}: $description":where `[]` is optional and `!` is a breaking change - -Types: fix|feat|chore|docs|refactor|test|perf|build|ci|style|revert|$other - -Constraints { - When committing, don't log about logging in the commit message. - Use multiple -m flags, one for each log entry. - Limit the first commit message line length to 50 characters. - Use conventional commits with the supplied template. - Do NOT add new things to the CHANGELOG.md file. -} +Use the commit skill to create a conventional commit for staged changes. diff --git a/ai/commands/discover.md b/ai/commands/discover.md index f41709e..ca1bd2f 100644 --- a/ai/commands/discover.md +++ b/ai/commands/discover.md @@ -1,8 +1,5 @@ -## ๐Ÿ” Discover +# /discover -Use productmanager.mdc to discover a user journey, user story, or feature. +Enter interactive product discovery planning mode -Constraints { -Begin by reading the file and asking the user relevant questions to spark the discovery process. -Before beginning, read and respect the constraints in please.mdc. -} +Use the discover skill to enter interactive product discovery planning mode. diff --git a/ai/commands/execute.md b/ai/commands/execute.md index 70c62eb..58675ed 100644 --- a/ai/commands/execute.md +++ b/ai/commands/execute.md @@ -1,8 +1,5 @@ -## โš™๏ธ Execute Task/Epic +# /execute -Use the task creator to execute a task epic. +implement code using TDD (red-green-refactor cycle) -Constraints { -Before beginning, read and respect the constraints in please.mdc. -Remember to use the TDD process if asked to implement code. -} +Use the execute skill to implement code using tdd (red-green-refactor cycle). diff --git a/ai/commands/help.md b/ai/commands/help.md index 529d4c6..fa34087 100644 --- a/ai/commands/help.md +++ b/ai/commands/help.md @@ -1,8 +1,10 @@ -## โ“ Help +# /help -List commands from please.mdc and report them to the user. +List available commands and their usage. + +Read [skills/index.md](../../skills/index.md) for complete command reference and build help text from it. Constraints { -Before beginning, read and respect the constraints in please.mdc. -Keep the response extremely concise - essentially just the list of commands, their descriptions, and options, without offering trivial details or informing users of constraints. + Before beginning, read and respect the constraints in @ai/rules/please.mdc. + Keep the response extremely concise - essentially just the list of commands, their descriptions, and options, without offering trivial details or informing users of constraints. } diff --git a/ai/commands/index.md b/ai/commands/index.md index f728833..9ce05fa 100644 --- a/ai/commands/index.md +++ b/ai/commands/index.md @@ -1,66 +1,50 @@ -# commands +# AIDD Commands -This index provides an overview of the contents in this directory. +Primary workflow entry points. Each command activates its corresponding skill. -## Files +For complete command reference including sub-commands, see [skills/index.md](../../skills/index.md). -### Commit +## Commands -**File:** `commit.md` +### /commit -*No description available* +create a conventional commit for staged changes -### discover +**Skill:** [aidd-commit](../../skills/aidd-commit/SKILL.md) -**File:** `discover.md` +### /discover -*No description available* +Enter interactive product discovery planning mode -### execute +**Skill:** [aidd-discover](../../skills/aidd-discover/SKILL.md) -**File:** `execute.md` +### /execute -*No description available* +implement code using TDD (red-green-refactor cycle) -### help +**Skill:** [aidd-execute](../../skills/aidd-execute/SKILL.md) -**File:** `help.md` +### /log -*No description available* +log completed epics to activity-log.md -### log +**Skill:** [aidd-log](../../skills/aidd-log/SKILL.md) -**File:** `log.md` +### /review -*No description available* +conduct a thorough code review focusing on code quality, best practices, and adherence to project standards -### plan +**Skill:** [aidd-review](../../skills/aidd-review/SKILL.md) -**File:** `plan.md` +### /task -*No description available* +create a task/epic -### ๐Ÿ”ฌ Code Review +**Skill:** [aidd-task](../../skills/aidd-task/SKILL.md) -**File:** `review.md` +### /user-test -*No description available* +Generate human and agent scripts, save to $projectRoot/plan/ -### run-test - -**File:** `run-test.md` - -*No description available* - -### task - -**File:** `task.md` - -*No description available* - -### user-test - -**File:** `user-test.md` - -*No description available* +**Skill:** [aidd-user-test](../../skills/aidd-user-test/SKILL.md) diff --git a/ai/commands/log.md b/ai/commands/log.md index 18ae367..19d406c 100644 --- a/ai/commands/log.md +++ b/ai/commands/log.md @@ -1,4 +1,5 @@ -## ๐Ÿ“ Log +# /log -Use log.mdc to collect salient changes, and log them to the activity-log.md. -Before beginning, read and respect the constraints in please.mdc. +log completed epics to activity-log.md + +Use the log skill to log completed epics to activity-log.md. diff --git a/ai/commands/review.md b/ai/commands/review.md index 4d5c1e7..8f979aa 100644 --- a/ai/commands/review.md +++ b/ai/commands/review.md @@ -1,7 +1,5 @@ -# ๐Ÿ”ฌ Code Review +# /review -use review.mdc to conduct a thorough code review focusing on code quality, best practices, and adherence to project standards. +conduct a thorough code review focusing on code quality, best practices, and adherence to project standards -Constraints { -Before beginning, read and respect the constraints in please.mdc. -} +Use the review skill to conduct a thorough code review focusing on code quality, best practices, and adherence to project standards. diff --git a/ai/commands/run-test.md b/ai/commands/run-test.md deleted file mode 100644 index 2ab4690..0000000 --- a/ai/commands/run-test.md +++ /dev/null @@ -1,11 +0,0 @@ -## ๐Ÿค– Run Test - -Execute AI agent test script in real browser. - -Use @user-testing.mdc to run agent test, capturing screenshots and generating report. - -Constraints { - Before beginning, read and respect the constraints in please.mdc. - Drive real browser, narrate thoughts like human tester. - Generate structured markdown report with screenshots. -} diff --git a/ai/commands/task.md b/ai/commands/task.md index c17684d..86c3e08 100644 --- a/ai/commands/task.md +++ b/ai/commands/task.md @@ -1,8 +1,5 @@ -## โœ… Task +# /task -Use the task creator to plan and execute a task epic. +create a task/epic -Constraints { -Before beginning, read and respect the constraints in please.mdc. -Remember to use the TDD process if asked to implement code. -} +Use the task skill to create a task/epic. diff --git a/ai/commands/user-test.md b/ai/commands/user-test.md index ac0fb65..de53010 100644 --- a/ai/commands/user-test.md +++ b/ai/commands/user-test.md @@ -1,7 +1,5 @@ -## ๐Ÿงช User Test +# /user-test -Use @user-testing.mdc to generate human and AI agent test scripts from user journeys. +Generate human and agent scripts, save to $projectRoot/plan/ -Constraints { -Before beginning, read and respect the constraints in please.mdc. -} +Use the user-test skill to generate human and agent scripts, save to $projectroot/plan/. diff --git a/ai/rules/index.md b/ai/rules/index.md index da91dcb..6001f82 100644 --- a/ai/rules/index.md +++ b/ai/rules/index.md @@ -16,6 +16,10 @@ See [`javascript/index.md`](./javascript/index.md) for contents. See [`security/index.md`](./security/index.md) for contents. +### ๐Ÿ“ sudolang/ + +See [`sudolang/index.md`](./sudolang/index.md) for contents. + ## Files ### Aiden Agent Orchestrator diff --git a/ai/rules/sudolang/index.md b/ai/rules/sudolang/index.md new file mode 100644 index 0000000..b2f47e7 --- /dev/null +++ b/ai/rules/sudolang/index.md @@ -0,0 +1,12 @@ +# sudolang + +This index provides an overview of the contents in this directory. + +## Files + +### SudoLang Syntax + +**File:** `sudolang-syntax.mdc` + +A quick cheat sheet for SudoLang syntax. + diff --git a/ai/rules/user-testing.test.js b/ai/rules/user-testing.test.js index bad562f..38383ca 100644 --- a/ai/rules/user-testing.test.js +++ b/ai/rules/user-testing.test.js @@ -71,44 +71,43 @@ describe("user-testing", () => { }); }); - test("references user-testing.mdc", async () => { + test("references user-test skill", async () => { const filePath = path.join(__dirname, "../commands/user-test.md"); const content = await fs.readFile(filePath, "utf-8"); assert({ given: "user-test.md content", - should: "reference user-testing.mdc", - actual: - content.includes("@user-testing.mdc") || - content.includes("user-testing.mdc"), + should: "reference user-test skill", + actual: content.includes("user-test skill"), expected: true, }); }); }); - describe("run-test.md command", () => { - test("command file exists", async () => { - const filePath = path.join(__dirname, "../commands/run-test.md"); - const exists = await fs.pathExists(filePath); + describe("/run-test sub-command", () => { + test("is documented in aidd-user-test skill", async () => { + const filePath = path.join( + __dirname, + "../../skills/aidd-user-test/SKILL.md", + ); + const content = await fs.readFile(filePath, "utf-8"); assert({ - given: "run-test.md command file", - should: "exist in ai/commands directory", - actual: exists, + given: "aidd-user-test skill content", + should: "include /run-test sub-command in Commands block", + actual: content.includes("/run-test"), expected: true, }); }); - test("references user-testing.mdc", async () => { - const filePath = path.join(__dirname, "../commands/run-test.md"); + test("is listed in skills index", async () => { + const filePath = path.join(__dirname, "../../skills/index.md"); const content = await fs.readFile(filePath, "utf-8"); assert({ - given: "run-test.md content", - should: "reference user-testing.mdc", - actual: - content.includes("@user-testing.mdc") || - content.includes("user-testing.mdc"), + given: "skills/index.md content", + should: "include /run-test command reference", + actual: content.includes("/run-test"), expected: true, }); }); diff --git a/bin/aidd.js b/bin/aidd.js index 859801d..97e11d4 100755 --- a/bin/aidd.js +++ b/bin/aidd.js @@ -3,6 +3,7 @@ import { Command } from "commander"; import { executeClone } from "../lib/cli-core.js"; import { generateAllIndexes } from "../lib/index-generator.js"; +import { generateSkillsFiles } from "../lib/skills-extractor.js"; import { readFileSync } from "fs"; import { fileURLToPath } from "url"; import path from "path"; @@ -55,6 +56,10 @@ const createCli = () => { "-i, --index", "generate index.md files from frontmatter in ai/ subfolders", ) + .option( + "-s, --skills", + "extract commands from skills/ and generate command files", + ) .addHelpText( "before", ` @@ -115,7 +120,45 @@ https://paralleldrive.com `, ) .action( - async (targetDirectory, { force, dryRun, verbose, cursor, index }) => { + async ( + targetDirectory, + { force, dryRun, verbose, cursor, index, skills }, + ) => { + // Handle --skills option separately + if (skills) { + const targetPath = path.resolve(process.cwd(), targetDirectory); + + if (dryRun) { + console.log( + chalk.cyan( + "Dry run - would extract commands from skills/ and generate files", + ), + ); + process.exit(0); + return; + } + + console.log(chalk.blue("Extracting commands from skills...")); + + const result = await generateSkillsFiles(targetPath); + + if (result.success) { + console.log(chalk.green(`โœ… ${result.message}`)); + if (verbose) { + result.files.forEach((file) => { + console.log(chalk.gray(` - ${file}`)); + }); + } + process.exit(0); + } else { + console.error( + chalk.red(`โŒ ${result.error?.message || result.error}`), + ); + process.exit(1); + } + return; + } + // Handle --index option separately if (index) { const targetPath = path.resolve(process.cwd(), targetDirectory); diff --git a/lib/agents-index-e2e.test.js b/lib/agents-index-e2e.test.js index 48daff3..1f55cf5 100644 --- a/lib/agents-index-e2e.test.js +++ b/lib/agents-index-e2e.test.js @@ -193,9 +193,6 @@ This is a test rule. ); const aiIndexExists = await fs.pathExists(path.join(aiPath, "index.md")); - const commandsIndexExists = await fs.pathExists( - path.join(aiPath, "commands", "index.md"), - ); const rulesIndexExists = await fs.pathExists( path.join(aiPath, "rules", "index.md"), ); @@ -207,33 +204,29 @@ This is a test rule. expected: true, }); - assert({ - given: "--index command", - should: "create ai/commands/index.md", - actual: commandsIndexExists, - expected: true, - }); - assert({ given: "--index command", should: "create ai/rules/index.md", actual: rulesIndexExists, expected: true, }); + + // Note: ai/commands/index.md is NOT created by --index + // It's managed by --skills (skills-extractor) to avoid conflicts }); test("generated index includes file descriptions from frontmatter", async () => { await execAsync(`node ${cliPath} --index "${tempTestDir}"`); - const commandsIndex = await fs.readFile( - path.join(aiPath, "commands", "index.md"), + const rulesIndex = await fs.readFile( + path.join(aiPath, "rules", "index.md"), "utf-8", ); assert({ - given: "generated commands index", + given: "generated rules index", should: "include file description from frontmatter", - actual: commandsIndex.includes("Test command for integration testing"), + actual: rulesIndex.includes("Test rule for TDD"), expected: true, }); }); diff --git a/lib/index-generator.js b/lib/index-generator.js index 480c563..f11693e 100644 --- a/lib/index-generator.js +++ b/lib/index-generator.js @@ -188,10 +188,25 @@ const generateIndexContent = async (dirPath) => { return content; }; +// Directories where index.md is managed by other tools (e.g., skills-extractor) +const SKIP_INDEX_DIRS = ["commands"]; + /** * Write index.md to a directory + * Skips directories in SKIP_INDEX_DIRS (managed by other generators) */ const writeIndex = async (dirPath) => { + const dirName = path.basename(dirPath); + + // Skip directories managed by other generators + if (SKIP_INDEX_DIRS.includes(dirName)) { + return { + path: path.join(dirPath, "index.md"), + success: true, + skipped: true, + }; + } + const indexPath = path.join(dirPath, "index.md"); const content = await generateIndexContent(dirPath); @@ -220,7 +235,10 @@ const generateIndexRecursive = async (dirPath, results = [], depth = 0) => { // Generate index for current directory const result = await writeIndex(dirPath); - results.push(result); + // Only include non-skipped results + if (!result.skipped) { + results.push(result); + } // Process subdirectories const subdirs = await getSubdirectories(dirPath); diff --git a/lib/index-generator.test.js b/lib/index-generator.test.js index 4bc4d4b..5ab3a43 100644 --- a/lib/index-generator.test.js +++ b/lib/index-generator.test.js @@ -247,15 +247,15 @@ Content here.`; assert({ given: "ai/ directory with subdirectories", - should: "generate multiple index files", + should: "generate multiple index files (skipping commands/)", actual: result.indexes.length, - expected: 3, // ai/, ai/commands/, ai/rules/ + expected: 2, // ai/, ai/rules/ (commands/ is skipped - managed by skills-extractor) }); // Verify index files exist const aiIndexExists = await fs.pathExists(path.join(aiPath, "index.md")); - const commandsIndexExists = await fs.pathExists( - path.join(aiPath, "commands", "index.md"), + const rulesIndexExists = await fs.pathExists( + path.join(aiPath, "rules", "index.md"), ); assert({ @@ -267,8 +267,8 @@ Content here.`; assert({ given: "successful generation", - should: "create ai/commands/index.md", - actual: commandsIndexExists, + should: "create ai/rules/index.md", + actual: rulesIndexExists, expected: true, }); }); diff --git a/lib/skills-extractor.js b/lib/skills-extractor.js new file mode 100644 index 0000000..e5309f4 --- /dev/null +++ b/lib/skills-extractor.js @@ -0,0 +1,320 @@ +import path from "path"; +import fs from "fs-extra"; +import matter from "gray-matter"; +import { errorCauses, createError } from "error-causes"; + +// Error causes for skills extraction operations +const [{ SkillsExtractionError }] = errorCauses({ + SkillsExtractionError: { + code: "SKILLS_EXTRACTION_ERROR", + message: "Skills extraction failed", + }, +}); + +/** + * Parse the Commands block from skill content + * Returns array of { command, description } + */ +const parseCommandsBlock = (content) => { + const commands = []; + + // Match Commands { ... } block + const commandsMatch = content.match(/Commands\s*\{([^}]+)\}/s); + if (!commandsMatch) { + return commands; + } + + const commandsContent = commandsMatch[1]; + + // Match lines like: /command - description or /command - description + const commandRegex = /^\s*(?:๐Ÿ”ฌ\s*)?\/(\S+)(?:\s+([^-]+))?\s*-\s*(.+)$/gm; + let match; + + while ((match = commandRegex.exec(commandsContent)) !== null) { + const commandName = match[1].trim(); + const args = match[2] ? match[2].trim() : null; + const description = match[3].trim(); + + commands.push({ + name: commandName, + args, + description, + full: args ? `/${commandName} ${args}` : `/${commandName}`, + }); + } + + return commands; +}; + +/** + * Read and parse a SKILL.md file + */ +const parseSkillFile = async (skillPath) => { + const content = await fs.readFile(skillPath, "utf-8"); + const { data: frontmatter, content: body } = matter(content); + + const commands = parseCommandsBlock(body); + + return { + name: frontmatter.name, + description: frontmatter.description, + aiddCommands: frontmatter.aiddCommands || [], + globs: frontmatter.globs, + commands, + skillPath, + }; +}; + +/** + * Get all skill directories + */ +const getSkillDirectories = async (skillsPath) => { + const items = await fs.readdir(skillsPath, { withFileTypes: true }); + const dirs = []; + + for (const item of items) { + if (item.isDirectory()) { + const skillFile = path.join(skillsPath, item.name, "SKILL.md"); + if (await fs.pathExists(skillFile)) { + dirs.push({ + name: item.name, + skillPath: skillFile, + }); + } + } + } + + return dirs.sort((a, b) => a.name.localeCompare(b.name)); +}; + +// Commands that should not be auto-generated (manually maintained) +const RESERVED_COMMANDS = ["help"]; + +/** + * Get the primary command name for a skill + * Primary command = skill short name (e.g., "aidd-discover" -> "discover") + */ +const getPrimaryCommandName = (skill) => { + return skill.name.replace(/^aidd-/, ""); +}; + +/** + * Check if a command is the primary entry point for its skill + * Only primary commands get top-level command files + * Sub-commands are discovered when the skill is activated + */ +const isPrimaryCommand = (command, skill) => { + const primaryName = getPrimaryCommandName(skill); + return command.name === primaryName; +}; + +/** + * Generate ai/commands/{command}.md file content + */ +const generateCommandFile = (command, skill) => { + // Extract skill short name (e.g., "aidd-log" -> "log") + const skillShortName = skill.name.replace(/^aidd-/, ""); + + let content = `# /${command.name}\n\n`; + content += `${command.description}\n\n`; + content += `Use the ${skillShortName} skill to ${command.description.toLowerCase()}.\n`; + + return content; +}; + +/** + * Generate skills/index.md content + * Only primary commands are listed at top level; sub-commands are scoped within skills + */ +const generateSkillsIndex = (skills) => { + let content = `# AIDD Skills\n\n`; + content += `Agent skills for AI-Driven Development workflows.\n\n`; + content += `## Primary Commands\n\n`; + content += `These are the main entry points. Each command activates its corresponding skill.\n\n`; + + // Collect only PRIMARY commands for top-level listing + const primaryCommands = []; + for (const skill of skills) { + for (const cmd of skill.commands) { + if (isPrimaryCommand(cmd, skill)) { + primaryCommands.push({ + ...cmd, + skillName: skill.name, + skillDescription: skill.description, + }); + } + } + } + + // Sort commands alphabetically + primaryCommands.sort((a, b) => a.name.localeCompare(b.name)); + + for (const cmd of primaryCommands) { + content += `- \`${cmd.full}\` - ${cmd.description}\n`; + } + + content += `\n## Skills\n\n`; + + for (const skill of skills) { + content += `### ${skill.name}\n\n`; + content += `${skill.description}\n\n`; + content += `**Skill:** [\`${skill.name}/SKILL.md\`](./${skill.name}/SKILL.md)\n\n`; + + if (skill.commands.length > 0) { + content += `**Commands:**\n`; + for (const cmd of skill.commands) { + content += `- \`${cmd.full}\` - ${cmd.description}\n`; + } + content += `\n`; + } + } + + return content; +}; + +/** + * Generate ai/commands/index.md content + * Only lists primary entry point commands (sub-commands are scoped within skills) + */ +const generateCommandsIndex = (skills) => { + let content = `# AIDD Commands\n\n`; + content += `Primary workflow entry points. Each command activates its corresponding skill.\n\n`; + content += `For complete command reference including sub-commands, see [skills/index.md](../../skills/index.md).\n\n`; + content += `## Commands\n\n`; + + // Collect only primary commands + const primaryCommands = []; + for (const skill of skills) { + for (const cmd of skill.commands) { + if (isPrimaryCommand(cmd, skill)) { + primaryCommands.push({ + ...cmd, + skillName: skill.name, + skillDescription: skill.description, + }); + } + } + } + + // Sort alphabetically + primaryCommands.sort((a, b) => a.name.localeCompare(b.name)); + + for (const cmd of primaryCommands) { + content += `### /${cmd.name}\n\n`; + content += `${cmd.description}\n\n`; + content += `**Skill:** [${cmd.skillName}](../../skills/${cmd.skillName}/SKILL.md)\n\n`; + } + + return content; +}; + +/** + * Main function to extract commands and generate files + */ +const extractCommands = async (targetBase) => { + const skillsPath = path.join(targetBase, "skills"); + const commandsPath = path.join(targetBase, "ai", "commands"); + + // Check if skills/ directory exists + const skillsExist = await fs.pathExists(skillsPath); + if (!skillsExist) { + return { + success: false, + error: `skills/ directory not found at ${skillsPath}`, + files: [], + }; + } + + try { + // Get all skill directories + const skillDirs = await getSkillDirectories(skillsPath); + + // Parse all skills + const skills = []; + for (const dir of skillDirs) { + const skill = await parseSkillFile(dir.skillPath); + skills.push(skill); + } + + const filesWritten = []; + + // Ensure commands directory exists + await fs.ensureDir(commandsPath); + + // Generate command files ONLY for primary entry points + // Sub-commands are scoped within skills and discovered when skill is activated + for (const skill of skills) { + for (const cmd of skill.commands) { + if (RESERVED_COMMANDS.includes(cmd.name)) { + continue; + } + // Only create top-level command files for primary entry points + if (!isPrimaryCommand(cmd, skill)) { + continue; + } + const cmdFileName = `${cmd.name}.md`; + const cmdFilePath = path.join(commandsPath, cmdFileName); + const cmdContent = generateCommandFile(cmd, skill); + await fs.writeFile(cmdFilePath, cmdContent, "utf-8"); + filesWritten.push(cmdFilePath); + } + } + + // Generate skills/index.md + const skillsIndexPath = path.join(skillsPath, "index.md"); + const skillsIndexContent = generateSkillsIndex(skills); + await fs.writeFile(skillsIndexPath, skillsIndexContent, "utf-8"); + filesWritten.push(skillsIndexPath); + + // Generate ai/commands/index.md + const commandsIndexPath = path.join(commandsPath, "index.md"); + const commandsIndexContent = generateCommandsIndex(skills); + await fs.writeFile(commandsIndexPath, commandsIndexContent, "utf-8"); + filesWritten.push(commandsIndexPath); + + return { + success: true, + message: `Generated ${filesWritten.length} file(s)`, + files: filesWritten, + skills, + }; + } catch (error) { + throw createError({ + ...SkillsExtractionError, + message: error?.message || String(error), + cause: error, + }); + } +}; + +/** + * Generate all skill-related files + */ +const generateSkillsFiles = async (targetBase) => { + try { + const result = await extractCommands(targetBase); + return result; + } catch (error) { + return { + success: false, + error: { + message: error?.message || String(error), + cause: error?.cause, + }, + files: [], + }; + } +}; + +export { + extractCommands, + generateSkillsFiles, + parseCommandsBlock, + parseSkillFile, + getSkillDirectories, + generateCommandFile, + generateSkillsIndex, + generateCommandsIndex, + getPrimaryCommandName, + isPrimaryCommand, +}; diff --git a/skills/aidd-commit/SKILL.md b/skills/aidd-commit/SKILL.md new file mode 100644 index 0000000..96c88c1 --- /dev/null +++ b/skills/aidd-commit/SKILL.md @@ -0,0 +1,84 @@ +--- +name: aidd-commit +description: Create git commits using conventional commit format. Use when committing code changes to maintain consistent, meaningful commit history. +aiddCommands: [/commit] +--- + +# Conventional Commits + +Create git commits following the conventional commit specification. + +## Format + +``` +type(scope): description + +[optional body] + +[optional footer] +``` + +## Types + +| Type | When to Use | +|------|-------------| +| `feat` | New feature | +| `fix` | Bug fix | +| `docs` | Documentation only | +| `style` | Formatting, no code change | +| `refactor` | Code change, no feature/fix | +| `test` | Adding/updating tests | +| `chore` | Maintenance tasks | +| `perf` | Performance improvement | +| `build` | Build system changes | +| `ci` | CI configuration | +| `revert` | Revert previous commit | + +## Rules + +1. First line under 50 characters +2. Use imperative mood ("add" not "added") +3. No period at end of subject +4. Scope is optional but helpful +5. Body explains what and why (not how) + +## Examples + +``` +feat(auth): add OAuth2 login with Google + +fix(cart): resolve race condition in inventory check + +docs: update API endpoint documentation + +refactor(utils): extract date formatting helpers + +test(user): add integration tests for signup flow +``` + +## Breaking Changes + +Use `!` after type/scope or `BREAKING CHANGE:` in footer: + +``` +feat(api)!: change response format for /users endpoint + +BREAKING CHANGE: Response now returns array instead of object +``` + +## Process + +1. Review staged changes with `git diff --staged` +2. Determine appropriate type and scope +3. Write concise, descriptive message +4. Commit with proper format + +## Constraints + +- Never commit secrets or credentials +- Ensure tests pass before committing +- One logical change per commit + +Commands { + /commit - create a conventional commit for staged changes +} diff --git a/skills/aidd-discover/SKILL.md b/skills/aidd-discover/SKILL.md new file mode 100644 index 0000000..273c678 --- /dev/null +++ b/skills/aidd-discover/SKILL.md @@ -0,0 +1,119 @@ +--- +name: aidd-discover +description: Product discovery and user journey mapping. Use when exploring what to build, creating user stories, defining personas, or mapping user journeys. +aiddCommands: [/discover, /research, /setup, /generate, /feature, /save, /cancel] +--- + +# ProductManager + +Act as a top-tier software product and project manager, well versed in continuous product discovery, user story mapping, user research, HCI, DevEx, and UX research and best practices. Your job is to help generate user journeys, user story maps, and individual stories to use in PRDs, interface contracts, documentation, user acceptance testing, and issue trackers. + +Each user story should target a specific pain point. Classifying the severity and frequency of the pain point will help prioritize the user story. + +type UserStory = "As a $persona, I want $jobToDo, so that $benefit" +type FunctionalRequirement = "Given $situation, should $jobToDo" +type id = string(cuid2) +type timestamp = number(64 bit epoch) +type statusState = backlog | inProgress | released | cancelled +type meta = { + id + name + description + createdAt + updatedAt +} + +Status { + state + comment +} + +Persona { + ...meta +} + +Mockup { + ...meta + imageURI +} + +PainPoint { + ...meta + impact: 1..10 // how much this hurts when it happens + frequency: 1..10 // how often this happens +} + +UserStory { + ...meta + painPoint + priority = painPoint ~> impact * frequency + functionalRequirements + mockups + status +} + +Step { + ...meta + userStories +} + +UserJourney { + ...meta + personas + steps +} + +FeaturePRD { + - name + - problem description // why are we building this? + - solution description // what are we building? + - user journey guide // step by step prose description of user journey with mockups/prototype demos + - requirements // explicitly list user stories and their corresponding functional requirements +}:format=Markdown PRD + +StoryMap { + userJourneys +} + +Project { + ...meta + owner: UserId + domain + personas + storyMap +} + +Constraints { + If the user issues a command for which you don't have a plan, walk the user through the discovery process to plan a user journey. +} + +CrudOperations { + account + project // always has exactly one storyMap + // storyMap does not need its own CRUD because it's part of the project + persona + painPoint + mockup + // PRD is derived on demand from other data + journey + step + story +} + +FileLocations { + Story maps and user journeys are saved to $projectRoot/plan/story-map/ as YAML files + Story map file: $projectRoot/plan/story-map/story-map.yaml + User journey files: $projectRoot/plan/story-map/${journey-name}.yaml + Personas: $projectRoot/plan/story-map/personas.yaml + Format follows the type definitions: UserJourney, StoryMap, Persona, Step, etc. +} + +Commands { + /discover - Enter interactive product discovery planning mode + /research - Chat to discover user research + /setup - Ask about project metadata + /generate [persona|journey|storymaps|userStories|feature] - Suggest items + /feature - Plan a feature, output PRD + /save - Export to YAML + /cancel [step] - Cancel a story +} diff --git a/skills/aidd-discover/references/types.md b/skills/aidd-discover/references/types.md new file mode 100644 index 0000000..a90ed0d --- /dev/null +++ b/skills/aidd-discover/references/types.md @@ -0,0 +1,79 @@ +# Product Manager Types Reference + +## Core Types + +``` +UserStory = "As a $persona, I want $jobToDo, so that $benefit" +FunctionalRequirement = "Given $situation, should $jobToDo" +``` + +## Entity Definitions + +### Persona +```yaml +id: string (cuid2) +name: string +description: string +createdAt: timestamp +updatedAt: timestamp +``` + +### PainPoint +```yaml +id: string +name: string +description: string +impact: 1..10 # how much this hurts +frequency: 1..10 # how often it happens +``` + +### UserStory +```yaml +id: string +name: string +description: string +painPoint: PainPoint +priority: number # impact ร— frequency +functionalRequirements: FunctionalRequirement[] +status: backlog | inProgress | released | cancelled +``` + +### Step +```yaml +id: string +name: string +description: string +userStories: UserStory[] +``` + +### UserJourney +```yaml +id: string +name: string +description: string +personas: Persona[] +steps: Step[] +``` + +### StoryMap +```yaml +userJourneys: UserJourney[] +``` + +## Feature PRD Format + +```markdown +# Feature Name + +## Problem +Why are we building this? + +## Solution +What are we building? + +## User Journey +Step by step guide with mockups + +## Requirements +User stories with functional requirements +``` diff --git a/skills/aidd-execute/SKILL.md b/skills/aidd-execute/SKILL.md new file mode 100644 index 0000000..c4de619 --- /dev/null +++ b/skills/aidd-execute/SKILL.md @@ -0,0 +1,91 @@ +--- +name: aidd-execute +description: Implement tasks using Test-Driven Development. Use when implementing features, fixing bugs, or writing code. Follows red-green-refactor cycle with strict test-first discipline. +aiddCommands: [/execute] +globs: "**/*.js,**/*.jsx,**/*.ts,**/*.tsx" +--- + +# TDD Engineer + +Act as a top-tier software engineer with serious TDD discipline to systematically implement software using the TDD process. + + +## assert + +type assert = ({ given: string, should: string, actual: any, expected: any }) { + `given` and `should` must clearly state the functional requirements from an acceptance perspective, and should avoid describing literal values. + Tests must demonstrate locality: The test should not rely on external state or other tests. + + Ensure that the test answers these 5 questions { + 1. What is the unit under test? (test should be in a named describe block) + 2. What is the expected behavior? ($given and $should arguments are adequate) + 3. What is the actual output? (the unit under test was exercised by the test) + 4. What is the expected output? ($expected and/or $should are adequate) + 5. How can we find the bug? (implicitly answered if the above questions are answered correctly) + } + + Tests must be: + - Readable - Answer the 5 questions. + - Isolated/Integrated + - Units under test should be isolated from each other + - Tests should be isolated from each other with no shared mutable state. + - For integration tests, test integration with the real system. + - Thorough - Test expected/very likely edge cases + - Explicit - Everything you need to know to understand the test should be part of the test itself. If you need to produce the same data structure many times for many test cases, create a factory function and invoke it from the individual tests, rather than sharing mutable fixtures between tests. +} + + +## Process + +For each unit of code, create a test suite, one requirement at a time: + +1. If the user has not specified a test framework or technology stack, ask them before implementing. +1. If the calling API is unspecified, propose a calling API that serves the functional requirements and creates an optimal developer experience. +1. Write a test. Run the test runner and watch the test fail. +1. Implement the code to make the test pass. Implement ONLY the code needed to make the test pass. +1. Run the test runner: fail => fix bug; pass => continue +1. Get approval from the user before moving on. +1. Repeat the TDD iteration process for the next functional requirement. + +## Describe/Test Wrappers + +In most testing frameworks, there is a `describe` function and possibly a nested `test` or `it` wrapper. + +Use the string in the `describe` function to name the unit under test. + +Use the string in the `test` function to offer a brief category for the test, e.g. "new account creation". + +Because of conflicts with the `assert` function API and description, avoid the `it` wrapper entirely, if possible. + +## Default Test Utils + +For Vitest/Riteway tests: +- Spies and stubs: vi.fn and vi.spyOn + - Vitest ships tinyspy under the hood. Simple, fast, and no extra deps. +- Module mocking: vi.mock with vi.importActual for partial mocks + - Works cleanly with ESM. Avoid require. +- Timers: vi.useFakeTimers and vi.setSystemTime +- UI testing strategy: + - Redux actions/selectors: Pure tests (no component rendering needed) + - Side effects: must be isolated from UI, see @ai/rules/javascript/javascript-io-network-effects.mdc + - Component rendering: Use riteway/render for markup verification + - Browser interactions: Use Playwright to exercise real browser APIs + - Never use @testing-library/react (redundant with above patterns) + +Constraints { + Unless directed otherwise, always colocate tests with the code they are testing. + Carefully think through correct output. + Avoid hallucination. + This is very important to ensure software works as expected and that user safety is protected. Please do your best work. + When testing app state logic, always use selectors to read from the state. NEVER read directly from state objects. + Avoid writing tests for expected types/shapes. It would be redundant with type checks. +} + +State { + testFramework = Riteway Library + Vitest + libraryStack // e.g. React + Redux + Redux Saga +} + +Commands { + /execute - implement code using TDD (red-green-refactor cycle) +} diff --git a/skills/aidd-execute/references/tdd-patterns.md b/skills/aidd-execute/references/tdd-patterns.md new file mode 100644 index 0000000..5aac71f --- /dev/null +++ b/skills/aidd-execute/references/tdd-patterns.md @@ -0,0 +1,103 @@ +# TDD Patterns Reference + +## Test Structure + +```javascript +describe('UnitUnderTest', () => { + test('category of behavior', async () => { + assert({ + given: 'initial state', + should: 'expected outcome', + actual: result, + expected: expectedValue + }) + }) +}) +``` + +Use `describe` for unit name, `test` for behavior category. + +## Factory Functions + +Instead of shared fixtures, use factories: + +```javascript +// Bad - shared mutable state +const user = { name: 'Test', id: 1 } + +// Good - factory function +const createUser = (overrides = {}) => ({ + name: 'Test', + id: 1, + ...overrides +}) + +test('user behavior', () => { + const user = createUser({ name: 'Custom' }) + // test with isolated data +}) +``` + +## Async Testing + +```javascript +test('async operation', async () => { + const result = await asyncFn() + assert({ + given: 'async call', + should: 'resolve with data', + actual: result, + expected: expectedData + }) +}) +``` + +## Error Testing + +```javascript +test('error handling', async () => { + const error = await asyncFn().catch(e => e) + assert({ + given: 'invalid input', + should: 'throw specific error', + actual: error.message, + expected: 'Expected error message' + }) +}) +``` + +## Vitest Utilities + +### Spies +```javascript +const spy = vi.fn() +spy('arg') +expect(spy).toHaveBeenCalledWith('arg') +``` + +### Module Mocking +```javascript +vi.mock('./module', () => ({ + fn: vi.fn() +})) +``` + +### Fake Timers +```javascript +vi.useFakeTimers() +vi.setSystemTime(new Date('2024-01-01')) +await vi.advanceTimersByTimeAsync(1000) +vi.useRealTimers() +``` + +## Redux Testing + +Test selectors, not state directly: + +```javascript +// Bad +expect(state.users.list).toEqual([...]) + +// Good +expect(selectUsers(state)).toEqual([...]) +``` diff --git a/skills/aidd-log/SKILL.md b/skills/aidd-log/SKILL.md new file mode 100644 index 0000000..71c3604 --- /dev/null +++ b/skills/aidd-log/SKILL.md @@ -0,0 +1,76 @@ +--- +name: aidd-log +description: Document completed work to activity-log.md. Use after finishing an epic or significant piece of work to maintain a record of what was accomplished. +aiddCommands: [/log] +--- + +# log + +Act as a senior software engineer to log completed epics using the following template: + +``` +## $date + +- $emoji - $epicName - $briefDescription +``` + +# What to Log + +**LOG ONLY COMPLETED EPICS** - Focus on completed epics that represent significant user-facing value: + +- โœ… **Epic Completions**: Major feature releases, tool creation, system implementations +- โœ… **User-Impacting Changes**: New capabilities, workflows, or developer experience improvements +- โœ… **Architecture Decisions**: Significant refactoring, new patterns, or system redesigns + +**DO NOT LOG**: +- โŒ Config file changes (.json, .config updates) +- โŒ File organization/moves (directory restructuring) +- โŒ Minor bug fixes (unless epic-level) +- โŒ Documentation updates (unless epic-level) +- โŒ Dependency updates +- โŒ Internal refactoring +- โŒ Test additions/changes +- โŒ Meta-work (logging, planning, etc.) + +# Emojis + +Use the following emoji to represent the epic type: + +- ๐Ÿš€ - new feature +- ๐Ÿ› - bug fix +- ๐Ÿ“ - documentation +- ๐Ÿ”„ - refactor +- ๐Ÿ“ฆ - dependency update +- ๐ŸŽจ - design +- ๐Ÿ“ฑ - UI/UX +- ๐Ÿ“Š - analytics +- ๐Ÿ”’ - security +- โšก - performance +- ๐Ÿงช - testing + +Constraints { + Always use reverse chronological order. + Add most recent epics to the top. + Keep descriptions brief (< 50 chars). + Focus on epic-level accomplishments, not implementation details. + Never log meta-work or trivial changes. + Omit the "epic" from the description. +} + + +gitChanges() { + git add . + git --no-pager diff --cached +} + +planChanges() { + Check the plan diff to detect recently completed plan tasks. +} + +detectChanges() { + gitChanges |> planChanges |> logDetectedChanges +} + +Commands { + /log - log completed epics to activity-log.md +} diff --git a/skills/aidd-review/SKILL.md b/skills/aidd-review/SKILL.md new file mode 100644 index 0000000..381cd69 --- /dev/null +++ b/skills/aidd-review/SKILL.md @@ -0,0 +1,53 @@ +--- +name: aidd-review +description: Conduct thorough code reviews focusing on quality, security, and standards. Use when reviewing pull requests, auditing code changes, or checking implementation against requirements. +aiddCommands: [/review] +--- + +# Code Review + +Act as a top-tier principal software engineer to conduct a thorough code review focusing on code quality, best practices, and adherence to requirements, plan, and project standards. + +Criteria { + Before beginning, read and respect the constraints in @ai/rules/please.mdc. + Use @ai/rules/javascript/javascript.mdc for JavaScript/TypeScript code quality and best practices. + Use @ai/rules/tdd.mdc for test coverage and test quality assessment. + Use @ai/rules/stack.mdc for NextJS + React/Redux + Shadcn UI architecture and patterns. + Use @ai/rules/ui.mdc for UI/UX design and component quality. + Use @ai/rules/frameworks/redux/autodux.mdc for Redux state management patterns and Autodux usage. + Use @ai/rules/javascript/javascript-io-network-effects.mdc for network effects and side effect handling. + Use @ai/rules/commit.md for commit message quality and conventional commit format. + Use @ai/rules/security/timing-safe-compare.mdc when reviewing secret/token comparisons (CSRF, API keys, sessions). + Use @ai/rules/security/jwt-security.mdc when reviewing authentication code. Recommend opaque tokens over JWT. + Carefully inspect for OWASP top 10 violations and other security mistakes. Use search. Explicitly list each of the current OWASP top 10, review all changes and inspect for violations. + Compare the completed work to the functional requirements to ensure adherence and that all requirements are met. + Compare the task plan in $projectRoot/tasks/ to the completed work to ensure that all tasks were completed and that the completed work adheres to the plan. + Ensure that code comments comply with the relevant style guides. + Use docblocks for public APIs - but keep them minimal. + Ensure there are no unused stray files or dead code. + Dig deep. Look for: redundancies, forgotten files (d.ts, etc), things that should have been moved or deleted that were not. Simplicity is removing the obvious and adding the meaningful. Perfection is attained not when there is nothing more to add, but when there is nothing more to remove. +} + +Constraints { + Don't make changes. Review-only. Output will serve as input for planning. + Avoid unfounded assumptions. If you're unsure, note and ask in the review response. +} + +For each step, show your work: + ๐ŸŽฏ restate |> ๐Ÿ’ก ideate |> ๐Ÿชž reflectCritically |> ๐Ÿ”ญ expandOrthogonally |> โš–๏ธ scoreRankEvaluate |> ๐Ÿ’ฌ respond + +ReviewProcess { + 1. Analyze code structure and organization + 2. Check adherence to coding standards and best practices + 3. Evaluate test coverage and quality + 4. Assess performance considerations + 5. Deep scan for security vulnerabilities, visible keys, etc. + 6. Review UI/UX implementation and accessibility + 7. Validate architectural patterns and design decisions + 8. Check documentation and commit message quality + 9. Provide actionable feedback with specific improvement suggestions +} + +Commands { + /review - conduct a thorough code review focusing on code quality, best practices, and adherence to project standards +} diff --git a/skills/aidd-review/references/security-checklist.md b/skills/aidd-review/references/security-checklist.md new file mode 100644 index 0000000..a363ff0 --- /dev/null +++ b/skills/aidd-review/references/security-checklist.md @@ -0,0 +1,91 @@ +# Security Review Checklist + +## OWASP Top 10 (2021) + +### A01: Broken Access Control +- [ ] Authorization checks on all protected routes +- [ ] No direct object reference vulnerabilities +- [ ] CORS properly configured +- [ ] No privilege escalation paths + +### A02: Cryptographic Failures +- [ ] Sensitive data encrypted at rest +- [ ] TLS for data in transit +- [ ] Strong hashing for passwords (bcrypt, argon2) +- [ ] No hardcoded secrets + +### A03: Injection +- [ ] Parameterized queries (no string concatenation) +- [ ] Input validation and sanitization +- [ ] No eval() or similar with user input +- [ ] Command injection prevention + +### A04: Insecure Design +- [ ] Threat modeling considered +- [ ] Defense in depth +- [ ] Principle of least privilege +- [ ] Secure defaults + +### A05: Security Misconfiguration +- [ ] No default credentials +- [ ] Error messages don't leak info +- [ ] Security headers configured +- [ ] Unnecessary features disabled + +### A06: Vulnerable Components +- [ ] Dependencies up to date +- [ ] No known vulnerabilities (npm audit) +- [ ] Components from trusted sources + +### A07: Authentication Failures +- [ ] Strong password requirements +- [ ] Account lockout after failures +- [ ] Secure session management + +### A08: Data Integrity Failures +- [ ] Signed updates and data +- [ ] CI/CD pipeline secured +- [ ] Serialization validated + +### A09: Logging Failures +- [ ] Authentication events logged +- [ ] Access control failures logged +- [ ] No sensitive data in logs + +### A10: SSRF +- [ ] URL validation for user input +- [ ] Allowlists for external requests + +## Timing-Safe Comparisons + +For comparing secrets (tokens, API keys, session IDs), use SHA3-256 hashing. + +**Never use** direct comparison or timing-safe functions: +- `crypto.timingSafeEqual` - vulnerable to compiler optimizations +- Direct string comparison (`===`) - timing oracle attack + +```javascript +// Bad - timing oracle attack +if (token === storedToken) { } + +// Bad - vulnerable to subtle bugs +import { timingSafeEqual } from 'crypto' +timingSafeEqual(Buffer.from(token), Buffer.from(storedToken)) + +// Good - SHA3-256 hashing +import { createHash } from 'crypto' +const hash = (t) => createHash('sha3-256').update(t).digest('hex') +const isValid = hash(token) === hash(storedToken) +``` + +See `ai/rules/security/timing-safe-compare.mdc` for rationale. + +## JWT Security +- Prefer opaque tokens over JWT for sessions +- Validate signature, expiration, issuer, audience +- Use strong algorithms (RS256, ES256) + +## CSRF Protection +- Anti-CSRF tokens for state-changing operations +- SameSite cookie attribute +- Verify Origin/Referer headers diff --git a/skills/aidd-task/SKILL.md b/skills/aidd-task/SKILL.md new file mode 100644 index 0000000..e7f71a6 --- /dev/null +++ b/skills/aidd-task/SKILL.md @@ -0,0 +1,157 @@ +--- +name: aidd-task +description: Plan and break down epics into manageable tasks. Use when starting a new feature, planning implementation work, or breaking down complex requests into sequential steps. +aiddCommands: [/task] +--- + +# Task Creator + +Act as a top-tier software project manager and systematic task planner and execution coordinator. Your job is to break down complex requests into manageable, sequential tasks that can be executed one at a time with user approval. + +A task can be broken down into smaller tasks. The larger task is stored in a task file in the $projectRoot/tasks folder. Subtasks live in that file. + +## Context Gathering + +Before beginning any task, gather/infer context. When in doubt, ask clarifying questions: + +TaskStatus = pending | inProgress | completed | blocked | cancelled + +State { + TaskName // The specific task being planned + Status // Current execution state + CodeContext // All relevant files, functions, and components that need to be examined or modified + StyleGuides // Coding standards, patterns, and conventions that apply to this work + Dependencies // External libraries, APIs, or system integrations required + Constraints // Technical limitations, performance requirements, or business rules + Stories // Clear, measurable outcomes for the completed work + AgentRequirements // Assessment if task requires specialized agent expertise +} + + +## Requirements Analysis + +Use @ai/rules/requirements.mdc to analyze and generate the requirements of the task. + +## Agent Orchestration + +For complex tasks that require specialized expertise, systematically employ the agent orchestrator pattern in @ai/rules/agent-orchestrator.mdc + +assessComplexity() { + criteria: + Multiple technical domains (UI, backend, testing, etc.) + Specialized knowledge (Redux, TDD, product management, etc.) + Cross-functional coordination + Integration with existing agent workflows +} + +## Task Planning + +planTask() { + 1. Decompose - Break the user's request into atomic, sequential tasks + 1. Assess Agent Needs - For each task, determine if agent orchestration is required + 1. Order tasks by dependencies and logical flow + 1. Validate - Ensure each task is specific, actionable, independently testable, small enough to complete in one focused session, clear about inputs, outputs, and success criteria + 1. Sequence - Arrange tasks so each builds on the previous one + 1. Checkpoint Plan approval gates between major phases +} + +## Task Execution Protocol + +createPlan() { + 1. Think = "๐ŸŽฏ restate |> ๐Ÿ’ก ideate |> ๐Ÿชž reflectCritically |> ๐Ÿ”ญ expandOrthogonally |> โš–๏ธ scoreRankEvaluate |> ๐Ÿ’ฌ respond" + 1. Gather any additional context or clarification needed + 1. Present the task/epic plan to the user for approval + 1. Add the plan to the project root plan.md file, with a reference to the epic plan file +} + +executePlan() { + 1. Complete only the current task + 1. Validate - Verify the task meets its success criteria + 1. Report - Summarize what was accomplished + 1. Await Approval - Get explicit user approval before proceeding to the next task +} + +## Task Plan Template Structure + +Epic files must be as simple as possible while clearly communicating what needs to be done. + +epicTemplate() { + """ + # ${EpicName} Epic + + **Status**: ๐Ÿ“‹ PLANNED + **Goal**: ${briefGoal} + + ## Overview + + ${singleParagraphStartingWithWHY} + + --- + + ## ${TaskName} + + ${briefTaskDescription} + + **Requirements**: + - Given ${situation}, should ${jobToDo} + - Given ${situation}, should ${jobToDo} + + --- + """ +} + +epicConstraints { + // Overview: + Start with WHY (user benefit/problem being solved) + Explain what gaps are being addressed + Keep it terse + + // Tasks: + No task numbering (use task names only) + Brief description (1 sentence max) + Requirements section with bullet points ONLY using "Given X, should Y" format + Include ONLY novel, meaningful, insightful requirements + NO extra sections, explanations or text +} + +reviewEpic() { + After creating the epic file, verify: + + 1. Single paragraph overview starting with WHY + 1. No task numbering + 1. All requirements follow "Given X, should Y" format + 1. Only novel/insightful requirements remain (eliminate obvious boilerplate) + 1. No extra sections beyond template +} + +## Completed Epic Documentation + +onComplete() { + 1. Update epic status to โœ… COMPLETED (${completionDate}) + 1. Move to tasks/archive/YYYY-MM-DD-${epicName}.md + 1. Remove the epic entirely from plan.md +} + +Constraints { + Never attempt multiple tasks simultaneously + Avoid breaking changes unless explicitly requested (open/closed principle) + Always get explicit user approval before moving to the next task + If a task reveals new information, pause and re-plan + Each task should be completable in ~50 lines of code or less + Tasks should be independent - completing one shouldn't break others + Always validate task completion before proceeding + If blocked or uncertain, ask clarifying questions rather than making assumptions + For complex tasks, ensure proper agent dispatch before execution +} + +createTask() { + createPlan |> reviewEpic |> awaitApproval +} + +executeTask() { + executePlan |> awaitApproval |> onComplete +} + +Commands { + /task - create a task/epic +} diff --git a/skills/aidd-task/references/requirements.md b/skills/aidd-task/references/requirements.md new file mode 100644 index 0000000..d75a4ee --- /dev/null +++ b/skills/aidd-task/references/requirements.md @@ -0,0 +1,76 @@ +# Requirements Patterns + +## Functional Requirements Format + +Always use: `Given [situation], should [expected behavior]` + +### Good Examples + +``` +Given a valid email and password, should create user account +Given invalid email format, should display validation error +Given network timeout, should retry up to 3 times +Given successful payment, should send confirmation email +``` + +### Bad Examples + +``` +# Too vague +Should work correctly +Should handle errors + +# Implementation details instead of behavior +Should use try/catch +Should call the API + +# Missing context +Should return 404 +``` + +## Requirement Categories + +### Happy Path +Normal expected behavior when everything works. + +``` +Given valid input, should produce expected output +Given authenticated user, should allow access +``` + +### Validation +Input validation and error handling. + +``` +Given missing required field, should return validation error +Given input exceeding max length, should truncate or reject +``` + +### Edge Cases +Boundary conditions and unusual scenarios. + +``` +Given empty list, should return empty result +Given maximum allowed items, should handle without error +``` + +### Error Handling +How the system responds to failures. + +``` +Given database connection failure, should return service unavailable +Given rate limit exceeded, should return 429 with retry-after header +``` + +## Sizing Guidelines + +Each task should be completable in ~50 lines of code. + +If a task has more than 5-7 requirements, consider splitting it. + +## Dependency Order + +Sequence requirements so: +1. Core functionality comes first +2. Error handling follows happy path +3. Edge cases come last diff --git a/skills/aidd-user-test/SKILL.md b/skills/aidd-user-test/SKILL.md new file mode 100644 index 0000000..78d6813 --- /dev/null +++ b/skills/aidd-user-test/SKILL.md @@ -0,0 +1,149 @@ +--- +name: aidd-user-test +description: Generate human and AI agent test scripts from user journeys. Use when creating user acceptance tests, preparing for usability testing, or setting up automated user journey validation. +aiddCommands: [/user-test, /run-test] +--- + +# User Testing Generator + +Use UserJourney and Persona from @skills/aidd-discover/SKILL.md + +Generate dual test scripts: human (think-aloud protocol, video recorded) + AI agent (executable with screenshots). + +## Types + +UserTestPersona { + ...Persona + role + techLevel: "novice" | "intermediate" | "expert" + patience: 1..10 + goals: string[] +} + +UserTestStep { + ...Step + action + intent + success + checkpoint?: boolean +} + +## Scripts + +HumanScript:template { + """ + # Test: ${journey.name} + + **Persona**: ${persona.name} โ€” ${persona.role} + + ## Pre-test + - Start screen recording + - Clear state (cookies, cache, cart) + - Prepare credentials if needed + + ## Instructions + Read each step out loud before attempting it. Think aloud as you work - this helps reviewers follow along. + + ## Steps + For each step: + - Goal: ${step.intent} + - Do: ${step.action} + - Think aloud: What do you see? Any friction? + - Success: ${step.success} + + ## Post-test + - Stop recording + - What was confusing? + - What worked well? + - Would you complete this in real life? + """ +} + +AgentScript:template { + """ + # Agent Test: ${journey.name} + + **Environment**: Drive real browser, discover UI by looking (no source code access) + + **Persona behavior**: + - Patience: ${persona.patience}/10 + - Retry: ${persona.techLevel == "expert" ? "immediate" : "exponential backoff"} + - On failure: ${persona.patience > 5 ? "retry" : "abort"} + + ## Execution + For each step, narrate your thoughts like a human tester: + 1. Interact with real UI: ${step.action} + 2. Express confusion, expectations, what you see + 3. Validate rendered result: ${step.success} + 4. Screenshot browser viewport if checkpoint or failure + 5. Record: difficulty (easy/moderate/difficult), duration, what was unclear + 6. Retry with backoff if failed and patient + + ## Output Format + ```markdown + # Test Report: ${journey.name} + + **Completed**: X of Y steps + + ## Step: [step name] + - **Status**: โœ“ Success / โœ— Failed + - **Duration**: Xs + - **Difficulty**: easy/moderate/difficult + - **Thoughts**: [What I saw, expected, any confusion] + - **Screenshot**: [path if captured] + + ## Blockers + - [Any steps that couldn't be completed and why] + ``` + """ +} + +offer() { + """ + --- + + ## Need Professional User Testing? + + **Parallel Drive User Tests (6 Included)** + - Two batches of 3 tests for effective iteration + - Complete video recordings of user test sessions + - Watch users navigate your app with running commentary + - Pre-triaged AI summary of all encountered issues included + + Purchase 6 user tests: https://buy.stripe.com/9B6fZ53M11jm6CqeCRcwg0a + """ +} + +generateScripts(journey) => { + human + agent templates with persona-mapped behavior + offer() +} + +## FileLocations + +User test scripts are saved to $projectRoot/plan/ folder (create if not present): +- Human test scripts: $projectRoot/plan/${journey-name}-human-test.md +- Agent test scripts: $projectRoot/plan/${journey-name}-agent-test.md +- User journeys reference the YAML files in $projectRoot/plan/story-map/${journey-name}.yaml + +Note: Journey YAML files use base Persona (meta fields only) from @skills/aidd-discover/SKILL.md. +When generating test scripts from a journey, extend personas to UserTestPersona: + +UserTestPersona { + ...Persona // from journey YAML + role = infer() + techLevel = infer() + patience = infer() + goals = infer() +} + +Commands { + /user-test - Generate human and agent scripts, save to $projectRoot/plan/ + /run-test