feat: inject cmd working directory into system prompt for hipico mode

When using :hipico from cmd mode after cd'ing to a subdirectory, the AI
now sees the current working directory in the system prompt instead of
just a weak user-message prefix. This ensures the AI resolves file paths
relative to the cmd working directory, not the workspace root.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Orlando Chen 2026-02-24 17:34:10 +09:00
parent 454d12f5c6
commit a9f1d0eb67
2 changed files with 28 additions and 7 deletions

View file

@ -204,10 +204,8 @@ func interactiveMode(agentLoop *agent.AgentLoop, sessionKey string) {
continue continue
} }
contextPrefix := fmt.Sprintf("[Command mode context: working directory is %s]\n\n", cmdWorkingDir)
ctx := context.Background() ctx := context.Background()
response, err := agentLoop.ProcessDirect(ctx, contextPrefix+initialMsg, hipicoSessionKey) response, err := agentLoop.ProcessDirectWithWorkDir(ctx, initialMsg, hipicoSessionKey, cmdWorkingDir)
if err != nil { if err != nil {
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
continue continue
@ -295,10 +293,8 @@ func simpleInteractiveMode(agentLoop *agent.AgentLoop, sessionKey string) {
continue continue
} }
contextPrefix := fmt.Sprintf("[Command mode context: working directory is %s]\n\n", cmdWorkingDir)
ctx := context.Background() ctx := context.Background()
response, err := agentLoop.ProcessDirect(ctx, contextPrefix+initialMsg, hipicoSessionKey) response, err := agentLoop.ProcessDirectWithWorkDir(ctx, initialMsg, hipicoSessionKey, cmdWorkingDir)
if err != nil { if err != nil {
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
continue continue

View file

@ -63,6 +63,7 @@ type processOptions struct {
EnableSummary bool // Whether to trigger summarization EnableSummary bool // Whether to trigger summarization
SendResponse bool // Whether to send response via bus SendResponse bool // Whether to send response via bus
NoHistory bool // If true, don't load session history (for heartbeat) NoHistory bool // If true, don't load session history (for heartbeat)
WorkingDir string // Current working directory override (for hipico from cmd mode)
} }
func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers.LLMProvider) *AgentLoop { func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers.LLMProvider) *AgentLoop {
@ -273,6 +274,20 @@ func (al *AgentLoop) ProcessDirect(ctx context.Context, content, sessionKey stri
return al.ProcessDirectWithChannel(ctx, content, sessionKey, "cli", "direct") return al.ProcessDirectWithChannel(ctx, content, sessionKey, "cli", "direct")
} }
// ProcessDirectWithWorkDir processes a message with an explicit working directory context.
// The workDir is injected into the system prompt so the AI resolves file paths relative to it.
func (al *AgentLoop) ProcessDirectWithWorkDir(ctx context.Context, content, sessionKey, workDir string) (string, error) {
msg := bus.InboundMessage{
Channel: "cli",
SenderID: "cron",
ChatID: "direct",
Content: content,
SessionKey: sessionKey,
Metadata: map[string]string{"work_dir": workDir},
}
return al.processMessage(ctx, msg)
}
func (al *AgentLoop) ProcessDirectWithChannel( func (al *AgentLoop) ProcessDirectWithChannel(
ctx context.Context, ctx context.Context,
content, sessionKey, channel, chatID string, content, sessionKey, channel, chatID string,
@ -371,7 +386,6 @@ func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage)
if workDir == "" { if workDir == "" {
workDir = agent.Workspace workDir = agent.Workspace
} }
userMessage = fmt.Sprintf("[Command mode context: working directory is %s]\n\n%s", workDir, userMessage)
hipicoSessionKey := sessionKey + ":hipico" hipicoSessionKey := sessionKey + ":hipico"
return al.runAgentLoop(ctx, agent, processOptions{ return al.runAgentLoop(ctx, agent, processOptions{
SessionKey: hipicoSessionKey, SessionKey: hipicoSessionKey,
@ -381,6 +395,7 @@ func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage)
DefaultResponse: "I've completed processing but have no response to give.", DefaultResponse: "I've completed processing but have no response to give.",
EnableSummary: false, EnableSummary: false,
SendResponse: false, SendResponse: false,
WorkingDir: workDir,
}) })
} }
} }
@ -399,6 +414,7 @@ func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage)
DefaultResponse: "I've completed processing but have no response to give.", DefaultResponse: "I've completed processing but have no response to give.",
EnableSummary: true, EnableSummary: true,
SendResponse: false, SendResponse: false,
WorkingDir: msg.Metadata["work_dir"],
}) })
} }
} }
@ -491,6 +507,15 @@ func (al *AgentLoop) runAgentLoop(ctx context.Context, agent *AgentInstance, opt
opts.ChatID, opts.ChatID,
) )
// 2b. Inject current working directory into system prompt if set
if opts.WorkingDir != "" && len(messages) > 0 && messages[0].Role == "system" {
messages[0].Content += fmt.Sprintf(
"\n\n## Current Working Directory\nThe user is currently working in: %s\n"+
"When the user refers to files or directories, resolve them relative to this path, not the workspace root.",
opts.WorkingDir,
)
}
// 3. Save user message to session // 3. Save user message to session
agent.Sessions.AddMessage(opts.SessionKey, "user", opts.UserMessage) agent.Sessions.AddMessage(opts.SessionKey, "user", opts.UserMessage)