Skip to content

feat: init command#23

Merged
feloy merged 5 commits intokortex-hub:mainfrom
feloy:cmd-init
Mar 6, 2026
Merged

feat: init command#23
feloy merged 5 commits intokortex-hub:mainfrom
feloy:cmd-init

Conversation

@feloy
Copy link
Contributor

@feloy feloy commented Mar 6, 2026

related to #18

Does not cover the --name flag yet, and only covers the registration and storage of the workspace (no agent is started).

When new workspaces are registered, they are stored in the storage file (~/.kortex-cli/instances.json by default).

Example:

$ rm -rf ~/.kortex-cli

$ pwd
/Users/phmartin/Documents/gits/kortex-cli/cmd-init

$ ./kortex-cli init --verbose
Registered workspace:
  ID: d12898a033e0df1a0ab3ae14355627006f59016a9c089d474d795bb7f43d62a5
  Sources directory: /Users/phmartin/Documents/gits/kortex-cli/cmd-init
  Configuration directory: /Users/phmartin/Documents/gits/kortex-cli/cmd-init/.kortex

$ cat ~/.kortex-cli/instances.json 
[
  {
    "id": "a55bd85c6430656be5f656feda9771477f75d4d92c3a051f9fa4bba12e91c181",
    "paths": {
      "source": "/Users/phmartin/Documents/gits/kortex-cli/cmd-init",
      "configuration": "/Users/phmartin/Documents/gits/kortex-cli/cmd-init/.kortex"
    }
  }
]

# Add another workspace (not verbose)
./kortex-cli init
a6f998a033e0df1a0ab3ae14355627006f59016a9c089d474d795bb7f43d62a5

Signed-off-by: Philippe Martin <phmartin@redhat.com>

Co-Authored-By: Claude Code (Claude Sonnet 4.5) <noreply@anthropic.com>
@feloy feloy requested a review from benoitf March 6, 2026 13:16
@coderabbitai
Copy link

coderabbitai bot commented Mar 6, 2026

📝 Walkthrough

Walkthrough

Adds an init CLI subcommand that resolves absolute source/config paths, initializes an instances.Manager from storage, registers a workspace instance, and prints its metadata. Adds extensive documentation in AGENTS.md and updates instances serialization to nest paths under Paths.

Changes

Cohort / File(s) Summary
Documentation
AGENTS.md
Added extensive sections: JSON storage structure, Command Implementation Pattern, Testing Pattern for Commands, Instances Manager usage, and cross-platform path handling.
Init command implementation
pkg/cmd/init.go
New init subcommand: initCmd type with preRun (manager init, resolve sources/config, compute absolute paths) and run (create/register instance, print ID and paths); NewInitCmd() constructor and --workspace-configuration/--verbose flags.
Init command tests
pkg/cmd/init_test.go
New unit tests for preRun (various arg/flag permutations) and E2E tests exercising CLI registration, absolute path resolution, verbose output, and multi-workspace scenarios.
Root command registration
pkg/cmd/root.go
Registered NewInitCmd() as a new subcommand on the root command.
Instances serialization
pkg/instances/instance.go, pkg/instances/manager_test.go
Public serialization changed: InstanceData now nests paths under Paths (InstancePaths{Source, Configuration}); code/tests updated to use Paths.Source/Paths.Configuration.
Module file
go.mod
Minor single-line update.

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as Init Command
    participant Manager as Instances Manager
    participant Storage as Storage

    User->>CLI: run init [sources-dir] [--workspace-configuration=<dir>] [--verbose]
    CLI->>CLI: preRun: parse args & flags, resolve paths to absolute
    CLI->>Storage: open storage (default or --storage)
    Storage-->>Manager: provide manager instance
    CLI->>Manager: Create NewInstance(absSource, absConfig)
    Manager->>Storage: persist instance metadata (ID, paths)
    Manager-->>CLI: return created instance (ID, paths)
    CLI->>User: print "Registered workspace:" (ID, sources, config) or verbose metadata
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: init command' directly and clearly describes the main change: adding a new init command to the CLI.
Description check ✅ Passed The description is directly related to the changeset, explaining the init command implementation, its behavior, and providing concrete examples.

✏️ 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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
AGENTS.md (2)

239-255: Fix variable shadowing and redeclaration in code examples.

The code examples have minor issues that could confuse readers:

  • Line 246: instance shadows the variable declared on line 229
  • Line 252: err := redeclares err which was already declared on line 241
Suggested fixes
 // Get specific instance
-instance, err := manager.Get(id)
+inst, err := manager.Get(id)
 if err != nil {
     return fmt.Errorf("instance not found: %w", err)
 }

 // Delete instance
-err := manager.Delete(id)
+err = manager.Delete(id)
 if err != nil {
     return fmt.Errorf("failed to delete instance: %w", err)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 239 - 255, The example has variable
shadowing/redeclaration: reuse the previously declared err and avoid shadowing
instance—change the `instance` declaration from a new declaration to an
assignment (or rename the earlier variable) when calling `manager.Get(id)`, and
change the `err :=` before `manager.Delete(id)` to `err =` so you don't
redeclare `err` (references: instancesList, manager.List, instance, manager.Get,
err, manager.Delete).

196-198: Reference could be more accurate.

Line 198 references pkg/instances/instance_test.go for command testing patterns, but pkg/cmd/init_test.go (mentioned on line 214) is a better reference since it demonstrates the actual command testing pattern with preRun unit tests and E2E tests.

Suggested change
-Commands should have two types of tests following the pattern in `pkg/instances/instance_test.go`:
+Commands should have two types of tests:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 196 - 198, Update the reference in AGENTS.md: replace
the mention of pkg/instances/instance_test.go with pkg/cmd/init_test.go as the
canonical example for command testing patterns; specifically call out the use of
preRun unit tests and E2E tests shown in init_test.go so readers are directed to
the correct test examples.
pkg/cmd/init.go (1)

40-42: Consider handling the error from GetString.

Silently discarding the error with _ could mask issues if the flag is misconfigured. While Cobra typically ensures the flag exists when using persistent flags, explicitly handling the error provides better diagnostics.

Proposed fix
-	storageDir, _ := cmd.Flags().GetString("storage")
+	storageDir, err := cmd.Flags().GetString("storage")
+	if err != nil {
+		return fmt.Errorf("failed to get storage flag: %w", err)
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/cmd/init.go` around lines 40 - 42, The call to
cmd.Flags().GetString("storage") in init.go currently ignores the error return
(storageDir, _), which can hide flag retrieval problems; change it to capture
the error (e.g., storageDir, err := cmd.Flags().GetString("storage")) and handle
it by returning or logging a descriptive error (for example return
fmt.Errorf("failed to read --storage flag: %w", err) or use the command's
Print/Logger) before proceeding, so any misconfigured or missing flag is
surfaced; keep the variable name storageDir and the GetString call intact when
implementing the check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/cmd/init.go`:
- Around line 1-119: The file fails gofmt style checks; run the formatter (e.g.
make fmt or go fmt ./...) to fix formatting issues in pkg/cmd/init.go (affects
symbols InitCmd, preRun, run, NewInitCmd); commit the reformatted file so the CI
formatting check passes.

---

Nitpick comments:
In `@AGENTS.md`:
- Around line 239-255: The example has variable shadowing/redeclaration: reuse
the previously declared err and avoid shadowing instance—change the `instance`
declaration from a new declaration to an assignment (or rename the earlier
variable) when calling `manager.Get(id)`, and change the `err :=` before
`manager.Delete(id)` to `err =` so you don't redeclare `err` (references:
instancesList, manager.List, instance, manager.Get, err, manager.Delete).
- Around line 196-198: Update the reference in AGENTS.md: replace the mention of
pkg/instances/instance_test.go with pkg/cmd/init_test.go as the canonical
example for command testing patterns; specifically call out the use of preRun
unit tests and E2E tests shown in init_test.go so readers are directed to the
correct test examples.

In `@pkg/cmd/init.go`:
- Around line 40-42: The call to cmd.Flags().GetString("storage") in init.go
currently ignores the error return (storageDir, _), which can hide flag
retrieval problems; change it to capture the error (e.g., storageDir, err :=
cmd.Flags().GetString("storage")) and handle it by returning or logging a
descriptive error (for example return fmt.Errorf("failed to read --storage flag:
%w", err) or use the command's Print/Logger) before proceeding, so any
misconfigured or missing flag is surfaced; keep the variable name storageDir and
the GetString call intact when implementing the check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 91dfada7-f98b-469c-be8e-3aa9e15ac841

📥 Commits

Reviewing files that changed from the base of the PR and between b3f9350 and 69c1804.

📒 Files selected for processing (4)
  • AGENTS.md
  • pkg/cmd/init.go
  • pkg/cmd/init_test.go
  • pkg/cmd/root.go

Signed-off-by: Philippe Martin <phmartin@redhat.com>

Co-Authored-By: Claude Code (Claude Sonnet 4.5) <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
pkg/cmd/init.go (1)

30-35: Inject factories so the command can be unit-tested with fakes.

preRun and run are hard-wired to instances.NewManager and instances.NewInstance, which is why the "unit" tests still hit the real filesystem. Adding unexported factory fields on the command state would let the tests use fake implementations and keep storage side effects out of the fast path.

💡 Refactor sketch
 type InitCmd struct {
 	sourcesDir         string
 	workspaceConfigDir string
 	absSourcesDir      string
 	absConfigDir       string
 	manager            instances.Manager
+	newManager         func(string) (instances.Manager, error)
+	newInstance        func(string, string) (instances.Instance, error)
 }
@@
-	manager, err := instances.NewManager(storageDir)
+	manager, err := i.newManager(storageDir)
@@
-	instance, err := instances.NewInstance(i.absSourcesDir, i.absConfigDir)
+	instance, err := i.newInstance(i.absSourcesDir, i.absConfigDir)
@@
 func NewInitCmd() *cobra.Command {
-	initCmd := &InitCmd{}
+	initCmd := &InitCmd{
+		newManager:  instances.NewManager,
+		newInstance: instances.NewInstance,
+	}

As per coding guidelines, "When testing code that uses interfaces, use fake implementations instead of real implementations or mocks" and "Create unexported fake structs that implement interfaces for testing purposes."

Also applies to: 43-45, 81-83

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

In `@pkg/cmd/init.go` around lines 30 - 35, The InitCmd struct hard-wires
instances.NewManager and instances.NewInstance in preRun/run which forces tests
to touch the real filesystem; add unexported factory fields on InitCmd (e.g.,
newManagerFn func(...) instances.Manager and newInstanceFn func(...)
instances.Instance or similarly named constructors) and use those factories
inside preRun and run instead of directly calling instances.NewManager and
instances.NewInstance, defaulting the factories to the real constructors in the
command setup so production behavior is unchanged but unit tests can inject fake
implementations via those factory fields to avoid storage side effects; update
usages of the manager field and any call sites in preRun/run to use the injected
factories.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/cmd/init_test.go`:
- Around line 276-280: The test currently logs a wrong slice length with
t.Errorf and then immediately indexes instancesList (e.g., inst :=
instancesList[0]), which can panic and mask the real failure; change the
assertion to fail fast by replacing the t.Errorf length checks with t.Fatalf (or
call t.FailNow()/return immediately) before any indexing so the test stops and
reports the assertion instead of panicking—apply this change for the
instancesList checks shown and the similar block around lines 554-564.

In `@pkg/cmd/init.go`:
- Around line 29-36: Rename the exported struct InitCmd to an unexported initCmd
and update all references (including the constructor NewInitCmd, any tests, and
usages) to use the new unexported type; ensure NewInitCmd continues to be
exported and returns the same concrete pointer type (now *initCmd) or an
appropriate exported interface if already used elsewhere, and update field names
and method receivers from InitCmd to initCmd so the command internals remain
unexported while the factory function stays public.
- Around line 39-46: In preRun, handle the error returned by
cmd.Flags().GetString("storage") instead of ignoring it: check the err from
GetString and return a wrapped fmt.Errorf if non-nil before calling
instances.NewManager; reference the preRun function and the GetString call so
the flag error is validated prior to invoking instances.NewManager.

---

Nitpick comments:
In `@pkg/cmd/init.go`:
- Around line 30-35: The InitCmd struct hard-wires instances.NewManager and
instances.NewInstance in preRun/run which forces tests to touch the real
filesystem; add unexported factory fields on InitCmd (e.g., newManagerFn
func(...) instances.Manager and newInstanceFn func(...) instances.Instance or
similarly named constructors) and use those factories inside preRun and run
instead of directly calling instances.NewManager and instances.NewInstance,
defaulting the factories to the real constructors in the command setup so
production behavior is unchanged but unit tests can inject fake implementations
via those factory fields to avoid storage side effects; update usages of the
manager field and any call sites in preRun/run to use the injected factories.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ea94c810-650a-47ed-93b6-b0cab48bf3c4

📥 Commits

Reviewing files that changed from the base of the PR and between 69c1804 and 3ea0e2b.

📒 Files selected for processing (4)
  • AGENTS.md
  • pkg/cmd/init.go
  • pkg/cmd/init_test.go
  • pkg/cmd/root.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/cmd/root.go

@benoitf
Copy link
Contributor

benoitf commented Mar 6, 2026

question: should the output be by default only the id in case you want to automate/reuse cli

for now I see

 ./kortex-cli init                                                                                                                                                                                                                  Registered workspace:
  ID: dc610bffa75f21b5b043f98aff12b157fb16fae6c0ac3139c28f85d6defbe017
  Sources directory: /Users/benoitf/git/kortex/kortex-cli
  Configuration directory: /Users/benoitf/git/kortex/kortex-cli/.kortex

I would expect

 ./kortex-cli init                                                                                                                                                                                                                  dc610bffa75f21b5b043f98aff12b157fb16fae6c0ac3139c28f85d6defbe017

verbose mode or JSON output would display your content ?

and question around the internal file, why using _ ?

to have best of world or JSON files

I see you're using snake_case but it's not straightforward in JS/TS world

so could we have a sub-entry

[
  {
    "id": "dc610bffa75f21b5b043f98aff12b157fb16fae6c0ac3139c28f85d6defbe017",
    "source_dir": "/Users/benoitf/git/kortex/kortex-cli",
    "config_dir": "/Users/benoitf/git/kortex/kortex-cli/.kortex"
  }
]

-->

[
  {
    "id": "dc610bffa75f21b5b043f98aff12b157fb16fae6c0ac3139c28f85d6defbe017",
    "paths": {
       "source": "/Users/benoitf/git/kortex/kortex-cli",
       "configuration": "/Users/benoitf/git/kortex/kortex-cli/.kortex"
     }
  }
]

so it's neither camelCase or snake_case only lowercase ?

feloy and others added 3 commits March 6, 2026 14:31
Signed-off-by: Philippe Martin <phmartin@redhat.com>

Co-Authored-By: Claude Code (Claude Sonnet 4.5) <noreply@anthropic.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>

Co-Authored-By: Claude Code (Claude Sonnet 4.5) <noreply@anthropic.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>

Co-Authored-By: Claude Code (Claude Sonnet 4.5) <noreply@anthropic.com>
Copy link
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.

tried different cli options and it was working as expected

(default storage, custom storage) etc

Copy link

@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: 2

Caution

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

⚠️ Outside diff range comments (1)
pkg/instances/instance.go (1)

149-163: ⚠️ Potential issue | 🟠 Major

Add migration path for legacy storage schema or risk breaking existing installations.

Recent changes nested path fields under Paths (paths.source, paths.configuration), but manager.go line 247 uses standard json.Unmarshal with no fallback. Any existing instances.json with flat-level source_dir/config_dir fields will fail to unmarshal, crashing on startup. Add either:

  • A custom UnmarshalJSON on InstanceData to handle both old and new formats, or
  • A migration step before loading, or
  • A regression test that loads the old schema to catch this in future
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/instances/instance.go` around lines 149 - 163, The code now expects
nested path fields under Paths which will break older JSON with flat fields like
source_dir/config_dir; implement a custom UnmarshalJSON on the InstanceData type
to accept both schemas: try decoding into the new structure with Paths, and if
missing, fall back to reading flat fields (e.g., source_dir, config_dir) and
populate Paths.Source and Paths.Configuration accordingly; ensure
InstanceData.UnmarshalJSON sets all related fields used by NewInstanceFromData
and add a regression test that unmarshals an old-format JSON to prevent future
breaks.
🧹 Nitpick comments (1)
pkg/instances/manager_test.go (1)

813-833: Assert the wire format, not just the typed values.

This test unmarshals into InstanceData, so it would still pass if the file accidentally included legacy source_dir / config_dir fields in addition to paths. Since this PR changes the on-disk schema, add a raw JSON assertion that paths.source and paths.configuration exist and the flat keys do not.

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

In `@pkg/instances/manager_test.go` around lines 813 - 833, The test currently
only unmarshals into InstanceData and checks typed fields (instances,
InstanceData, generatedID, expectedSource, expectedConfig); change the test to
also unmarshal the raw JSON (e.g. into []map[string]interface{} or
[]json.RawMessage) and assert that each item contains a "paths" object with
"source" and "configuration" keys present and that legacy flat keys "source_dir"
and "config_dir" are absent; keep the existing typed assertions but add these
raw JSON checks to verify the wire format rather than only the typed values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@AGENTS.md`:
- Around line 176-186: The example exposes concrete DTO structs InstancePaths
and InstanceData in pkg/, which conflicts with the module rule that public types
must be interfaces; either document a DTO exception or change the example to
comply. Update AGENTS.md to explicitly state that simple data-transfer structs
(DTOs) such as InstancePaths and InstanceData are exempt from the interface-only
rule (and describe the exemption criteria), or replace the example with an
interface-based pattern and mention factory functions and unexported
implementations to align with the "pkg/ MUST follow interface-based design"
guidance.

In `@pkg/cmd/init.go`:
- Around line 54-79: The code currently accepts and stores a normalized sources
path (i.sourcesDir / absSourcesDir) without verifying it exists or is a
directory; update the preRun/init logic where i.sourcesDir and absSourcesDir are
set to stat the resolved path (use os.Stat) and fail fast with a clear error if
the path does not exist or is not a directory before assigning i.absSourcesDir
(and before defaulting workspaceConfigDir); reference i.sourcesDir,
absSourcesDir, i.absSourcesDir and instances.NewInstance() so the guard prevents
registering broken workspace entries.

---

Outside diff comments:
In `@pkg/instances/instance.go`:
- Around line 149-163: The code now expects nested path fields under Paths which
will break older JSON with flat fields like source_dir/config_dir; implement a
custom UnmarshalJSON on the InstanceData type to accept both schemas: try
decoding into the new structure with Paths, and if missing, fall back to reading
flat fields (e.g., source_dir, config_dir) and populate Paths.Source and
Paths.Configuration accordingly; ensure InstanceData.UnmarshalJSON sets all
related fields used by NewInstanceFromData and add a regression test that
unmarshals an old-format JSON to prevent future breaks.

---

Nitpick comments:
In `@pkg/instances/manager_test.go`:
- Around line 813-833: The test currently only unmarshals into InstanceData and
checks typed fields (instances, InstanceData, generatedID, expectedSource,
expectedConfig); change the test to also unmarshal the raw JSON (e.g. into
[]map[string]interface{} or []json.RawMessage) and assert that each item
contains a "paths" object with "source" and "configuration" keys present and
that legacy flat keys "source_dir" and "config_dir" are absent; keep the
existing typed assertions but add these raw JSON checks to verify the wire
format rather than only the typed values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a03a6223-aea8-4582-b94e-9f7ae9a24f10

📥 Commits

Reviewing files that changed from the base of the PR and between 3ea0e2b and 9dc470e.

📒 Files selected for processing (5)
  • AGENTS.md
  • pkg/cmd/init.go
  • pkg/cmd/init_test.go
  • pkg/instances/instance.go
  • pkg/instances/manager_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/cmd/init_test.go

@feloy feloy merged commit b20b222 into kortex-hub:main Mar 6, 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.

2 participants