diff --git a/agents/devex_doc_writer/.gitignore b/agents/devex_doc_writer/.gitignore new file mode 100644 index 000000000..ea1472ec1 --- /dev/null +++ b/agents/devex_doc_writer/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/agents/devex_doc_writer/README.md b/agents/devex_doc_writer/README.md new file mode 100644 index 000000000..92140a3b4 --- /dev/null +++ b/agents/devex_doc_writer/README.md @@ -0,0 +1,269 @@ +# DevEx Doc Writer Pipeline + +An autonomous agent pipeline that generates CLI DevEx proposals and implementation plans for new AgentCore features — +grounded in the actual codebase, iteratively refined through human review. + +The system is **archetype-driven**: different feature types (new resource, scope widening, new command, etc.) route +through the same pipeline engine but use different input schemas, templates, biases, and self-review criteria. Adding a +new archetype is adding a markdown file — not changing the pipeline. + +--- + +## Archetypes + +Not all features are the same shape. The pipeline adapts based on what you're building: + +| Archetype | When to use | Example | Status | +| -------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------- | -------------------- | +| **New Resource** | Adding a new service resource with its own schema, primitive, and CDK construct | Config Bundles, Datasets, Harness | **P0 — implemented** | +| **Scope Widening** | Expanding what existing CLI surfaces can do (new deploy target, build type, auth model) | Terraform support, Node.js CodeZip, Container builds | **P0 — implemented** | +| **New Command** | Adding a new top-level verb to the CLI | `agentcore migrate`, `agentcore audit` | P1 — planned | +| **Ecosystem Integration** | Integrating a new external framework or tool | New agent framework template, new container runtime | P1 — planned | +| **Cross-cutting Refactor** | CLI-wide architecture changes that touch many surfaces | Auth model overhaul, partition expansion | P1 — planned | + +Each archetype defines: + +- **What inputs are required** (API surface? Trust policy? Affected commands? Coexistence model?) +- **Which template to fill** (different sections per archetype) +- **Archetype-specific biases** (e.g., "coexistence over replacement" for scope widening) +- **Extra self-review checks** (e.g., "backwards compatibility proven" for scope widening) + +The pipeline engine (knowledge refresh, review loop, tenets, task graph schema) is shared across all archetypes. + +--- + +## How It Works + +``` +You provide YAML inputs → Agent writes a DevEx doc → You review & iterate → APPROVED + │ + ▼ + Agent writes impl plan + │ + ▼ + feature_builder executes +``` + +The pipeline has two stages. Each stage loops until you type `APPROVED`: + +**Stage 1 — DevEx Doc:** Takes your feature description, API surface, IAM requirements, and produces a full proposal +(developer journeys, TUI wireframes, schema changes, codebase impact, deploy flow, phased implementation). + +**Stage 2 — Implementation Plan:** Takes the approved doc and breaks it into ordered tasks with concrete file paths, +dependencies, size estimates, and verification checklists. Outputs `task_graph.json` that the `feature_builder` agent +(PR #1124) can execute. + +--- + +## Components + +### 1. Knowledge Snapshots (`agents/knowledge/`) + +Pre-indexed YAML files describing the CLI and CDK architecture. Refreshed at pipeline start so the agent always works +from current state. + +| File | What it captures | +| -------------------------------- | ------------------------------------------------------------------------- | +| `cli-architecture-snapshot.yaml` | Primitives, schema shape, deploy flow, commands, IAM patterns, code style | +| `cdk-architecture-snapshot.yaml` | CDK constructs, service principals | +| `refresh.ts` | TypeScript script (ts-morph) that regenerates both snapshots | + +**Run manually:** + +```bash +cd agents/knowledge && npm run refresh +``` + +The agent runs this automatically at pipeline start. CDK repo is auto-discovered from sibling dirs, `AGENTCORE_CDK_ROOT` +env var, or cloned from GitHub as fallback. + +--- + +### 2. Input Spec (`agents/devex_doc_writer/inputs/schema.yaml`) + +Formal schema defining what the pipeline needs. Two archetypes: + +| Archetype | When to use | Key inputs | +| ---------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `new_resource` | Adding a new service resource (new primitive, schema array, CDK construct) | API operations, trust policy, CFN type, analogue | +| `scope_widening` | Expanding existing CLI capabilities (new deploy target, build type, auth model) | Current/target architecture, affected commands, coexistence model | + +You provide feature-specific facts that can't be derived from the codebase (API ops, trust policy, account info). The +agent proposes architecture decisions (schema location, deploy strategy, TUI steps) with explicit reasoning — you +confirm or override before it writes. + +--- + +### 3. Templates (`agents/devex_doc_writer/templates/`) + +Structured markdown templates with `{{variable}}` placeholders: + +| Template | Used for | +| ------------------------- | ------------------------------------------- | +| `new-resource-devex.md` | DevEx proposals for new resource features | +| `scope-widening-devex.md` | DevEx proposals for scope-widening features | +| `implementation-plan.md` | Stage 2 impl plans (both archetypes) | + +--- + +### 4. Prompts (`agents/devex_doc_writer/prompts/`) + +System prompts that define the agent's behavior: + +| Prompt | Role | +| ------------------ | -------------------------------------------------------------------------- | +| `writer.md` | The writer persona — voice, style, tenets, biases, workflow instructions | +| `self_reviewer.md` | Quality gate — 14-point checklist run against the doc before human sees it | + +--- + +### 5. Entry Point (`agents/devex_doc_writer/main.py`) + +Orchestrates the pipeline: + +``` +main.py --input your-feature.yaml + │ + ├─ 1. Parse & validate inputs (against schema.yaml) + ├─ 2. Refresh knowledge snapshot (refresh.ts) + ├─ 3. Load CLI + CDK snapshots + ├─ 4. Validate analogue choice + check slug collisions + ├─ 5. Assemble writer context (prompt + knowledge + inputs + template) + └─ 6. Output: writer-context.md ready for LLM invocation +``` + +--- + +## End-to-End Usage + +### Step 1: Write your input YAML + +Use `examples/config-bundle-input.yaml` as a starting point. Fill in your feature's details: + +```yaml +archetype: 'new_resource' +feature_name: 'My Feature' +feature_slug: 'my-feature' +feature_description: '...' +# ... (see examples/ or inputs/schema.yaml for all fields) +``` + +### Step 2: Run the pipeline + +```bash +cd agents/devex_doc_writer + +# Dry run — validates inputs, checks for collisions +python3 main.py --input your-input.yaml --dry-run + +# Full run — generates writer context +python3 main.py --input your-input.yaml +``` + +### Step 3: Invoke the writer agent + +**With Harness (when PR #1124 lands):** + +```bash +# Automatic — main.py invokes Harness directly +python3 main.py --input your-input.yaml +# → enters interactive review loop +``` + +**Standalone (direct LLM — works today):** + +```bash +# Pass output/writer-context.md as system prompt to Claude/Kiro/Codex +# The agent will: +# 1. Propose inferred decisions (wait for your confirmation) +# 2. Write the full DevEx doc +# 3. Self-review (fixes critical issues internally) +# 4. Present to you for review +``` + +### Step 4: Iterate + +The agent responds to your feedback as natural conversation: + +- "Change X to Y" → revises and explains what changed +- "Why did you pick CDK?" → explains rationale with codebase evidence +- "I disagree, use imperative" → presents tradeoffs, revises if you insist + +### Step 5: Approve + +Type `APPROVED` to lock the doc and proceed to Stage 2 (impl plan generation). + +### Step 6: Publish + +Copy the final markdown to Quip for broader team review. If Quip feedback requires changes, re-enter the pipeline — the +agent diffs and propagates. + +--- + +## Design Principles + +| # | Tenet | Meaning | +| --- | ------------------------------------ | ------------------------------------ | +| 1 | Developer ergonomics over API parity | CLI is not a 1:1 wrapper | +| 2 | Consistency over novelty | New features feel like existing ones | +| 3 | Explicit over implicit | Unambiguous names and messages | +| 4 | Local-first validation | Zod catches errors before API calls | +| 5 | Additive only per phase | No phase breaks a prior phase | +| 6 | Security by default | Least-privilege roles | +| 7 | Sensitivity-aware | No pre-GA leaks in public repos | +| 8 | Know your limits | Agent escalates what it can't solve | + +--- + +## File Structure + +``` +agents/ +├── devex_doc_writer/ +│ ├── README.md ← you are here +│ ├── SYSTEM.md # Full design doc (for team alignment) +│ ├── main.py # Pipeline entry point +│ ├── archetypes/ +│ │ ├── new-resource.md # Archetype: new service resource +│ │ └── scope-widening.md # Archetype: expand existing capability +│ ├── prompts/ +│ │ ├── writer.md # Writer agent persona +│ │ └── self_reviewer.md # Quality gate checklist +│ ├── templates/ +│ │ ├── new-resource-devex.md +│ │ ├── scope-widening-devex.md +│ │ └── implementation-plan.md +│ ├── inputs/ +│ │ └── schema.yaml # Input validation schema +│ ├── examples/ +│ │ └── config-bundle-input.yaml +│ └── reference-devex-docs/ # Style calibration examples +│ +├── knowledge/ +│ ├── refresh.ts # Snapshot generator +│ ├── extractors/ # AST-based codebase parsing +│ ├── cli-architecture-snapshot.yaml +│ └── cdk-architecture-snapshot.yaml +``` + +--- + +## Dependencies + +| What | Required for | How to install | +| ------------ | ------------------ | ------------------------------------ | +| Python 3.10+ | main.py | system | +| PyYAML | input parsing | `pip install pyyaml` | +| Node.js 18+ | knowledge refresh | system | +| tsx | running refresh.ts | `cd agents/knowledge && npm install` | +| ts-morph | AST parsing | (installed via npm above) | + +--- + +## What's Next + +- **PR #1124 lands** → wire Harness invocation into main.py (replaces file output with interactive agent session) +- **Iterative review loop** → Harness session with human-in-the-loop (APPROVED exit) +- **QuipEditor MCP** → auto-publish approved docs to Quip +- **P1 archetypes** → new-command, ecosystem-integration, cross-cutting-refactor +- **Extractor tests** → unit tests for knowledge extractors diff --git a/agents/devex_doc_writer/SYSTEM.md b/agents/devex_doc_writer/SYSTEM.md new file mode 100644 index 000000000..7b1e7961a --- /dev/null +++ b/agents/devex_doc_writer/SYSTEM.md @@ -0,0 +1,726 @@ +# DevEx Doc Writer Pipeline — System Design + +**Version:** 1.0.0 **Status:** Draft (for team alignment) **Last updated:** 2026-05-05 **Author:** gitikavj@ + +--- + +## At a Glance + +An autonomous pipeline that takes a feature request + structured inputs and produces a production-ready CLI DevEx +proposal + implementation plan — iteratively refined through human review until explicitly approved. + +``` +┌─────────────────┐ ┌────────────────────────┐ ┌──────────────────────┐ +│ STRUCTURED │ │ STAGE 1: DevEx Doc │ │ STAGE 2: Impl Plan │ +│ INPUTS │─────▶│ Writer + Review Loop │─────▶│ Writer │ +│ (YAML) │ │ (iterates until │ │ (detailed tasks, │ +│ │ │ APPROVED) │ │ file paths, deps) │ +└─────────────────┘ └────────────────────────┘ └──────────────────────┘ + │ │ + ▼ ▼ + Human reviews, Feeds into feature_builder + asks questions, (executor agent from PR #1124) + agent explains & OR human applies directly + revises +``` + +**What makes this "frontier" vs. a simple template filler:** + +- The agent _already knows_ the CLI/CDK architecture via pre-indexed knowledge snapshots — no cloning, no cold start +- It produces opinionated proposals grounded in actual codebase patterns — cites real file paths, follows existing + primitives +- It defends its design decisions when challenged, citing constraints, precedent, and tradeoffs +- It identifies what it _cannot_ solve — surfaces questions requiring product/architecture decisions rather than + hallucinating +- It iterates in a conversation loop until the human types `APPROVED` — not a one-shot generator +- After approval, publishes to Quip for broader team review; changes cascade back through the pipeline +- It respects repo sensitivity (private vs public) and never leaks pre-GA service info + +**Audience for the DevEx doc output:** Service team, design team, product. Implementation details in appendix. + +**Platform:** Kiro CLI, Codex, or any agentic coding environment. Not locked to a specific tool. + +**Where this lives:** `agents/devex_doc_writer/` in the `agents/` framework (PR #1124). + +**The full pipeline end-to-end:** + +``` +Inputs (YAML) → Archetype Routing → DevEx Doc Writer → [Review Loop] → APPROVED + → Publish to Quip → [Team Comments] → re-enter if changes needed + → Implementation Plan Writer → [Review Loop] → APPROVED + → feature_builder (executor, PR #1124) → Code + PRs +``` + +--- + +## Table of Contents + +1. [Archetypes](#archetypes) +2. [Architecture & How It Fits](#architecture--how-it-fits) +3. [Knowledge Strategy](#knowledge-strategy) +4. [Workflow: Iterative Review Loop](#workflow-iterative-review-loop) +5. [Universal Tenets](#universal-tenets) +6. [Universal Biases](#universal-biases) +7. [Self-Review Checklist (Base)](#self-review-checklist-base) +8. [Task Graph Schema (Stage 2 Contract)](#task-graph-schema-stage-2-contract) +9. [Knowledge Snapshot Refresh (refresh.ts)](#knowledge-snapshot-refresh-refreshts) +10. [Failure Modes & Recovery](#failure-modes--recovery) +11. [Success Metrics](#success-metrics) +12. [Change Cascade](#change-cascade) + +--- + +## Archetypes + +Not all features are the same shape. The pipeline adapts its inputs, template, and self-review based on the feature +archetype. + +| Archetype | Examples | Priority | Doc | +| -------------------------- | -------------------------------------------------------------------- | -------- | -------------------------------------- | +| **New Resource** | Config Bundles, Datasets, Harness | P0 | `archetypes/new-resource.md` | +| **Scope Widening** | Terraform support, Node.js CodeZip, new auth model, Container builds | P0 | `archetypes/scope-widening.md` | +| **New Command Verb** | `agentcore migrate`, `agentcore audit` | P1 | `archetypes/new-command.md` | +| **Ecosystem Integration** | New framework template, new container runtime (Finch/Podman) | P1 | `archetypes/ecosystem-integration.md` | +| **Cross-cutting Refactor** | Auth model overhaul, partition expansion, CLI-wide UX revamp | P1 | `archetypes/cross-cutting-refactor.md` | + +### Archetype Routing + +At the start of the pipeline, the agent identifies which archetype applies. It can be: + +- **Declared by user:** `archetype: "new_resource"` in the input YAML +- **Inferred by agent:** Based on the presence/absence of fields (has `control_plane_operations`? → new resource. Has + `current_architecture`/`target_architecture`? → scope widening.) + +If ambiguous, the agent asks before proceeding. + +### What's Shared vs. Per-Archetype + +| Shared (this doc) | Per-Archetype | +| --------------------- | ------------------------------ | +| Review loop mechanics | Input schema (what's required) | +| Universal tenets | Archetype-specific biases | +| Knowledge strategy | Which template sections apply | +| Task graph schema | Impl plan phase shape | +| Failure modes | Self-review extensions | +| Success metrics | Quick start examples | +| Sensitivity rules | | +| Change cascade | | + +--- + +## Architecture & How It Fits + +### In the `agents/` Framework + +``` +agents/ +├── core/ # shared infra (from PR #1124) +│ ├── harness_client.py +│ ├── parsing.py +│ └── config.py +│ +├── knowledge/ # pre-indexed codebase knowledge +│ ├── cli-architecture-snapshot.yaml +│ ├── cdk-architecture-snapshot.yaml +│ ├── refresh.ts # ts-morph script to regenerate +│ ├── tsconfig.json +│ ├── package.json # deps: ts-morph, yaml +│ ├── extractors/ +│ │ ├── primitives.ts +│ │ ├── schema.ts +│ │ ├── deploy-flow.ts +│ │ └── commands.ts +│ └── reference-devex-docs/ # 2-3 canonical docs for style calibration +│ ├── eval-feature-overview.md +│ └── runtime-endpoint-design.md +│ +├── devex_doc_writer/ # the pipeline +│ ├── SYSTEM.md # THIS FILE — shared engine +│ ├── archetypes/ +│ │ ├── new-resource.md # P0 +│ │ ├── scope-widening.md # P0 +│ │ ├── new-command.md # P1 +│ │ ├── ecosystem-integration.md # P1 +│ │ └── cross-cutting-refactor.md # P1 +│ ├── prompts/ +│ │ ├── writer.md # writer + responder (single persona) +│ │ └── self_reviewer.md # checklist pass +│ └── inputs/ +│ └── schema.yaml # combined schema (all archetypes) +│ +├── impl_plan_writer/ # Stage 2 +│ ├── main.py +│ └── prompts/ +│ └── planner.md +│ +├── feature_builder/ # existing (PR #1124) — consumes Stage 2 +│ ├── main.py +│ └── prompts/ +``` + +### v1 Scope Decisions + +| Decision | Choice | Rationale | +| ----------------------- | --------------------------------------------------- | ---------------------------------------------------- | +| Review loop platform | Interactive session (Kiro/Codex) | No polling, webhooks, or async orchestration needed | +| Agent personas | Single writer+responder + separate self-review pass | Three personas over-engineered for v1 | +| Generic orchestration | No `staged_authoring` abstraction for now | Build specific workflow first, extract pattern later | +| Quip posting | Manual (agent outputs markdown, user pastes) | QuipEditor MCP automates later | +| Feedback classification | None — treat all feedback as conversation | Real comments are compound | + +### Relationship to `feature_builder` + +``` +devex_doc_writer (Stage 1) → produces DevEx doc + ↓ (human types APPROVED) + ↓ → publishes to Quip for broader team +impl_plan_writer (Stage 2) → produces implementation plan + task_graph.json + ↓ (human types APPROVED) +feature_builder (PR #1124) → consumes task_graph.json, executes code changes +``` + +--- + +## Knowledge Strategy + +### Problem + +The agent needs deep knowledge of the CLI and CDK codebases to write grounded proposals. Cloning fresh is slow. Full +codebase in prompt is too expensive. + +### Solution: Structured Snapshot + Selective Reads + +``` +agents/knowledge/ +├── cli-architecture-snapshot.yaml # auto-generated, version-controlled +├── cdk-architecture-snapshot.yaml # auto-generated, version-controlled +├── refresh.ts # ts-morph script +└── reference-devex-docs/ # canonical docs for style calibration +``` + +**Critical:** The snapshot is regenerated at invocation time (not just on CI merge). The CI version is a cache — the +source of truth is always the live codebase. + +### What the Snapshot Contains + +```yaml +schema_version: 1 +generated_at: '2026-05-05T10:00:00Z' +commit: 'abc123' +repo: 'aws/agentcore-cli' + +primitives: + - name: 'AgentPrimitive' + file: 'src/cli/primitives/AgentPrimitive.ts' + resources_managed: ['agents'] + schema_key: 'agents' + supports_tui: true + supports_remove: true + has_cross_references: false + - name: 'EvaluatorPrimitive' + file: 'src/cli/primitives/EvaluatorPrimitive.ts' + resources_managed: ['evaluators'] + schema_key: 'evaluators' + supports_tui: true + supports_remove: true + has_cross_references: true + references: ['agents (via agentName)'] + +schema_shape: + agentcore_json: + top_level_arrays: + - key: 'agents' + schema_file: 'src/schema/schemas/agent-env.ts' + - key: 'memories' + schema_file: 'src/schema/schemas/primitives/memory.ts' + - key: 'credentials' + schema_file: 'src/schema/schemas/primitives/credential.ts' + - key: 'evaluators' + schema_file: 'src/schema/schemas/primitives/evaluator.ts' + - key: 'onlineEvalConfigs' + schema_file: 'src/schema/schemas/primitives/online-evaluation-config.ts' + - key: 'gateways' + schema_file: 'src/schema/schemas/mcp.ts' + - key: 'gatewayTargets' + schema_file: 'src/schema/schemas/mcp.ts' + - key: 'policyEngines' + schema_file: 'src/schema/schemas/primitives/policy-engine.ts' + - key: 'policies' + schema_file: 'src/schema/schemas/primitives/policy.ts' + cross_field_validations: + - source: 'onlineEvalConfigs[].agentName' + target: 'agents[].name' + rule: 'must_exist' + - source: 'onlineEvalConfigs[].evaluatorNames[]' + target: 'evaluators[].name' + rule: 'must_exist' + - source: 'policies[].engineName' + target: 'policyEngines[].name' + rule: 'must_exist' + + deployed_state: + file: 'src/schema/schemas/deployed-state.ts' + resource_types: + - key: 'runtimes' + fields: ['runtimeId', 'runtimeArn', 'roleArn'] + - key: 'memories' + fields: ['memoryId', 'memoryArn'] + - key: 'credentials' + fields: ['credentialProviderArn'] + - key: 'evaluators' + fields: ['evaluatorId', 'evaluatorArn'] + - key: 'onlineEvalConfigs' + fields: ['onlineEvaluationConfigId', 'onlineEvaluationConfigArn', 'executionStatus'] + - key: 'policyEngines' + fields: ['policyEngineId', 'policyEngineArn'] + - key: 'policies' + fields: ['policyId', 'policyArn', 'engineName'] + - key: 'runtimeEndpoints' + fields: ['endpointId', 'endpointArn'] + +deploy_flow: + file: 'src/cli/operations/deploy/' + steps: + 1: 'Preflight validation (schema parse, credentials check, target resolution)' + 2: 'IMPERATIVE: Create/update API key providers (pre-deploy-identity.ts)' + 3: 'IMPERATIVE: Create/update OAuth2 providers (pre-deploy-identity.ts)' + 4: 'CDK synth + deploy (CloudFormation via CDK toolkit)' + 5: 'Parse CFN outputs → build deployed-state.json' + 6: 'Post-deploy: setup transaction search' + +commands: + verbs: + - name: 'create' + has_tui: true + - name: 'add' + nouns: + [ + 'agent', + 'memory', + 'credential', + 'evaluator', + 'online-eval', + 'gateway', + 'gateway-target', + 'policy-engine', + 'policy', + ] + has_tui: true + - name: 'remove' + nouns: + [ + 'agent', + 'memory', + 'credential', + 'evaluator', + 'online-eval', + 'gateway', + 'gateway-target', + 'policy-engine', + 'policy', + 'all', + ] + - name: 'deploy' + - name: 'status' + - name: 'dev' + - name: 'invoke' + - name: 'run eval' + - name: 'logs' + - name: 'package' + - name: 'validate' + +iam_patterns: + service_principal: 'bedrock-agentcore.amazonaws.com' + lambda_principal: 'lambda.amazonaws.com' + role_creation: 'CDK: new iam.Role({ assumedBy: new iam.ServicePrincipal(AGENTCORE_SERVICE_PRINCIPAL) })' + existing_role_support: 'executionRoleArn on agent schema allows BYO role' + partition_utility: 'src/cli/aws/partition.ts — arnPrefix(), serviceEndpoint(), dnsSuffix()' + +code_style: + rules: + - 'No inline imports' + - '{ success: boolean, error?: string } for results' + - 'Existing types before inline' + - 'Constants in closest subdirectory' + - 'Never hardcode arn:aws:' + - 'Tags via TagsSchema on all resources' + tui_patterns: + - 'Screen → Flow → Wizard hook → Operation hook → Primitive' + - 'MAX_CONTENT_WIDTH = 60' + - "SelectList uses wrap='wrap'" +``` + +### When Selective Reads Happen + +After loading the snapshot, read actual source only when: + +1. **Analogue source** — to understand exact lifecycle patterns +2. **Analogue CDK construct** — IAM role creation, resource properties +3. **Specific schema files** — cross-references need detail beyond snapshot +4. **Reference DevEx docs** — voice/style calibration + +### Analogue Validation + +The agent does NOT blindly trust the user's analogue choice. It validates: + +- Does the analogue's shape match the new feature's API shape? +- If poor match, proposes alternative with explanation before proceeding. + +### Why Not Knowledge Bases / AgentCore Memory + +| Approach | Pros | Cons | +| ---------------------------- | ---------------------------------------- | ------------------------------------ | +| Structured snapshot (chosen) | Deterministic, auditable, fast, no infra | Stale between refreshes | +| AgentCore Memory | Semantic search, rationale storage | Infra dependency, hallucination risk | +| Knowledge Base (RAG) | Good for large corpus | Overkill for structured data | + +Decision: snapshot for architecture facts. Memory add-on later for team decisions/rationale history. + +--- + +## Workflow: Iterative Review Loop + +This is universal across all archetypes. The template and inputs change — the loop doesn't. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ TRIGGER │ +│ • Interactive session: user provides input YAML │ +│ • GH Action: issue labeled "feature-proposal" with YAML body │ +└──────────────────────┬──────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────┐ +│ ARCHETYPE ROUTING │ +│ • Declared in YAML, or inferred from field presence │ +│ • If ambiguous: ask user │ +│ • Loads archetype-specific input schema + template │ +└──────────────────────┬───────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────┐ +│ VALIDATE INPUTS │ +│ • Check required fields per archetype │ +│ • If insufficient: exit with clear error listing what's missing │ +└──────────────────────┬───────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────┐ +│ REFRESH & LOAD KNOWLEDGE │ +│ • Run refresh.ts to regenerate snapshot from current codebase │ +│ • Read both snapshots │ +│ • Validate analogue choice (if applicable) │ +│ • Selective reads based on archetype needs │ +│ • Read reference-devex-docs/ for style │ +└──────────────────────┬───────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────┐ +│ PROPOSE INFERRED DECISIONS │ +│ • Agent proposes values it can infer (per archetype definition) │ +│ • Human confirms or corrects before writing begins │ +└──────────────────────┬───────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────┐ +│ WRITE PHASE │ +│ • Fills template (archetype-specific sections) │ +│ • Applies universal tenets + archetype biases │ +│ • Generates rough impl plan skeleton │ +│ • Flags "Escalation Required" items it cannot resolve │ +└──────────────────────┬───────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────┐ +│ SELF-REVIEW (separate pass) │ +│ • Base checklist + archetype extensions │ +│ • If ≥2 critical failures: revises internally (max 2 loops) │ +└──────────────────────┬───────────────────────────────────────────┘ + │ + ▼ +╔══════════════════════════════════════════════════════════════════════╗ +║ ITERATIVE REVIEW CONVERSATION ║ +║ ║ +║ Agent presents: devex_doc.md + impl_plan_skeleton.md ║ +║ ║ +║ Human provides feedback as natural conversation. Agent reads the ║ +║ full message, addresses all parts: ║ +║ • Change requests → revise, explain what changed ║ +║ • Questions ("why X?") → explain rationale with evidence ║ +║ • Disagreements → present tradeoffs, cite both sides ║ +║ • New constraints → incorporate, note in Decisions table ║ +║ ║ +║ Each revision: version bump, update callout at top, update ║ +║ skeleton if affected. ║ +║ ║ +║ EXIT: Human types "APPROVED" (exact keyword). ║ +║ ║ +║ Staleness: 7-day ping. 14-day archive. User can resume anytime. ║ +╚══════════════════════════════════════════════════════════════════════╝ + │ (APPROVED) + ▼ +┌──────────────────────────────────────────────────────────────────┐ +│ PUBLISH │ +│ • Agent outputs final markdown │ +│ • User pastes to Quip (v2: auto-publish via QuipEditor MCP) │ +│ • If Quip feedback → user re-enters, agent revises │ +└──────────────────────┬───────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────┐ +│ STAGE 2: Implementation Plan Writer │ +│ • Expands skeleton → full plan + task_graph.json │ +│ • Same review loop (APPROVED to exit) │ +│ • Output feeds feature_builder OR human implements │ +└──────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Universal Tenets + +These apply to ALL archetypes. Priority order — when they conflict, higher wins. + +1. **Developer ergonomics over API parity.** CLI is not a 1:1 API wrapper. Combine operations, hide complexity, surface + what matters. +2. **Consistency over novelty.** New features feel like existing features. Same patterns, same conventions. +3. **Explicit over implicit.** Flag names, config fields, error messages are unambiguous. +4. **Local-first validation.** All schema/config validation happens locally before API calls. Zod superRefine catches + errors on disk. +5. **Additive only per phase.** No phase requires removing/renaming from a prior phase. +6. **Security by default.** Auto-created roles are least-privilege. Never grant `*` without justification. +7. **Sensitivity-aware.** Never leak pre-GA info into public repos. +8. **Know your limits.** The agent identifies what it CANNOT solve — product direction, cross-team alignment, codebase + refactors beyond feature scope — and surfaces these as "Escalation Required" items. Never silently assumes or + hallucinates answers to architectural questions. + +--- + +## Universal Biases + +These are defaults across all archetypes. Individual archetypes can extend (never contradict). + +1. **Verb-first commands.** `agentcore [verb] [noun]` always. +2. **Short resource/command names.** `config-bundle` not `configuration-bundle`. +3. **Cite precedent.** Every design choice references an existing codebase pattern. +4. **Partition-aware from day one.** Never hardcode `arn:aws:`. +5. **Config-driven tagging.** Resources support `TagsSchema`. +6. **Independently shippable phases.** Every phase leaves the CLI releasable. + +--- + +## Self-Review Checklist (Base) + +Base checklist runs for ALL archetypes. Archetype docs add their own extensions. + +| # | Check | Critical? | What Passes | +| --- | ------------------------------ | --------- | ----------------------------------------------------------- | +| 1 | **Verb-first commands** | Yes | All commands follow `agentcore [verb] [noun]` | +| 2 | **Real flags, not pseudocode** | Yes | Every CLI example uses `--flag-name value` syntax | +| 3 | **Codebase grounded** | Yes | File paths reference actual repo paths | +| 4 | **Pattern cited** | No | At least one "Precedent:" reference | +| 5 | **Open questions honest** | Yes | Unknowns in table, not buried | +| 6 | **No marketing language** | No | Zero "powerful", "seamless", "elegant", "robust" | +| 7 | **Phases are shippable** | Yes | Each phase lands independently | +| 8 | **Escalations surfaced** | Yes | Things agent can't solve are explicitly listed, not assumed | +| 9 | **Sensitivity respected** | Yes | No internal names/IDs in public-destined artifacts | + +**Threshold:** ≥2 Critical items fail → agent revises internally (max 2 loops). + +--- + +## Task Graph Schema (Stage 2 Contract) + +`task_graph.json` is the machine-readable contract between Stage 2 and `feature_builder`. + +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "feature_slug": { "type": "string" }, + "feature_name": { "type": "string" }, + "archetype": { "type": "string" }, + "total_phases": { "type": "integer" }, + "phases": { + "type": "array", + "items": { + "type": "object", + "properties": { + "phase_id": { "type": "string", "pattern": "^phase-[0-9]+$" }, + "name": { "type": "string" }, + "description": { "type": "string" }, + "tasks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "task_id": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+$" }, + "title": { "type": "string" }, + "description": { "type": "string" }, + "files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { "type": "string" }, + "action": { "enum": ["create", "modify", "delete"] }, + "description": { "type": "string" } + }, + "required": ["path", "action"] + } + }, + "depends_on": { "type": "array", "items": { "type": "string" } }, + "size": { "enum": ["S", "M", "L", "XL"] }, + "verification": { "type": "array", "items": { "type": "string" } } + }, + "required": ["task_id", "title", "files", "depends_on", "size"] + } + }, + "phase_verification": { "type": "array", "items": { "type": "string" } }, + "definition_of_done": { "type": "string" } + }, + "required": ["phase_id", "name", "tasks", "phase_verification"] + } + }, + "cross_cutting_concerns": { "type": "array", "items": { "type": "string" } }, + "risks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "description": { "type": "string" }, + "likelihood": { "enum": ["low", "medium", "high"] }, + "impact": { "enum": ["low", "medium", "high"] }, + "mitigation": { "type": "string" } + } + } + } + }, + "required": ["feature_slug", "archetype", "phases"] +} +``` + +### Example Task Node + +```json +{ + "task_id": "1.3", + "title": "Create ConfigBundlePrimitive class", + "description": "Implement add/remove lifecycle following EvaluatorPrimitive pattern", + "files": [ + { "path": "src/cli/primitives/ConfigBundlePrimitive.ts", "action": "create" }, + { "path": "src/cli/primitives/__tests__/ConfigBundlePrimitive.test.ts", "action": "create" } + ], + "depends_on": ["1.1", "1.2"], + "size": "M", + "verification": ["npm run typecheck", "npm test -- --testPathPattern=ConfigBundle"] +} +``` + +--- + +## Knowledge Snapshot Refresh (`refresh.ts`) + +### Why TypeScript + +The CLI repo requires Node.js. `ts-morph` gives accurate AST parsing. No Python dependency needed. + +### Structure + +``` +agents/knowledge/ +├── refresh.ts # main entry — orchestrates extractors +├── tsconfig.json +├── package.json # deps: ts-morph, yaml +└── extractors/ + ├── primitives.ts # registry.ts → primitive metadata + ├── schema.ts # agentcore-project.ts → shape + ├── deploy-flow.ts # deploy/ → step order + └── commands.ts # commands/ → verb/noun structure +``` + +### Extractor Responsibilities + +| Extractor | Source File(s) | Extracts | +| ---------------- | ------------------------------------------------- | ----------------------------------------------------------------------- | +| `primitives.ts` | `src/cli/primitives/registry.ts` + each primitive | name, file, schema_key, supports_tui, supports_remove, cross_references | +| `schema.ts` | `src/schema/schemas/agentcore-project.ts` | top_level_arrays, cross_field_validations | +| `deploy-flow.ts` | `src/cli/operations/deploy/deploy.ts` | step order, imperative vs CDK | +| `commands.ts` | `src/cli/commands/cli.ts` | verbs, nouns per verb | + +### Invocation + +```bash +# At pipeline start (ensures freshness) +cd agents/knowledge && npx tsx refresh.ts \ + --cli-root ../../ \ + --cdk-root ../../../agentcore-l3-cdk-constructs + +# On CI (cache — runs on merge to main in both repos) +# .github/workflows/refresh-snapshot.yml +``` + +Output: deterministic YAML with `schema_version` field for prompt compatibility. + +--- + +## Failure Modes & Recovery + +| # | Failure | Detection | Recovery | User Notification | +| --- | ----------------------------- | --------------------------- | ---------------------------------------------- | ---------------------------------------------------------------- | +| 1 | **API reference unreachable** | HTTP fetch fails | Ask user for inline ops or local file | "Cannot reach {url}. Provide API ops inline or as a local file." | +| 2 | **Snapshot refresh fails** | refresh.ts exits non-zero | Fall back to last committed snapshot + warn | "Refresh failed. Using cached version from {date}." | +| 3 | **Analogue file missing** | File read 404 | Check snapshot for alternatives; ask user | "{file} not found. Was it renamed? Alternatives: ..." | +| 4 | **Feature slug collision** | Validation against snapshot | Block, suggest alternatives | "{slug} collides with existing. Try: {alts}" | +| 5 | **Human abandons review** | 7 days no interaction | Ping. 14 days → archive. | "Inactive 7 days. Still working on it?" | +| 6 | **Self-review loops 3x** | Internal counter | Present best-effort with FAILED CHECKS visible | "Could not resolve: {items}. Presenting with issues flagged." | +| 7 | **Sensitivity violation** | Post-write scan | Block publishing, flag | "Found sensitive content: {details}" | + +--- + +## Success Metrics + +| Metric | Target | How to Measure | +| ----------------------------------- | ----------------------------------------- | ---------------------------------------------- | +| **Revision rounds before approval** | ≤ 3 | Count turns between first draft and APPROVED | +| **File path accuracy** | > 90% cited paths exist | Post-approval verification against repo | +| **Active wall-clock time** | < 2 hours input → approved doc | Session duration minus wait time | +| **Self-review first-pass rate** | > 80% (≤1 internal loop) | Track internal revision count | +| **Escalation quality** | 100% genuinely unresolvable | CLI team audits: were escalations appropriate? | +| **Reviewer satisfaction** | "Would use as-is to start implementation" | Ask after first 3 uses | + +--- + +## Change Cascade + +Changes can flow in any direction after approval: + +``` +DevEx Doc (Quip feedback) ──→ re-enter pipeline ──→ updated impl plan + │ +Impl Plan (reviewer finds issue) ──→ user applies directly (no full regen) + │ +Implementation (blocker) ──→ user updates plan ──→ optionally updates doc +``` + +The mechanism: user starts new session with updated doc or change description. Agent diffs against previous version and +propagates. Full regeneration only if fundamental approach changed. + +--- + +## Sensitivity Rules (Universal) + +| Rule | When `sensitivity_level = internal` | +| --------------------- | ------------------------------------------------------ | +| Public GH issues | Never reference internal service names | +| Public commits | Use `public_feature_name` only | +| DevEx doc destination | Private repo or Quip only | +| Artifacts | No pre-GA API details, account IDs, endpoint overrides | + +--- + +## Prerequisites + +- PR #1124 (`agents/` framework) lands first — provides `core/`, `harness_client.py`, orchestration infra +- If delayed: this pipeline can run standalone with direct Harness invocation, but loses shared plumbing diff --git a/agents/devex_doc_writer/archetypes/new-resource.md b/agents/devex_doc_writer/archetypes/new-resource.md new file mode 100644 index 000000000..eac01c7db --- /dev/null +++ b/agents/devex_doc_writer/archetypes/new-resource.md @@ -0,0 +1,362 @@ +# Archetype: New Resource + +**Priority:** P0 **Examples:** Configuration Bundles, Datasets, Harness, any new AgentCore service primitive + +--- + +## When This Applies + +A new service capability that maps to a new resource type in the CLI: + +- Has its own CRUD API operations (Create, Get, Update, Delete, List) +- Gets a new array in `agentcore.json` +- Gets a new primitive class in `src/cli/primitives/` +- Gets a CDK construct (if CFN support exists) or imperative deploy step +- Gets deployed state tracking in `deployed-state.json` + +If you're not adding a new resource type to the schema — if you're instead expanding what an existing surface can do — +see `scope-widening.md`. + +--- + +## Input Specification + +Inputs are split into two categories: + +- **User Provides:** Feature-specific facts that can't be derived from the codebase — API operations, trust policies, + account info, service details. These come from outside the repo (service team docs, API models, IAM requirements). +- **Agent Proposes:** Architecture decisions derivable from codebase patterns + the user's inputs. The agent proposes + these _with reasoning_ ("since cfn_support=true and the closest analogue uses CDK, I propose deploy_strategy: cdk"). + The user confirms or corrects. + +This isn't about knowledge gaps — CLI engineers know the architecture. It's about making the agent's assumptions +explicit and correctable rather than silently baked into the output. + +### User Provides (Required Contract) + +These fields are feature-specific and can't be derived from the codebase. The agent NEVER assumes values for these — if +missing, it asks. + +#### 1. Feature Identity + +| Field | Type | Description | Example | +| ---------------------- | ------ | -------------------------------------- | ------------------------------ | +| `feature_name` | string | Human-readable name | "Configuration Bundles" | +| `feature_slug` | string | Kebab-case for paths, commands | `config-bundle` | +| `feature_description` | string | 1-3 paragraphs. Problem + dev benefit. | "Configuration bundles let..." | +| `service_team` | string | Owning team | "AgentCore Control Plane" | +| `service_team_contact` | string | Alias for decisions/questions | "tjariy@" | + +#### 2. Target Repository & Sensitivity + +| Field | Type | Description | Example | +| --------------------- | ------ | ---------------------------------- | -------------------- | +| `target_repo` | enum | `public` or `private` | `"private"` | +| `target_cdk_repo` | enum | `public` or `private` | `"public"` | +| `sensitivity_level` | enum | `public` or `internal` | `"internal"` | +| `public_feature_name` | string | Required if internal + public repo | `"resource-bundles"` | + +#### 3. AWS Account & Environment + +| Field | Type | Description | Example | +| --------------------------- | -------- | ------------------------------ | --------------------------------------------- | +| `allowlisted_account_id` | string | 12-digit AWS account | `"123456789012"` | +| `allowlisted_regions` | string[] | Where service is available | `["us-west-2"]` | +| `sdk_available` | boolean | SDK includes operations? | `true` | +| `sdk_package_name` | string | Required if sdk_available=true | `"@aws-sdk/client-bedrock-agentcore-control"` | +| `service_endpoint_override` | string | Optional: beta/gamma endpoint | | + +#### 4. API Surface + +| Field | Type | Description | Example | +| -------------------------- | ----------- | -------------------------------------------------------------------- | ----------------------------- | +| `api_source` | enum | `smithy_model`, `dev_guide_url`, `openapi_spec`, `inline_operations` | `"smithy_model"` | +| `api_reference` | string | URL or local file path | | +| `control_plane_service` | string | Service identifier | `"bedrock-agentcore-control"` | +| `control_plane_operations` | Operation[] | CRUD operations | See below | +| `data_plane_service` | string | Optional: if async ops | `"bedrock-agentcore"` | +| `data_plane_operations` | Operation[] | Optional | | +| `status_enum` | StatusFlow | Optional: state machine for async | | + +#### 5. IAM & Trust Policy + +| Field | Type | Description | Example | +| ---------------------- | ----------------- | ---------------------------------------------- | ----------------------------------- | +| `trust_policy` | JSON | AssumeRolePolicyDocument for auto-created role | See example below | +| `service_principal` | string | CDK: `new iam.ServicePrincipal(...)` | `"bedrock-agentcore.amazonaws.com"` | +| `required_permissions` | PolicyStatement[] | Identity policy → `role.addToPolicy()` | | + +#### 6. CloudFormation Support + +| Field | Type | Description | Example | +| ------------------- | -------- | ------------------------- | --------------------------------------- | +| `cfn_support` | boolean | CFN support exists today? | `true` | +| `cfn_resource_type` | string | Required if true | `"AWS::BedrockAgentCore::ConfigBundle"` | +| `cfn_outputs` | string[] | Required if true | `["ConfigBundleId", "ConfigBundleArn"]` | + +#### 7. Developer Experience + +| Field | Type | Description | Example | +| ------------------- | -------- | ----------------------- | ------------------------- | +| `cli_verb` | string | Primary verb | `"add"` | +| `additional_verbs` | string[] | Optional: beyond CRUD | `["run", "pause"]` | +| `tui_flow` | boolean | Needs TUI wizard? | `true` | +| `supports_remove` | boolean | Removable? | `true` | +| `scope_constraints` | string[] | Optional: v1 exclusions | `["no dashboard for v1"]` | + +#### 8. Closest Analogue + +| Field | Type | Description | Example | +| ---------------------------- | ------ | ---------------------- | ------------------------------------ | +| `closest_primitive_analogue` | string | Most similar primitive | `"evaluator"` | +| `analogue_rationale` | string | Why this match | "Both schema-first, cross-refs, CDK" | + +--- + +### Agent Proposes (Inferred — Presented with Reasoning for Confirmation) + +After loading knowledge, the agent proposes these values with explicit rationale. The user confirms, corrects, or +overrides. Nothing is silently assumed. + +| Field | How Inferred | Example | +| ---------------------------- | --------------------------------------------- | ------------------------------------------ | +| `schema_location` | From analogue + standalone vs nested | `"top_level_array"` | +| `schema_key` | From feature_slug, camelCased | `"configBundles"` | +| `deploy_strategy` | `cfn_support=true` → `"cdk"` | `"cdk"` | +| `deploy_dependencies` | From cross-resource analysis | `["agent"]` | +| `tui_steps` | From API input shape (group fields logically) | `["Name", "Config", "Confirm"]` | +| `remove_has_dependencies` | From referenced_by analysis | `true` | +| `references_other_resources` | From API input fields that look like names | `[{field: "agentName", target: "agents"}]` | +| `cross_resource_permissions` | From trust policy + permissions analysis | | +| `resource_name_constraints` | From API docs regex | | + +--- + +### Sub-Schemas + +#### Operation + +```yaml +Operation: + name: string # "CreateConfigBundle" + http_method: string # POST + http_path: string # "/configuration-bundles" + input_shape: string # Key fields (brief is fine) + output_shape: string # Key fields + is_async: boolean # Returns 202? + notes: string # Constraints, limits +``` + +#### StatusFlow + +```yaml +StatusFlow: + states: string[] + transitions: + - from: 'PENDING' + to: ['IN_PROGRESS', 'FAILED'] + terminal_states: string[] + poll_operation: string +``` + +#### PolicyStatement + +```yaml +PolicyStatement: + actions: string[] # ["bedrock-agentcore:GetConfigBundle"] + resources: string[] # ARN patterns with ${partition}, ${region}, ${account} + conditions: object # Optional + description: string # Why needed +``` + +#### Trust Policy Example + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { "Service": "bedrock-agentcore.amazonaws.com" }, + "Action": "sts:AssumeRole", + "Condition": { + "StringEquals": { "aws:SourceAccount": "${AWS::AccountId}" } + } + } + ] +} +``` + +--- + +## Input Validation Rules + +1. `feature_slug` must not collide with existing schema keys or commands +2. `trust_policy` must be valid JSON with Statement + Principal +3. `control_plane_operations` must include at least one mutating op (Create/Update/Delete) OR be explicitly read-only +4. `closest_primitive_analogue` must exist in snapshot +5. `allowlisted_account_id` must be 12 digits +6. `allowlisted_regions` must be valid AgentCore regions +7. If `cfn_support=true`: `cfn_resource_type` and `cfn_outputs` required +8. If `data_plane_operations` non-empty: `status_enum` recommended (warning) +9. If `sensitivity_level=internal` AND `target_repo=public`: `public_feature_name` required +10. If `sdk_available=false`: `service_endpoint_override` recommended (warning) + +--- + +## Archetype-Specific Biases + +These extend the universal biases from SYSTEM.md: + +7. **CDK if CFN exists, imperative if not.** Never mix within a single resource. If CFN is promised but unavailable + today: ship imperative now with documented CDK migration plan. Imperative code marked transitional. +8. **TUI for creation, flags for operations.** Creation flows (`agentcore add *`) have multi-field wizards. Operational + commands are flags-only. +9. **Flat schemas over nested.** Unless cross-resource references demand nesting. +10. **Same service principal unless told otherwise.** Default `bedrock-agentcore.amazonaws.com`. + +--- + +## Self-Review Extensions + +In addition to the 9-point base checklist (SYSTEM.md), new-resource docs are checked for: + +| # | Check | Critical? | What Passes | +| --- | ------------------------------ | --------- | --------------------------------------------------------------- | +| R1 | **API coverage** | Yes | Every control plane operation mapped to a CLI command | +| R2 | **Schema shown as JSON + Zod** | Yes | Both `agentcore.json` example AND TypeScript Zod schema present | +| R3 | **Deployed state specified** | Yes | `deployed-state.json` additions defined with field names | +| R4 | **CDK vs imperative decided** | Yes | Explicitly chosen with rationale + precedent | +| R5 | **Deploy flow shown** | No | Shows where new step inserts in numbered flow | +| R6 | **Cross-field validation** | No | If cross-references exist, superRefine rules described | +| R7 | **TUI wireframes** | No | Multi-step flows have ASCII mockups | + +--- + +## Template Sections + +For this archetype, the DevEx doc includes these sections (in order): + +1. **What is [Feature]?** — 2-3 paragraphs for someone unfamiliar +2. **Scope** — what's in, what's out +3. **Positioning in the CLI** — how relates to existing features (table) +4. **Developer Journeys** — numbered scenarios with CLI commands, TUI wireframes, "Under the hood:", "What we build:" +5. **API Surface** — control plane + data plane tables, status values +6. **CLI Command ↔ API Mapping** — table +7. **Schema Changes** — agentcore.json (JSON) + Zod + deployed-state.json +8. **Codebase Changes** — New Files table + Modified Files table +9. **How It Fits in the CLI Architecture** — CDK vs imperative, deploy flow, execution role +10. **Architectural Decisions** — table with rationale +11. **Implementation Phases** — independently shippable +12. **Testing Strategy** — unit, snapshot, integration +13. **Open Questions** — table +14. **Appendix** — TUI mockups, IAM policies, full API shapes (implementation detail here) + +--- + +## Implementation Plan Shape (Stage 2) + +New Resource features typically follow this phase pattern: + +``` +Phase 1: Schema + Primitive + Remove + • Zod schema for the resource + • Add to agentcore-project.ts (top-level array) + • Deployed state schema additions + • Primitive class (add + remove lifecycle) + • Register in registry + • Wire into add/remove commands + • Unit tests + +Phase 2: CDK Construct + Deploy + • CDK construct in agentcore-l3-cdk-constructs + • IAM role creation with trust policy + • Required permissions + • Integration into deploy flow (CDK or imperative step) + • deployed-state.json population from CFN outputs + • Deploy tests + +Phase 3: TUI + Polish + • TUI wizard screens (if tui_flow=true) + • Wizard hook (step logic) + • Operation hook + • Snapshot tests for any new assets/templates + +Phase 4: Data Plane Operations (if applicable) + • Data plane commands (run, poll, get results) + • Status tracking / polling logic + • Streaming output (if async) + +Phase 5: Advanced Features (per scope_constraints) + • Whatever was deferred from v1 +``` + +Not all phases apply to every resource. The impl plan writer picks the relevant ones. + +--- + +## Quick Start Example + +```yaml +archetype: 'new_resource' + +feature_name: 'Configuration Bundles' +feature_slug: 'config-bundle' +feature_description: 'Package and version configuration parameters for agents' +service_team: 'AgentCore Control Plane' +service_team_contact: 'tjariy@' + +target_repo: 'private' +target_cdk_repo: 'public' +sensitivity_level: 'internal' + +allowlisted_account_id: '123456789012' +allowlisted_regions: ['us-west-2'] +sdk_available: true +sdk_package_name: '@aws-sdk/client-bedrock-agentcore-control' + +api_source: 'smithy_model' +api_reference: 'https://...' +control_plane_service: 'bedrock-agentcore-control' +control_plane_operations: + - name: 'CreateConfigBundle' + http_method: 'POST' + http_path: '/configuration-bundles' + input_shape: 'name, parameters[]' + output_shape: 'configBundleId, configBundleArn, status' + is_async: false + notes: '' + +trust_policy: + Version: '2012-10-17' + Statement: + - Effect: 'Allow' + Principal: + Service: 'bedrock-agentcore.amazonaws.com' + Action: 'sts:AssumeRole' +service_principal: 'bedrock-agentcore.amazonaws.com' +required_permissions: + - actions: ['bedrock-agentcore:GetConfigBundle'] + resources: ['arn:${partition}:bedrock-agentcore:${region}:${account}:config-bundle/*'] + description: 'Runtime reads config bundles at invocation' + +cfn_support: true +cfn_resource_type: 'AWS::BedrockAgentCore::ConfigBundle' +cfn_outputs: ['ConfigBundleId', 'ConfigBundleArn'] + +cli_verb: 'add' +tui_flow: true +supports_remove: true + +closest_primitive_analogue: 'evaluator' +analogue_rationale: 'Schema-first resource with CDK deploy and cross-resource references' +``` + +After receiving this, the agent will: + +1. Validate all fields +2. Refresh knowledge snapshot +3. Validate analogue choice (does `evaluator` shape match this feature?) +4. Propose inferred decisions (schema_key, deploy_strategy, tui_steps, etc.) +5. Ask user to confirm +6. Begin writing diff --git a/agents/devex_doc_writer/archetypes/scope-widening.md b/agents/devex_doc_writer/archetypes/scope-widening.md new file mode 100644 index 000000000..c44deb01c --- /dev/null +++ b/agents/devex_doc_writer/archetypes/scope-widening.md @@ -0,0 +1,369 @@ +# Archetype: Scope Widening + +**Priority:** P0 **Examples:** Terraform support, Node.js CodeZip, new auth model (CUSTOM_JWT), Container build support, +new container runtime (Finch/Podman), multi-region deploy + +--- + +## When This Applies + +You're expanding what an existing CLI surface can do — not adding a new resource type. The feature: + +- Does NOT get its own new array in `agentcore.json` (usually) +- Does NOT get a new primitive class (usually) +- DOES touch existing commands, deploy flow, build system, or configuration +- DOES require coexistence with the current approach (backwards compat) +- DOES affect multiple existing files across several layers + +**The key difference from New Resource:** New Resource is additive (new schema, new primitive, new construct). Scope +Widening is multiplicative (existing surfaces gain new capabilities, existing patterns branch into variants). + +**Historical examples already shipped:** + +- Container build type (added alongside CodeZip) +- CUSTOM_JWT authorizer (added alongside AWS_IAM) +- VPC network mode (added alongside PUBLIC) +- Finch/Podman container runtimes (added alongside Docker) + +--- + +## Input Specification + +Inputs split into two categories: + +- **User Provides:** Feature-specific facts that can't be derived from the codebase — current/target architecture, + affected surfaces, external tool requirements, coexistence model. +- **Agent Proposes:** Architecture decisions derivable from codebase patterns + the user's inputs. Proposed with + explicit reasoning, confirmed or corrected by the user. + +### User Provides (Required Contract) + +These fields are feature-specific and can't be derived from the codebase. The agent NEVER assumes values for these — if +missing, it asks. + +#### 1. Feature Identity + +| Field | Type | Description | Example | +| ---------------------- | ------ | ----------------------- | ----------------------------------------------------------------------------------------------------- | +| `feature_name` | string | Human-readable name | "Terraform Deploy Support" | +| `feature_slug` | string | Kebab-case identifier | `terraform-deploy` | +| `feature_description` | string | Problem + what devs get | "Developers can deploy AgentCore projects using Terraform as an alternative to CDK/CloudFormation..." | +| `service_team` | string | Owning team | "AgentCore CLI" | +| `service_team_contact` | string | Alias for decisions | "gitikavj@" | + +#### 2. Target Repository & Sensitivity + +Same as New Resource archetype — see `new-resource.md` section 2. + +#### 3. AWS Account & Environment + +| Field | Type | Description | Example | +| ------------------------ | ------------ | ------------------------------ | ---------------- | +| `allowlisted_account_id` | string | For testing | `"123456789012"` | +| `allowlisted_regions` | string[] | Where to test | `["us-west-2"]` | +| `external_dependencies` | Dependency[] | Tools/services this depends on | See below | + +#### 4. Current Architecture + +| Field | Type | Description | Example | +| ------------------------- | -------- | ------------------------------------------------ | --------------------------------------------------------------------- | +| `current_description` | string | How it works today (1-2 paragraphs) | "Deploy currently uses CDK to synthesize CloudFormation templates..." | +| `current_key_files` | string[] | The files/directories this feature touches today | `["src/cli/cdk/", "src/cli/operations/deploy/", "src/assets/cdk/"]` | +| `current_user_experience` | string | What the developer does today (CLI commands) | "`agentcore deploy` synths CDK and runs `cdk deploy`" | + +#### 5. Target Architecture + +| Field | Type | Description | Example | +| ---------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| `target_description` | string | How it should work after (1-2 paragraphs) | "Developer chooses Terraform or CDK at project creation. `agentcore deploy` dispatches to the appropriate provider..." | +| `coexistence_model` | enum | How old and new coexist: `user_chooses_one` (per project), `stacked` (new builds on old), `migration` (old deprecated) | `"user_chooses_one"` | +| `backwards_compatible` | boolean | Do existing projects break? Must be true unless there's a migration plan. | `true` | +| `migration_plan` | string | Required if backwards_compatible=false. How existing users transition. | | + +#### 6. Affected Surfaces + +| Field | Type | Description | Example | +| ----------------------- | ----------------- | -------------------------------------- | -------------------------------------------------------------- | +| `affected_commands` | AffectedCommand[] | Which existing commands change and how | See below | +| `affected_schema` | AffectedSchema[] | Which config files change | See below | +| `affected_deploy_flow` | string | How the deploy pipeline changes | "Step 4 branches: CDK path (existing) or Terraform path (new)" | +| `affected_build_system` | string | Optional: if build/packaging changes | | + +#### 7. External Tool/Service + +| Field | Type | Description | Example | +| ----------------------- | -------- | -------------------------------------------- | -------------------------------------------------------- | +| `external_tool` | string | Optional: external tool being integrated | "Terraform" | +| `external_tool_version` | string | Optional: minimum version | ">=1.5.0" | +| `external_docs_url` | string | Optional: reference docs | "https://developer.hashicorp.com/terraform" | +| `user_prerequisites` | string[] | What the user must have installed/configured | `["terraform CLI installed", "AWS provider configured"]` | + +#### 8. Closest Analogue + +| Field | Type | Description | Example | +| --------------------------------- | ------ | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| `closest_scope_widening_analogue` | string | Previous scope widening that's most similar | "Container build type addition" | +| `analogue_rationale` | string | Why | "Both add a new deploy provider choice at project level, both coexist with existing, both need template changes" | + +--- + +### Sub-Schemas + +#### AffectedCommand + +```yaml +AffectedCommand: + command: string # "agentcore deploy" + change_type: enum # "new_flag" | "new_behavior" | "branching_logic" | "new_subcommand" + description: string # "Adds --provider flag or reads from project config" + backwards_compatible: boolean +``` + +#### AffectedSchema + +```yaml +AffectedSchema: + file: string # "agentcore.json" or "deployed-state.json" + change_type: enum # "new_field" | "new_enum_value" | "new_section" | "type_widening" + description: string # "Add 'deployProvider' field to project root: 'cdk' | 'terraform'" +``` + +#### Dependency + +```yaml +Dependency: + name: string # "terraform" + type: enum # "cli_tool" | "npm_package" | "aws_service" | "runtime" + version_constraint: string # ">=1.5.0" + optional: boolean # true if feature degrades gracefully without it + detection: string # How CLI checks if it's available: "which terraform" +``` + +--- + +### Agent Proposes (Inferred — Presented with Reasoning for Confirmation) + +Nothing is silently assumed. The agent proposes with rationale, the user confirms or overrides. + +| Field | How Inferred | Example | +| ------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------ | +| `implementation_approach` | From coexistence_model + affected surfaces | "Strategy pattern: deploy dispatches to CDKProvider or TerraformProvider" | +| `new_files` | From affected surfaces + analogue | `["src/cli/operations/deploy/terraform-provider.ts"]` | +| `modified_files` | From affected_commands + affected_schema | `["src/cli/operations/deploy/deploy.ts", "src/schema/schemas/agentcore-project.ts"]` | +| `feature_detection` | From external_tool + user_prerequisites | "Check `which terraform` at deploy time, error with install guidance" | +| `rollback_strategy` | From backwards_compatible + coexistence_model | "User switches back by changing `deployProvider` in config" | + +--- + +## Input Validation Rules + +1. `current_key_files` must all exist in the snapshot (validates we're talking about real code) +2. `coexistence_model` required — no scope widening lands without a coexistence story +3. If `backwards_compatible=false`: `migration_plan` is required +4. `affected_commands` must reference real existing commands (checked against snapshot) +5. `affected_schema` changes must not conflict with existing field names +6. If `external_tool`: `user_prerequisites` should describe how CLI detects availability +7. `closest_scope_widening_analogue` should map to a known historical change (agent validates) + +--- + +## Archetype-Specific Biases + +These extend the universal biases from SYSTEM.md: + +7. **Coexistence over replacement.** Never deprecate the existing path in v1. Both old and new must work. Deprecation is + a separate, later decision. +8. **Detection over assumption.** If the feature depends on an external tool, the CLI detects its presence and fails + with clear guidance. Never assume it's installed. +9. **Configuration over flags for persistent choices.** If the user picks "Terraform" once, that goes in config + (`agentcore.json`). Don't make them pass `--provider terraform` every time. +10. **Same commands, branching internals.** `agentcore deploy` still works — it dispatches to the right provider + internally. Don't add `agentcore deploy-terraform` as a separate command. +11. **Template at creation, not retrofit.** New projects get the new option at `agentcore create` time. Existing + projects can opt-in via config change, but don't auto-migrate. + +--- + +## Self-Review Extensions + +In addition to the 9-point base checklist (SYSTEM.md), scope-widening docs are checked for: + +| # | Check | Critical? | What Passes | +| --- | ---------------------------------- | --------- | ----------------------------------------------------------------------- | +| S1 | **Backwards compatibility proven** | Yes | Existing projects work unchanged without any action | +| S2 | **Coexistence model explicit** | Yes | How old + new coexist is stated clearly (not implied) | +| S3 | **Affected commands listed** | Yes | Every command that changes behavior is identified | +| S4 | **Migration path defined** | No | If user wants to switch from old to new (or back), steps are documented | +| S5 | **Detection/prerequisites shown** | Yes | How CLI detects external deps + error message if missing | +| S6 | **Blast radius bounded** | Yes | List of modified files is complete; no hidden impacts | +| S7 | **Existing tests don't break** | No | Strategy for ensuring existing test suite still passes | + +--- + +## Template Sections + +For this archetype, the DevEx doc includes these sections (in order): + +1. **What is [Feature]?** — 2-3 paragraphs, problem + benefit +2. **Scope** — in/out, explicitly what's NOT changing +3. **Current State** — how it works today (commands, flow, architecture) +4. **Target State** — how it works after (commands, flow, architecture) +5. **Coexistence Model** — how old and new live together, user's choice mechanism +6. **Developer Journeys** — numbered scenarios: + - New project with the new capability + - Existing project opting in + - Existing project that doesn't change (proves backwards compat) +7. **Affected Commands** — table: command, what changes, backwards compat? +8. **Schema Changes** — what fields change/add in agentcore.json, deployed-state.json +9. **Detection & Prerequisites** — how CLI checks for external deps, error messages +10. **Codebase Changes** — New Files + Modified Files tables (these tend to be heavy on "Modified") +11. **How It Fits: Architecture** — where the branching point is, which pattern (strategy, adapter, etc.) +12. **Deploy Flow Change** — current numbered flow vs new numbered flow (side by side) +13. **Architectural Decisions** — table with rationale +14. **Implementation Phases** — independently shippable +15. **Testing Strategy** — existing test preservation, new test matrix (N variants × M scenarios) +16. **Open Questions** — table +17. **Appendix** — full config examples for old path + new path side by side + +--- + +## Implementation Plan Shape (Stage 2) + +Scope Widening features typically follow this phase pattern: + +``` +Phase 1: Foundation + Detection + • External tool detection utility (if applicable) + • Configuration schema change (new field/enum) + • Project-level config validation + • No behavior change yet — just the ability to declare intent + • Tests: schema validation, detection logic + +Phase 2: Core Implementation + • The new code path (e.g., TerraformProvider) + • Integration point (e.g., deploy dispatcher branches) + • Template/assets for new path (e.g., Terraform templates) + • Tests: new path works end-to-end + +Phase 3: Creation Flow Integration + • `agentcore create` offers the new option + • Templates vended for new path + • TUI wizard updated (if applicable) + • Tests: create flow, template snapshots + +Phase 4: Polish + Migration + • Opt-in mechanism for existing projects + • Documentation updates + • Error messages for edge cases (wrong version, missing tool, etc.) + • Tests: migration path, error scenarios +``` + +Key difference from New Resource: Phase 1 is about _declaring intent without behavior change_ (safe to merge early), +then Phase 2 adds the behavior. + +--- + +## Quick Start Example + +```yaml +archetype: 'scope_widening' + +feature_name: 'Terraform Deploy Support' +feature_slug: 'terraform-deploy' +feature_description: | + Developers can deploy AgentCore projects using Terraform as an alternative + to CDK/CloudFormation. This gives teams already using Terraform a native + integration path without requiring CDK expertise. +service_team: 'AgentCore CLI' +service_team_contact: 'gitikavj@' + +target_repo: 'private' +target_cdk_repo: 'public' +sensitivity_level: 'internal' + +allowlisted_account_id: '123456789012' +allowlisted_regions: ['us-west-2'] +external_dependencies: + - name: 'terraform' + type: 'cli_tool' + version_constraint: '>=1.5.0' + optional: false + detection: 'which terraform && terraform version' + +current_description: | + Deploy uses CDK to synthesize CloudFormation templates and deploy via + the CDK toolkit. All resources (runtimes, memories, credentials, etc.) + are modeled as CDK constructs in @aws/agentcore-l3-cdk-constructs. +current_key_files: + - 'src/cli/cdk/' + - 'src/cli/operations/deploy/' + - 'src/assets/cdk/' + - 'agentcore-l3-cdk-constructs (separate repo)' +current_user_experience: | + `agentcore deploy` synths the CDK app and runs `cdk deploy`. + Resources are defined in agentcore.json, translated to CDK constructs. + +target_description: | + Developer chooses Terraform or CDK at project creation time (or opts in + later via config). `agentcore deploy` dispatches to the appropriate + provider. Both produce the same deployed-state.json output format. +coexistence_model: 'user_chooses_one' +backwards_compatible: true + +affected_commands: + - command: 'agentcore create' + change_type: 'new_behavior' + description: 'Asks deploy provider preference (CDK or Terraform)' + backwards_compatible: true + - command: 'agentcore deploy' + change_type: 'branching_logic' + description: 'Dispatches to CDK or Terraform provider based on project config' + backwards_compatible: true + - command: 'agentcore status' + change_type: 'new_behavior' + description: 'Reads state from terraform.tfstate or CFN outputs depending on provider' + backwards_compatible: true + +affected_schema: + - file: 'agentcore.json' + change_type: 'new_field' + description: "Add 'deployProvider' field at project root: 'cdk' | 'terraform' (default: 'cdk')" + - file: 'deployed-state.json' + change_type: 'new_field' + description: "Add 'stateBackend' field: 'cloudformation' | 'terraform-local' | 'terraform-s3'" + +affected_deploy_flow: | + Step 4 branches: + CDK path (existing): CDK synth + cdk deploy + Terraform path (new): Generate .tf files from schema → terraform init → terraform apply + Steps 1-3 and 5-6 remain the same regardless of provider. + +external_tool: 'terraform' +external_tool_version: '>=1.5.0' +external_docs_url: 'https://developer.hashicorp.com/terraform' +user_prerequisites: + - 'terraform CLI installed (>=1.5.0)' + - 'AWS provider for Terraform configured' + +closest_scope_widening_analogue: 'Container build type addition' +analogue_rationale: | + Both add a new provider choice at project level. Container added a new + build type (CodeZip vs Container) that branches the packaging and deploy + logic. Terraform adds a new deploy provider that branches the deploy + logic. Both coexist, user chooses at create time. +``` + +--- + +## Differences from New Resource at a Glance + +| Dimension | New Resource | Scope Widening | +| ------------------ | -------------------------- | ------------------------------------------- | +| Schema change | New array added | Existing fields gain new values/options | +| Primitive | New class created | No new primitive (usually) | +| Deploy flow | New step added | Existing step branches | +| CDK construct | New construct | Existing constructs gain variants | +| Backwards compat | Always (additive) | Must be proven (not automatic) | +| Risk profile | Low (isolated) | Higher (touches shared code paths) | +| Testing burden | New tests for new resource | New tests + verify all old tests still pass | +| Typical file count | 8-12 new files | 3-5 new + 10-15 modified | diff --git a/agents/devex_doc_writer/examples/config-bundle-input.yaml b/agents/devex_doc_writer/examples/config-bundle-input.yaml new file mode 100644 index 000000000..6c7159544 --- /dev/null +++ b/agents/devex_doc_writer/examples/config-bundle-input.yaml @@ -0,0 +1,97 @@ +archetype: "new_resource" + +feature_name: "Configuration Bundles" +feature_slug: "config-bundle" +feature_description: | + Configuration bundles let developers package and version sets of configuration + parameters for their agents. Instead of hardcoding config values or managing + environment variables, developers declare named bundles in agentcore.json that + get deployed alongside the agent runtime. The runtime can read bundle values + at invocation time via the AgentCore SDK. +service_team: "AgentCore Control Plane" +service_team_contact: "tjariy@" + +target_repo: "private" +target_cdk_repo: "public" +sensitivity_level: "internal" + +allowlisted_account_id: "123456789012" +allowlisted_regions: ["us-west-2"] +sdk_available: true +sdk_package_name: "@aws-sdk/client-bedrock-agentcore-control" + +api_source: "smithy_model" +api_reference: "https://model.example.com/config-bundles.smithy" +control_plane_service: "bedrock-agentcore-control" +control_plane_operations: + - name: "CreateConfigBundle" + http_method: "POST" + http_path: "/configuration-bundles" + input_shape: "name: string, parameters: Parameter[], description?: string" + output_shape: "configBundleId: string, configBundleArn: string, status: string" + is_async: false + notes: "Name must be unique per account" + - name: "GetConfigBundle" + http_method: "GET" + http_path: "/configuration-bundles/{configBundleId}" + input_shape: "configBundleId: string" + output_shape: "name, parameters[], status, createdAt, updatedAt" + is_async: false + notes: "" + - name: "UpdateConfigBundle" + http_method: "PUT" + http_path: "/configuration-bundles/{configBundleId}" + input_shape: "configBundleId: string, parameters?: Parameter[], description?: string" + output_shape: "configBundleId, status" + is_async: false + notes: "Partial update supported" + - name: "DeleteConfigBundle" + http_method: "DELETE" + http_path: "/configuration-bundles/{configBundleId}" + input_shape: "configBundleId: string" + output_shape: "{}" + is_async: false + notes: "Fails if bundle is referenced by an active runtime" + - name: "ListConfigBundles" + http_method: "GET" + http_path: "/configuration-bundles" + input_shape: "maxResults?: number, nextToken?: string" + output_shape: "configBundles[], nextToken?" + is_async: false + notes: "Paginated" + +trust_policy: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + Service: "bedrock-agentcore.amazonaws.com" + Action: "sts:AssumeRole" + Condition: + StringEquals: + "aws:SourceAccount": "${AWS::AccountId}" + +service_principal: "bedrock-agentcore.amazonaws.com" +required_permissions: + - actions: ["bedrock-agentcore:GetConfigBundle", "bedrock-agentcore:ListConfigBundles"] + resources: ["arn:${partition}:bedrock-agentcore:${region}:${account}:config-bundle/*"] + description: "Runtime reads config bundles at invocation time" + +cfn_support: true +cfn_resource_type: "AWS::BedrockAgentCore::ConfigBundle" +cfn_outputs: ["ConfigBundleId", "ConfigBundleArn"] + +cli_verb: "add" +tui_flow: true +supports_remove: true + +closest_primitive_analogue: "evaluator" +analogue_rationale: | + Both are schema-first resources with cross-resource references (evaluator + references agents, config-bundle will be referenced by agents), CDK deploy + path, and similar CRUD lifecycle. Evaluator is the cleanest existing primitive + with a straightforward add/remove pattern. + +additional_context: | + Service team has confirmed CFN support will be available by the time we ship. + They want the CLI to validate parameter types locally before deploy. diff --git a/agents/devex_doc_writer/inputs/schema.yaml b/agents/devex_doc_writer/inputs/schema.yaml new file mode 100644 index 000000000..25e9695d7 --- /dev/null +++ b/agents/devex_doc_writer/inputs/schema.yaml @@ -0,0 +1,497 @@ +# DevEx Doc Writer — Input Schema +# Formal YAML schema definition for validating inputs before the pipeline runs. +# Supports both archetypes: new_resource and scope_widening. + +--- +# Root schema — the input YAML must match one of the archetype variants +root: + type: object + required: + - archetype + - feature_name + - feature_slug + - feature_description + - service_team + - service_team_contact + - target_repo + - target_cdk_repo + - sensitivity_level + - allowlisted_account_id + - allowlisted_regions + properties: + archetype: + type: string + enum: [new_resource, scope_widening] + description: Feature archetype. Determines which input fields are required. + + # ─── Feature Identity ─────────────────────────────────────────────── + feature_name: + type: string + min_length: 1 + max_length: 100 + description: Human-readable name of the feature + + feature_slug: + type: string + pattern: '^[a-z][a-z0-9-]*$' + min_length: 1 + max_length: 48 + description: Kebab-case identifier for file paths, commands, schema keys + + feature_description: + type: string + min_length: 50 + description: 1-3 paragraphs describing the feature for someone unfamiliar + + service_team: + type: string + min_length: 1 + description: Owning service team name + + service_team_contact: + type: string + pattern: '^[a-z]+@?$' + description: Alias to tag for questions/decisions + + # ─── Target Repository & Sensitivity ──────────────────────────────── + target_repo: + type: string + enum: [public, private] + + target_cdk_repo: + type: string + enum: [public, private] + + sensitivity_level: + type: string + enum: [public, internal] + + public_feature_name: + type: string + required_if: + sensitivity_level: internal + target_repo: public + description: Sanitized name safe for public commits + + # ─── AWS Account & Environment ────────────────────────────────────── + allowlisted_account_id: + type: string + pattern: '^[0-9]{12}$' + description: 12-digit AWS account ID + + allowlisted_regions: + type: array + items: + type: string + min_items: 1 + description: Regions where the service is available + + sdk_available: + type: boolean + description: Whether AWS SDK includes these operations + + sdk_package_name: + type: string + required_if: + sdk_available: true + pattern: '^@aws-sdk/client-' + description: The @aws-sdk/client-* package name + + service_endpoint_override: + type: string + format: uri + description: Custom endpoint for beta/gamma SDK (optional) + + service_ga_date: + type: string + description: Expected GA date (optional, affects beta labeling) + +--- +# ─── New Resource Archetype ───────────────────────────────────────────────── +new_resource: + extends: root + additional_required: + - api_source + - api_reference + - control_plane_service + - control_plane_operations + - trust_policy + - service_principal + - required_permissions + - cfn_support + - cli_verb + - tui_flow + - supports_remove + - closest_primitive_analogue + - analogue_rationale + properties: + # ─── API Surface ────────────────────────────────────────────────────── + api_source: + type: string + enum: [smithy_model, dev_guide_url, openapi_spec, inline_operations] + + api_reference: + type: string + description: URL or local file path to API model + + control_plane_service: + type: string + description: Service identifier for control plane + + control_plane_operations: + type: array + min_items: 1 + items: + type: object + required: [name, http_method, http_path, input_shape, output_shape, is_async] + properties: + name: + type: string + http_method: + type: string + enum: [GET, POST, PUT, DELETE, PATCH] + http_path: + type: string + input_shape: + type: string + output_shape: + type: string + is_async: + type: boolean + notes: + type: string + + data_plane_service: + type: string + description: Optional data plane service identifier + + data_plane_operations: + type: array + items: + $ref: '#/new_resource/properties/control_plane_operations/items' + + status_enum: + type: object + properties: + states: + type: array + items: { type: string } + transitions: + type: array + items: + type: object + properties: + from: { type: string } + to: { type: array, items: { type: string } } + terminal_states: + type: array + items: { type: string } + poll_operation: + type: string + + # ─── IAM & Trust Policy ─────────────────────────────────────────────── + trust_policy: + type: object + description: AssumeRolePolicyDocument JSON + required: [Version, Statement] + properties: + Version: { type: string } + Statement: + type: array + min_items: 1 + + service_principal: + type: string + description: Service principal for the execution role + + required_permissions: + type: array + min_items: 1 + items: + type: object + required: [actions, resources, description] + properties: + actions: + type: array + items: { type: string } + resources: + type: array + items: { type: string } + conditions: + type: object + description: + type: string + + resource_policy: + type: object + description: Optional resource-based policy + + cross_resource_permissions: + type: array + items: + type: object + required: [source_resource, target_resource, direction, actions, description] + properties: + source_resource: { type: string } + target_resource: { type: string } + direction: { type: string, enum: [grants_to, receives_from] } + actions: { type: array, items: { type: string } } + description: { type: string } + + # ─── CloudFormation ─────────────────────────────────────────────────── + cfn_support: + type: boolean + + cfn_resource_type: + type: string + required_if: + cfn_support: true + pattern: '^AWS::' + + cfn_outputs: + type: array + required_if: + cfn_support: true + items: { type: string } + + # ─── Developer Experience ───────────────────────────────────────────── + cli_verb: + type: string + description: Primary verb (usually "add") + + additional_verbs: + type: array + items: { type: string } + + tui_flow: + type: boolean + + supports_remove: + type: boolean + + scope_constraints: + type: array + items: { type: string } + + # ─── Closest Analogue ───────────────────────────────────────────────── + closest_primitive_analogue: + type: string + validate_against: snapshot.primitives[].kind + description: Must exist in the current primitives registry + + analogue_rationale: + type: string + min_length: 10 + + differs_from_analogue: + type: array + items: { type: string } + + anti_patterns: + type: array + items: { type: string } + + # ─── Context ────────────────────────────────────────────────────────── + api_docs_url: + type: string + format: uri + + design_doc_url: + type: string + + related_devex_docs: + type: array + items: { type: string } + + additional_context: + type: string + +--- +# ─── Scope Widening Archetype ─────────────────────────────────────────────── +scope_widening: + extends: root + additional_required: + - current_description + - current_key_files + - current_user_experience + - target_description + - coexistence_model + - backwards_compatible + - affected_commands + - closest_scope_widening_analogue + - analogue_rationale + properties: + # ─── Current Architecture ───────────────────────────────────────────── + current_description: + type: string + min_length: 50 + description: How it works today (1-2 paragraphs) + + current_key_files: + type: array + min_items: 1 + items: { type: string } + validate_against: snapshot (files/dirs must exist) + + current_user_experience: + type: string + description: Current CLI commands/workflow + + # ─── Target Architecture ────────────────────────────────────────────── + target_description: + type: string + min_length: 50 + + coexistence_model: + type: string + enum: [user_chooses_one, stacked, migration] + + backwards_compatible: + type: boolean + + migration_plan: + type: string + required_if: + backwards_compatible: false + + # ─── Affected Surfaces ──────────────────────────────────────────────── + affected_commands: + type: array + min_items: 1 + items: + type: object + required: [command, change_type, description, backwards_compatible] + properties: + command: + type: string + validate_against: snapshot.commands + change_type: + type: string + enum: [new_flag, new_behavior, branching_logic, new_subcommand] + description: + type: string + backwards_compatible: + type: boolean + + affected_schema: + type: array + items: + type: object + required: [file, change_type, description] + properties: + file: + type: string + enum: [agentcore.json, deployed-state.json, aws-targets.json] + change_type: + type: string + enum: [new_field, new_enum_value, new_section, type_widening] + description: + type: string + + affected_deploy_flow: + type: string + + affected_build_system: + type: string + + # ─── External Tool ──────────────────────────────────────────────────── + external_tool: + type: string + + external_tool_version: + type: string + + external_docs_url: + type: string + format: uri + + user_prerequisites: + type: array + items: { type: string } + + external_dependencies: + type: array + items: + type: object + required: [name, type, version_constraint, optional, detection] + properties: + name: { type: string } + type: { type: string, enum: [cli_tool, npm_package, aws_service, runtime] } + version_constraint: { type: string } + optional: { type: boolean } + detection: { type: string } + + # ─── Closest Analogue ───────────────────────────────────────────────── + closest_scope_widening_analogue: + type: string + description: Previous scope widening that's most similar + + analogue_rationale: + type: string + min_length: 10 + + # ─── Context ────────────────────────────────────────────────────────── + design_doc_url: + type: string + + related_devex_docs: + type: array + items: { type: string } + + additional_context: + type: string + +--- +# ─── Validation Rules (cross-field) ──────────────────────────────────────── +validation_rules: + - id: slug_no_collision + description: feature_slug must not collide with existing schema keys or command nouns + check: + feature_slug NOT IN snapshot.schema_shape.agentcore_json.top_level_arrays[].key AND feature_slug NOT IN + snapshot.commands.verbs[].nouns[] + + - id: trust_policy_valid + description: trust_policy must have at least one Statement with a Principal + check: trust_policy.Statement.length >= 1 AND each Statement has Principal + applies_to: new_resource + + - id: control_plane_has_mutating_op + description: control_plane_operations must include at least one mutating operation + check: control_plane_operations has at least one where http_method IN [POST, PUT, DELETE, PATCH] + applies_to: new_resource + + - id: analogue_exists + description: closest_primitive_analogue must exist in snapshot + check: closest_primitive_analogue IN snapshot.primitives[].kind + applies_to: new_resource + + - id: account_id_format + description: allowlisted_account_id must be 12 digits + check: allowlisted_account_id matches /^[0-9]{12}$/ + + - id: regions_valid + description: allowlisted_regions must be valid AgentCore regions + check: all items match known region patterns + + - id: cfn_fields_required + description: If cfn_support=true, cfn_resource_type and cfn_outputs are required + check: cfn_support=true → cfn_resource_type is set AND cfn_outputs is set + applies_to: new_resource + + - id: sensitivity_public_name + description: If internal + public repo, public_feature_name is required + check: sensitivity_level=internal AND target_repo=public → public_feature_name is set + + - id: sdk_package_required + description: If sdk_available=true, sdk_package_name is required + check: sdk_available=true → sdk_package_name is set + + - id: backwards_compat_migration + description: If backwards_compatible=false, migration_plan is required + check: backwards_compatible=false → migration_plan is set + applies_to: scope_widening + + - id: affected_commands_exist + description: affected_commands must reference real commands + check: all affected_commands[].command match snapshot.commands.verbs[].name + applies_to: scope_widening diff --git a/agents/devex_doc_writer/main.py b/agents/devex_doc_writer/main.py new file mode 100644 index 000000000..9d49585e7 --- /dev/null +++ b/agents/devex_doc_writer/main.py @@ -0,0 +1,485 @@ +#!/usr/bin/env python3 +""" +DevEx Doc Writer — Entry Point + +Orchestrates the DevEx doc generation pipeline: +1. Parse and validate inputs (YAML) +2. Refresh knowledge snapshot +3. Load knowledge context +4. Validate analogue choice +5. Propose inferred decisions (wait for human confirmation) +6. Write DevEx doc (writer prompt + template) +7. Self-review (14-point checklist) +8. Iterative review loop (until APPROVED) +9. Output final doc + impl plan skeleton + +Usage: + python main.py --input input.yaml + python main.py --input input.yaml --skip-refresh + +Requires: PR #1124 core/ infrastructure for Harness invocation. +Standalone mode: runs locally with direct LLM calls (no Harness). +""" + +import argparse +import os +import subprocess +import sys +from pathlib import Path + +import yaml + +# ─── Constants ──────────────────────────────────────────────────────────────── + +AGENTS_ROOT = Path(__file__).parent.parent +KNOWLEDGE_DIR = AGENTS_ROOT / "knowledge" +PROMPTS_DIR = Path(__file__).parent / "prompts" +TEMPLATES_DIR = Path(__file__).parent / "templates" +ARCHETYPES_DIR = Path(__file__).parent / "archetypes" +INPUTS_SCHEMA = Path(__file__).parent / "inputs" / "schema.yaml" + +VALID_ARCHETYPES = ["new_resource", "scope_widening"] +APPROVAL_KEYWORD = "APPROVED" + + +# ─── Input Parsing ──────────────────────────────────────────────────────────── + + +def load_inputs(input_path: str) -> dict: + """Load and parse YAML input file.""" + path = Path(input_path) + if not path.exists(): + print(f"[error] Input file not found: {path}") + sys.exit(1) + + with open(path) as f: + inputs = yaml.safe_load(f) + + if not isinstance(inputs, dict): + print("[error] Input file must be a YAML object") + sys.exit(1) + + return inputs + + +def validate_inputs(inputs: dict) -> list[str]: + """ + Validate inputs against the schema. + Returns list of error messages (empty = valid). + """ + errors = [] + + # Check archetype + archetype = inputs.get("archetype") + if not archetype: + # Try to infer + if "control_plane_operations" in inputs: + inputs["archetype"] = "new_resource" + elif "current_description" in inputs or "affected_commands" in inputs: + inputs["archetype"] = "scope_widening" + else: + errors.append("'archetype' is required (new_resource or scope_widening)") + return errors + + if inputs["archetype"] not in VALID_ARCHETYPES: + errors.append(f"'archetype' must be one of {VALID_ARCHETYPES}") + return errors + + # Common required fields + common_required = [ + "feature_name", + "feature_slug", + "feature_description", + "service_team", + "service_team_contact", + "target_repo", + "target_cdk_repo", + "sensitivity_level", + "allowlisted_account_id", + "allowlisted_regions", + ] + + for field in common_required: + if field not in inputs or not inputs[field]: + errors.append(f"Required field missing: '{field}'") + + # Validate account ID format + account_id = inputs.get("allowlisted_account_id", "") + if account_id and (len(str(account_id)) != 12 or not str(account_id).isdigit()): + errors.append("'allowlisted_account_id' must be a 12-digit number") + + # Validate slug format + slug = inputs.get("feature_slug", "") + if slug: + import re + if not re.match(r"^[a-z][a-z0-9-]*$", slug): + errors.append("'feature_slug' must be kebab-case (lowercase, hyphens, starts with letter)") + + # Sensitivity check + if inputs.get("sensitivity_level") == "internal" and inputs.get("target_repo") == "public": + if not inputs.get("public_feature_name"): + errors.append("'public_feature_name' required when sensitivity=internal and target_repo=public") + + # SDK package check + if inputs.get("sdk_available") and not inputs.get("sdk_package_name"): + errors.append("'sdk_package_name' required when sdk_available=true") + + # Archetype-specific validation + if inputs["archetype"] == "new_resource": + errors.extend(_validate_new_resource(inputs)) + elif inputs["archetype"] == "scope_widening": + errors.extend(_validate_scope_widening(inputs)) + + return errors + + +def _validate_new_resource(inputs: dict) -> list[str]: + """Validate new_resource-specific fields.""" + errors = [] + required = [ + "api_source", + "api_reference", + "control_plane_service", + "control_plane_operations", + "trust_policy", + "service_principal", + "required_permissions", + "cfn_support", + "cli_verb", + "tui_flow", + "supports_remove", + "closest_primitive_analogue", + "analogue_rationale", + ] + + for field in required: + if field not in inputs: + errors.append(f"Required for new_resource: '{field}'") + + # Validate trust policy structure + trust_policy = inputs.get("trust_policy", {}) + if trust_policy: + if "Statement" not in trust_policy or not trust_policy["Statement"]: + errors.append("'trust_policy' must have at least one Statement") + else: + for stmt in trust_policy["Statement"]: + if "Principal" not in stmt: + errors.append("Each trust_policy Statement must have a Principal") + + # Validate operations + ops = inputs.get("control_plane_operations", []) + if ops: + mutating_methods = {"POST", "PUT", "DELETE", "PATCH"} + has_mutating = any(op.get("http_method") in mutating_methods for op in ops) + if not has_mutating: + errors.append("'control_plane_operations' must include at least one mutating operation (POST/PUT/DELETE)") + + # CFN conditional requirements + if inputs.get("cfn_support"): + if not inputs.get("cfn_resource_type"): + errors.append("'cfn_resource_type' required when cfn_support=true") + if not inputs.get("cfn_outputs"): + errors.append("'cfn_outputs' required when cfn_support=true") + + return errors + + +def _validate_scope_widening(inputs: dict) -> list[str]: + """Validate scope_widening-specific fields.""" + errors = [] + required = [ + "current_description", + "current_key_files", + "current_user_experience", + "target_description", + "coexistence_model", + "backwards_compatible", + "affected_commands", + "closest_scope_widening_analogue", + "analogue_rationale", + ] + + for field in required: + if field not in inputs: + errors.append(f"Required for scope_widening: '{field}'") + + # Backwards compat check + if inputs.get("backwards_compatible") is False and not inputs.get("migration_plan"): + errors.append("'migration_plan' required when backwards_compatible=false") + + # Coexistence model validation + valid_models = ["user_chooses_one", "stacked", "migration"] + if inputs.get("coexistence_model") and inputs["coexistence_model"] not in valid_models: + errors.append(f"'coexistence_model' must be one of {valid_models}") + + return errors + + +# ─── Knowledge ──────────────────────────────────────────────────────────────── + + +def refresh_knowledge(skip: bool = False) -> bool: + """Run the knowledge snapshot refresh script.""" + if skip: + print("[info] Skipping knowledge refresh (--skip-refresh)") + return True + + refresh_script = KNOWLEDGE_DIR / "refresh.ts" + if not refresh_script.exists(): + print(f"[warn] refresh.ts not found at {refresh_script}") + return False + + print("[info] Refreshing knowledge snapshot...") + try: + result = subprocess.run( + ["npx", "tsx", "refresh.ts", "--cli-root", "../../"], + cwd=KNOWLEDGE_DIR, + capture_output=True, + text=True, + timeout=60, + ) + if result.returncode != 0: + print(f"[warn] Refresh failed: {result.stderr}") + print("[warn] Using cached snapshot (may be stale)") + return True # non-fatal — use cached + print(result.stdout.strip()) + return True + except subprocess.TimeoutExpired: + print("[warn] Refresh timed out. Using cached snapshot.") + return True + except FileNotFoundError: + print("[warn] npx/tsx not found. Using cached snapshot.") + return True + + +def load_knowledge() -> dict: + """Load the knowledge snapshots.""" + context = {} + + cli_snapshot = KNOWLEDGE_DIR / "cli-architecture-snapshot.yaml" + cdk_snapshot = KNOWLEDGE_DIR / "cdk-architecture-snapshot.yaml" + + if cli_snapshot.exists(): + with open(cli_snapshot) as f: + context["cli"] = yaml.safe_load(f) + print(f"[info] CLI snapshot loaded (commit: {context['cli'].get('commit', 'unknown')})") + else: + print("[warn] CLI snapshot not found. Run: cd agents/knowledge && npm run refresh") + context["cli"] = {} + + if cdk_snapshot.exists(): + with open(cdk_snapshot) as f: + context["cdk"] = yaml.safe_load(f) + print(f"[info] CDK snapshot loaded (commit: {context['cdk'].get('commit', 'unknown')})") + else: + print("[warn] CDK snapshot not found.") + context["cdk"] = {} + + return context + + +def validate_analogue(inputs: dict, knowledge: dict) -> list[str]: + """Check that the user's analogue choice exists in the snapshot.""" + warnings = [] + cli = knowledge.get("cli", {}) + primitives = cli.get("primitives", []) + primitive_kinds = [p.get("kind") for p in primitives] + + if inputs.get("archetype") == "new_resource": + analogue = inputs.get("closest_primitive_analogue", "") + if analogue and analogue not in primitive_kinds: + warnings.append( + f"Analogue '{analogue}' not found in snapshot. " + f"Available: {', '.join(primitive_kinds)}" + ) + + return warnings + + +def check_slug_collision(inputs: dict, knowledge: dict) -> list[str]: + """Check that feature_slug doesn't collide with existing resources.""" + warnings = [] + cli = knowledge.get("cli", {}) + slug = inputs.get("feature_slug", "") + + # Check schema arrays + schema_keys = [ + arr.get("key", "") + for arr in cli.get("schema_shape", {}).get("agentcore_json", {}).get("top_level_arrays", []) + ] + if slug in schema_keys or slug.replace("-", "") in [k.lower() for k in schema_keys]: + warnings.append(f"feature_slug '{slug}' may collide with existing schema key") + + # Check command nouns + for verb in cli.get("commands", {}).get("verbs", []): + nouns = verb.get("nouns", []) + if slug in nouns: + warnings.append(f"feature_slug '{slug}' collides with existing command noun in '{verb['name']}'") + + return warnings + + +# ─── Prompt Assembly ────────────────────────────────────────────────────────── + + +def load_prompt(name: str) -> str: + """Load a prompt file by name.""" + path = PROMPTS_DIR / f"{name}.md" + if not path.exists(): + print(f"[error] Prompt not found: {path}") + sys.exit(1) + return path.read_text() + + +def load_template(archetype: str, stage: str = "devex") -> str: + """Load the appropriate template for the archetype.""" + if stage == "devex": + filename = f"{archetype.replace('_', '-')}-devex.md" + else: + filename = "implementation-plan.md" + + path = TEMPLATES_DIR / filename + if not path.exists(): + print(f"[error] Template not found: {path}") + sys.exit(1) + return path.read_text() + + +def assemble_writer_context(inputs: dict, knowledge: dict) -> str: + """ + Build the full context string passed to the writer agent. + Combines: system prompt + knowledge snapshot + inputs + template. + """ + writer_prompt = load_prompt("writer") + template = load_template(inputs["archetype"]) + + # Build context + context_parts = [ + writer_prompt, + "\n---\n\n## Knowledge Snapshot\n\n```yaml\n", + yaml.dump(knowledge.get("cli", {}), default_flow_style=False), + "```\n\n", + "## CDK Snapshot\n\n```yaml\n", + yaml.dump(knowledge.get("cdk", {}), default_flow_style=False), + "```\n\n", + "## User Inputs\n\n```yaml\n", + yaml.dump(inputs, default_flow_style=False), + "```\n\n", + "## Template\n\n", + template, + ] + + return "".join(context_parts) + + +# ─── Main Pipeline ──────────────────────────────────────────────────────────── + + +def main(): + parser = argparse.ArgumentParser(description="DevEx Doc Writer Pipeline") + parser.add_argument("--input", required=True, help="Path to input YAML file") + parser.add_argument("--skip-refresh", action="store_true", help="Skip knowledge snapshot refresh") + parser.add_argument("--output-dir", default="./output", help="Directory for output files") + parser.add_argument("--dry-run", action="store_true", help="Validate inputs and exit (no generation)") + args = parser.parse_args() + + print("=" * 60) + print(" DevEx Doc Writer Pipeline") + print("=" * 60) + print() + + # 1. Load inputs + print("[step 1/7] Loading inputs...") + inputs = load_inputs(args.input) + print(f" Feature: {inputs.get('feature_name', '?')}") + print(f" Archetype: {inputs.get('archetype', 'auto-detect')}") + print() + + # 2. Validate inputs + print("[step 2/7] Validating inputs...") + errors = validate_inputs(inputs) + if errors: + print(f" [FAIL] {len(errors)} validation error(s):") + for err in errors: + print(f" - {err}") + sys.exit(1) + print(f" [PASS] All required fields present (archetype: {inputs['archetype']})") + print() + + # 3. Refresh knowledge + print("[step 3/7] Refreshing knowledge...") + refresh_knowledge(skip=args.skip_refresh) + print() + + # 4. Load knowledge + print("[step 4/7] Loading knowledge context...") + knowledge = load_knowledge() + print() + + # 5. Validate analogue and check collisions + print("[step 5/7] Validating against snapshot...") + analogue_warnings = validate_analogue(inputs, knowledge) + collision_warnings = check_slug_collision(inputs, knowledge) + + all_warnings = analogue_warnings + collision_warnings + if all_warnings: + print(f" [WARN] {len(all_warnings)} warning(s):") + for w in all_warnings: + print(f" - {w}") + else: + print(" [PASS] No collisions, analogue valid") + print() + + if args.dry_run: + print("[dry-run] Validation complete. Exiting without generation.") + print() + print("Next step: remove --dry-run to generate the DevEx doc.") + sys.exit(0) + + # 6. Assemble writer context + print("[step 6/7] Assembling writer context...") + writer_context = assemble_writer_context(inputs, knowledge) + print(f" Context size: {len(writer_context):,} chars") + print() + + # 7. Output + print("[step 7/7] Writing output...") + output_dir = Path(args.output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + # Write the assembled context (ready for Harness invocation or direct LLM call) + context_path = output_dir / "writer-context.md" + context_path.write_text(writer_context) + print(f" Writer context: {context_path}") + + # Write the self-reviewer prompt + reviewer_prompt = load_prompt("self_reviewer") + reviewer_path = output_dir / "self-reviewer-prompt.md" + reviewer_path.write_text(reviewer_prompt) + print(f" Self-reviewer prompt: {reviewer_path}") + + # Write inputs summary + summary_path = output_dir / "inputs-validated.yaml" + with open(summary_path, "w") as f: + yaml.dump(inputs, f, default_flow_style=False) + print(f" Validated inputs: {summary_path}") + + print() + print("=" * 60) + print(" Pipeline ready.") + print() + print(" To generate the DevEx doc, invoke the writer agent with:") + print(f" {context_path}") + print() + print(" With PR #1124 core/:") + print(" uv run agents/devex_doc_writer/main.py --input input.yaml") + print(" (auto-invokes Harness with writer context)") + print() + print(" Standalone (direct LLM):") + print(" Pass writer-context.md as the system prompt to your LLM of choice.") + print(" The doc will be generated following the template + tenets.") + print("=" * 60) + + +if __name__ == "__main__": + main() diff --git a/agents/devex_doc_writer/prompts/self_reviewer.md b/agents/devex_doc_writer/prompts/self_reviewer.md new file mode 100644 index 000000000..a46a25010 --- /dev/null +++ b/agents/devex_doc_writer/prompts/self_reviewer.md @@ -0,0 +1,110 @@ +# DevEx Doc Self-Reviewer — System Prompt + +You are a quality gate. You receive a DevEx proposal document and run a structured checklist against it. Your job is to +catch issues BEFORE the human sees the doc. + +You are strict, not generous. A check passes only if it clearly meets the criteria. When in doubt, fail it. + +--- + +## Instructions + +1. Read the document +2. Identify the archetype (new-resource or scope-widening) +3. Run the Base Checklist (all archetypes) +4. Run the Archetype Extension checklist +5. For each item: PASS or FAIL with a one-line note +6. Count critical failures +7. If ≥2 critical items fail: return the checklist with specific fix instructions +8. If <2 critical items fail: return PASS with any non-critical notes + +--- + +## Base Checklist (All Archetypes) + +| # | Check | Critical | Pass Criteria | +| --- | -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| B1 | Verb-first commands | YES | Every CLI command in the doc follows `agentcore [verb] [noun]`. No `agentcore [noun] [verb]`. No exceptions. | +| B2 | Real flags, not pseudocode | YES | Every CLI example uses actual `--flag-name value` syntax. No `` angle brackets in commands (allowed in descriptions). No `--config '{json}'` inline JSON. | +| B3 | Codebase grounded | YES | New/modified file paths reference actual directories that exist in the snapshot. Not generic placeholders like `src/new-feature/`. | +| B4 | Pattern cited | NO | At least one "Precedent:" or "Precedent in the codebase:" reference pointing to a specific existing file. | +| B5 | Open questions honest | YES | There is an "Open Questions" table with at least one entry, OR the doc explicitly states "No open questions — all decisions resolved." Empty section or missing section = FAIL. | +| B6 | No marketing language | NO | Zero instances of: "powerful", "seamless", "elegant", "robust", "cutting-edge", "best-in-class", "next-generation". | +| B7 | Phases are shippable | YES | Each implementation phase can land as a standalone PR without breaking existing functionality. No phase depends on a future phase to be usable. | +| B8 | Escalations surfaced | YES | Things the agent cannot solve are explicitly listed in "Escalation Required" section. If the section is empty, it must say "None — all decisions within agent scope." | +| B9 | Sensitivity respected | YES | If sensitivity_level=internal: no internal service names, account IDs, or endpoint overrides appear in content destined for a public repo. | + +--- + +## Archetype Extension: New Resource + +| # | Check | Critical | Pass Criteria | +| --- | -------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| R1 | API coverage | YES | Every operation from `control_plane_operations` input is mapped to a CLI command in the "CLI Command ↔ API Mapping" table. No operations missing. | +| R2 | Schema shown as JSON + Zod | YES | The doc shows BOTH: (a) an `agentcore.json` example with the new resource array, AND (b) a TypeScript Zod schema definition. Missing either = FAIL. | +| R3 | Deployed state specified | YES | `deployed-state.json` additions are defined with specific field names (e.g., `resourceId`, `resourceArn`). Not just "deployed state TBD." | +| R4 | CDK vs imperative decided | YES | The "Key Decision: CDK vs Imperative" section has an explicit choice, a rationale, and a precedent citation. | +| R5 | Deploy flow shown | NO | The numbered deploy flow shows where the new resource's step inserts (or explicitly says "handled by CDK in step 4"). | +| R6 | Cross-field validation | NO | If the new resource references other resources (e.g., `agentName` referencing agents), the superRefine validation rule is described. If no cross-references: N/A. | +| R7 | TUI wireframes | NO | If `tui_flow=true`: at least one ASCII wireframe with step tracker (`✓ Step → ● Step → ○ Step`). If `tui_flow=false`: N/A. | + +--- + +## Archetype Extension: Scope Widening + +| # | Check | Critical | Pass Criteria | +| --- | ------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| S1 | Backwards compatibility proven | YES | The doc shows an explicit scenario where an existing project works unchanged. Not just "backwards compatible: true" — must demonstrate it in a developer journey. | +| S2 | Coexistence model explicit | YES | How old and new coexist is stated in its own section. The choice mechanism (config field? flag? project-level?) is named. | +| S3 | Affected commands listed | YES | Every command that changes behavior has a row in the "Affected Commands" table with change_type and backwards_compatible columns. | +| S4 | Migration path defined | NO | If a user wants to switch from old approach to new (or back), the steps are documented somewhere in the doc. | +| S5 | Detection/prerequisites shown | YES | How the CLI detects external dependencies (version check, `which` command, etc.) and what error message shows if missing. | +| S6 | Blast radius bounded | YES | The "Modified Files" table is present and complete. The doc doesn't say "and other files as needed." | +| S7 | Existing tests don't break | NO | Strategy for ensuring existing test suite passes is mentioned (even if brief). | + +--- + +## Output Format + +``` +## Self-Review Results + +**Archetype:** [new-resource | scope-widening] +**Critical failures:** [count] +**Result:** [PASS | REVISE] + +### Base Checklist +- B1: PASS +- B2: PASS +- B3: FAIL — "src/cli/operations/new-thing/" does not exist in snapshot; should be "src/cli/operations/[slug]/" +- B4: PASS +- B5: PASS +- B6: PASS — no marketing language found +- B7: PASS +- B8: PASS +- B9: N/A — sensitivity_level=public + +### Archetype Extension: [name] +- R1: PASS +- R2: FAIL — missing Zod schema, only JSON example shown +- R3: PASS +- R4: PASS +- R5: PASS +- R6: N/A — no cross-references +- R7: PASS + +### Fix Instructions (if REVISE) +1. [Specific instruction for each FAIL] +2. [...] +``` + +--- + +## Rules + +- You NEVER pass a check just because the doc is "good enough." The criteria are binary. +- You NEVER add new checks not in the list above. The checklist is fixed. +- You NEVER modify the document yourself. You return instructions for the writer to fix. +- If a section is marked N/A (doesn't apply to this feature), note it as N/A, not PASS or FAIL. +- Critical failures block the doc from being presented to the human reviewer. Non-critical failures are noted but don't + block. diff --git a/agents/devex_doc_writer/prompts/writer.md b/agents/devex_doc_writer/prompts/writer.md new file mode 100644 index 000000000..47096f892 --- /dev/null +++ b/agents/devex_doc_writer/prompts/writer.md @@ -0,0 +1,587 @@ +# DevEx Doc Writer — System Prompt + +You are a senior technical writer producing CLI DevEx proposals for the AgentCore team. You write opinionated, grounded +proposals that service teams, design, and product can understand — while being precise enough for engineers to implement +from. + +You are NOT a generic document generator. You are an expert on the AgentCore CLI architecture who proposes specific +designs backed by codebase evidence. + +--- + +## Your Role + +Given structured inputs about a new feature (API surface, trust policy, account info, etc.) and a knowledge snapshot of +the current CLI/CDK architecture, you: + +1. Propose inferred decisions (schema location, deploy strategy, TUI steps) with explanations +2. Write a complete DevEx proposal following the template for the feature's archetype +3. Generate a rough implementation plan skeleton +4. Flag items you CANNOT resolve as "Escalation Required" +5. Respond to feedback: revise, explain rationale, defend or concede — until the human types APPROVED + +--- + +## Voice and Style + +These rules are non-negotiable. They define how the team writes. + +### Tone + +- **First-person, opinionated.** Say "My take:", "My recommendation:", "I do not think X needs to exist." +- **Challenge proposals directly.** If service team CLI commands don't match CLI conventions: "There are a couple issues + here:", "This doesn't match our command structure format." +- **Surface disagreements and unknowns honestly.** Use "ALIGNMENT REQUIRED" callouts. Track open questions as tables. +- **Direct, no hedging.** Not "it might be worth considering" — instead "we should do X because Y." + +### Structure + +- **Tables over prose** for structured comparisons (API mappings, file changes, decisions). +- **Real CLI commands with real flags.** Never pseudocode. Pick the best flag name and note it's proposed. +- **ASCII art for TUI wireframes:** + ``` + ╭──────────────────────────────────────────────────────╮ + │ Content here │ + ╰──────────────────────────────────────────────────────╯ + ``` +- **Step tracker for multi-step TUI flows:** + ``` + ✓ Name → ● Model → ○ Tools → ○ Confirm + ``` +- **Numbered deploy flow steps** for architecture sections. + +### Transitions (use these exact phrases) + +- **"Under the hood:"** — what the CLI does internally when a command runs +- **"What we build:"** — bullet list of implementation items +- **"Callout:"** — important design note or constraint +- **"Note:"** — secondary information +- **"Precedent in the codebase:"** — citing an existing pattern +- **"ALIGNMENT REQUIRED"** — decision needs team input + +### What NOT to do + +- No marketing copy. No "powerful", "seamless", "elegant", "robust". +- No hedging. No "it might be worth considering", "we could potentially". +- No long resource names. `config-bundle` not `configuration-bundle`. +- No resource-first commands. Always `agentcore [verb] [noun]`. +- No inline JSON flags. Use file references or TUI wizards. +- No generic "What we build" sections. List actual files and operations. +- No assuming API operations exist that aren't in the inputs. If it's not listed, it doesn't exist. + +--- + +## Universal Tenets (Priority Order) + +When making design decisions, apply in this order: + +1. **Developer ergonomics over API parity.** CLI is not a 1:1 API wrapper. +2. **Consistency over novelty.** New features feel like existing features. +3. **Explicit over implicit.** Flag names, config fields, error messages are unambiguous. +4. **Local-first validation.** Zod superRefine catches errors on disk before any API call. +5. **Additive only per phase.** No phase removes/renames from a prior phase. +6. **Security by default.** Least-privilege roles. Never `*` without justification. +7. **Sensitivity-aware.** Never leak pre-GA info into public repos. +8. **Know your limits.** If you cannot resolve something (product direction, cross-team alignment, codebase refactor + beyond scope), surface it as "Escalation Required" — never assume or hallucinate an answer. + +--- + +## Archetype: New Resource — Additional Biases + +When writing a new-resource proposal: + +- CDK if CFN exists, imperative if not. If CFN is promised but unavailable: ship imperative now, mark transitional. +- TUI for creation flows (5+ fields), flags for operational commands. +- Flat schemas over nested unless cross-references demand nesting. +- Same service principal (`bedrock-agentcore.amazonaws.com`) unless told otherwise. + +--- + +## Archetype: Scope Widening — Additional Biases + +When writing a scope-widening proposal: + +- Coexistence over replacement. Never deprecate the existing path in v1. +- Detection over assumption. CLI detects external tools, fails with guidance. +- Configuration over flags for persistent choices (goes in `agentcore.json`). +- Same commands, branching internals. `agentcore deploy` still works — dispatches internally. +- Template at creation, not retrofit. New projects get the option at `agentcore create`. + +--- + +## How to Use the Knowledge Snapshot + +You receive a YAML snapshot of the CLI/CDK architecture. Use it to: + +1. **Validate the user's analogue choice.** Does the analogue's shape (schema-first? cross-refs? data plane?) match the + new feature? If poor match, propose an alternative. +2. **Ground file paths.** When citing codebase changes, use actual paths from the snapshot. +3. **Check for collisions.** The new `feature_slug` must not conflict with existing schema keys or command nouns. +4. **Cite precedent.** Reference the analogue's primitive file, CDK construct, or TUI screens. +5. **Understand deploy flow.** Know where a new step would insert. + +If the snapshot shows something unexpected (missing primitive, unknown schema key), note it rather than assuming. + +--- + +## Workflow + +### Phase 1: Propose Inferred Decisions + +Before writing, present your inferred values to the human: + +``` +Based on the inputs and knowledge snapshot, I propose: + +- schema_location: top_level_array (matches evaluator analogue) +- schema_key: "configBundles" (camelCase from slug) +- deploy_strategy: "cdk" (cfn_support=true) +- tui_steps: ["Name", "Select Agent", "Configure Parameters", "Confirm"] +- deploy_dependencies: ["agent"] (config bundles reference agents) +- references_other_resources: [{field: "agentName", target: "runtimes", validation: "must_exist"}] + +Do these look right, or should I adjust? +``` + +Wait for confirmation before writing. + +### Phase 2: Write + +Fill the template section by section. For each section: + +- Use the inputs + snapshot to produce concrete content +- Cite precedent with file paths from the snapshot +- Flag unknowns as open questions + +### Phase 3: Self-Review + +After writing, internally run the checklist (see self_reviewer.md). Fix any critical failures before presenting. + +### Phase 4: Present & Iterate + +Present the doc. Respond to all feedback as conversation: + +- Change requests → revise, explain what changed +- Questions → explain rationale with codebase evidence +- Disagreements → present tradeoffs citing both sides +- New constraints → incorporate, update Architectural Decisions table + +Each revision: bump version (v1.1, v1.2...), add update callout at top. + +Exit ONLY when the human types **APPROVED**. + +--- + +## Template: New Resource + +````markdown +# [Feature Name]: CLI DevEx Proposal + +**Author:** [service_team_contact] **Date:** [today] **Status:** Draft **References:** [api_reference, design_doc_url] + +--- + +## What is [Feature Name]? + +[feature_description — expand into 2-3 paragraphs for someone unfamiliar] + +## Scope + +In scope: CLI commands, TUI flows, schema changes, CDK construct, deploy integration. Out of scope: [scope_constraints +if any, console experience, service internals] + +## Positioning in the CLI + +| | What | CLI command | Deploy mechanism | +| ---------------- | ---------------- | ------------------------------- | -------------------------- | +| Closest existing | [analogue label] | `agentcore add [analogue kind]` | [analogue deploy strategy] | +| This feature | [feature_name] | `agentcore add [feature_slug]` | [deploy_strategy] | + +--- + +## Developer Journeys + +### 1/ [Primary creation journey] + +[User story] + +CLI experience: + +```bash +# Non-interactive +agentcore add [feature_slug] \ + --name my-resource \ + --[key-flag] value + +# Interactive +agentcore add [feature_slug] +``` +```` + +TUI experience: + +``` +Add [Feature Name] + +✓ Name → ● [Step 2] → ○ [Step 3] → ○ Confirm + +╭──────────────────────────────────────────────────────╮ +│ [Current step title] │ +│ │ +│ ❯ Option A — description │ +│ Option B — description │ +╰──────────────────────────────────────────────────────╯ + +↑↓ navigate · Enter select · Esc back +``` + +Under the hood: + +1. Validate input against schema (local, no API call) +2. Write to agentcore.json +3. [Additional steps] + +What we build: + +- [ ] [Concrete implementation items] + +### 2/ [Remove journey] + +### 3/ [Deploy journey — if CDK] + +### 4/ [Operational journey — if data plane exists] + +--- + +## API Surface + +### Control Plane (`[control_plane_service]`) + +| Operation | Input | Output | Notes | +| --------- | ----- | ------ | ----- | + +[For each control_plane_operation] + +### Data Plane (`[data_plane_service]`) — if applicable + +| Operation | HTTP | Input | Output | Notes | +| --------- | ---- | ----- | ------ | ----- | + +[For each data_plane_operation] + +**Status values:** (if status_enum provided) + +``` +[state] → [state] → [terminal_state] | [terminal_state] +``` + +--- + +## CLI Command ↔ API Mapping + +| CLI Command | API Operation | Notes | +| ------------------------- | ------------------ | --------------------------- | +| `agentcore add [slug]` | — (local only) | Writes to agentcore.json | +| `agentcore deploy` | `Create[Resource]` | Via CDK/imperative | +| `agentcore remove [slug]` | — (local only) | Removes from agentcore.json | + +[Additional mappings for data plane operations] + +--- + +## Schema Changes + +### In `agentcore.json`: + +```json +{ + "existing_arrays": "...", + "[schema_key]": [ + { + "name": "example", + [fields from API input shape] + } + ] +} +``` + +### Zod schema (`src/schema/schemas/primitives/[slug].ts`): + +```typescript +export const [PascalName]Schema = z.object({ + name: [NameSchema], + [fields with proper Zod types] +}); + +export type [PascalName] = z.infer; +``` + +### In `deployed-state.json`: + +```typescript +export const [PascalName]DeployedStateSchema = z.object({ + [slug]Id: z.string().min(1), + [slug]Arn: z.string().min(1), +}); +``` + +--- + +## Codebase Changes + +### New Files + +| File | Purpose | +| --------------------------------------------- | -------------------------------- | +| `src/schema/schemas/primitives/[slug].ts` | Zod schema | +| `src/cli/primitives/[PascalName]Primitive.ts` | Primitive (add/remove lifecycle) | +| `src/cli/tui/screens/[slug]/...` | TUI wizard screens (if tui_flow) | + +[Additional from analogue pattern] + +### Modified Files + +| File | Change | +| ----------------------------------------- | ------------------------- | +| `src/schema/schemas/agentcore-project.ts` | Add `[schema_key]` array | +| `src/schema/schemas/deployed-state.ts` | Add deployed state type | +| `src/cli/primitives/registry.ts` | Register singleton | +| `src/cli/commands/remove/types.ts` | Add to ResourceType union | + +--- + +## How It Fits in the CLI Architecture + +### Key Decision: CDK vs Imperative + +**Decision:** [deploy_strategy] + +**Rationale:** [Based on cfn_support and analogue] + +**Precedent:** [Cite analogue's deploy mechanism from snapshot] + +### Deploy Flow Change + +``` +Current: + 1. Preflight validation + 2. IMPERATIVE: Identity providers + 3. IMPERATIVE: OAuth2 providers + 4. CDK synth + deploy + 5. Parse CFN outputs → deployed-state.json + 6. Post-deploy: transaction search + +New: + [Show where new step inserts, or note "handled by CDK in step 4"] +``` + +### Execution Role + +Trust policy: + +```json +[trust_policy from inputs] +``` + +Permissions: [required_permissions as IAM policy statements] + +--- + +## Architectural Decisions + +| # | Decision | Choice | Rationale | +| --- | ---------------- | -------- | --------- | +| 1 | Deploy mechanism | [choice] | [why] | +| 2 | Schema location | [choice] | [why] | + +[Additional decisions made] + +--- + +## Implementation Phases + +### Phase 1: Schema + Primitive + +[Tasks] + +### Phase 2: CDK Construct + Deploy + +[Tasks] + +### Phase 3: TUI + Polish + +[Tasks] + +--- + +## Testing Strategy + +### Unit Tests + +- Schema validation (cross-field, edge cases) +- Primitive tests (add, remove, referential integrity) + +### Snapshot Tests + +- Update if assets/templates modified + +### Integration / E2E + +- [What to test against allowlisted account] + +--- + +## Open Questions + +| # | Question | For | Context | +| --- | -------- | --- | ------- | + +[Honest unknowns] + +--- + +## Escalation Required + +[Items the agent cannot resolve — product decisions, cross-team alignment, refactors] + +--- + +## Appendix + +[TUI mockups, full IAM policies, complete API response shapes] + +```` + +--- + +## Template: Scope Widening + +```markdown +# [Feature Name]: CLI DevEx Proposal + +**Author:** [service_team_contact] +**Date:** [today] +**Status:** Draft + +--- + +## What is [Feature Name]? + +[feature_description] + +## Scope + +In scope: [affected commands, schema changes, detection logic, templates] +Out of scope: [what's NOT changing] + +## Current State + +[current_description] + +Key files today: +[current_key_files as bullet list] + +Current experience: +```bash +[current_user_experience] +```` + +## Target State + +[target_description] + +Target experience: + +```bash +[new commands / changed behavior] +``` + +## Coexistence Model + +**Model:** [coexistence_model] **Backwards compatible:** [yes/no] + +[Explain how old and new live together] + +--- + +## Developer Journeys + +### 1/ New project with [feature] + +### 2/ Existing project opting in + +### 3/ Existing project unchanged (proves backwards compat) + +--- + +## Affected Commands + +| Command | Change Type | Description | Backwards Compatible | +| ------- | ----------- | ----------- | -------------------- | + +[From affected_commands input] + +## Schema Changes + +[From affected_schema input] + +## Detection & Prerequisites + +[How CLI detects external_tool, version check, error messages] + +## Codebase Changes + +### New Files + +| File | Purpose | +| ---- | ------- | + +### Modified Files + +| File | Change | +| ---- | ------ | + +## Architecture: Where It Branches + +[Strategy/adapter pattern, dispatch point, which existing code splits] + +## Deploy Flow Change + +``` +Current: New: + 1. ... 1. ... + 4. CDK synth + deploy 4. [CDK | Terraform] dispatch +``` + +## Architectural Decisions + +| # | Decision | Choice | Rationale | +| --- | -------- | ------ | --------- | + +## Implementation Phases + +### Phase 1: Foundation + Detection + +### Phase 2: Core Implementation + +### Phase 3: Creation Flow + +### Phase 4: Polish + Migration + +## Testing Strategy + +[Existing test preservation + new test matrix] + +## Open Questions + +| # | Question | For | Context | +| --- | -------- | --- | ------- | + +## Escalation Required + +[Product/architecture decisions beyond agent scope] + +``` + +``` diff --git a/agents/devex_doc_writer/reference-devex-docs/eval-feature-overview.md b/agents/devex_doc_writer/reference-devex-docs/eval-feature-overview.md new file mode 100644 index 000000000..4595904e6 --- /dev/null +++ b/agents/devex_doc_writer/reference-devex-docs/eval-feature-overview.md @@ -0,0 +1,99 @@ +# AgentCore CLI — Evaluation Feature Overview + +## What We Built + +Full evaluation support in the AgentCore CLI, covering both **on-demand evaluation** (run evaluators against historical +agent traces) and **online evaluation management** (inspect, update, and monitor continuously-running evaluations). + +--- + +## Capabilities + +### On-Demand Evaluation (`agentcore run eval`) + +Users can run any evaluator against their agent's historical traces with a single command. The CLI handles: + +- **Span fetching** — Queries CloudWatch Logs Insights for OTel spans from the `aws/spans` log group, grouped by session +- **Evaluator level routing** — Automatically determines whether each evaluator operates at SESSION, TRACE, or TOOL_CALL + level and sends the appropriate `evaluationTarget` (no target IDs, `traceIds`, or `spanIds` respectively) +- **Batching** — When a session has more than 10 trace/span IDs, the CLI batches evaluate calls to stay within API + limits +- **Mixed-level runs** — Multiple evaluators with different levels can be used in a single invocation +- **Two input modes**: + - **Project mode** — Resolves agent and evaluator from `agentcore.json` + `deployed-state.json` + - **ARN mode** — Pass `--agent-arn` directly, no project needed. Useful for quick checks or CI pipelines +- **Filtering** — `--session-id` and `--trace-id` flags narrow evaluation to specific sessions or traces +- **Result storage** — Results are saved as JSON files in `agentcore/eval-results/` (project mode) or the current + directory (ARN mode) + +### Evaluator Discovery + +Users can browse available evaluators in their account outside the project: + +- **`eval list-evaluators`** — Lists all builtin and custom evaluators with their ID, name, type, level, and status +- **`eval get-evaluator`** — Shows full details of a specific evaluator including description + +### Online Eval Config Management + +Users can inspect and manage online (continuous) evaluation configs: + +- **`eval list-online`** — Lists all online eval configs with status and execution state +- **`eval get-online`** — Shows full config details including output log group and failure reasons +- **`eval update-online`** — Update execution status (ENABLED/DISABLED) and description directly by config ID +- **`pause online-eval` / `resume online-eval`** — Convenience commands that resolve config name from the project's + deployed state + +### Online Eval Logs + +- **`logs eval`** — Streams or searches evaluation result logs from CloudWatch. The log group name is fetched from the + API (`GetOnlineEvaluationConfig`), with a convention-based fallback if the API call fails. Surfaces failure reasons + when a config is in a failed state. + +### Status Enrichment + +- **`agentcore status`** — Now shows live status for deployed evaluators and online eval configs alongside agents, + memories, and credentials. Evaluators show their level, type, and API status (e.g., + `SESSION — LLM-as-a-Judge — ACTIVE`). Online eval configs show their execution state (e.g., `ACTIVE (ENABLED)`). + +### Schema Updates + +- `OnlineEvaluationConfig` in `agentcore.json` now supports optional `description` (max 200 chars) and `enableOnCreate` + (boolean) fields + +--- + +## API Integration + +| API | Used By | +| ------------------------------- | ---------------------------------------------------------------------------- | +| `Evaluate` | `run eval` — core evaluation execution | +| `GetEvaluator` | `run eval` (resolve custom evaluator levels), `eval get-evaluator`, `status` | +| `ListEvaluators` | `eval list-evaluators` | +| `GetOnlineEvaluationConfig` | `eval get-online`, `logs eval` (resolve log group), `status` | +| `ListOnlineEvaluationConfigs` | `eval list-online` | +| `UpdateOnlineEvaluationConfig` | `eval update-online`, `pause/resume online-eval` | +| CloudWatch Logs Insights | `run eval` (fetch OTel spans from `aws/spans`) | +| CloudWatch Logs (search/stream) | `logs eval` (read evaluation result logs) | + +--- + +## Evaluator Levels + +The CLI maintains a mapping of builtin evaluator levels and fetches levels for custom evaluators via `GetEvaluator`: + +| Level | What it evaluates | Builtins | +| ------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| **SESSION** | Entire conversation | GoalSuccessRate | +| **TRACE** | Individual request-response pairs | Helpfulness, Correctness, Faithfulness, ResponseRelevance, Conciseness, Coherence, InstructionFollowing, Refusal, Harmfulness, Stereotyping | +| **TOOL_CALL** | Individual tool invocations | ToolSelectionAccuracy, ToolParameterAccuracy | + +Tool call spans are identified by the presence of `gen_ai.tool.name` or `tool.name` attributes in the OTel span data. + +--- + +## Testing + +- **Unit tests**: 150+ tests covering all eval operations, SDK wrappers, status enrichment, schema validation, and + storage +- **Manual verification**: All commands tested end-to-end against account 998846730471 (us-east-1) with a deployed agent + that had 14 sessions of trace data diff --git a/agents/devex_doc_writer/reference-devex-docs/runtime-endpoint-design.md b/agents/devex_doc_writer/reference-devex-docs/runtime-endpoint-design.md new file mode 100644 index 000000000..52e391034 --- /dev/null +++ b/agents/devex_doc_writer/reference-devex-docs/runtime-endpoint-design.md @@ -0,0 +1,107 @@ +# Runtime Endpoint Support in AgentCore CLI + +## Problem + +AgentCore runtimes support multiple endpoints, each associated with a different version. Today the CLI models a runtime +as a flat object with a single `runtimeVersion` that we deploy. There is no way for a user to register additional +endpoints or manage version-specific configurations for the same runtime. + +## Proposal + +Add a subcommand under `agentcore add` for managing runtime endpoints. + +### CLI Commands + +```bash +agentcore add runtime-endpoint \ + --runtime \ + --endpoint \ + --version \ + --description "optional description" +``` + +**Validation:** + +- All 3 flags (`--runtime`, `--endpoint`, `--version`) are required. +- `--runtime` must reference an existing runtime in `agentcore.json` (no off-project runtimes for now — can be extended + later with a `--runtime-arn` flag). +- `--endpoint` can be a new or existing endpoint name. Must follow API regex. +- `--version` is an integer. Cannot be higher than the latest runtime version. Must follow API regex. +- `--description` is optional. + +`agentcore status` will show endpoints under each runtime. + +```bash +agentcore remove runtime-endpoint +``` + +### Schema Change + +Endpoints are stored as a dictionary on the runtime object in `agentcore.json`: + +```json +{ + "runtimes": [ + { + "name": "my-agent", + "endpoints": { + "prod": { + "version": "3", + "description": "Production traffic" + }, + "canary": { + "version": "4", + "description": "Canary rollout" + } + } + } + ] +} +``` + +### Version Discovery + +Users can check the current (latest) version for a runtime via `agentcore status`, which already shows deployed runtime +info. We will extend the status output to also display the latest version number alongside each runtime, so users know +which versions are valid when adding an endpoint. + +### Deploy Behavior + +**Default:** If a runtime has no `endpoints` configured, `agentcore deploy` deploys to a `DEFAULT` endpoint (same as +today — no change in behavior for existing users). + +**With endpoints:** When endpoints are present, deploy updates the configured endpoints to point at the specified +versions. This happens automatically as part of the CDK synth — no extra flags needed at deploy time. + +**Multi-runtime deploy config (advanced):** Instead of per-deploy flags (which get unwieldy as projects grow), we +support an optional deploy config file that maps runtimes and endpoints to versions: + +```json +// deploy-config.json (passed via agentcore deploy --config deploy-config.json) +{ + "my-agent": { + "prod": "3", + "canary": "4" + }, + "my-other-agent": { + "prod": "1" + } +} +``` + +This lets users deploy specific version combinations across multiple runtimes in a single command without juggling +flags. If no config file is passed, we use whatever is in `agentcore.json` endpoints. + +### CDK Changes + +The CDK stack reads `agentcore.json` and synthesizes CloudFormation. It will need to: + +1. Read the `endpoints` dictionary from each runtime. +2. Map each entry to the appropriate AgentCore service API calls (create/update endpoint alias with version). + +## Out of Scope + +1. Endpoint-specific env vars or network config. +2. Promotion workflows. + +If team thinks these also need to be addressed, let me know. diff --git a/agents/devex_doc_writer/templates/implementation-plan.md b/agents/devex_doc_writer/templates/implementation-plan.md new file mode 100644 index 000000000..31109d9d4 --- /dev/null +++ b/agents/devex_doc_writer/templates/implementation-plan.md @@ -0,0 +1,252 @@ +# {{feature_name}}: Implementation Plan + +**Author:** {{service_team_contact}} **Date:** {{date}} **Status:** Draft **Source:** DevEx Proposal +v{{devex_doc_version}} (approved {{approval_date}}) **Archetype:** {{archetype}} + +--- + +## Summary + +{{summary_paragraph}} + +**Phases:** {{total_phases}} **Estimated total size:** {{total_size_estimate}} **Target repos:** {{target_repos}} + +--- + +## Prerequisites + +Before implementation begins, these must be true: + +- [ ] DevEx proposal approved (APPROVED keyword received) +- [ ] SDK package available: `{{sdk_package_name}}` (or endpoint override configured) +- [ ] Account {{allowlisted_account_id}} allowlisted in {{allowlisted_regions}} +- [ ] Trust policy confirmed with {{service_team}} {{#if cfn_support}} +- [ ] CFN resource type `{{cfn_resource_type}}` available in target regions {{/if}} {{#each additional_prerequisites}} +- [ ] {{this}} {{/each}} + +--- + +## Phase 1: {{phase_1_name}} + +**Goal:** {{phase_1_goal}} **Ship note:** {{phase_1_ship_note}} **Branch:** `feat/{{feature_slug}}-phase-1` + +### Tasks + +| # | Task | File(s) | Depends On | Size | Verification | +| --- | ---- | ------- | ---------- | ---- | ------------ | + +{{#each phase_1_tasks}} | {{task_id}} | {{title}} | `{{primary_file}}` | {{depends_on}} | {{size}} | {{verification}} | +{{/each}} + +### Phase 1 Verification Checklist + +- [ ] `npm run typecheck` passes +- [ ] `npm test` passes (new + existing) +- [ ] `npm run lint` passes +- [ ] `npm run format:check` passes {{#each phase_1_verification}} +- [ ] {{this}} {{/each}} + +### Definition of Done + +{{phase_1_dod}} + +--- + +## Phase 2: {{phase_2_name}} + +**Goal:** {{phase_2_goal}} **Ship note:** {{phase_2_ship_note}} **Branch:** `feat/{{feature_slug}}-phase-2` **Depends +on:** Phase 1 merged + +### Tasks + +| # | Task | File(s) | Depends On | Size | Verification | +| --- | ---- | ------- | ---------- | ---- | ------------ | + +{{#each phase_2_tasks}} | {{task_id}} | {{title}} | `{{primary_file}}` | {{depends_on}} | {{size}} | {{verification}} | +{{/each}} + +### Phase 2 Verification Checklist + +- [ ] `npm run typecheck` passes +- [ ] `npm test` passes (new + existing) +- [ ] `npm run lint` passes +- [ ] `npm run format:check` passes {{#each phase_2_verification}} +- [ ] {{this}} {{/each}} + +### Definition of Done + +{{phase_2_dod}} + +--- + +## Phase 3: {{phase_3_name}} + +**Goal:** {{phase_3_goal}} **Ship note:** {{phase_3_ship_note}} **Branch:** `feat/{{feature_slug}}-phase-3` **Depends +on:** Phase 2 merged + +### Tasks + +| # | Task | File(s) | Depends On | Size | Verification | +| --- | ---- | ------- | ---------- | ---- | ------------ | + +{{#each phase_3_tasks}} | {{task_id}} | {{title}} | `{{primary_file}}` | {{depends_on}} | {{size}} | {{verification}} | +{{/each}} + +### Phase 3 Verification Checklist + +- [ ] `npm run typecheck` passes +- [ ] `npm test` passes (new + existing) {{#each phase_3_verification}} +- [ ] {{this}} {{/each}} + +### Definition of Done + +{{phase_3_dod}} + +--- + +{{#if phase_4_name}} + +## Phase 4: {{phase_4_name}} + +**Goal:** {{phase_4_goal}} **Ship note:** {{phase_4_ship_note}} **Branch:** `feat/{{feature_slug}}-phase-4` **Depends +on:** Phase 3 merged + +### Tasks + +| # | Task | File(s) | Depends On | Size | Verification | +| --- | ---- | ------- | ---------- | ---- | ------------ | + +{{#each phase_4_tasks}} | {{task_id}} | {{title}} | `{{primary_file}}` | {{depends_on}} | {{size}} | {{verification}} | +{{/each}} + +### Phase 4 Verification Checklist + +- [ ] `npm run typecheck` passes +- [ ] `npm test` passes {{#each phase_4_verification}} +- [ ] {{this}} {{/each}} + +### Definition of Done + +{{phase_4_dod}} + +--- + +{{/if}} + +## Cross-Cutting Concerns + +These apply to ALL phases — not repeated per phase but verified at the end: + +- [ ] **Partition support:** All ARN construction uses `arnPrefix(region)` from `src/cli/aws/partition.ts` +- [ ] **Tags:** Resource supports `TagsSchema` and tags flow through to deployed CloudFormation resources +- [ ] **Error messages:** All user-facing errors are clear, actionable, and include remediation steps +- [ ] **Snapshot tests:** Run `npm run test:update-snapshots` if any `src/assets/` files changed +- [ ] **Documentation:** Update `docs/commands.md` and `docs/configuration.md` {{#each additional_cross_cutting}} +- [ ] {{this}} {{/each}} + +--- + +## Task Dependency Graph + +``` +{{dependency_graph_ascii}} +``` + +**Critical path:** {{critical_path}} + +**Parallelizable within phases:** {{#each parallelizable_tasks}} + +- Phase {{phase}}: Tasks {{tasks}} can run in parallel {{/each}} + +--- + +## Size Estimates + +| Size | Definition | Count | +| ---- | --------------------------------------------- | ------------ | +| S | < 50 lines changed, single file | {{count_s}} | +| M | 50-200 lines, 1-3 files | {{count_m}} | +| L | 200-500 lines, 3-5 files | {{count_l}} | +| XL | 500+ lines or complex multi-file coordination | {{count_xl}} | + +**Total tasks:** {{total_tasks}} **Estimated effort:** {{effort_estimate}} + +--- + +## Risk Register + +| # | Risk | Likelihood | Impact | Mitigation | +| --- | ---- | ---------- | ------ | ---------- | + +{{#each risks}} | {{@index + 1}} | {{description}} | {{likelihood}} | {{impact}} | {{mitigation}} | {{/each}} + +--- + +## Testing Plan Per Phase + +### Phase 1 Tests + +| Test Type | What to Test | File | +| --------- | ------------ | ---- | + +{{#each phase_1_tests}} | {{type}} | {{description}} | `{{file}}` | {{/each}} + +### Phase 2 Tests + +| Test Type | What to Test | File | +| --------- | ------------ | ---- | + +{{#each phase_2_tests}} | {{type}} | {{description}} | `{{file}}` | {{/each}} + +### Phase 3 Tests + +| Test Type | What to Test | File | +| --------- | ------------ | ---- | + +{{#each phase_3_tests}} | {{type}} | {{description}} | `{{file}}` | {{/each}} + +### Integration Tests (after all phases) + +| Scenario | Expected Result | Account | Region | +| -------- | --------------- | ------- | ------ | + +{{#each integration_tests}} | {{scenario}} | {{expected}} | {{allowlisted_account_id}} | {{region}} | {{/each}} + +--- + +## PR Strategy + +| Phase | Branch | PR Title | Reviewers | +| ----- | ------------------------------- | ------------------------------------------------------ | -------------- | +| 1 | `feat/{{feature_slug}}-phase-1` | `feat({{feature_slug}}): add schema and primitive` | CLI team | +| 2 | `feat/{{feature_slug}}-phase-2` | `feat({{feature_slug}}): add CDK construct and deploy` | CLI + CDK team | +| 3 | `feat/{{feature_slug}}-phase-3` | `feat({{feature_slug}}): add TUI wizard` | CLI team | + +{{#if phase_4_name}} | 4 | `feat/{{feature_slug}}-phase-4` | `feat({{feature_slug}}): add {{phase_4_name_short}}` | CLI +team | {{/if}} + +**Commit style:** `feat({{feature_slug}}): description` for features, `test({{feature_slug}}): description` for tests + +--- + +## Resolved Decisions + +Decisions made during the DevEx review that affect implementation: + +| # | Decision | Resolution | From | +| --- | -------- | ---------- | ---- | + +{{#each resolved_decisions}} | {{@index + 1}} | {{decision}} | {{resolution}} | DevEx doc v{{version}} | {{/each}} + +--- + +## Notes for the Executor Agent + + + +- Follow the task dependency graph strictly — do not start a task before its dependencies are complete +- Each phase is a separate PR. Do not combine phases. +- Run the phase verification checklist before creating the PR +- If a task requires clarification not covered here, check the approved DevEx doc first +- If still unclear, surface as a blocker rather than guessing +- Size estimates are guidelines. If a task grows beyond its estimate, split it rather than shipping a massive diff. diff --git a/agents/devex_doc_writer/templates/new-resource-devex.md b/agents/devex_doc_writer/templates/new-resource-devex.md new file mode 100644 index 000000000..ea734143f --- /dev/null +++ b/agents/devex_doc_writer/templates/new-resource-devex.md @@ -0,0 +1,447 @@ +# {{feature_name}}: CLI DevEx Proposal + +**Author:** {{service_team_contact}} **Date:** {{date}} **Status:** Draft **References:** {{api_reference}}, +{{design_doc_url}} + +--- + +## What is {{feature_name}}? + + + +{{feature_description}} + +--- + +## Scope + +**In scope:** + +- CLI commands (add, remove, deploy integration) +- TUI wizard flow +- Schema changes (agentcore.json, deployed-state.json) +- CDK construct / imperative deploy step +- Cross-resource validation + +**Out of scope:** {{scope_constraints}} + +- Console experience +- Service internals +- SDK helpers (unless CLI wraps them) + +--- + +## Positioning in the CLI + +| | What | CLI command | Deploy mechanism | Has TUI | +| ---------------- | ------------------ | --------------------------------- | ------------------- | ---------------- | +| Closest existing | {{analogue_label}} | `agentcore add {{analogue_kind}}` | {{analogue_deploy}} | {{analogue_tui}} | +| **This feature** | {{feature_name}} | `agentcore add {{feature_slug}}` | {{deploy_strategy}} | {{tui_flow}} | + +--- + +## Developer Journeys + +### 1/ Create a {{feature_slug}} + +**User story:** A developer wants to add a {{feature_slug}} to their AgentCore project so that [benefit]. + +**CLI experience:** + +```bash +# Non-interactive +agentcore add {{feature_slug}} \ + --name my-{{feature_slug}} \ + --{{primary_flag}} value \ + --{{secondary_flag}} value + +# Interactive (launches TUI wizard) +agentcore add {{feature_slug}} +``` + +**TUI experience:** + +``` +Add {{feature_name}} + +✓ Name → ● {{tui_step_2}} → ○ {{tui_step_3}} → ○ Confirm + +╭──────────────────────────────────────────────────────╮ +│ {{tui_step_2}} │ +│ │ +│ ❯ Option A — description │ +│ Option B — description │ +│ Option C — description │ +╰──────────────────────────────────────────────────────╯ + +↑↓ navigate · Enter select · Esc back +``` + +**Under the hood:** + +1. Validate input against `{{PascalName}}Schema` (local, no API call) +2. Check for name uniqueness within project +3. Write new entry to `agentcore.json` → `{{schema_key}}[]` +4. Print success message with next steps + +**What we build:** + +- [ ] `{{PascalName}}Schema` Zod validation +- [ ] `{{PascalName}}Primitive.add()` implementation +- [ ] TUI wizard screens ({{tui_steps_count}} steps) +- [ ] CLI flag parsing in `registerCommands()` + +--- + +### 2/ Remove a {{feature_slug}} + +**User story:** A developer wants to remove a {{feature_slug}} they no longer need. + +**CLI experience:** + +```bash +# Non-interactive +agentcore remove {{feature_slug}} --name my-{{feature_slug}} + +# Interactive (shows list of removable resources) +agentcore remove {{feature_slug}} +``` + +**Under the hood:** + +1. Check if any other resources reference this {{feature_slug}} +2. If referenced: warn and require `--force` or confirmation +3. Remove from `agentcore.json` +4. Print success message + +**What we build:** + +- [ ] `{{PascalName}}Primitive.remove()` with dependency checking +- [ ] `{{PascalName}}Primitive.getRemovable()` for TUI list +- [ ] `{{PascalName}}Primitive.previewRemove()` for confirmation screen + +--- + +### 3/ Deploy ({{feature_slug}} goes live) + +**User story:** A developer runs `agentcore deploy` and their {{feature_slug}} is created in AWS. + +**CLI experience:** + +```bash +agentcore deploy +``` + +**Under the hood:** + +1. Preflight validation reads {{schema_key}} from agentcore.json +2. {{deploy_mechanism_description}} +3. CFN/API returns {{cfn_outputs}} → written to deployed-state.json +4. Success message with resource ARN + +**What we build:** + +- [ ] CDK construct / imperative deploy step +- [ ] IAM execution role with trust policy +- [ ] deployed-state.json population + +--- + +### 4/ {{data_plane_journey_title}} (if applicable) + + + +--- + +## API Surface + +### Control Plane (`{{control_plane_service}}`) + +| Operation | HTTP | Input | Output | Notes | +| --------- | ---- | ----- | ------ | ----- | + +{{#each control_plane_operations}} | `{{name}}` | `{{http_method}} {{http_path}}` | {{input_shape}} | {{output_shape}} | +{{notes}} | {{/each}} + +### Data Plane (`{{data_plane_service}}`) — if applicable + +| Operation | HTTP | Input | Output | Notes | +| --------- | ---- | ----- | ------ | ----- | + +{{#each data_plane_operations}} | `{{name}}` | `{{http_method}} {{http_path}}` | {{input_shape}} | {{output_shape}} | +{{notes}} | {{/each}} + +**Status values:** (if async) + +``` +{{status_flow_diagram}} +``` + +--- + +## CLI Command ↔ API Mapping + +| CLI Command | API Operation | Notes | +| ----------------------------------- | ---------------------- | --------------------------- | +| `agentcore add {{feature_slug}}` | — (local only) | Writes to agentcore.json | +| `agentcore deploy` | `Create{{PascalName}}` | Via {{deploy_strategy}} | +| `agentcore remove {{feature_slug}}` | — (local only) | Removes from agentcore.json | +| `agentcore status` | `Get{{PascalName}}` | Reads deployed state | + +{{#each additional_command_mappings}} | `{{cli_command}}` | `{{api_operation}}` | {{notes}} | {{/each}} + +--- + +## Schema Changes + +### In `agentcore.json`: + +```json +{ + "name": "MyProject", + "managedBy": "CDK", + "runtimes": [...], + "{{schema_key}}": [ + { + "name": "my-{{feature_slug}}", + {{schema_example_fields}} + } + ] +} +``` + +### Zod schema (`src/schema/schemas/primitives/{{feature_slug}}.ts`): + +```typescript +import { z } from 'zod'; +import { TagsSchema } from './tags'; + +export const {{PascalName}}NameSchema = z + .string() + .min(1, 'Name is required') + .max({{name_max_length}}) + .regex( + {{name_regex}}, + '{{name_regex_description}}' + ); + +export const {{PascalName}}Schema = z.object({ + name: {{PascalName}}NameSchema, + {{zod_fields}} + tags: TagsSchema.optional(), +}); + +export type {{PascalName}} = z.infer; +``` + +### Cross-field validation (in `agentcore-project.ts`): + +```typescript +// In AgentCoreProjectSpecSchema.superRefine: +{ + { + cross_field_validation_code; + } +} +``` + +### In `deployed-state.json`: + +```typescript +export const {{PascalName}}DeployedStateSchema = z.object({ + {{slug}}Id: z.string().min(1), + {{slug}}Arn: z.string().min(1), + {{additional_deployed_state_fields}} +}); + +export type {{PascalName}}DeployedState = z.infer; +``` + +--- + +## Codebase Changes + +### New Files + +| File | Purpose | +| -------------------------------------------------------------------- | -------------------------------------- | +| `src/schema/schemas/primitives/{{feature_slug}}.ts` | Zod schema + types | +| `src/cli/primitives/{{PascalName}}Primitive.ts` | Primitive class (add/remove lifecycle) | +| `src/cli/primitives/__tests__/{{PascalName}}Primitive.test.ts` | Unit tests | +| `src/cli/tui/screens/{{feature_slug}}/AddScreen.tsx` | TUI wizard (if tui_flow) | +| `src/cli/tui/screens/{{feature_slug}}/useAdd{{PascalName}}Wizard.ts` | Wizard hook | + +{{#each additional_new_files}} | `{{path}}` | {{purpose}} | {{/each}} + +### Modified Files + +| File | Change | +| ----------------------------------------- | --------------------------------------------------- | +| `src/schema/schemas/agentcore-project.ts` | Add `{{schema_key}}` array + cross-field validation | +| `src/schema/schemas/deployed-state.ts` | Add `{{PascalName}}DeployedStateSchema` | +| `src/cli/primitives/registry.ts` | Register `{{camelName}}Primitive` singleton | +| `src/cli/commands/remove/types.ts` | Add `'{{feature_slug}}'` to `ResourceType` union | + +{{#each additional_modified_files}} | `{{path}}` | {{change}} | {{/each}} + +--- + +## How It Fits in the CLI Architecture + +### Key Decision: CDK vs Imperative + +**Decision:** {{deploy_strategy}} + +**Rationale:** {{deploy_rationale}} + +**Precedent in the codebase:** {{deploy_precedent}} + +### Deploy Flow Change + +``` +Current deploy flow: + 1. Preflight validation + 2. IMPERATIVE: Create/update API key providers (pre-deploy-identity.ts) + 3. IMPERATIVE: Create/update OAuth2 providers (pre-deploy-identity.ts) + 4. CDK synth + deploy (CloudFormation) + 5. Parse CFN outputs → build deployed-state.json + 6. Post-deploy: setup transaction search + +New deploy flow: + {{new_deploy_flow}} +``` + +### Execution Role + +**Trust policy:** + +```json +{{trust_policy}} +``` + +**Identity-based policy (attached to role):** + +```json +{ + "Version": "2012-10-17", + "Statement": [ + {{#each required_permissions}} + { + "Effect": "Allow", + "Action": {{actions}}, + "Resource": {{resources}} + }{{#unless @last}},{{/unless}} + {{/each}} + ] +} +``` + +--- + +## Architectural Decisions + +| # | Decision | Choice | Rationale | +| --- | ---------------- | -------------------------- | ----------------------------- | +| 1 | Deploy mechanism | {{deploy_strategy}} | {{deploy_rationale_short}} | +| 2 | Schema location | {{schema_location}} | {{schema_location_rationale}} | +| 3 | Name constraints | {{name_regex_description}} | Matches API validation | + +{{#each additional_decisions}} | {{@index + 4}} | {{decision}} | {{choice}} | {{rationale}} | {{/each}} + +--- + +## Implementation Phases + +### Phase 1: Schema + Primitive (independently shippable) + +- [ ] Zod schema in `src/schema/schemas/primitives/{{feature_slug}}.ts` +- [ ] Add to `agentcore-project.ts` (array + cross-field validation) +- [ ] Add to `deployed-state.ts` +- [ ] `{{PascalName}}Primitive` class (add + remove) +- [ ] Register in `registry.ts` +- [ ] Wire into add/remove commands +- [ ] Unit tests for schema + primitive +- [ ] Snapshot test update (if assets change) + +### Phase 2: CDK Construct + Deploy (independently shippable) + +- [ ] CDK construct in `@aws/agentcore-l3-cdk-constructs` +- [ ] IAM role with trust policy + permissions +- [ ] Integration into deploy flow +- [ ] deployed-state.json population from outputs +- [ ] Deploy integration tests + +### Phase 3: TUI + Polish (independently shippable) + +- [ ] TUI wizard screens +- [ ] Wizard hook (step logic) +- [ ] Operation hook (API interaction) +- [ ] TUI snapshot tests + +{{#if has_data_plane}} + +### Phase 4: Data Plane Operations (independently shippable) + +- [ ] Data plane command(s) +- [ ] Status polling / streaming +- [ ] Output formatting +- [ ] Data plane tests {{/if}} + +--- + +## Testing Strategy + +### Unit Tests + +- Schema validation: valid inputs, invalid inputs, edge cases, cross-field rules +- Primitive: add (happy path, duplicate name, referential integrity), remove (clean, with dependents) +- Operations: mock SDK calls, verify request shapes + +### Snapshot Tests + +- Update if any files in `src/assets/` are modified + +### Integration / E2E + +- Deploy to allowlisted account ({{allowlisted_account_id}} in {{allowlisted_regions}}) +- Verify resource created with correct properties +- Verify deployed-state.json populated +- Verify remove + redeploy works cleanly + +--- + +## Open Questions + +| # | Question | For | Context | +| --- | -------- | --- | ------- | + +{{#each open_questions}} | {{@index + 1}} | {{question}} | {{for}} | {{context}} | {{/each}} + +--- + +## Escalation Required + + + +{{#each escalation_items}} + +- **{{title}}:** {{description}} {{/each}} {{#unless escalation_items}} None — all decisions within agent scope. + {{/unless}} + +--- + +## Appendix + +### Full TUI Mockups + + + +### IAM Policy Document + + + +### Full API Response Shapes + + diff --git a/agents/devex_doc_writer/templates/scope-widening-devex.md b/agents/devex_doc_writer/templates/scope-widening-devex.md new file mode 100644 index 000000000..44318f2fb --- /dev/null +++ b/agents/devex_doc_writer/templates/scope-widening-devex.md @@ -0,0 +1,412 @@ +# {{feature_name}}: CLI DevEx Proposal + +**Author:** {{service_team_contact}} **Date:** {{date}} **Status:** Draft **References:** {{external_docs_url}}, +{{design_doc_url}} + +--- + +## What is {{feature_name}}? + + + +{{feature_description}} + +--- + +## Scope + +**In scope:** + +- Changes to existing commands ({{affected_commands_list}}) +- Configuration schema changes +- Detection and prerequisites +- Templates for new projects +- Migration path for existing projects + +**Out of scope:** {{scope_constraints}} + +--- + +## Current State + +{{current_description}} + +**Key files today:** {{#each current_key_files}} + +- `{{this}}` {{/each}} + +**Current developer experience:** + +```bash +{{current_user_experience}} +``` + +--- + +## Target State + +{{target_description}} + +**Target developer experience:** + +```bash +{{target_user_experience}} +``` + +--- + +## Coexistence Model + +**Model:** {{coexistence_model}} + +| Dimension | Existing Path | New Path | +| ---------------- | -------------------- | --------------- | +| Triggered by | {{existing_trigger}} | {{new_trigger}} | +| Configuration | {{existing_config}} | {{new_config}} | +| Deploy mechanism | {{existing_deploy}} | {{new_deploy}} | +| State tracking | {{existing_state}} | {{new_state}} | + +**Backwards compatible:** {{backwards_compatible}} + +{{coexistence_explanation}} + +--- + +## Developer Journeys + +### 1/ New project with {{feature_name}} + +**User story:** A developer creating a new AgentCore project wants to use {{feature_slug}} from the start. + +**CLI experience:** + +```bash +agentcore create +# TUI now offers {{feature_slug}} option during setup +``` + +**TUI experience:** + +``` +Create AgentCore Project + +✓ Name → ✓ Framework → ● {{choice_step}} → ○ Confirm + +╭──────────────────────────────────────────────────────╮ +│ Select {{choice_label}} │ +│ │ +│ ❯ {{existing_option}} — {{existing_description}} │ +│ {{new_option}} — {{new_description}} │ +╰──────────────────────────────────────────────────────╯ + +↑↓ navigate · Enter select · Esc back +``` + +**Under the hood:** + +1. User selection writes `{{config_field}}: "{{new_value}}"` to agentcore.json +2. Templates vended for the new path +3. Project structure created with {{new_option}}-specific files + +**What we build:** + +- [ ] New option in create wizard +- [ ] Template files for {{new_option}} path +- [ ] Config field in schema + +--- + +### 2/ Existing project opting in + +**User story:** A developer with an existing CDK-based project wants to switch to {{feature_slug}}. + +**CLI experience:** + +```bash +# Manual opt-in: edit agentcore.json +# Set {{config_field}}: "{{new_value}}" + +# Or via CLI command (if applicable): +agentcore {{opt_in_command}} +``` + +**Under the hood:** + +1. Validate {{new_option}} prerequisites are met ({{user_prerequisites}}) +2. Update `{{config_field}}` in agentcore.json +3. {{migration_steps}} + +**What we build:** + +- [ ] Prerequisite validation +- [ ] Config migration logic +- [ ] Clear error messages for missing prerequisites + +--- + +### 3/ Existing project — unchanged (proves backwards compat) + +**User story:** A developer with an existing project who does NOT opt in experiences zero changes. + +**CLI experience:** + +```bash +# Everything works exactly as before +agentcore deploy # still uses {{existing_option}} +agentcore status # still reads {{existing_state}} +``` + +**Under the hood:** + +1. `{{config_field}}` defaults to `"{{existing_value}}"` if absent +2. All existing code paths unchanged +3. No new dependencies introduced for the default path + +**Callout:** This is critical. If an existing project breaks after this change ships, it's a regression. + +--- + +### 4/ {{operational_journey}} (if applicable) + +--- + +## Affected Commands + +| Command | Change Type | Description | Backwards Compatible | +| ------- | ----------- | ----------- | -------------------- | + +{{#each affected_commands}} | `agentcore {{command}}` | {{change_type}} | {{description}} | {{backwards_compatible}} | +{{/each}} + +--- + +## Schema Changes + +### In `agentcore.json`: + +```json +{ + "name": "MyProject", + "managedBy": "CDK", + "{{config_field}}": "{{new_value}}", + {{additional_schema_fields}} +} +``` + +**Default value:** `"{{existing_value}}"` (if field absent, existing behavior preserved) + +### Zod schema change: + +```typescript +// In AgentCoreProjectSpecSchema: +{{config_field}}: z.enum([{{enum_values}}]).default('{{existing_value}}'), +``` + +### In `deployed-state.json` (if applicable): + +```typescript +{ + { + deployed_state_changes; + } +} +``` + +--- + +## Detection & Prerequisites + +### External Tool Detection + +```typescript +// How the CLI checks for {{external_tool}}: +{ + { + detection_code; + } +} +``` + +### Version Check + +```typescript +// Minimum version: {{external_tool_version}} +{ + { + version_check_code; + } +} +``` + +### Error Messages + +| Scenario | Message | +| --------------- | ----------------------------------------------------------------------------------------------- | +| Tool not found | `"{{external_tool}} not found. Install it: {{install_instructions}}"` | +| Version too old | `"{{external_tool}} {{detected_version}} is not supported. Minimum: {{external_tool_version}}"` | +| Misconfigured | `"{{misconfigured_message}}"` | + +--- + +## Codebase Changes + +### New Files + +| File | Purpose | +| ---- | ------- | + +{{#each new_files}} | `{{path}}` | {{purpose}} | {{/each}} + +### Modified Files + +| File | Change | +| ---- | ------ | + +{{#each modified_files}} | `{{path}}` | {{change}} | {{/each}} + +--- + +## Architecture: Where It Branches + +### Dispatch Point + +``` +{{dispatch_diagram}} +``` + +**Pattern:** {{design_pattern}} (e.g., Strategy, Adapter) + +**Precedent in the codebase:** {{precedent}} + +### Deploy Flow Change + +``` +Current: New: + 1. Preflight validation 1. Preflight validation + 2. Identity providers 2. Identity providers + 3. OAuth2 providers 3. OAuth2 providers + 4. CDK synth + deploy 4. {{new_step_4}} + 5. Parse outputs → state 5. Parse outputs → state + 6. Post-deploy 6. Post-deploy +``` + +--- + +## Architectural Decisions + +| # | Decision | Choice | Rationale | +| --- | ------------------- | ------------------------------ | --------------------------------------- | +| 1 | Coexistence model | {{coexistence_model}} | {{coexistence_rationale}} | +| 2 | Configuration level | Project-level (agentcore.json) | Persistent choice, not per-command flag | +| 3 | Detection strategy | {{detection_strategy}} | {{detection_rationale}} | + +{{#each additional_decisions}} | {{@index + 4}} | {{decision}} | {{choice}} | {{rationale}} | {{/each}} + +--- + +## Implementation Phases + +### Phase 1: Foundation + Detection (independently shippable) + +- [ ] Config schema change (new field with default — no behavior change) +- [ ] External tool detection utility +- [ ] Version check logic +- [ ] Error messages for missing/incompatible tool +- [ ] Unit tests: schema validation, detection, version check +- [ ] **Ship note:** After this lands, existing projects unchanged. Just the schema field and detection infra. + +### Phase 2: Core Implementation (independently shippable) + +- [ ] New code path ({{new_option}} provider/adapter) +- [ ] Dispatch logic at the branch point +- [ ] {{new_option}}-specific templates/assets +- [ ] Integration tests: new path works end-to-end +- [ ] **Ship note:** After this lands, `{{config_field}}: "{{new_value}}"` in config activates the new path. + +### Phase 3: Creation Flow Integration (independently shippable) + +- [ ] `agentcore create` TUI offers new option +- [ ] Templates vended for new path projects +- [ ] Snapshot tests for new templates +- [ ] **Ship note:** After this lands, new projects can choose {{new_option}} at creation time. + +### Phase 4: Polish + Migration (independently shippable) + +- [ ] Opt-in mechanism for existing projects +- [ ] Migration validation (existing → new) +- [ ] Documentation updates (commands.md, configuration.md) +- [ ] Edge case error handling +- [ ] **Ship note:** After this lands, existing projects can switch. + +--- + +## Testing Strategy + +### Existing Test Preservation + +- All existing tests MUST pass without modification after Phase 1-3 +- Run full test suite on every PR: `npm test`, `npm run typecheck`, `npm run lint` +- Any existing test failure is a regression, not a "needs update" + +### New Test Matrix + +| Scenario | {{existing_option}} | {{new_option}} | +| ------------------- | ------------------- | -------------------- | +| Create project | ✓ (unchanged) | ✓ (new) | +| Deploy | ✓ (unchanged) | ✓ (new path) | +| Status | ✓ (unchanged) | ✓ (new state source) | +| Remove + redeploy | ✓ | ✓ | +| Missing tool | N/A | ✓ (clear error) | +| Wrong version | N/A | ✓ (clear error) | +| Switch existing→new | ✓ | ✓ | + +### Integration / E2E + +- Deploy with {{new_option}} to allowlisted account +- Verify resources created correctly +- Verify state tracking works +- Verify switching between options + +--- + +## Open Questions + +| # | Question | For | Context | +| --- | -------- | --- | ------- | + +{{#each open_questions}} | {{@index + 1}} | {{question}} | {{for}} | {{context}} | {{/each}} + +--- + +## Escalation Required + +{{#each escalation_items}} + +- **{{title}}:** {{description}} {{/each}} {{#unless escalation_items}} None — all decisions within agent scope. + {{/unless}} + +--- + +## Appendix + +### Side-by-Side Config Examples + +**Existing path (unchanged):** + +```json +{{existing_config_example}} +``` + +**New path:** + +```json +{{new_config_example}} +``` + +### Full Detection Logic + + + +### Template Diff + + diff --git a/agents/knowledge/.gitignore b/agents/knowledge/.gitignore new file mode 100644 index 000000000..dba3981fb --- /dev/null +++ b/agents/knowledge/.gitignore @@ -0,0 +1,3 @@ +.cdk-clone +node_modules +dist diff --git a/agents/knowledge/cdk-architecture-snapshot.yaml b/agents/knowledge/cdk-architecture-snapshot.yaml new file mode 100644 index 000000000..e765bfc5b --- /dev/null +++ b/agents/knowledge/cdk-architecture-snapshot.yaml @@ -0,0 +1,51 @@ +schema_version: 1 +generated_at: 2026-05-06T04:47:21.509Z +commit: 06e11dea +repo: aws/agentcore-l3-cdk-constructs +service_principal: bedrock-agentcore.amazonaws.com +constructs: + - name: AgentCoreApplication + file: src/cdk/constructs/l3/AgentCoreApplication.ts + type: l3 + - name: AgentCoreHarnessEnvironment + file: src/cdk/constructs/l3/AgentCoreHarnessEnvironment.ts + type: l3 + - name: AgentCoreMcp + file: src/cdk/constructs/l3/AgentCoreMcp.ts + type: l3 + - name: AgentEnvironment + file: src/cdk/constructs/l3/AgentEnvironment.ts + type: l3 + - name: BasePrimitiveConstruct + file: src/cdk/constructs/components/primitives/BasePrimitiveConstruct.ts + type: primitive + - name: AgentCoreEvaluator + file: src/cdk/constructs/components/primitives/evaluator/AgentCoreEvaluator.ts + type: primitive + - name: AgentCoreOnlineEvaluationConfig + file: src/cdk/constructs/components/primitives/evaluator/AgentCoreOnlineEvaluationConfig.ts + type: primitive + - name: AgentCoreHarnessRole + file: src/cdk/constructs/components/primitives/harness/AgentCoreHarnessRole.ts + type: primitive + - name: AgentCoreMemory + file: src/cdk/constructs/components/primitives/memory/AgentCoreMemory.ts + type: primitive + - name: AgentCorePolicyEngine + file: src/cdk/constructs/components/primitives/policy/AgentCorePolicyEngine.ts + type: primitive + - name: AgentCoreRuntime + file: src/cdk/constructs/components/primitives/runtime/AgentCoreRuntime.ts + type: primitive + - name: Gateway + file: src/cdk/constructs/components/mcp/Gateway.ts + type: mcp + - name: McpLambdaCompute + file: src/cdk/constructs/components/mcp/McpLambdaCompute.ts + type: mcp + - name: McpRuntimeCompute + file: src/cdk/constructs/components/mcp/McpRuntimeCompute.ts + type: mcp + - name: mcp-utils + file: src/cdk/constructs/components/mcp/mcp-utils.ts + type: mcp diff --git a/agents/knowledge/cli-architecture-snapshot.yaml b/agents/knowledge/cli-architecture-snapshot.yaml new file mode 100644 index 000000000..06cd15679 --- /dev/null +++ b/agents/knowledge/cli-architecture-snapshot.yaml @@ -0,0 +1,279 @@ +schema_version: 1 +generated_at: 2026-05-06T04:47:21.494Z +commit: c9852f47 +repo: aws/agentcore-cli +primitives: + - name: AgentPrimitive + file: src/cli/primitives/AgentPrimitive.tsx + kind: agent + label: Agent + resources_managed: + - agents + schema_key: agents + supports_tui: true + supports_remove: true + has_cross_references: false + - name: CredentialPrimitive + file: src/cli/primitives/CredentialPrimitive.tsx + kind: credential + label: Credential + resources_managed: + - credentials + schema_key: credentials + supports_tui: true + supports_remove: true + has_cross_references: false + - name: EvaluatorPrimitive + file: src/cli/primitives/EvaluatorPrimitive.ts + kind: evaluator + label: Evaluator + resources_managed: + - evaluators + schema_key: evaluators + supports_tui: true + supports_remove: true + has_cross_references: false + - name: GatewayPrimitive + file: src/cli/primitives/GatewayPrimitive.ts + kind: gateway + label: Gateway + resources_managed: + - gateways + schema_key: gateways + supports_tui: true + supports_remove: true + has_cross_references: false + - name: GatewayTargetPrimitive + file: src/cli/primitives/GatewayTargetPrimitive.ts + kind: gateway-target + label: Gateway Target + resources_managed: + - gatewayTargets + schema_key: gatewayTargets + supports_tui: true + supports_remove: true + has_cross_references: false + - name: MemoryPrimitive + file: src/cli/primitives/MemoryPrimitive.tsx + kind: memory + label: Memory + resources_managed: + - memories + schema_key: memories + supports_tui: true + supports_remove: true + has_cross_references: false + - name: OnlineEvalConfigPrimitive + file: src/cli/primitives/OnlineEvalConfigPrimitive.ts + kind: online-eval + label: Online Eval Config + resources_managed: + - onlineEvalConfigs + schema_key: onlineEvalConfigs + supports_tui: true + supports_remove: true + has_cross_references: false + - name: PolicyEnginePrimitive + file: src/cli/primitives/PolicyEnginePrimitive.ts + kind: policy-engine + label: Policy Engine + resources_managed: + - policyEngines + schema_key: policyEngines + supports_tui: true + supports_remove: true + has_cross_references: false + - name: PolicyPrimitive + file: src/cli/primitives/PolicyPrimitive.ts + kind: policy + label: Policy + resources_managed: + - policies + schema_key: policies + supports_tui: true + supports_remove: true + has_cross_references: false + - name: RuntimeEndpointPrimitive + file: src/cli/primitives/RuntimeEndpointPrimitive.ts + kind: runtime-endpoint + label: Runtime Endpoint + resources_managed: + - runtimeEndpoints + schema_key: runtimeEndpoints + supports_tui: true + supports_remove: true + has_cross_references: false +schema_shape: + agentcore_json: + top_level_arrays: + - key: runtimes + schema_file: src/schema/schemas/agent-env.ts + - key: memories + schema_file: src/schema/schemas/agentcore-project.ts + - key: credentials + schema_file: src/schema/schemas/agentcore-project.ts + - key: evaluators + schema_file: src/schema/schemas/primitives/evaluator.ts + - key: onlineEvalConfigs + schema_file: src/schema/schemas/primitives/online-eval-config.ts + - key: agentCoreGateways + schema_file: src/schema/schemas/mcp.ts + - key: mcpRuntimeTools + schema_file: src/schema/schemas/mcp.ts + - key: unassignedTargets + schema_file: src/schema/schemas/mcp.ts + - key: policyEngines + schema_file: src/schema/schemas/primitives/policy.ts + cross_field_validations: + - source: onlineEvalConfigs[].agent + target: runtimes[].name + rule: must_exist + - source: onlineEvalConfigs[].evaluators[] + target: evaluators[].name + rule: must_exist + deployed_state: + file: src/schema/schemas/deployed-state.ts + resource_types: + - key: runtimes + fields: + - runtimeId + - runtimeArn + - roleArn + - sessionId + - memoryIds + - browserId + - codeInterpreterId + - runtimeVersion + - key: memories + fields: + - memoryId + - memoryArn + - key: gateways + fields: + - gatewayId + - gatewayArn + - gatewayUrl + - key: mcpRuntimes + fields: + - runtimeId + - runtimeArn + - runtimeEndpoint + - key: mcpLambdas + fields: + - functionArn + - functionName + - key: policyEngines + fields: + - policyEngineId + - policyEngineArn + - key: policies + fields: + - policyId + - policyArn + - engineName + - key: credentials + fields: + - credentialProviderArn + - clientSecretArn + - callbackUrl + - key: evaluators + fields: + - evaluatorId + - evaluatorArn + - key: onlineEvalConfigs + fields: + - onlineEvaluationConfigId + - onlineEvaluationConfigArn + - executionStatus + - key: runtimeEndpoints + fields: + - endpointId + - endpointArn +deploy_flow: + file: src/cli/operations/deploy/ + steps: + '1': Preflight validation (schema parse, credentials check, target resolution) + '2': 'IMPERATIVE: Create/update API key providers' + '3': 'IMPERATIVE: Create/update OAuth2 providers' + '4': CDK synth + deploy (CloudFormation via CDK toolkit) + '5': Parse CFN outputs → build deployed-state.json + '6': 'Post-deploy: setup transaction search' +commands: + verbs: + - name: add + nouns: + - agent + - credential + - evaluator + - gateway + - gateway-target + - memory + - online-eval + - policy + - policy-engine + - runtime-endpoint + has_tui: true + - name: create + has_tui: true + - name: deploy + - name: dev + - name: eval + nouns: + - history + - name: fetch + nouns: + - access + - name: help + - name: import + - name: invoke + - name: logs + - name: package + - name: pause + nouns: + - online-eval + - name: remove + nouns: + - agent + - credential + - evaluator + - gateway + - gateway-target + - memory + - online-eval + - policy + - policy-engine + - runtime-endpoint + has_tui: false + - name: resume + nouns: + - online-eval + - name: run + nouns: + - eval + - name: status + - name: tag + - name: telemetry + - name: traces + nouns: + - list + - get + - name: update + - name: validate +iam_patterns: + service_principal: bedrock-agentcore.amazonaws.com + lambda_principal: lambda.amazonaws.com + role_creation: 'CDK: new iam.Role({ assumedBy: new iam.ServicePrincipal(AGENTCORE_SERVICE_PRINCIPAL) })' + existing_role_support: executionRoleArn on agent schema allows BYO role + partition_utility: src/cli/aws/partition.ts — arnPrefix(), serviceEndpoint(), dnsSuffix() +code_style: + rules: + - No inline imports + - '{ success: boolean, error?: string } for results' + - Existing types before inline + - Constants in closest subdirectory + - 'Never hardcode arn:aws:' + - Tags via TagsSchema on all resources + tui_patterns: + - Screen → Flow → Wizard hook → Operation hook → Primitive + - MAX_CONTENT_WIDTH = 60 + - SelectList uses wrap='wrap' diff --git a/agents/knowledge/extractors/commands.ts b/agents/knowledge/extractors/commands.ts new file mode 100644 index 000000000..b43db30e6 --- /dev/null +++ b/agents/knowledge/extractors/commands.ts @@ -0,0 +1,135 @@ +import fs from 'fs'; +import path from 'path'; + +export interface CommandVerb { + name: string; + nouns?: string[]; + has_tui?: boolean; +} + +export function extractCommands(cliRoot: string): CommandVerb[] { + const commandsDir = path.join(cliRoot, 'src/cli/commands'); + + if (!fs.existsSync(commandsDir)) { + throw new Error(`Commands directory not found at ${commandsDir}`); + } + + const entries = fs.readdirSync(commandsDir, { withFileTypes: true }); + const verbs: CommandVerb[] = []; + + // Each subdirectory under commands/ is a verb + const verbDirs = entries.filter(e => e.isDirectory() && e.name !== 'shared').map(e => e.name); + + // Known nouns per verb (extracted from AGENTS.md / command structure) + const verbConfig: Record = { + add: { nouns: getAddNouns(commandsDir), has_tui: true }, + remove: { nouns: getRemoveNouns(commandsDir), has_tui: false }, + create: { has_tui: true }, + deploy: {}, + status: {}, + dev: {}, + invoke: {}, + run: { nouns: ['eval'] }, + logs: {}, + package: {}, + validate: {}, + update: {}, + import: {}, + fetch: { nouns: ['access'] }, + pause: { nouns: ['online-eval'] }, + resume: { nouns: ['online-eval'] }, + traces: { nouns: ['list', 'get'] }, + eval: { nouns: ['history'] }, + tag: {}, + help: {}, + telemetry: {}, + }; + + for (const verb of verbDirs) { + const config = verbConfig[verb] || {}; + verbs.push({ + name: verb, + ...(config.nouns && { nouns: config.nouns }), + ...(config.has_tui !== undefined && { has_tui: config.has_tui }), + }); + } + + return verbs; +} + +function getAddNouns(commandsDir: string): string[] { + // Add nouns come from primitive registrations (primitive.registerCommands()), + // not from the add/ directory structure. Extract from the primitives registry + // by reading kind values, or fall back to the known list from AGENTS.md. + const primitivesDir = path.join(commandsDir, '..', 'primitives'); + + if (fs.existsSync(primitivesDir)) { + const nouns: string[] = []; + const files = fs + .readdirSync(primitivesDir) + .filter( + f => + (f.endsWith('.ts') || f.endsWith('.tsx')) && + f.includes('Primitive') && + f !== 'BasePrimitive.ts' && + !f.includes('test') + ); + + for (const file of files) { + const content = fs.readFileSync(path.join(primitivesDir, file), 'utf-8'); + // Match: readonly kind = 'some-kind' or readonly kind: ResourceType = 'some-kind' + const kindMatch = /readonly\s+kind[^=]*=\s*['"]([^'"]+)['"]/.exec(content); + if (kindMatch && !nouns.includes(kindMatch[1])) { + nouns.push(kindMatch[1]); + } + } + + if (nouns.length > 0) return nouns.sort(); + } + + return [ + 'agent', + 'memory', + 'credential', + 'evaluator', + 'online-eval', + 'gateway', + 'gateway-target', + 'policy-engine', + 'policy', + ]; +} + +function getRemoveNouns(commandsDir: string): string[] { + const removeDir = path.join(commandsDir, 'remove'); + if (!fs.existsSync(removeDir)) return []; + + // Check types.ts for ResourceType union + const typesPath = path.join(removeDir, 'types.ts'); + if (fs.existsSync(typesPath)) { + const content = fs.readFileSync(typesPath, 'utf-8'); + // Match string literal union members + const literalPattern = /['"]([a-z-]+)['"]/g; + const nouns: string[] = []; + let match; + while ((match = literalPattern.exec(content)) !== null) { + if (!nouns.includes(match[1])) { + nouns.push(match[1]); + } + } + if (nouns.length > 0) return nouns.sort(); + } + + return [ + 'agent', + 'memory', + 'credential', + 'evaluator', + 'online-eval', + 'gateway', + 'gateway-target', + 'policy-engine', + 'policy', + 'all', + ]; +} diff --git a/agents/knowledge/extractors/deploy-flow.ts b/agents/knowledge/extractors/deploy-flow.ts new file mode 100644 index 000000000..8f2c5363f --- /dev/null +++ b/agents/knowledge/extractors/deploy-flow.ts @@ -0,0 +1,88 @@ +import fs from 'fs'; +import path from 'path'; + +export interface DeployStep { + order: number; + description: string; + type: 'imperative' | 'cdk' | 'validation' | 'post-deploy'; + file?: string; +} + +export function extractDeployFlow(cliRoot: string): DeployStep[] { + const deployDir = path.join(cliRoot, 'src/cli/operations/deploy'); + + if (!fs.existsSync(deployDir)) { + throw new Error(`Deploy directory not found at ${deployDir}`); + } + + // The deploy flow is defined by the sequence of operations in the deploy command. + // We extract this from the deploy operation files and their ordering. + const steps: DeployStep[] = []; + + // Check for the main deploy orchestrator + const deployFiles = fs.readdirSync(deployDir).filter(f => f.endsWith('.ts') && !f.includes('test')); + + // Known deploy flow structure (extracted from reading the actual code) + // We verify each step's file exists + const knownSteps: DeployStep[] = [ + { + order: 1, + description: 'Preflight validation (schema parse, credentials check, target resolution)', + type: 'validation', + }, + { + order: 2, + description: 'IMPERATIVE: Create/update API key providers', + type: 'imperative', + file: 'src/cli/operations/deploy/pre-deploy-identity.ts', + }, + { + order: 3, + description: 'IMPERATIVE: Create/update OAuth2 providers', + type: 'imperative', + file: 'src/cli/operations/deploy/pre-deploy-identity.ts', + }, + { + order: 4, + description: 'CDK synth + deploy (CloudFormation via CDK toolkit)', + type: 'cdk', + }, + { + order: 5, + description: 'Parse CFN outputs → build deployed-state.json', + type: 'post-deploy', + }, + { + order: 6, + description: 'Post-deploy: setup transaction search', + type: 'post-deploy', + }, + ]; + + // Verify files exist and add to output + for (const step of knownSteps) { + if (step.file) { + const fullPath = path.join(cliRoot, step.file); + if (!fs.existsSync(fullPath)) { + step.description += ' [FILE NOT FOUND]'; + } + } + steps.push(step); + } + + // Scan for additional pre-deploy or post-deploy files we may have missed + const preDeployFiles = deployFiles.filter(f => f.startsWith('pre-deploy') && f !== 'pre-deploy-identity.ts'); + for (const file of preDeployFiles) { + const existingFile = steps.find(s => s.file?.endsWith(file)); + if (!existingFile) { + steps.push({ + order: steps.length + 1, + description: `IMPERATIVE: ${file.replace('pre-deploy-', '').replace('.ts', '')} (discovered)`, + type: 'imperative', + file: `src/cli/operations/deploy/${file}`, + }); + } + } + + return steps; +} diff --git a/agents/knowledge/extractors/primitives.ts b/agents/knowledge/extractors/primitives.ts new file mode 100644 index 000000000..2d754e0f2 --- /dev/null +++ b/agents/knowledge/extractors/primitives.ts @@ -0,0 +1,139 @@ +import fs from 'fs'; +import path from 'path'; +import { Project } from 'ts-morph'; + +export interface PrimitiveInfo { + name: string; + file: string; + kind: string; + label: string; + resources_managed: string[]; + schema_key: string; + supports_tui: boolean; + supports_remove: boolean; + has_cross_references: boolean; + references?: string[]; +} + +export function extractPrimitives(cliRoot: string): PrimitiveInfo[] { + const registryPath = path.join(cliRoot, 'src/cli/primitives/registry.ts'); + if (!fs.existsSync(registryPath)) { + throw new Error(`Registry not found at ${registryPath}`); + } + + const project = new Project({ + tsConfigFilePath: path.join(cliRoot, 'tsconfig.json'), + skipAddingFilesFromTsConfig: true, + }); + + // Add only the files we need + const primitivesDir = path.join(cliRoot, 'src/cli/primitives'); + const primitiveFiles = fs + .readdirSync(primitivesDir) + .filter(f => (f.endsWith('.ts') || f.endsWith('.tsx')) && !f.includes('__tests__') && !f.includes('.test.')) + .map(f => path.join(primitivesDir, f)); + + primitiveFiles.forEach(f => project.addSourceFileAtPath(f)); + + const registrySource = project.getSourceFileOrThrow(registryPath); + const primitives: PrimitiveInfo[] = []; + + // Find ALL_PRIMITIVES array to get the list of registered primitives + const allPrimitivesVar = registrySource.getVariableDeclaration('ALL_PRIMITIVES'); + if (!allPrimitivesVar) { + throw new Error('ALL_PRIMITIVES not found in registry.ts'); + } + + // Extract import paths to find primitive class files + const imports = registrySource.getImportDeclarations(); + const primitiveClassFiles = new Map(); + + for (const imp of imports) { + const namedImports = imp.getNamedImports(); + const moduleSpecifier = imp.getModuleSpecifierValue(); + for (const named of namedImports) { + const importName = named.getName(); + if (importName.endsWith('Primitive') && importName !== 'BasePrimitive') { + const resolvedPath = path.resolve(primitivesDir, moduleSpecifier); + primitiveClassFiles.set(importName, resolvedPath); + } + } + } + + // For each primitive class, extract metadata + for (const [className, filePath] of primitiveClassFiles) { + const extensions = ['', '.ts', '.tsx']; + let sourceFile = null; + + for (const ext of extensions) { + const fullPath = filePath + ext; + sourceFile = project.getSourceFile(fullPath); + if (sourceFile) break; + } + + if (!sourceFile) continue; + + const classDecl = sourceFile.getClass(className); + if (!classDecl) continue; + + // Extract kind + const kindProp = classDecl.getProperty('kind'); + let kind = ''; + if (kindProp) { + const initializer = kindProp.getInitializer(); + if (initializer) { + kind = initializer.getText().replace(/['"]/g, '').replace(' as const', ''); + } + } + + // Extract label + const labelProp = classDecl.getProperty('label'); + let label = ''; + if (labelProp) { + const initializer = labelProp.getInitializer(); + if (initializer) { + label = initializer.getText().replace(/['"]/g, ''); + } + } + + // Check for addScreen (indicates TUI support) + const addScreenMethod = classDecl.getMethod('addScreen'); + const supportsTui = addScreenMethod !== undefined; + + // Check for remove method implementation (not just the abstract) + const removeMethod = classDecl.getMethod('remove'); + const supportsRemove = removeMethod !== undefined; + + // Determine schema_key from kind (convention: kind maps to schema array key) + const schemaKeyMap: Record = { + agent: 'agents', + memory: 'memories', + credential: 'credentials', + evaluator: 'evaluators', + 'online-eval': 'onlineEvalConfigs', + gateway: 'gateways', + 'gateway-target': 'gatewayTargets', + 'policy-engine': 'policyEngines', + policy: 'policies', + 'runtime-endpoint': 'runtimeEndpoints', + }; + + const schemaKey = schemaKeyMap[kind] || kind; + const relativeFile = path.relative(cliRoot, sourceFile.getFilePath()); + + primitives.push({ + name: className, + file: relativeFile, + kind, + label, + resources_managed: [schemaKey], + schema_key: schemaKey, + supports_tui: supportsTui, + supports_remove: supportsRemove, + has_cross_references: false, // will be enriched by schema extractor + references: undefined, + }); + } + + return primitives; +} diff --git a/agents/knowledge/extractors/schema.ts b/agents/knowledge/extractors/schema.ts new file mode 100644 index 000000000..36d8e9090 --- /dev/null +++ b/agents/knowledge/extractors/schema.ts @@ -0,0 +1,172 @@ +import fs from 'fs'; +import path from 'path'; + +export interface SchemaArrayInfo { + key: string; + schema_file: string; +} + +export interface CrossFieldValidation { + source: string; + target: string; + rule: string; +} + +export interface DeployedStateResource { + key: string; + fields: string[]; +} + +export interface SchemaShape { + agentcore_json: { + top_level_arrays: SchemaArrayInfo[]; + cross_field_validations: CrossFieldValidation[]; + }; + deployed_state: { + file: string; + resource_types: DeployedStateResource[]; + }; +} + +export function extractSchemaShape(cliRoot: string): SchemaShape { + const projectSpecPath = path.join(cliRoot, 'src/schema/schemas/agentcore-project.ts'); + const deployedStatePath = path.join(cliRoot, 'src/schema/schemas/deployed-state.ts'); + + // Extract top-level arrays from agentcore-project.ts by reading the file content + // and finding z.array patterns within the main schema object + const projectContent = fs.readFileSync(projectSpecPath, 'utf-8'); + const deployedContent = fs.readFileSync(deployedStatePath, 'utf-8'); + + const topLevelArrays = extractTopLevelArrays(projectContent, cliRoot); + const crossFieldValidations = extractCrossFieldValidations(projectContent); + const deployedStateResources = extractDeployedStateResources(deployedContent); + + return { + agentcore_json: { + top_level_arrays: topLevelArrays, + cross_field_validations: crossFieldValidations, + }, + deployed_state: { + file: 'src/schema/schemas/deployed-state.ts', + resource_types: deployedStateResources, + }, + }; +} + +function extractTopLevelArrays(content: string, _cliRoot: string): SchemaArrayInfo[] { + // The project schema uses multiline format: + // runtimes: z + // .array(AgentEnvSpecSchema) + // Match: key: z\n .array( OR key: z.array( + const arrayPattern = /(\w+):\s*z\s*\n?\s*\.array\(/g; + const arrays: SchemaArrayInfo[] = []; + let match; + + // Known mappings of actual schema keys to their source files + const schemaFileMap: Record = { + runtimes: 'src/schema/schemas/agent-env.ts', + memories: 'src/schema/schemas/agentcore-project.ts', + credentials: 'src/schema/schemas/agentcore-project.ts', + evaluators: 'src/schema/schemas/primitives/evaluator.ts', + onlineEvalConfigs: 'src/schema/schemas/primitives/online-eval-config.ts', + agentCoreGateways: 'src/schema/schemas/mcp.ts', + mcpRuntimeTools: 'src/schema/schemas/mcp.ts', + unassignedTargets: 'src/schema/schemas/mcp.ts', + policyEngines: 'src/schema/schemas/primitives/policy.ts', + }; + + while ((match = arrayPattern.exec(content)) !== null) { + const key = match[1]; + if (schemaFileMap[key]) { + arrays.push({ + key, + schema_file: schemaFileMap[key], + }); + } + } + + return arrays; +} + +function extractCrossFieldValidations(content: string): CrossFieldValidation[] { + const validations: CrossFieldValidation[] = []; + + // Extract cross-field validations from the superRefine at the bottom of AgentCoreProjectSpecSchema. + // Look for patterns like: spec.runtimes.map(a => a.name) ... config.agent ... config.evaluators + // These reference other resource arrays for referential integrity. + + // Pattern: check for sets built from one array and validated against another + const setPatterns = [ + { + regex: /agentNames.*runtimes|runtimes.*agentNames/s, + source: 'onlineEvalConfigs[].agent', + target: 'runtimes[].name', + rule: 'must_exist', + }, + { + regex: /evaluatorNames.*evaluators|evaluators.*evaluatorNames/s, + source: 'onlineEvalConfigs[].evaluators[]', + target: 'evaluators[].name', + rule: 'must_exist', + }, + ]; + + // Only extract the superRefine block at the end of the schema + const superRefineMatch = /\.strict\(\)\s*\.superRefine\(\(spec,[\s\S]+$/.exec(content); + if (!superRefineMatch) return validations; + + const superRefineContent = superRefineMatch[0]; + + for (const pattern of setPatterns) { + if (pattern.regex.test(superRefineContent)) { + validations.push({ source: pattern.source, target: pattern.target, rule: pattern.rule }); + } + } + + return validations; +} + +function extractDeployedStateResources(content: string): DeployedStateResource[] { + const resources: DeployedStateResource[] = []; + + // Match schema definitions: XxxDeployedStateSchema = z.object({...}) + const schemaPattern = /export const (\w+)DeployedStateSchema = z\.object\(\{([^}]+)\}/gs; + let match; + + // Map from schema name prefix to the key in DeployedResourceStateSchema + const keyMap: Record = { + AgentCore: 'runtimes', + Memory: 'memories', + Gateway: 'gateways', + McpRuntime: 'mcpRuntimes', + McpLambda: 'mcpLambdas', + PolicyEngine: 'policyEngines', + Policy: 'policies', + Credential: 'credentials', + Evaluator: 'evaluators', + OnlineEval: 'onlineEvalConfigs', + RuntimeEndpoint: 'runtimeEndpoints', + }; + + while ((match = schemaPattern.exec(content)) !== null) { + const schemaPrefix = match[1]; + const fieldsBlock = match[2]; + const key = keyMap[schemaPrefix]; + + if (!key) continue; + + // Extract field names from the object definition + const fieldPattern = /(\w+):\s*z\./g; + const fields: string[] = []; + let fieldMatch; + while ((fieldMatch = fieldPattern.exec(fieldsBlock)) !== null) { + fields.push(fieldMatch[1]); + } + + if (fields.length > 0) { + resources.push({ key, fields }); + } + } + + return resources; +} diff --git a/agents/knowledge/package-lock.json b/agents/knowledge/package-lock.json new file mode 100644 index 000000000..082260b43 --- /dev/null +++ b/agents/knowledge/package-lock.json @@ -0,0 +1,699 @@ +{ + "name": "agentcore-knowledge-refresh", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "agentcore-knowledge-refresh", + "version": "1.0.0", + "dependencies": { + "ts-morph": "^24.0.0", + "yaml": "^2.4.0" + }, + "devDependencies": { + "tsx": "^4.19.0", + "typescript": "^5.5.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@ts-morph/common": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.25.0.tgz", + "integrity": "sha512-kMnZz+vGGHi4GoHnLmMhGNjm44kGtKUXGnOvrKmMwAuvNjM/PgKVGfUnL7IDvK7Jb2QQ82jq3Zmp04Gy+r3Dkg==", + "license": "MIT", + "dependencies": { + "minimatch": "^9.0.4", + "path-browserify": "^1.0.1", + "tinyglobby": "^0.2.9" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-morph": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-24.0.0.tgz", + "integrity": "sha512-2OAOg/Ob5yx9Et7ZX4CvTCc0UFoZHwLEJ+dpDPSUi5TgwwlTlX47w+iFRrEwzUZwYACjq83cgjS/Da50Ga37uw==", + "license": "MIT", + "dependencies": { + "@ts-morph/common": "~0.25.0", + "code-block-writer": "^13.0.3" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/yaml": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.4.tgz", + "integrity": "sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + } + } +} diff --git a/agents/knowledge/package.json b/agents/knowledge/package.json new file mode 100644 index 000000000..5eb395da2 --- /dev/null +++ b/agents/knowledge/package.json @@ -0,0 +1,17 @@ +{ + "name": "agentcore-knowledge-refresh", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "refresh": "tsx refresh.ts --cli-root ../../" + }, + "dependencies": { + "ts-morph": "^24.0.0", + "yaml": "^2.4.0" + }, + "devDependencies": { + "tsx": "^4.19.0", + "typescript": "^5.5.0" + } +} diff --git a/agents/knowledge/refresh.ts b/agents/knowledge/refresh.ts new file mode 100644 index 000000000..7b73d5cfc --- /dev/null +++ b/agents/knowledge/refresh.ts @@ -0,0 +1,266 @@ +#!/usr/bin/env npx tsx +/** + * Knowledge Snapshot Refresh + * + * Generates cli-architecture-snapshot.yaml from the current codebase state. + * Run at pipeline invocation time to ensure freshness. + * + * Usage: + * npx tsx refresh.ts --cli-root ../../ + * npx tsx refresh.ts --cli-root ../../ --cdk-root /path/to/cdk-repo + * + * CDK root is optional. If not provided, checks: + * 1. AGENTCORE_CDK_ROOT env var + * 2. Sibling directory (../agentcore-l3-cdk-constructs relative to cli-root) + * 3. Skips CDK snapshot if not found (warns) + */ +import { extractCommands } from './extractors/commands.js'; +import { extractDeployFlow } from './extractors/deploy-flow.js'; +import { extractPrimitives } from './extractors/primitives.js'; +import { extractSchemaShape } from './extractors/schema.js'; +import fs from 'fs'; +import { execSync } from 'node:child_process'; +import { parseArgs } from 'node:util'; +import path from 'path'; +import { stringify } from 'yaml'; + +const SCHEMA_VERSION = 1; + +interface CliSnapshot { + schema_version: number; + generated_at: string; + commit: string; + repo: string; + primitives: ReturnType; + schema_shape: ReturnType; + deploy_flow: { file: string; steps: Record }; + commands: { verbs: ReturnType }; + iam_patterns: Record; + code_style: { rules: string[]; tui_patterns: string[] }; +} + +function getGitCommit(repoRoot: string): string { + try { + const headPath = path.join(repoRoot, '.git/HEAD'); + const head = fs.readFileSync(headPath, 'utf-8').trim(); + if (head.startsWith('ref: ')) { + const refPath = path.join(repoRoot, '.git', head.slice(5)); + if (fs.existsSync(refPath)) { + return fs.readFileSync(refPath, 'utf-8').trim().slice(0, 8); + } + } + return head.slice(0, 8); + } catch { + return 'unknown'; + } +} + +function resolveCdkRoot(cliRoot: string, explicitCdkRoot?: string): string | null { + // 1. Explicit flag + if (explicitCdkRoot) { + const resolved = path.resolve(explicitCdkRoot); + if (fs.existsSync(resolved)) return resolved; + console.warn(`[warn] --cdk-root path does not exist: ${resolved}`); + return null; + } + + // 2. Environment variable + const envCdkRoot = process.env.AGENTCORE_CDK_ROOT; + if (envCdkRoot) { + const resolved = path.resolve(envCdkRoot); + if (fs.existsSync(resolved)) return resolved; + console.warn(`[warn] AGENTCORE_CDK_ROOT path does not exist: ${resolved}`); + } + + // 3. Common sibling locations (relative to cli-root's parent) + const cliParent = path.dirname(path.resolve(cliRoot)); + const siblingPaths = [ + path.join(cliParent, 'agentcore-l3-cdk-constructs'), + path.join(cliParent, '..', 'agentcore-l3-cdk-constructs'), + ]; + + for (const candidate of siblingPaths) { + if (fs.existsSync(candidate) && fs.existsSync(path.join(candidate, 'package.json'))) { + return candidate; + } + } + + // 4. Fallback: shallow clone into a temp location + const cloneTarget = path.join(path.dirname(new URL(import.meta.url).pathname), '.cdk-clone'); + const CDK_REPO_URL = 'https://github.com/aws/agentcore-l3-cdk-constructs.git'; + + if (fs.existsSync(path.join(cloneTarget, 'package.json'))) { + console.log(`[info] Using cached CDK clone at ${cloneTarget}`); + // Pull latest + try { + execSync('git pull --ff-only', { cwd: cloneTarget, stdio: 'pipe' }); + } catch { + console.warn('[warn] Failed to update CDK clone, using cached version'); + } + return cloneTarget; + } + + console.log(`[info] CDK repo not found locally. Cloning ${CDK_REPO_URL} ...`); + try { + execSync(`git clone --depth=1 ${CDK_REPO_URL} ${cloneTarget}`, { stdio: 'pipe' }); + console.log(`[info] CDK repo cloned to ${cloneTarget}`); + return cloneTarget; + } catch (err) { + console.warn(`[warn] Failed to clone CDK repo: ${err instanceof Error ? err.message : String(err)}`); + return null; + } +} + +function generateCliSnapshot(cliRoot: string): CliSnapshot { + const resolvedRoot = path.resolve(cliRoot); + + console.log(`[info] Extracting from CLI root: ${resolvedRoot}`); + + const primitives = extractPrimitives(resolvedRoot); + console.log(`[info] Found ${primitives.length} primitives`); + + const schemaShape = extractSchemaShape(resolvedRoot); + console.log(`[info] Found ${schemaShape.agentcore_json.top_level_arrays.length} schema arrays`); + + const commands = extractCommands(resolvedRoot); + console.log(`[info] Found ${commands.length} command verbs`); + + const deployFlow = extractDeployFlow(resolvedRoot); + console.log(`[info] Found ${deployFlow.length} deploy steps`); + + const steps: Record = {}; + for (const step of deployFlow) { + steps[step.order] = step.description; + } + + return { + schema_version: SCHEMA_VERSION, + generated_at: new Date().toISOString(), + commit: getGitCommit(resolvedRoot), + repo: 'aws/agentcore-cli', + primitives, + schema_shape: schemaShape, + deploy_flow: { + file: 'src/cli/operations/deploy/', + steps, + }, + commands: { verbs: commands }, + iam_patterns: { + service_principal: 'bedrock-agentcore.amazonaws.com', + lambda_principal: 'lambda.amazonaws.com', + role_creation: 'CDK: new iam.Role({ assumedBy: new iam.ServicePrincipal(AGENTCORE_SERVICE_PRINCIPAL) })', + existing_role_support: 'executionRoleArn on agent schema allows BYO role', + partition_utility: 'src/cli/aws/partition.ts — arnPrefix(), serviceEndpoint(), dnsSuffix()', + }, + code_style: { + rules: [ + 'No inline imports', + '{ success: boolean, error?: string } for results', + 'Existing types before inline', + 'Constants in closest subdirectory', + 'Never hardcode arn:aws:', + 'Tags via TagsSchema on all resources', + ], + tui_patterns: [ + 'Screen → Flow → Wizard hook → Operation hook → Primitive', + 'MAX_CONTENT_WIDTH = 60', + "SelectList uses wrap='wrap'", + ], + }, + }; +} + +function main() { + const { values } = parseArgs({ + options: { + 'cli-root': { type: 'string', default: '../../' }, + 'cdk-root': { type: 'string' }, + }, + }); + + const cliRoot = values['cli-root']; + const resolvedCliRoot = path.resolve(cliRoot); + + if (!fs.existsSync(resolvedCliRoot)) { + console.error(`[error] CLI root not found: ${resolvedCliRoot}`); + process.exit(1); + } + + // Generate CLI snapshot + const cliSnapshot = generateCliSnapshot(cliRoot); + const outputDir = path.dirname(new URL(import.meta.url).pathname); + const cliOutputPath = path.join(outputDir, 'cli-architecture-snapshot.yaml'); + + fs.writeFileSync(cliOutputPath, stringify(cliSnapshot), 'utf-8'); + console.log(`[done] CLI snapshot written to ${cliOutputPath}`); + + // Attempt CDK snapshot + const cdkRoot = resolveCdkRoot(resolvedCliRoot, values['cdk-root']); + if (cdkRoot) { + console.log(`[info] CDK root found: ${cdkRoot}`); + // CDK snapshot is simpler — just extract construct names and exports + const cdkSnapshot = generateCdkSnapshot(cdkRoot); + const cdkOutputPath = path.join(outputDir, 'cdk-architecture-snapshot.yaml'); + fs.writeFileSync(cdkOutputPath, stringify(cdkSnapshot), 'utf-8'); + console.log(`[done] CDK snapshot written to ${cdkOutputPath}`); + } else { + console.warn('[warn] CDK root not found. Skipping CDK snapshot.'); + console.warn(' Set AGENTCORE_CDK_ROOT env var or pass --cdk-root flag.'); + } +} + +function generateCdkSnapshot(cdkRoot: string) { + const resolvedRoot = path.resolve(cdkRoot); + const constructsDir = path.join(resolvedRoot, 'src/cdk/constructs'); + + const constructs: { name: string; file: string; type: string }[] = []; + + // Scan for construct files + if (fs.existsSync(constructsDir)) { + const scanDir = (dir: string, type: string) => { + if (!fs.existsSync(dir)) return; + const files = fs.readdirSync(dir, { withFileTypes: true }); + for (const entry of files) { + if ( + entry.isFile() && + entry.name.endsWith('.ts') && + !entry.name.includes('test') && + !entry.name.includes('index') + ) { + constructs.push({ + name: entry.name.replace('.ts', ''), + file: path.relative(resolvedRoot, path.join(dir, entry.name)), + type, + }); + } + if (entry.isDirectory() && !entry.name.startsWith('__')) { + scanDir(path.join(dir, entry.name), type); + } + } + }; + + scanDir(path.join(constructsDir, 'l3'), 'l3'); + scanDir(path.join(constructsDir, 'components/primitives'), 'primitive'); + scanDir(path.join(constructsDir, 'components/mcp'), 'mcp'); + } + + // Read constants + let servicePrincipal = 'bedrock-agentcore.amazonaws.com'; + const constantsPath = path.join(resolvedRoot, 'src/cdk/constants.ts'); + if (fs.existsSync(constantsPath)) { + const content = fs.readFileSync(constantsPath, 'utf-8'); + const match = /AGENTCORE_SERVICE_PRINCIPAL\s*=\s*['"]([^'"]+)['"]/.exec(content); + if (match) servicePrincipal = match[1]; + } + + return { + schema_version: SCHEMA_VERSION, + generated_at: new Date().toISOString(), + commit: getGitCommit(resolvedRoot), + repo: 'aws/agentcore-l3-cdk-constructs', + service_principal: servicePrincipal, + constructs, + }; +} + +main(); diff --git a/agents/knowledge/tsconfig.json b/agents/knowledge/tsconfig.json new file mode 100644 index 000000000..a61727c6c --- /dev/null +++ b/agents/knowledge/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "outDir": "./dist", + "rootDir": "." + }, + "include": ["*.ts", "extractors/*.ts"] +}