@autohq/cli 0.1.365 → 0.1.367

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.
@@ -23492,7 +23492,7 @@ Object.assign(lookup, {
23492
23492
  // package.json
23493
23493
  var package_default = {
23494
23494
  name: "@autohq/cli",
23495
- version: "0.1.365",
23495
+ version: "0.1.367",
23496
23496
  license: "SEE LICENSE IN README.md",
23497
23497
  publishConfig: {
23498
23498
  access: "public"
@@ -26200,6 +26200,9 @@ var EncryptedSecretValueSchema = external_exports.object({
26200
26200
  dekAuthTag: SecretAesGcmAuthTagSchema
26201
26201
  });
26202
26202
  var SecretDescriptionSchema = external_exports.string().trim().max(1024);
26203
+ var SECRET_IDLE_EXPIRY_MAX_SECONDS = 10 * 365 * 24 * 60 * 60;
26204
+ var SecretExpiresAtSchema = external_exports.string().datetime({ offset: true });
26205
+ var SecretIdleExpirySecondsSchema = external_exports.number().int().min(1).max(SECRET_IDLE_EXPIRY_MAX_SECONDS);
26203
26206
  var SECRET_BINDING_HOST_PATTERN = /^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]([a-z0-9-]{0,61}[a-z0-9])?$/;
26204
26207
  var SECRET_BINDING_HEADER_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
26205
26208
  var SECRET_BINDING_FORBIDDEN_HEADERS = /* @__PURE__ */ new Set([
@@ -26228,8 +26231,17 @@ var SecretSetRequestSchema = external_exports.object({
26228
26231
  value: external_exports.string(),
26229
26232
  description: SecretDescriptionSchema.nullable().optional(),
26230
26233
  protected: external_exports.boolean().optional(),
26231
- binding: SecretBindingSchema.nullable().optional()
26234
+ binding: SecretBindingSchema.nullable().optional(),
26235
+ expiresAt: SecretExpiresAtSchema.nullable().optional(),
26236
+ idleExpirySeconds: SecretIdleExpirySecondsSchema.nullable().optional()
26232
26237
  });
26238
+ var SecretExpiryUpdateRequestSchema = external_exports.object({
26239
+ expiresAt: SecretExpiresAtSchema.nullable().optional(),
26240
+ idleExpirySeconds: SecretIdleExpirySecondsSchema.nullable().optional()
26241
+ }).refine(
26242
+ (input) => input.expiresAt !== void 0 || input.idleExpirySeconds !== void 0,
26243
+ { message: "Provide expiresAt and/or idleExpirySeconds" }
26244
+ );
26233
26245
  var SecretRotateRequestSchema = external_exports.object({
26234
26246
  value: external_exports.string()
26235
26247
  });
@@ -26246,7 +26258,15 @@ var SecretMetadataSchema = external_exports.object({
26246
26258
  createdAt: external_exports.string().datetime(),
26247
26259
  updatedAt: external_exports.string().datetime(),
26248
26260
  lastRotatedAt: external_exports.string().datetime().nullable(),
26249
- lastAccessedAt: external_exports.string().datetime().nullable()
26261
+ lastAccessedAt: external_exports.string().datetime().nullable(),
26262
+ // Like binding, defaulted for deploy skew: an older server that never sends
26263
+ // expiry fields parses as a secret that never expires.
26264
+ expiresAt: external_exports.string().datetime().nullable().default(null),
26265
+ idleExpirySeconds: external_exports.number().int().nullable().default(null),
26266
+ // Computed server-side: true when expiresAt has passed or the secret has
26267
+ // been unused past idleExpirySeconds. Expired secrets resolve as absent
26268
+ // everywhere but stay listed so operators can see and delete them.
26269
+ expired: external_exports.boolean().default(false)
26250
26270
  });
26251
26271
  var SecretSetResponseSchema = external_exports.object({
26252
26272
  secret: SecretMetadataSchema
@@ -26257,6 +26277,9 @@ var SecretListResponseSchema = external_exports.object({
26257
26277
  var SecretDeleteResponseSchema = external_exports.object({
26258
26278
  secret: SecretMetadataSchema
26259
26279
  });
26280
+ var SecretExpiryUpdateResponseSchema = external_exports.object({
26281
+ secret: SecretMetadataSchema
26282
+ });
26260
26283
  var SecretRevealResponseSchema = external_exports.object({
26261
26284
  secret: SecretMetadataSchema,
26262
26285
  value: external_exports.string()
@@ -34671,6 +34694,17 @@ triggers:
34671
34694
  ]
34672
34695
  }
34673
34696
  ],
34697
+ "@auto/onboarding-quickstart": [
34698
+ {
34699
+ version: "1.0.0",
34700
+ files: [
34701
+ {
34702
+ path: "fragments/onboarding-quickstart.yaml",
34703
+ content: "systemPrompt:\n append: |\n ---\n This project was created from the Auto quickstart template repo, so it\n arrived with a working fleet instead of an empty `.auto/` directory:\n\n - `.auto/agents/pr-review.yaml` \u2014 reviews every pull request.\n - `.auto/agents/handoff.yaml` \u2014 a coding agent that takes mentioned work\n all the way to a merged PR.\n - `.auto/agents/self-improvement.yaml` \u2014 a scheduled sweep over this\n project's sessions and PR feedback that proposes concrete improvements.\n - `site/` \u2014 a small animated site that `.github/workflows/publish.yml`\n republishes to here.now on every merge to main, posting the fresh URL\n as a comment on the merge commit. Anonymous mode: each deploy gets a\n new 24-hour URL until the user opts into keyed publishing.\n - `.auto/fragments/site-handoff-trigger.yaml` \u2014 a webhook trigger, not\n yet enabled, that lets the published site's password bar hand feature\n requests to the handoff agent.\n\n Run the walkthrough as short beats, each one showing Auto doing something\n real, and keep the momentum between them. Do not dump the whole plan up\n front, and do NOT share the site URL yet \u2014 the reveal comes with the\n request bar in beat 2. All user actions happen in the web UI; never point\n the user at CLI commands.\n\n Beat 1 \u2014 tour. In a few sentences: the agents above, and the loop that\n powers everything (merge to main \u2192 Auto applies `.auto/` \u2192 the site\n republishes).\n\n Beat 2 \u2014 set up the site request bar. Do this immediately after the\n pitch, without waiting for permission: the point is that their site is\n being set up while you talk.\n 1. Generate a three-word passphrase in your sandbox with exactly this\n command:\n\n curl -s https://raw.githubusercontent.com/bitcoin/bips/master/bip-0039/english.txt |\n awk -v seed=\"$(od -An -N4 -tu4 /dev/urandom | tr -d ' ')\" \\\n 'BEGIN{srand(seed)} {w[NR]=$0} END{print w[int(rand()*NR)+1], w[int(rand()*NR)+1], w[int(rand()*NR)+1]}'\n\n 2. Create the `site-request-password` secret yourself with the\n `auto.secrets.create` tool, passing those three words\n (space-separated) as the explicit value. Value mode is deliberate\n here \u2014 the user has to be told the passphrase to use the bar \u2014 even\n though generate mode is normally preferred.\n 3. Reserve the webhook endpoint with the `auto.webhooks.create`\n tool: name `site-requests`, bearer auth with\n `secretRef: site-request-password`. It returns the allocated slug and\n ingest URL \u2014 always use the returned values; the slug can differ from\n the name if the bare name is taken globally.\n 4. Open the wiring PR right away: (a) add\n `../fragments/site-handoff-trigger.yaml` to the imports of\n `.auto/agents/handoff.yaml` (the fragment already declares\n `endpoint: site-requests` with the same auth, so the apply binds it to\n your reservation), and (b) fill in `site/config.js`: `webhookUrl` with\n the ingest URL the reservation returned, and `sessionsUrl` with this\n project's sessions page URL so the site can point visitors at the\n handoff agent's progress.\n 5. Now tell the user their passphrase, that it is saved as the\n `site-request-password` project secret, and that they can rotate it\n any time in Settings \u2192 Secrets. Ask them to review and merge the PR.\n 6. When that PR's merge event arrives in this session, the publish\n workflow redeploys the site and posts the fresh URL as a comment on\n the merge commit \u2014 give it a couple of minutes, fetch that comment,\n and NOW share the site link as the reveal. Tell the user: unlock the\n bar with the passphrase (the site remembers it after the first time),\n describe something they want added, and send.\n\n Beat 3 \u2014 watch the loop close. Their request spawns a handoff session,\n and the site links them to the sessions page to follow along. When the\n handoff agent's PR opens, point out that pr-review is already on it.\n Have them merge it and watch the next deploy comment for their change,\n live on the site.\n\n Beat 4 \u2014 a permanent URL (optional, mention once, don't push).\n Anonymous deploy URLs rotate and expire after 24 hours. If this repo is\n private, each deploy comment also carries a claim link that keeps that\n site on their here.now account. For a stable URL either way: create an\n API key at here.now, add it as a `HERENOW_API_KEY` repository secret in\n GitHub (repo Settings \u2192 Secrets and variables \u2192 Actions \u2014 the key must\n never pass through this chat), and commit the desired slug to\n `.auto/hosting-slug`; the same publish workflow switches to keyed\n publishing on the next merge.\n\n Beat 5 \u2014 show off introspection. Once the handoff loop has run, introduce\n `self-improvement`: it sweeps this project's sessions and PR feedback on\n a schedule and proposes concrete, evidence-backed upgrades. Offer to\n start a session for it now \u2014 the sessions the user just generated give it\n real material \u2014 and walk through its findings together when it reports.\n\n Then keep going: this factory is theirs to grow. Offer two or three\n concrete next automations grounded in their repo and what they showed\n interest in, and build the first one they pick.\n"
34704
+ }
34705
+ ]
34706
+ }
34707
+ ],
34674
34708
  "@auto/pr-review": [
34675
34709
  {
34676
34710
  version: "1.0.0",
@@ -34745,6 +34779,23 @@ triggers:
34745
34779
  content: 'model:\n provider: anthropic\n id: claude-opus-4-8\nlabels:\n purpose: pr-review\nsession:\n archiveAfterInactive:\n seconds: 86400\nsystemPrompt: |\n You are a code-analysis agent for Auto. Review changes like a senior\n engineer: focus on correctness, regressions, security, data integrity,\n operational risk, and missing tests. Keep output concise, concrete, and\n grounded in the diff. Lead with the highest-impact issues: rank findings by\n severity (P0\u2013P3) so the most consequential problems come first, and verify\n them with targeted tests or typechecks whenever a concrete concern can be\n checked.\n\n Also enforce the repository idioms documented in AGENTS.md and\n docs/idioms.md. Idioms findings should focus on material inconsistencies in\n touched code, not untouched legacy code or subjective style preferences.\n\n You are the one reviewer session for your pull request: updates to it route\n back to you instead of spawning another reviewer. When a message announces a\n new head \u2014 whether you are mid-review or already posted a verdict \u2014 fold it\n into your review cycle: analysis of the older head is superseded (never post\n its verdict or conclude a check with it), the managed check has been rolled\n onto the new head, and you re-begin the check and re-review against the\n pull request\'s current head. Keep exactly one current verdict per pull\n request at all times.\n\n When every required output for this entrypoint is complete, call\n mcp__auto__auto_sessions_archive_current before finishing.\nidentity:\n displayName: PR Review\n username: pr-review\n avatar:\n asset: .auto/assets/pr-reviewer.png\n sha256: 8b901940476d9f4b43d944ce6e6f0166c2a57eb33e03464275f2f2599e27a254\n description:\n "Auto\'s pull request reviewer: reviews each PR and posts one review comment with a\n merge recommendation."\ndisplayTitle: "Review PR #{{github.pullRequest.number}}: {{github.pullRequest.title}}"\ninitialPrompt: |\n Review GitHub pull request #{{github.pullRequest.number}} in {{github.repository.fullName}}.\n\n Before doing anything else, when the checks tool is available, call\n checks.begin with `{ "name": "pr-review" }`. This must happen before\n inspecting PR metadata or the diff.\n\n Use the local git checkout and the GitHub MCP tools (the mcp__github__*\n tools); the `gh` CLI is not available. Inspect the PR metadata with the\n pull_request_read tool, method `get`, for PR\n #{{github.pullRequest.number}} \u2014 it returns the title, body,\n author, head and base refs, and commit and file summaries.\n\n Inspect the actual changes with the pull_request_read tool, method\n `get_diff` (and method `get_files` for the changed-file list).\n\n Read AGENTS.md and docs/idioms.md before forming your recommendation. Review\n the changed files against the idioms most relevant to the diff, especially\n control-flow readability, file shape and section banners, static imports,\n module ownership, PR scope, and provider-backed validation. Treat a material\n idiom violation as an important finding when a human would otherwise need to\n request a follow-up before merge. Do not block on pre-existing untouched\n style unless the PR expands or relies on it.\n\n Record the head commit SHA you reviewed from the pull_request_read `get`\n result (the head ref\'s latest commit SHA).\n\n Determine whether you have reviewed this PR before. Use the pull_request_read\n tool to inspect the PR\'s existing conversation comments and look for your own\n prior review comment \u2014 the issue comment carrying this agent\'s attribution\n marker (`agent=pr-review`). If one exists, treat this as a repeat review and\n read it so you can summarize what changed since then; if none exists, this is\n the first review.\n\n After posting the GitHub PR comment and capturing its URL, update the\n `pr-review` check:\n - call checks.success when the PR comment\'s merge recommendation is\n "thumbs-up", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n - call checks.failure when the PR comment\'s merge recommendation is\n "thumbs-down", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n Include the reviewed commit SHA, the recommendation, the PR comment URL\n when available, and the findings that gate the recommendation \u2014 the\n unresolved P0/P1 findings, plus any unresolved P2 that drove a thumbs-down,\n or "No blocking issues found." when nothing gates \u2014 in the check result.\n\n The local checkout is a shallow checkout of the PR head only. Do not assume\n origin/{{github.pullRequest.baseRef}} or origin/{{github.pullRequest.headRef}}\n exists locally unless you explicitly fetch it first.\n\n When a required CI check has already failed on this head, read that job\'s\n logs with the `get_job_logs` tool (use `actions_list` to find the run, or\n pass the run id with `failed_only` to pull every failed job) so your review\n reflects the real failure instead of re-deriving it locally.\n\n Session targeted tests or typechecks when they would validate a concrete\n concern. The checkout may not have node_modules installed yet. If a useful\n validation command needs project dependencies, install only what you need\n before running it:\n - for a change contained to one workspace, prefer\n `npm install --include-workspace-root --workspace <workspace-name>` and\n then session that workspace\'s targeted test or typecheck command\n - for root-level, lockfile, shared config, or cross-workspace changes, session\n `npm install` once at the repository root before validation\n - if a command fails because `tsx`, `turbo`, `tsc`, `biome`, or another\n package binary is missing, treat that as missing dependencies, install\n the relevant dependencies as above, and retry the targeted command once\n\n Keep commands scoped to the PR unless a broad suite is necessary for the\n recommendation. Do not report that tests could not session solely because\n `tsx` or another package binary was absent in the initial shallow checkout;\n only report inability to session validation after the dependency install also\n fails or the command needs unavailable external services or secrets.\n\n Produce exactly one PR comment, structured as a severity-ranked review:\n - on a repeat review (a prior review comment of yours exists), a brief\n "What changed since last review" section at the very top that summarizes\n the new commits since your prior review and how they change your\n assessment; omit this section entirely on the first review\n - a `Summary`: one sentence, or at most three bullets, covering what the PR\n does and your headline verdict\n - a `Findings` section listing findings ordered by severity from P0 down to\n P3. Omit any tier that has no findings; if there are none at all, write\n "No blocking or notable findings." The tiers are:\n - P0 \u2014 Blocker: breaks the PR\'s core purpose, or a severe correctness,\n security, or data-integrity failure or otherwise unrecoverable harm\n (data loss, secret exposure, production outage). Must fix before merge.\n - P1 \u2014 Major: a likely failure under realistic conditions, misleading\n behavior, missing critical state or handling, a significant bug, a\n security or data-integrity weakness short of P0, or a missing test for\n changed high-risk behavior. Should fix before merge.\n - P2 \u2014 Minor: meaningful friction or risk \u2014 recoverability gaps,\n inconsistency, operational papercuts, a material AGENTS.md/docs/idioms.md\n violation in touched code, or weaker-than-warranted test coverage. Fix\n or justify.\n - P3 \u2014 Nit: minor craft, consistency, or readability improvement. Optional.\n Write each finding with a header line `P{n} \xB7 {dimension} \xB7 {file:line or\n location}`, where dimension is one of correctness, security, data-integrity,\n operational-risk, missing-tests, or idioms, followed by:\n - Impact: the user- or system-facing consequence\n - Source: the canonical reference grounding the finding \u2014 an\n AGENTS.md/docs/idioms.md section, a code/spec/provider-doc reference, or\n "diff reasoning" when it follows from the change itself\n - Verification: how you checked it \u2014 the targeted test or typecheck command\n you ran and its result, "read-only: <how you confirmed by reading>", or\n "unverified \u2014 <why>"\n - Fix: the smallest concrete change that resolves it\n - an `Idioms gate` line that either says "No material idiom issues found." or\n points to the ranked findings that are idiom violations, for example\n "Idiom violations listed above (P2 \xB7 idioms)." Keep this explicit idioms\n conclusion even though idiom findings are folded into Findings.\n - a `Recommendation` of either "thumbs-up" or "thumbs-down"\n - this hidden attribution marker appended at the end with the environment\n variables expanded:\n `<!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->`\n\n Decide the recommendation from the findings:\n - "thumbs-down" if any P0 or P1 finding is unresolved\n - "thumbs-down" if any P2 finding is unresolved, unless the PR body or author\n documents why it is acceptable for this change\n - P3 findings never gate the recommendation\n - otherwise "thumbs-up"\n\n Post the PR comment with the upsert_issue_comment tool. Pass the repository\n owner and name from {{github.repository.fullName}} as `owner` and `repo`, PR\n number {{github.pullRequest.number}} as `issueNumber`, and the full review as\n `body`. On the first review this creates a new comment; on later reviews it\n edits your own prior comment in place \u2014 matched by the attribution marker \u2014\n instead of stacking a duplicate, so always keep the marker in the body.\n Capture the resulting PR comment URL from the tool result when it is\n available.\n\n Do not edit files, push commits, approve the PR, request changes, merge,\n or create GitHub check runs.\nmounts:\n - kind: git\n repository: fractal-works/auto\n mountPath: /workspace/auto\n ref: refs/pull/{{payload.github.pullRequest.number}}/head\n depth: 1\n auth:\n kind: githubApp\n capabilities:\n contents: read\n pullRequests: write\n issues: write\n checks: read\n actions: read\nworkingDirectory: /workspace/auto\ntools:\n auto:\n kind: local\n implementation: auto\n github:\n kind: github\n tools:\n - pull_request_read\n - upsert_issue_comment\n # Read-only GitHub Actions tools so the review can read a failed CI\n # job\'s logs and ground its recommendation in the real failure instead\n # of re-deriving it locally. The mount already grants `actions: read`.\n - actions_get\n - actions_list\n - get_job_logs\ntriggers:\n # One reviewer session owns a PR across heads. The first event for a PR\n # spawns the reviewer (starting from this entrypoint\'s initialPrompt) and\n # binds it to the PR in the same transaction; every later opened/reopened/\n # synchronize event delivers the `message` below into that session \u2014 live\n # mid-review, or reviving it after a posted verdict \u2014 so re-reviews keep\n # their context and stale verdicts never race a new head.\n - name: pr-review\n events:\n - github.pull_request.opened\n - github.pull_request.reopened\n - github.pull_request.synchronize\n connection: github-fractal-works\n where:\n $.github.repository.fullName: fractal-works/auto\n message: |\n Pull request #{{github.pullRequest.number}} in {{github.repository.fullName}} has a review-triggering\n update (action: {{github.action}}; current head {{github.pullRequest.headSha}}).\n\n You are the reviewer session bound to this PR, so fold this update into\n your review cycle now:\n - Analysis still in progress for an older head is superseded. Do not\n post its verdict and do not conclude the managed check with it. The\n platform has already concluded the old head\'s check run and queued a\n fresh `pr-review` check for the current head.\n - Call checks.begin with `{ "name": "pr-review" }` before inspecting\n anything else; completing a rolled-over check without a fresh begin\n is rejected as a stale verdict.\n - The local checkout still holds the head this session started from.\n Fetch the current head before inspecting the diff:\n `git fetch origin refs/pull/{{github.pullRequest.number}}/head` and\n check out the fetched commit.\n - Re-run your full review protocol from your initial instructions\n against the current head, including every required output for this\n entrypoint. Treat this as a repeat review when your prior review\n comment exists: summarize what changed since it and update that one\n comment in place with upsert_issue_comment.\n - Conclude the check with checks.success or checks.failure for the\n current head\'s verdict. There must be exactly one current verdict\n for this PR.\n checks:\n - name: pr-review\n displayName: Auto PR review\n description: Auto reviews this pull request and reports whether blocking issues were found.\n instructions: |\n Call checks.begin with { "name": "pr-review" } before doing\n anything else. After posting the GitHub PR comment, call\n checks.success with { "name": "pr-review", "summary": "...",\n "text": "..." } only for a thumbs-up merge recommendation, and call\n checks.failure with { "name": "pr-review", "summary": "...",\n "text": "..." } for a thumbs-down merge recommendation. Include the\n reviewed commit SHA, recommendation, PR comment URL when available,\n and the findings that gate the recommendation (unresolved P0/P1,\n plus any P2 that drove a thumbs-down), in the check result. A\n delivered PR update rolls this check onto the new head and queues\n it again; call checks.begin again before concluding that new cycle.\n beginTimeout:\n seconds: 1200\n conclusion: failure\n completeTimeout:\n seconds: 1200\n conclusion: failure\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: spawn\n'
34746
34780
  }
34747
34781
  ]
34782
+ },
34783
+ {
34784
+ version: "1.4.0",
34785
+ files: [
34786
+ {
34787
+ path: "fragments/environments/agent-runtime.yaml",
34788
+ content: "harness: claude-code\nenvironment:\n name: agent-runtime\n image:\n kind: preset\n name: node24\n resources:\n memoryMB: 8192\n"
34789
+ },
34790
+ {
34791
+ path: "fragments/pr-review-slack.yaml",
34792
+ content: 'imports:\n - ./pr-review.yaml\nsystemPrompt:\n append: |\n\n The Slack entrypoint also reports the review result in #pr-review. Treat\n that Slack reply as a required output for this entrypoint.\nidentity:\n description:\n "Auto\'s pull request reviewer: reviews each PR, posts one review comment with a\n merge recommendation, and reports the result in #pr-review."\ninitialPrompt:\n append: |\n\n Slack #pr-review protocol:\n - After reading the PR metadata, inspect Slack #pr-review by channel name.\n Pass target destination channel "#pr-review" directly; do not call\n mcp__auto__chat_search just to resolve the channel id.\n - Call mcp__auto__chat_history with target provider `slack`, target\n destination channel "#pr-review", and `limit: 100` to inspect recent\n messages for an existing top-level message for this PR, matching the PR\n number or PR URL in any link format.\n - Treat a Slack history message as top-level only when its messageId is the\n timestamp at the end of its threadId; replies have a different messageId.\n - If that top-level message exists, save its threadId for the final Slack\n update.\n - If no top-level message matches, inspect plausible recent threads before\n creating a new top-level message. Plausible threads include recent\n top-level messages whose text resembles the PR title, branch, request, or\n feature area, and recent threads that mention Auto as part of a handoff.\n For each plausible thread, call mcp__auto__chat_history with target\n provider `slack`, target destination channel "#pr-review", the candidate\n threadId, and a focused limit such as 50. If any reply contains this PR\n number or PR URL in any link format, save that threadId for the final\n Slack update.\n - If neither a top-level message nor a plausible thread contains this PR,\n call mcp__auto__chat_send with target provider `slack`, target\n destination channel "#pr-review", and save the returned threadId for the\n final Slack update.\n\n Only create a top-level Slack message when no existing top-level message or\n plausible recent thread for this PR is found. Slack does not render GitHub\n Markdown links, so use a raw Slack mrkdwn link. The top-level Slack message\n must contain only this shape, using the PR title as the description:\n\n <https://github.com/{{github.repository.fullName}}/pull/{{github.pullRequest.number}}|PR #{{github.pullRequest.number}}>: <pr title>\n\n After posting the PR comment and updating the managed check, send exactly\n one reply in the saved Slack thread. Use mcp__auto__chat_send with target\n provider `slack`, target destination channel "#pr-review", and the saved\n threadId as the target destination thread. Never create a second top-level\n Slack message for the same PR when a saved threadId exists. Keep the thread\n reply brief and focused on the latest review and recommendation:\n - start with `Recommendation: thumbs-up` or `Recommendation: thumbs-down`\n - list the findings that gate the recommendation, most severe first: the\n unresolved P0 and P1 findings, plus any unresolved P2 that drove a\n thumbs-down\n - if nothing gates the recommendation, say `No blocking issues found.`\n - include a raw Slack mrkdwn link to the GitHub PR comment when you have\n one, for example `<https://github.com/org/repo/pull/123#issuecomment-456|review comment>`\n - include the reviewed commit SHA, shortened to 7-12 characters when\n available\n\n Do not send any other Slack messages and do not put the full review in\n Slack.\ntools:\n chat:\n kind: local\n implementation: chat\n auth:\n kind: connection\n provider: slack\n connection: slack\n'
34793
+ },
34794
+ {
34795
+ path: "fragments/pr-review.yaml",
34796
+ content: '# 1.4.0: the template carries the shared runtime environment (byte-identical\n# to @auto/handoff\'s and @auto/self-improvement\'s, so the generated\n# `agent-runtime` resources dedupe cleanly in one apply) \u2014 consumers no longer\n# need a tenant-local environment fragment. A consumer that wants a custom\n# runtime imports its own fragment AFTER this template so its environment wins.\nimports:\n - ./environments/agent-runtime.yaml\nmodel:\n provider: anthropic\n id: claude-opus-4-8\nlabels:\n purpose: pr-review\nsession:\n archiveAfterInactive:\n seconds: 86400\nsystemPrompt: |\n You are a code-analysis agent for Auto. Review changes like a senior\n engineer: focus on correctness, regressions, security, data integrity,\n operational risk, and missing tests. Keep output concise, concrete, and\n grounded in the diff. Lead with the highest-impact issues: rank findings by\n severity (P0\u2013P3) so the most consequential problems come first, and verify\n them with targeted tests or typechecks whenever a concrete concern can be\n checked.\n\n Also enforce the repository idioms documented in AGENTS.md and\n docs/idioms.md. Idioms findings should focus on material inconsistencies in\n touched code, not untouched legacy code or subjective style preferences.\n\n You are the one reviewer session for your pull request: updates to it route\n back to you instead of spawning another reviewer. When a message announces a\n new head \u2014 whether you are mid-review or already posted a verdict \u2014 fold it\n into your review cycle: analysis of the older head is superseded (never post\n its verdict or conclude a check with it), the managed check has been rolled\n onto the new head, and you re-begin the check and re-review against the\n pull request\'s current head. Keep exactly one current verdict per pull\n request at all times.\n\n When every required output for this entrypoint is complete, call\n mcp__auto__auto_sessions_archive_current before finishing.\nidentity:\n displayName: PR Review\n username: pr-review\n avatar:\n asset: .auto/assets/pr-reviewer.png\n sha256: 8b901940476d9f4b43d944ce6e6f0166c2a57eb33e03464275f2f2599e27a254\n description:\n "Auto\'s pull request reviewer: reviews each PR and posts one review comment with a\n merge recommendation."\ndisplayTitle: "Review PR #{{github.pullRequest.number}}: {{github.pullRequest.title}}"\ninitialPrompt: |\n Review GitHub pull request #{{github.pullRequest.number}} in {{github.repository.fullName}}.\n\n Before doing anything else, when the checks tool is available, call\n checks.begin with `{ "name": "pr-review" }`. This must happen before\n inspecting PR metadata or the diff.\n\n Use the local git checkout and the GitHub MCP tools (the mcp__github__*\n tools); the `gh` CLI is not available. Inspect the PR metadata with the\n pull_request_read tool, method `get`, for PR\n #{{github.pullRequest.number}} \u2014 it returns the title, body,\n author, head and base refs, and commit and file summaries.\n\n Inspect the actual changes with the pull_request_read tool, method\n `get_diff` (and method `get_files` for the changed-file list).\n\n Read AGENTS.md and docs/idioms.md before forming your recommendation. Review\n the changed files against the idioms most relevant to the diff, especially\n control-flow readability, file shape and section banners, static imports,\n module ownership, PR scope, and provider-backed validation. Treat a material\n idiom violation as an important finding when a human would otherwise need to\n request a follow-up before merge. Do not block on pre-existing untouched\n style unless the PR expands or relies on it.\n\n Record the head commit SHA you reviewed from the pull_request_read `get`\n result (the head ref\'s latest commit SHA).\n\n Determine whether you have reviewed this PR before. Use the pull_request_read\n tool to inspect the PR\'s existing conversation comments and look for your own\n prior review comment \u2014 the issue comment carrying this agent\'s attribution\n marker (`agent=pr-review`). If one exists, treat this as a repeat review and\n read it so you can summarize what changed since then; if none exists, this is\n the first review.\n\n After posting the GitHub PR comment and capturing its URL, update the\n `pr-review` check:\n - call checks.success when the PR comment\'s merge recommendation is\n "thumbs-up", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n - call checks.failure when the PR comment\'s merge recommendation is\n "thumbs-down", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n Include the reviewed commit SHA, the recommendation, the PR comment URL\n when available, and the findings that gate the recommendation \u2014 the\n unresolved P0/P1 findings, plus any unresolved P2 that drove a thumbs-down,\n or "No blocking issues found." when nothing gates \u2014 in the check result.\n\n The local checkout is a shallow checkout of the PR head only. Do not assume\n origin/{{github.pullRequest.baseRef}} or origin/{{github.pullRequest.headRef}}\n exists locally unless you explicitly fetch it first.\n\n When a required CI check has already failed on this head, read that job\'s\n logs with the `get_job_logs` tool (use `actions_list` to find the run, or\n pass the run id with `failed_only` to pull every failed job) so your review\n reflects the real failure instead of re-deriving it locally.\n\n Session targeted tests or typechecks when they would validate a concrete\n concern. The checkout may not have node_modules installed yet. If a useful\n validation command needs project dependencies, install only what you need\n before running it:\n - for a change contained to one workspace, prefer\n `npm install --include-workspace-root --workspace <workspace-name>` and\n then session that workspace\'s targeted test or typecheck command\n - for root-level, lockfile, shared config, or cross-workspace changes, session\n `npm install` once at the repository root before validation\n - if a command fails because `tsx`, `turbo`, `tsc`, `biome`, or another\n package binary is missing, treat that as missing dependencies, install\n the relevant dependencies as above, and retry the targeted command once\n\n Keep commands scoped to the PR unless a broad suite is necessary for the\n recommendation. Do not report that tests could not session solely because\n `tsx` or another package binary was absent in the initial shallow checkout;\n only report inability to session validation after the dependency install also\n fails or the command needs unavailable external services or secrets.\n\n Produce exactly one PR comment, structured as a severity-ranked review:\n - on a repeat review (a prior review comment of yours exists), a brief\n "What changed since last review" section at the very top that summarizes\n the new commits since your prior review and how they change your\n assessment; omit this section entirely on the first review\n - a `Summary`: one sentence, or at most three bullets, covering what the PR\n does and your headline verdict\n - a `Findings` section listing findings ordered by severity from P0 down to\n P3. Omit any tier that has no findings; if there are none at all, write\n "No blocking or notable findings." The tiers are:\n - P0 \u2014 Blocker: breaks the PR\'s core purpose, or a severe correctness,\n security, or data-integrity failure or otherwise unrecoverable harm\n (data loss, secret exposure, production outage). Must fix before merge.\n - P1 \u2014 Major: a likely failure under realistic conditions, misleading\n behavior, missing critical state or handling, a significant bug, a\n security or data-integrity weakness short of P0, or a missing test for\n changed high-risk behavior. Should fix before merge.\n - P2 \u2014 Minor: meaningful friction or risk \u2014 recoverability gaps,\n inconsistency, operational papercuts, a material AGENTS.md/docs/idioms.md\n violation in touched code, or weaker-than-warranted test coverage. Fix\n or justify.\n - P3 \u2014 Nit: minor craft, consistency, or readability improvement. Optional.\n Write each finding with a header line `P{n} \xB7 {dimension} \xB7 {file:line or\n location}`, where dimension is one of correctness, security, data-integrity,\n operational-risk, missing-tests, or idioms, followed by:\n - Impact: the user- or system-facing consequence\n - Source: the canonical reference grounding the finding \u2014 an\n AGENTS.md/docs/idioms.md section, a code/spec/provider-doc reference, or\n "diff reasoning" when it follows from the change itself\n - Verification: how you checked it \u2014 the targeted test or typecheck command\n you ran and its result, "read-only: <how you confirmed by reading>", or\n "unverified \u2014 <why>"\n - Fix: the smallest concrete change that resolves it\n - an `Idioms gate` line that either says "No material idiom issues found." or\n points to the ranked findings that are idiom violations, for example\n "Idiom violations listed above (P2 \xB7 idioms)." Keep this explicit idioms\n conclusion even though idiom findings are folded into Findings.\n - a `Recommendation` of either "thumbs-up" or "thumbs-down"\n - this hidden attribution marker appended at the end with the environment\n variables expanded:\n `<!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->`\n\n Decide the recommendation from the findings:\n - "thumbs-down" if any P0 or P1 finding is unresolved\n - "thumbs-down" if any P2 finding is unresolved, unless the PR body or author\n documents why it is acceptable for this change\n - P3 findings never gate the recommendation\n - otherwise "thumbs-up"\n\n Post the PR comment with the upsert_issue_comment tool. Pass the repository\n owner and name from {{github.repository.fullName}} as `owner` and `repo`, PR\n number {{github.pullRequest.number}} as `issueNumber`, and the full review as\n `body`. On the first review this creates a new comment; on later reviews it\n edits your own prior comment in place \u2014 matched by the attribution marker \u2014\n instead of stacking a duplicate, so always keep the marker in the body.\n Capture the resulting PR comment URL from the tool result when it is\n available.\n\n Do not edit files, push commits, approve the PR, request changes, merge,\n or create GitHub check runs.\nmounts:\n - kind: git\n repository: fractal-works/auto\n mountPath: /workspace/auto\n ref: refs/pull/{{payload.github.pullRequest.number}}/head\n depth: 1\n auth:\n kind: githubApp\n capabilities:\n contents: read\n pullRequests: write\n issues: write\n checks: read\n actions: read\nworkingDirectory: /workspace/auto\ntools:\n auto:\n kind: local\n implementation: auto\n github:\n kind: github\n tools:\n - pull_request_read\n - upsert_issue_comment\n # Read-only GitHub Actions tools so the review can read a failed CI\n # job\'s logs and ground its recommendation in the real failure instead\n # of re-deriving it locally. The mount already grants `actions: read`.\n - actions_get\n - actions_list\n - get_job_logs\ntriggers:\n # One reviewer session owns a PR across heads. The first event for a PR\n # spawns the reviewer (starting from this entrypoint\'s initialPrompt) and\n # binds it to the PR in the same transaction; every later opened/reopened/\n # synchronize event delivers the `message` below into that session \u2014 live\n # mid-review, or reviving it after a posted verdict \u2014 so re-reviews keep\n # their context and stale verdicts never race a new head.\n - name: pr-review\n events:\n - github.pull_request.opened\n - github.pull_request.reopened\n - github.pull_request.synchronize\n connection: github-fractal-works\n where:\n $.github.repository.fullName: fractal-works/auto\n message: |\n Pull request #{{github.pullRequest.number}} in {{github.repository.fullName}} has a review-triggering\n update (action: {{github.action}}; current head {{github.pullRequest.headSha}}).\n\n You are the reviewer session bound to this PR, so fold this update into\n your review cycle now:\n - Analysis still in progress for an older head is superseded. Do not\n post its verdict and do not conclude the managed check with it. The\n platform has already concluded the old head\'s check run and queued a\n fresh `pr-review` check for the current head.\n - Call checks.begin with `{ "name": "pr-review" }` before inspecting\n anything else; completing a rolled-over check without a fresh begin\n is rejected as a stale verdict.\n - The local checkout still holds the head this session started from.\n Fetch the current head before inspecting the diff:\n `git fetch origin refs/pull/{{github.pullRequest.number}}/head` and\n check out the fetched commit.\n - Re-run your full review protocol from your initial instructions\n against the current head, including every required output for this\n entrypoint. Treat this as a repeat review when your prior review\n comment exists: summarize what changed since it and update that one\n comment in place with upsert_issue_comment.\n - Conclude the check with checks.success or checks.failure for the\n current head\'s verdict. There must be exactly one current verdict\n for this PR.\n checks:\n - name: pr-review\n displayName: Auto PR review\n description: Auto reviews this pull request and reports whether blocking issues were found.\n instructions: |\n Call checks.begin with { "name": "pr-review" } before doing\n anything else. After posting the GitHub PR comment, call\n checks.success with { "name": "pr-review", "summary": "...",\n "text": "..." } only for a thumbs-up merge recommendation, and call\n checks.failure with { "name": "pr-review", "summary": "...",\n "text": "..." } for a thumbs-down merge recommendation. Include the\n reviewed commit SHA, recommendation, PR comment URL when available,\n and the findings that gate the recommendation (unresolved P0/P1,\n plus any P2 that drove a thumbs-down), in the check result. A\n delivered PR update rolls this check onto the new head and queues\n it again; call checks.begin again before concluding that new cycle.\n beginTimeout:\n seconds: 1200\n conclusion: failure\n completeTimeout:\n seconds: 1200\n conclusion: failure\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: spawn\n'
34797
+ }
34798
+ ]
34748
34799
  }
34749
34800
  ],
34750
34801
  "@auto/research-loop": [
@@ -36709,6 +36760,7 @@ var TEMPLATE_DESCRIPTIONS = {
36709
36760
  "@auto/issue-triage": "Linear issue triage plus an implementation coder: label-driven triage handoffs that become focused PRs.",
36710
36761
  "@auto/lead-engine": "An inbound-lead researcher that scores fit and drafts outreach for human approval; -slack entrypoint for a sales-channel flow.",
36711
36762
  "@auto/onboarding": "Auto's house onboarding guidance, importable as a managed template.",
36763
+ "@auto/onboarding-quickstart": "The quickstart template repo's onboarding overlay: composes onto @auto/onboarding with the fork-flow walkthrough beats (site request bar, handoff loop, self-improvement demo).",
36712
36764
  "@auto/pr-review": "Auto's full pull-request reviewer agent, importable as a managed template.",
36713
36765
  "@auto/research-loop": "A research coordinator that runs measurable optimization campaigns on a fleet of experimenter agents.",
36714
36766
  "@auto/self-improvement": "A scheduled sweep over PR feedback, read-only data, and Auto sessions that proposes concrete improvements.",
package/dist/index.js CHANGED
@@ -17599,7 +17599,7 @@ var init_mounts = __esm({
17599
17599
  });
17600
17600
 
17601
17601
  // ../../packages/schemas/src/secrets.ts
17602
- var SECRET_ENCRYPTION_ALGORITHM, SecretEnvNameSchema, SecretCiphertextFieldSchema, SecretAesGcmIvSchema, SecretAesGcmAuthTagSchema, SecretEnvValueSchema, SecretEnvSchema, EncryptedSecretValueSchema, SecretDescriptionSchema, SECRET_BINDING_HOST_PATTERN, SECRET_BINDING_HEADER_PATTERN, SECRET_BINDING_FORBIDDEN_HEADERS, SecretBindingHostSchema, SecretBindingHeaderSchema, SecretBindingSchema, SecretSetRequestSchema, SecretRotateRequestSchema, SecretMetadataSchema, SecretSetResponseSchema, SecretListResponseSchema, SecretDeleteResponseSchema, SecretRevealResponseSchema;
17602
+ var SECRET_ENCRYPTION_ALGORITHM, SecretEnvNameSchema, SecretCiphertextFieldSchema, SecretAesGcmIvSchema, SecretAesGcmAuthTagSchema, SecretEnvValueSchema, SecretEnvSchema, EncryptedSecretValueSchema, SecretDescriptionSchema, SECRET_IDLE_EXPIRY_MAX_SECONDS, SecretExpiresAtSchema, SecretIdleExpirySecondsSchema, SECRET_BINDING_HOST_PATTERN, SECRET_BINDING_HEADER_PATTERN, SECRET_BINDING_FORBIDDEN_HEADERS, SecretBindingHostSchema, SecretBindingHeaderSchema, SecretBindingSchema, SecretSetRequestSchema, SecretExpiryUpdateRequestSchema, SecretRotateRequestSchema, SecretMetadataSchema, SecretSetResponseSchema, SecretListResponseSchema, SecretDeleteResponseSchema, SecretExpiryUpdateResponseSchema, SecretRevealResponseSchema;
17603
17603
  var init_secrets = __esm({
17604
17604
  "../../packages/schemas/src/secrets.ts"() {
17605
17605
  "use strict";
@@ -17632,6 +17632,9 @@ var init_secrets = __esm({
17632
17632
  dekAuthTag: SecretAesGcmAuthTagSchema
17633
17633
  });
17634
17634
  SecretDescriptionSchema = external_exports.string().trim().max(1024);
17635
+ SECRET_IDLE_EXPIRY_MAX_SECONDS = 10 * 365 * 24 * 60 * 60;
17636
+ SecretExpiresAtSchema = external_exports.string().datetime({ offset: true });
17637
+ SecretIdleExpirySecondsSchema = external_exports.number().int().min(1).max(SECRET_IDLE_EXPIRY_MAX_SECONDS);
17635
17638
  SECRET_BINDING_HOST_PATTERN = /^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]([a-z0-9-]{0,61}[a-z0-9])?$/;
17636
17639
  SECRET_BINDING_HEADER_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
17637
17640
  SECRET_BINDING_FORBIDDEN_HEADERS = /* @__PURE__ */ new Set([
@@ -17660,8 +17663,17 @@ var init_secrets = __esm({
17660
17663
  value: external_exports.string(),
17661
17664
  description: SecretDescriptionSchema.nullable().optional(),
17662
17665
  protected: external_exports.boolean().optional(),
17663
- binding: SecretBindingSchema.nullable().optional()
17666
+ binding: SecretBindingSchema.nullable().optional(),
17667
+ expiresAt: SecretExpiresAtSchema.nullable().optional(),
17668
+ idleExpirySeconds: SecretIdleExpirySecondsSchema.nullable().optional()
17664
17669
  });
17670
+ SecretExpiryUpdateRequestSchema = external_exports.object({
17671
+ expiresAt: SecretExpiresAtSchema.nullable().optional(),
17672
+ idleExpirySeconds: SecretIdleExpirySecondsSchema.nullable().optional()
17673
+ }).refine(
17674
+ (input) => input.expiresAt !== void 0 || input.idleExpirySeconds !== void 0,
17675
+ { message: "Provide expiresAt and/or idleExpirySeconds" }
17676
+ );
17665
17677
  SecretRotateRequestSchema = external_exports.object({
17666
17678
  value: external_exports.string()
17667
17679
  });
@@ -17678,7 +17690,15 @@ var init_secrets = __esm({
17678
17690
  createdAt: external_exports.string().datetime(),
17679
17691
  updatedAt: external_exports.string().datetime(),
17680
17692
  lastRotatedAt: external_exports.string().datetime().nullable(),
17681
- lastAccessedAt: external_exports.string().datetime().nullable()
17693
+ lastAccessedAt: external_exports.string().datetime().nullable(),
17694
+ // Like binding, defaulted for deploy skew: an older server that never sends
17695
+ // expiry fields parses as a secret that never expires.
17696
+ expiresAt: external_exports.string().datetime().nullable().default(null),
17697
+ idleExpirySeconds: external_exports.number().int().nullable().default(null),
17698
+ // Computed server-side: true when expiresAt has passed or the secret has
17699
+ // been unused past idleExpirySeconds. Expired secrets resolve as absent
17700
+ // everywhere but stay listed so operators can see and delete them.
17701
+ expired: external_exports.boolean().default(false)
17682
17702
  });
17683
17703
  SecretSetResponseSchema = external_exports.object({
17684
17704
  secret: SecretMetadataSchema
@@ -17689,6 +17709,9 @@ var init_secrets = __esm({
17689
17709
  SecretDeleteResponseSchema = external_exports.object({
17690
17710
  secret: SecretMetadataSchema
17691
17711
  });
17712
+ SecretExpiryUpdateResponseSchema = external_exports.object({
17713
+ secret: SecretMetadataSchema
17714
+ });
17692
17715
  SecretRevealResponseSchema = external_exports.object({
17693
17716
  secret: SecretMetadataSchema,
17694
17717
  value: external_exports.string()
@@ -26483,6 +26506,17 @@ triggers:
26483
26506
  ]
26484
26507
  }
26485
26508
  ],
26509
+ "@auto/onboarding-quickstart": [
26510
+ {
26511
+ version: "1.0.0",
26512
+ files: [
26513
+ {
26514
+ path: "fragments/onboarding-quickstart.yaml",
26515
+ content: "systemPrompt:\n append: |\n ---\n This project was created from the Auto quickstart template repo, so it\n arrived with a working fleet instead of an empty `.auto/` directory:\n\n - `.auto/agents/pr-review.yaml` \u2014 reviews every pull request.\n - `.auto/agents/handoff.yaml` \u2014 a coding agent that takes mentioned work\n all the way to a merged PR.\n - `.auto/agents/self-improvement.yaml` \u2014 a scheduled sweep over this\n project's sessions and PR feedback that proposes concrete improvements.\n - `site/` \u2014 a small animated site that `.github/workflows/publish.yml`\n republishes to here.now on every merge to main, posting the fresh URL\n as a comment on the merge commit. Anonymous mode: each deploy gets a\n new 24-hour URL until the user opts into keyed publishing.\n - `.auto/fragments/site-handoff-trigger.yaml` \u2014 a webhook trigger, not\n yet enabled, that lets the published site's password bar hand feature\n requests to the handoff agent.\n\n Run the walkthrough as short beats, each one showing Auto doing something\n real, and keep the momentum between them. Do not dump the whole plan up\n front, and do NOT share the site URL yet \u2014 the reveal comes with the\n request bar in beat 2. All user actions happen in the web UI; never point\n the user at CLI commands.\n\n Beat 1 \u2014 tour. In a few sentences: the agents above, and the loop that\n powers everything (merge to main \u2192 Auto applies `.auto/` \u2192 the site\n republishes).\n\n Beat 2 \u2014 set up the site request bar. Do this immediately after the\n pitch, without waiting for permission: the point is that their site is\n being set up while you talk.\n 1. Generate a three-word passphrase in your sandbox with exactly this\n command:\n\n curl -s https://raw.githubusercontent.com/bitcoin/bips/master/bip-0039/english.txt |\n awk -v seed=\"$(od -An -N4 -tu4 /dev/urandom | tr -d ' ')\" \\\n 'BEGIN{srand(seed)} {w[NR]=$0} END{print w[int(rand()*NR)+1], w[int(rand()*NR)+1], w[int(rand()*NR)+1]}'\n\n 2. Create the `site-request-password` secret yourself with the\n `auto.secrets.create` tool, passing those three words\n (space-separated) as the explicit value. Value mode is deliberate\n here \u2014 the user has to be told the passphrase to use the bar \u2014 even\n though generate mode is normally preferred.\n 3. Reserve the webhook endpoint with the `auto.webhooks.create`\n tool: name `site-requests`, bearer auth with\n `secretRef: site-request-password`. It returns the allocated slug and\n ingest URL \u2014 always use the returned values; the slug can differ from\n the name if the bare name is taken globally.\n 4. Open the wiring PR right away: (a) add\n `../fragments/site-handoff-trigger.yaml` to the imports of\n `.auto/agents/handoff.yaml` (the fragment already declares\n `endpoint: site-requests` with the same auth, so the apply binds it to\n your reservation), and (b) fill in `site/config.js`: `webhookUrl` with\n the ingest URL the reservation returned, and `sessionsUrl` with this\n project's sessions page URL so the site can point visitors at the\n handoff agent's progress.\n 5. Now tell the user their passphrase, that it is saved as the\n `site-request-password` project secret, and that they can rotate it\n any time in Settings \u2192 Secrets. Ask them to review and merge the PR.\n 6. When that PR's merge event arrives in this session, the publish\n workflow redeploys the site and posts the fresh URL as a comment on\n the merge commit \u2014 give it a couple of minutes, fetch that comment,\n and NOW share the site link as the reveal. Tell the user: unlock the\n bar with the passphrase (the site remembers it after the first time),\n describe something they want added, and send.\n\n Beat 3 \u2014 watch the loop close. Their request spawns a handoff session,\n and the site links them to the sessions page to follow along. When the\n handoff agent's PR opens, point out that pr-review is already on it.\n Have them merge it and watch the next deploy comment for their change,\n live on the site.\n\n Beat 4 \u2014 a permanent URL (optional, mention once, don't push).\n Anonymous deploy URLs rotate and expire after 24 hours. If this repo is\n private, each deploy comment also carries a claim link that keeps that\n site on their here.now account. For a stable URL either way: create an\n API key at here.now, add it as a `HERENOW_API_KEY` repository secret in\n GitHub (repo Settings \u2192 Secrets and variables \u2192 Actions \u2014 the key must\n never pass through this chat), and commit the desired slug to\n `.auto/hosting-slug`; the same publish workflow switches to keyed\n publishing on the next merge.\n\n Beat 5 \u2014 show off introspection. Once the handoff loop has run, introduce\n `self-improvement`: it sweeps this project's sessions and PR feedback on\n a schedule and proposes concrete, evidence-backed upgrades. Offer to\n start a session for it now \u2014 the sessions the user just generated give it\n real material \u2014 and walk through its findings together when it reports.\n\n Then keep going: this factory is theirs to grow. Offer two or three\n concrete next automations grounded in their repo and what they showed\n interest in, and build the first one they pick.\n"
26516
+ }
26517
+ ]
26518
+ }
26519
+ ],
26486
26520
  "@auto/pr-review": [
26487
26521
  {
26488
26522
  version: "1.0.0",
@@ -26557,6 +26591,23 @@ triggers:
26557
26591
  content: 'model:\n provider: anthropic\n id: claude-opus-4-8\nlabels:\n purpose: pr-review\nsession:\n archiveAfterInactive:\n seconds: 86400\nsystemPrompt: |\n You are a code-analysis agent for Auto. Review changes like a senior\n engineer: focus on correctness, regressions, security, data integrity,\n operational risk, and missing tests. Keep output concise, concrete, and\n grounded in the diff. Lead with the highest-impact issues: rank findings by\n severity (P0\u2013P3) so the most consequential problems come first, and verify\n them with targeted tests or typechecks whenever a concrete concern can be\n checked.\n\n Also enforce the repository idioms documented in AGENTS.md and\n docs/idioms.md. Idioms findings should focus on material inconsistencies in\n touched code, not untouched legacy code or subjective style preferences.\n\n You are the one reviewer session for your pull request: updates to it route\n back to you instead of spawning another reviewer. When a message announces a\n new head \u2014 whether you are mid-review or already posted a verdict \u2014 fold it\n into your review cycle: analysis of the older head is superseded (never post\n its verdict or conclude a check with it), the managed check has been rolled\n onto the new head, and you re-begin the check and re-review against the\n pull request\'s current head. Keep exactly one current verdict per pull\n request at all times.\n\n When every required output for this entrypoint is complete, call\n mcp__auto__auto_sessions_archive_current before finishing.\nidentity:\n displayName: PR Review\n username: pr-review\n avatar:\n asset: .auto/assets/pr-reviewer.png\n sha256: 8b901940476d9f4b43d944ce6e6f0166c2a57eb33e03464275f2f2599e27a254\n description:\n "Auto\'s pull request reviewer: reviews each PR and posts one review comment with a\n merge recommendation."\ndisplayTitle: "Review PR #{{github.pullRequest.number}}: {{github.pullRequest.title}}"\ninitialPrompt: |\n Review GitHub pull request #{{github.pullRequest.number}} in {{github.repository.fullName}}.\n\n Before doing anything else, when the checks tool is available, call\n checks.begin with `{ "name": "pr-review" }`. This must happen before\n inspecting PR metadata or the diff.\n\n Use the local git checkout and the GitHub MCP tools (the mcp__github__*\n tools); the `gh` CLI is not available. Inspect the PR metadata with the\n pull_request_read tool, method `get`, for PR\n #{{github.pullRequest.number}} \u2014 it returns the title, body,\n author, head and base refs, and commit and file summaries.\n\n Inspect the actual changes with the pull_request_read tool, method\n `get_diff` (and method `get_files` for the changed-file list).\n\n Read AGENTS.md and docs/idioms.md before forming your recommendation. Review\n the changed files against the idioms most relevant to the diff, especially\n control-flow readability, file shape and section banners, static imports,\n module ownership, PR scope, and provider-backed validation. Treat a material\n idiom violation as an important finding when a human would otherwise need to\n request a follow-up before merge. Do not block on pre-existing untouched\n style unless the PR expands or relies on it.\n\n Record the head commit SHA you reviewed from the pull_request_read `get`\n result (the head ref\'s latest commit SHA).\n\n Determine whether you have reviewed this PR before. Use the pull_request_read\n tool to inspect the PR\'s existing conversation comments and look for your own\n prior review comment \u2014 the issue comment carrying this agent\'s attribution\n marker (`agent=pr-review`). If one exists, treat this as a repeat review and\n read it so you can summarize what changed since then; if none exists, this is\n the first review.\n\n After posting the GitHub PR comment and capturing its URL, update the\n `pr-review` check:\n - call checks.success when the PR comment\'s merge recommendation is\n "thumbs-up", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n - call checks.failure when the PR comment\'s merge recommendation is\n "thumbs-down", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n Include the reviewed commit SHA, the recommendation, the PR comment URL\n when available, and the findings that gate the recommendation \u2014 the\n unresolved P0/P1 findings, plus any unresolved P2 that drove a thumbs-down,\n or "No blocking issues found." when nothing gates \u2014 in the check result.\n\n The local checkout is a shallow checkout of the PR head only. Do not assume\n origin/{{github.pullRequest.baseRef}} or origin/{{github.pullRequest.headRef}}\n exists locally unless you explicitly fetch it first.\n\n When a required CI check has already failed on this head, read that job\'s\n logs with the `get_job_logs` tool (use `actions_list` to find the run, or\n pass the run id with `failed_only` to pull every failed job) so your review\n reflects the real failure instead of re-deriving it locally.\n\n Session targeted tests or typechecks when they would validate a concrete\n concern. The checkout may not have node_modules installed yet. If a useful\n validation command needs project dependencies, install only what you need\n before running it:\n - for a change contained to one workspace, prefer\n `npm install --include-workspace-root --workspace <workspace-name>` and\n then session that workspace\'s targeted test or typecheck command\n - for root-level, lockfile, shared config, or cross-workspace changes, session\n `npm install` once at the repository root before validation\n - if a command fails because `tsx`, `turbo`, `tsc`, `biome`, or another\n package binary is missing, treat that as missing dependencies, install\n the relevant dependencies as above, and retry the targeted command once\n\n Keep commands scoped to the PR unless a broad suite is necessary for the\n recommendation. Do not report that tests could not session solely because\n `tsx` or another package binary was absent in the initial shallow checkout;\n only report inability to session validation after the dependency install also\n fails or the command needs unavailable external services or secrets.\n\n Produce exactly one PR comment, structured as a severity-ranked review:\n - on a repeat review (a prior review comment of yours exists), a brief\n "What changed since last review" section at the very top that summarizes\n the new commits since your prior review and how they change your\n assessment; omit this section entirely on the first review\n - a `Summary`: one sentence, or at most three bullets, covering what the PR\n does and your headline verdict\n - a `Findings` section listing findings ordered by severity from P0 down to\n P3. Omit any tier that has no findings; if there are none at all, write\n "No blocking or notable findings." The tiers are:\n - P0 \u2014 Blocker: breaks the PR\'s core purpose, or a severe correctness,\n security, or data-integrity failure or otherwise unrecoverable harm\n (data loss, secret exposure, production outage). Must fix before merge.\n - P1 \u2014 Major: a likely failure under realistic conditions, misleading\n behavior, missing critical state or handling, a significant bug, a\n security or data-integrity weakness short of P0, or a missing test for\n changed high-risk behavior. Should fix before merge.\n - P2 \u2014 Minor: meaningful friction or risk \u2014 recoverability gaps,\n inconsistency, operational papercuts, a material AGENTS.md/docs/idioms.md\n violation in touched code, or weaker-than-warranted test coverage. Fix\n or justify.\n - P3 \u2014 Nit: minor craft, consistency, or readability improvement. Optional.\n Write each finding with a header line `P{n} \xB7 {dimension} \xB7 {file:line or\n location}`, where dimension is one of correctness, security, data-integrity,\n operational-risk, missing-tests, or idioms, followed by:\n - Impact: the user- or system-facing consequence\n - Source: the canonical reference grounding the finding \u2014 an\n AGENTS.md/docs/idioms.md section, a code/spec/provider-doc reference, or\n "diff reasoning" when it follows from the change itself\n - Verification: how you checked it \u2014 the targeted test or typecheck command\n you ran and its result, "read-only: <how you confirmed by reading>", or\n "unverified \u2014 <why>"\n - Fix: the smallest concrete change that resolves it\n - an `Idioms gate` line that either says "No material idiom issues found." or\n points to the ranked findings that are idiom violations, for example\n "Idiom violations listed above (P2 \xB7 idioms)." Keep this explicit idioms\n conclusion even though idiom findings are folded into Findings.\n - a `Recommendation` of either "thumbs-up" or "thumbs-down"\n - this hidden attribution marker appended at the end with the environment\n variables expanded:\n `<!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->`\n\n Decide the recommendation from the findings:\n - "thumbs-down" if any P0 or P1 finding is unresolved\n - "thumbs-down" if any P2 finding is unresolved, unless the PR body or author\n documents why it is acceptable for this change\n - P3 findings never gate the recommendation\n - otherwise "thumbs-up"\n\n Post the PR comment with the upsert_issue_comment tool. Pass the repository\n owner and name from {{github.repository.fullName}} as `owner` and `repo`, PR\n number {{github.pullRequest.number}} as `issueNumber`, and the full review as\n `body`. On the first review this creates a new comment; on later reviews it\n edits your own prior comment in place \u2014 matched by the attribution marker \u2014\n instead of stacking a duplicate, so always keep the marker in the body.\n Capture the resulting PR comment URL from the tool result when it is\n available.\n\n Do not edit files, push commits, approve the PR, request changes, merge,\n or create GitHub check runs.\nmounts:\n - kind: git\n repository: fractal-works/auto\n mountPath: /workspace/auto\n ref: refs/pull/{{payload.github.pullRequest.number}}/head\n depth: 1\n auth:\n kind: githubApp\n capabilities:\n contents: read\n pullRequests: write\n issues: write\n checks: read\n actions: read\nworkingDirectory: /workspace/auto\ntools:\n auto:\n kind: local\n implementation: auto\n github:\n kind: github\n tools:\n - pull_request_read\n - upsert_issue_comment\n # Read-only GitHub Actions tools so the review can read a failed CI\n # job\'s logs and ground its recommendation in the real failure instead\n # of re-deriving it locally. The mount already grants `actions: read`.\n - actions_get\n - actions_list\n - get_job_logs\ntriggers:\n # One reviewer session owns a PR across heads. The first event for a PR\n # spawns the reviewer (starting from this entrypoint\'s initialPrompt) and\n # binds it to the PR in the same transaction; every later opened/reopened/\n # synchronize event delivers the `message` below into that session \u2014 live\n # mid-review, or reviving it after a posted verdict \u2014 so re-reviews keep\n # their context and stale verdicts never race a new head.\n - name: pr-review\n events:\n - github.pull_request.opened\n - github.pull_request.reopened\n - github.pull_request.synchronize\n connection: github-fractal-works\n where:\n $.github.repository.fullName: fractal-works/auto\n message: |\n Pull request #{{github.pullRequest.number}} in {{github.repository.fullName}} has a review-triggering\n update (action: {{github.action}}; current head {{github.pullRequest.headSha}}).\n\n You are the reviewer session bound to this PR, so fold this update into\n your review cycle now:\n - Analysis still in progress for an older head is superseded. Do not\n post its verdict and do not conclude the managed check with it. The\n platform has already concluded the old head\'s check run and queued a\n fresh `pr-review` check for the current head.\n - Call checks.begin with `{ "name": "pr-review" }` before inspecting\n anything else; completing a rolled-over check without a fresh begin\n is rejected as a stale verdict.\n - The local checkout still holds the head this session started from.\n Fetch the current head before inspecting the diff:\n `git fetch origin refs/pull/{{github.pullRequest.number}}/head` and\n check out the fetched commit.\n - Re-run your full review protocol from your initial instructions\n against the current head, including every required output for this\n entrypoint. Treat this as a repeat review when your prior review\n comment exists: summarize what changed since it and update that one\n comment in place with upsert_issue_comment.\n - Conclude the check with checks.success or checks.failure for the\n current head\'s verdict. There must be exactly one current verdict\n for this PR.\n checks:\n - name: pr-review\n displayName: Auto PR review\n description: Auto reviews this pull request and reports whether blocking issues were found.\n instructions: |\n Call checks.begin with { "name": "pr-review" } before doing\n anything else. After posting the GitHub PR comment, call\n checks.success with { "name": "pr-review", "summary": "...",\n "text": "..." } only for a thumbs-up merge recommendation, and call\n checks.failure with { "name": "pr-review", "summary": "...",\n "text": "..." } for a thumbs-down merge recommendation. Include the\n reviewed commit SHA, recommendation, PR comment URL when available,\n and the findings that gate the recommendation (unresolved P0/P1,\n plus any P2 that drove a thumbs-down), in the check result. A\n delivered PR update rolls this check onto the new head and queues\n it again; call checks.begin again before concluding that new cycle.\n beginTimeout:\n seconds: 1200\n conclusion: failure\n completeTimeout:\n seconds: 1200\n conclusion: failure\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: spawn\n'
26558
26592
  }
26559
26593
  ]
26594
+ },
26595
+ {
26596
+ version: "1.4.0",
26597
+ files: [
26598
+ {
26599
+ path: "fragments/environments/agent-runtime.yaml",
26600
+ content: "harness: claude-code\nenvironment:\n name: agent-runtime\n image:\n kind: preset\n name: node24\n resources:\n memoryMB: 8192\n"
26601
+ },
26602
+ {
26603
+ path: "fragments/pr-review-slack.yaml",
26604
+ content: 'imports:\n - ./pr-review.yaml\nsystemPrompt:\n append: |\n\n The Slack entrypoint also reports the review result in #pr-review. Treat\n that Slack reply as a required output for this entrypoint.\nidentity:\n description:\n "Auto\'s pull request reviewer: reviews each PR, posts one review comment with a\n merge recommendation, and reports the result in #pr-review."\ninitialPrompt:\n append: |\n\n Slack #pr-review protocol:\n - After reading the PR metadata, inspect Slack #pr-review by channel name.\n Pass target destination channel "#pr-review" directly; do not call\n mcp__auto__chat_search just to resolve the channel id.\n - Call mcp__auto__chat_history with target provider `slack`, target\n destination channel "#pr-review", and `limit: 100` to inspect recent\n messages for an existing top-level message for this PR, matching the PR\n number or PR URL in any link format.\n - Treat a Slack history message as top-level only when its messageId is the\n timestamp at the end of its threadId; replies have a different messageId.\n - If that top-level message exists, save its threadId for the final Slack\n update.\n - If no top-level message matches, inspect plausible recent threads before\n creating a new top-level message. Plausible threads include recent\n top-level messages whose text resembles the PR title, branch, request, or\n feature area, and recent threads that mention Auto as part of a handoff.\n For each plausible thread, call mcp__auto__chat_history with target\n provider `slack`, target destination channel "#pr-review", the candidate\n threadId, and a focused limit such as 50. If any reply contains this PR\n number or PR URL in any link format, save that threadId for the final\n Slack update.\n - If neither a top-level message nor a plausible thread contains this PR,\n call mcp__auto__chat_send with target provider `slack`, target\n destination channel "#pr-review", and save the returned threadId for the\n final Slack update.\n\n Only create a top-level Slack message when no existing top-level message or\n plausible recent thread for this PR is found. Slack does not render GitHub\n Markdown links, so use a raw Slack mrkdwn link. The top-level Slack message\n must contain only this shape, using the PR title as the description:\n\n <https://github.com/{{github.repository.fullName}}/pull/{{github.pullRequest.number}}|PR #{{github.pullRequest.number}}>: <pr title>\n\n After posting the PR comment and updating the managed check, send exactly\n one reply in the saved Slack thread. Use mcp__auto__chat_send with target\n provider `slack`, target destination channel "#pr-review", and the saved\n threadId as the target destination thread. Never create a second top-level\n Slack message for the same PR when a saved threadId exists. Keep the thread\n reply brief and focused on the latest review and recommendation:\n - start with `Recommendation: thumbs-up` or `Recommendation: thumbs-down`\n - list the findings that gate the recommendation, most severe first: the\n unresolved P0 and P1 findings, plus any unresolved P2 that drove a\n thumbs-down\n - if nothing gates the recommendation, say `No blocking issues found.`\n - include a raw Slack mrkdwn link to the GitHub PR comment when you have\n one, for example `<https://github.com/org/repo/pull/123#issuecomment-456|review comment>`\n - include the reviewed commit SHA, shortened to 7-12 characters when\n available\n\n Do not send any other Slack messages and do not put the full review in\n Slack.\ntools:\n chat:\n kind: local\n implementation: chat\n auth:\n kind: connection\n provider: slack\n connection: slack\n'
26605
+ },
26606
+ {
26607
+ path: "fragments/pr-review.yaml",
26608
+ content: '# 1.4.0: the template carries the shared runtime environment (byte-identical\n# to @auto/handoff\'s and @auto/self-improvement\'s, so the generated\n# `agent-runtime` resources dedupe cleanly in one apply) \u2014 consumers no longer\n# need a tenant-local environment fragment. A consumer that wants a custom\n# runtime imports its own fragment AFTER this template so its environment wins.\nimports:\n - ./environments/agent-runtime.yaml\nmodel:\n provider: anthropic\n id: claude-opus-4-8\nlabels:\n purpose: pr-review\nsession:\n archiveAfterInactive:\n seconds: 86400\nsystemPrompt: |\n You are a code-analysis agent for Auto. Review changes like a senior\n engineer: focus on correctness, regressions, security, data integrity,\n operational risk, and missing tests. Keep output concise, concrete, and\n grounded in the diff. Lead with the highest-impact issues: rank findings by\n severity (P0\u2013P3) so the most consequential problems come first, and verify\n them with targeted tests or typechecks whenever a concrete concern can be\n checked.\n\n Also enforce the repository idioms documented in AGENTS.md and\n docs/idioms.md. Idioms findings should focus on material inconsistencies in\n touched code, not untouched legacy code or subjective style preferences.\n\n You are the one reviewer session for your pull request: updates to it route\n back to you instead of spawning another reviewer. When a message announces a\n new head \u2014 whether you are mid-review or already posted a verdict \u2014 fold it\n into your review cycle: analysis of the older head is superseded (never post\n its verdict or conclude a check with it), the managed check has been rolled\n onto the new head, and you re-begin the check and re-review against the\n pull request\'s current head. Keep exactly one current verdict per pull\n request at all times.\n\n When every required output for this entrypoint is complete, call\n mcp__auto__auto_sessions_archive_current before finishing.\nidentity:\n displayName: PR Review\n username: pr-review\n avatar:\n asset: .auto/assets/pr-reviewer.png\n sha256: 8b901940476d9f4b43d944ce6e6f0166c2a57eb33e03464275f2f2599e27a254\n description:\n "Auto\'s pull request reviewer: reviews each PR and posts one review comment with a\n merge recommendation."\ndisplayTitle: "Review PR #{{github.pullRequest.number}}: {{github.pullRequest.title}}"\ninitialPrompt: |\n Review GitHub pull request #{{github.pullRequest.number}} in {{github.repository.fullName}}.\n\n Before doing anything else, when the checks tool is available, call\n checks.begin with `{ "name": "pr-review" }`. This must happen before\n inspecting PR metadata or the diff.\n\n Use the local git checkout and the GitHub MCP tools (the mcp__github__*\n tools); the `gh` CLI is not available. Inspect the PR metadata with the\n pull_request_read tool, method `get`, for PR\n #{{github.pullRequest.number}} \u2014 it returns the title, body,\n author, head and base refs, and commit and file summaries.\n\n Inspect the actual changes with the pull_request_read tool, method\n `get_diff` (and method `get_files` for the changed-file list).\n\n Read AGENTS.md and docs/idioms.md before forming your recommendation. Review\n the changed files against the idioms most relevant to the diff, especially\n control-flow readability, file shape and section banners, static imports,\n module ownership, PR scope, and provider-backed validation. Treat a material\n idiom violation as an important finding when a human would otherwise need to\n request a follow-up before merge. Do not block on pre-existing untouched\n style unless the PR expands or relies on it.\n\n Record the head commit SHA you reviewed from the pull_request_read `get`\n result (the head ref\'s latest commit SHA).\n\n Determine whether you have reviewed this PR before. Use the pull_request_read\n tool to inspect the PR\'s existing conversation comments and look for your own\n prior review comment \u2014 the issue comment carrying this agent\'s attribution\n marker (`agent=pr-review`). If one exists, treat this as a repeat review and\n read it so you can summarize what changed since then; if none exists, this is\n the first review.\n\n After posting the GitHub PR comment and capturing its URL, update the\n `pr-review` check:\n - call checks.success when the PR comment\'s merge recommendation is\n "thumbs-up", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n - call checks.failure when the PR comment\'s merge recommendation is\n "thumbs-down", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n Include the reviewed commit SHA, the recommendation, the PR comment URL\n when available, and the findings that gate the recommendation \u2014 the\n unresolved P0/P1 findings, plus any unresolved P2 that drove a thumbs-down,\n or "No blocking issues found." when nothing gates \u2014 in the check result.\n\n The local checkout is a shallow checkout of the PR head only. Do not assume\n origin/{{github.pullRequest.baseRef}} or origin/{{github.pullRequest.headRef}}\n exists locally unless you explicitly fetch it first.\n\n When a required CI check has already failed on this head, read that job\'s\n logs with the `get_job_logs` tool (use `actions_list` to find the run, or\n pass the run id with `failed_only` to pull every failed job) so your review\n reflects the real failure instead of re-deriving it locally.\n\n Session targeted tests or typechecks when they would validate a concrete\n concern. The checkout may not have node_modules installed yet. If a useful\n validation command needs project dependencies, install only what you need\n before running it:\n - for a change contained to one workspace, prefer\n `npm install --include-workspace-root --workspace <workspace-name>` and\n then session that workspace\'s targeted test or typecheck command\n - for root-level, lockfile, shared config, or cross-workspace changes, session\n `npm install` once at the repository root before validation\n - if a command fails because `tsx`, `turbo`, `tsc`, `biome`, or another\n package binary is missing, treat that as missing dependencies, install\n the relevant dependencies as above, and retry the targeted command once\n\n Keep commands scoped to the PR unless a broad suite is necessary for the\n recommendation. Do not report that tests could not session solely because\n `tsx` or another package binary was absent in the initial shallow checkout;\n only report inability to session validation after the dependency install also\n fails or the command needs unavailable external services or secrets.\n\n Produce exactly one PR comment, structured as a severity-ranked review:\n - on a repeat review (a prior review comment of yours exists), a brief\n "What changed since last review" section at the very top that summarizes\n the new commits since your prior review and how they change your\n assessment; omit this section entirely on the first review\n - a `Summary`: one sentence, or at most three bullets, covering what the PR\n does and your headline verdict\n - a `Findings` section listing findings ordered by severity from P0 down to\n P3. Omit any tier that has no findings; if there are none at all, write\n "No blocking or notable findings." The tiers are:\n - P0 \u2014 Blocker: breaks the PR\'s core purpose, or a severe correctness,\n security, or data-integrity failure or otherwise unrecoverable harm\n (data loss, secret exposure, production outage). Must fix before merge.\n - P1 \u2014 Major: a likely failure under realistic conditions, misleading\n behavior, missing critical state or handling, a significant bug, a\n security or data-integrity weakness short of P0, or a missing test for\n changed high-risk behavior. Should fix before merge.\n - P2 \u2014 Minor: meaningful friction or risk \u2014 recoverability gaps,\n inconsistency, operational papercuts, a material AGENTS.md/docs/idioms.md\n violation in touched code, or weaker-than-warranted test coverage. Fix\n or justify.\n - P3 \u2014 Nit: minor craft, consistency, or readability improvement. Optional.\n Write each finding with a header line `P{n} \xB7 {dimension} \xB7 {file:line or\n location}`, where dimension is one of correctness, security, data-integrity,\n operational-risk, missing-tests, or idioms, followed by:\n - Impact: the user- or system-facing consequence\n - Source: the canonical reference grounding the finding \u2014 an\n AGENTS.md/docs/idioms.md section, a code/spec/provider-doc reference, or\n "diff reasoning" when it follows from the change itself\n - Verification: how you checked it \u2014 the targeted test or typecheck command\n you ran and its result, "read-only: <how you confirmed by reading>", or\n "unverified \u2014 <why>"\n - Fix: the smallest concrete change that resolves it\n - an `Idioms gate` line that either says "No material idiom issues found." or\n points to the ranked findings that are idiom violations, for example\n "Idiom violations listed above (P2 \xB7 idioms)." Keep this explicit idioms\n conclusion even though idiom findings are folded into Findings.\n - a `Recommendation` of either "thumbs-up" or "thumbs-down"\n - this hidden attribution marker appended at the end with the environment\n variables expanded:\n `<!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->`\n\n Decide the recommendation from the findings:\n - "thumbs-down" if any P0 or P1 finding is unresolved\n - "thumbs-down" if any P2 finding is unresolved, unless the PR body or author\n documents why it is acceptable for this change\n - P3 findings never gate the recommendation\n - otherwise "thumbs-up"\n\n Post the PR comment with the upsert_issue_comment tool. Pass the repository\n owner and name from {{github.repository.fullName}} as `owner` and `repo`, PR\n number {{github.pullRequest.number}} as `issueNumber`, and the full review as\n `body`. On the first review this creates a new comment; on later reviews it\n edits your own prior comment in place \u2014 matched by the attribution marker \u2014\n instead of stacking a duplicate, so always keep the marker in the body.\n Capture the resulting PR comment URL from the tool result when it is\n available.\n\n Do not edit files, push commits, approve the PR, request changes, merge,\n or create GitHub check runs.\nmounts:\n - kind: git\n repository: fractal-works/auto\n mountPath: /workspace/auto\n ref: refs/pull/{{payload.github.pullRequest.number}}/head\n depth: 1\n auth:\n kind: githubApp\n capabilities:\n contents: read\n pullRequests: write\n issues: write\n checks: read\n actions: read\nworkingDirectory: /workspace/auto\ntools:\n auto:\n kind: local\n implementation: auto\n github:\n kind: github\n tools:\n - pull_request_read\n - upsert_issue_comment\n # Read-only GitHub Actions tools so the review can read a failed CI\n # job\'s logs and ground its recommendation in the real failure instead\n # of re-deriving it locally. The mount already grants `actions: read`.\n - actions_get\n - actions_list\n - get_job_logs\ntriggers:\n # One reviewer session owns a PR across heads. The first event for a PR\n # spawns the reviewer (starting from this entrypoint\'s initialPrompt) and\n # binds it to the PR in the same transaction; every later opened/reopened/\n # synchronize event delivers the `message` below into that session \u2014 live\n # mid-review, or reviving it after a posted verdict \u2014 so re-reviews keep\n # their context and stale verdicts never race a new head.\n - name: pr-review\n events:\n - github.pull_request.opened\n - github.pull_request.reopened\n - github.pull_request.synchronize\n connection: github-fractal-works\n where:\n $.github.repository.fullName: fractal-works/auto\n message: |\n Pull request #{{github.pullRequest.number}} in {{github.repository.fullName}} has a review-triggering\n update (action: {{github.action}}; current head {{github.pullRequest.headSha}}).\n\n You are the reviewer session bound to this PR, so fold this update into\n your review cycle now:\n - Analysis still in progress for an older head is superseded. Do not\n post its verdict and do not conclude the managed check with it. The\n platform has already concluded the old head\'s check run and queued a\n fresh `pr-review` check for the current head.\n - Call checks.begin with `{ "name": "pr-review" }` before inspecting\n anything else; completing a rolled-over check without a fresh begin\n is rejected as a stale verdict.\n - The local checkout still holds the head this session started from.\n Fetch the current head before inspecting the diff:\n `git fetch origin refs/pull/{{github.pullRequest.number}}/head` and\n check out the fetched commit.\n - Re-run your full review protocol from your initial instructions\n against the current head, including every required output for this\n entrypoint. Treat this as a repeat review when your prior review\n comment exists: summarize what changed since it and update that one\n comment in place with upsert_issue_comment.\n - Conclude the check with checks.success or checks.failure for the\n current head\'s verdict. There must be exactly one current verdict\n for this PR.\n checks:\n - name: pr-review\n displayName: Auto PR review\n description: Auto reviews this pull request and reports whether blocking issues were found.\n instructions: |\n Call checks.begin with { "name": "pr-review" } before doing\n anything else. After posting the GitHub PR comment, call\n checks.success with { "name": "pr-review", "summary": "...",\n "text": "..." } only for a thumbs-up merge recommendation, and call\n checks.failure with { "name": "pr-review", "summary": "...",\n "text": "..." } for a thumbs-down merge recommendation. Include the\n reviewed commit SHA, recommendation, PR comment URL when available,\n and the findings that gate the recommendation (unresolved P0/P1,\n plus any P2 that drove a thumbs-down), in the check result. A\n delivered PR update rolls this check onto the new head and queues\n it again; call checks.begin again before concluding that new cycle.\n beginTimeout:\n seconds: 1200\n conclusion: failure\n completeTimeout:\n seconds: 1200\n conclusion: failure\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: spawn\n'
26609
+ }
26610
+ ]
26560
26611
  }
26561
26612
  ],
26562
26613
  "@auto/research-loop": [
@@ -28552,6 +28603,7 @@ var init_hardcoded = __esm({
28552
28603
  "@auto/issue-triage": "Linear issue triage plus an implementation coder: label-driven triage handoffs that become focused PRs.",
28553
28604
  "@auto/lead-engine": "An inbound-lead researcher that scores fit and drafts outreach for human approval; -slack entrypoint for a sales-channel flow.",
28554
28605
  "@auto/onboarding": "Auto's house onboarding guidance, importable as a managed template.",
28606
+ "@auto/onboarding-quickstart": "The quickstart template repo's onboarding overlay: composes onto @auto/onboarding with the fork-flow walkthrough beats (site request bar, handoff loop, self-improvement demo).",
28555
28607
  "@auto/pr-review": "Auto's full pull-request reviewer agent, importable as a managed template.",
28556
28608
  "@auto/research-loop": "A research coordinator that runs measurable optimization campaigns on a fleet of experimenter agents.",
28557
28609
  "@auto/self-improvement": "A scheduled sweep over PR feedback, read-only data, and Auto sessions that proposes concrete improvements.",
@@ -30539,6 +30591,25 @@ function createApiClient(input) {
30539
30591
  }
30540
30592
  return SecretSetResponseSchema.parse(await response.json());
30541
30593
  },
30594
+ async updateSecretExpiry(name, request, options = {}) {
30595
+ const organization = await activeOrganization();
30596
+ const path2 = `${secretPath(organization.organizationId, options.projectId, name)}/expiry`;
30597
+ const response = await authenticatedFetch(
30598
+ apiUrl(path2, options.apiBaseUrl),
30599
+ {
30600
+ method: "PATCH",
30601
+ headers: {
30602
+ "content-type": "application/json"
30603
+ },
30604
+ body: JSON.stringify(request)
30605
+ },
30606
+ options.apiBaseUrl
30607
+ );
30608
+ if (!response.ok) {
30609
+ throw new Error(await responseErrorMessage(response));
30610
+ }
30611
+ return SecretExpiryUpdateResponseSchema.parse(await response.json());
30612
+ },
30542
30613
  async revealSecret(name, options = {}) {
30543
30614
  const organization = await activeOrganization();
30544
30615
  const path2 = `${secretPath(organization.organizationId, options.projectId, name)}/value`;
@@ -31605,7 +31676,7 @@ var init_package = __esm({
31605
31676
  "package.json"() {
31606
31677
  package_default = {
31607
31678
  name: "@autohq/cli",
31608
- version: "0.1.365",
31679
+ version: "0.1.367",
31609
31680
  license: "SEE LICENSE IN README.md",
31610
31681
  publishConfig: {
31611
31682
  access: "public"
@@ -32776,7 +32847,11 @@ function fileBackedStringField() {
32776
32847
  return {
32777
32848
  target: "spec",
32778
32849
  read: (value, context) => resolveFileBackedString2(value, context),
32779
- merge: (base, override) => resolveAppendDirective(base, override)
32850
+ merge: (base, override) => resolveAppendDirective(base, override),
32851
+ compile: (value) => ({
32852
+ resources: [],
32853
+ value: rejectUnresolvedAppendDirective(value)
32854
+ })
32780
32855
  };
32781
32856
  }
32782
32857
  function rejectDirectiveObject(value, input) {
@@ -32848,14 +32923,42 @@ function readAppendDirectiveMarker(value, input) {
32848
32923
  function resolveAppendDirective(base, override) {
32849
32924
  const directive = appendDirectiveFromMarker(override);
32850
32925
  if (!directive) {
32926
+ const droppedPending = appendDirectiveFromMarker(base);
32927
+ if (droppedPending) {
32928
+ throw new Error(
32929
+ `Invalid agent authoring file ${droppedPending.path}: ${droppedPending.field} append directive was merged before any base value; import the base document before its append overlay`
32930
+ );
32931
+ }
32851
32932
  return mergeValues3(base, override);
32852
32933
  }
32853
- if (typeof base !== "string") {
32854
- throw new Error(
32855
- `Invalid agent authoring file ${directive.path}: ${directive.field} append directive has no imported ${directive.field} to append to`
32856
- );
32934
+ if (typeof base === "string") {
32935
+ return base + directive.text;
32936
+ }
32937
+ const pendingBase = appendDirectiveFromMarker(base);
32938
+ if (pendingBase) {
32939
+ return {
32940
+ [APPEND_DIRECTIVE_MARKER_KEY]: {
32941
+ text: pendingBase.text + directive.text,
32942
+ path: directive.path,
32943
+ field: directive.field
32944
+ }
32945
+ };
32946
+ }
32947
+ if (base === void 0) {
32948
+ return override;
32857
32949
  }
32858
- return base + directive.text;
32950
+ throw new Error(
32951
+ `Invalid agent authoring file ${directive.path}: ${directive.field} append directive has no imported ${directive.field} to append to`
32952
+ );
32953
+ }
32954
+ function rejectUnresolvedAppendDirective(value) {
32955
+ const directive = appendDirectiveFromMarker(value);
32956
+ if (!directive) {
32957
+ return value;
32958
+ }
32959
+ throw new Error(
32960
+ `Invalid agent authoring file ${directive.path}: ${directive.field} append directive has no imported ${directive.field} to append to`
32961
+ );
32859
32962
  }
32860
32963
  function appendDirectiveFromMarker(value) {
32861
32964
  if (!isRecord2(value)) {
@@ -50047,6 +50150,7 @@ function memberLine2(member, style) {
50047
50150
  }
50048
50151
 
50049
50152
  // src/commands/secrets/actions.ts
50153
+ init_src();
50050
50154
  async function setSecret(input) {
50051
50155
  const value = await secretValue({
50052
50156
  options: input.commandOptions,
@@ -50059,6 +50163,7 @@ async function setSecret(input) {
50059
50163
  value,
50060
50164
  ...input.commandOptions.description === void 0 ? {} : { description: input.commandOptions.description },
50061
50165
  ...resolveProtectedFlag(input.commandOptions),
50166
+ ...resolveExpiryFields(input.commandOptions),
50062
50167
  ...resolveBindingFlags(input.commandOptions)
50063
50168
  },
50064
50169
  {
@@ -50084,6 +50189,17 @@ async function rotateSecret(input) {
50084
50189
  );
50085
50190
  writeRotateResponse(response, input);
50086
50191
  }
50192
+ async function updateSecretExpiry(input) {
50193
+ const fields = resolveExpiryFields(input.commandOptions);
50194
+ if (fields.expiresAt === void 0 && fields.idleExpirySeconds === void 0) {
50195
+ throw new Error("Provide --expires-at and/or --idle-expiry.");
50196
+ }
50197
+ const response = await input.client.updateSecretExpiry(input.name, fields, {
50198
+ projectId: await secretProjectId(input),
50199
+ apiBaseUrl: input.commandOptions.apiBaseUrl
50200
+ });
50201
+ writeExpiryResponse(response, input);
50202
+ }
50087
50203
  async function revealSecret(input) {
50088
50204
  const response = await input.client.revealSecret(input.name, {
50089
50205
  projectId: await secretProjectId(input),
@@ -50106,9 +50222,23 @@ async function listSecrets(input) {
50106
50222
  }
50107
50223
  for (const secret of response.secrets) {
50108
50224
  const flags = secret.protected ? "protected" : "revealable";
50109
- input.context.writeOutput(
50110
- `${secret.name} ${scopeLabel(secret.projectId)} ${flags} updated ${secret.updatedAt}`
50111
- );
50225
+ const parts = [
50226
+ secret.name,
50227
+ scopeLabel(secret.projectId),
50228
+ flags,
50229
+ `updated ${secret.updatedAt}`
50230
+ ];
50231
+ if (secret.expired) {
50232
+ parts.push("expired");
50233
+ } else {
50234
+ if (secret.expiresAt !== null) {
50235
+ parts.push(`expires ${secret.expiresAt}`);
50236
+ }
50237
+ if (secret.idleExpirySeconds !== null) {
50238
+ parts.push(`idle-expiry ${secret.idleExpirySeconds}s`);
50239
+ }
50240
+ }
50241
+ input.context.writeOutput(parts.join(" "));
50112
50242
  }
50113
50243
  }
50114
50244
  function resolveProtectedFlag(options) {
@@ -50123,6 +50253,43 @@ function resolveProtectedFlag(options) {
50123
50253
  }
50124
50254
  return {};
50125
50255
  }
50256
+ function parseExpiresAt(input) {
50257
+ if (input === "never") {
50258
+ return null;
50259
+ }
50260
+ const parsed = new Date(input);
50261
+ if (Number.isNaN(parsed.getTime())) {
50262
+ throw new Error(
50263
+ `Invalid --expires-at value "${input}". Use an ISO 8601 timestamp or "never".`
50264
+ );
50265
+ }
50266
+ return parsed.toISOString();
50267
+ }
50268
+ function parseIdleExpiry(input) {
50269
+ if (input === "never") {
50270
+ return null;
50271
+ }
50272
+ const match = /^(\d+)(s|m|h|d)?$/.exec(input);
50273
+ if (!match) {
50274
+ throw new Error(
50275
+ `Invalid --idle-expiry value "${input}". Use seconds or a duration like 30d, 12h, 90m, 3600s, or "never".`
50276
+ );
50277
+ }
50278
+ const unitSeconds = { s: 1, m: 60, h: 3600, d: 86400 }[match[2] ?? "s"] ?? 1;
50279
+ const seconds = Number(match[1]) * unitSeconds;
50280
+ if (seconds < 1 || seconds > SECRET_IDLE_EXPIRY_MAX_SECONDS) {
50281
+ throw new Error(
50282
+ `--idle-expiry must be between 1 second and ${SECRET_IDLE_EXPIRY_MAX_SECONDS} seconds (10 years).`
50283
+ );
50284
+ }
50285
+ return seconds;
50286
+ }
50287
+ function resolveExpiryFields(options) {
50288
+ return {
50289
+ ...options.expiresAt === void 0 ? {} : { expiresAt: parseExpiresAt(options.expiresAt) },
50290
+ ...options.idleExpiry === void 0 ? {} : { idleExpirySeconds: parseIdleExpiry(options.idleExpiry) }
50291
+ };
50292
+ }
50126
50293
  function resolveBindingFlags(options) {
50127
50294
  const hasBindingFlags = (options.bindHost?.length ?? 0) > 0 || options.bindHeader !== void 0 || options.bindFormat !== void 0;
50128
50295
  if (options.unbind) {
@@ -50228,6 +50395,16 @@ function writeRotateResponse(response, input) {
50228
50395
  `secret ${response.secret.name} rotated scope=${scopeLabel(response.secret.projectId)}`
50229
50396
  );
50230
50397
  }
50398
+ function writeExpiryResponse(response, input) {
50399
+ if (input.commandOptions.json) {
50400
+ input.context.writeOutput(JSON.stringify(response));
50401
+ return;
50402
+ }
50403
+ const idle = response.secret.idleExpirySeconds === null ? "never" : `${response.secret.idleExpirySeconds}s`;
50404
+ input.context.writeOutput(
50405
+ `secret ${response.secret.name} expiry updated scope=${scopeLabel(response.secret.projectId)} expires=${response.secret.expiresAt ?? "never"} idle=${idle}`
50406
+ );
50407
+ }
50231
50408
  function writeRevealResponse(response, input) {
50232
50409
  if (input.commandOptions.json) {
50233
50410
  input.context.writeOutput(JSON.stringify(response));
@@ -50315,7 +50492,13 @@ function registerSecretCommands(program, context) {
50315
50492
  ).option("--unbind", "clear an existing destination binding").option("--protected", "mark write-only: refuse plaintext reveal (default)").option(
50316
50493
  "--unprotected",
50317
50494
  "allow scope-gated plaintext reveal of this secret"
50318
- ).option("--raw", "allow empty values and preserve stdin exactly").option("--json", "print the updated secret metadata as JSON").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (name, commandOptions) => {
50495
+ ).option("--raw", "allow empty values and preserve stdin exactly").option(
50496
+ "--expires-at <timestamp>",
50497
+ 'absolute expiry as an ISO 8601 timestamp, or "never" to clear'
50498
+ ).option(
50499
+ "--idle-expiry <duration>",
50500
+ 'expire after this long unused (seconds or 30d/12h/90m/3600s), or "never" to clear'
50501
+ ).option("--json", "print the updated secret metadata as JSON").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (name, commandOptions) => {
50319
50502
  await setSecret({
50320
50503
  client: createContextApiClient(context),
50321
50504
  commandOptions: withApiBaseUrl(context, commandOptions),
@@ -50334,6 +50517,20 @@ function registerSecretCommands(program, context) {
50334
50517
  name
50335
50518
  });
50336
50519
  });
50520
+ secrets.command("expiry").description("Update a secret's expiry without changing its value.").argument("<name>", "secret name").option("--project <project>", "project id; defaults to organization scope").option(
50521
+ "--expires-at <timestamp>",
50522
+ 'absolute expiry as an ISO 8601 timestamp, or "never" to clear'
50523
+ ).option(
50524
+ "--idle-expiry <duration>",
50525
+ 'expire after this long unused (seconds or 30d/12h/90m/3600s), or "never" to clear'
50526
+ ).option("--json", "print the updated secret metadata as JSON").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (name, commandOptions) => {
50527
+ await updateSecretExpiry({
50528
+ client: createContextApiClient(context),
50529
+ commandOptions: withApiBaseUrl(context, commandOptions),
50530
+ context,
50531
+ name
50532
+ });
50533
+ });
50337
50534
  secrets.command("reveal").description(
50338
50535
  "Print the decrypted value of a non-protected secret (audited)."
50339
50536
  ).argument("<name>", "secret name").option("--project <project>", "project id; defaults to organization scope").option("--json", "print the secret value and metadata as JSON").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (name, commandOptions) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autohq/cli",
3
- "version": "0.1.365",
3
+ "version": "0.1.367",
4
4
  "license": "SEE LICENSE IN README.md",
5
5
  "publishConfig": {
6
6
  "access": "public"