@agent-native/core 0.51.5 → 0.51.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +1 -1
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/templates-meta.js +1 -1
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/coding-tools/run-code.d.ts.map +1 -1
- package/dist/coding-tools/run-code.js +435 -3
- package/dist/coding-tools/run-code.js.map +1 -1
- package/dist/provider-api/corpus-jobs-store.d.ts +95 -0
- package/dist/provider-api/corpus-jobs-store.d.ts.map +1 -0
- package/dist/provider-api/corpus-jobs-store.js +394 -0
- package/dist/provider-api/corpus-jobs-store.js.map +1 -0
- package/dist/provider-api/corpus-jobs.d.ts +146 -0
- package/dist/provider-api/corpus-jobs.d.ts.map +1 -0
- package/dist/provider-api/corpus-jobs.js +1192 -0
- package/dist/provider-api/corpus-jobs.js.map +1 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +9 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth-marketing.js +4 -4
- package/dist/server/auth-marketing.js.map +1 -1
- package/docs/content/cloneable-saas.md +1 -1
- package/docs/content/getting-started.md +1 -1
- package/docs/content/local-file-mode.md +6 -1
- package/docs/content/template-analytics.md +0 -8
- package/docs/content/template-assets.md +0 -6
- package/docs/content/template-brain.md +0 -8
- package/docs/content/template-calendar.md +0 -8
- package/docs/content/template-clips.md +0 -8
- package/docs/content/template-content.md +27 -23
- package/docs/content/template-design.md +0 -6
- package/docs/content/template-forms.md +0 -10
- package/docs/content/template-mail.md +0 -8
- package/docs/content/template-plan.md +180 -0
- package/docs/content/template-slides.md +0 -8
- package/docs/content/template-videos.md +0 -8
- package/package.json +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr-visual-recap-workflow.js","sourceRoot":"","sources":["../../src/cli/pr-visual-recap-workflow.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,4BAA4B,GACvC,o8oCAAo8oC,CAAC","sourcesContent":["/**\n * Bundled copy of .github/workflows/pr-visual-recap.yml used by\n * `agent-native recap setup`. Keep byte-identical to the source workflow.\n *\n * This file is generated from the workflow source; tests assert the exported\n * string stays in sync.\n */\n\nexport const PR_VISUAL_RECAP_WORKFLOW_YML =\n 'name: PR Visual Recap\\n\\n# Visual code review: a coding agent runs the repo\\'s visual-recap skill over the\\n# PR diff, publishes a plan, and upserts one sticky comment with a screenshot.\\n# Plain `pull_request` (NOT `pull_request_target`) so fork code never sees secrets.\\n\\non:\\n pull_request:\\n types: [opened, synchronize, reopened, ready_for_review]\\n\\npermissions:\\n contents: read\\n\\nconcurrency:\\n group: pr-visual-recap-${{ github.event.pull_request.number }}\\n cancel-in-progress: true\\n\\nenv:\\n VISUAL_RECAP_AGENT: ${{ vars.VISUAL_RECAP_AGENT || \\'claude\\' }}\\n VISUAL_RECAP_SKILL_SOURCE: ${{ vars.VISUAL_RECAP_SKILL_SOURCE || \\'auto\\' }}\\n VISUAL_RECAP_SECRET_SCAN: ${{ vars.VISUAL_RECAP_SECRET_SCAN || \\'high-confidence\\' }}\\n\\njobs:\\n gate:\\n name: Gate\\n runs-on: ubuntu-latest\\n timeout-minutes: 10\\n permissions:\\n contents: read\\n issues: write\\n pull-requests: write\\n outputs:\\n run: ${{ steps.decide.outputs.run }}\\n agent: ${{ steps.decide.outputs.agent }}\\n steps:\\n - id: decide\\n uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\\n env:\\n # Presence-only signals — never expose secret VALUES to the gate.\\n HAS_PLAN: ${{ secrets.PLAN_RECAP_TOKEN != \\'\\' }}\\n HAS_ANTHROPIC: ${{ secrets.ANTHROPIC_API_KEY != \\'\\' }}\\n HAS_OPENAI: ${{ secrets.OPENAI_API_KEY != \\'\\' }}\\n AGENT: ${{ env.VISUAL_RECAP_AGENT }}\\n VISUAL_RECAP_MODEL: ${{ vars.VISUAL_RECAP_MODEL }}\\n VISUAL_RECAP_SKILL_SOURCE: ${{ env.VISUAL_RECAP_SKILL_SOURCE }}\\n HEAD_SHA: ${{ github.event.pull_request.head.sha }}\\n with:\\n script: |\\n const pr = context.payload.pull_request;\\n const reasons = [];\\n\\n if (!pr) reasons.push(\\'no pull_request payload\\');\\n if (pr && pr.draft) reasons.push(\\'draft PR\\');\\n\\n // Fork PRs only receive repo secrets when the org/repo opts into\\n // GitHub\\'s \"Send secrets to workflows from pull requests\" setting\\n // (common in private orgs that use forks heavily). Gate on secret\\n // availability, not fork-ness: run on forks that have the token,\\n // and skip — with an actionable hint — those that don\\'t.\\n const headRepo = pr && pr.head && pr.head.repo && pr.head.repo.full_name;\\n const isFork = !!(pr && headRepo && headRepo !== process.env.GITHUB_REPOSITORY);\\n if (isFork && process.env.HAS_PLAN !== \\'true\\') {\\n reasons.push(`fork PR (${headRepo}) without secret access — enable \"Send secrets to workflows from pull requests\" (and write tokens) in the repo/org Actions settings to run recaps on forks`);\\n }\\n\\n const login = (pr && pr.user && pr.user.login || \\'\\').toLowerCase();\\n const botAuthors = [\\'dependabot[bot]\\', \\'dependabot\\', \\'renovate[bot]\\', \\'renovate\\'];\\n if (botAuthors.includes(login)) reasons.push(`bot author (${login})`);\\n if (pr && pr.user && pr.user.type === \\'Bot\\') reasons.push(\\'bot author (type=Bot)\\');\\n\\n if (!isFork && process.env.HAS_PLAN !== \\'true\\') reasons.push(\\'PLAN_RECAP_TOKEN not configured\\');\\n\\n // Normalize + validate the agent so a mis-cased value can\\'t pass the\\n // gate and then match neither agent step below.\\n const agent = (process.env.AGENT || \\'claude\\').toLowerCase();\\n if (agent !== \\'claude\\' && agent !== \\'codex\\') {\\n reasons.push(`unsupported VISUAL_RECAP_AGENT \"${process.env.AGENT}\" (expected \"claude\" or \"codex\")`);\\n } else if (agent === \\'codex\\') {\\n if (process.env.HAS_OPENAI !== \\'true\\') reasons.push(\\'OPENAI_API_KEY not configured (codex backend)\\');\\n } else {\\n if (process.env.HAS_ANTHROPIC !== \\'true\\') reasons.push(\\'ANTHROPIC_API_KEY not configured (claude backend)\\');\\n }\\n\\n // Validate the model before it reaches the agent CLI.\\n const model = process.env.VISUAL_RECAP_MODEL || \\'\\';\\n if (model && !/^[a-zA-Z0-9._-]{1,80}$/.test(model)) {\\n reasons.push(`invalid VISUAL_RECAP_MODEL value (must match [a-zA-Z0-9._-]{1,80})`);\\n }\\n\\n const skillSource = (process.env.VISUAL_RECAP_SKILL_SOURCE || \\'auto\\').toLowerCase();\\n if (![\\'auto\\', \\'latest\\', \\'repo\\'].includes(skillSource)) {\\n reasons.push(\\'invalid VISUAL_RECAP_SKILL_SOURCE value (expected \"auto\", \"latest\", or \"repo\")\\');\\n }\\n const usesRepoSkill = skillSource === \\'repo\\';\\n\\n // Self-modifying guard, evaluated in the trusted gate (runs NO\\n // PR-checked-out code): skip the ENTIRE job if the PR touches the\\n // repo-pinned skill instructions or any agent config the runner\\n // loads, so a PR can\\'t rewrite what the agent loads and exfiltrate\\n // secrets. With the default bundled skill source, visual skill and\\n // recap workflow files are reviewed content, not instructions loaded\\n // by the runner.\\n if (pr) {\\n try {\\n const files = await github.paginate(github.rest.pulls.listFiles, {\\n owner: context.repo.owner,\\n repo: context.repo.repo,\\n pull_number: pr.number,\\n per_page: 100,\\n });\\n const isSensitive = (p) =>\\n (usesRepoSkill && /(^|\\\\/)skills\\\\/visual-(recap|plan|plans)\\\\//.test(p)) ||\\n /(^|\\\\/)\\\\.claude\\\\//.test(p) ||\\n /(^|\\\\/)CLAUDE\\\\.md$/.test(p) ||\\n /(^|\\\\/)AGENTS\\\\.md$/.test(p) ||\\n /(^|\\\\/)\\\\.mcp\\\\.json$/.test(p);\\n const hits = files.map((f) => f.filename).filter(isSensitive);\\n if (hits.length) {\\n reasons.push(`PR modifies recap-control files (${hits.slice(0, 3).join(\\', \\')}${hits.length > 3 ? \\', …\\' : \\'\\'}) — skipping so untrusted PR code never runs with secrets`);\\n }\\n } catch (e) {\\n // Fail closed: if the file list can\\'t be read, skip.\\n reasons.push(`could not list PR files for the self-modifying guard (${e.message}); skipping to be safe`);\\n }\\n }\\n\\n const run = reasons.length === 0;\\n core.setOutput(\\'run\\', run ? \\'true\\' : \\'false\\');\\n core.setOutput(\\'agent\\', agent);\\n core.info(run ? `Visual recap will run (${agent}).` : `Visual recap skipped: ${reasons.join(\\'; \\')}`);\\n\\n // When skipping, upsert a sticky recap comment with a short skip\\n // line so the PR always explains why the recap job did not run.\\n if (!run && pr) {\\n try {\\n const MARKER = \\'<!-- pr-visual-recap -->\\';\\n const { data: comments } = await github.rest.issues.listComments({\\n owner: context.repo.owner,\\n repo: context.repo.repo,\\n issue_number: pr.number,\\n per_page: 100,\\n });\\n const existing = comments.find(\\n (c) => c.user && c.user.type === \\'Bot\\' && c.body && c.body.includes(MARKER)\\n );\\n const headShort = (process.env.HEAD_SHA || \\'\\').slice(0, 7);\\n const shaRef = headShort ? `\\\\`${headShort}\\\\`` : \\'latest push\\';\\n const primaryReason = reasons.filter(\\n (r) => !r.startsWith(\\'could not list PR files for the self-modifying guard\\')\\n )[0] || reasons[0] || \\'skipped\\';\\n const skipLine = `_Recap skipped for ${shaRef}: ${primaryReason}._`;\\n const baseBody = `${MARKER}\\\\n### Visual recap — skipped\\\\n\\\\nThe visual recap job did not run for this pull request. This is informational only and does **not** block the PR.`;\\n const withoutPrev = (existing && existing.body ? existing.body : baseBody)\\n .split(\\'\\\\n\\')\\n .filter((l) => !/_Recap skipped for .+_$/.test(l.trim()))\\n .join(\\'\\\\n\\')\\n .trimEnd();\\n const updatedBody = `${withoutPrev}\\\\n\\\\n${skipLine}`;\\n if (existing) {\\n await github.rest.issues.updateComment({\\n owner: context.repo.owner,\\n repo: context.repo.repo,\\n comment_id: existing.id,\\n body: updatedBody,\\n });\\n } else {\\n await github.rest.issues.createComment({\\n owner: context.repo.owner,\\n repo: context.repo.repo,\\n issue_number: pr.number,\\n body: updatedBody,\\n });\\n }\\n } catch (e) {\\n core.warning(`Could not update recap skip comment: ${e.message}`);\\n }\\n }\\n\\n recap:\\n name: Generate visual recap\\n needs: gate\\n if: needs.gate.outputs.run == \\'true\\'\\n runs-on: ubuntu-latest\\n timeout-minutes: 30\\n permissions:\\n actions: write\\n checks: write\\n contents: read\\n issues: write\\n pull-requests: write\\n env:\\n PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL || \\'https://plan.agent-native.com\\' }}\\n PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }}\\n GH_TOKEN: ${{ github.token }}\\n PR_NUMBER: ${{ github.event.pull_request.number }}\\n HEAD_SHA: ${{ github.event.pull_request.head.sha }}\\n VISUAL_RECAP_MODEL: ${{ vars.VISUAL_RECAP_MODEL }}\\n VISUAL_RECAP_REASONING: ${{ vars.VISUAL_RECAP_REASONING }}\\n VISUAL_RECAP_SKILL_SOURCE: ${{ vars.VISUAL_RECAP_SKILL_SOURCE || \\'auto\\' }}\\n VISUAL_RECAP_SECRET_SCAN: ${{ vars.VISUAL_RECAP_SECRET_SCAN || \\'high-confidence\\' }}\\n steps:\\n - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3\\n with:\\n fetch-depth: 0\\n # This job runs an agent over untrusted PR diff; don\\'t leave the token\\n # in .git/config (it uses GH_TOKEN for gh API calls, never git push).\\n persist-credentials: false\\n\\n # Dogfood trusted base-branch source inside this monorepo, else install the\\n # published package once. Never execute PR-head recap CLI code.\\n - name: Resolve recap CLI\\n id: cli\\n env:\\n # Optional: pin the consumer CLI version (e.g. \"1.2.3\"). Defaults to\\n # \"latest\" when unset. Set via repository variable RECAP_CLI_VERSION.\\n RECAP_CLI_VERSION: ${{ vars.RECAP_CLI_VERSION || \\'latest\\' }}\\n run: |\\n if [ \"$GITHUB_REPOSITORY\" = \"BuilderIO/agent-native\" ] && [ -f packages/core/src/cli/index.ts ]; then\\n echo \"local=true\" >> \"$GITHUB_OUTPUT\"\\n else\\n echo \"local=false\" >> \"$GITHUB_OUTPUT\"\\n fi\\n\\n - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3\\n if: steps.cli.outputs.local == \\'true\\'\\n with:\\n ref: ${{ github.event.pull_request.base.sha }}\\n path: .recap-cli-source\\n fetch-depth: 1\\n persist-credentials: false\\n\\n - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8\\n if: steps.cli.outputs.local == \\'true\\'\\n\\n - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0\\n with:\\n node-version: \"22\"\\n cache: ${{ steps.cli.outputs.local == \\'true\\' && \\'pnpm\\' || \\'\\' }}\\n\\n - name: Install trusted workspace recap CLI\\n if: steps.cli.outputs.local == \\'true\\'\\n working-directory: .recap-cli-source\\n run: |\\n set -euo pipefail\\n pnpm install --frozen-lockfile --ignore-scripts\\n echo \"RECAP_CLI=$PWD/node_modules/.bin/tsx $PWD/packages/core/src/cli/index.ts\" >> \"$GITHUB_ENV\"\\n echo \"RECAP_PLAYWRIGHT=$PWD/node_modules/.bin/playwright\" >> \"$GITHUB_ENV\"\\n\\n - name: Install published recap CLI\\n if: steps.cli.outputs.local != \\'true\\'\\n env:\\n RECAP_CLI_VERSION: ${{ vars.RECAP_CLI_VERSION || \\'latest\\' }}\\n run: |\\n set -euo pipefail\\n VERSION=\"$RECAP_CLI_VERSION\"\\n if [ \"$VERSION\" = \"latest\" ]; then\\n VERSION=\"$(npm view @agent-native/core@latest version)\"\\n fi\\n for attempt in 1 2 3; do\\n if npm install --prefix \"$RUNNER_TEMP/recap-cli\" --no-audit --no-fund \"@agent-native/core@$VERSION\"; then\\n break\\n fi\\n if [ \"$attempt\" = \"3\" ]; then exit 1; fi\\n sleep $((attempt * 10))\\n done\\n echo \"RECAP_CLI=$RUNNER_TEMP/recap-cli/node_modules/.bin/agent-native\" >> \"$GITHUB_ENV\"\\n echo \"RECAP_PLAYWRIGHT=$RUNNER_TEMP/recap-cli/node_modules/.bin/playwright\" >> \"$GITHUB_ENV\"\\n\\n - name: Start visual recap check\\n id: recap_check\\n continue-on-error: true\\n run: |\\n set -uo pipefail\\n $RECAP_CLI recap check start --sha \"$HEAD_SHA\" --workflow-url \"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID\"\\n\\n - name: Collect bounded diff\\n id: diff\\n env:\\n BASE_SHA: ${{ github.event.pull_request.base.sha }}\\n run: |\\n set -euo pipefail\\n $RECAP_CLI recap collect-diff --base \"$BASE_SHA\" --head \"$HEAD_SHA\" --out recap.diff --stat recap.stat\\n\\n - name: Probe plan-app auth\\n id: auth_probe\\n if: steps.diff.outputs.tiny != \\'true\\'\\n continue-on-error: true\\n run: |\\n set -uo pipefail\\n # Hit the plan app\\'s action surface with the publish token. A 401 means\\n # the token is expired/revoked; surface it in the sticky comment so the\\n # repo owner knows to re-mint it instead of seeing a generic failure.\\n HTTP_STATUS=$(node -e \\'\\n const https = require(\"https\");\\n const url = new URL(\"/_agent-native/actions/record-recap-usage\", process.env.PLAN_RECAP_APP_URL || \"https://plan.agent-native.com\");\\n const req = https.request(url, { method: \"POST\", headers: { \"authorization\": \"Bearer \" + process.env.PLAN_RECAP_TOKEN, \"content-type\": \"application/json\" }, timeout: 8000 }, (res) => { process.stdout.write(String(res.statusCode)); req.destroy(); });\\n req.on(\"error\", () => process.stdout.write(\"0\"));\\n req.end(JSON.stringify({ planId: \"__probe__\" }));\\n \\' 2>/dev/null || echo \"0\")\\n if [ \"$HTTP_STATUS\" = \"401\" ]; then\\n echo \"auth_failed=true\" >> \"$GITHUB_OUTPUT\"\\n else\\n echo \"auth_failed=false\" >> \"$GITHUB_OUTPUT\"\\n fi\\n\\n - name: Probe plan-app route health\\n id: route_health\\n if: steps.diff.outputs.tiny != \\'true\\'\\n continue-on-error: true\\n run: |\\n set -uo pipefail\\n # Pre-publish health gate: confirm the plan app\\'s recap action routes\\n # are actually deployed BEFORE the agent runs. A 404 from\\n # create-visual-recap (POST) or get-plan-blocks (GET) means the\\n # plan-app deploy has not propagated yet (the client is ahead of the\\n # deployed server). Say that plainly here instead of letting the agent\\n # run and then fail confusingly at publish time. A 401 or 200 is\\n # healthy — the route exists, it just rejected/accepted the probe.\\n probe_status() {\\n ROUTE=\"$1\" METHOD=\"$2\" node -e \\'\\n const https = require(\"https\");\\n const base = process.env.PLAN_RECAP_APP_URL || \"https://plan.agent-native.com\";\\n const url = new URL(process.env.ROUTE, base);\\n if (process.env.METHOD === \"GET\") url.searchParams.set(\"format\", \"reference\");\\n const req = https.request(url, { method: process.env.METHOD, headers: { \"authorization\": \"Bearer \" + (process.env.PLAN_RECAP_TOKEN || \"\"), \"content-type\": \"application/json\" }, timeout: 8000 }, (res) => { process.stdout.write(String(res.statusCode)); req.destroy(); });\\n req.on(\"error\", () => process.stdout.write(\"0\"));\\n req.on(\"timeout\", () => { process.stdout.write(\"0\"); req.destroy(); });\\n if (process.env.METHOD === \"POST\") { req.end(JSON.stringify({ __probe__: true })); } else { req.end(); }\\n \\' 2>/dev/null || echo \"0\"\\n }\\n CREATE_STATUS=\"$(probe_status /_agent-native/actions/create-visual-recap POST)\"\\n BLOCKS_STATUS=\"$(probe_status /_agent-native/actions/get-plan-blocks GET)\"\\n REASON=\"\"\\n if [ \"$CREATE_STATUS\" = \"404\" ] || [ \"$BLOCKS_STATUS\" = \"404\" ]; then\\n REASON=\"Plan app routes return 404 — deploy not yet propagated (create-visual-recap: $CREATE_STATUS, get-plan-blocks: $BLOCKS_STATUS). The plan-app client is ahead of the deployed server; re-run once the deploy finishes propagating.\"\\n echo \"::error::$REASON\"\\n echo \"unhealthy=true\" >> \"$GITHUB_OUTPUT\"\\n else\\n echo \"unhealthy=false\" >> \"$GITHUB_OUTPUT\"\\n fi\\n {\\n echo \\'reason<<__RECAP_ROUTE_HEALTH_EOF__\\'\\n echo \"$REASON\"\\n echo \\'__RECAP_ROUTE_HEALTH_EOF__\\'\\n } >> \"$GITHUB_OUTPUT\"\\n\\n - name: Secret scan\\n id: scan\\n if: steps.diff.outputs.tiny != \\'true\\'\\n run: |\\n set -uo pipefail\\n # Fail CLOSED: a scanner error or invalid JSON suppresses the diff so a\\n # credential-bearing diff is never handed to the agent / plan service.\\n if ! SCAN_JSON=\"$($RECAP_CLI recap scan --diff recap.diff --mode \"$VISUAL_RECAP_SECRET_SCAN\")\"; then\\n SCAN_JSON=\\'{\"suppressed\":true,\"reason\":\"secret scan failed to run; failing closed\"}\\'\\n fi\\n {\\n echo \\'json<<__RECAP_SCAN_EOF__\\'\\n echo \"$SCAN_JSON\"\\n echo \\'__RECAP_SCAN_EOF__\\'\\n } >> \"$GITHUB_OUTPUT\"\\n SUPPRESSED=$(node -e \\'try{process.stdout.write(JSON.parse(process.argv[1]).suppressed?\"true\":\"false\")}catch{process.stdout.write(\"true\")}\\' \"$SCAN_JSON\")\\n echo \"suppressed=$SUPPRESSED\" >> \"$GITHUB_OUTPUT\"\\n\\n - name: Read previous plan id\\n id: prev\\n continue-on-error: true\\n run: |\\n set -euo pipefail\\n PLAN_ID=\"$($RECAP_CLI recap comment find-plan-id --repo \"$GITHUB_REPOSITORY\" --issue \"$PR_NUMBER\" --token \"$GH_TOKEN\")\"\\n echo \"plan_id=$PLAN_ID\" >> \"$GITHUB_OUTPUT\"\\n\\n - name: Fetch plan block reference\\n id: block_reference\\n if: steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n continue-on-error: true\\n run: |\\n set -uo pipefail\\n if $RECAP_CLI recap block-reference --app-url \"$PLAN_RECAP_APP_URL\" --out recap-blocks.md; then\\n echo \"ok=true\" >> \"$GITHUB_OUTPUT\"\\n else\\n echo \"ok=false\" >> \"$GITHUB_OUTPUT\"\\n {\\n echo \\'summary<<__RECAP_BLOCK_REFERENCE_EOF__\\'\\n echo \"Could not fetch the live plan block reference; the agent will fall back to bundled visual-recap instructions and the publisher will validate the final MDX.\"\\n echo \\'__RECAP_BLOCK_REFERENCE_EOF__\\'\\n } >> \"$GITHUB_OUTPUT\"\\n cat > recap-blocks.md <<\\'EOF\\'\\n Live plan block reference unavailable. Follow the bundled visual-recap skill and author conservative MDX; the deterministic publisher will validate the source before posting.\\n EOF\\n fi\\n\\n - name: Build recap prompt\\n id: prompt\\n if: steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n env:\\n # Pass step outputs via env, NOT ${{ }} interpolation into the run body:\\n # the prev plan id is parsed from a PR comment and could inject shell.\\n PREV_PLAN_ID: ${{ steps.prev.outputs.plan_id }}\\n DIFF_HUGE: ${{ steps.diff.outputs.huge }}\\n IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }}\\n run: |\\n set -euo pipefail\\n ARGS=(--diff recap.diff --stat recap.stat --block-reference recap-blocks.md --pr \"$PR_NUMBER\" --repo \"$GITHUB_REPOSITORY\" --head \"$HEAD_SHA\" --app-url \"$PLAN_RECAP_APP_URL\" --skill-source \"$VISUAL_RECAP_SKILL_SOURCE\" --out recap-prompt.md)\\n if [ \"${DIFF_HUGE:-}\" = \"true\" ]; then ARGS+=(--huge); fi\\n if [ \"${IS_FORK:-}\" = \"true\" ]; then ARGS+=(--fork-pr true); fi\\n if [ -n \"${PREV_PLAN_ID:-}\" ]; then ARGS+=(--prev-plan-id \"$PREV_PLAN_ID\"); fi\\n $RECAP_CLI recap build-prompt \"${ARGS[@]}\"\\n\\n - name: Run agent (Claude Code)\\n id: claude\\n if: needs.gate.outputs.agent == \\'claude\\' && steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n continue-on-error: true\\n env:\\n ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}\\n run: |\\n set -uo pipefail\\n CLAUDE_ALLOWED_TOOLS=\"Read,Write,Bash(git diff:*)\"\\n CLAUDE_ARGS=(-p \"$(cat recap-prompt.md)\" --allowedTools \"$CLAUDE_ALLOWED_TOOLS\" --permission-mode dontAsk --output-format json)\\n if [ -n \"${VISUAL_RECAP_MODEL:-}\" ]; then CLAUDE_ARGS+=(--model \"$VISUAL_RECAP_MODEL\"); fi\\n rm -f recap-source.json recap-url.txt recap-url-reason.txt claude-result.json claude-stderr.log\\n run_claude() {\\n set +e\\n npx -y @anthropic-ai/claude-code@2 \"${CLAUDE_ARGS[@]}\" > claude-result.json 2> claude-stderr.log\\n CLAUDE_STATUS=\"$?\"\\n set -e\\n echo \"$CLAUDE_STATUS\" > claude-exit-code.txt\\n }\\n run_claude\\n\\n - name: Run agent (Codex)\\n id: codex\\n if: needs.gate.outputs.agent == \\'codex\\' && steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n continue-on-error: true\\n env:\\n OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\\n run: |\\n set -uo pipefail\\n # `codex login` writes ~/.codex/auth.json (the bare env var is dropped on\\n # the gpt-5.5 wss transport); stdin keeps the key out of process args.\\n printenv OPENAI_API_KEY | npx -y @openai/codex@0 login --with-api-key || true\\n # The runner is itself an ephemeral sandbox; bypass Codex\\'s own sandbox\\n # (bubblewrap can\\'t init here) and approval gate (cancels the MCP write).\\n CODEX_ARGS=(exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check)\\n if [ -n \"${VISUAL_RECAP_MODEL:-}\" ]; then CODEX_ARGS+=(--model \"$VISUAL_RECAP_MODEL\"); fi\\n # Validate reasoning against the enum before embedding it in the TOML override.\\n case \"${VISUAL_RECAP_REASONING:-}\" in\\n none|minimal|low|medium|high|xhigh)\\n CODEX_ARGS+=(-c \"model_reasoning_effort=\\\\\"$VISUAL_RECAP_REASONING\\\\\"\") ;;\\n \"\") ;;\\n *) echo \"Ignoring invalid VISUAL_RECAP_REASONING: $VISUAL_RECAP_REASONING\" ;;\\n esac\\n rm -f recap-source.json recap-url.txt recap-url-reason.txt codex-events.jsonl codex-stderr.log\\n run_codex() {\\n set +e\\n npx -y @openai/codex@0 \"${CODEX_ARGS[@]}\" --json \"$(cat recap-prompt.md)\" 2> codex-stderr.log | tee codex-events.jsonl\\n CODEX_STATUS=\"${PIPESTATUS[0]}\"\\n set -e\\n echo \"$CODEX_STATUS\" > codex-exit-code.txt\\n }\\n run_codex\\n\\n - name: Publish recap source\\n id: publish\\n if: steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n continue-on-error: true\\n env:\\n PREV_PLAN_ID: ${{ steps.prev.outputs.plan_id }}\\n run: |\\n set -uo pipefail\\n ARGS=(--source recap-source.json --out recap-url.txt --repo \"$GITHUB_REPOSITORY\" --pr \"$PR_NUMBER\" --app-url \"$PLAN_RECAP_APP_URL\" --token \"$PLAN_RECAP_TOKEN\")\\n if [ -n \"${PREV_PLAN_ID:-}\" ]; then ARGS+=(--prev-plan-id \"$PREV_PLAN_ID\"); fi\\n $RECAP_CLI recap publish \"${ARGS[@]}\"\\n\\n - name: Read plan URL\\n id: url\\n if: steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n run: |\\n set -uo pipefail\\n PLAN_URL=\"\"\\n URL_REASON=\"\"\\n if [ -f recap-url.txt ]; then\\n PLAN_URL=\"$(tr -d \\'\\\\r\\\\n\\' < recap-url.txt | tr -d \\' \\')\"\\n elif [ -f recap-url-reason.txt ]; then\\n URL_REASON=\"$(cat recap-url-reason.txt)\"\\n else\\n URL_REASON=\"recap-url.txt was not created.\"\\n fi\\n # recap-url.txt is agent-written -> untrusted. Rebuild a canonical\\n # recap URL from the trusted app base and a strictly validated plan id,\\n # preserving path-prefixed self-hosted mounts.\\n if [ -z \"$URL_REASON\" ]; then\\n URL_RESULT=$(PLAN_URL=\"$PLAN_URL\" node <<\\'NODE\\'\\n const emit = (value) => process.stdout.write(JSON.stringify(value));\\n try {\\n const raw = process.env.PLAN_URL || \"\";\\n if (!raw) {\\n emit({ url: \"\", reason: \"recap-url.txt was empty\" });\\n process.exit(0);\\n }\\n const trusted = new URL(process.env.PLAN_RECAP_APP_URL || \"https://plan.agent-native.com\");\\n const parsed = /^https?:\\\\/\\\\//i.test(raw)\\n ? new URL(raw)\\n : new URL(raw, trusted);\\n if (parsed.origin !== trusted.origin) {\\n emit({ url: \"\", reason: `recap-url.txt points at ${parsed.origin}, expected ${trusted.origin}` });\\n process.exit(0);\\n }\\n\\n const base = trusted.pathname.replace(/\\\\/$/, \"\");\\n const paths = [parsed.pathname];\\n if (base && parsed.pathname.startsWith(`${base}/`)) {\\n paths.push(parsed.pathname.slice(base.length) || \"/\");\\n }\\n\\n for (const path of paths) {\\n const match = path.match(/^\\\\/(?:plans|recaps)\\\\/([A-Za-z0-9_-]+)\\\\/?$/);\\n if (match) {\\n emit({ url: `${trusted.origin}${base}/recaps/${match[1]}`, reason: \"\" });\\n process.exit(0);\\n }\\n }\\n emit({ url: \"\", reason: \"recap-url.txt did not contain a valid /plans/<id> or /recaps/<id> URL for the configured plan app\" });\\n } catch {\\n emit({ url: \"\", reason: \"recap-url.txt was not a valid URL or recap path\" });\\n }\\n NODE\\n )\\n CANONICAL_URL=$(node -e \\'try{process.stdout.write(JSON.parse(process.argv[1]).url||\"\")}catch{process.stdout.write(\"\")}\\' \"$URL_RESULT\")\\n URL_REASON=$(node -e \\'try{process.stdout.write(JSON.parse(process.argv[1]).reason||\"\")}catch{process.stdout.write(\"recap-url.txt URL validation failed\")}\\' \"$URL_RESULT\")\\n else\\n CANONICAL_URL=\"\"\\n fi\\n if [ -n \"$CANONICAL_URL\" ]; then\\n echo \"plan_url=$CANONICAL_URL\" >> \"$GITHUB_OUTPUT\"; echo \"ok=true\" >> \"$GITHUB_OUTPUT\"\\n else\\n echo \"plan_url=\" >> \"$GITHUB_OUTPUT\"; echo \"ok=false\" >> \"$GITHUB_OUTPUT\"\\n fi\\n {\\n echo \\'reason<<__RECAP_URL_REASON_EOF__\\'\\n echo \"$URL_REASON\"\\n echo \\'__RECAP_URL_REASON_EOF__\\'\\n } >> \"$GITHUB_OUTPUT\"\\n\\n - name: Summarize agent failure\\n id: agent_summary\\n if: steps.url.outputs.ok != \\'true\\' && steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n continue-on-error: true\\n env:\\n RECAP_AGENT: ${{ needs.gate.outputs.agent }}\\n RECAP_BLOCK_REFERENCE_SUMMARY: ${{ steps.block_reference.outputs.summary }}\\n RECAP_PUBLISH_REASON: ${{ steps.publish.outputs.reason }}\\n run: |\\n set -uo pipefail\\n if [ -n \"${RECAP_BLOCK_REFERENCE_SUMMARY:-}\" ]; then\\n {\\n echo \\'summary<<__RECAP_BLOCK_REFERENCE_SUMMARY_EOF__\\'\\n echo \"$RECAP_BLOCK_REFERENCE_SUMMARY\"\\n echo \\'__RECAP_BLOCK_REFERENCE_SUMMARY_EOF__\\'\\n } >> \"$GITHUB_OUTPUT\"\\n node -e \\'process.stdout.write(JSON.stringify({ ok: true, summary: process.env.RECAP_BLOCK_REFERENCE_SUMMARY || \"\" }) + \"\\\\n\")\\'\\n exit 0\\n fi\\n if [ -n \"${RECAP_PUBLISH_REASON:-}\" ]; then\\n {\\n echo \\'summary<<__RECAP_PUBLISH_SUMMARY_EOF__\\'\\n echo \"$RECAP_PUBLISH_REASON\"\\n echo \\'__RECAP_PUBLISH_SUMMARY_EOF__\\'\\n } >> \"$GITHUB_OUTPUT\"\\n node -e \\'process.stdout.write(JSON.stringify({ ok: true, summary: process.env.RECAP_PUBLISH_REASON || \"\" }) + \"\\\\n\")\\'\\n exit 0\\n fi\\n RESULT=claude-result.json\\n STDERR=claude-stderr.log\\n EXIT_CODE=claude-exit-code.txt\\n if [ \"$RECAP_AGENT\" = \"codex\" ]; then\\n RESULT=codex-events.jsonl\\n STDERR=codex-stderr.log\\n EXIT_CODE=codex-exit-code.txt\\n fi\\n $RECAP_CLI recap agent-summary --agent \"$RECAP_AGENT\" --result-file \"$RESULT\" --stderr-file \"$STDERR\" --exit-code-file \"$EXIT_CODE\" || true\\n\\n - name: Attach usage\\n if: steps.url.outputs.ok == \\'true\\'\\n continue-on-error: true\\n env:\\n PLAN_URL: ${{ steps.url.outputs.plan_url }}\\n # Use the gate-normalized agent so \"Codex\" still selects the right file.\\n RECAP_AGENT: ${{ needs.gate.outputs.agent }}\\n run: |\\n set -uo pipefail\\n RESULT=claude-result.json\\n if [ \"$RECAP_AGENT\" = \"codex\" ]; then RESULT=codex-events.jsonl; fi\\n if [ -f \"$RESULT\" ]; then $RECAP_CLI recap usage --plan-url \"$PLAN_URL\" --agent \"$RECAP_AGENT\" --result-file \"$RESULT\" --model \"${VISUAL_RECAP_MODEL:-}\" --app-url \"$PLAN_RECAP_APP_URL\" --token \"$PLAN_RECAP_TOKEN\" || true; fi\\n\\n - name: Cache Playwright browsers\\n if: steps.url.outputs.ok == \\'true\\'\\n uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3\\n with:\\n path: ~/.cache/ms-playwright\\n key: playwright-1-${{ runner.os }}\\n\\n - name: Screenshot + upload\\n id: shot\\n if: steps.url.outputs.ok == \\'true\\'\\n continue-on-error: true\\n env:\\n # recap-url.txt is untrusted agent output; pass via env, never ${{ }}.\\n PLAN_URL: ${{ steps.url.outputs.plan_url }}\\n run: |\\n set -uo pipefail\\n if [ -n \"${RECAP_PLAYWRIGHT:-}\" ] && [ -x \"$RECAP_PLAYWRIGHT\" ]; then\\n \"$RECAP_PLAYWRIGHT\" install --with-deps chromium || true\\n elif command -v pnpm >/dev/null 2>&1; then\\n pnpm exec playwright install --with-deps chromium 2>/dev/null || npx -y playwright@1 install --with-deps chromium || true\\n else\\n npx -y playwright@1 install --with-deps chromium || true\\n fi\\n LIGHT_SHOT_JSON=\"$($RECAP_CLI recap shot --url \"$PLAN_URL\" --token \"$PLAN_RECAP_TOKEN\" --app-url \"$PLAN_RECAP_APP_URL\" --out recap.png --theme light || echo \\'{}\\')\"\\n DARK_SHOT_JSON=\"$($RECAP_CLI recap shot --url \"$PLAN_URL\" --token \"$PLAN_RECAP_TOKEN\" --app-url \"$PLAN_RECAP_APP_URL\" --out recap-dark.png --theme dark || echo \\'{}\\')\"\\n for SHOT_LABEL in light dark; do\\n if [ \"$SHOT_LABEL\" = \"light\" ]; then SHOT_JSON=\"$LIGHT_SHOT_JSON\"; else SHOT_JSON=\"$DARK_SHOT_JSON\"; fi\\n SHOT_LABEL=\"$SHOT_LABEL\" SHOT_JSON=\"$SHOT_JSON\" node -e \\'const label = process.env.SHOT_LABEL || \"shot\"; let parsed = {}; try { parsed = JSON.parse(process.env.SHOT_JSON || \"{}\"); } catch { parsed = { ok: false, reason: \"invalid shot JSON\" }; } const summary = { ok: parsed.ok === true, imageUrl: parsed.imageUrl ? \"[present]\" : \"\", out: typeof parsed.out === \"string\" ? parsed.out : \"\", reason: typeof parsed.reason === \"string\" ? parsed.reason.slice(0, 500) : \"\" }; console.log(`[recap shot] ${label}: ${JSON.stringify(summary)}`);\\'\\n done\\n IMAGE_URL=$(node -e \\'try{process.stdout.write(JSON.parse(process.argv[1]).imageUrl||\"\")}catch{process.stdout.write(\"\")}\\' \"$LIGHT_SHOT_JSON\")\\n DARK_IMAGE_URL=$(node -e \\'try{process.stdout.write(JSON.parse(process.argv[1]).imageUrl||\"\")}catch{process.stdout.write(\"\")}\\' \"$DARK_SHOT_JSON\")\\n if [ -z \"$IMAGE_URL\" ] && [ -z \"$DARK_IMAGE_URL\" ]; then\\n echo \"::warning::Visual recap screenshot unavailable; posting link-only recap comment.\"\\n fi\\n echo \"image_url=$IMAGE_URL\" >> \"$GITHUB_OUTPUT\"\\n echo \"light_image_url=$IMAGE_URL\" >> \"$GITHUB_OUTPUT\"\\n echo \"dark_image_url=$DARK_IMAGE_URL\" >> \"$GITHUB_OUTPUT\"\\n if [ -f recap.png ] || [ -f recap-dark.png ]; then echo \"captured=true\" >> \"$GITHUB_OUTPUT\"; else echo \"captured=false\" >> \"$GITHUB_OUTPUT\"; fi\\n\\n - name: Upload recap screenshot artifact\\n if: steps.shot.outputs.captured == \\'true\\'\\n uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\\n with:\\n name: pr-visual-recap-${{ github.event.pull_request.number }}\\n path: |\\n recap.png\\n recap-dark.png\\n if-no-files-found: ignore\\n retention-days: 14\\n\\n - name: Upload recap source artifact\\n if: always() && !cancelled()\\n uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\\n with:\\n # The agent-authored recap-source.json is the only window into WHAT the\\n # agent emitted when a publish fails (no plan URL). The sticky comment\\n # only shows the screenshot, so without this artifact a failed recap is\\n # undebuggable. Uploaded on success and failure; tolerant when absent.\\n name: pr-visual-recap-source-${{ github.event.pull_request.number }}\\n path: recap-source.json\\n if-no-files-found: ignore\\n retention-days: 14\\n\\n - name: Upsert sticky comment\\n if: always() && !cancelled()\\n continue-on-error: true\\n env:\\n PLAN_URL: ${{ steps.url.outputs.plan_url }}\\n RECAP_IMAGE_URL: ${{ steps.shot.outputs.image_url }}\\n RECAP_LIGHT_IMAGE_URL: ${{ steps.shot.outputs.light_image_url }}\\n RECAP_DARK_IMAGE_URL: ${{ steps.shot.outputs.dark_image_url }}\\n SUPPRESSED: ${{ steps.scan.outputs.suppressed }}\\n SUPPRESSED_JSON: ${{ steps.scan.outputs.json }}\\n DIFF_HUGE: ${{ steps.diff.outputs.huge }}\\n DIFF_TINY: ${{ steps.diff.outputs.tiny }}\\n PREV_PLAN_ID: ${{ steps.prev.outputs.plan_id }}\\n RECAP_AUTH_FAILED: ${{ steps.auth_probe.outputs.auth_failed }}\\n RECAP_AGENT_SUMMARY: ${{ steps.agent_summary.outputs.summary }}\\n # Prefer the route-health diagnostic when the plan app routes are not\\n # yet deployed so the comment explains the 404 instead of a generic\\n # \"recap-url.txt was not created\" message.\\n RECAP_URL_REASON: ${{ steps.route_health.outputs.reason || steps.url.outputs.reason }}\\n run: |\\n set -euo pipefail\\n $RECAP_CLI recap comment upsert --repo \"$GITHUB_REPOSITORY\" --issue \"$PR_NUMBER\" --token \"$GH_TOKEN\" --head-sha \"$HEAD_SHA\"\\n\\n - name: Complete visual recap check\\n if: always() && !cancelled() && steps.recap_check.outputs.check_run_id != \\'\\'\\n continue-on-error: true\\n env:\\n # Untrusted/step values via env (NOT ${{ }}-interpolated into the run\\n # body): the agent-written plan URL and the scan JSON could inject shell.\\n CHECK_RUN_ID: ${{ steps.recap_check.outputs.check_run_id }}\\n PLAN_OK: ${{ steps.url.outputs.ok }}\\n PLAN_URL: ${{ steps.url.outputs.plan_url }}\\n SUPPRESSED: ${{ steps.scan.outputs.suppressed }}\\n SUPPRESSED_JSON: ${{ steps.scan.outputs.json }}\\n DIFF_HUGE: ${{ steps.diff.outputs.huge }}\\n DIFF_TINY: ${{ steps.diff.outputs.tiny }}\\n RECAP_AGENT_SUMMARY: ${{ steps.agent_summary.outputs.summary }}\\n RECAP_URL_REASON: ${{ steps.route_health.outputs.reason || steps.url.outputs.reason }}\\n run: |\\n set -uo pipefail\\n $RECAP_CLI recap check complete \\\\\\n --check-run-id \"$CHECK_RUN_ID\" \\\\\\n --plan-ok \"$PLAN_OK\" \\\\\\n --plan-url \"$PLAN_URL\" \\\\\\n --suppressed \"$SUPPRESSED\" \\\\\\n --suppressed-json \"$SUPPRESSED_JSON\" \\\\\\n --huge \"$DIFF_HUGE\" \\\\\\n --tiny \"$DIFF_TINY\" \\\\\\n --failure-summary \"$RECAP_AGENT_SUMMARY\" \\\\\\n --url-reason \"$RECAP_URL_REASON\" \\\\\\n --workflow-url \"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID\"\\n';\n"]}
|
|
1
|
+
{"version":3,"file":"pr-visual-recap-workflow.js","sourceRoot":"","sources":["../../src/cli/pr-visual-recap-workflow.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,4BAA4B,GACvC,m2sCAAm2sC,CAAC","sourcesContent":["/**\n * Bundled copy of .github/workflows/pr-visual-recap.yml used by\n * `agent-native recap setup`. Keep byte-identical to the source workflow.\n *\n * This file is generated from the workflow source; tests assert the exported\n * string stays in sync.\n */\n\nexport const PR_VISUAL_RECAP_WORKFLOW_YML =\n 'name: PR Visual Recap\\n\\n# Visual code review: a coding agent runs the repo\\'s visual-recap skill over the\\n# PR diff, publishes a plan, and upserts one sticky comment with a screenshot.\\n# Plain `pull_request` (NOT `pull_request_target`) so fork code never sees secrets.\\n\\non:\\n pull_request:\\n types: [opened, synchronize, reopened, ready_for_review]\\n\\npermissions:\\n contents: read\\n\\nconcurrency:\\n group: pr-visual-recap-${{ github.event.pull_request.number }}\\n cancel-in-progress: true\\n\\nenv:\\n VISUAL_RECAP_AGENT: ${{ vars.VISUAL_RECAP_AGENT || \\'claude\\' }}\\n VISUAL_RECAP_SKILL_SOURCE: ${{ vars.VISUAL_RECAP_SKILL_SOURCE || \\'auto\\' }}\\n VISUAL_RECAP_SECRET_SCAN: ${{ vars.VISUAL_RECAP_SECRET_SCAN || \\'high-confidence\\' }}\\n\\njobs:\\n gate:\\n name: Gate\\n runs-on: ubuntu-latest\\n timeout-minutes: 10\\n permissions:\\n contents: read\\n issues: write\\n pull-requests: write\\n outputs:\\n run: ${{ steps.decide.outputs.run }}\\n agent: ${{ steps.decide.outputs.agent }}\\n steps:\\n - id: decide\\n uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\\n env:\\n # Presence-only signals — never expose secret VALUES to the gate.\\n HAS_PLAN: ${{ secrets.PLAN_RECAP_TOKEN != \\'\\' }}\\n HAS_ANTHROPIC: ${{ secrets.ANTHROPIC_API_KEY != \\'\\' }}\\n HAS_OPENAI: ${{ secrets.OPENAI_API_KEY != \\'\\' }}\\n AGENT: ${{ env.VISUAL_RECAP_AGENT }}\\n VISUAL_RECAP_MODEL: ${{ vars.VISUAL_RECAP_MODEL }}\\n VISUAL_RECAP_SKILL_SOURCE: ${{ env.VISUAL_RECAP_SKILL_SOURCE }}\\n HEAD_SHA: ${{ github.event.pull_request.head.sha }}\\n with:\\n script: |\\n const pr = context.payload.pull_request;\\n const reasons = [];\\n\\n if (!pr) reasons.push(\\'no pull_request payload\\');\\n if (pr && pr.draft) reasons.push(\\'draft PR\\');\\n\\n // Fork PRs only receive repo secrets when the org/repo opts into\\n // GitHub\\'s \"Send secrets to workflows from pull requests\" setting\\n // (common in private orgs that use forks heavily). Gate on secret\\n // availability, not fork-ness: run on forks that have the token,\\n // and skip — with an actionable hint — those that don\\'t.\\n const headRepo = pr && pr.head && pr.head.repo && pr.head.repo.full_name;\\n const isFork = !!(pr && headRepo && headRepo !== process.env.GITHUB_REPOSITORY);\\n const isPrivate = !!(context.payload.repository && context.payload.repository.private);\\n if (isFork && process.env.HAS_PLAN !== \\'true\\') {\\n reasons.push(`fork PR (${headRepo}) without secret access — enable \"Send secrets to workflows from pull requests\" (and write tokens) in the repo/org Actions settings to run recaps on forks`);\\n }\\n\\n const login = (pr && pr.user && pr.user.login || \\'\\').toLowerCase();\\n const botAuthors = [\\'dependabot[bot]\\', \\'dependabot\\', \\'renovate[bot]\\', \\'renovate\\'];\\n if (botAuthors.includes(login)) reasons.push(`bot author (${login})`);\\n if (pr && pr.user && pr.user.type === \\'Bot\\') reasons.push(\\'bot author (type=Bot)\\');\\n\\n if (!isFork && process.env.HAS_PLAN !== \\'true\\') reasons.push(\\'PLAN_RECAP_TOKEN not configured\\');\\n\\n // Normalize + validate the agent so a mis-cased value can\\'t pass the\\n // gate and then match neither agent step below.\\n const agent = (process.env.AGENT || \\'claude\\').toLowerCase();\\n if (agent !== \\'claude\\' && agent !== \\'codex\\') {\\n reasons.push(`unsupported VISUAL_RECAP_AGENT \"${process.env.AGENT}\" (expected \"claude\" or \"codex\")`);\\n } else if (agent === \\'codex\\') {\\n if (process.env.HAS_OPENAI !== \\'true\\') reasons.push(\\'OPENAI_API_KEY not configured (codex backend)\\');\\n } else {\\n if (process.env.HAS_ANTHROPIC !== \\'true\\') reasons.push(\\'ANTHROPIC_API_KEY not configured (claude backend)\\');\\n }\\n\\n // Validate the model before it reaches the agent CLI.\\n const model = process.env.VISUAL_RECAP_MODEL || \\'\\';\\n if (model && !/^[a-zA-Z0-9._-]{1,80}$/.test(model)) {\\n reasons.push(`invalid VISUAL_RECAP_MODEL value (must match [a-zA-Z0-9._-]{1,80})`);\\n }\\n\\n const skillSource = (process.env.VISUAL_RECAP_SKILL_SOURCE || \\'auto\\').toLowerCase();\\n if (![\\'auto\\', \\'latest\\', \\'repo\\'].includes(skillSource)) {\\n reasons.push(\\'invalid VISUAL_RECAP_SKILL_SOURCE value (expected \"auto\", \"latest\", or \"repo\")\\');\\n }\\n const usesRepoSkill = skillSource === \\'repo\\';\\n\\n // Self-modifying guard, evaluated in the trusted gate (runs NO\\n // PR-checked-out code): skip the ENTIRE job if the PR touches the\\n // repo-pinned skill instructions or any agent config the runner\\n // loads, so a PR can\\'t rewrite what the agent loads and exfiltrate\\n // secrets. With the default bundled skill source, visual skill and\\n // recap workflow files are reviewed content, not instructions loaded\\n // by the runner.\\n // Keep this guard for forks AND all public-repo PRs: a fork or a\\n // public same-repo author could rewrite loaded instruction files\\n // (AGENTS.md/CLAUDE.md/.claude/.mcp.json) and exfiltrate the\\n // secret-backed agent run. Skip it ONLY for private-repo same-repo\\n // PRs, where the author is a trusted org member — a deliberate owner\\n // risk acceptance so legit instruction edits don\\'t false-skip recaps.\\n if (pr && (isFork || !isPrivate)) {\\n try {\\n const files = await github.paginate(github.rest.pulls.listFiles, {\\n owner: context.repo.owner,\\n repo: context.repo.repo,\\n pull_number: pr.number,\\n per_page: 100,\\n });\\n const isSensitive = (p) =>\\n (usesRepoSkill && /(^|\\\\/)skills\\\\/visual-(recap|plan|plans)\\\\//.test(p)) ||\\n /(^|\\\\/)\\\\.claude\\\\//.test(p) ||\\n /(^|\\\\/)CLAUDE\\\\.md$/.test(p) ||\\n /(^|\\\\/)AGENTS\\\\.md$/.test(p) ||\\n /(^|\\\\/)\\\\.mcp\\\\.json$/.test(p);\\n const hits = files.map((f) => f.filename).filter(isSensitive);\\n if (hits.length) {\\n reasons.push(`PR modifies recap-control files (${hits.slice(0, 3).join(\\', \\')}${hits.length > 3 ? \\', …\\' : \\'\\'}) — skipping so untrusted PR code never runs with secrets`);\\n }\\n } catch (e) {\\n // Fail closed: if the file list can\\'t be read, skip.\\n reasons.push(`could not list PR files for the self-modifying guard (${e.message}); skipping to be safe`);\\n }\\n }\\n\\n const run = reasons.length === 0;\\n core.setOutput(\\'run\\', run ? \\'true\\' : \\'false\\');\\n core.setOutput(\\'agent\\', agent);\\n if (run) {\\n core.info(`Visual recap will run (${agent}).`);\\n } else {\\n // Surface the skip reason as a run-summary annotation, not just a\\n // buried info log, so it\\'s clear in the Actions UI why we skipped.\\n core.notice(`Visual recap skipped: ${reasons.join(\\'; \\')}`);\\n }\\n\\n // When skipping, upsert a sticky recap comment with a short skip\\n // line so the PR always explains why the recap job did not run.\\n if (!run && pr) {\\n try {\\n const MARKER = \\'<!-- pr-visual-recap -->\\';\\n const { data: comments } = await github.rest.issues.listComments({\\n owner: context.repo.owner,\\n repo: context.repo.repo,\\n issue_number: pr.number,\\n per_page: 100,\\n });\\n const existing = comments.find(\\n (c) => c.user && c.user.type === \\'Bot\\' && c.body && c.body.includes(MARKER)\\n );\\n const headShort = (process.env.HEAD_SHA || \\'\\').slice(0, 7);\\n const shaRef = headShort ? `\\\\`${headShort}\\\\`` : \\'latest push\\';\\n const primaryReason = reasons.filter(\\n (r) => !r.startsWith(\\'could not list PR files for the self-modifying guard\\')\\n )[0] || reasons[0] || \\'skipped\\';\\n const skipLine = `_Recap skipped for ${shaRef}: ${primaryReason}._`;\\n const baseBody = `${MARKER}\\\\n### Visual recap — skipped\\\\n\\\\nThe visual recap job did not run for this pull request. This is informational only and does **not** block the PR.`;\\n const withoutPrev = (existing && existing.body ? existing.body : baseBody)\\n .split(\\'\\\\n\\')\\n .filter((l) => !/_Recap skipped for .+_$/.test(l.trim()))\\n .join(\\'\\\\n\\')\\n .trimEnd();\\n const updatedBody = `${withoutPrev}\\\\n\\\\n${skipLine}`;\\n if (existing) {\\n await github.rest.issues.updateComment({\\n owner: context.repo.owner,\\n repo: context.repo.repo,\\n comment_id: existing.id,\\n body: updatedBody,\\n });\\n } else {\\n await github.rest.issues.createComment({\\n owner: context.repo.owner,\\n repo: context.repo.repo,\\n issue_number: pr.number,\\n body: updatedBody,\\n });\\n }\\n } catch (e) {\\n core.warning(`Could not update recap skip comment: ${e.message}`);\\n }\\n }\\n\\n recap:\\n name: Generate visual recap\\n needs: gate\\n if: needs.gate.outputs.run == \\'true\\'\\n runs-on: ubuntu-latest\\n timeout-minutes: 30\\n permissions:\\n actions: write\\n checks: write\\n contents: read\\n issues: write\\n pull-requests: write\\n env:\\n PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL || \\'https://plan.agent-native.com\\' }}\\n PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }}\\n GH_TOKEN: ${{ github.token }}\\n PR_NUMBER: ${{ github.event.pull_request.number }}\\n HEAD_SHA: ${{ github.event.pull_request.head.sha }}\\n VISUAL_RECAP_MODEL: ${{ vars.VISUAL_RECAP_MODEL }}\\n VISUAL_RECAP_REASONING: ${{ vars.VISUAL_RECAP_REASONING }}\\n VISUAL_RECAP_SKILL_SOURCE: ${{ vars.VISUAL_RECAP_SKILL_SOURCE || \\'auto\\' }}\\n VISUAL_RECAP_SECRET_SCAN: ${{ vars.VISUAL_RECAP_SECRET_SCAN || \\'high-confidence\\' }}\\n steps:\\n - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3\\n with:\\n fetch-depth: 0\\n # This job runs an agent over untrusted PR diff; don\\'t leave the token\\n # in .git/config (it uses GH_TOKEN for gh API calls, never git push).\\n persist-credentials: false\\n\\n # Dogfood trusted base-branch source inside this monorepo, else install the\\n # published package once. Never execute PR-head recap CLI code.\\n - name: Resolve recap CLI\\n id: cli\\n env:\\n # Optional: pin the consumer CLI version (e.g. \"1.2.3\"). Defaults to\\n # \"latest\" when unset. Set via repository variable RECAP_CLI_VERSION.\\n RECAP_CLI_VERSION: ${{ vars.RECAP_CLI_VERSION || \\'latest\\' }}\\n run: |\\n if [ \"$GITHUB_REPOSITORY\" = \"BuilderIO/agent-native\" ] && [ -f packages/core/src/cli/index.ts ]; then\\n echo \"local=true\" >> \"$GITHUB_OUTPUT\"\\n else\\n echo \"local=false\" >> \"$GITHUB_OUTPUT\"\\n fi\\n\\n - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3\\n if: steps.cli.outputs.local == \\'true\\'\\n with:\\n ref: ${{ github.event.pull_request.base.sha }}\\n path: .recap-cli-source\\n fetch-depth: 1\\n persist-credentials: false\\n\\n - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8\\n if: steps.cli.outputs.local == \\'true\\'\\n\\n - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0\\n with:\\n node-version: \"22\"\\n cache: ${{ steps.cli.outputs.local == \\'true\\' && \\'pnpm\\' || \\'\\' }}\\n\\n - name: Install trusted workspace recap CLI\\n if: steps.cli.outputs.local == \\'true\\'\\n working-directory: .recap-cli-source\\n run: |\\n set -euo pipefail\\n pnpm install --frozen-lockfile --ignore-scripts\\n echo \"RECAP_CLI=$PWD/node_modules/.bin/tsx $PWD/packages/core/src/cli/index.ts\" >> \"$GITHUB_ENV\"\\n echo \"RECAP_PLAYWRIGHT=$PWD/node_modules/.bin/playwright\" >> \"$GITHUB_ENV\"\\n\\n - name: Install published recap CLI\\n if: steps.cli.outputs.local != \\'true\\'\\n env:\\n RECAP_CLI_VERSION: ${{ vars.RECAP_CLI_VERSION || \\'latest\\' }}\\n run: |\\n set -euo pipefail\\n VERSION=\"$RECAP_CLI_VERSION\"\\n if [ \"$VERSION\" = \"latest\" ]; then\\n VERSION=\"$(npm view @agent-native/core@latest version)\"\\n fi\\n for attempt in 1 2 3; do\\n if npm install --prefix \"$RUNNER_TEMP/recap-cli\" --no-audit --no-fund \"@agent-native/core@$VERSION\"; then\\n break\\n fi\\n if [ \"$attempt\" = \"3\" ]; then exit 1; fi\\n sleep $((attempt * 10))\\n done\\n echo \"RECAP_CLI=$RUNNER_TEMP/recap-cli/node_modules/.bin/agent-native\" >> \"$GITHUB_ENV\"\\n echo \"RECAP_PLAYWRIGHT=$RUNNER_TEMP/recap-cli/node_modules/.bin/playwright\" >> \"$GITHUB_ENV\"\\n\\n - name: Start visual recap check\\n id: recap_check\\n continue-on-error: true\\n run: |\\n set -uo pipefail\\n $RECAP_CLI recap check start --sha \"$HEAD_SHA\" --workflow-url \"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID\"\\n\\n - name: Collect bounded diff\\n id: diff\\n env:\\n BASE_SHA: ${{ github.event.pull_request.base.sha }}\\n run: |\\n set -euo pipefail\\n $RECAP_CLI recap collect-diff --base \"$BASE_SHA\" --head \"$HEAD_SHA\" --out recap.diff --stat recap.stat\\n\\n - name: Probe plan-app auth\\n id: auth_probe\\n if: steps.diff.outputs.tiny != \\'true\\'\\n continue-on-error: true\\n run: |\\n set -uo pipefail\\n # Hit the plan app\\'s action surface with the publish token. A 401 means\\n # the token is expired/revoked; surface it in the sticky comment so the\\n # repo owner knows to re-mint it instead of seeing a generic failure.\\n HTTP_STATUS=$(node -e \\'\\n const https = require(\"https\");\\n const url = new URL(\"/_agent-native/actions/record-recap-usage\", process.env.PLAN_RECAP_APP_URL || \"https://plan.agent-native.com\");\\n const req = https.request(url, { method: \"POST\", headers: { \"authorization\": \"Bearer \" + process.env.PLAN_RECAP_TOKEN, \"content-type\": \"application/json\" }, timeout: 8000 }, (res) => { process.stdout.write(String(res.statusCode)); req.destroy(); });\\n req.on(\"error\", () => process.stdout.write(\"0\"));\\n req.end(JSON.stringify({ planId: \"__probe__\" }));\\n \\' 2>/dev/null || echo \"0\")\\n if [ \"$HTTP_STATUS\" = \"401\" ]; then\\n echo \"auth_failed=true\" >> \"$GITHUB_OUTPUT\"\\n else\\n echo \"auth_failed=false\" >> \"$GITHUB_OUTPUT\"\\n fi\\n\\n - name: Probe plan-app route health\\n id: route_health\\n if: steps.diff.outputs.tiny != \\'true\\'\\n continue-on-error: true\\n run: |\\n set -uo pipefail\\n # Pre-publish health gate: confirm the plan app\\'s recap action routes\\n # are actually deployed BEFORE the agent runs. A 404 from\\n # create-visual-recap (POST) or get-plan-blocks (GET) means the\\n # plan-app deploy has not propagated yet (the client is ahead of the\\n # deployed server). Say that plainly here instead of letting the agent\\n # run and then fail confusingly at publish time. A 401 or 200 is\\n # healthy — the route exists, it just rejected/accepted the probe.\\n probe_status() {\\n ROUTE=\"$1\" METHOD=\"$2\" node -e \\'\\n const https = require(\"https\");\\n const base = process.env.PLAN_RECAP_APP_URL || \"https://plan.agent-native.com\";\\n const url = new URL(process.env.ROUTE, base);\\n if (process.env.METHOD === \"GET\") url.searchParams.set(\"format\", \"reference\");\\n const req = https.request(url, { method: process.env.METHOD, headers: { \"authorization\": \"Bearer \" + (process.env.PLAN_RECAP_TOKEN || \"\"), \"content-type\": \"application/json\" }, timeout: 8000 }, (res) => { process.stdout.write(String(res.statusCode)); req.destroy(); });\\n req.on(\"error\", () => process.stdout.write(\"0\"));\\n req.on(\"timeout\", () => { process.stdout.write(\"0\"); req.destroy(); });\\n if (process.env.METHOD === \"POST\") { req.end(JSON.stringify({ __probe__: true })); } else { req.end(); }\\n \\' 2>/dev/null || echo \"0\"\\n }\\n CREATE_STATUS=\"$(probe_status /_agent-native/actions/create-visual-recap POST)\"\\n BLOCKS_STATUS=\"$(probe_status /_agent-native/actions/get-plan-blocks GET)\"\\n REASON=\"\"\\n if [ \"$CREATE_STATUS\" = \"404\" ] || [ \"$BLOCKS_STATUS\" = \"404\" ]; then\\n REASON=\"Plan app routes return 404 — deploy not yet propagated (create-visual-recap: $CREATE_STATUS, get-plan-blocks: $BLOCKS_STATUS). The plan-app client is ahead of the deployed server; re-run once the deploy finishes propagating.\"\\n echo \"::error::$REASON\"\\n echo \"unhealthy=true\" >> \"$GITHUB_OUTPUT\"\\n else\\n echo \"unhealthy=false\" >> \"$GITHUB_OUTPUT\"\\n fi\\n {\\n echo \\'reason<<__RECAP_ROUTE_HEALTH_EOF__\\'\\n echo \"$REASON\"\\n echo \\'__RECAP_ROUTE_HEALTH_EOF__\\'\\n } >> \"$GITHUB_OUTPUT\"\\n\\n - name: Secret scan\\n id: scan\\n if: steps.diff.outputs.tiny != \\'true\\'\\n run: |\\n set -uo pipefail\\n # Fail CLOSED: a scanner error or invalid JSON suppresses the diff so a\\n # credential-bearing diff is never handed to the agent / plan service.\\n if ! SCAN_JSON=\"$($RECAP_CLI recap scan --diff recap.diff --mode \"$VISUAL_RECAP_SECRET_SCAN\")\"; then\\n SCAN_JSON=\\'{\"suppressed\":true,\"reason\":\"secret scan failed to run; failing closed\"}\\'\\n fi\\n {\\n echo \\'json<<__RECAP_SCAN_EOF__\\'\\n echo \"$SCAN_JSON\"\\n echo \\'__RECAP_SCAN_EOF__\\'\\n } >> \"$GITHUB_OUTPUT\"\\n SUPPRESSED=$(node -e \\'try{process.stdout.write(JSON.parse(process.argv[1]).suppressed?\"true\":\"false\")}catch{process.stdout.write(\"true\")}\\' \"$SCAN_JSON\")\\n echo \"suppressed=$SUPPRESSED\" >> \"$GITHUB_OUTPUT\"\\n\\n - name: Read previous plan id\\n id: prev\\n continue-on-error: true\\n run: |\\n set -euo pipefail\\n PLAN_ID=\"$($RECAP_CLI recap comment find-plan-id --repo \"$GITHUB_REPOSITORY\" --issue \"$PR_NUMBER\" --token \"$GH_TOKEN\")\"\\n echo \"plan_id=$PLAN_ID\" >> \"$GITHUB_OUTPUT\"\\n\\n - name: Fetch plan block reference\\n id: block_reference\\n if: steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n continue-on-error: true\\n run: |\\n set -uo pipefail\\n if $RECAP_CLI recap block-reference --app-url \"$PLAN_RECAP_APP_URL\" --out recap-blocks.md; then\\n echo \"ok=true\" >> \"$GITHUB_OUTPUT\"\\n else\\n echo \"ok=false\" >> \"$GITHUB_OUTPUT\"\\n {\\n echo \\'summary<<__RECAP_BLOCK_REFERENCE_EOF__\\'\\n echo \"Could not fetch the live plan block reference; the agent will fall back to bundled visual-recap instructions and the publisher will validate the final MDX.\"\\n echo \\'__RECAP_BLOCK_REFERENCE_EOF__\\'\\n } >> \"$GITHUB_OUTPUT\"\\n cat > recap-blocks.md <<\\'EOF\\'\\n Live plan block reference unavailable. Follow the bundled visual-recap skill and author conservative MDX; the deterministic publisher will validate the source before posting.\\n EOF\\n fi\\n\\n - name: Build recap prompt\\n id: prompt\\n if: steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n env:\\n # Pass step outputs via env, NOT ${{ }} interpolation into the run body:\\n # the prev plan id is parsed from a PR comment and could inject shell.\\n PREV_PLAN_ID: ${{ steps.prev.outputs.plan_id }}\\n DIFF_HUGE: ${{ steps.diff.outputs.huge }}\\n IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }}\\n run: |\\n set -euo pipefail\\n ARGS=(--diff recap.diff --stat recap.stat --block-reference recap-blocks.md --pr \"$PR_NUMBER\" --repo \"$GITHUB_REPOSITORY\" --head \"$HEAD_SHA\" --app-url \"$PLAN_RECAP_APP_URL\" --skill-source \"$VISUAL_RECAP_SKILL_SOURCE\" --out recap-prompt.md)\\n if [ \"${DIFF_HUGE:-}\" = \"true\" ]; then ARGS+=(--huge); fi\\n if [ \"${IS_FORK:-}\" = \"true\" ]; then ARGS+=(--fork-pr true); fi\\n if [ -n \"${PREV_PLAN_ID:-}\" ]; then ARGS+=(--prev-plan-id \"$PREV_PLAN_ID\"); fi\\n $RECAP_CLI recap build-prompt \"${ARGS[@]}\"\\n\\n - name: Run agent (Claude Code)\\n id: claude\\n if: needs.gate.outputs.agent == \\'claude\\' && steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n continue-on-error: true\\n env:\\n ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}\\n run: |\\n set -uo pipefail\\n CLAUDE_ALLOWED_TOOLS=\"Read,Write,Bash(git diff:*)\"\\n CLAUDE_ARGS=(-p \"$(cat recap-prompt.md)\" --allowedTools \"$CLAUDE_ALLOWED_TOOLS\" --permission-mode dontAsk --output-format json)\\n if [ -n \"${VISUAL_RECAP_MODEL:-}\" ]; then CLAUDE_ARGS+=(--model \"$VISUAL_RECAP_MODEL\"); fi\\n rm -f recap-source.json recap-url.txt recap-url-reason.txt claude-result.json claude-stderr.log\\n run_claude() {\\n set +e\\n npx -y @anthropic-ai/claude-code@2 \"${CLAUDE_ARGS[@]}\" > claude-result.json 2> claude-stderr.log\\n CLAUDE_STATUS=\"$?\"\\n set -e\\n echo \"$CLAUDE_STATUS\" > claude-exit-code.txt\\n }\\n run_claude\\n # A clean agent exit WITHOUT recap-source.json is the strongest\\n # \"retry me\" signal — the deterministic publisher needs that file, and\\n # the agent occasionally finishes a turn without writing it. Retry once.\\n if [ ! -s recap-source.json ]; then\\n echo \"::warning::recap-source.json missing after the agent run; retrying the agent once.\"\\n sleep 5\\n run_claude\\n fi\\n\\n - name: Run agent (Codex)\\n id: codex\\n if: needs.gate.outputs.agent == \\'codex\\' && steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n continue-on-error: true\\n env:\\n OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\\n run: |\\n set -uo pipefail\\n # `codex login` writes ~/.codex/auth.json (the bare env var is dropped on\\n # the gpt-5.5 wss transport); stdin keeps the key out of process args.\\n printenv OPENAI_API_KEY | npx -y @openai/codex@0 login --with-api-key || true\\n # The runner is itself an ephemeral sandbox; bypass Codex\\'s own sandbox\\n # (bubblewrap can\\'t init here) and approval gate (cancels the MCP write).\\n CODEX_ARGS=(exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check)\\n if [ -n \"${VISUAL_RECAP_MODEL:-}\" ]; then CODEX_ARGS+=(--model \"$VISUAL_RECAP_MODEL\"); fi\\n # Validate reasoning against the enum before embedding it in the TOML override.\\n case \"${VISUAL_RECAP_REASONING:-}\" in\\n none|minimal|low|medium|high|xhigh)\\n CODEX_ARGS+=(-c \"model_reasoning_effort=\\\\\"$VISUAL_RECAP_REASONING\\\\\"\") ;;\\n \"\") ;;\\n *) echo \"Ignoring invalid VISUAL_RECAP_REASONING: $VISUAL_RECAP_REASONING\" ;;\\n esac\\n rm -f recap-source.json recap-url.txt recap-url-reason.txt codex-events.jsonl codex-stderr.log\\n run_codex() {\\n set +e\\n npx -y @openai/codex@0 \"${CODEX_ARGS[@]}\" --json \"$(cat recap-prompt.md)\" 2> codex-stderr.log | tee codex-events.jsonl\\n CODEX_STATUS=\"${PIPESTATUS[0]}\"\\n set -e\\n echo \"$CODEX_STATUS\" > codex-exit-code.txt\\n }\\n run_codex\\n # Retry once if the agent exited without writing recap-source.json\\n # (see the Claude step) — the publisher needs that file.\\n if [ ! -s recap-source.json ]; then\\n echo \"::warning::recap-source.json missing after the agent run; retrying the agent once.\"\\n sleep 5\\n run_codex\\n fi\\n\\n - name: Publish recap source\\n id: publish\\n if: steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n continue-on-error: true\\n env:\\n PREV_PLAN_ID: ${{ steps.prev.outputs.plan_id }}\\n run: |\\n set -uo pipefail\\n ARGS=(--source recap-source.json --out recap-url.txt --repo \"$GITHUB_REPOSITORY\" --pr \"$PR_NUMBER\" --app-url \"$PLAN_RECAP_APP_URL\" --token \"$PLAN_RECAP_TOKEN\")\\n if [ -n \"${PREV_PLAN_ID:-}\" ]; then ARGS+=(--prev-plan-id \"$PREV_PLAN_ID\"); fi\\n $RECAP_CLI recap publish \"${ARGS[@]}\"\\n\\n - name: Read plan URL\\n id: url\\n if: steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n run: |\\n set -uo pipefail\\n PLAN_URL=\"\"\\n URL_REASON=\"\"\\n if [ -f recap-url.txt ]; then\\n PLAN_URL=\"$(tr -d \\'\\\\r\\\\n\\' < recap-url.txt | tr -d \\' \\')\"\\n elif [ -f recap-url-reason.txt ]; then\\n URL_REASON=\"$(cat recap-url-reason.txt)\"\\n else\\n URL_REASON=\"recap-url.txt was not created.\"\\n fi\\n # recap-url.txt is agent-written -> untrusted. Rebuild a canonical\\n # recap URL from the trusted app base and a strictly validated plan id,\\n # preserving path-prefixed self-hosted mounts.\\n if [ -z \"$URL_REASON\" ]; then\\n URL_RESULT=$(PLAN_URL=\"$PLAN_URL\" node <<\\'NODE\\'\\n const emit = (value) => process.stdout.write(JSON.stringify(value));\\n try {\\n const raw = process.env.PLAN_URL || \"\";\\n if (!raw) {\\n emit({ url: \"\", reason: \"recap-url.txt was empty\" });\\n process.exit(0);\\n }\\n const trusted = new URL(process.env.PLAN_RECAP_APP_URL || \"https://plan.agent-native.com\");\\n const parsed = /^https?:\\\\/\\\\//i.test(raw)\\n ? new URL(raw)\\n : new URL(raw, trusted);\\n if (parsed.origin !== trusted.origin) {\\n emit({ url: \"\", reason: `recap-url.txt points at ${parsed.origin}, expected ${trusted.origin}` });\\n process.exit(0);\\n }\\n\\n const base = trusted.pathname.replace(/\\\\/$/, \"\");\\n const paths = [parsed.pathname];\\n if (base && parsed.pathname.startsWith(`${base}/`)) {\\n paths.push(parsed.pathname.slice(base.length) || \"/\");\\n }\\n\\n for (const path of paths) {\\n const match = path.match(/^\\\\/(?:plans|recaps)\\\\/([A-Za-z0-9_-]+)\\\\/?$/);\\n if (match) {\\n emit({ url: `${trusted.origin}${base}/recaps/${match[1]}`, reason: \"\" });\\n process.exit(0);\\n }\\n }\\n emit({ url: \"\", reason: \"recap-url.txt did not contain a valid /plans/<id> or /recaps/<id> URL for the configured plan app\" });\\n } catch {\\n emit({ url: \"\", reason: \"recap-url.txt was not a valid URL or recap path\" });\\n }\\n NODE\\n )\\n CANONICAL_URL=$(node -e \\'try{process.stdout.write(JSON.parse(process.argv[1]).url||\"\")}catch{process.stdout.write(\"\")}\\' \"$URL_RESULT\")\\n URL_REASON=$(node -e \\'try{process.stdout.write(JSON.parse(process.argv[1]).reason||\"\")}catch{process.stdout.write(\"recap-url.txt URL validation failed\")}\\' \"$URL_RESULT\")\\n else\\n CANONICAL_URL=\"\"\\n fi\\n if [ -n \"$CANONICAL_URL\" ]; then\\n echo \"plan_url=$CANONICAL_URL\" >> \"$GITHUB_OUTPUT\"; echo \"ok=true\" >> \"$GITHUB_OUTPUT\"\\n else\\n echo \"plan_url=\" >> \"$GITHUB_OUTPUT\"; echo \"ok=false\" >> \"$GITHUB_OUTPUT\"\\n fi\\n {\\n echo \\'reason<<__RECAP_URL_REASON_EOF__\\'\\n echo \"$URL_REASON\"\\n echo \\'__RECAP_URL_REASON_EOF__\\'\\n } >> \"$GITHUB_OUTPUT\"\\n\\n - name: Summarize agent failure\\n id: agent_summary\\n if: steps.url.outputs.ok != \\'true\\' && steps.diff.outputs.tiny != \\'true\\' && steps.scan.outputs.suppressed != \\'true\\'\\n continue-on-error: true\\n env:\\n RECAP_AGENT: ${{ needs.gate.outputs.agent }}\\n RECAP_BLOCK_REFERENCE_SUMMARY: ${{ steps.block_reference.outputs.summary }}\\n RECAP_PUBLISH_REASON: ${{ steps.publish.outputs.reason }}\\n run: |\\n set -uo pipefail\\n if [ -n \"${RECAP_BLOCK_REFERENCE_SUMMARY:-}\" ]; then\\n {\\n echo \\'summary<<__RECAP_BLOCK_REFERENCE_SUMMARY_EOF__\\'\\n echo \"$RECAP_BLOCK_REFERENCE_SUMMARY\"\\n echo \\'__RECAP_BLOCK_REFERENCE_SUMMARY_EOF__\\'\\n } >> \"$GITHUB_OUTPUT\"\\n node -e \\'process.stdout.write(JSON.stringify({ ok: true, summary: process.env.RECAP_BLOCK_REFERENCE_SUMMARY || \"\" }) + \"\\\\n\")\\'\\n exit 0\\n fi\\n if [ -n \"${RECAP_PUBLISH_REASON:-}\" ]; then\\n {\\n echo \\'summary<<__RECAP_PUBLISH_SUMMARY_EOF__\\'\\n echo \"$RECAP_PUBLISH_REASON\"\\n echo \\'__RECAP_PUBLISH_SUMMARY_EOF__\\'\\n } >> \"$GITHUB_OUTPUT\"\\n node -e \\'process.stdout.write(JSON.stringify({ ok: true, summary: process.env.RECAP_PUBLISH_REASON || \"\" }) + \"\\\\n\")\\'\\n exit 0\\n fi\\n RESULT=claude-result.json\\n STDERR=claude-stderr.log\\n EXIT_CODE=claude-exit-code.txt\\n if [ \"$RECAP_AGENT\" = \"codex\" ]; then\\n RESULT=codex-events.jsonl\\n STDERR=codex-stderr.log\\n EXIT_CODE=codex-exit-code.txt\\n fi\\n $RECAP_CLI recap agent-summary --agent \"$RECAP_AGENT\" --result-file \"$RESULT\" --stderr-file \"$STDERR\" --exit-code-file \"$EXIT_CODE\" || true\\n\\n - name: Attach usage\\n if: steps.url.outputs.ok == \\'true\\'\\n continue-on-error: true\\n env:\\n PLAN_URL: ${{ steps.url.outputs.plan_url }}\\n # Use the gate-normalized agent so \"Codex\" still selects the right file.\\n RECAP_AGENT: ${{ needs.gate.outputs.agent }}\\n run: |\\n set -uo pipefail\\n RESULT=claude-result.json\\n if [ \"$RECAP_AGENT\" = \"codex\" ]; then RESULT=codex-events.jsonl; fi\\n if [ -f \"$RESULT\" ]; then $RECAP_CLI recap usage --plan-url \"$PLAN_URL\" --agent \"$RECAP_AGENT\" --result-file \"$RESULT\" --model \"${VISUAL_RECAP_MODEL:-}\" --app-url \"$PLAN_RECAP_APP_URL\" --token \"$PLAN_RECAP_TOKEN\" || true; fi\\n\\n - name: Cache Playwright browsers\\n if: steps.url.outputs.ok == \\'true\\'\\n uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3\\n with:\\n path: ~/.cache/ms-playwright\\n key: playwright-1-${{ runner.os }}\\n\\n - name: Screenshot + upload\\n id: shot\\n if: steps.url.outputs.ok == \\'true\\'\\n continue-on-error: true\\n env:\\n # recap-url.txt is untrusted agent output; pass via env, never ${{ }}.\\n PLAN_URL: ${{ steps.url.outputs.plan_url }}\\n run: |\\n set -uo pipefail\\n if [ -n \"${RECAP_PLAYWRIGHT:-}\" ] && [ -x \"$RECAP_PLAYWRIGHT\" ]; then\\n \"$RECAP_PLAYWRIGHT\" install --with-deps chromium || true\\n elif command -v pnpm >/dev/null 2>&1; then\\n pnpm exec playwright install --with-deps chromium 2>/dev/null || npx -y playwright@1 install --with-deps chromium || true\\n else\\n npx -y playwright@1 install --with-deps chromium || true\\n fi\\n LIGHT_SHOT_JSON=\"$($RECAP_CLI recap shot --url \"$PLAN_URL\" --token \"$PLAN_RECAP_TOKEN\" --app-url \"$PLAN_RECAP_APP_URL\" --out recap.png --theme light || echo \\'{}\\')\"\\n DARK_SHOT_JSON=\"$($RECAP_CLI recap shot --url \"$PLAN_URL\" --token \"$PLAN_RECAP_TOKEN\" --app-url \"$PLAN_RECAP_APP_URL\" --out recap-dark.png --theme dark || echo \\'{}\\')\"\\n for SHOT_LABEL in light dark; do\\n if [ \"$SHOT_LABEL\" = \"light\" ]; then SHOT_JSON=\"$LIGHT_SHOT_JSON\"; else SHOT_JSON=\"$DARK_SHOT_JSON\"; fi\\n SHOT_LABEL=\"$SHOT_LABEL\" SHOT_JSON=\"$SHOT_JSON\" node -e \\'const label = process.env.SHOT_LABEL || \"shot\"; let parsed = {}; try { parsed = JSON.parse(process.env.SHOT_JSON || \"{}\"); } catch { parsed = { ok: false, reason: \"invalid shot JSON\" }; } const summary = { ok: parsed.ok === true, imageUrl: parsed.imageUrl ? \"[present]\" : \"\", out: typeof parsed.out === \"string\" ? parsed.out : \"\", reason: typeof parsed.reason === \"string\" ? parsed.reason.slice(0, 500) : \"\" }; console.log(`[recap shot] ${label}: ${JSON.stringify(summary)}`);\\'\\n done\\n IMAGE_URL=$(node -e \\'try{process.stdout.write(JSON.parse(process.argv[1]).imageUrl||\"\")}catch{process.stdout.write(\"\")}\\' \"$LIGHT_SHOT_JSON\")\\n DARK_IMAGE_URL=$(node -e \\'try{process.stdout.write(JSON.parse(process.argv[1]).imageUrl||\"\")}catch{process.stdout.write(\"\")}\\' \"$DARK_SHOT_JSON\")\\n if [ -z \"$IMAGE_URL\" ] && [ -z \"$DARK_IMAGE_URL\" ]; then\\n echo \"::warning::Visual recap screenshot unavailable; posting link-only recap comment.\"\\n fi\\n echo \"image_url=$IMAGE_URL\" >> \"$GITHUB_OUTPUT\"\\n echo \"light_image_url=$IMAGE_URL\" >> \"$GITHUB_OUTPUT\"\\n echo \"dark_image_url=$DARK_IMAGE_URL\" >> \"$GITHUB_OUTPUT\"\\n if [ -f recap.png ] || [ -f recap-dark.png ]; then echo \"captured=true\" >> \"$GITHUB_OUTPUT\"; else echo \"captured=false\" >> \"$GITHUB_OUTPUT\"; fi\\n\\n - name: Upload recap screenshot artifact\\n if: steps.shot.outputs.captured == \\'true\\'\\n uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\\n with:\\n name: pr-visual-recap-${{ github.event.pull_request.number }}\\n path: |\\n recap.png\\n recap-dark.png\\n if-no-files-found: ignore\\n retention-days: 14\\n\\n - name: Upload recap source artifact\\n if: always() && !cancelled()\\n uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\\n with:\\n # recap-source.json + the agent transcript (claude-result.json /\\n # codex-events.jsonl + stderr) are the only window into WHAT the agent\\n # did when a publish fails (no plan URL) — INCLUDING the case where it\\n # finished without writing recap-source.json at all. The sticky comment\\n # only shows the screenshot, so without these a failed recap is\\n # undebuggable. Uploaded on success + failure; tolerant when absent.\\n name: pr-visual-recap-source-${{ github.event.pull_request.number }}\\n path: |\\n recap-source.json\\n claude-result.json\\n claude-stderr.log\\n codex-events.jsonl\\n codex-stderr.log\\n if-no-files-found: ignore\\n retention-days: 14\\n\\n - name: Upsert sticky comment\\n if: always() && !cancelled()\\n continue-on-error: true\\n env:\\n PLAN_URL: ${{ steps.url.outputs.plan_url }}\\n RECAP_IMAGE_URL: ${{ steps.shot.outputs.image_url }}\\n RECAP_LIGHT_IMAGE_URL: ${{ steps.shot.outputs.light_image_url }}\\n RECAP_DARK_IMAGE_URL: ${{ steps.shot.outputs.dark_image_url }}\\n SUPPRESSED: ${{ steps.scan.outputs.suppressed }}\\n SUPPRESSED_JSON: ${{ steps.scan.outputs.json }}\\n DIFF_HUGE: ${{ steps.diff.outputs.huge }}\\n DIFF_TINY: ${{ steps.diff.outputs.tiny }}\\n PREV_PLAN_ID: ${{ steps.prev.outputs.plan_id }}\\n RECAP_AUTH_FAILED: ${{ steps.auth_probe.outputs.auth_failed }}\\n RECAP_AGENT_SUMMARY: ${{ steps.agent_summary.outputs.summary }}\\n # Prefer the route-health diagnostic when the plan app routes are not\\n # yet deployed so the comment explains the 404 instead of a generic\\n # \"recap-url.txt was not created\" message.\\n RECAP_URL_REASON: ${{ steps.route_health.outputs.reason || steps.url.outputs.reason }}\\n run: |\\n set -euo pipefail\\n $RECAP_CLI recap comment upsert --repo \"$GITHUB_REPOSITORY\" --issue \"$PR_NUMBER\" --token \"$GH_TOKEN\" --head-sha \"$HEAD_SHA\"\\n\\n - name: Complete visual recap check\\n if: always() && !cancelled() && steps.recap_check.outputs.check_run_id != \\'\\'\\n continue-on-error: true\\n env:\\n # Untrusted/step values via env (NOT ${{ }}-interpolated into the run\\n # body): the agent-written plan URL and the scan JSON could inject shell.\\n CHECK_RUN_ID: ${{ steps.recap_check.outputs.check_run_id }}\\n PLAN_OK: ${{ steps.url.outputs.ok }}\\n PLAN_URL: ${{ steps.url.outputs.plan_url }}\\n SUPPRESSED: ${{ steps.scan.outputs.suppressed }}\\n SUPPRESSED_JSON: ${{ steps.scan.outputs.json }}\\n DIFF_HUGE: ${{ steps.diff.outputs.huge }}\\n DIFF_TINY: ${{ steps.diff.outputs.tiny }}\\n RECAP_AGENT_SUMMARY: ${{ steps.agent_summary.outputs.summary }}\\n RECAP_URL_REASON: ${{ steps.route_health.outputs.reason || steps.url.outputs.reason }}\\n run: |\\n set -uo pipefail\\n $RECAP_CLI recap check complete \\\\\\n --check-run-id \"$CHECK_RUN_ID\" \\\\\\n --plan-ok \"$PLAN_OK\" \\\\\\n --plan-url \"$PLAN_URL\" \\\\\\n --suppressed \"$SUPPRESSED\" \\\\\\n --suppressed-json \"$SUPPRESSED_JSON\" \\\\\\n --huge \"$DIFF_HUGE\" \\\\\\n --tiny \"$DIFF_TINY\" \\\\\\n --failure-summary \"$RECAP_AGENT_SUMMARY\" \\\\\\n --url-reason \"$RECAP_URL_REASON\" \\\\\\n --workflow-url \"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID\"\\n';\n"]}
|
|
@@ -31,7 +31,7 @@ export const TEMPLATES = [
|
|
|
31
31
|
{
|
|
32
32
|
name: "content",
|
|
33
33
|
label: "Content",
|
|
34
|
-
hint: "
|
|
34
|
+
hint: "Open-source Obsidian for MDX — edit local docs with agent assistance",
|
|
35
35
|
icon: "FileText",
|
|
36
36
|
color: "#10B981",
|
|
37
37
|
colorRgb: "16 185 129",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates-meta.js","sourceRoot":"","sources":["../../src/cli/templates-meta.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * First-party template metadata used by the `agent-native` CLI.\n *\n * This file is intentionally inlined here (rather than imported from a\n * separate workspace package) so that the published `@agent-native/core`\n * has no `workspace:*` runtime dependencies. Without this inlining, `npx\n * @agent-native/core create ...` fails on a fresh machine with:\n *\n * npm error code EUNSUPPORTEDPROTOCOL\n * npm error Unsupported URL Type \"workspace:\": workspace:*\n *\n * Keep this list in sync with `packages/shared-app-config/templates.ts`,\n * which serves the same metadata to the desktop / mobile / frame packages\n * that always run inside the workspace. Duplication is intentional: the\n * CLI must remain installable outside the monorepo.\n */\n\nexport interface TemplateMeta {\n /** Directory name under templates/ and package name */\n name: string;\n /** Display name in pickers */\n label: string;\n /** One-line description shown in the picker */\n hint: string;\n /** Longer description (optional) */\n description?: string;\n /** Tabler icon name used in the desktop sidebar */\n icon: string;\n /** Hex accent color */\n color: string;\n /** CSS-safe RGB triplet (e.g. \"59 130 246\") */\n colorRgb: string;\n /** Dev server port for desktop `pnpm dev` */\n devPort: number;\n /** Production URL when running as a first-party app on agent-native.com */\n prodUrl?: string;\n /** Default URL path when deployed in a workspace (defaults to \"/<name>\") */\n prodPath?: string;\n /** Default mode when added to desktop app */\n defaultMode?: \"dev\" | \"prod\";\n /** Hide from pickers but still scaffoldable via explicit --template */\n hidden?: boolean;\n /** Include as a built-in connected A2A agent even when hidden from pickers */\n defaultAgent?: boolean;\n /** Always scaffold without prompting (e.g. starter as fallback) */\n alwaysAvailable?: boolean;\n /** Internal workspace packages this template depends on (e.g. \"scheduling\") */\n requiredPackages?: string[];\n /** Core app — featured in the CLI picker, homepage, and docs gallery */\n core?: boolean;\n}\n\nexport const TEMPLATES: TemplateMeta[] = [\n {\n name: \"calendar\",\n label: \"Calendar\",\n hint: \"Agent-native Google Calendar — manage events, sync, and public booking\",\n icon: \"CalendarDays\",\n color: \"#00B5FF\",\n colorRgb: \"0 181 255\",\n devPort: 8082,\n prodUrl: \"https://calendar.agent-native.com\",\n defaultMode: \"prod\",\n requiredPackages: [\"scheduling\"],\n core: true,\n },\n {\n name: \"content\",\n label: \"Content\",\n hint: \"Agent-native Notion/Google Docs — write and organize with agent assistance\",\n icon: \"FileText\",\n color: \"#10B981\",\n colorRgb: \"16 185 129\",\n devPort: 8083,\n prodUrl: \"https://content.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"plan\",\n label: \"Plan\",\n hint: \"Structured visual plans and PR recaps with diagrams, wireframes, prototypes, annotations, and sharing\",\n icon: \"FileText\",\n color: \"#52525B\",\n colorRgb: \"82 82 91\",\n devPort: 8105,\n prodUrl: \"https://plan.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"slides\",\n label: \"Slides\",\n hint: \"Agent-native Google Slides — generate and edit React presentations\",\n icon: \"GalleryHorizontal\",\n color: \"#EC4899\",\n colorRgb: \"236 72 153\",\n devPort: 8086,\n prodUrl: \"https://slides.agent-native.com\",\n defaultMode: \"prod\",\n requiredPackages: [\"pinpoint\"],\n core: true,\n },\n {\n name: \"videos\",\n label: \"Video\",\n hint: \"Agent-native video editing with Remotion\",\n icon: \"Video\",\n color: \"#EF4444\",\n colorRgb: \"239 68 68\",\n devPort: 8087,\n prodUrl: \"https://videos.agent-native.com\",\n defaultMode: \"prod\",\n requiredPackages: [\"pinpoint\"],\n },\n {\n name: \"analytics\",\n label: \"Analytics\",\n hint: \"Agent-native Amplitude/Mixpanel — connect data sources, prompt for charts\",\n icon: \"BarChart2\",\n color: \"#F59E0B\",\n colorRgb: \"245 158 11\",\n devPort: 8088,\n prodUrl: \"https://analytics.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"mail\",\n label: \"Mail\",\n hint: \"Agent-native Superhuman — email client with keyboard shortcuts and AI triage\",\n icon: \"Mail\",\n color: \"#3B82F6\",\n colorRgb: \"59 130 246\",\n devPort: 8085,\n prodUrl: \"https://mail.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"dispatch\",\n label: \"Dispatch\",\n hint: \"Central Slack/Telegram router with jobs, memory, approvals, and A2A delegation\",\n icon: \"MessageCircle\",\n color: \"#14B8A6\",\n colorRgb: \"20 184 166\",\n devPort: 8092,\n prodUrl: \"https://dispatch.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"forms\",\n label: \"Forms\",\n hint: \"Agent-native form builder — create, edit, and manage forms\",\n icon: \"ClipboardList\",\n color: \"#06B6D4\",\n colorRgb: \"6 182 212\",\n devPort: 8084,\n prodUrl: \"https://forms.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"starter\",\n label: \"Starter\",\n hint: \"Minimal scaffold with the agent chat and core architecture wired up\",\n icon: \"Code\",\n color: \"#71717A\",\n colorRgb: \"113 113 122\",\n devPort: 8089,\n prodUrl: \"https://starter.agent-native.com\",\n defaultMode: \"prod\",\n alwaysAvailable: true,\n core: true,\n hidden: true,\n },\n {\n name: \"clips\",\n label: \"Clips\",\n hint: \"Screen recording, meeting notes, and voice dictation — all with AI\",\n icon: \"ScreenShare\",\n color: \"#0EA5E9\",\n colorRgb: \"14 165 233\",\n devPort: 8094,\n prodUrl: \"https://clips.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"brain\",\n label: \"Brain\",\n hint: \"Cited company knowledge from Slack, meetings, transcripts, and decisions\",\n icon: \"Brain\",\n color: \"#8B5CF6\",\n colorRgb: \"139 92 246\",\n devPort: 8102,\n prodUrl: \"https://brain.agent-native.com\",\n defaultMode: \"prod\",\n defaultAgent: true,\n core: true,\n },\n {\n name: \"design\",\n label: \"Design\",\n hint: \"Agent-native design tool — create and edit visual designs with agent assistance\",\n icon: \"Brush\",\n color: \"#F472B6\",\n colorRgb: \"244 114 182\",\n devPort: 8099,\n prodUrl: \"https://design.agent-native.com\",\n defaultMode: \"prod\",\n requiredPackages: [\"pinpoint\", \"embedding\"],\n core: true,\n },\n {\n name: \"assets\",\n label: \"Assets\",\n hint: \"Digital asset manager — upload, organize, search, and generate on-brand images and videos\",\n icon: \"Photo\",\n color: \"#0F766E\",\n colorRgb: \"15 118 110\",\n devPort: 8100,\n prodUrl: \"https://assets.agent-native.com\",\n defaultMode: \"prod\",\n defaultAgent: true,\n core: true,\n },\n {\n name: \"macros\",\n label: \"Macros\",\n hint: \"Internal template — not shown in pickers\",\n icon: \"Code\",\n color: \"#71717A\",\n colorRgb: \"113 113 122\",\n devPort: 8093,\n prodUrl: \"https://macros.agent-native.com\",\n hidden: true,\n defaultMode: \"dev\",\n },\n];\n\n/** Return templates visible in user-facing pickers (excludes hidden). */\nexport function visibleTemplates(): TemplateMeta[] {\n return TEMPLATES.filter((t) => !t.hidden);\n}\n\n/** Return core templates — the featured set shown in CLI pickers by default. */\nexport function coreTemplates(): TemplateMeta[] {\n return TEMPLATES.filter((t) => t.core);\n}\n\n/** Lookup by name. Returns undefined for unknown names. */\nexport function getTemplate(name: string): TemplateMeta | undefined {\n // Tolerate legacy / renamed aliases.\n if (name === \"video\") name = \"videos\";\n if (name === \"image\" || name === \"images\" || name === \"asset\") {\n name = \"assets\";\n }\n if (name === \"contracts\" || name === \"visual-plans\") name = \"plan\";\n return TEMPLATES.find((t) => t.name === name);\n}\n\n/** Names of all templates (including hidden) for validation. */\nexport function allTemplateNames(): string[] {\n return TEMPLATES.map((t) => t.name);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"templates-meta.js","sourceRoot":"","sources":["../../src/cli/templates-meta.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * First-party template metadata used by the `agent-native` CLI.\n *\n * This file is intentionally inlined here (rather than imported from a\n * separate workspace package) so that the published `@agent-native/core`\n * has no `workspace:*` runtime dependencies. Without this inlining, `npx\n * @agent-native/core create ...` fails on a fresh machine with:\n *\n * npm error code EUNSUPPORTEDPROTOCOL\n * npm error Unsupported URL Type \"workspace:\": workspace:*\n *\n * Keep this list in sync with `packages/shared-app-config/templates.ts`,\n * which serves the same metadata to the desktop / mobile / frame packages\n * that always run inside the workspace. Duplication is intentional: the\n * CLI must remain installable outside the monorepo.\n */\n\nexport interface TemplateMeta {\n /** Directory name under templates/ and package name */\n name: string;\n /** Display name in pickers */\n label: string;\n /** One-line description shown in the picker */\n hint: string;\n /** Longer description (optional) */\n description?: string;\n /** Tabler icon name used in the desktop sidebar */\n icon: string;\n /** Hex accent color */\n color: string;\n /** CSS-safe RGB triplet (e.g. \"59 130 246\") */\n colorRgb: string;\n /** Dev server port for desktop `pnpm dev` */\n devPort: number;\n /** Production URL when running as a first-party app on agent-native.com */\n prodUrl?: string;\n /** Default URL path when deployed in a workspace (defaults to \"/<name>\") */\n prodPath?: string;\n /** Default mode when added to desktop app */\n defaultMode?: \"dev\" | \"prod\";\n /** Hide from pickers but still scaffoldable via explicit --template */\n hidden?: boolean;\n /** Include as a built-in connected A2A agent even when hidden from pickers */\n defaultAgent?: boolean;\n /** Always scaffold without prompting (e.g. starter as fallback) */\n alwaysAvailable?: boolean;\n /** Internal workspace packages this template depends on (e.g. \"scheduling\") */\n requiredPackages?: string[];\n /** Core app — featured in the CLI picker, homepage, and docs gallery */\n core?: boolean;\n}\n\nexport const TEMPLATES: TemplateMeta[] = [\n {\n name: \"calendar\",\n label: \"Calendar\",\n hint: \"Agent-native Google Calendar — manage events, sync, and public booking\",\n icon: \"CalendarDays\",\n color: \"#00B5FF\",\n colorRgb: \"0 181 255\",\n devPort: 8082,\n prodUrl: \"https://calendar.agent-native.com\",\n defaultMode: \"prod\",\n requiredPackages: [\"scheduling\"],\n core: true,\n },\n {\n name: \"content\",\n label: \"Content\",\n hint: \"Open-source Obsidian for MDX — edit local docs with agent assistance\",\n icon: \"FileText\",\n color: \"#10B981\",\n colorRgb: \"16 185 129\",\n devPort: 8083,\n prodUrl: \"https://content.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"plan\",\n label: \"Plan\",\n hint: \"Structured visual plans and PR recaps with diagrams, wireframes, prototypes, annotations, and sharing\",\n icon: \"FileText\",\n color: \"#52525B\",\n colorRgb: \"82 82 91\",\n devPort: 8105,\n prodUrl: \"https://plan.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"slides\",\n label: \"Slides\",\n hint: \"Agent-native Google Slides — generate and edit React presentations\",\n icon: \"GalleryHorizontal\",\n color: \"#EC4899\",\n colorRgb: \"236 72 153\",\n devPort: 8086,\n prodUrl: \"https://slides.agent-native.com\",\n defaultMode: \"prod\",\n requiredPackages: [\"pinpoint\"],\n core: true,\n },\n {\n name: \"videos\",\n label: \"Video\",\n hint: \"Agent-native video editing with Remotion\",\n icon: \"Video\",\n color: \"#EF4444\",\n colorRgb: \"239 68 68\",\n devPort: 8087,\n prodUrl: \"https://videos.agent-native.com\",\n defaultMode: \"prod\",\n requiredPackages: [\"pinpoint\"],\n },\n {\n name: \"analytics\",\n label: \"Analytics\",\n hint: \"Agent-native Amplitude/Mixpanel — connect data sources, prompt for charts\",\n icon: \"BarChart2\",\n color: \"#F59E0B\",\n colorRgb: \"245 158 11\",\n devPort: 8088,\n prodUrl: \"https://analytics.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"mail\",\n label: \"Mail\",\n hint: \"Agent-native Superhuman — email client with keyboard shortcuts and AI triage\",\n icon: \"Mail\",\n color: \"#3B82F6\",\n colorRgb: \"59 130 246\",\n devPort: 8085,\n prodUrl: \"https://mail.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"dispatch\",\n label: \"Dispatch\",\n hint: \"Central Slack/Telegram router with jobs, memory, approvals, and A2A delegation\",\n icon: \"MessageCircle\",\n color: \"#14B8A6\",\n colorRgb: \"20 184 166\",\n devPort: 8092,\n prodUrl: \"https://dispatch.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"forms\",\n label: \"Forms\",\n hint: \"Agent-native form builder — create, edit, and manage forms\",\n icon: \"ClipboardList\",\n color: \"#06B6D4\",\n colorRgb: \"6 182 212\",\n devPort: 8084,\n prodUrl: \"https://forms.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"starter\",\n label: \"Starter\",\n hint: \"Minimal scaffold with the agent chat and core architecture wired up\",\n icon: \"Code\",\n color: \"#71717A\",\n colorRgb: \"113 113 122\",\n devPort: 8089,\n prodUrl: \"https://starter.agent-native.com\",\n defaultMode: \"prod\",\n alwaysAvailable: true,\n core: true,\n hidden: true,\n },\n {\n name: \"clips\",\n label: \"Clips\",\n hint: \"Screen recording, meeting notes, and voice dictation — all with AI\",\n icon: \"ScreenShare\",\n color: \"#0EA5E9\",\n colorRgb: \"14 165 233\",\n devPort: 8094,\n prodUrl: \"https://clips.agent-native.com\",\n defaultMode: \"prod\",\n core: true,\n },\n {\n name: \"brain\",\n label: \"Brain\",\n hint: \"Cited company knowledge from Slack, meetings, transcripts, and decisions\",\n icon: \"Brain\",\n color: \"#8B5CF6\",\n colorRgb: \"139 92 246\",\n devPort: 8102,\n prodUrl: \"https://brain.agent-native.com\",\n defaultMode: \"prod\",\n defaultAgent: true,\n core: true,\n },\n {\n name: \"design\",\n label: \"Design\",\n hint: \"Agent-native design tool — create and edit visual designs with agent assistance\",\n icon: \"Brush\",\n color: \"#F472B6\",\n colorRgb: \"244 114 182\",\n devPort: 8099,\n prodUrl: \"https://design.agent-native.com\",\n defaultMode: \"prod\",\n requiredPackages: [\"pinpoint\", \"embedding\"],\n core: true,\n },\n {\n name: \"assets\",\n label: \"Assets\",\n hint: \"Digital asset manager — upload, organize, search, and generate on-brand images and videos\",\n icon: \"Photo\",\n color: \"#0F766E\",\n colorRgb: \"15 118 110\",\n devPort: 8100,\n prodUrl: \"https://assets.agent-native.com\",\n defaultMode: \"prod\",\n defaultAgent: true,\n core: true,\n },\n {\n name: \"macros\",\n label: \"Macros\",\n hint: \"Internal template — not shown in pickers\",\n icon: \"Code\",\n color: \"#71717A\",\n colorRgb: \"113 113 122\",\n devPort: 8093,\n prodUrl: \"https://macros.agent-native.com\",\n hidden: true,\n defaultMode: \"dev\",\n },\n];\n\n/** Return templates visible in user-facing pickers (excludes hidden). */\nexport function visibleTemplates(): TemplateMeta[] {\n return TEMPLATES.filter((t) => !t.hidden);\n}\n\n/** Return core templates — the featured set shown in CLI pickers by default. */\nexport function coreTemplates(): TemplateMeta[] {\n return TEMPLATES.filter((t) => t.core);\n}\n\n/** Lookup by name. Returns undefined for unknown names. */\nexport function getTemplate(name: string): TemplateMeta | undefined {\n // Tolerate legacy / renamed aliases.\n if (name === \"video\") name = \"videos\";\n if (name === \"image\" || name === \"images\" || name === \"asset\") {\n name = \"assets\";\n }\n if (name === \"contracts\" || name === \"visual-plans\") name = \"plan\";\n return TEMPLATES.find((t) => t.name === name);\n}\n\n/** Names of all templates (including hidden) for validation. */\nexport function allTemplateNames(): string[] {\n return TEMPLATES.map((t) => t.name);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-code.d.ts","sourceRoot":"","sources":["../../src/coding-tools/run-code.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AASH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AA8DhE,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EAC7C,IAAI,GAAE,cAAmB,GACxB,WAAW,
|
|
1
|
+
{"version":3,"file":"run-code.d.ts","sourceRoot":"","sources":["../../src/coding-tools/run-code.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AASH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AA8DhE,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EAC7C,IAAI,GAAE,cAAmB,GACxB,WAAW,CA4Mb"}
|