Skip to content

feat(config): add multi-level workspace configuration system#99

Merged
feloy merged 2 commits intokortex-hub:mainfrom
feloy:config-levels
Mar 25, 2026
Merged

feat(config): add multi-level workspace configuration system#99
feloy merged 2 commits intokortex-hub:mainfrom
feloy:config-levels

Conversation

@feloy
Copy link
Copy Markdown
Contributor

@feloy feloy commented Mar 24, 2026

Implements a three-level configuration system allowing users to customize workspace settings at different contexts with automatic merging and proper precedence:

  • Workspace config (.kortex/workspace.json) - shared, committed
  • Project config (~/.kortex-cli/config/projects.json) - user-specific
  • Agent config (~/.kortex-cli/config/agents.json) - per-agent overrides

Configuration merging is handled by Manager for better separation of concerns. Added global project config support via empty string key.

Closes #88

Implementation Plan: Multi-Level Workspace Configuration

Issue: #88 - Workspace configuration at different levels
Branch: config-levels
Date: 2026-03-24

Problem Statement

Currently, workspace configuration is only available via the .kortex directory (stored with sources in the repository). This configuration is shared with all developers of the project and used with all agents.

Users need the ability to:

  • Complete or overwrite the shared project-related configuration in .kortex
  • Customize configuration depending on the agent being used
  • Have user-specific settings that don't get committed to the repository

Solution Overview

Implement a three-level configuration system with proper precedence rules:

  1. Workspace-level (.kortex/workspace.json) - Shared project configuration
  2. Project-specific (~/.kortex-cli/config/projects.json) - User's custom config for specific projects
  3. Agent-specific (~/.kortex-cli/config/agents.json) - Per-agent overrides

Precedence: Agent-specific > Project-specific > Workspace-level

Configuration File Formats

Agent Configuration

Location: ~/.kortex-cli/config/agents.json (or $KORTEX_CLI_STORAGE/config/agents.json if set)

{
  "claude": {
    "environment": [
      {
        "name": "DEBUG",
        "value": "true"
      }
    ],
    "mounts": {
      "dependencies": ["../shared-libs"],
      "configs": [".claude-config"]
    }
  },
  "goose": {
    "environment": [
      {
        "name": "GOOSE_MODE",
        "value": "verbose"
      }
    ]
  }
}

Project Configuration

Location: ~/.kortex-cli/config/projects.json (or $KORTEX_CLI_STORAGE/config/projects.json if set)

{
  "": {
    "mounts": {
      "configs": [".gitconfig", ".ssh"]
    }
  },
  "github.com/kortex-hub/kortex-cli": {
    "environment": [
      {
        "name": "API_KEY",
        "secret": "my-api-key-secret"
      }
    ],
    "mounts": {
      "dependencies": ["../kortex-common"]
    }
  },
  "/home/user/my/project": {
    "environment": [
      {
        "name": "LOCAL_DEV",
        "value": "true"
      }
    ]
  }
}

Special Key - Empty String "":

  • An empty string key ("") represents a global/default configuration that applies to all projects
  • Useful for common settings like GitHub config, SSH keys, or global environment variables
  • This configuration is merged first, then project-specific configs override it
  • Merging order: global ("") → project-specific → agent-specific

Note: Each configuration uses the same WorkspaceConfiguration structure defined in the kortex-cli-api package.

Implementation Tasks

Phase 1: Core Infrastructure

Task 1: Configuration Merger

File: pkg/config/merger.go

  • Create ConfigMerger interface for merging WorkspaceConfiguration objects
  • Implement merger with these rules:
    • Environment variables: Later configs override earlier ones (by name)
      • If the same variable name appears in multiple configs, use the one from the higher-precedence config
    • Mount dependencies: Deduplicated list (preserve order, no duplicates)
    • Mount configs: Deduplicated list (preserve order, no duplicates)
  • Handle nil pointers and empty arrays gracefully
  • Validate merged result using existing validation logic

Merging Algorithm:

merged = workspace_config
merged = merge(merged, project_config)
merged = merge(merged, agent_config)
return merged

Task 2: Agent Configuration Loader

File: pkg/config/agents.go

  • Create AgentConfig interface for loading agent-specific configuration
  • Implement LoadAgentConfig(storageDir string, agentName string) (*WorkspaceConfiguration, error)
  • Read from <storageDir>/config/agents.json
  • The storageDir parameter comes from the --storage flag or KORTEX_CLI_STORAGE environment variable
  • Return empty config (not error) if file doesn't exist
  • Return error if file exists but is invalid JSON
  • Use module design pattern (interface + unexported implementation)

Task 3: Project Configuration Loader

File: pkg/config/projects.go

  • Create ProjectConfig interface for loading project-specific configuration
  • Implement LoadProjectConfig(storageDir string, projectID string) (*WorkspaceConfiguration, error)
  • Read from <storageDir>/config/projects.json
  • The storageDir parameter comes from the --storage flag or KORTEX_CLI_STORAGE environment variable
  • Return empty config (not error) if file doesn't exist
  • Return error if file exists but is invalid JSON
  • Support exact match for project ID
  • Support global/default configuration: Load config from empty string "" key and merge with project-specific config
    • First load global config ("" key)
    • Then load project-specific config (exact match on projectID)
    • Merge them with project-specific taking precedence
    • Return the merged result
  • More specific paths take precedence (e.g., /home/user/project/subdir over github.com/user/repo)
  • Use module design pattern (interface + unexported implementation)

Phase 2: Integration

Task 4: Manager merges Configs

Files: pkg/instances/manager.go, pkg/cmd/init.go

Architecture Decision: Merging logic by the Manager. This provides:

  • Separation of concerns: Commands parse input, Manager handles instance lifecycle
  • Encapsulation: Config loaders are internal to Manager
  • Consistency: Manager already handles project detection
  • Reusability: Any command creating instances gets config merging automatically

Changes to pkg/instances/manager.go:

  1. Add Agent field to AddOptions struct
  2. Store storageDir in manager struct (needed for config loaders)
  3. Update Add() method to:
    • Load project config using project ID (auto-detected or custom)
    • Load agent config if Agent field is provided
    • Merge configs: workspace → project (global + specific) → agent
    • Pass merged config to runtime

Changes to pkg/cmd/init.go:

  1. Add --agent flag
  2. Load workspace config from .kortex/workspace.json (existing)
  3. Pass workspace config and agent name to manager.Add()

New AddOptions structure:

type AddOptions struct {
    Instance        Instance
    RuntimeType     string
    WorkspaceConfig *workspace.WorkspaceConfiguration  // From .kortex/workspace.json
    Project         string  // Optional custom project ID
    Agent           string  // Optional agent name
}

Command logic:

func (i *initCmd) run(cmd *cobra.Command, args []string) error {
    instance, err := instances.NewInstance(...)

    // Manager handles all config loading and merging
    addedInstance, err := i.manager.Add(cmd.Context(), instances.AddOptions{
        Instance:        instance,
        RuntimeType:     i.runtime,
        WorkspaceConfig: i.workspaceConfig,  // From .kortex/
        Project:         i.project,
        Agent:           i.agent,
    })

    return err
}

Phase 3: Testing

Task 5: Merger Tests

File: pkg/config/merger_test.go

Test scenarios:

  • Merging environment variables with duplicates (later overrides earlier)
  • Merging environment variables with value vs secret
  • Merging mount dependencies with deduplication
  • Merging mount configs with deduplication
  • Multi-level merge (3+ configs) with correct precedence
  • Edge cases: nil pointers, empty arrays, all-empty configs
  • Validation of merged result

Task 6: Agent Config Tests

File: pkg/config/agents_test.go

Test scenarios:

  • Loading valid agent config
  • Handling missing file (returns empty config)
  • Handling invalid JSON (returns error)
  • Handling unknown agent names (returns empty config)
  • Cross-platform path handling
  • Module design pattern enforcement

Task 7: Project Config Tests

File: pkg/config/projects_test.go

Test scenarios:

  • Loading valid project config
  • Handling missing file (returns empty config)
  • Handling invalid JSON (returns error)
  • Project ID exact matching
  • Global configuration (empty string "" key)
    • Loading global config when project ID doesn't match
    • Merging global config with project-specific config
    • Project-specific config overrides global config
  • Project ID prefix matching with precedence
  • Cross-platform path handling
  • Module design pattern enforcement

Task 8: Init Command Tests

File: pkg/cmd/init_test.go

Test scenarios:

  • Loading and merging all three config levels
  • Agent-specific config override
  • Project-specific config override
  • Precedence rules working correctly
  • Default behavior when optional configs don't exist
  • Integration with existing init command tests

Phase 4: Documentation

Task 9: Update Documentation

Files: CLAUDE.md, README.md

CLAUDE.md - Add new section: "Multi-Level Configuration System"

Document:

  • Overview of the three configuration levels
  • File locations and formats
  • Merging precedence rules
  • Example configurations for agents.json and projects.json
  • How to use the configuration loaders in code
  • Testing guidelines for configuration code

README.md - Add subsection to "Workspace Configuration" section

Document:

  • Multi-level configuration overview for end users
  • Configuration file locations and structure
  • Note that ~/.kortex-cli can be overridden with KORTEX_CLI_STORAGE environment variable or --storage flag
  • Precedence rules (agent-specific > project-specific > global > workspace-level)
  • Example configurations for agents.json and projects.json
  • Global project configuration: Document the empty string "" key in projects.json for settings that apply to all projects
  • Use cases for each configuration level
  • How configurations are merged during init

Design Decisions

File Storage Locations

  • Agent configs: ~/.kortex-cli/config/agents.json (default) or $KORTEX_CLI_STORAGE/config/agents.json (if environment variable is set)
  • Project configs: ~/.kortex-cli/config/projects.json (default) or $KORTEX_CLI_STORAGE/config/projects.json (if environment variable is set)
  • Workspace configs: .kortex/workspace.json (existing, stored with sources)

Note: The storage directory can be customized using the --storage flag or KORTEX_CLI_STORAGE environment variable (see README.md for details). Agent and project configurations are stored under <storage-dir>/config/.

Precedence Order (highest to lowest)

  1. Agent-specific configuration
  2. Project-specific configuration (from projects.json using exact project ID match)
  3. Global project configuration (from projects.json using empty string "" key)
  4. Workspace-level configuration (from .kortex/workspace.json)

Project ID Matching

  • Uses existing project detection logic from pkg/instances/manager.go
  • Git repository with remote: Uses repository URL + relative path
  • Git repository without remote: Uses repository root + relative path
  • Non-git directory: Uses source directory path
  • More specific paths take precedence over less specific ones
  • Global/default configuration: Empty string "" key in projects.json applies to all projects
    • Always loaded first (before project-specific config)
    • Useful for common settings (e.g., GitHub credentials, SSH keys, global env vars)

Merging Behavior

Environment Variables:

  • Keyed by name field
  • Later configs override earlier configs with the same name
  • Preserves order of appearance (first occurrence position)

Mount Paths:

  • Deduplicated by value
  • Preserves order (first occurrence)
  • No special precedence - just removes duplicates

Error Handling

  • Missing config files return empty configs (not errors)
  • Invalid JSON in config files returns errors
  • Validation errors in configs return errors
  • Follow existing error handling patterns from pkg/config/config.go

Cross-Platform Compatibility

  • Use filepath.Join() for all path operations
  • Use t.TempDir() in tests (never hardcode paths)
  • Test on Linux, macOS, and Windows CI

Testing Strategy

  1. Unit tests for each component (merger, loaders)
  2. Integration tests for init command with multi-level configs
  3. E2E tests verifying full workflow including persistence
  4. Cross-platform tests on all supported platforms
  5. Example-based tests using realistic configuration scenarios

Success Criteria

  • Users can define agent-specific configurations
  • Users can define project-specific configurations
  • Configurations merge with correct precedence
  • Missing config files don't break existing functionality
  • All tests pass on Linux, macOS, and Windows
  • Documentation is complete and accurate
  • Code follows existing patterns and conventions
  • No breaking changes to existing API

Future Enhancements

Potential future work (not in scope for this issue):

  • CLI commands to manage agent/project configs (config set, config get)
  • Configuration validation command
  • Support for environment-specific configs (dev/staging/prod)
  • Configuration inheritance/templates
  • Schema validation for config files

References

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Mar 24, 2026

Codecov Report

❌ Patch coverage is 85.79235% with 26 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/config/merger.go 84.26% 11 Missing and 3 partials ⚠️
pkg/instances/manager.go 75.00% 3 Missing and 3 partials ⚠️
pkg/config/agents.go 89.28% 2 Missing and 1 partial ⚠️
pkg/config/projects.go 92.10% 2 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

📝 Walkthrough

Walkthrough

This PR implements a multi-level workspace configuration system allowing users to define configurations at four precedence layers: workspace-level (.kortex/workspace.json), global project configuration, per-project configuration, and per-agent configuration. The system merges these layers with explicit precedence rules (agent > project > global > workspace) and is integrated into the instance manager and init command.

Changes

Cohort / File(s) Summary
Documentation
AGENTS.md, README.md
Added comprehensive documentation describing the multi-level configuration system, merge precedence rules, configuration scope definitions, JSON schema examples, and CLI usage patterns. Documentation is read-only without code changes.
Configuration Loaders
pkg/config/agents.go, pkg/config/agents_test.go, pkg/config/projects.go, pkg/config/projects_test.go
Introduced two new configuration loaders: AgentConfigLoader for agent-scoped configurations from config/agents.json, and ProjectConfigLoader for project-scoped configurations from config/projects.json. Both handle missing files gracefully, parse JSON, validate configurations, and include comprehensive test coverage with error scenarios.
Configuration Merger
pkg/config/merger.go, pkg/config/merger_test.go
Added Merger interface and implementation to combine configurations with explicit precedence rules: environment variables merge by name (later overrides earlier), and mount dependencies/configs deduplicate while preserving order. Extensive test suite covers nil handling, override precedence, multi-level merging, and edge cases.
CLI Integration
pkg/cmd/init.go, pkg/cmd/init_test.go
Extended init command with --agent/-a flag to capture agent name and pass it to instance manager via AddOptions.Agent. Added E2E test validating multi-level config resolution across workspace, project, global, and agent scopes, plus edge cases when configs are absent.
Manager Integration
pkg/instances/manager.go, pkg/instances/manager_test.go
Enhanced instance manager to load and merge configurations from all layers: added Agent field to AddOptions, integrated loaders into (*manager).mergeConfigurations() method, and updated Add to resolve and apply merged configuration. Test suite validates merging behavior, precedence rules, error propagation, and nil handling.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI/init
    participant Manager as Manager
    participant PLoader as ProjectConfigLoader
    participant ALoader as AgentConfigLoader
    participant Merger as Merger
    participant Runtime as Runtime

    CLI->>Manager: Add(opts: Agent, Project, WorkspaceConfig)
    Manager->>Manager: mergeConfigurations(project, workspace, agent)
    Manager->>PLoader: Load(projectID)
    PLoader->>PLoader: Read projects.json
    PLoader-->>Manager: project config
    Manager->>ALoader: Load(agentName)
    ALoader->>ALoader: Read agents.json
    ALoader-->>Manager: agent config
    Manager->>Merger: Merge(workspace, project config)
    Merger-->>Manager: merged config
    Manager->>Merger: Merge(merged, agent config)
    Merger-->>Manager: final config
    Manager->>Runtime: CreateParams.WorkspaceConfig = final
    Manager-->>CLI: instance registered
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #19 — Introduces the instance manager infrastructure that this PR extends with multi-level configuration loading and merging logic.
  • PR #80 — Modifies the workspace-configuration flow and manager Add callsite, overlapping with the configuration resolution changes in this PR.
  • PR #91 — Adds project detection/identification that this PR leverages when loading per-project configurations in the merge flow.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.11% 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 title 'feat(config): add multi-level workspace configuration system' directly and accurately summarizes the main change: implementation of a multi-level configuration system for workspace settings.
Description check ✅ Passed The description comprehensively covers the implementation of multi-level configuration with workspace, project, and agent levels, merging strategy, file formats, and integration details related to the PR changes.
Linked Issues check ✅ Passed The PR successfully implements all requirements from issue #88: per-agent configuration in agents.json, per-project configuration in projects.json with global empty-string key support, storage under kortex-cli config directory, and Manager-coordinated merging with proper precedence.
Out of Scope Changes check ✅ Passed All code changes are directly related to implementing the multi-level configuration system. Documentation updates (AGENTS.md, README.md) and CLI flag additions support the core feature. No unrelated changes detected.

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


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: 1

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

Inline comments:
In `@docs/plans/88.md`:
- Around line 113-118: The fenced code block containing the pseudocode lines
starting with "merged = workspace_config" should include a language tag to
satisfy markdownlint MD040; update the opening fence from ``` to ```text (or
another appropriate language identifier) so the block reads like ```text
followed by the existing lines and the closing ```.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c55854ee-d47f-4a78-ac18-bddbbeb00041

📥 Commits

Reviewing files that changed from the base of the PR and between a5d4c31 and e9fa571.

📒 Files selected for processing (13)
  • AGENTS.md
  • README.md
  • docs/plans/88.md
  • pkg/cmd/init.go
  • pkg/cmd/init_test.go
  • pkg/config/agents.go
  • pkg/config/agents_test.go
  • pkg/config/merger.go
  • pkg/config/merger_test.go
  • pkg/config/projects.go
  • pkg/config/projects_test.go
  • pkg/instances/manager.go
  • pkg/instances/manager_test.go
👮 Files not reviewed due to content moderation or server errors (8)
  • pkg/cmd/init.go
  • pkg/cmd/init_test.go
  • README.md
  • pkg/config/merger.go
  • pkg/instances/manager.go
  • pkg/instances/manager_test.go
  • pkg/config/merger_test.go
  • pkg/config/projects_test.go

@feloy feloy requested review from benoitf and jeffmaury March 24, 2026 09:59
@benoitf
Copy link
Copy Markdown
Contributor

benoitf commented Mar 25, 2026

hello, I've a question about the empty string and the configuration

I think I'm lost on how to configure it

if I see

{
  "claude": {
    "environment": [
      {
        "name": "DEBUG",
        "value": "true"
      }
    ],
    "mounts": {
      "dependencies": ["../shared-libs"],
      "configs": [".claude-config"]
    }
  },
  "goose": {
    "environment": [
      {
        "name": "GOOSE_MODE",
        "value": "verbose"
      }
    ]
  }
}
    "mounts": {
      "dependencies": ["../shared-libs"],

I don't understand where is that relative path (I don't think we should use relative path as you never know where it is)

I don't see the difference between a dependencies or a configs mounts
AFAIK I want to mount paths, the nature of 'is it a dependency' or a 'config', I'm not sure what the difference is

but it seems unrelated to this PR allowing to have multi-level but I think I'm lost

@feloy
Copy link
Copy Markdown
Contributor Author

feloy commented Mar 25, 2026

hello, I've a question about the empty string and the configuration

I think I'm lost on how to configure it

if I see

{
  "claude": {
    "environment": [
      {
        "name": "DEBUG",
        "value": "true"
      }
    ],
    "mounts": {
      "dependencies": ["../shared-libs"],
      "configs": [".claude-config"]
    }
  },
  "goose": {
    "environment": [
      {
        "name": "GOOSE_MODE",
        "value": "verbose"
      }
    ]
  }
}
    "mounts": {
      "dependencies": ["../shared-libs"],

I don't understand where is that relative path (I don't think we should use relative path as you never know where it is)

I don't see the difference between a dependencies or a configs mounts AFAIK I want to mount paths, the nature of 'is it a dependency' or a 'config', I'm not sure what the difference is

but it seems unrelated to this PR allowing to have multi-level but I think I'm lost

The dependencies are relative to the sources directory. Its main usage is for the worktree support, which uses some relative path to access the real .git directory:

% cat .git
gitdir: ../main/.git/worktrees/output-stdout

The configs are relative to the home directory, so they can be specified for any user.

I tried to document it here: https://github.com/kortex-hub/kortex-cli#mount-paths. Please tell me if it is not clear enough, I can add some more details

@benoitf
Copy link
Copy Markdown
Contributor

benoitf commented Mar 25, 2026

I think it's adding a layer of complexity and you're not free to mount things where you want

I could want to mount my $HOME/kortex-config to $HOME/.config or a folder /tmp/foo to /tmp/bar inside the VM

when I use docker/podman/compose (all things mounting volumes)

I say the source and the dest and the type (rw or ro)

so I would expect something where I say the folder that I have locally and where I want it inside my agent

but there is no 'dependency' or 'config' meaning.

Copy link
Copy Markdown
Contributor

@benoitf benoitf left a comment

Choose a reason for hiding this comment

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

this PR is unrelated to the mount definition, so it should not be hold

@feloy
Copy link
Copy Markdown
Contributor Author

feloy commented Mar 25, 2026

I think it's adding a layer of complexity and you're not free to mount things where you want

I could want to mount my $HOME/kortex-config to $HOME/.config or a folder /tmp/foo to /tmp/bar inside the VM

when I use docker/podman/compose (all things mounting volumes)

I say the source and the dest and the type (rw or ro)

so I would expect something where I say the folder that I have locally and where I want it inside my agent

but there is no 'dependency' or 'config' meaning.

OK, I get your point. This will need to introduce two special variables $HOME and $SOURCES, for the user to be able to indicate that it wants to mount directories relative to these points (which are not known by the user, and probably different from one runtime to another). And in this case, we can remove the configs/dependencies and have only a mounts: []string

feloy and others added 2 commits March 25, 2026 11:58
Implements a three-level configuration system allowing users to
customize workspace settings at different contexts with automatic
merging and proper precedence:

- Workspace config (.kortex/workspace.json) - shared, committed
- Project config (~/.kortex-cli/config/projects.json) - user-specific
- Agent config (~/.kortex-cli/config/agents.json) - per-agent overrides

Configuration merging is handled by Manager for better separation of
concerns. Added global project config support via empty string key.

Closes kortex-hub#88

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>
Signed-off-by: Philippe Martin <phmartin@redhat.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.

🧹 Nitpick comments (2)
pkg/instances/manager.go (1)

563-571: Consider caching config loaders for efficiency.

New ProjectConfigLoader and AgentConfigLoader instances are created on each Add() call. While acceptable for CLI usage patterns, caching these loaders in the manager struct would avoid repeated allocations if the manager is reused.

💡 Optional: Cache loaders in manager struct
 type manager struct {
 	storageFile     string
 	storageDir      string
 	mu              sync.RWMutex
 	factory         InstanceFactory
 	generator       generator.Generator
 	runtimeRegistry runtime.Registry
 	gitDetector     git.Detector
+	projectLoader   config.ProjectConfigLoader
+	agentLoader     config.AgentConfigLoader
 }

Then initialize in newManagerWithFactory and reuse in mergeConfigurations.

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

In `@pkg/instances/manager.go` around lines 563 - 571, The Add() path recreates
ProjectConfigLoader and AgentConfigLoader on each call (see
NewProjectConfigLoader/NewAgentConfigLoader usage in mergeConfigurations),
causing repeated allocations; modify the manager struct to hold cached loader
instances (e.g., projectLoader and agentLoader), initialize them in
newManagerWithFactory using m.storageDir, and then reuse those fields inside
mergeConfigurations/Add() instead of calling
config.NewProjectConfigLoader/config.NewAgentConfigLoader on every invocation.
pkg/config/projects.go (1)

121-129: Potential issue: Returning pointer to local map value.

Lines 123 and 128 return pointers to values stored in the local projectsConfig map (&projectCfg, &globalCfg). While this works in Go (the map values are copied when extracted), it's clearer and safer to explicitly copy the configuration. The Merger.Merge method on line 132 already handles this correctly by returning a new object.

Consider using copyConfig (from merger.go) for consistency, or this pattern is acceptable since the map values are already copies.

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

In `@pkg/config/projects.go` around lines 121 - 129, The function currently
returns pointers to locally scoped map-extracted values (&projectCfg and
&globalCfg), which can be confusing; change those returns to return explicit
copies using the existing copy helper (copyConfig from merger.go) or the same
copy logic Merger.Merge uses so callers receive a fresh object (e.g., replace
returning &projectCfg and &globalCfg with copyConfig(projectCfg) /
copyConfig(globalCfg) or equivalent) and keep Merger.Merge usage unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/config/projects.go`:
- Around line 121-129: The function currently returns pointers to locally scoped
map-extracted values (&projectCfg and &globalCfg), which can be confusing;
change those returns to return explicit copies using the existing copy helper
(copyConfig from merger.go) or the same copy logic Merger.Merge uses so callers
receive a fresh object (e.g., replace returning &projectCfg and &globalCfg with
copyConfig(projectCfg) / copyConfig(globalCfg) or equivalent) and keep
Merger.Merge usage unchanged.

In `@pkg/instances/manager.go`:
- Around line 563-571: The Add() path recreates ProjectConfigLoader and
AgentConfigLoader on each call (see NewProjectConfigLoader/NewAgentConfigLoader
usage in mergeConfigurations), causing repeated allocations; modify the manager
struct to hold cached loader instances (e.g., projectLoader and agentLoader),
initialize them in newManagerWithFactory using m.storageDir, and then reuse
those fields inside mergeConfigurations/Add() instead of calling
config.NewProjectConfigLoader/config.NewAgentConfigLoader on every invocation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2897accb-8314-4174-8546-29529aea44e0

📥 Commits

Reviewing files that changed from the base of the PR and between e9fa571 and fb70639.

📒 Files selected for processing (12)
  • AGENTS.md
  • README.md
  • pkg/cmd/init.go
  • pkg/cmd/init_test.go
  • pkg/config/agents.go
  • pkg/config/agents_test.go
  • pkg/config/merger.go
  • pkg/config/merger_test.go
  • pkg/config/projects.go
  • pkg/config/projects_test.go
  • pkg/instances/manager.go
  • pkg/instances/manager_test.go
✅ Files skipped from review due to trivial changes (5)
  • pkg/cmd/init.go
  • pkg/cmd/init_test.go
  • README.md
  • AGENTS.md
  • pkg/config/merger_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/instances/manager_test.go
  • pkg/config/merger.go
  • pkg/config/agents.go
  • pkg/config/agents_test.go

@feloy feloy merged commit b1374ab into kortex-hub:main Mar 25, 2026
6 checks passed
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.

workspace configuration at different levels

3 participants