feat: add interactive CLI installer for multi-agent rule setup#166
feat: add interactive CLI installer for multi-agent rule setup#166inariku wants to merge 6 commits intoawslabs:mainfrom
Conversation
- Supports all 6 documented agents (Kiro, Amazon Q, Cursor, Cline, Claude Code, GitHub Copilot) - Interactive agent selection with auto-detection - Symlink and copy installation modes - Lock file for tracking installations - Uninstall (remove) and list commands - CI-friendly flags (--yes, --agent, --copy) Refs: awslabs#165
There was a problem hiding this comment.
Pull request overview
Adds a new Node-based CLI (npx @inariku/aidlc-install) to install/uninstall AI-DLC rule files for multiple supported coding agents, aiming to automate the manual setup steps currently documented in the repo README.
Changes:
- Introduces an interactive/non-interactive CLI with agent auto-detection, install mode selection (symlink/copy), and
add/remove/listcommands. - Adds per-user config + lockfile under
~/.aidlc/to remember selections and track installed paths for uninstall. - Adds a dedicated
cli/package with documentation and npm metadata.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
cli/src/index.mjs |
Implements the installer/uninstaller CLI, agent definitions, filesystem operations, and lock/config handling. |
cli/README.md |
Documents usage, supported agents, commands, and CI flags. |
cli/package.json |
Defines the published CLI package metadata, bin entry, dependencies, and pack scripts. |
cli/package-lock.json |
Locks npm dependencies for the CLI package. |
cli/.gitignore |
Ignores CLI build artifacts and vendored rule files during packing. |
Files not reviewed (1)
- cli/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "version": "0.1.0", | ||
| "lockfileVersion": 3, | ||
| "requires": true, | ||
| "packages": { | ||
| "": { | ||
| "name": "@inariku/aidlc-install", | ||
| "version": "0.1.0", |
There was a problem hiding this comment.
package.json is versioned as 0.1.1, but package-lock.json still has 0.1.0 at the root and in packages[""]. This version skew can cause confusing publishes/installs and should be updated to match the package version.
| "version": "0.1.0", | |
| "lockfileVersion": 3, | |
| "requires": true, | |
| "packages": { | |
| "": { | |
| "name": "@inariku/aidlc-install", | |
| "version": "0.1.0", | |
| "version": "0.1.1", | |
| "lockfileVersion": 3, | |
| "requires": true, | |
| "packages": { | |
| "": { | |
| "name": "@inariku/aidlc-install", | |
| "version": "0.1.1", |
| | Agent | Install Path | | ||
| |-------|-------------| | ||
| | Kiro | `.kiro/steering/aws-aidlc-rules` | | ||
| | Amazon Q Developer | `.amazonq/rules/aws-aidlc-rules` | | ||
| | Cursor IDE | `.cursor/rules/ai-dlc-workflow.mdc` | | ||
| | Cline | `.clinerules/core-workflow.md` | | ||
| | Claude Code | `CLAUDE.md` | | ||
| | GitHub Copilot | `.github/copilot-instructions.md` | |
There was a problem hiding this comment.
The markdown table under "Supported Agents" uses || at the start of each row, which renders as an extra empty column in many renderers. Use a single leading | per row to form a standard 2-column table.
| | Agent | Install Path | | ||
| |-------|-------------| | ||
| | Kiro | `.kiro/steering/aws-aidlc-rules` | | ||
| | Amazon Q Developer | `.amazonq/rules/aws-aidlc-rules` | | ||
| | Cursor IDE | `.cursor/rules/ai-dlc-workflow.mdc` | | ||
| | Cline | `.clinerules/core-workflow.md` | | ||
| | Claude Code | `CLAUDE.md` | | ||
| | GitHub Copilot | `.github/copilot-instructions.md` | |
There was a problem hiding this comment.
The "Supported Agents" table lists only the primary rules path, but the installer also writes the rule-details directory (e.g., .aidlc-rule-details/ or .kiro/aws-aidlc-rule-details). Consider documenting both paths per agent here so users know what files/folders will be created/overwritten.
| | Agent | Install Path | | |
| |-------|-------------| | |
| | Kiro | `.kiro/steering/aws-aidlc-rules` | | |
| | Amazon Q Developer | `.amazonq/rules/aws-aidlc-rules` | | |
| | Cursor IDE | `.cursor/rules/ai-dlc-workflow.mdc` | | |
| | Cline | `.clinerules/core-workflow.md` | | |
| | Claude Code | `CLAUDE.md` | | |
| | GitHub Copilot | `.github/copilot-instructions.md` | | |
| | Agent | Rules Path | Rule Details Path | | |
| |-------|------------|-------------------| | |
| | Kiro | `.kiro/steering/aws-aidlc-rules` | `.kiro/aws-aidlc-rule-details/` | | |
| | Amazon Q Developer | `.amazonq/rules/aws-aidlc-rules` | `.aidlc-rule-details/` | | |
| | Cursor IDE | `.cursor/rules/ai-dlc-workflow.mdc` | `.aidlc-rule-details/` | | |
| | Cline | `.clinerules/core-workflow.md` | `.aidlc-rule-details/` | | |
| | Claude Code | `CLAUDE.md` | `.aidlc-rule-details/` | | |
| | GitHub Copilot | `.github/copilot-instructions.md` | `.aidlc-rule-details/` | |
| if (isCancel(choice)) cancel(); | ||
| toRemove = relevant.filter((e) => choice.includes(e.agent)); | ||
| } | ||
|
|
There was a problem hiding this comment.
When remove is invoked with --agent and none of the specified agents are installed for this cwd, toRemove becomes empty and the command still proceeds (prompting "Remove rules for ?" and then doing nothing). Add a guard to error out (or at least outro and exit non-zero) when toRemove.length === 0 after filtering.
| if (!toRemove || toRemove.length === 0) { | |
| outro("No matching agents found to remove in this project."); | |
| process.exit(1); | |
| } |
| for (const entry of toRemove) { | ||
| for (const p of entry.paths) { | ||
| if (existsSync(p)) rmSync(p, { recursive: true }); | ||
| } |
There was a problem hiding this comment.
remove deletes every path recorded in the global lock file without validating that those paths are inside the current project directory. If ~/.aidlc/lock.json is stale or manually edited, this could delete arbitrary filesystem locations. Before deleting, verify each recorded path starts with cwd (or matches the expected install locations for the agent) and refuse to proceed otherwise.
| const mdc = join(cwd, ".cursor", "rules", "ai-dlc-workflow.mdc"); | ||
| const d = join(cwd, ".aidlc-rule-details"); | ||
| return [ | ||
| { dest: mdc, type: "generate-mdc" }, | ||
| { src: detailsDir, dest: d, type: "dir-contents" }, | ||
| ]; | ||
| }, | ||
| paths: [".cursor/rules/ai-dlc-workflow.mdc", ".aidlc-rule-details/"], |
There was a problem hiding this comment.
Cursor agent detection includes .cursorrules, but installation always writes to .cursor/rules/ai-dlc-workflow.mdc. If a project uses .cursorrules (and not .cursor), the tool will mark Cursor as detected yet install rules where they may not be picked up. Either align detection to the supported install path or add logic to install into .cursorrules when that's what was detected.
| const mdc = join(cwd, ".cursor", "rules", "ai-dlc-workflow.mdc"); | |
| const d = join(cwd, ".aidlc-rule-details"); | |
| return [ | |
| { dest: mdc, type: "generate-mdc" }, | |
| { src: detailsDir, dest: d, type: "dir-contents" }, | |
| ]; | |
| }, | |
| paths: [".cursor/rules/ai-dlc-workflow.mdc", ".aidlc-rule-details/"], | |
| const useCursorRulesDir = | |
| existsSync(join(cwd, ".cursorrules")) && !existsSync(join(cwd, ".cursor")); | |
| const baseDir = useCursorRulesDir ? ".cursorrules" : ".cursor"; | |
| const mdc = join(cwd, baseDir, "rules", "ai-dlc-workflow.mdc"); | |
| const d = join(cwd, ".aidlc-rule-details"); | |
| return [ | |
| { dest: mdc, type: "generate-mdc" }, | |
| { src: detailsDir, dest: d, type: "dir-contents" }, | |
| ]; | |
| }, | |
| paths: [ | |
| ".cursor/rules/ai-dlc-workflow.mdc", | |
| ".cursorrules/rules/ai-dlc-workflow.mdc", | |
| ".aidlc-rule-details/", | |
| ], |
| value: "claude", | ||
| label: "Claude Code", | ||
| hint: "CLAUDE.md", | ||
| detect: [".claude", "CLAUDE.md"], | ||
| // cp core-workflow.md ./CLAUDE.md + cp -R aws-aidlc-rule-details/* .aidlc-rule-details/ | ||
| install(cwd) { | ||
| const r = join(cwd, "CLAUDE.md"); | ||
| const d = join(cwd, ".aidlc-rule-details"); | ||
| return [ | ||
| { src: coreWorkflow, dest: r, type: "file" }, | ||
| { src: detailsDir, dest: d, type: "dir-contents" }, | ||
| ]; | ||
| }, | ||
| paths: ["CLAUDE.md", ".aidlc-rule-details/"], | ||
| }, |
There was a problem hiding this comment.
Claude Code detection includes .claude, but installation always writes to ./CLAUDE.md in the project root. The upstream README documents .claude/CLAUDE.md as an alternative supported location; as written, projects already using .claude/ will be detected but still get the file installed to the root. Consider prompting for (or auto-selecting) the target location based on what exists in the repo, to better match the documented setup options.
| if (op.type === "file") { | ||
| mkdirSync(dirname(op.dest), { recursive: true }); | ||
| copyFileSync(op.src, op.dest); | ||
| return "copy"; | ||
| } |
There was a problem hiding this comment.
In "symlink" mode, file operations (Cline / Claude / Copilot core workflow) are still copied via copyFileSync, so these installs will not auto-update even though the UI describes symlink mode as linking to source. Either implement file symlinks for op.type === "file" when mode === "symlink", or adjust the mode description/output so users aren't misled.
| } else { | ||
| runAdd(); |
There was a problem hiding this comment.
Unknown positionals are treated as the default add command (anything other than remove/list falls through to runAdd()). This can mask typos like aidlc-install remvoe. Consider validating command against an allowlist (add, remove, list) and showing help / exiting non-zero on unknown commands.
| } else { | |
| runAdd(); | |
| } else if (!command || command === "add") { | |
| runAdd(); | |
| } else { | |
| console.error(`Unknown command: ${command}`); | |
| showHelp(); | |
| process.exitCode = 1; |
| function trySymlink(src, dest) { | ||
| try { | ||
| mkdirSync(dirname(dest), { recursive: true }); | ||
| if (existsSync(dest)) rmSync(dest, { recursive: true }); | ||
| symlinkSync(src, dest, "dir"); | ||
| return true; |
There was a problem hiding this comment.
trySymlink() uses existsSync(dest) to decide whether to remove the destination before creating a new symlink. existsSync returns false for broken symlinks (it stats the target), so an existing broken link at dest can cause symlinkSync to throw EEXIST and force an unnecessary fallback to copy. Consider using lstatSync/rmSync(dest, { force: true }) (or a try/catch around rmSync) to reliably remove an existing symlink even when its target is missing.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 4 comments.
Files not reviewed (1)
- cli/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (op.type === "dir-contents") { | ||
| if (mode === "symlink" && trySymlink(op.src, op.dest)) return "symlink"; | ||
| copyDirContents(op.src, op.dest); | ||
| return "copy"; | ||
| } |
There was a problem hiding this comment.
Same overwrite concern for dir-contents: if .aidlc-rule-details already exists (or is a symlink), cpSync will copy into/over it rather than replacing it cleanly. Consider removing the destination first when overwriting to keep installs deterministic.
| } else if (flags.agent.length > 0) { | ||
| toRemove = relevant.filter((e) => flags.agent.includes(e.agent)); | ||
| } else { |
There was a problem hiding this comment.
If --agent is provided, toRemove = relevant.filter(...) can produce an empty list when none of the requested agents are installed in this cwd. Add a toRemove.length === 0 check before the confirmation prompt and exit with a clear message (and optionally a non-zero status).
| if (op.type === "dir") { | ||
| if (mode === "symlink" && trySymlink(op.src, op.dest)) return "symlink"; | ||
| copyDir(op.src, op.dest); | ||
| return "copy"; | ||
| } |
There was a problem hiding this comment.
For directory installs, the copy path doesn’t remove an existing destination before cpSync. Since the flow warns about overwriting, delete op.dest first (including if it’s a symlink from a prior install) to avoid merges/nesting or copying into a symlink target.
| | Agent | Install Path | | ||
| |-------|-------------| | ||
| | Kiro | `.kiro/steering/aws-aidlc-rules` | | ||
| | Amazon Q Developer | `.amazonq/rules/aws-aidlc-rules` | | ||
| | Cursor IDE | `.cursor/rules/ai-dlc-workflow.mdc` | | ||
| | Cline | `.clinerules/core-workflow.md` | | ||
| | Claude Code | `CLAUDE.md` | | ||
| | GitHub Copilot | `.github/copilot-instructions.md` | | ||
|
|
There was a problem hiding this comment.
The “Supported Agents” table omits the rule-details destinations that are also installed (e.g. .aidlc-rule-details/, .kiro/aws-aidlc-rule-details, .amazonq/aws-aidlc-rule-details). Update this section to include both installed paths so the docs match actual installer behavior.
| | Agent | Install Path | | |
| |-------|-------------| | |
| | Kiro | `.kiro/steering/aws-aidlc-rules` | | |
| | Amazon Q Developer | `.amazonq/rules/aws-aidlc-rules` | | |
| | Cursor IDE | `.cursor/rules/ai-dlc-workflow.mdc` | | |
| | Cline | `.clinerules/core-workflow.md` | | |
| | Claude Code | `CLAUDE.md` | | |
| | GitHub Copilot | `.github/copilot-instructions.md` | | |
| | Agent | Install Path(s) | | |
| |-------|-----------------| | |
| | Kiro | `.kiro/steering/aws-aidlc-rules`, `.kiro/aws-aidlc-rule-details` | | |
| | Amazon Q Developer | `.amazonq/rules/aws-aidlc-rules`, `.amazonq/aws-aidlc-rule-details` | | |
| | Cursor IDE | `.cursor/rules/ai-dlc-workflow.mdc` | | |
| | Cline | `.clinerules/core-workflow.md` | | |
| | Claude Code | `CLAUDE.md` | | |
| | GitHub Copilot | `.github/copilot-instructions.md` | | |
| The installer also creates a workspace-level `.aidlc-rule-details/` directory that contains shared rule details, alongside the per-agent rule-details directories such as `.kiro/aws-aidlc-rule-details` and `.amazonq/aws-aidlc-rule-details`. |
Summary
Adds an interactive CLI tool that installs AI-DLC rules to the correct paths for each supported agent, matching the README instructions exactly.
Closes #165
Changes
cli/directory with an npm package (@inariku/aidlc-install)src/index.mjs) using@clack/promptsfor interactive UIUser experience
Before: Users manually run multiple
mkdirandcpcommands per agent, following README instructions.After:
Interactive prompt auto-detects agents and installs rules to the correct paths.
Checklist
Test Plan
npx @inariku/aidlc-installin a project directorynpx @inariku/aidlc-install listto confirm trackingnpx @inariku/aidlc-install removeto clean upAcknowledgment
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the project license.