Skip to content

feat: forge-agnostic workflow commands (Gitea/GitLab support)#1104

Open
truck0321 wants to merge 7 commits intocoleam00:devfrom
truck0321:feat/gitea-forge-support
Open

feat: forge-agnostic workflow commands (Gitea/GitLab support)#1104
truck0321 wants to merge 7 commits intocoleam00:devfrom
truck0321:feat/gitea-forge-support

Conversation

@truck0321
Copy link
Copy Markdown

@truck0321 truck0321 commented Apr 12, 2026

Summary

  • Problem: Archon workflows hardcode gh CLI for all git forge operations, making them GitHub-only
  • Why it matters: Users with Gitea or GitLab repos cannot use Archon workflow commands (PR create, issue management, code review, etc.)
  • What changed: Auto-detect forge type from git remote URL, inject FORGE_TYPE/FORGE_API_BASE/FORGE_CLI into workflow env, replace all hardcoded gh calls with bun "$FORGE_CLI" across 24 command files and 7 workflow YAMLs. New forge-cli.ts (822 lines) routes pr/issue/label/repo commands to the correct API.
  • What did not change (scope boundary): Core orchestrator, database schema, platform adapters (Slack/Telegram/Discord), Web UI. No new dependencies.

UX Journey

Before

User                    Archon                     Forge
----                    ------                     -----
@archon create PR ----> workflow node runs
                        gh pr create ... --------> GitHub API (works)
                                                   Gitea API  (fails - gh doesn't speak Gitea)
                                                   GitLab API (fails - gh doesn't speak GitLab)

After

User                    Archon                          Forge
----                    ------                          -----
@archon create PR ----> executor detects forge
                        from git remote URL
                        injects FORGE_TYPE, FORGE_CLI
                        into config.envVars
                        workflow node runs
                        bun "$FORGE_CLI" pr create
                          github -> shells to gh -----> GitHub API (works)
                          gitea  -> native fetch() --> Gitea API  (works)
                          gitlab -> native fetch() --> GitLab API (works)

Architecture Diagram

Before

executor.ts --> dag-executor.ts --> AI node (Claude/Codex)
                                        prompt references "gh pr create"
                                    bash node
                                        runs "gh ..." directly

After

executor.ts
  [+] detectForge(cwd) --> @archon/git/forge.ts [+]
  [~] config.envVars += {FORGE_TYPE, FORGE_API_BASE, FORGE_CLI}
  --> dag-executor.ts
        AI node -> env has FORGE_* vars -> prompt uses bun "$FORGE_CLI"
        bash node -> subprocessEnv has FORGE_* vars
        script node -> subprocessEnv has FORGE_* vars

[+] .archon/scripts/forge-cli.ts (new, 822 lines)
      github backend -> shells to gh CLI
      gitea backend -> native fetch() to Gitea API v1
      gitlab backend -> native fetch() to GitLab API v4

Connection inventory:

From To Status Notes
executor.ts @archon/git/forge.ts new detectForge() call at workflow start
executor.ts config.envVars modified Injects FORGE_TYPE, FORGE_API_BASE, FORGE_CLI
dag-executor.ts forge params removed No longer passes forgeType/forgeApiBase/forgeCli as function params (flows via config.envVars)
24 command .md files gh CLI modified Replaced with bun "$FORGE_CLI"
7 workflow .yaml files gh CLI modified Bash nodes use bun "$FORGE_CLI"
server/index.ts gh auth check modified Skipped when Gitea/GitLab-only setup detected
@archon/git/forge.ts -- new Forge detection from git remote URL
.archon/scripts/forge-cli.ts -- new Unified CLI wrapper for GitHub/Gitea/GitLab

Label Snapshot

  • Risk: risk: medium
  • Size: size: XL
  • Scope: workflows, git, server, cli, commands
  • Module: workflows:executor, git:forge, server:startup

Change Metadata

  • Change type: feature
  • Primary scope: multi

Linked Issue

Closes #1101

Validation Evidence (required)

bun run validate  # All pass (type-check + lint + format + test)
  • 11 unit tests for forge detection (packages/git/src/forge.test.ts)
  • All existing workflow tests updated and passing (dag-executor.test.ts, executor.test.ts)
  • Bundled defaults regenerated after command file changes

Security Impact (required)

  • New permissions/capabilities? No -- uses existing git remote URL (read-only), existing tokens (GITEA_TOKEN, GITLAB_TOKEN from env)
  • New external network calls? Yes -- forge-cli.ts makes API calls to Gitea/GitLab instances using native fetch(). Only called when forge type is detected as gitea/gitlab. Uses user-provided tokens from env vars.
  • Secrets/tokens handling changed? No -- tokens read from existing env vars (GITEA_TOKEN, GITLAB_TOKEN, GH_TOKEN), never logged or stored
  • File system access scope changed? No
  • If any Yes, describe risk and mitigation: forge-cli.ts validates API base URLs, uses HTTPS, masks tokens in error messages

Compatibility / Migration

  • Backward compatible? Yes -- defaults to github forge type when detection fails or returns unknown
  • Config/env changes? Yes -- new optional env vars: GITEA_URL, GITEA_TOKEN, GITEA_WEBHOOK_SECRET, GITLAB_URL, GITLAB_TOKEN, GITLAB_WEBHOOK_SECRET. None required for existing GitHub users.
  • Database migration needed? No
  • Setup wizard updated to collect Gitea credentials when selected

Human Verification (required)

What was personally validated beyond CI:

  • Verified scenarios:
    • E2E tested against production Gitea (git.axiodev.com): issue create/comment/view, PR create/comment/diff/list, repo info, label list
    • E2E tested against GitHub (truck0321/axiodev): full parity with existing gh behavior
    • Forge detection tested with github.com, gitea, gitlab, SSH URLs, URLs with ports
  • Edge cases checked:
    • Unknown forge hostname falls back to github defaults
    • SSH URLs with non-standard ports (ssh://git@host:2222/owner/repo)
    • Missing tokens produce clear error messages
    • GitLab draft MR title prefix (Draft: vs GitHub [Draft])
    • GitLab raw diff endpoint (/merge_requests/:id/raw_diffs)
  • What was not verified:
    • GitLab E2E against live instance (API paths implemented from docs, not yet tested)
    • GitHub Enterprise detection (env var added but not tested against GHE instance)

Side Effects / Blast Radius (required)

  • Affected subsystems: Workflow executor, all default commands/workflows that reference git forge operations, server startup (gh auth check), CLI setup wizard
  • Potential unintended effects: Commands that previously assumed gh output format may behave differently with forge-cli normalized JSON. The normalization layer maps Gitea/GitLab field names to GitHub equivalents to minimize this.
  • Guardrails: forge-cli.ts normalizes all JSON output to GitHub-equivalent field names. Unknown forge type falls back to github. All existing tests pass unchanged (except call signature updates from merge resolution).

Rollback Plan (required)

  • Fast rollback: Revert merge commit (single commit on dev)
  • Feature flags: None -- but forge detection only activates for non-GitHub remotes. GitHub users see zero behavioral change.
  • Observable failure symptoms: Workflow commands fail with "command not found" or API errors if forge-cli.ts has issues. Visible in workflow logs and platform messages.

Risks and Mitigations

  • Risk: GitLab API paths not E2E tested against live instance
    • Mitigation: API paths taken from official GitLab API v4 docs. Can be validated incrementally post-merge with a GitLab test repo.
  • Risk: forge-cli.ts JSON normalization may miss edge cases in Gitea/GitLab API responses
    • Mitigation: Normalization covers all fields used by default commands (number, title, body, url, labels, headRefName, baseRefName). Unknown fields pass through unchanged.
  • Risk: Large change surface (310 files, mostly from merge with dev)
    • Mitigation: Only 6 original commits are ours. The rest are dev merge. Our changes are isolated to forge detection, forge-cli, command/workflow text, and env injection.

Add forge detection, variable injection ($FORGE_TYPE, $FORGE_API_BASE,
$FORGE_CLI), and a TypeScript CLI wrapper (forge-cli.ts) that routes
pr/issue/label/repo commands to gh, Gitea API, or GitLab API based on
the detected forge. Bash nodes use it deterministically; AI prompts get
an auto-injected preamble for non-GitHub forges.

Also fixes DATABASE_URL pollution from target repo .env files in CLI.

Closes coleam00#1101

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR enables Archon workflows to execute against multiple git forges (GitHub, Gitea, GitLab) by introducing forge detection, a unified CLI wrapper script, and substituting all hardcoded gh CLI calls with forge-agnostic bun "$FORGE_CLI" invocations throughout command and workflow files.

Changes

Cohort / File(s) Summary
Forge Detection Core
packages/git/src/forge.ts, packages/git/src/forge.test.ts
New forge detection module that parses git remote URLs and environment variables (GITEA_URL, GITLAB_URL) to determine forge type and API base URL; includes comprehensive test coverage for GitHub, Gitea, GitLab, self-hosted instances, and edge cases.
Forge CLI Wrapper
.archon/scripts/forge-cli.ts
New 822-line TypeScript script that unifies pr, issue, label, and repo commands across forges, routing to gh CLI for GitHub and native fetch() REST calls for Gitea/GitLab with response normalization and argument parsing.
Public API Exports
packages/git/src/index.ts
Re-exports new forge detection types (ForgeType, ForgeInfo) and function (detectForge) to make them available as part of the @archon/git package.
Workflow Infrastructure
packages/workflows/src/executor.ts, packages/workflows/src/executor-shared.ts, packages/workflows/src/dag-executor.ts, packages/workflows/src/dag-executor.test.ts, packages/workflows/src/executor.test.ts
Updates workflow execution pipeline to perform git-remote forge auto-detection on startup, inject FORGE_TYPE, FORGE_API_BASE, FORGE_CLI, and FORGE_NAME into workflow environment variables and prompts, and add forge-specific compatibility preamble for non-GitHub forges.
Tooling & Configuration
eslint.config.mjs, packages/server/src/index.ts
Adds targeted ESLint rules for forge-cli.ts and updates GitHub authentication check to skip when alternate forge is configured without GitHub token.
Command Files: Core PR/Issue Operations
.archon/commands/defaults/archon-create-pr.md, .archon/commands/defaults/archon-finalize-pr.md, .archon/commands/defaults/archon-create-issue.yaml, .archon/commands/defaults/archon-fix-github-issue.yaml, .archon/commands/defaults/archon-investigate-issue.md
Replace gh pr/issue commands with bun "$FORGE_CLI" equivalents; update documentation and prompts to reference $FORGE_NAME instead of hardcoded "GitHub"; add forge notes and variable declarations.
Command Files: PR Review & Validation
.archon/commands/defaults/archon-validate-pr.yaml, .archon/commands/defaults/archon-pr-review-scope.md, .archon/commands/defaults/archon-validate-pr-code-review-main.md, .archon/commands/defaults/archon-validate-pr-e2e-main.md, .archon/commands/defaults/archon-validate-pr-code-review-feature.md, .archon/commands/defaults/archon-validate-pr-report.md
Update PR viewing, diffing, and metadata extraction to use forge-agnostic CLI wrapper while preserving argument structures and JSON field queries.
Command Files: Review Reports & Comments
.archon/commands/defaults/archon-auto-fix-review.md, .archon/commands/defaults/archon-implement-review-fixes.md, .archon/commands/defaults/archon-post-review-to-pr.md, .archon/commands/defaults/archon-synthesize-review.md, .archon/commands/defaults/archon-code-review-agent.md, .archon/commands/defaults/archon-comment-quality-agent.md, .archon/commands/defaults/archon-docs-impact-agent.md, .archon/commands/defaults/archon-error-handling-agent.md, .archon/commands/defaults/archon-test-coverage-agent.md
Update PR comment posting and diff retrieval to use bun "$FORGE_CLI" instead of gh; parameterize output references from "GitHub" to $FORGE_NAME.
Command Files: Issue & Planning
.archon/commands/defaults/archon-issue-completion-report.md, .archon/commands/defaults/archon-implement.md, .archon/commands/defaults/archon-plan-setup.md, .archon/commands/defaults/archon-web-research.md, .archon/commands/defaults/archon-implement-issue.md
Update issue viewing/commenting and repository metadata commands to use forge-agnostic CLI; rename status keys from GITHUB_POSTED to FORGE_POSTED where applicable.
Command Files: Resolution & Self-Fix
.archon/commands/defaults/archon-resolve-merge-conflicts.md, .archon/commands/defaults/archon-self-fix-all.md, .archon/commands/defaults/archon-simplify-changes.md, .archon/commands/defaults/archon-workflow-summary.md
Replace GitHub CLI calls with forge-parameterized equivalents and update all comment-posting and branch-lookup references to use $FORGE_NAME and bun "$FORGE_CLI".
Workflow Files: Feature & Issue Workflows
.archon/workflows/defaults/archon-comprehensive-pr-review.yaml, .archon/workflows/defaults/archon-feature-development.yaml, .archon/workflows/defaults/archon-idea-to-pr.yaml, .archon/workflows/defaults/archon-issue-review-full.yaml, .archon/workflows/defaults/archon-plan-to-pr.yaml
Update workflow descriptions to reference $FORGE_NAME issue instead of GitHub issue; replace hardcoded GitHub terminology with parameterized forge variables.
Workflow Files: Execution & Planning
.archon/workflows/defaults/archon-create-issue.yaml, .archon/workflows/defaults/archon-piv-loop.yaml, .archon/workflows/defaults/archon-architect.yaml, .archon/workflows/defaults/archon-ralph-dag.yaml, .archon/workflows/defaults/archon-refactor-safely.yaml
Update bash node commands to invoke PR/issue operations via bun "$FORGE_CLI" instead of gh; maintain same arguments and JSON field queries.

Sequence Diagram

sequenceDiagram
    participant WE as Workflow Executor
    participant FD as Forge Detection
    participant GR as Git Remote
    participant FCW as Forge CLI Wrapper
    participant GH as GitHub/Gitea/GitLab

    WE->>FD: detectForge(repoPath)
    FD->>GR: git remote get-url origin
    GR-->>FD: remote URL
    FD->>FD: Parse hostname & match against<br/>GITEA_URL/GITLAB_URL env vars
    FD-->>WE: {type, apiBase}
    
    WE->>WE: Inject $FORGE_TYPE,<br/>$FORGE_API_BASE,<br/>$FORGE_CLI, $FORGE_NAME<br/>into config.envVars
    
    WE->>WE: Execute workflow steps with<br/>substituted variables
    WE->>FCW: bun "$FORGE_CLI" pr create ...
    
    alt forge_type == 'github'
        FCW->>GH: Shell to gh CLI
    else forge_type == 'gitea' or 'gitlab'
        FCW->>FCW: Build auth headers from<br/>GITEA_TOKEN/GITLAB_TOKEN
        FCW->>GH: fetch() REST API call
        GH-->>FCW: Response JSON
        FCW->>FCW: Normalize response to<br/>GitHub field shape
    end
    
    FCW-->>WE: Command output/result
    WE->>WE: Continue workflow with<br/>forge-agnostic data
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #1011: Modifies checkGhAuth() in packages/server/src/index.ts to conditionally skip GitHub auth when alternate forges are configured—directly complementary to this PR's forge detection and initialization logic.
  • PR #1206: Adds new exports to packages/git/src/index.ts (verifyWorktreeOwnership); this PR also extends the same module with forge detection types and functions, creating parallel API surface additions.
  • PR #1178: Propagates config.envVars through subprocesses and providers; this PR injects forge variables into config.envVars, making the two changes part of the same env-passthrough pipeline.

Poem

🐰 A rabbit's ode to the forge-agnostic future:

GitHub, Gitea, GitLab too,
Archon now knows what to do—
One CLI to bind them all,
Hopping freely through the hall. 🌿
The forge is free, the paths align,
Multi-hosted workflows shine! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.98% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and specifically describes the main change: adding forge-agnostic (Gitea/GitLab) support to workflow commands, which is the central objective of this changeset.
Linked Issues check ✅ Passed The PR fully implements all objectives from #1101: forge detection via detectForge(), variable injection ($FORGE_TYPE, $FORGE_API_BASE, $FORGE_CLI), forge-cli.ts wrapper routing to gh/Gitea/GitLab APIs, and extensive E2E testing against GitHub and Gitea with all operations succeeding.
Out of Scope Changes check ✅ Passed All changes directly support the stated PR objective of forge-agnostic workflow commands. The ESLint config update, test file additions, and documentation updates are necessary supporting changes. The only tangential fix (DATABASE_URL pollution) is minor and mentioned in the description.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering problem statement, business impact, detailed technical changes, scope boundaries, UX journey, architecture diagrams, validation evidence, security analysis, compatibility assessment, and rollback planning.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.archon/workflows/defaults/archon-fix-github-issue.yaml (2)

151-182: ⚠️ Potential issue | 🟠 Major

Inconsistency: create-pr node still uses gh directly.

The create-pr prompt at lines 169-181 still references gh pr list, gh pr create, and gh pr view directly, while the fetch-issue node was updated to use bun "$FORGE_CLI". This inconsistency means PR operations will fail on non-GitHub forges.

Consider updating the prompt to use $FORGE_CLI or adding a forge note similar to the command files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/workflows/defaults/archon-fix-github-issue.yaml around lines 151 -
182, The create-pr node's prompt still hardcodes GitHub CLI calls (gh pr list,
gh pr create, gh pr view) causing failures on non-GitHub forges; update the
prompt in the create-pr node to invoke the configured CLI via $FORGE_CLI (e.g.,
bun "$FORGE_CLI" pr list/create/view) or add the same forge-note/conditional
logic used in the fetch-issue node so PR commands are executed via $FORGE_CLI
when present and fall back appropriately for GitHub; ensure references to gh pr
list, gh pr create, and gh pr view are replaced/guarded and that the artifact
capture steps (PR_NUMBER/PR_URL) remain unchanged.

40-49: ⚠️ Potential issue | 🟡 Minor

Add normalization for consistent JSON field handling across forges.

The forge-cli.ts implementation handles the --json field selector inconsistently: GitHub passes it through to gh for field filtering, but Gitea and GitLab branches ignore it and return full API responses.

The workflow command at line 48 includes --json title,body,labels,comments,state,url,author, which works as expected for GitHub but returns unfiltered API responses for Gitea/GitLab. The downstream classify node expects GitHub-style field names and structures.

While basic fields (title, body, state) should exist across all forge APIs and likely work, normalize the response structure in forge-cli.ts to ensure consistent field filtering for Gitea/GitLab before full E2E support for those forges.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/workflows/defaults/archon-fix-github-issue.yaml around lines 40 -
49, The workflow passes "--json title,body,labels,comments,state,url,author" to
the "issue view" command but forge-cli.ts currently only forwards that selector
to gh and returns full API responses for Gitea/GitLab; update forge-cli.ts (the
issue view handler / command implementation that handles the "issue view"
subcommand and the --json flag) to parse the incoming --json field list, then
normalize Gitea/GitLab API responses into a GitHub-style filtered object (pick
and map fields: title, body, state, url, labels => array of names, comments =>
array with body/author/date, author => {login,url} etc.) and return only those
fields so downstream consumers get consistent JSON regardless of forge. Ensure
the normalization runs when the command handler sees the "--json" parameter and
that unknown/missing fields are omitted or set to null.
🧹 Nitpick comments (5)
eslint.config.mjs (1)

20-20: Don’t fully exclude .archon/scripts from ESLint coverage.

This ignore disables linting for forge-cli.ts, which is now part of the workflow execution path. Prefer keeping .archon/scripts/**/*.ts linted (optionally with a lighter override if type-aware linting is problematic) instead of blanket-ignore.

♻️ Suggested config adjustment
   {
     ignores: [
@@
-      '.archon/scripts/**',
       '**/*.js',
       '*.mjs',
@@
   {
     files: ['packages/*/src/**/*.{ts,tsx}'],
@@
   }
+  ,
+  // Lint workflow scripts (non-typechecked) instead of fully ignoring them
+  {
+    files: ['.archon/scripts/**/*.ts'],
+    languageOptions: {
+      parserOptions: {
+        projectService: false,
+      },
+    },
+    rules: {
+      '@typescript-eslint/no-explicit-any': 'error',
+    },
+  }
 );

Based on learnings: "Applies to **/*.{ts,tsx} : Enforce zero-tolerance ESLint policy: CI runs with --max-warnings 0 - no warnings allowed".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eslint.config.mjs` at line 20, The .archon/scripts/** ignore entry currently
skips linting for TypeScript scripts (including forge-cli.ts); remove or narrow
this ignore so .archon/scripts/**/*.ts (and .tsx if needed) are linted. Replace
the blanket ignore with a pattern that excludes only non-TS files (e.g. change
the ignore to avoid *.ts and *.tsx) or remove the line and add an ESLint
override for files matching .archon/scripts/**/*.ts to run linting (optionally
with lighter/type-free rules) so forge-cli.ts remains checked under the repo’s
zero-warnings policy.
packages/git/src/forge.ts (1)

64-104: Consider adding GitHub Enterprise detection.

The current detection logic handles github.com, Gitea via GITEA_URL, and GitLab (public + self-hosted). Self-hosted GitHub Enterprise instances (e.g., github.mycompany.com) would fall through to unknown.

If GitHub Enterprise support is planned, you could add a GITHUB_URL env var check similar to GitLab. However, since the PR objectives don't mention GHE, this may be intentional out-of-scope.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/git/src/forge.ts` around lines 64 - 104, detectForge currently
misses self-hosted GitHub Enterprise; add a GITHUB_URL env check (using
hostnameFromEnv('GITHUB_URL') like the GITLAB_URL/GITEA_URL checks) inside
detectForge so that when process.env.GITHUB_URL is set and its hostname matches
the repo remote hostname you return { type: 'github', apiBase:
`${cleanUrl}/api/v3` } (ensure you trim trailing slashes from the env value into
cleanUrl); reference detectForge and hostnameFromEnv to locate where to add this
block.
.archon/scripts/forge-cli.ts (2)

95-97: The gh helper doesn't handle errors gracefully.

execFileSync throws on non-zero exit codes. While the top-level try-catch in main() handles this, the error message will be the raw stderr which may be verbose. This is acceptable since it's delegating to gh, but consider whether cleaner error extraction is needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/scripts/forge-cli.ts around lines 95 - 97, The gh helper function
currently calls execFileSync('gh', ...) which will throw raw errors (with
verbose stderr) on non-zero exits; update the gh function to catch errors from
execFileSync, extract a cleaner message (e.g., prefer error.stderr or
error.message trimmed) and rethrow a new Error with a concise, contextual
message (or return a structured result) so the top-level main() receives a
clearer failure reason; reference the gh(args: string[]) helper and ensure any
thrown error preserves original stderr for debugging while presenting a short
message to callers.

159-166: prCreate requires the user to be on the branch they want to push.

The head branch is determined by git branch --show-current, which means the CLI must be run from the correct branch. This is a reasonable assumption for workflow use but differs from gh pr create which can infer or accept --head explicitly.

Consider documenting this behavior or adding --head flag support for flexibility.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/scripts/forge-cli.ts around lines 159 - 166, The prCreate function
currently derives the head branch via execFileSync('git', ['branch',
'--show-current']) which forces the user to run the CLI from that branch; update
prCreate to accept an explicit --head flag (parseArgs -> flags['--head']) and
use that value when provided, otherwise fall back to the current-branch
detection (the existing head variable), trim and validate the resulting branch
name, and use it where head is referenced; also add a clear error when no head
can be determined and ensure getOwnerRepo and existing logic continue to work
with the new head value.
packages/workflows/src/executor-shared.ts (1)

372-387: Preamble uses $FORGE_CLI but substitution already occurred.

The preamble is appended after substituteWorkflowVariables() completes, so the $FORGE_CLI in the preamble won't be substituted — it will appear literally as $FORGE_CLI in the prompt sent to the AI.

However, looking at the preamble text, it says bun "$FORGE_CLI" which is shell syntax showing the AI how to invoke it. This is intentional — the AI will write bash commands that use the $FORGE_CLI env var which is set in the subprocess environment.

This is correct but potentially confusing. A clarifying comment would help future readers.

📝 Proposed clarification comment
   if (resolvedForgeType !== 'github' && resolvedForgeType !== 'unknown') {
+    // The $FORGE_CLI in the preamble is intentionally NOT substituted here.
+    // It's shell syntax showing the AI how to reference the FORGE_CLI env var
+    // that will be available in subprocess execution environments.
     const preamble =
       '## Forge Compatibility\n\n' +
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/workflows/src/executor-shared.ts` around lines 372 - 387, The
appended forge preamble uses the literal string $FORGE_CLI intentionally (it
will remain un-substituted because substituteWorkflowVariables() has already
run) so add a short clarifying comment above the preamble construction noting:
1) substitution already occurred earlier via substituteWorkflowVariables(), 2)
$FORGE_CLI is meant to be left literal so the AI outputs shell snippets that
reference the runtime env var, and 3) that the subprocess environment will set
$FORGE_CLI; place this comment near the resolvedForgeType/finalPrompt logic
(references: resolvedForgeType, finalPrompt) so future readers aren’t confused.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.archon/commands/defaults/archon-create-pr.md:
- Around line 10-15: The markdown has an extra blank line between two
blockquotes (the linebreak separating the blockquote starting "If a base branch
was provided..." and the blockquote starting "**Forge note**: When
`$FORGE_TYPE`..."), which triggers markdownlint; remove the blank line (or merge
the two blockquotes) so the blockquotes are adjacent or combined and the lint
warning on line 13 is resolved.

In @.archon/commands/defaults/archon-finalize-pr.md:
- Line 11: The examples in the Forge note are inconsistent: the sentence uses
bun "$FORGE_CLI" but the examples use "$FORGE_CLI" pr ... without bun; update
the examples so they match the required invocation form when $FORGE_TYPE is not
github (i.e., prepend bun to each example such as bun "$FORGE_CLI" pr create,
bun "$FORGE_CLI" pr list, bun "$FORGE_CLI" pr edit), or alternatively change the
sentence to state that examples show the bare "$FORGE_CLI" form—ensure the
references to "$FORGE_CLI" and bun "$FORGE_CLI" are consistent throughout the
file.

In @.archon/scripts/forge-cli.ts:
- Around line 266-271: The Gitea branch in the prDiff logic fetches the pull
request diff but calls res.text() without checking response status; update the
case 'gitea' block (the fetch to
`${FORGE_API_BASE}/repos/${ownerRepo}/pulls/${number}.diff`) to verify res.ok
and throw or log a descriptive error (including status and statusText or
response body) when !res.ok before calling res.text(), matching the error
handling used in the other API branches.
- Around line 25-43: getOwnerRepo currently parses HTTPS remotes fine but its
SSH regex (sshMatch) fails for non-standard SSH URLs that include the ssh://
scheme and an embedded port (e.g., ssh://git@host:2222/owner/repo) and may
return the port as part of the captured group; update getOwnerRepo to
add/replace the SSH matching branch so it handles both the scp-like form
(git@host:owner/repo) and the full URL form (ssh://user@host[:port]/owner/repo),
capturing only the owner/repo path in either case (adjust the ssh regex used by
sshMatch to accept an optional ^ssh://(?:[^@]+@)?[^/:]+(?::\d+)?/([^\s]+)$
and/or a combined pattern) so non-standard SSH remotes with ports return the
correct owner/repo.

In @.archon/workflows/defaults/archon-create-issue.yaml:
- Around line 137-140: The dedup logic assumes a normalized JSON shape
(number,title,url,labels) but the wrapper in .archon/scripts/forge-cli.ts
returns raw forge payloads for non-GitHub forges; update the issue-list handler
(the "issue list" command implementation / listIssues function in forge-cli.ts)
to always map/normalize outputs to an object with keys number (numeric or
string), title, url, and labels (array of strings) regardless of forge before
emitting JSON, and ensure the YAML callers (archon-create-issue.yaml) can keep
requesting --json number,title,url,labels safely; also add/adjust unit or
integration tests to validate the normalized shape for at least one non-GitHub
forge.

In @.archon/workflows/defaults/archon-validate-pr.yaml:
- Line 31: The pr view invocation is missing the required PR identifier and
relies on the wrapper preserving the -q flag, which can leave
PR_NUMBER/PR_HEAD/PR_BASE empty; update the calls that set PR_NUMBER, PR_HEAD
and PR_BASE to invoke the forge-cli with an explicit PR identifier (e.g., pr
view <PR_ID> or a known env var) and stop relying on -q output from the
wrapper—instead use --json and pipe the output to jq (or another JSON parser) to
extract .number, .head.ref and .base.ref so the values are reliably populated
even when the wrapper alters CLI behavior.

In `@packages/workflows/src/executor.ts`:
- Around line 283-285: detectForge currently overwrites previously initialized
defaults (forgeType, forgeApiBase) even when it returns type 'unknown' or an
empty apiBase; change the assignment so you only replace the defaults when
detectForge(toRepoPath(cwd)) yields a supported forge and a non-empty apiBase
(e.g., check forgeInfo.type !== 'unknown' && forgeInfo.apiBase). Update the
block around detectForge/forgeInfo to guard assignments to forgeType and
forgeApiBase accordingly so defaults remain when detection is inconclusive.

---

Outside diff comments:
In @.archon/workflows/defaults/archon-fix-github-issue.yaml:
- Around line 151-182: The create-pr node's prompt still hardcodes GitHub CLI
calls (gh pr list, gh pr create, gh pr view) causing failures on non-GitHub
forges; update the prompt in the create-pr node to invoke the configured CLI via
$FORGE_CLI (e.g., bun "$FORGE_CLI" pr list/create/view) or add the same
forge-note/conditional logic used in the fetch-issue node so PR commands are
executed via $FORGE_CLI when present and fall back appropriately for GitHub;
ensure references to gh pr list, gh pr create, and gh pr view are
replaced/guarded and that the artifact capture steps (PR_NUMBER/PR_URL) remain
unchanged.
- Around line 40-49: The workflow passes "--json
title,body,labels,comments,state,url,author" to the "issue view" command but
forge-cli.ts currently only forwards that selector to gh and returns full API
responses for Gitea/GitLab; update forge-cli.ts (the issue view handler /
command implementation that handles the "issue view" subcommand and the --json
flag) to parse the incoming --json field list, then normalize Gitea/GitLab API
responses into a GitHub-style filtered object (pick and map fields: title, body,
state, url, labels => array of names, comments => array with body/author/date,
author => {login,url} etc.) and return only those fields so downstream consumers
get consistent JSON regardless of forge. Ensure the normalization runs when the
command handler sees the "--json" parameter and that unknown/missing fields are
omitted or set to null.

---

Nitpick comments:
In @.archon/scripts/forge-cli.ts:
- Around line 95-97: The gh helper function currently calls execFileSync('gh',
...) which will throw raw errors (with verbose stderr) on non-zero exits; update
the gh function to catch errors from execFileSync, extract a cleaner message
(e.g., prefer error.stderr or error.message trimmed) and rethrow a new Error
with a concise, contextual message (or return a structured result) so the
top-level main() receives a clearer failure reason; reference the gh(args:
string[]) helper and ensure any thrown error preserves original stderr for
debugging while presenting a short message to callers.
- Around line 159-166: The prCreate function currently derives the head branch
via execFileSync('git', ['branch', '--show-current']) which forces the user to
run the CLI from that branch; update prCreate to accept an explicit --head flag
(parseArgs -> flags['--head']) and use that value when provided, otherwise fall
back to the current-branch detection (the existing head variable), trim and
validate the resulting branch name, and use it where head is referenced; also
add a clear error when no head can be determined and ensure getOwnerRepo and
existing logic continue to work with the new head value.

In `@eslint.config.mjs`:
- Line 20: The .archon/scripts/** ignore entry currently skips linting for
TypeScript scripts (including forge-cli.ts); remove or narrow this ignore so
.archon/scripts/**/*.ts (and .tsx if needed) are linted. Replace the blanket
ignore with a pattern that excludes only non-TS files (e.g. change the ignore to
avoid *.ts and *.tsx) or remove the line and add an ESLint override for files
matching .archon/scripts/**/*.ts to run linting (optionally with
lighter/type-free rules) so forge-cli.ts remains checked under the repo’s
zero-warnings policy.

In `@packages/git/src/forge.ts`:
- Around line 64-104: detectForge currently misses self-hosted GitHub
Enterprise; add a GITHUB_URL env check (using hostnameFromEnv('GITHUB_URL') like
the GITLAB_URL/GITEA_URL checks) inside detectForge so that when
process.env.GITHUB_URL is set and its hostname matches the repo remote hostname
you return { type: 'github', apiBase: `${cleanUrl}/api/v3` } (ensure you trim
trailing slashes from the env value into cleanUrl); reference detectForge and
hostnameFromEnv to locate where to add this block.

In `@packages/workflows/src/executor-shared.ts`:
- Around line 372-387: The appended forge preamble uses the literal string
$FORGE_CLI intentionally (it will remain un-substituted because
substituteWorkflowVariables() has already run) so add a short clarifying comment
above the preamble construction noting: 1) substitution already occurred earlier
via substituteWorkflowVariables(), 2) $FORGE_CLI is meant to be left literal so
the AI outputs shell snippets that reference the runtime env var, and 3) that
the subprocess environment will set $FORGE_CLI; place this comment near the
resolvedForgeType/finalPrompt logic (references: resolvedForgeType, finalPrompt)
so future readers aren’t confused.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4e3ce44a-9218-4955-a1db-56a658687f73

📥 Commits

Reviewing files that changed from the base of the PR and between 536584d and d0ea249.

📒 Files selected for processing (19)
  • .archon/commands/defaults/archon-create-pr.md
  • .archon/commands/defaults/archon-finalize-pr.md
  • .archon/commands/defaults/archon-investigate-issue.md
  • .archon/scripts/forge-cli.ts
  • .archon/workflows/defaults/archon-create-issue.yaml
  • .archon/workflows/defaults/archon-fix-github-issue.yaml
  • .archon/workflows/defaults/archon-validate-pr.yaml
  • eslint.config.mjs
  • packages/cli/src/cli.ts
  • packages/core/src/utils/env-allowlist.ts
  • packages/git/src/exec.ts
  • packages/git/src/forge.test.ts
  • packages/git/src/forge.ts
  • packages/git/src/index.ts
  • packages/workflows/src/dag-executor.test.ts
  • packages/workflows/src/dag-executor.ts
  • packages/workflows/src/executor-shared.ts
  • packages/workflows/src/executor.test.ts
  • packages/workflows/src/executor.ts

Comment thread .archon/commands/defaults/archon-create-pr.md
Comment thread .archon/commands/defaults/archon-finalize-pr.md
Comment thread .archon/scripts/forge-cli.ts
Comment thread .archon/scripts/forge-cli.ts
Comment thread .archon/workflows/defaults/archon-create-issue.yaml
Comment thread .archon/workflows/defaults/archon-validate-pr.yaml Outdated
Comment thread packages/workflows/src/executor.ts Outdated
When GITEA_URL or GITLAB_URL is set but no GH_TOKEN/GITHUB_TOKEN is
configured, the gh CLI auth warning is irrelevant and confusing. Skip
the check and log an info message instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/server/src/index.ts (1)

732-732: Use a stable structured event name for the skip log.

Line 732 currently uses a prose string as the log event. Prefer a normalized event key and keep details in fields.

Suggested patch
-    getLog().info('gh_auth.skipped — no GitHub token configured, using alternate forge');
+    getLog().info({ hasGhToken, hasOtherForge }, 'gh_auth.check_skipped');

As per coding guidelines, "Use structured logging with Pino; event naming format: {domain}.{action}_{state}; always pair _started with _completed or _failed; include context IDs and error details."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/server/src/index.ts` at line 732, Replace the prose log string in
the getLog().info call with a structured Pino event: use the normalized event
key "gh_auth.skipped" (domain.action_state pattern) and move the human details
into fields (e.g., reason: "no_github_token", forge: "alternate"); ensure any
available context IDs (requestId, userId, repoId) are included as fields and
that matching `_started`/`_completed|_failed` events exist where applicable;
update the getLog().info invocation in packages/server/src/index.ts to emit the
event name plus these structured fields instead of a freeform message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/server/src/index.ts`:
- Around line 730-731: The detection for alternate forge is too narrow: update
the hasOtherForge logic (where hasOtherForge is defined) to also consider a
GitLab setup that uses GITLAB_TOKEN and GITLAB_WEBHOOK_SECRET even if GITLAB_URL
is unset; specifically set hasOtherForge to true when process.env.GITEA_URL ||
process.env.GITLAB_URL || (process.env.GITLAB_TOKEN &&
process.env.GITLAB_WEBHOOK_SECRET) so the gh auth skip/warning path triggers
correctly when GitLab is configured via tokens.

---

Nitpick comments:
In `@packages/server/src/index.ts`:
- Line 732: Replace the prose log string in the getLog().info call with a
structured Pino event: use the normalized event key "gh_auth.skipped"
(domain.action_state pattern) and move the human details into fields (e.g.,
reason: "no_github_token", forge: "alternate"); ensure any available context IDs
(requestId, userId, repoId) are included as fields and that matching
`_started`/`_completed|_failed` events exist where applicable; update the
getLog().info invocation in packages/server/src/index.ts to emit the event name
plus these structured fields instead of a freeform message.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8fc04d29-5a26-4fa3-a4f3-c967119301bb

📥 Commits

Reviewing files that changed from the base of the PR and between d0ea249 and 96819ed.

📒 Files selected for processing (1)
  • packages/server/src/index.ts

Comment on lines +730 to +731
const hasOtherForge = !!(process.env.GITEA_URL || process.env.GITLAB_URL);
if (!hasGhToken && hasOtherForge) {
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 12, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Broaden alternate-forge detection to cover GitLab setups without GITLAB_URL.

On Line 730, hasOtherForge only checks URL vars. But GitLab is considered configured elsewhere with GITLAB_TOKEN + GITLAB_WEBHOOK_SECRET even when GITLAB_URL is unset (see Lines 345-353). In that case this skip path won’t trigger, and users can still get a misleading gh auth warning.

Suggested patch
-  const hasOtherForge = !!(process.env.GITEA_URL || process.env.GITLAB_URL);
+  const hasGiteaConfigured = Boolean(
+    process.env.GITEA_URL && process.env.GITEA_TOKEN && process.env.GITEA_WEBHOOK_SECRET
+  );
+  const hasGitLabConfigured = Boolean(
+    process.env.GITLAB_TOKEN && process.env.GITLAB_WEBHOOK_SECRET
+  );
+  const hasOtherForge =
+    hasGiteaConfigured || hasGitLabConfigured || Boolean(process.env.GITEA_URL || process.env.GITLAB_URL);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const hasOtherForge = !!(process.env.GITEA_URL || process.env.GITLAB_URL);
if (!hasGhToken && hasOtherForge) {
const hasGiteaConfigured = Boolean(
process.env.GITEA_URL && process.env.GITEA_TOKEN && process.env.GITEA_WEBHOOK_SECRET
);
const hasGitLabConfigured = Boolean(
process.env.GITLAB_TOKEN && process.env.GITLAB_WEBHOOK_SECRET
);
const hasOtherForge =
hasGiteaConfigured || hasGitLabConfigured || Boolean(process.env.GITEA_URL || process.env.GITLAB_URL);
if (!hasGhToken && hasOtherForge) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/server/src/index.ts` around lines 730 - 731, The detection for
alternate forge is too narrow: update the hasOtherForge logic (where
hasOtherForge is defined) to also consider a GitLab setup that uses GITLAB_TOKEN
and GITLAB_WEBHOOK_SECRET even if GITLAB_URL is unset; specifically set
hasOtherForge to true when process.env.GITEA_URL || process.env.GITLAB_URL ||
(process.env.GITLAB_TOKEN && process.env.GITLAB_WEBHOOK_SECRET) so the gh auth
skip/warning path triggers correctly when GitLab is configured via tokens.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Noted. The current detection relies on \ for self-hosted GitLab. Also added \ env var for GitHub Enterprise detection in this commit. Broadening to auto-detect GitLab without env vars would require heuristics (e.g., checking \ endpoint) — deferring to a follow-up.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@truck0321, understood — relying on GITLAB_URL as the explicit signal for self-hosted GitLab is a reasonable and deliberate choice, and heuristic detection (e.g., probing /api/v4/version) would indeed add complexity and latency at startup. The GitHub Enterprise env var addition also makes sense for symmetry.

Would you like me to open a follow-up GitHub issue to track the broader hasOtherForge detection improvement (covering GitLab configured via GITLAB_TOKEN/GITLAB_WEBHOOK_SECRET without GITLAB_URL)? That way it won't get lost.


🧠 Learnings used
Learnt from: dhcdata
Repo: coleam00/Archon PR: 0
File: :0-0
Timestamp: 2026-04-12T22:05:38.453Z
Learning: In `packages/git/src/repo.ts`, `parseOwnerRepoFromRemoteUrl` uses an `isSafePathSegment` helper with allowlist regex `/^[A-Za-z0-9._-]+$/` plus explicit rejection of `.` and `..` (the pre-checks are necessary because `.` and `..` both match the regex character class). Applied to owner and repo before returning, preventing path traversal via crafted git remote URLs into `~/.archon/workspaces/<owner>/<repo>/`.

Learnt from: CR
Repo: coleam00/Archon PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-13T12:22:57.653Z
Learning: Applies to **/*.{ts,tsx} : Use `archon/git` functions for git operations. Use `execFileAsync` (not `exec`) when calling git directly. Never run `git clean -fd` — use `git checkout .` instead to avoid permanently deleting untracked files.

Learnt from: CR
Repo: coleam00/Archon PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-13T12:22:57.653Z
Learning: Applies to packages/cli/src/**/*.ts : Variable substitution in commands supports: `$1`, `$2`, `$3` (positional args), `$ARGUMENTS` (all args), `$ARTIFACTS_DIR` (workflow artifacts), `$WORKFLOW_ID` (run ID), `$BASE_BRANCH` (auto-detected from git), `$DOCS_DIR` (from config, defaults to `docs/`), `$LOOP_USER_INPUT` (approval feedback), `$REJECTION_REASON` (reviewer feedback). Document which variables are supported in error messages.

Add $FORGE_NAME variable (GitHub/Gitea/GitLab proper case) and replace
~60 hardcoded "GitHub" references in commands and workflows with it.
Prompts now dynamically name the correct forge platform.

Also skip gh auth check at server startup for Gitea/GitLab-only setups.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.archon/commands/defaults/archon-validate-pr-code-review-main.md (1)

40-48: ⚠️ Potential issue | 🔴 Critical

Replace hardcoded gh calls with forge CLI in this linked-issue fetch block.

Line 40 makes this section forge-aware, but Lines 45 and 47 are still GitHub-specific. This will break linked-issue retrieval on Gitea/GitLab.

Proposed fix
 # Extract issue number from PR body (looks for "Fixes `#N`", "Closes `#N`", etc.)
 PR_NUMBER=$(cat $ARTIFACTS_DIR/.pr-number | tr -d '\n')
-ISSUE_NUMBER=$(gh pr view "$PR_NUMBER" --json body -q '.body' | grep -oE '(Fixes|Closes|Resolves)\s*#[0-9]+' | grep -oE '[0-9]+' | head -1)
+ISSUE_NUMBER=$(bun "$FORGE_CLI" pr view "$PR_NUMBER" --json body -q '.body' | grep -oE '(Fixes|Closes|Resolves)\s*#[0-9]+' | grep -oE '[0-9]+' | head -1)
 if [ -n "$ISSUE_NUMBER" ]; then
-  gh issue view "$ISSUE_NUMBER" --json title,body,labels,comments
+  bun "$FORGE_CLI" issue view "$ISSUE_NUMBER" --json title,body,labels,comments
 fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/commands/defaults/archon-validate-pr-code-review-main.md around
lines 40 - 48, The linked-issue fetch block currently calls GitHub CLI directly
(gh pr view and gh issue view); change those to the generic forge CLI used
elsewhere so this works for Gitea/GitLab too: replace the gh invocation that
reads the PR body (gh pr view ... -q '.body') with the equivalent forge pr view
command and replace gh issue view "$ISSUE_NUMBER" --json ... with the forge
issue view equivalent, keeping the existing variables PR_NUMBER and ISSUE_NUMBER
and preserving the same fields (title,body,labels,comments) and output parsing
logic.
.archon/commands/defaults/archon-auto-fix-review.md (1)

12-27: ⚠️ Potential issue | 🟠 Major

$FORGE_NAME wording is updated, but execution commands are still GitHub-specific.

This command template now promises forge-agnostic posting, but operational steps still call gh directly (gh pr view, gh pr comment). Replace those with bun "$FORGE_CLI" ... equivalents so non-GitHub forges actually work.

Also applies to: 264-316

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/commands/defaults/archon-auto-fix-review.md around lines 12 - 27,
The forge-posting commands in the archon-auto-fix-review template still call
GitHub's CLI directly (e.g., `gh pr view`, `gh pr comment`); update all
instances in this file (including the block referenced 264-316) to invoke the
forge CLI via the `$FORGE_CLI` wrapper using the `bun "$FORGE_CLI"` prefix
(e.g., replace `gh pr view`/`gh pr comment` and any other `gh` invocations with
`bun "$FORGE_CLI" pr view`/`bun "$FORGE_CLI" pr comment` equivalents),
preserving existing flags/arguments and any output handling so the commands
remain forge-agnostic. Ensure every `gh` call in the document is replaced and
test that the variable `$FORGE_CLI` is used consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.archon/commands/defaults/archon-create-pr.md:
- Around line 14-15: The forge-agnostic note warns to use bun "$FORGE_CLI" when
"$FORGE_TYPE" != "github", but the runnable command examples still call gh
directly (e.g., "gh pr list", "gh pr create", "gh pr view"); update those
snippets to invoke the forge wrapper instead — replace direct "gh ..." usages
with the wrapper variable (e.g., use "$FORGE_CLI pr list", "$FORGE_CLI pr
create", "$FORGE_CLI pr view" or prefix with bun "$FORGE_CLI" where applicable)
so the examples honor the "$FORGE_TYPE"/"$FORGE_CLI" behavior described in the
note.

In @.archon/commands/defaults/archon-investigate-issue.md:
- Around line 11-12: Update the runnable examples so they consistently use the
forge CLI wrapper instead of hard-coded gh commands: replace occurrences of "gh
issue view", "gh issue comment", etc. with "$FORGE_CLI issue view" and
"$FORGE_CLI issue comment", and ensure the note about non-GitHub forges
clarifies invoking the wrapper via bun (i.e., use bun "$FORGE_CLI" ... when
$FORGE_TYPE is not github) so all examples work outside GitHub.

In @.archon/commands/defaults/archon-issue-completion-report.md:
- Around line 222-225: The template currently hardcodes the GitHub CLI
invocation "gh issue comment" instead of using the forge-agnostic variable;
locate occurrences of the literal command string "gh issue comment" in the
template (references: Phase 4 block and the other blocks noted around the same
sections) and replace them with the forge-agnostic invocation bun "$FORGE_CLI"
issue comment ... so the command uses the $FORGE_CLI variable and works for
non-GitHub forges; ensure all three occurrences (the Phase 4 block plus the
other two blocks you flagged) are updated consistently.

In @.archon/commands/defaults/archon-synthesize-review.md:
- Around line 259-263: Phase 4's PR comment step uses the GitHub-only command
`gh pr comment`; update the posting block to invoke the configured forge CLI via
`bun "$FORGE_CLI"` (or the equivalent wrapper used in this project) instead of
hardcoding `gh`, ensuring the `$FORGE_NAME`-formatted review is posted through
the generic forge command; apply the same replacement for the other occurrence
noted around lines 376-377 (search for uses of `gh pr comment` in the Phase
4/post block and replace them with the `bun "$FORGE_CLI" ...` invocation).

---

Outside diff comments:
In @.archon/commands/defaults/archon-auto-fix-review.md:
- Around line 12-27: The forge-posting commands in the archon-auto-fix-review
template still call GitHub's CLI directly (e.g., `gh pr view`, `gh pr comment`);
update all instances in this file (including the block referenced 264-316) to
invoke the forge CLI via the `$FORGE_CLI` wrapper using the `bun "$FORGE_CLI"`
prefix (e.g., replace `gh pr view`/`gh pr comment` and any other `gh`
invocations with `bun "$FORGE_CLI" pr view`/`bun "$FORGE_CLI" pr comment`
equivalents), preserving existing flags/arguments and any output handling so the
commands remain forge-agnostic. Ensure every `gh` call in the document is
replaced and test that the variable `$FORGE_CLI` is used consistently.

In @.archon/commands/defaults/archon-validate-pr-code-review-main.md:
- Around line 40-48: The linked-issue fetch block currently calls GitHub CLI
directly (gh pr view and gh issue view); change those to the generic forge CLI
used elsewhere so this works for Gitea/GitLab too: replace the gh invocation
that reads the PR body (gh pr view ... -q '.body') with the equivalent forge pr
view command and replace gh issue view "$ISSUE_NUMBER" --json ... with the forge
issue view equivalent, keeping the existing variables PR_NUMBER and ISSUE_NUMBER
and preserving the same fields (title,body,labels,comments) and output parsing
logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 33457cc7-d147-413c-857e-f4fa82bc5f5c

📥 Commits

Reviewing files that changed from the base of the PR and between 96819ed and 20b0df8.

📒 Files selected for processing (25)
  • .archon/commands/defaults/archon-auto-fix-review.md
  • .archon/commands/defaults/archon-create-pr.md
  • .archon/commands/defaults/archon-implement-review-fixes.md
  • .archon/commands/defaults/archon-implement.md
  • .archon/commands/defaults/archon-investigate-issue.md
  • .archon/commands/defaults/archon-issue-completion-report.md
  • .archon/commands/defaults/archon-plan-setup.md
  • .archon/commands/defaults/archon-post-review-to-pr.md
  • .archon/commands/defaults/archon-pr-review-scope.md
  • .archon/commands/defaults/archon-resolve-merge-conflicts.md
  • .archon/commands/defaults/archon-self-fix-all.md
  • .archon/commands/defaults/archon-simplify-changes.md
  • .archon/commands/defaults/archon-synthesize-review.md
  • .archon/commands/defaults/archon-validate-pr-code-review-main.md
  • .archon/commands/defaults/archon-web-research.md
  • .archon/commands/defaults/archon-workflow-summary.md
  • .archon/workflows/defaults/archon-comprehensive-pr-review.yaml
  • .archon/workflows/defaults/archon-create-issue.yaml
  • .archon/workflows/defaults/archon-feature-development.yaml
  • .archon/workflows/defaults/archon-fix-github-issue.yaml
  • .archon/workflows/defaults/archon-idea-to-pr.yaml
  • .archon/workflows/defaults/archon-issue-review-full.yaml
  • .archon/workflows/defaults/archon-piv-loop.yaml
  • .archon/workflows/defaults/archon-plan-to-pr.yaml
  • packages/workflows/src/executor-shared.ts
✅ Files skipped from review due to trivial changes (17)
  • .archon/commands/defaults/archon-pr-review-scope.md
  • .archon/commands/defaults/archon-plan-setup.md
  • .archon/workflows/defaults/archon-comprehensive-pr-review.yaml
  • .archon/commands/defaults/archon-simplify-changes.md
  • .archon/workflows/defaults/archon-feature-development.yaml
  • .archon/commands/defaults/archon-post-review-to-pr.md
  • .archon/workflows/defaults/archon-plan-to-pr.yaml
  • .archon/workflows/defaults/archon-idea-to-pr.yaml
  • .archon/commands/defaults/archon-web-research.md
  • .archon/commands/defaults/archon-implement-review-fixes.md
  • .archon/workflows/defaults/archon-issue-review-full.yaml
  • .archon/commands/defaults/archon-self-fix-all.md
  • .archon/commands/defaults/archon-resolve-merge-conflicts.md
  • .archon/commands/defaults/archon-implement.md
  • .archon/workflows/defaults/archon-piv-loop.yaml
  • .archon/commands/defaults/archon-workflow-summary.md
  • packages/workflows/src/executor-shared.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • .archon/workflows/defaults/archon-fix-github-issue.yaml
  • .archon/workflows/defaults/archon-create-issue.yaml

Comment thread .archon/commands/defaults/archon-create-pr.md
Comment thread .archon/commands/defaults/archon-investigate-issue.md
Comment thread .archon/commands/defaults/archon-issue-completion-report.md
Comment thread .archon/commands/defaults/archon-synthesize-review.md
- Replace all hardcoded `gh` CLI calls with `bun "$FORGE_CLI"` across
  24 command files and 7 workflow YAMLs
- Add JSON field normalization in forge-cli.ts so Gitea/GitLab responses
  match GitHub's field names (number, title, body, url, headRefName, etc.)
- Add pr edit, pr ready, pr checks commands to forge-cli.ts
- Guard against 'unknown' forge type overwriting github defaults in executor
- Fix Gitea pr diff missing res.ok check
- Fix SSH URL parsing for ssh://host:port format
- Add GitHub Enterprise detection via GITHUB_URL env var
- Clean gh CLI error messages (extract first stderr line)
- Add --head flag support to pr create
- Light-lint forge-cli.ts under zero-warnings ESLint policy
- Fix pr view fallback in archon-validate-pr.yaml to use pr list
- Remove unsupported -q flag usage in resolve-paths bash node
- Add clarifying comment for un-substituted $FORGE_CLI in preamble

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@truck0321
Copy link
Copy Markdown
Author

Review Feedback Addressed (354f2f9)

All CodeRabbit comments resolved in a single commit:

# Issue Resolution
1 Create-PR node hardcodes gh Replaced with bun "$FORGE_CLI"
2 JSON field normalization Added normalizer layer in forge-cli.ts — Gitea/GitLab responses now emit GitHub-equivalent field names (number, title, body, url, headRefName, baseRefName, etc.) and respect --json field filtering
3 PR view missing identifier Fallback now uses pr list --head <branch> instead of unsupported no-arg pr view; removed -q flag usage
4 Unknown forge overwrites defaults Guarded — only overrides when type !== 'unknown'
5 Hardcoded gh in commands Replaced across all 24 default command files + 7 workflow YAMLs
6 Gitea diff missing res.ok Added error check
7 ESLint blanket-ignores scripts Now lightly lints forge-cli.ts (disableTypeChecked + style rules)
8 SSH URL with port fails Added ssh:// protocol URL parsing
9 gh helper raw stderr Catches and extracts first stderr line
10 prCreate no --head flag Added optional --head override
11 No GitHub Enterprise detection Added GITHUB_URL env var check (→ /api/v3)

New forge-cli commands added: pr edit, pr ready, pr checks

Validation: type-check ✓, lint ✓, format ✓, all tests pass (0 failures)

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
.archon/commands/defaults/archon-investigate-issue.md (1)

11-11: ⚠️ Potential issue | 🟡 Minor

The forge note still shows non-runnable inline examples.

The sentence correctly says to invoke the wrapper via bun "$FORGE_CLI", but the examples immediately after that drop bun. On non-GitHub forges, people copying the note will still get the wrong command form.

✏️ Proposed fix
-> **Forge note**: When `$FORGE_TYPE` is not `github`, use `bun "$FORGE_CLI"` instead of `gh` for all forge operations (e.g., `"$FORGE_CLI" issue view`, `"$FORGE_CLI" issue comment`). The tool handles authentication and API differences automatically.
+> **Forge note**: When `$FORGE_TYPE` is not `github`, use `bun "$FORGE_CLI"` instead of `gh` for all forge operations (e.g., `bun "$FORGE_CLI" issue view`, `bun "$FORGE_CLI" issue comment`). The tool handles authentication and API differences automatically.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/commands/defaults/archon-investigate-issue.md at line 11, The Forge
note shows correct guidance but the inline examples still drop the wrapper;
update the examples in archon-investigate-issue.md so they use the wrapper form
(e.g., replace instances of `"$FORGE_CLI" issue view` and `"$FORGE_CLI" issue
comment` with `bun "$FORGE_CLI" issue view` and `bun "$FORGE_CLI" issue comment`
or otherwise present both forms conditionally) ensuring the examples match the
sentence about invoking the wrapper when `$FORGE_TYPE` is not `github`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.archon/scripts/forge-cli.ts:
- Around line 15-16: Replace the ad-hoc git subprocess usage: remove the
execFileSync import and any local remote-parsing/branch-lookup logic and instead
import and call the shared helpers from `@archon/git` (e.g., use a resolver like
getRepoOwnerAndName and getCurrentBranch from the package, or add those helpers
in `@archon/git` if they don't exist). If you still need to shell out to git
directly, use the execFileAsync helper (not exec/execFileSync) exposed by
`@archon/git` or child utilities to run git safely. Update all spots that perform
owner/repo resolution and current-branch lookup (the blocks replacing ad-hoc
parsing and subprocess calls) to use these centralized helpers so parsing/safety
fixes remain in the shared package.
- Around line 311-316: In the GitLab branch (case 'gitlab') change the request
so it no longer sends the unsupported draft field: before calling
apiPost(`${FORGE_API_BASE}/projects/${pid}/merge_requests`, ...) detect the
draft flag and, if true, prefix the MR title (the title variable) with a GitLab
draft marker such as "Draft: " or "WIP: " (avoiding double-prefixing if already
present), then call apiPost without the draft property; update any relevant
local variables (title/head/base/body) and keep using gitlabProjectId and
normalizeGitlabMr as-is.
- Around line 405-415: The current 'gitlab' case uses gitlabProjectId and apiGet
to call the repository/compare endpoint and reconstruct diffs from target_branch
and source_branch (compare variable), which can drift; replace this with a call
to the merge request raw diffs endpoint
(/projects/:id/merge_requests/:iid/raw_diffs) using FORGE_API_BASE and the same
pid and number to fetch the MR's original diff blob, then print that blob
(instead of iterating compare.diffs) so the output reflects the exact MR patch
as reviewed; update the logic in the 'gitlab' case that currently builds 'mr'
and 'compare' to fetch and log the raw_diffs via apiGet.

---

Duplicate comments:
In @.archon/commands/defaults/archon-investigate-issue.md:
- Line 11: The Forge note shows correct guidance but the inline examples still
drop the wrapper; update the examples in archon-investigate-issue.md so they use
the wrapper form (e.g., replace instances of `"$FORGE_CLI" issue view` and
`"$FORGE_CLI" issue comment` with `bun "$FORGE_CLI" issue view` and `bun
"$FORGE_CLI" issue comment` or otherwise present both forms conditionally)
ensuring the examples match the sentence about invoking the wrapper when
`$FORGE_TYPE` is not `github`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bb7f9f3a-9f29-42ae-91f2-72591fac7295

📥 Commits

Reviewing files that changed from the base of the PR and between 20b0df8 and 354f2f9.

📒 Files selected for processing (36)
  • .archon/commands/defaults/archon-auto-fix-review.md
  • .archon/commands/defaults/archon-code-review-agent.md
  • .archon/commands/defaults/archon-comment-quality-agent.md
  • .archon/commands/defaults/archon-create-pr.md
  • .archon/commands/defaults/archon-docs-impact-agent.md
  • .archon/commands/defaults/archon-error-handling-agent.md
  • .archon/commands/defaults/archon-finalize-pr.md
  • .archon/commands/defaults/archon-implement-issue.md
  • .archon/commands/defaults/archon-implement-review-fixes.md
  • .archon/commands/defaults/archon-investigate-issue.md
  • .archon/commands/defaults/archon-issue-completion-report.md
  • .archon/commands/defaults/archon-plan-setup.md
  • .archon/commands/defaults/archon-post-review-to-pr.md
  • .archon/commands/defaults/archon-pr-review-scope.md
  • .archon/commands/defaults/archon-resolve-merge-conflicts.md
  • .archon/commands/defaults/archon-self-fix-all.md
  • .archon/commands/defaults/archon-synthesize-review.md
  • .archon/commands/defaults/archon-test-coverage-agent.md
  • .archon/commands/defaults/archon-validate-pr-code-review-feature.md
  • .archon/commands/defaults/archon-validate-pr-code-review-main.md
  • .archon/commands/defaults/archon-validate-pr-e2e-main.md
  • .archon/commands/defaults/archon-validate-pr-report.md
  • .archon/commands/defaults/archon-web-research.md
  • .archon/commands/defaults/archon-workflow-summary.md
  • .archon/scripts/forge-cli.ts
  • .archon/workflows/defaults/archon-architect.yaml
  • .archon/workflows/defaults/archon-create-issue.yaml
  • .archon/workflows/defaults/archon-fix-github-issue.yaml
  • .archon/workflows/defaults/archon-piv-loop.yaml
  • .archon/workflows/defaults/archon-ralph-dag.yaml
  • .archon/workflows/defaults/archon-refactor-safely.yaml
  • .archon/workflows/defaults/archon-validate-pr.yaml
  • eslint.config.mjs
  • packages/git/src/forge.ts
  • packages/workflows/src/executor-shared.ts
  • packages/workflows/src/executor.ts
✅ Files skipped from review due to trivial changes (6)
  • .archon/commands/defaults/archon-validate-pr-code-review-main.md
  • .archon/commands/defaults/archon-web-research.md
  • .archon/workflows/defaults/archon-piv-loop.yaml
  • .archon/workflows/defaults/archon-validate-pr.yaml
  • .archon/commands/defaults/archon-self-fix-all.md
  • .archon/commands/defaults/archon-synthesize-review.md
🚧 Files skipped from review as they are similar to previous changes (9)
  • .archon/commands/defaults/archon-plan-setup.md
  • .archon/commands/defaults/archon-post-review-to-pr.md
  • .archon/workflows/defaults/archon-create-issue.yaml
  • .archon/commands/defaults/archon-resolve-merge-conflicts.md
  • eslint.config.mjs
  • .archon/commands/defaults/archon-workflow-summary.md
  • .archon/commands/defaults/archon-issue-completion-report.md
  • packages/workflows/src/executor-shared.ts
  • packages/git/src/forge.ts
👮 Files not reviewed due to content moderation or server errors (5)
  • .archon/commands/defaults/archon-auto-fix-review.md
  • .archon/commands/defaults/archon-finalize-pr.md
  • .archon/commands/defaults/archon-create-pr.md
  • .archon/commands/defaults/archon-implement-review-fixes.md
  • .archon/workflows/defaults/archon-fix-github-issue.yaml

Comment on lines +15 to +16
import { execFileSync } from 'child_process';

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 13, 2026

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Replace the ad-hoc git shelling with @archon/git helpers.

This script now reimplements remote parsing and branch lookup with direct git subprocess calls, which is exactly the repo pattern we're trying to avoid in TypeScript. Please route owner/repo and current-branch resolution through the shared git package (or add a helper there) so parsing and safety fixes stay centralized. As per coding guidelines: Use @archon/git functions for git operations. Use execFileAsync (not exec) when calling git directly.

Also applies to: 25-47, 292-293

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/scripts/forge-cli.ts around lines 15 - 16, Replace the ad-hoc git
subprocess usage: remove the execFileSync import and any local
remote-parsing/branch-lookup logic and instead import and call the shared
helpers from `@archon/git` (e.g., use a resolver like getRepoOwnerAndName and
getCurrentBranch from the package, or add those helpers in `@archon/git` if they
don't exist). If you still need to shell out to git directly, use the
execFileAsync helper (not exec/execFileSync) exposed by `@archon/git` or child
utilities to run git safely. Update all spots that perform owner/repo resolution
and current-branch lookup (the blocks replacing ad-hoc parsing and subprocess
calls) to use these centralized helpers so parsing/safety fixes remain in the
shared package.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Acknowledged. forge-cli.ts is a standalone Bun script with zero \ dependencies by design — it runs in workflow bash nodes via . Importing \ would couple it to the monorepo's package graph and require bundling. Leaving this for the repo owner to decide.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@truck0321, understood — that's a valid architectural constraint. A script executed via bun in workflow bash nodes outside the monorepo context can't rely on @archon/git being resolvable, making zero-dependency standalone the right call here. I'll note this for future reviews.


✏️ Learnings added
Learnt from: truck0321
Repo: coleam00/Archon PR: 1104
File: .archon/scripts/forge-cli.ts:15-16
Timestamp: 2026-04-15T12:56:28.206Z
Learning: `.archon/scripts/forge-cli.ts` is an intentionally standalone Bun script with zero dependencies by design. It runs in workflow bash nodes via `bun` execution outside the monorepo package graph, so importing `archon/git` or any other monorepo package would require bundling and is explicitly not desired. Do not suggest importing monorepo packages into this script.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: coleam00/Archon PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-15T06:52:05.753Z
Learning: Applies to packages/{git,isolation,cli,core,adapters,server}/**/*.ts : Git Operations: Use `archon/git` functions for git operations; use `execFileAsync` (not `exec`) when calling git directly

Learnt from: CR
Repo: coleam00/Archon PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-15T06:52:05.753Z
Learning: Architecture: `archon/git` handles git operations - worktrees, branches, repos, exec wrappers (depends only on archon/paths)

Learnt from: CR
Repo: coleam00/Archon PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-15T06:52:05.753Z
Learning: Applies to packages/{git,isolation,cli,core,adapters,server}/**/*.ts : Git Operations: NEVER run `git clean -fd` - it permanently deletes untracked files (use `git checkout .` instead)

Learnt from: CR
Repo: coleam00/Archon PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-15T06:52:05.753Z
Learning: Applies to packages/{core,adapters,server}/**/*.ts : Git Error Handling: Use `classifyIsolationError()` from `archon/isolation` to map git errors to user-friendly messages, always log raw error and send classified message to user

Learnt from: dhcdata
Repo: coleam00/Archon PR: 0
File: :0-0
Timestamp: 2026-04-12T22:05:38.453Z
Learning: In `packages/git/src/repo.ts`, `parseOwnerRepoFromRemoteUrl` uses an `isSafePathSegment` helper with allowlist regex `/^[A-Za-z0-9._-]+$/` plus explicit rejection of `.` and `..` (the pre-checks are necessary because `.` and `..` both match the regex character class). Applied to owner and repo before returning, preventing path traversal via crafted git remote URLs into `~/.archon/workspaces/<owner>/<repo>/`.

Learnt from: CR
Repo: coleam00/Archon PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-15T06:52:05.753Z
Learning: Applies to packages/{core,workflows}/src/**/*.ts : Commands: Variable substitution must support `$1`, `$2`, `$3`, `$ARGUMENTS`, `$ARTIFACTS_DIR`, `$WORKFLOW_ID`, `$BASE_BRANCH`, `$DOCS_DIR`, `$LOOP_USER_INPUT`, and `$REJECTION_REASON`

Comment thread .archon/scripts/forge-cli.ts
Comment thread .archon/scripts/forge-cli.ts
- GitLab API doesn't accept 'draft' param on MR creation; prefix title
  with "Draft: " instead
- Use /merge_requests/:iid/raw_diffs endpoint for GitLab pr diff instead
  of repository/compare, which drifts as branches move

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented Apr 20, 2026

@truck0321 this is excellent work, please resolve the conflicts and we should be able to merge this in, i will do a proper review once the conflicts are resolved

Adapt forge-agnostic feature to dev's refactored workflow engine:
- Forge env vars (FORGE_TYPE, FORGE_API_BASE, FORGE_CLI) now flow through
  config.envVars instead of explicit function parameters
- Accept dev's baseOptions/nodeConfig/SendQueryOptions pattern
- Accept dev's strip-cwd-env-boot env loading in CLI
- Remove deleted env-allowlist.ts (replaced by @archon/paths env-loader)
- Update all test call sites and expectations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.archon/scripts/forge-cli.ts:
- Around line 721-749: The repoInfo function currently returns Gitea/GitLab JSON
with defaultBranch, but GitHub returns defaultBranchRef.name; update the Gitea
and GitLab branches in repoInfo to emit the same shape as GitHub by replacing
defaultBranch with defaultBranchRef: { name: <branchName> } (keep nameWithOwner
identical), locating the transformation in the gitea and gitlab case blocks
where data.default_branch is used; ensure JSON.stringify outputs nameWithOwner
and defaultBranchRef to match GitHub's structure.
- Around line 593-601: The current label resolution silently drops labels that
don't exist in Gitea: in the block that builds labelIds using labels, allLabels
and apiGet, detect any requested label names from the input variable labels that
are not present in allLabels, and instead of filtering them out, throw a clear
error (including the missing label names) so callers fail fast; update the logic
around labelIds / labels / allLabels to compute missing = labels.filter(name =>
!allLabels.some(l => l.name === name)) and throw an Error listing missing before
continuing to build labelIds.
- Around line 257-282: The prView function currently rejects calls without a PR
number before switching on FORGE_TYPE, breaking gh pr view's current-branch
fallback; update prView so the early existence check for number is removed or
deferred and instead enforce a missing-number error only for gitea/gitlab
branches (inside their case blocks), while the 'github' case should build ghArgs
conditionally (include number only if present) and call gh(ghArgs) so gh's
native current-branch behavior is preserved; locate symbols prView, parseArgs,
FORGE_TYPE and the 'github' case to implement this change.
- Around line 504-515: The GitHub branch in prChecks currently only forwards
--json and --jq and drops all other flags/positionals; update prChecks so when
FORGE_TYPE === 'github' you build ghArgs starting with ['pr','checks',
...positional] (use the full positional array, not just positional[0]) and then
iterate over Object.entries(flags) to push each flag: if the flag value is true
push only the flag name (e.g., '--watch'), otherwise push the flag name and its
value (e.g., '--json', value). Keep using gh(ghArgs) so all original gh pr
checks selectors and flags are forwarded unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8fff695e-c401-42c1-a40d-54f93be2534b

📥 Commits

Reviewing files that changed from the base of the PR and between 354f2f9 and 686336e.

📒 Files selected for processing (13)
  • .archon/commands/defaults/archon-implement-issue.md
  • .archon/commands/defaults/archon-implement.md
  • .archon/commands/defaults/archon-plan-setup.md
  • .archon/scripts/forge-cli.ts
  • eslint.config.mjs
  • packages/git/src/index.ts
  • packages/server/src/index.ts
  • packages/workflows/src/dag-executor.test.ts
  • packages/workflows/src/dag-executor.ts
  • packages/workflows/src/defaults/bundled-defaults.generated.ts
  • packages/workflows/src/executor-shared.ts
  • packages/workflows/src/executor.test.ts
  • packages/workflows/src/executor.ts
✅ Files skipped from review due to trivial changes (4)
  • packages/workflows/src/dag-executor.test.ts
  • .archon/commands/defaults/archon-implement.md
  • .archon/commands/defaults/archon-plan-setup.md
  • .archon/commands/defaults/archon-implement-issue.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/server/src/index.ts
  • packages/workflows/src/executor.test.ts
  • packages/workflows/src/executor.ts
  • packages/workflows/src/executor-shared.ts

Comment on lines +257 to +282
async function prView(args: string[]): Promise<void> {
const { positional, flags } = parseArgs(args);
const number = positional[0];
if (!number) { console.error('error: pr view requires a number'); process.exit(1); }
const ownerRepo = getOwnerRepo();
const jsonFields = flags['--json'] as string | undefined;

switch (FORGE_TYPE) {
case 'github': {
const ghArgs = ['pr', 'view', number];
if (jsonFields) ghArgs.push('--json', jsonFields);
console.log(gh(ghArgs));
break;
}
case 'gitea': {
const data = await apiGet(`${FORGE_API_BASE}/repos/${ownerRepo}/pulls/${number}`);
outputNormalized(data, normalizeGiteaPr, jsonFields);
break;
}
case 'gitlab': {
const pid = await gitlabProjectId(ownerRepo);
const data = await apiGet(`${FORGE_API_BASE}/projects/${pid}/merge_requests/${number}`);
outputNormalized(data, normalizeGitlabMr, jsonFields);
break;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Preserve gh pr view’s current-branch fallback.

Line 260 rejects pr view --json ... without a number, but gh pr view supports viewing the PR for the current branch. Since workflows now route through this wrapper, this breaks GitHub behavior and also blocks the intended non-GitHub fallback via pr list --head.

🛠️ Proposed direction
 async function prView(args: string[]): Promise<void> {
   const { positional, flags } = parseArgs(args);
   const number = positional[0];
-  if (!number) { console.error('error: pr view requires a number'); process.exit(1); }
   const ownerRepo = getOwnerRepo();
   const jsonFields = flags['--json'] as string | undefined;
+  const head = (flags['--head'] as string | undefined)
+    ?? execFileSync('git', ['branch', '--show-current'], { encoding: 'utf8' }).trim();

   switch (FORGE_TYPE) {
     case 'github': {
-      const ghArgs = ['pr', 'view', number];
+      const ghArgs = ['pr', 'view'];
+      if (number) ghArgs.push(number);
       if (jsonFields) ghArgs.push('--json', jsonFields);
       console.log(gh(ghArgs));
       break;
     }
     case 'gitea': {
+      if (!number) {
+        const data = (await apiGet(
+          `${FORGE_API_BASE}/repos/${ownerRepo}/pulls?state=open&head=${ownerRepo.split('/')[0]}:${encodeURIComponent(head)}`,
+        )) as unknown[];
+        if (!data[0]) throw new Error(`No open PR found for head branch ${head}`);
+        outputNormalized(data[0], normalizeGiteaPr, jsonFields);
+        break;
+      }
       const data = await apiGet(`${FORGE_API_BASE}/repos/${ownerRepo}/pulls/${number}`);
       outputNormalized(data, normalizeGiteaPr, jsonFields);
       break;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/scripts/forge-cli.ts around lines 257 - 282, The prView function
currently rejects calls without a PR number before switching on FORGE_TYPE,
breaking gh pr view's current-branch fallback; update prView so the early
existence check for number is removed or deferred and instead enforce a
missing-number error only for gitea/gitlab branches (inside their case blocks),
while the 'github' case should build ghArgs conditionally (include number only
if present) and call gh(ghArgs) so gh's native current-branch behavior is
preserved; locate symbols prView, parseArgs, FORGE_TYPE and the 'github' case to
implement this change.

Comment on lines +504 to +515
async function prChecks(args: string[]): Promise<void> {
const { positional, flags } = parseArgs(args);
const number = positional[0];
if (!number) { console.error('error: pr checks requires a number'); process.exit(1); }
const ownerRepo = getOwnerRepo();

switch (FORGE_TYPE) {
case 'github': {
const ghArgs = ['pr', 'checks', number];
if (flags['--json']) ghArgs.push('--json', flags['--json'] as string);
if (flags['--jq']) ghArgs.push('--jq', flags['--jq'] as string);
console.log(gh(ghArgs));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Forward all GitHub pr checks arguments instead of dropping flags.

The GitHub path currently discards valid gh pr checks flags/selectors other than --json and --jq — for example --watch, --required, or no selector for the current branch. That violates the “zero behavior change for GitHub” objective.

🛠️ Proposed fix for the GitHub branch
 async function prChecks(args: string[]): Promise<void> {
   const { positional, flags } = parseArgs(args);
   const number = positional[0];
-  if (!number) { console.error('error: pr checks requires a number'); process.exit(1); }
   const ownerRepo = getOwnerRepo();

   switch (FORGE_TYPE) {
     case 'github': {
-      const ghArgs = ['pr', 'checks', number];
-      if (flags['--json']) ghArgs.push('--json', flags['--json'] as string);
-      if (flags['--jq']) ghArgs.push('--jq', flags['--jq'] as string);
-      console.log(gh(ghArgs));
+      console.log(gh(['pr', 'checks', ...args]));
       break;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/scripts/forge-cli.ts around lines 504 - 515, The GitHub branch in
prChecks currently only forwards --json and --jq and drops all other
flags/positionals; update prChecks so when FORGE_TYPE === 'github' you build
ghArgs starting with ['pr','checks', ...positional] (use the full positional
array, not just positional[0]) and then iterate over Object.entries(flags) to
push each flag: if the flag value is true push only the flag name (e.g.,
'--watch'), otherwise push the flag name and its value (e.g., '--json', value).
Keep using gh(ghArgs) so all original gh pr checks selectors and flags are
forwarded unchanged.

Comment on lines +593 to +601
// Gitea uses label IDs — resolve from names
let labelIds: number[] = [];
if (labels.length > 0) {
const allLabels = (await apiGet(
`${FORGE_API_BASE}/repos/${ownerRepo}/labels`,
)) as { id: number; name: string }[];
labelIds = labels
.map((name) => allLabels.find((l) => l.name === name)?.id)
.filter((id): id is number => id !== undefined);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Do not silently drop missing Gitea labels.

If a requested label is not found, the issue is created without it. That can hide automation mistakes and produce incorrectly categorized issues.

🛡️ Proposed fix
         const allLabels = (await apiGet(
           `${FORGE_API_BASE}/repos/${ownerRepo}/labels`,
         )) as { id: number; name: string }[];
+        const missingLabels = labels.filter((name) => !allLabels.some((l) => l.name === name));
+        if (missingLabels.length > 0) {
+          throw new Error(`Gitea labels not found: ${missingLabels.join(', ')}`);
+        }
         labelIds = labels
           .map((name) => allLabels.find((l) => l.name === name)?.id)
           .filter((id): id is number => id !== undefined);

As per coding guidelines: “Prefer throwing early with a clear error for unsupported or unsafe states — never silently swallow errors or broaden permissions.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/scripts/forge-cli.ts around lines 593 - 601, The current label
resolution silently drops labels that don't exist in Gitea: in the block that
builds labelIds using labels, allLabels and apiGet, detect any requested label
names from the input variable labels that are not present in allLabels, and
instead of filtering them out, throw a clear error (including the missing label
names) so callers fail fast; update the logic around labelIds / labels /
allLabels to compute missing = labels.filter(name => !allLabels.some(l => l.name
=== name)) and throw an Error listing missing before continuing to build
labelIds.

Comment on lines +721 to +749
async function repoInfo(): Promise<void> {
const ownerRepo = getOwnerRepo();

switch (FORGE_TYPE) {
case 'github':
console.log(gh(['repo', 'view', '--json', 'nameWithOwner,defaultBranchRef']));
break;
case 'gitea': {
const data = (await apiGet(`${FORGE_API_BASE}/repos/${ownerRepo}`)) as {
full_name: string;
default_branch: string;
};
console.log(JSON.stringify({
nameWithOwner: data.full_name,
defaultBranch: data.default_branch,
}, null, 2));
break;
}
case 'gitlab': {
const pid = await gitlabProjectId(ownerRepo);
const data = (await apiGet(`${FORGE_API_BASE}/projects/${pid}`)) as {
path_with_namespace: string;
default_branch: string;
};
console.log(JSON.stringify({
nameWithOwner: data.path_with_namespace,
defaultBranch: data.default_branch,
}, null, 2));
break;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Normalize repo info to GitHub’s defaultBranchRef shape.

The GitHub branch emits defaultBranchRef, but Gitea/GitLab emit defaultBranch. Any workflow expecting GitHub-compatible JSON such as defaultBranchRef.name will fail on non-GitHub forges.

🛠️ Proposed fix
       console.log(JSON.stringify({
         nameWithOwner: data.full_name,
-        defaultBranch: data.default_branch,
+        defaultBranchRef: { name: data.default_branch },
       }, null, 2));
@@
       console.log(JSON.stringify({
         nameWithOwner: data.path_with_namespace,
-        defaultBranch: data.default_branch,
+        defaultBranchRef: { name: data.default_branch },
       }, null, 2));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.archon/scripts/forge-cli.ts around lines 721 - 749, The repoInfo function
currently returns Gitea/GitLab JSON with defaultBranch, but GitHub returns
defaultBranchRef.name; update the Gitea and GitLab branches in repoInfo to emit
the same shape as GitHub by replacing defaultBranch with defaultBranchRef: {
name: <branchName> } (keep nameWithOwner identical), locating the transformation
in the gitea and gitlab case blocks where data.default_branch is used; ensure
JSON.stringify outputs nameWithOwner and defaultBranchRef to match GitHub's
structure.

@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented Apr 22, 2026

PR Review Summary (Multi-Agent)

Comprehensive review by 6 specialist agents: code-reviewer, docs-impact, pr-test-analyzer, silent-failure-hunter, type-design-analyzer, code-simplifier.

Verdict: NEEDS FIXES — two blockers break the feature for its target users

The forge-agnostic design is sound and forge.ts detection is well-tested, but the feature is currently non-functional for command/prompt nodes on Gitea/GitLab (C1), silent-fails on missing tokens (C3), and silent-fails on self-hosted instances that forget URL env vars (C4). The forge-cli.ts also silently drops several flags (--jq, --fill, --comments) that the AI is told to use in command files.


Critical Issues (must fix before merge)

# Agent Issue Location
C1 code-reviewer $FORGE_TYPE substitutes to 'github' and $FORGE_CLI substitutes to '' in every command: and prompt: node. executeNodeInternal never receives forge params, so buildPromptWithContext runs with forgeType=undefined. The AI reads broken instructions like bun "" pr list and the non-GitHub preamble injection is skipped. Core feature is non-functional for AI nodes on Gitea/GitLab. packages/workflows/src/dag-executor.ts (executeNodeInternal call sites), executor-shared.ts:327-330
C2 code-reviewer FORGE_CLI path uses getAppArchonBasePath() which resolves via import.meta.dir — in a compiled binary this points to ~/.local/bin/, so the script doesn't exist. Every bun "$FORGE_CLI" invocation in a binary install will silently fail. Unlike commands/workflows, forge-cli.ts has no entry in bundled-defaults.generated.ts. packages/workflows/src/executor.ts:300
C3 silent-failure-hunter Missing GITEA_TOKEN / GITLAB_TOKEN / GH_TOKEN produces Authorization: token (empty) or empty PRIVATE-TOKEN: . GitLab may silently return public/anonymous data instead of 401, so workflows complete "successfully" against the wrong data set. No fail-fast guard. .archon/scripts/forge-cli.ts authHeaders()
C4 silent-failure-hunter detectForge returning 'unknown' silently injects GitHub defaults. A Gitea/GitLab user who forgets GITEA_URL/GITLAB_URL gets FORGE_TYPE=github — workflows then shell out to gh against a non-GitHub remote with cryptic auth errors, with no path back to the root cause. Violates CLAUDE.md's explicit Fail-Fast rule. Only warn-logged to server Pino; user sees nothing. packages/workflows/src/executor.ts forge detection block, packages/git/src/forge.ts
C5 pr-test-analyzer detectForge is not in the mock.module('@archon/git', ...) factory in executor.test.ts. Bun replaces it with undefined, the TypeError is swallowed by the try/catch, and the fallback path runs. The only test that could catch a regression in FORGE_* injection ('merges DB env vars on top of file config envVars') was weakened from toEqual to toMatchObject, so it now asserts nothing about forge vars. You could delete the entire forge-injection block and every test still passes. packages/workflows/src/executor.test.ts:30-33, 723

Important Issues (should fix before merge)

# Agent Issue Location
I1 code-simplifier / silent-failure-hunter --jq is silently dropped for Gitea/GitLab. 15+ command files use $(bun "$FORGE_CLI" pr view … --jq '.headRefName') — on Gitea/GitLab the shell variable receives {"headRefName":"main"} instead of main, breaking every downstream bash op. .archon/scripts/forge-cli.ts outputNormalized/filterFields; all command .md files
I2 silent-failure-hunter --fill is silently ignored for all forges (including GitHub) — prCreate builds ghArgs without it. archon-create-pr.md's "simple path" creates a PR with empty title and body. .archon/scripts/forge-cli.ts prCreate()
I3 silent-failure-hunter --comments flag (used by archon-post-review-to-pr.md) is never parsed; output is the PR object's comment-count integer, not comment bodies. Verification steps pass/fail incorrectly. .archon/scripts/forge-cli.ts prView()
I4 silent-failure-hunter archon-implement-issue.md calls bun "$FORGE_CLI" pr view --json url with no PR number — forge-cli has no gh-style branch auto-detection and exits with code 1 mid-workflow. .archon/commands/defaults/archon-implement-issue.md
I5 silent-failure-hunter / code-reviewer --draft is silently dropped by Gitea prCreate (no warning). prReady for Gitea at least outputs a message; prCreate is silent. .archon/scripts/forge-cli.ts prCreate() gitea case
I6 docs-impact CLAUDE.md "Variable Substitution" list is missing $FORGE_TYPE, $FORGE_CLI, $FORGE_API_BASE — the three main user-facing surfaces of this PR. CLAUDE.md (~line 683)
I7 docs-impact docs/reference/variables.md doesn't list the three new vars; Variable Availability + Substitution Order tables unchanged. packages/docs-web/src/content/docs/reference/variables.md
I8 docs-impact docs/reference/configuration.md has no GitLab env var section despite GITLAB_URL/GITLAB_TOKEN/GITLAB_WEBHOOK_SECRET being in .env.example. packages/docs-web/src/content/docs/reference/configuration.md
I9 docs-impact docs/adapters/community/gitlab.md still instructs users to brew install glab + glab auth login; the agent no longer calls glab, it calls bun "$FORGE_CLI" which hits the GitLab API natively. packages/docs-web/src/content/docs/adapters/community/gitlab.md
I10 docs-impact No CHANGELOG.md Unreleased entry — PR targets dev, which merges to main via /release. CHANGELOG.md
I11 pr-test-analyzer substituteWorkflowVariables has 17 tests covering every variable except the 4 new forge tokens ($FORGE_TYPE, $FORGE_API_BASE, $FORGE_CLI, $FORGE_NAME). A regex typo would silently pass literal $FORGE_TYPE to the AI. packages/workflows/src/executor-shared.test.ts
I12 pr-test-analyzer buildPromptWithContext forge preamble injection (the primary AI-behavior mechanism for Gitea/GitLab) has zero tests. All 3 existing tests call it with forgeType=undefined. packages/workflows/src/executor-shared.test.ts
I13 pr-test-analyzer forge.ts has no test for GitHub Enterprise detection (GITHUB_URL env), no test for SSH URL with non-standard port — both called out as tested in the PR description but absent from forge.test.ts. packages/git/src/forge.test.ts
I14 silent-failure-hunter / code-reviewer gh_auth.skipped log uses prose event name + info level + is fired when `GITEA_URL
I15 silent-failure-hunter repoInfo returns defaultBranchRef: { name } for GitHub but defaultBranch: "main" for Gitea/GitLab — field name mismatch, consumers using --jq '.defaultBranchRef.name' silently get null on non-GitHub. .archon/scripts/forge-cli.ts repoInfo()

Suggestions (nice to have)

# Agent Suggestion Location
S1 type-design-analyzer Type forgeType as ForgeType (not string) in executor.ts + function signatures. Restores exhaustiveness checking on forge dispatch; prevents accidental 'GITHUB' etc. from compiling. executor.ts, executor-shared.ts
S2 type-design-analyzer Make ForgeInfo a discriminated union — {type: KnownForgeType; apiBase: string} | {type: 'unknown'; apiBase: ''} — so the apiBase: '' invariant for unknown is unrepresentable as valid. packages/git/src/forge.ts
S3 code-simplifier substituteWorkflowVariables has 9 positional params with inline // loopUserInput // rejectionReason undefined placeholders at call sites. Move optional params to an options object. executor-shared.ts, dag-executor.ts
S4 code-simplifier Add missing apiPut helper — two GitLab prEdit/prReady blocks inline the fetch+ok-check+json pattern instead of reusing apiPost/apiPatch. .archon/scripts/forge-cli.ts
S5 code-simplifier issueCreate calls parseArgs for --title/--body then manually re-scans args with a for loop for --label (two parsing strategies on one array). Extend parseArgs to collect repeated flags, or pick one strategy. .archon/scripts/forge-cli.ts issueCreate()
S6 code-simplifier / code-reviewer Fix duplicate step // 3. comment in detection order (Gitea and GitLab public both labeled 3). Renumber 3/4/5; update JSDoc step list. packages/git/src/forge.ts
S7 code-simplifier archon-validate-pr.yaml uses inline bun -e JS to parse JSON — becomes simple --jq expressions once I1 is fixed. .archon/workflows/defaults/archon-validate-pr.yaml
S8 code-simplifier Extract forge preamble string into a named buildForgePreamble(forgeType) helper for testability and clarity of the "post-substitution" construction. executor-shared.ts

Strengths

  • forge.ts detection logic is clean, uses RepoPath branded-type consistent with @archon/git convention, and has 11 unit tests covering public/self-hosted/SSH/trailing-slash/invalid-env/case-insensitive cases.
  • Normalization layer (normalizeGiteaPr, normalizeGitlabMr) mapping Gitea/GitLab field names to GitHub-equivalent keys is the right design — contains the compatibility surface.
  • apiGet / apiPost / apiPatch consistently check !res.ok and surface the response body in thrown errors.
  • Top-level try/catch in main() of forge-cli.ts ensures unhandled async errors → console.error + exit(1).
  • authHeaders default: case correctly process.exit(1) on unknown FORGE_TYPE.
  • PR description is thorough (UX journey, architecture diagrams, explicit "not verified" list).
  • .env.example already contains all new Gitea/GitLab env vars with correct comments.

Recommended Actions (in order)

  1. Fix C1: thread config.envVars.FORGE_* (or the three fields) into executeNodeInternalbuildPromptWithContext. Without this, the PR is a no-op for AI nodes on Gitea/GitLab.
  2. Fix C3 + C4: fail-fast on missing tokens; surface unknown forge type to the user (platform message, not just log.warn).
  3. Fix C2: either bundle forge-cli.ts into bundled-defaults.generated.ts (binary installs) or add a startup existence check with a clear error.
  4. Fix C5: add detectForge to the @archon/git mock in executor.test.ts; restore toEqual or add an explicit assertion on FORGE_TYPE/FORGE_API_BASE/FORGE_CLI values.
  5. Fix I1–I5 (flag gaps): either implement --jq / --fill / --comments or fail fast with an explicit error. Silently creating blank PRs or returning JSON objects where a scalar is expected is the worst failure mode.
  6. Address docs gaps I6–I10: these are user-visible.
  7. Add tests I11–I13: forge substitution, preamble injection, GHE + SSH-port detection.
  8. Polish: S1–S8 after the above.

Review generated by 6 specialist agents on feat/gitea-forge-support → dev. All findings have file:line references in the agent detail above; happy to drill into any specific issue.

@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented Apr 27, 2026

@truck0321 this PR appears to fully address #1101. Consider adding Closes #1101 to the PR body so the issue auto-closes on merge.

@truck0321 truck0321 closed this Apr 29, 2026
@truck0321 truck0321 reopened this Apr 29, 2026
# Conflicts:
#	.archon/commands/defaults/archon-implement-issue.md
#	.archon/workflows/defaults/archon-architect.yaml
#	.archon/workflows/defaults/archon-refactor-safely.yaml
#	eslint.config.mjs
#	packages/workflows/src/dag-executor.ts
#	packages/workflows/src/defaults/bundled-defaults.generated.ts
#	packages/workflows/src/executor-shared.ts
#	packages/workflows/src/executor.ts
@truck0321
Copy link
Copy Markdown
Author

This one is kind of timely ;)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: forge-agnostic workflow commands (Gitea/GitLab support)

2 participants