Skip to content

feat: aidd create --prompt epic#143

Merged
ericelliott merged 93 commits intomainfrom
cursor/aidd-prompt-epic-ab39
Mar 29, 2026
Merged

feat: aidd create --prompt epic#143
ericelliott merged 93 commits intomainfrom
cursor/aidd-prompt-epic-ab39

Conversation

@ericelliott
Copy link
Copy Markdown
Collaborator

@ericelliott ericelliott commented Mar 11, 2026

This PR implements the "aidd-create-prompt-epic," introducing comprehensive agent delegation capabilities to the aidd framework.

Key Changes:

New CLI surface

  • npx aidd agent --prompt "<text>" — new subcommand that delegates a prompt to an AI agent (claude, opencode, or cursor) directly from the terminal
  • --prompt <text> on npx aidd create — after scaffolding completes, runs an AI agent in the new project directory to kick off autonomous development
  • --agent-config <name|path> (renamed from --agent) — configures which AI agent to use; accepts a preset name, YAML config path, or falls through to env var / config file / claude default
  • Agent config resolution chain: explicit flag → AIDD_AGENT_CONFIG env var → agent-config in aidd-custom/config.yml → claude

Agent runtime modules (lib/agent-cli/)

  • errors.jsAgentConfigReadError, AgentConfigParseError, AgentConfigValidationError
  • config.jsgetAgentConfig (preset map) + resolveAgentConfig (full resolution chain)
  • runner.jsrunAgent no-shell spawn with E2BIG, ENOBUFS, signal, and ENOENT handling
  • command.jsregisterAgentCommand wired into bin/aidd.js

Programmatic exports

  • aidd/agentrunAgent with full TypeScript types
  • aidd/agent-configgetAgentConfig / resolveAgentConfig with full TypeScript types

Manifest integration

  • parseManifest prompt-ordering guard — a prompt: step must follow a run: step that invokes the aidd CLI
  • runManifest uses the agent resolution chain for prompt steps (non-interactive, no shell injection)

Quality improvements (from review)

  • ScaffoldDestinationError — dedicated error type with clear UX message when destination folder exists
  • Auto-cleanup of ~/.aidd/scaffold/ in a finally block after npx aidd create <url> (success or failure)
  • agent-config documented in docs/aidd-custom.md and pre-populated as a commented example in the config template
  • All tests use real filesystem / real processes — no mock IO injections
  • TDD skill updated: "Mocking is a code smell" constraint added

@cursor
Copy link
Copy Markdown
Contributor

cursor bot commented Mar 11, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@ericelliott
Copy link
Copy Markdown
Collaborator Author

@cursoragent please /review using ai/commands/review

@cursor
Copy link
Copy Markdown
Contributor

cursor bot commented Mar 13, 2026

Now I have all the context needed for a complete review. Let me produce it:


🔬 Code Review — PR #143: Aidd Prompt Epic

1. Churn Analysis

Top hotspots in the diff cross-referenced against the churn leaderboard:

File Churn Score In Diff? Risk
lib/scaffold-runner.js 13,104 ✅ Modified High — already highest-complexity file in the diff, now gains new import coupling
lib/scaffold-runner.test.js 11,592 ✅ Modified Medium
lib/scaffold-commands.js 5,568 ✅ Modified Medium
lib/scaffold-create.test.js 4,023 ✅ Modified Low
bin/create-e2e.test.js 4,950 ✅ Modified Low
lib/cli-core.js 28,240 ✅ Modified Highest score in project — single-line template change, but any regression here is high-impact

New files (lib/agent-cli/) are currently low-churn by definition but will become hotspots as the agent system matures.


2. Code Structure and Organization

🎯 Is the new code well-organized?

✅ Good:

  • lib/agent-cli/ is a clean collocation of the full agent invocation stack — errors, config, runner, command. Feature-grouped, not type-grouped. Aligns with JS guide.
  • registerAgentCommand(program) follows the same pattern as registerScaffoldCommands(program). Consistent.
  • Single import + single call in bin/aidd.js. Epic requirement honored exactly.
  • resolveAgentConfig covers the full resolution chain (explicit → env → config file → default) in one place.

⚠️ Concern:

  • lib/scaffold-runner.js now has two hard imports from lib/agent-cli/ (resolveAgentConfig, runAgent) at the module level. This creates a circular-ish coupling: scaffold-runner depends on agent-cli, and agent-cli/runner depends on scaffold-errors (from the same lib/ root). It's not a circular import, but it makes scaffold-runner harder to test without mocking two new modules. The existing tests use vi.mock to work around this, so it functions — but it's worth noting for future complexity.

3. Coding Standards and Best Practices

🎯 Does the code follow the JavaScript guide?

✅ Good:

  • Arrow functions, destructuring, default parameters in signatures — all consistent with the guide.
  • isYamlPath is a clean predicate (reads like a yes/no question).
  • Functions are short and single-purpose.
  • @ts-expect-error comments are used appropriately rather than @ts-ignore.

❌ Issue — getAgentConfig returns a mutable direct reference to a shared preset:

  return preset;

agentPresets.claude etc. are shared module-level objects. getAgentConfig('claude') returns the same object identity on every call:

getAgentConfig('claude') === getAgentConfig('claude') // true
getAgentConfig('claude').args === getAgentConfig('claude').args // true — same array

Any caller that mutates result.args corrupts the preset for the entire process lifetime. The JS guide says "Prefer immutability; use const, spread, and rest operators instead of mutation." Should return { command: preset.command, args: [...preset.args] }.

❌ Issue — command.js error handler has identical bodies for all three handleAgentErrors branches:

          handleAgentErrors({
            AgentConfigParseError: ({ message }) => {
              console.error(chalk.red(`\n❌ Agent config error: ${message}`));
            },
            AgentConfigReadError: ({ message }) => {
              console.error(chalk.red(`\n❌ Agent config error: ${message}`));
            },
            AgentConfigValidationError: ({ message }) => {
              console.error(chalk.red(`\n❌ Agent config error: ${message}`));
            },
          })(/** @type {Error} */ (err));

All three handlers have identical bodies. The JS guide says DRY. A single shared handler or a lookup map would suffice. Minor, but real redundancy.


4. Test Coverage and Quality

🎯 Are tests thorough, minimal, and following TDD patterns?

✅ Good:

  • All new modules have colocated test files with riteway/vitest assert calls.
  • errors.test.js covers shape, thrown error carry-through, exhaustive handler enforcement, and routing.
  • config.test.js covers all 13 requirements including YAML loading, error types, env var resolution, aidd-custom/config.yml fallback, and the default.
  • command.test.js covers all 5 requirements cleanly with injectable mocks.
  • scaffold-runner.test.js properly mocks resolveAgentConfig and runAgent at module level.

❌ Issue — runner.test.js has no test for the spawn error event (ENOENT):

The test file covers close events (non-zero exit and zero exit) but has no test for when spawn itself emits error (e.g., command not found). This is the companion to the runtime bug below.

❌ Issue — lib/exports.test.js has no coverage for ./agent and ./agent-config exports:

The existing exports.test.js covers ./server and ./utils with import tests and subpath existence assertions. The new ./agent and ./agent-config entries were added to package.json but no corresponding tests were added. The epic explicitly required these exports.


5. Performance

❌ Issue — resolveAgentConfig is called on every prompt: step in runManifest:

      const agentCfg = await resolveAgentConfig({
        cwd: folder,
        value: agentConfig,
      });
      await runAgent({
        agentConfig: agentCfg,
        cwd: folder,
        prompt: step.prompt,
      });

For a manifest with N prompt steps, resolveAgentConfig is called N times — each potentially reading aidd-custom/config.yml from disk. The config cannot change between steps in the same run. Resolve once before the loop, lazily (only if a prompt step exists, to honor the requirement "Given no prompt: steps, never calls resolveAgentConfig").


6. Security (OWASP Top 10 Scan)

OWASP 2021 Applies? Finding
A01 Broken Access Control No CLI tool, no access control layer
A02 Cryptographic Failures No No crypto operations
A03 Injection Yes spawn uses shell: false with array args — correct. YAML loaded with JSON_SCHEMA — correct. However, the comment explaining the JSON_SCHEMA injection protection was deleted (see below).
A04 Insecure Design Partial resolveAgentConfig accepts a raw { command, args } object as value. In a CLI context this is intentional (user is the author). Not a vulnerability here. The mutable preset is a code quality issue, not a security one in this context.
A05 Security Misconfiguration No
A06 Vulnerable Components Not reviewed Out of scope for this diff
A07 Auth Failures No No auth in diff
A08 Software/Data Integrity Yes The new prompt-ordering guard in parseManifest is a positive integrity control — prevents a manifest from injecting prompt steps before the aidd runtime is installed.
A09 Logging/Monitoring No
A10 SSRF No loadYamlConfig reads local file paths; CLI context, by design

❌ Security-relevant comments deleted from parseManifest:

The original parseManifest had three comments explaining non-obvious constraints. The diff removed all of them:

-  // Use JSON_SCHEMA to restrict parsing to plain JSON types (strings, numbers,
-  // booleans, null, arrays, objects). This prevents YAML-specific extensions
-  // like !!binary or !!timestamp from being accepted in untrusted manifests.
-  // No steps key — treat as an empty manifest (backward-compatible default).
-  // Validate that steps is an array. A string or plain object means the YAML
-  // was written incorrectly and would silently iterate unexpected values.

Per the JS guide: "Comments should stand-alone months or years later. Assume that the reader is not familiar with the task plan or epic." The JSON_SCHEMA comment is especially important — it documents a security constraint that is completely invisible from the code alone.


7. UI/UX

N/A — no UI changes in this diff. CLI UX is clean: --prompt and --agentConfig are well-named, error messages use chalk and emoji consistently with the rest of the CLI.


8. Architecture

✅ Good:

  • The separation of concerns in lib/agent-cli/ is correct: errors → config → runner → command. Each depends only on those below it.
  • resolveAgentConfig is the single source of truth for config resolution — no other module reinvents it. Epic constraint honored.
  • Injectable dependencies (resolveAgentConfigFn, runAgentFn) in runCreate maintain testability without requiring module-level mocking.

❌ Bug — runner.js missing error event handler (process crash on ENOENT):

This is the most critical issue in the PR. runAgent only listens for close:

  return new Promise((resolve, reject) => {
    const child = spawn(command, spawnArgs, {
      cwd,
      shell: false,
      stdio: "inherit",
    });
    child.on("close", (code) => {
      if (code !== 0) {
        reject(
          createError({
            ...ScaffoldStepError,
            message: `Agent exited with code ${code}: ${[command, ...spawnArgs].join(" ")}`,
          }),
        );
      } else {
        resolve();
      }
    });
  });

When spawn encounters ENOENT (agent command not in PATH), Node.js emits an error event on the ChildProcess. With no listener registered, Node.js throws an unhandled exception and crashes the entire process. Verified:

node -e "
  const { spawn } = require('child_process');
  const child = spawn('nonexistent-cmd-xyz', []);
  child.on('close', (code) => {});
"
// node:events:497 — throw er; Unhandled 'error' event; Error: spawn ENOENT

The correct pattern is already in defaultExecStep in lib/scaffold-runner.jserror event handler with E2BIG/ENOBUFS special cases. runAgent should mirror it.


9. Documentation and Commits

Commits: All 8 commits follow conventional commit format (feat(scope): description). Messages are concise and accurate. ✅

Docs: docs/aidd-custom.md table row added correctly. aidd-custom/config.yml template updated with commented example. ✅

Docblocks: Public function signatures in config.js and runner.js have minimal JSDoc annotations. ✅


10. Requirements Checklist vs. Epic

Cross-referencing tasks/aidd-create-prompt-epic.md:

Requirement Status
errors.js — all 4 exports in one errorCauses call
config.js — all 13 resolution requirements
runner.js — no-shell spawn, ScaffoldStepError on non-zero ✅ (but missing error event)
runner.js"./agent" export
config.js"./agent-config" export
Manifest validation — prompt before aidd run throws
runManifest — calls resolveAgentConfig + runAgent for prompt steps ✅ (but per-step, not once)
runManifest — never calls resolveAgentConfig if no prompt steps
command.js — one import + one call in bin/aidd.js
npx aidd agent --prompt calls resolveAgentConfig then runAgent
create--agentConfig rename
create--prompt post-scaffold agent
aidd-custom/config.yml template with # agent-config: claude
docs/aidd-custom.md agent-config row

Summary of Issues (ranked by severity)

# Severity Issue
1 🔴 Bug runner.js missing error event handler — ENOENT crashes the process
2 🟠 Bug runManifest calls resolveAgentConfig per prompt step instead of once
3 🟠 Bug getAgentConfig returns mutable reference to shared preset
4 🟡 Quality Security-relevant comments deleted from parseManifest
5 🟡 Quality command.js — three identical handleAgentErrors branches (DRY)
6 🟡 Test gap runner.test.js — no test for spawn error event (ENOENT)
7 🟡 Test gap exports.test.js — no coverage for ./agent and ./agent-config

All 7 issues have delegation prompts ready above. Issues 1–4, 6–7 have already been queued; issue 5 (DRY handlers in command.js) is a minor quality fix that can be bundled into the command.js fix or addressed separately.

Open in Web Open in Cursor 

@ericelliott
Copy link
Copy Markdown
Collaborator Author

@cursoragent please /review with ai/commands/review - validate all the issues mentioned in these comments and check to see if they are fixed, and look deeper for any other lingering issues.

@ericelliott ericelliott marked this pull request as ready for review March 14, 2026 00:31
Copilot AI review requested due to automatic review settings March 14, 2026 00:31
@cursor
Copy link
Copy Markdown
Contributor

cursor bot commented Mar 14, 2026

All checks done. Now writing the full review:


🔬 Code Review — PR #143 Follow-Up: Validation of All Previous Issues + Deep Scan

Issue Validation — All 7 Previous Findings


1. 🔴 runner.js missing error event handler (ENOENT crash) — ✅ FIXED

The error event handler is now present and follows the defaultExecStep pattern exactly:

    child.on("error", (spawnErr) => {
      reject(
        createError({
          ...ScaffoldStepError,
          cause: spawnErr,
          message: `Failed to spawn agent: ${[command, ...spawnArgs].join(" ")}`,
        }),
      );
    });

The companion ENOENT test is in runner.test.js and correctly asserts cause.code === 'SCAFFOLD_STEP_ERROR'. ✅


2. 🟠 runManifest calling resolveAgentConfig per prompt step — ✅ FIXED

The lazy pre-check pattern correctly resolves once before the loop, and never calls when no prompt steps exist:

  const hasPromptStep = steps.some((s) => s.prompt !== undefined);
  const agentCfg = hasPromptStep
    ? await resolveAgentConfig({ cwd: folder, value: agentConfig })
    : undefined;

The new test in scaffold-runner.test.js asserts resolveAgentConfig.mock.calls.length === 1 for a two-prompt-step manifest. ✅


3. 🟠 getAgentConfig returning mutable shared reference — ✅ FIXED

Returns a defensive copy:

  return { args: [...preset.args], command: preset.command };

Two new tests confirm: distinct object identity per call, and mutation isolation. ✅


4. 🟡 Security comments deleted from parseManifest — ✅ FIXED

All three comments restored to exact original positions:

  // Use JSON_SCHEMA to restrict parsing to plain JSON types (strings, numbers,
  // booleans, null, arrays, objects). This prevents YAML-specific extensions
  // like !!binary or !!timestamp from being accepted in untrusted manifests.
  const data = yaml.load(content, { schema: yaml.JSON_SCHEMA });
  const steps = /** @type {any} */ (data)?.steps;

  // No steps key — treat as an empty manifest (backward-compatible default).
  if (steps === undefined || steps === null) return [];

  // Validate that steps is an array. A string or plain object means the YAML
  // was written incorrectly and would silently iterate unexpected values.
  if (!Array.isArray(steps)) {

The two stale runManifest comments (referencing execStep for prompt steps) were correctly left out. ✅


5. 🟡 DRY: identical handleAgentErrors branches in command.js — ✅ FIXED

Shared handler extracted:

/** @type {(error: { message: string }) => void} */
const logAgentConfigError = ({ message }) =>
  console.error(chalk.red(`\n❌ Agent config error: ${message}`));

All three branches now reference logAgentConfigError. Three separate regression tests cover each error type. ✅


6. 🟡 runner.test.js missing ENOENT test — ✅ FIXED (combined with issue 1) ✅


7. 🟡 exports.test.js missing ./agent and ./agent-config tests — ✅ FIXED

Four new tests added following the exact ./utils pattern: subpath existence assertions and live import smoke tests for runAgent, getAgentConfig, and resolveAgentConfig. ✅


Deeper Scan — New Findings


🔴 New Bug: resolveAgentConfig silently ignores inline { command, args } objects in aidd-custom/config.yml

The epic explicitly requires:

"Given the aidd-custom/README.md config options table, adds an agent-config row documenting accepted values: an agent name, a path to a .yml agent config file, **or an inline { command, args } object**."

And docs/aidd-custom.md was correctly updated to document this:

| `agent-config`    | `claude` | Agent used for prompt manifest steps and `npx aidd agent`. Accepted values: `claude` \| `opencode` \| `cursor` \| a path to a `.yml` agent config file \| an inline `{ command, args }` object. |

However the implementation in resolveAgentConfig for the aidd-custom/config.yml case only handles strings:

      const agentConfigValue = /** @type {string} */ (parsed["agent-config"]);
      if (isYamlPath(agentConfigValue)) return loadYamlConfig(agentConfigValue);
      return getAgentConfig(agentConfigValue);

When a user writes this valid YAML in their config.yml:

agent-config:
  command: myagent
  args:
    - --run

parsed["agent-config"] is { command: 'myagent', args: ['--run'] }. The code casts it to string, isYamlPath returns false, then getAgentConfig(object) calls object.toLowerCase()throws. The outer try/catch swallows the error and silently falls back to claude. Confirmed live:

THROWS: agentConfigValue.toLowerCase is not a function

The value parameter path already handles this correctly (if (typeof value === "object") return value) — the aidd-custom/config.yml path needs the same check. This is a direct epic requirement gap.


⚠️ Observation: Duplicate fix commits (cosmetic, not actionable)

Two agents raced to fix runner.js and config.js, producing two commits for each:

  • 5617d43 + f9c0408 — both fix the ENOENT handler
  • c592362 + 1a26002 — both fix the mutable preset copy

The code is correct in its final state; lint, typecheck, and all 442 tests pass. The history is noisier than necessary but can't be cleaned up without a force-push. No code change needed.


ℹ️ Minor: runManifest NonNullable cast

        agentConfig: /** @type {NonNullable<typeof agentCfg>} */ (agentCfg),

The logic is correct: agentCfg is defined whenever the loop reaches a prompt: step (because hasPromptStep was true). TypeScript cannot reason through this invariant so the cast is needed. The pattern is safe. Not a bug — noting for awareness.


Summary

# Status Finding
1 ✅ Fixed runner.js ENOENT crash
2 ✅ Fixed runManifest per-step resolveAgentConfig
3 ✅ Fixed getAgentConfig mutable shared reference
4 ✅ Fixed Deleted security comments
5 ✅ Fixed DRY handleAgentErrors branches
6 ✅ Fixed ENOENT test in runner.test.js
7 ✅ Fixed Missing export tests
🔴 NEW Open resolveAgentConfig silently drops inline { command, args } objects from aidd-custom/config.yml — documented as supported, throws and falls back to claude

One new actionable bug remains. Delegation prompt:

/aidd-fix

## Branch
Work on `cursor/aidd-prompt-epic-ab39`. Do NOT create or switch to any other branch.
Do NOT touch `main`. Commit directly to `cursor/aidd-prompt-epic-ab39`.
Run `git pull --rebase origin cursor/aidd-prompt-epic-ab39` before pushing.

## Bug
`resolveAgentConfig` in `lib/agent-cli/config.js` silently drops inline
`{ command, args }` objects written in `aidd-custom/config.yml`.

The epic and `docs/aidd-custom.md` both document that `agent-config` in the
config file accepts an inline object:

    agent-config:
      command: myagent
      args:
        - --run

But the implementation casts the parsed value to string and calls
`.toLowerCase()` on it, which throws. The outer `try/catch` swallows
the error and silently falls back to claude. Confirmed:

    // TypeError: agentConfigValue.toLowerCase is not a function

The `value` parameter path in the same function already handles this
correctly with `if (typeof value === "object") return value;`. The
`aidd-custom/config.yml` branch needs the same check.

## Files
- `lib/agent-cli/config.js` — add object check in the config.yml branch
- `lib/agent-cli/config.test.js` — add failing test first
- `tasks/aidd-create-prompt-epic.md` — add requirement

## aidd-fix Steps

1. Read `lib/agent-cli/config.js`, `lib/agent-cli/config.test.js`, and
   `tasks/aidd-create-prompt-epic.md` in full.
2. Add to the epic: "Given `agent-config` in `aidd-custom/config.yml` is
   a YAML mapping with a `command` field, `resolveAgentConfig` should use
   it directly as the AgentConfig"
3. Write a failing test in `lib/agent-cli/config.test.js`:
   - Create a temp `aidd-custom/config.yml` containing a YAML object:
         agent-config:
           command: myagent
           args:
             - --run
   - Call `resolveAgentConfig({ cwd: tempDir })` and assert it returns
     `{ command: 'myagent', args: ['--run'] }`
   - Run `npx vitest run lib/agent-cli/config.test.js` and confirm the
     test fails (currently falls back to claude)
4. In the `aidd-custom/config.yml` branch of `resolveAgentConfig`, after
   confirming `parsed["agent-config"]` is truthy, add an object check
   before casting to string:
       const agentConfigValue = parsed["agent-config"];
       if (typeof agentConfigValue === "object") return agentConfigValue;
       if (isYamlPath(agentConfigValue)) return loadYamlConfig(agentConfigValue);
       return getAgentConfig(agentConfigValue);
   Also add a `command` field validation (to match `loadYamlConfig`
   behavior) and throw `AgentConfigValidationError` if `command` is absent.
5. Run `npx vitest run lib/agent-cli/config.test.js` — all tests pass
6. Run `npm test` — no regressions
7. Stage and push:
       git pull --rebase origin cursor/aidd-prompt-epic-ab39
       git add lib/agent-cli/config.js lib/agent-cli/config.test.js tasks/aidd-create-prompt-epic.md
       git commit -m "fix(agent-cli): support inline { command, args } objects in aidd-custom/config.yml"
       git push origin cursor/aidd-prompt-epic-ab39
Open in Web Open in Cursor 

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds “agent delegation” capabilities to the aidd scaffolding flow by introducing an agent CLI subsystem (config resolution + spawn runner + CLI subcommand) and integrating prompt: steps into manifest execution with new validation rules.

Changes:

  • Added lib/agent-cli/* modules for agent config resolution, error types, a generic runAgent spawn primitive, and a new aidd agent --prompt CLI command.
  • Updated scaffold manifest parsing/execution to validate prompt: ordering and to execute prompt steps via resolveAgentConfig + runAgent.
  • Updated aidd create to use --agentConfig (renamed from --agent) and added --prompt for post-scaffold agent execution; updated docs/tests accordingly.

Reviewed changes

Copilot reviewed 21 out of 23 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
tasks/aidd-create-prompt-epic.md Updates epic requirements for agent config/runner behavior and manifest execution expectations.
package.json Adds new public subpath exports for aidd/agent and aidd/agent-config.
lib/scaffold-runner.js Enforces prompt: ordering relative to an “aidd run” step; runs prompt steps via runAgent with resolved agent config.
lib/scaffold-runner.test.js Updates parsing/execution tests for new ordering rule and runner/config integration.
lib/scaffold-verifier.test.js Updates manifest validity tests to reflect the new ordering constraint.
lib/scaffold-create.js Adds prompt flow after scaffolding and wires in agent config/runner dependencies.
lib/scaffold-create.test.js Adds tests covering --prompt behavior and renamed agentConfig plumbing.
lib/scaffold-commands.js Renames --agent to --agentConfig and adds --prompt to create.
lib/scaffold-commands.test.js Updates CLI option assertions for --agentConfig.
lib/cli-core.js Updates aidd-custom/config.yml template comments with the new agent-config option.
lib/cli-core.test.js Tests presence of the commented agent-config example line.
lib/agent-cli/config.js Implements agent preset lookup + YAML loading + env/config-file based resolution.
lib/agent-cli/config.test.js Comprehensive tests for presets, YAML loading, env/config-file resolution.
lib/agent-cli/runner.js Implements runAgent spawn wrapper with ScaffoldStepError mapping.
lib/agent-cli/runner.test.js Tests spawn shape, exit-code failures, and ENOENT error handling.
lib/agent-cli/errors.js Adds agent-config error causes and an exhaustive dispatcher helper.
lib/agent-cli/errors.test.js Tests error-cause shapes and exhaustive dispatch behavior.
lib/agent-cli/command.js Adds aidd agent --prompt command wiring to config resolution and runner.
lib/agent-cli/command.test.js Tests CLI behavior, including config error formatting and exit handling.
lib/exports.test.js Adds tests asserting the new aidd/agent and aidd/agent-config exports.
docs/aidd-custom.md Documents the new agent-config setting in aidd-custom/config.yml.
bin/create-e2e.test.js Updates create e2e coverage for --agentConfig and new manifest ordering rule.
bin/aidd.js Registers the new agent subcommand.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings March 19, 2026 17:10
@ericelliott ericelliott review requested due to automatic review settings March 19, 2026 17:10
cursoragent and others added 2 commits March 29, 2026 19:29
Updated the release date for version 3.0.0 and added new CLI features.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds first-class “agent delegation” to the aidd CLI and runtime: a new aidd agent command, --prompt support on aidd create, agent-config resolution/export APIs, and manifest validation/runtime updates so prompt: steps only run after an aidd-installing run: step.

Changes:

  • Introduces lib/agent-cli/* modules (config resolution, runner, CLI command) and exports them via package.json as aidd/agent and aidd/agent-config.
  • Updates scaffold execution to (1) enforce prompt ordering at parse time and (2) execute prompt steps via runAgent with resolved agent config.
  • Expands tests/docs and updates the bundled next-shadcn scaffold to satisfy the new ordering rule.

Reviewed changes

Copilot reviewed 40 out of 44 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tasks/archive/2026-03-19-scaffold-review-followups-epic.md Marks archived epic as completed
tasks/aidd-create-prompt-epic.md Updates epic requirements/spec notes
package.json Adds ./agent and ./agent-config subpath exports
lib/scaffold-verifier.test.js Updates fixtures to satisfy prompt ordering rule
lib/scaffold-runner.test.js Adds ordering-guard tests; runs prompt steps via agent config
lib/scaffold-runner.js Adds prompt ordering guard + integrates agent config + runner
lib/scaffold-resolver.test.js Moves tests to real fs state vs injected exists fn
lib/scaffold-resolver.js Removes injectable existsFn; clarifies named scaffold resolution
lib/scaffold-create.test.js Refactors create tests to integration style + prompt support + cleanup
lib/scaffold-create.js Adds --prompt flow + agent config resolution + always-cleanup behavior
lib/scaffold-create-args.test.js New isolated tests for resolveCreateArgs
lib/scaffold-commands.test.js Removes outdated CLI surface tests for old --agent option
lib/scaffold-commands.js Renames --agent--agent-config; adds --prompt
lib/exports.test.js Verifies new package exports and type entries
lib/churn-collector.test.js Adds e2e git-based churn tests (PR-aware + default-branch aware)
lib/churn-collector.js Adds default-branch detection and PR diff “+1” counting
lib/aidd-custom/setup.test.js Ensures config template includes commented agent-config example
lib/aidd-custom/setup.js Adds agent-config template comments to config.yml scaffold
lib/agent-cli/runner.test.js Tests runAgent spawn behavior and error mapping
lib/agent-cli/runner.js Implements no-shell agent process spawning + error handling
lib/agent-cli/runner.d.ts Adds TS types for aidd/agent
lib/agent-cli/get-agent-config.test.js Tests preset mapping + immutability behavior
lib/agent-cli/errors.test.js Tests agent-config error cause types + handler dispatch
lib/agent-cli/errors.js Defines typed agent-config error causes + handler
lib/agent-cli/config.test.js Tests agent config resolution chain + warnings/fallbacks
lib/agent-cli/config.js Implements preset map + resolution chain + YAML loading
lib/agent-cli/config.d.ts Adds TS types for aidd/agent-config
lib/agent-cli/command.test.js Tests agent subcommand registration
lib/agent-cli/command.js Implements npx aidd agent --prompt ... command
docs/scaffold-authoring.md Documents ordering rule + “prefer run over prompt” guidance
docs/aidd-custom.md Documents agent-config option
docs/agent-cli.md New docs for agent CLI usage + resolution chain
docs/agent-api.md New docs for programmatic agent APIs
bin/create-e2e.test.js Adds e2e coverage for --agent-config and aidd agent
bin/aidd.js Registers the new agent subcommand
ai/skills/aidd-timing-safe-compare/SKILL.md Refines review rules around hashed comparisons
ai/skills/aidd-tdd/SKILL.md Adds “mocking is a code smell” constraint
ai/skills/aidd-churn/SKILL.md Updates churn analysis constraints
ai/scaffolds/next-shadcn/stash.mjs Adds stash helper to temporarily empty scaffold dir
ai/scaffolds/next-shadcn/restore.mjs Adds restore helper for stashed scaffold files
ai/scaffolds/next-shadcn/SCAFFOLD-MANIFEST.yml Replaces placeholder prompt with real run/prompt flow + ordering compliance
ai/scaffolds/next-shadcn/README.md Documents named scaffolds + ordering rule
README.md Adds quickstart examples + mentions new agent exports
CHANGELOG.md Records new features + breaking changes for 3.0.0

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +68 to +75
// Match only genuine aidd CLI invocations:
// - via a package runner: `npx aidd`, `bunx aidd`, `yarn dlx aidd`, `pnpm dlx aidd`
// - flags (e.g. `-y`, `--yes`) between the runner name and `aidd` are allowed
// - or as the first token of the command string: `aidd .`
// Rejects false positives like `echo aidd` (argument) or `npm install aidd-something`
// (hyphenated package name).
const AIDD_INVOCATION_RE =
/(?:(?:^|\s)(?:npx|bunx|yarn(?:\s+dlx)?|pnpm(?:\s+dlx)?)\s+(?:-\S+\s+)*aidd|^aidd)(?![-\w])/;
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AIDD_INVOCATION_RE is intended to match only genuine aidd CLI invocations, but the current pattern will also match substrings inside other commands because it allows (?:^|\s) before the runner token (e.g. run: echo npx aidd . would be treated as an invocation). It also permits yarn aidd / pnpm aidd because (?:\s+dlx)? is optional, which conflicts with the comment (“yarn dlx” / “pnpm dlx”). Consider anchoring to start-of-command (allowing leading whitespace) and requiring the correct runner forms (e.g. yarn\s+dlx, pnpm\s+dlx) to avoid false positives that would bypass the prompt ordering guard.

Copilot uses AI. Check for mistakes.
Comment on lines +55 to +67
if (
!parsed ||
typeof parsed !== "object" ||
!("command" in parsed) ||
!parsed.command
) {
throw createError({
...AgentConfigValidationError,
message: "Agent config must contain a 'command' field",
});
}

return /** @type {{ command: string, args?: string[] }} */ (parsed);
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent configs loaded from YAML (and inline objects) are only validated for a truthy command field, but not for type correctness. With js-yaml JSON schema, command could be a number/boolean and args could be a string/object; runAgent would then call spawn(command, [...args, prompt]) and can throw a synchronous TypeError or spread a string into characters. Please validate that command is a non-empty string and that args (if present) is an array of strings, otherwise throw AgentConfigValidationError so callers get a consistent, user-friendly error instead of runtime crashes.

Copilot uses AI. Check for mistakes.
Comment on lines +169 to +176
const resolveAgentConfig = async ({ value, cwd = process.cwd() } = {}) => {
if (value !== undefined) return resolveExplicitValue(value);
const envValue = process.env.AIDD_AGENT_CONFIG;
if (envValue) return resolveExplicitValue(envValue);
const configFile = path.join(cwd, "aidd-custom", "config.yml");
return (
(await resolveFromConfigFile(configFile, cwd)) ?? getAgentConfig("claude")
);
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolveAgentConfig ignores the provided cwd whenever an explicit value is passed (if (value !== undefined) return resolveExplicitValue(value)). This means relative YAML paths supplied via --agent-config ./agent.yml (or programmatic value: './agent.yml') are resolved relative to process.cwd() instead of the caller-provided cwd (notably, runManifest/runCreate pass cwd: folder). If cwd is meant to control relative path resolution, route explicit string YAML paths through the same resolver used for config-file entries (or resolve the path against cwd before calling loadYamlConfig).

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +43
const display = [command, ...spawnArgs].join(" ");
const child = spawn(command, spawnArgs, {
cwd,
shell: false,
stdio: "inherit",
});
child.on("close", (code, signal) => {
if (signal != null) {
reject(
createError({
...ScaffoldStepError,
message: `Agent killed by signal ${signal}: ${display}`,
}),
);
} else if (code !== 0) {
reject(
createError({
...ScaffoldStepError,
message: `Agent exited with code ${code}: ${display}`,
}),
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runAgent includes the full prompt text in display and then echoes it in error messages (non-zero exit, spawn failure, signal). Prompts may contain secrets (tokens, credentials, private code excerpts), so this can leak sensitive data into CI logs/terminal history. Consider omitting the prompt from the displayed command, or truncating/redacting it (e.g. show first N chars and length) while still logging the agent command/args for debugging.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

import { spawn } from "child_process";
import { createError } from "error-causes";

import { ScaffoldStepError } from "../scaffold-errors.js";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Public API runAgent uses scaffold-specific error type

Low Severity

runAgent is exported as the public aidd/agent API for third-party tools, but all its errors use ScaffoldStepError (code SCAFFOLD_STEP_ERROR). Consumers using runAgent outside of scaffolding (e.g., in their own CLI tools) will receive errors with a misleading scaffold-specific error code, making error handling and diagnostics confusing.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants