원클릭으로
working-with-instances-manager
Guide to using the instances manager API for workspace management and project detection
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Guide to using the instances manager API for workspace management and project detection
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Add a new runtime implementation to the kdn runtime system
Guide to understanding and working with the kdn runtime system architecture
Guide to configuring the Podman runtime including image setup, agent configuration, and containerfile generation
Conventional Commit Message Generator
Guide to the autoconf package — how secret detection, filtering, and the runner are wired together, and how to extend the system with new detector types
Guide to workspace configuration for environment variables, mount points, skills, MCP servers, secrets and network access at multiple levels
| name | working-with-instances-manager |
| description | Guide to using the instances manager API for workspace management and project detection |
| argument-hint |
The instances manager provides the API for managing workspace instances throughout their lifecycle. This skill covers the manager API and project detection functionality.
The instances manager handles:
In command preRun, create the manager from the storage flag:
storageDir, _ := cmd.Flags().GetString("storage")
manager, err := instances.NewManager(storageDir)
if err != nil {
return fmt.Errorf("failed to create manager: %w", err)
}
Add a new workspace instance to the manager:
instance, err := instances.NewInstance(instances.NewInstanceParams{
SourceDir: sourceDir,
ConfigDir: configDir,
Name: name, // Optional: user-provided name, sanitized by manager
})
if err != nil {
return fmt.Errorf("failed to create instance: %w", err)
}
addedInstance, err := manager.Add(ctx, instances.AddOptions{
Instance: instance,
RuntimeType: "fake",
WorkspaceConfig: workspaceConfig, // From .kaiden/workspace.json
Project: "custom-project", // Optional: overrides auto-detection
Agent: "claude", // Optional: agent name for agent-specific config
Model: "claude-sonnet-4", // Optional: model ID for agent (takes precedence over settings)
})
if err != nil {
return fmt.Errorf("failed to add instance: %w", err)
}
The Add() method:
Name is empty, it is derived from the source directory basename-2, -3, …) is appended if the name is already in use"" + project-specific merged)<storage-dir>/config/<agent>/ into map[string]agent.SettingsFileSkipOnboarding() method if agent is registered (e.g., Claude agent automatically sets onboarding flags)SetModel() method if model is specified (takes precedence over model in settings files). The manager resolves the container hostname via the HostResolver optional interface on the runtime (defaults to host.containers.internal) and passes it to SetModel() so localhost URLs are rewritten correctly.SetMCPServers() method if the merged config contains MCP servers (writes them into agent settings)ConfigTransformer, calls TransformConfig() to apply runtime-specific transformations (e.g., rewriting MCP URLs)Name sanitization rules: valid characters are [a-z0-9._-]. Uppercase letters are lowercased; any run of invalid characters (spaces, @, +, etc.) is collapsed into a single hyphen; leading and trailing separators (hyphens, dots, and underscores) are stripped. An empty result falls back to "workspace".
List all registered workspace instances:
instancesList, err := manager.List()
if err != nil {
return fmt.Errorf("failed to list instances: %w", err)
}
for _, instance := range instancesList {
fmt.Printf("ID: %s, State: %s, Project: %s\n",
instance.ID, instance.State, instance.Project)
}
Get a specific instance by name or ID:
instance, err := manager.Get(nameOrID)
if err != nil {
if errors.Is(err, instances.ErrInstanceNotFound) {
return fmt.Errorf("workspace not found: %s", nameOrID)
}
return fmt.Errorf("instance not found: %w", err)
}
fmt.Printf("Found instance: %s (State: %s)\n", instance.ID, instance.State)
Key Points:
Get() method accepts either a workspace name or IDDelete an instance from the manager (requires ID):
err := manager.Delete(ctx, id)
if err != nil {
if errors.Is(err, instances.ErrInstanceNotFound) {
return fmt.Errorf("workspace not found: %s", id)
}
return fmt.Errorf("failed to delete instance: %w", err)
}
For commands accepting name or ID:
Commands should resolve the name or ID to an instance first, then use the ID:
// Resolve name or ID to get the instance
instance, err := manager.Get(nameOrID)
if err != nil {
if errors.Is(err, instances.ErrInstanceNotFound) {
return fmt.Errorf("workspace not found: %s", nameOrID)
}
return err
}
// Use the resolved ID
err = manager.Delete(ctx, instance.GetID())
if err != nil {
return fmt.Errorf("failed to delete instance: %w", err)
}
Start a stopped instance (requires ID):
err := manager.Start(ctx, id)
if err != nil {
if errors.Is(err, instances.ErrInstanceNotFound) {
return fmt.Errorf("workspace not found: %s", id)
}
return fmt.Errorf("failed to start instance: %w", err)
}
For commands accepting name or ID:
Commands should resolve the name or ID to an instance first, then use the ID:
// Resolve name or ID to get the instance
instance, err := manager.Get(nameOrID)
if err != nil {
if errors.Is(err, instances.ErrInstanceNotFound) {
return fmt.Errorf("workspace not found: %s", nameOrID)
}
return err
}
// Use the resolved ID
instanceID := instance.GetID()
err = manager.Start(ctx, instanceID)
if err != nil {
return fmt.Errorf("failed to start instance: %w", err)
}
// Output the ID (not the name)
fmt.Fprintln(cmd.OutOrStdout(), instanceID)
Stop a running instance (requires ID):
err := manager.Stop(ctx, id)
if err != nil {
if errors.Is(err, instances.ErrInstanceNotFound) {
return fmt.Errorf("workspace not found: %s", id)
}
return fmt.Errorf("failed to stop instance: %w", err)
}
For commands accepting name or ID:
Commands should resolve the name or ID to an instance first, then use the ID:
// Resolve name or ID to get the instance
instance, err := manager.Get(nameOrID)
if err != nil {
if errors.Is(err, instances.ErrInstanceNotFound) {
return fmt.Errorf("workspace not found: %s", nameOrID)
}
return err
}
// Use the resolved ID
instanceID := instance.GetID()
err = manager.Stop(ctx, instanceID)
if err != nil {
return fmt.Errorf("failed to stop instance: %w", err)
}
// Output the ID (not the name)
fmt.Fprintln(cmd.OutOrStdout(), instanceID)
Connect to an instance with an interactive terminal (requires ID). If the instance is not running, it will be automatically started first:
err := manager.Terminal(cmd.Context(), id, []string{"bash"})
if err != nil {
if errors.Is(err, instances.ErrInstanceNotFound) {
return fmt.Errorf("workspace not found: %s\nUse 'workspace list' to see available workspaces", id)
}
return err
}
Terminal Method Behavior:
runtime.Terminal interfaceKey Points:
[]string{"bash"} or []string{"claude-code", "--debug"}ErrInstanceNotFound if instance doesn't existruntime.Terminal interfaceFor commands accepting name or ID:
Commands should resolve the name or ID to an instance first, then use the ID:
func (w *workspaceTerminalCmd) run(cmd *cobra.Command, args []string) error {
// Resolve name or ID to get the instance
instance, err := w.manager.Get(w.nameOrID)
if err != nil {
if errors.Is(err, instances.ErrInstanceNotFound) {
return fmt.Errorf("workspace not found: %s\nUse 'workspace list' to see available workspaces", w.nameOrID)
}
return err
}
// Get the actual ID (in case user provided a name)
instanceID := instance.GetID()
// Start terminal session
err = w.manager.Terminal(cmd.Context(), instanceID, w.command)
if err != nil {
return err
}
return nil
}
The manager integrates with the agent registry to provide automatic onboarding configuration for supported agents.
Register all available agents using the centralized registration:
import "github.com/openkaiden/kdn/pkg/agentsetup"
// In preRun or initialization code
if err := agentsetup.RegisterAll(manager); err != nil {
return fmt.Errorf("failed to register agents: %w", err)
}
This registers all available agents (e.g., Claude) with the manager's agent registry.
When adding an instance with an agent name, the manager automatically:
<storage-dir>/config/<agent>/ (e.g., config/claude/.claude.json)SkipOnboarding() if the agent is registered, passing:
/workspace/sources)Example - Claude Agent:
For the Claude agent, SkipOnboarding() automatically:
hasCompletedOnboarding: true to skip the first-run wizardhasTrustDialogAccepted: true for the workspace sources directorySetMCPServers() writes MCP server entries into .claude.json at the top-level mcpServers key (user scope). Command-based servers use type: "stdio", URL-based servers use type: "sse". Existing MCP server entries in the file are preserved.
Graceful Fallback:
If an agent name is provided but not registered:
SkipOnboarding() modification occursThis allows forward compatibility with new agents before they implement the onboarding interface.
// Create manager with agent registry
manager, _ := instances.NewManager(storageDir)
// Register a specific agent for testing
claudeAgent := agent.NewClaude()
if err := manager.RegisterAgent("claude", claudeAgent); err != nil {
t.Fatalf("Failed to register agent: %v", err)
}
// Add instance - Claude's SkipOnboarding will be called automatically
instance, err := manager.Add(ctx, instances.AddOptions{
Instance: inst,
RuntimeType: "fake",
Agent: "claude",
})
See pkg/instances/manager_test.go (TestManager_Add_AppliesAgentOnboarding) for comprehensive test examples.
Each workspace has a project field that enables grouping workspaces belonging to the same project across branches, forks, or subdirectories.
The manager automatically detects the project identifier when adding instances:
Git repository with remote: Uses repository remote URL (without .git) plus relative path
upstream remote first (useful for forks)origin remote if upstream doesn't existhttps://github.com/openkaiden/kdn/ (at root) or https://github.com/openkaiden/kdn/pkg/git (in subdirectory)Git repository without remote: Uses repository root directory plus relative path
/home/user/local-repo (at root) or /home/user/local-repo/pkg/utils (in subdirectory)Non-git directory: Uses the source directory path
/tmp/workspaceUsers can override auto-detection with the --project flag:
// Add instance with custom project
addedInstance, err := manager.Add(ctx, instances.AddOptions{
Instance: instance,
RuntimeType: "fake",
WorkspaceConfig: workspaceConfig,
Project: "custom-project-id", // Optional: overrides auto-detection
})
pkg/git provides git repository detection with testable abstractionsgit.Detector with DetectRepository(ctx, dir) methodgit.Executor abstracts git command execution for testingmanager.detectProject() is called during Add() if no custom project is provided// Use fake git detector in tests
gitDetector := newFakeGitDetectorWithRepo(
"/repo/root",
"https://github.com/user/repo",
"pkg/subdir", // relative path
)
manager, _ := newManagerWithFactory(
storageDir,
fakeInstanceFactory,
newFakeGenerator(),
newTestRegistry(tmpDir),
gitDetector,
)
See pkg/instances/manager_project_test.go for comprehensive test examples.
Common errors from the manager:
// Instance not found
if errors.Is(err, instances.ErrInstanceNotFound) {
return fmt.Errorf("workspace not found: %s", id)
}
// Runtime not found
if errors.Is(err, runtime.ErrRuntimeNotFound) {
return fmt.Errorf("runtime not found: %s", runtimeType)
}
// Instance already exists
if errors.Is(err, instances.ErrInstanceExists) {
return fmt.Errorf("workspace already exists: %s", id)
}
type myCmd struct {
manager instances.Manager
}
func (c *myCmd) preRun(cmd *cobra.Command, args []string) error {
storageDir, _ := cmd.Flags().GetString("storage")
manager, err := instances.NewManager(storageDir)
if err != nil {
return fmt.Errorf("failed to create manager: %w", err)
}
// Register runtimes
if err := runtimesetup.RegisterAll(manager); err != nil {
return fmt.Errorf("failed to register runtimes: %w", err)
}
// Register agents
if err := agentsetup.RegisterAll(manager); err != nil {
return fmt.Errorf("failed to register agents: %w", err)
}
c.manager = manager
return nil
}
func (c *myCmd) run(cmd *cobra.Command, args []string) error {
// Use manager to list instances
instances, err := c.manager.List()
if err != nil {
return fmt.Errorf("failed to list instances: %w", err)
}
for _, instance := range instances {
cmd.Printf("ID: %s, State: %s, Project: %s\n",
instance.ID, instance.State, instance.Project)
}
return nil
}
/working-with-config-system - Configuration merging and multi-level configs/working-with-runtime-system - Runtime system architecture/implementing-command-patterns - Command implementation patternspkg/instances/manager.gopkg/git/pkg/instances/manager_project_test.gopkg/cmd/init.go, pkg/cmd/workspace_list.go, pkg/cmd/workspace_terminal.go