feat: forge-agnostic workflow commands (Gitea/GitLab support)#1104
feat: forge-agnostic workflow commands (Gitea/GitLab support)#1104truck0321 wants to merge 7 commits intocoleam00:devfrom
Conversation
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>
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis 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 Changes
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 | 🟠 MajorInconsistency:
create-prnode still usesghdirectly.The
create-prprompt at lines 169-181 still referencesgh pr list,gh pr create, andgh pr viewdirectly, while thefetch-issuenode was updated to usebun "$FORGE_CLI". This inconsistency means PR operations will fail on non-GitHub forges.Consider updating the prompt to use
$FORGE_CLIor 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 | 🟡 MinorAdd normalization for consistent JSON field handling across forges.
The
forge-cli.tsimplementation handles the--jsonfield selector inconsistently: GitHub passes it through toghfor 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 downstreamclassifynode 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.tsto 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/scriptsfrom ESLint coverage.This ignore disables linting for
forge-cli.ts, which is now part of the workflow execution path. Prefer keeping.archon/scripts/**/*.tslinted (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 viaGITEA_URL, and GitLab (public + self-hosted). Self-hosted GitHub Enterprise instances (e.g.,github.mycompany.com) would fall through tounknown.If GitHub Enterprise support is planned, you could add a
GITHUB_URLenv 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: Theghhelper doesn't handle errors gracefully.
execFileSyncthrows on non-zero exit codes. While the top-level try-catch inmain()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:prCreaterequires the user to be on the branch they want to push.The
headbranch is determined bygit branch --show-current, which means the CLI must be run from the correct branch. This is a reasonable assumption for workflow use but differs fromgh pr createwhich can infer or accept--headexplicitly.Consider documenting this behavior or adding
--headflag 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_CLIbut substitution already occurred.The preamble is appended after
substituteWorkflowVariables()completes, so the$FORGE_CLIin the preamble won't be substituted — it will appear literally as$FORGE_CLIin 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_CLIenv 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
📒 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.yamleslint.config.mjspackages/cli/src/cli.tspackages/core/src/utils/env-allowlist.tspackages/git/src/exec.tspackages/git/src/forge.test.tspackages/git/src/forge.tspackages/git/src/index.tspackages/workflows/src/dag-executor.test.tspackages/workflows/src/dag-executor.tspackages/workflows/src/executor-shared.tspackages/workflows/src/executor.test.tspackages/workflows/src/executor.ts
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>
There was a problem hiding this comment.
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_startedwith_completedor_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
📒 Files selected for processing (1)
packages/server/src/index.ts
| const hasOtherForge = !!(process.env.GITEA_URL || process.env.GITLAB_URL); | ||
| if (!hasGhToken && hasOtherForge) { |
There was a problem hiding this comment.
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.
| 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@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>
There was a problem hiding this comment.
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 | 🔴 CriticalReplace hardcoded
ghcalls 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_NAMEwording is updated, but execution commands are still GitHub-specific.This command template now promises forge-agnostic posting, but operational steps still call
ghdirectly (gh pr view,gh pr comment). Replace those withbun "$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
📒 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.yamlpackages/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
- 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>
Review Feedback Addressed (354f2f9)All CodeRabbit comments resolved in a single commit:
New forge-cli commands added: Validation: type-check ✓, lint ✓, format ✓, all tests pass (0 failures) |
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
.archon/commands/defaults/archon-investigate-issue.md (1)
11-11:⚠️ Potential issue | 🟡 MinorThe 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 dropbun. 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
📒 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.yamleslint.config.mjspackages/git/src/forge.tspackages/workflows/src/executor-shared.tspackages/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
| import { execFileSync } from 'child_process'; | ||
|
|
There was a problem hiding this comment.
🛠️ 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@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`
- 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>
|
@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>
There was a problem hiding this comment.
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
📒 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.tseslint.config.mjspackages/git/src/index.tspackages/server/src/index.tspackages/workflows/src/dag-executor.test.tspackages/workflows/src/dag-executor.tspackages/workflows/src/defaults/bundled-defaults.generated.tspackages/workflows/src/executor-shared.tspackages/workflows/src/executor.test.tspackages/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
| 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; | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
| 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)); |
There was a problem hiding this comment.
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.
| // 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); |
There was a problem hiding this comment.
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.
| 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; |
There was a problem hiding this comment.
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.
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 usersThe forge-agnostic design is sound and Critical Issues (must fix before merge)
Important Issues (should fix before merge)
Suggestions (nice to have)
Strengths
Recommended Actions (in order)
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. |
|
@truck0321 this PR appears to fully address #1101. Consider adding |
# 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
|
This one is kind of timely ;) |
Summary
ghCLI for all git forge operations, making them GitHub-onlyFORGE_TYPE/FORGE_API_BASE/FORGE_CLIinto workflow env, replace all hardcodedghcalls withbun "$FORGE_CLI"across 24 command files and 7 workflow YAMLs. Newforge-cli.ts(822 lines) routes pr/issue/label/repo commands to the correct API.UX Journey
Before
After
Architecture Diagram
Before
After
Connection inventory:
Label Snapshot
risk: mediumsize: XLworkflows,git,server,cli,commandsworkflows:executor,git:forge,server:startupChange Metadata
featuremultiLinked Issue
Closes #1101
Validation Evidence (required)
bun run validate # All pass (type-check + lint + format + test)Security Impact (required)
No-- uses existing git remote URL (read-only), existing tokens (GITEA_TOKEN, GITLAB_TOKEN from env)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.No-- tokens read from existing env vars (GITEA_TOKEN, GITLAB_TOKEN, GH_TOKEN), never logged or storedNoCompatibility / Migration
Yes-- defaults to github forge type when detection fails or returns unknownYes-- new optional env vars: GITEA_URL, GITEA_TOKEN, GITEA_WEBHOOK_SECRET, GITLAB_URL, GITLAB_TOKEN, GITLAB_WEBHOOK_SECRET. None required for existing GitHub users.NoHuman Verification (required)
What was personally validated beyond CI:
Side Effects / Blast Radius (required)
Rollback Plan (required)
Risks and Mitigations