From 05e2acb73e4fa20f93847fc90cc63825b25738f3 Mon Sep 17 00:00:00 2001 From: Alisha Kawaguchi Date: Tue, 24 Mar 2026 09:07:40 -0700 Subject: [PATCH 1/2] feat: add GitHub Actions CI and lint workflows with golangci-lint config Add CI workflow (unit tests with race detector + build verification) and lint workflow (gofmt check + golangci-lint with inline PR annotations) triggered on PRs and pushes to main. Add .golangci.yaml v2 config with curated linter set adapted from devenv/cli. Fix existing lint issues: unused types, unchecked json.Marshal errors, formatting, and minor code quality improvements. Add .gitignore for runtime artifacts. Co-Authored-By: Claude Opus 4.6 (1M context) Entire-Checkpoint: 8e58bb2aa1e6 --- .github/workflows/ci.yml | 28 ++++++ .github/workflows/lint.yml | 44 +++++++++ .gitignore | 5 ++ .golangci.yaml | 90 +++++++++++++++++++ .../entire-agent-kiro/internal/kiro/agent.go | 6 +- .../entire-agent-kiro/internal/kiro/hooks.go | 5 +- .../internal/kiro/lifecycle_test.go | 4 +- .../internal/kiro/transcript.go | 27 +++--- .../entire-agent-kiro/internal/kiro/types.go | 12 --- .../internal/protocol/protocol.go | 3 +- 10 files changed, 193 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .gitignore create mode 100644 .golangci.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4dd29ed --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: CI + +on: + workflow_dispatch: + pull_request: + push: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - uses: actions/setup-go@v6 + with: + go-version: "stable" + + - name: Unit tests (agent-kiro) + working-directory: agents/entire-agent-kiro + run: go test -race -count=1 ./... + + - name: Build (agent-kiro) + working-directory: agents/entire-agent-kiro + run: go build -o /dev/null ./cmd/entire-agent-kiro diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..509821d --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,44 @@ +name: Lint + +on: + workflow_dispatch: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - uses: actions/setup-go@v6 + with: + go-version: "stable" + + - name: Check formatting + run: | + unformatted=$(gofmt -l .) + if [ -n "$unformatted" ]; then + echo "The following files are not formatted:" + echo "$unformatted" + exit 1 + fi + + - name: Run golangci-lint (agent-kiro) + uses: golangci/golangci-lint-action@v9 + with: + version: "v2.11.3" + working-directory: agents/entire-agent-kiro + + - name: Run golangci-lint (e2e) + uses: golangci/golangci-lint-action@v9 + with: + version: "v2.11.3" + working-directory: e2e diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b8c813d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Entire CLI runtime data +.entire/ + +# Built binaries +/agents/entire-agent-kiro/entire-agent-kiro diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..5e4d936 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,90 @@ +version: "2" + +issues: + max-issues-per-linter: 0 + max-same-issues: 0 + +linters: + default: standard + enable: + - asciicheck + - bidichk + - copyloopvar + - decorder + - durationcheck + - errchkjson + - errname + - errorlint + - exhaustive + - exptostd + - forcetypeassert + - gocheckcompilerdirectives + - gochecknoinits + - goconst + - gocritic + - gosec + - govet + - grouper + - importas + - ineffassign + - intrange + - makezero + - mirror + - misspell + - nakedret + - nilerr + - nilnesserr + - nolintlint + - perfsprint + - reassign + - recvcheck + - revive + - staticcheck + - testableexamples + - testifylint + - tparallel + - unconvert + - unparam + - unused + - usestdlibvars + - usetesting + - wastedassign + - whitespace + settings: + errcheck: + check-type-assertions: true + gosec: + excludes: + - G204 # subprocess with variables is expected for CLI tools + - G304 # file inclusion via variable is expected for CLI tools + - G705 # XSS not applicable to CLI stderr output + govet: + enable-all: true + disable: + - fieldalignment + - shadow + nolintlint: + require-explanation: true + require-specific: true + testifylint: + enable-all: true + exclusions: + presets: + - comments + - std-error-handling + rules: + - path: _test\.go + linters: + - errchkjson + - gosec + - goconst + - revive + - unparam + - path: ^e2e/ + linters: + - errcheck + - gochecknoinits + - goconst + - gosec + - revive + - usetesting diff --git a/agents/entire-agent-kiro/internal/kiro/agent.go b/agents/entire-agent-kiro/internal/kiro/agent.go index 2f55faf..d64b638 100644 --- a/agents/entire-agent-kiro/internal/kiro/agent.go +++ b/agents/entire-agent-kiro/internal/kiro/agent.go @@ -9,6 +9,8 @@ import ( "github.com/entireio/external-agents/agents/entire-agent-kiro/internal/protocol" ) +const stubSessionID = "stub-session-000" + type Agent struct{} func New() *Agent { @@ -48,7 +50,7 @@ func (a *Agent) GetSessionID(input *protocol.HookInputJSON) string { if input != nil && input.SessionID != "" { return input.SessionID } - return "stub-session-000" + return stubSessionID } func (a *Agent) ReadSession(input *protocol.HookInputJSON) (protocol.AgentSessionJSON, error) { @@ -58,7 +60,7 @@ func (a *Agent) ReadSession(input *protocol.HookInputJSON) (protocol.AgentSessio if err != nil { return protocol.AgentSessionJSON{}, err } - sessionRef := "" + var sessionRef string if input != nil && input.SessionRef != "" { sessionRef = input.SessionRef } else { diff --git a/agents/entire-agent-kiro/internal/kiro/hooks.go b/agents/entire-agent-kiro/internal/kiro/hooks.go index a6947f5..77de4a1 100644 --- a/agents/entire-agent-kiro/internal/kiro/hooks.go +++ b/agents/entire-agent-kiro/internal/kiro/hooks.go @@ -26,8 +26,8 @@ const ( localDevCommandBase = "go run ${KIRO_PROJECT_DIR}/cmd/entire/main.go hooks kiro " localDevTrustedCmd = "sh -c 'go run ${KIRO_PROJECT_DIR}/cmd/entire/main.go hooks *" prodHookCommandBase = "entire hooks kiro " - sessionIDFile = "kiro-active-session" - toolCallsFile = "kiro-tool-calls.jsonl" + sessionIDFile = "kiro-active-session" + toolCallsFile = "kiro-tool-calls.jsonl" ) type ideHookDef struct { @@ -504,7 +504,6 @@ func (a *Agent) sessionIDCachePath() string { return filepath.Join(protocol.RepoRoot(), ".entire", "tmp", sessionIDFile) } - func fallbackStopSessionID() string { return generateSessionID() } diff --git a/agents/entire-agent-kiro/internal/kiro/lifecycle_test.go b/agents/entire-agent-kiro/internal/kiro/lifecycle_test.go index 639af30..2d509be 100644 --- a/agents/entire-agent-kiro/internal/kiro/lifecycle_test.go +++ b/agents/entire-agent-kiro/internal/kiro/lifecycle_test.go @@ -75,7 +75,7 @@ func TestParseHookUserPromptSubmitSupportsIDEFallback(t *testing.T) { if event.Type != 2 { t.Fatalf("event.Type = %d, want %d", event.Type, 2) } - if event.SessionID == "" || event.SessionID == "stub-session-000" { + if event.SessionID == "" || event.SessionID == stubSessionID { t.Fatalf("session_id = %q, want generated stable ID", event.SessionID) } if event.Prompt != "ide prompt" { @@ -158,7 +158,7 @@ func TestParseHookStopWithoutCachedSessionIDUsesNonPredictableFallback(t *testin if event.SessionID == "" { t.Fatal("session_id should not be empty") } - if event.SessionID == "my-repo" || event.SessionID == "stub-session-000" { + if event.SessionID == "my-repo" || event.SessionID == stubSessionID { t.Fatalf("session_id = %q, want generated non-predictable fallback", event.SessionID) } diff --git a/agents/entire-agent-kiro/internal/kiro/transcript.go b/agents/entire-agent-kiro/internal/kiro/transcript.go index a29ebcc..50aee71 100644 --- a/agents/entire-agent-kiro/internal/kiro/transcript.go +++ b/agents/entire-agent-kiro/internal/kiro/transcript.go @@ -850,7 +850,10 @@ func convertExecutionActionsToHistoryEntries(actions []kiroExecutionAction) []ki if toolName, ok := execActionToToolName[action.ActionType]; ok { filePath := extractFilePath(action.Input) if filePath != "" { - args, _ := json.Marshal(map[string]string{"path": filePath}) + args, err := json.Marshal(map[string]string{"path": filePath}) + if err != nil { + continue + } toolCalls = append(toolCalls, kiroToolCall{ Name: toolName, Args: args, @@ -864,26 +867,32 @@ func convertExecutionActionsToHistoryEntries(actions []kiroExecutionAction) []ki if err := json.Unmarshal(action.Output, &out); err == nil && out.Message != "" { // Flush any pending tool calls first if len(toolCalls) > 0 { - toolUseJSON, _ := json.Marshal(kiroToolUseContent{ + toolUseJSON, err := json.Marshal(kiroToolUseContent{ ToolUse: kiroToolUsePayload{ToolUses: toolCalls}, }) - entries = append(entries, kiroHistoryEntry{Assistant: toolUseJSON}) + if err == nil { + entries = append(entries, kiroHistoryEntry{Assistant: toolUseJSON}) + } toolCalls = nil } - responseJSON, _ := json.Marshal(kiroResponseContent{ + responseJSON, err := json.Marshal(kiroResponseContent{ Response: kiroResponsePayload{Content: out.Message}, }) - entries = append(entries, kiroHistoryEntry{Assistant: responseJSON}) + if err == nil { + entries = append(entries, kiroHistoryEntry{Assistant: responseJSON}) + } } } } // Flush remaining tool calls without a trailing say if len(toolCalls) > 0 { - toolUseJSON, _ := json.Marshal(kiroToolUseContent{ + toolUseJSON, err := json.Marshal(kiroToolUseContent{ ToolUse: kiroToolUsePayload{ToolUses: toolCalls}, }) - entries = append(entries, kiroHistoryEntry{Assistant: toolUseJSON}) + if err == nil { + entries = append(entries, kiroHistoryEntry{Assistant: toolUseJSON}) + } } return entries @@ -924,9 +933,7 @@ func enrichIDETranscriptWithExecutionLogs(ideData []byte, execLogs map[string]*k if len(assistantEntries) > 0 { userEntry.Assistant = assistantEntries[0].Assistant transcript.History = append(transcript.History, userEntry) - for _, extra := range assistantEntries[1:] { - transcript.History = append(transcript.History, extra) - } + transcript.History = append(transcript.History, assistantEntries[1:]...) pendingUser = nil continue } diff --git a/agents/entire-agent-kiro/internal/kiro/types.go b/agents/entire-agent-kiro/internal/kiro/types.go index 8c65247..10e7bea 100644 --- a/agents/entire-agent-kiro/internal/kiro/types.go +++ b/agents/entire-agent-kiro/internal/kiro/types.go @@ -134,18 +134,6 @@ type kiroIDEHistoryMeta struct { // from the session files and contain the full action trace (tool calls, // agent responses, file modifications) for each agent turn. -type kiroExecutionIndex struct { - Executions []kiroExecutionIndexEntry `json:"executions"` -} - -type kiroExecutionIndexEntry struct { - ExecutionID string `json:"executionId"` - Type string `json:"type"` - Status string `json:"status"` - StartTime int64 `json:"startTime"` - EndTime int64 `json:"endTime"` -} - type kiroExecutionLog struct { ExecutionID string `json:"executionId"` ChatSessionID string `json:"chatSessionId"` diff --git a/agents/entire-agent-kiro/internal/protocol/protocol.go b/agents/entire-agent-kiro/internal/protocol/protocol.go index ae93d3b..39ff7fb 100644 --- a/agents/entire-agent-kiro/internal/protocol/protocol.go +++ b/agents/entire-agent-kiro/internal/protocol/protocol.go @@ -3,7 +3,6 @@ package protocol import ( "encoding/json" "flag" - "fmt" "io" "os" "path/filepath" @@ -309,5 +308,5 @@ func DefaultSessionDir(repoPath string) string { } func ResolveSessionFile(sessionDir, sessionID string) string { - return filepath.Join(sessionDir, fmt.Sprintf("%s.json", sessionID)) + return filepath.Join(sessionDir, sessionID+".json") } From 673f290a9e5dd8a74f59a9c3ca80f266cffc897d Mon Sep 17 00:00:00 2001 From: Alisha Kawaguchi Date: Tue, 24 Mar 2026 09:16:15 -0700 Subject: [PATCH 2/2] fix(ci): add gosec G703 exclusion and raise goconst min-occurrences CI golangci-lint v2.11.3 flagged G703 (path traversal via taint) for controlled file paths and goconst for "darwin" in runtime.GOOS switches. Exclude G703 (paths are constructed from controlled inputs) and raise goconst min-occurrences to 5 to avoid flagging idiomatic patterns. Co-Authored-By: Claude Opus 4.6 (1M context) Entire-Checkpoint: 93daed613361 --- .golangci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.golangci.yaml b/.golangci.yaml index 5e4d936..dcbbc01 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -53,10 +53,13 @@ linters: settings: errcheck: check-type-assertions: true + goconst: + min-occurrences: 5 gosec: excludes: - G204 # subprocess with variables is expected for CLI tools - G304 # file inclusion via variable is expected for CLI tools + - G703 # path traversal via taint — paths are constructed from controlled inputs - G705 # XSS not applicable to CLI stderr output govet: enable-all: true