mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
chore: batch update - cleanup ghost commands, ccw-help index refresh, CLI session/orchestrator enhancements, skill minor fixes
- Add cleanup-ghost-commands.mjs script - Refresh ccw-help index files (remove stale entries) - CLI session manager: add instruction assembler and launch registry - Frontend: orchestrator plan builder, property panel, dashboard toolbar updates - Flow executor and type updates - Minor fixes across multiple skills and commands
This commit is contained in:
@@ -46,7 +46,6 @@ import {
|
||||
import { useIssues, useIssueQueue } from '@/hooks/useIssues';
|
||||
import { useTerminalGridStore, selectTerminalGridFocusedPaneId } from '@/stores/terminalGridStore';
|
||||
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
|
||||
import { sendCliSessionText } from '@/lib/api';
|
||||
import { CliConfigModal, type CliSessionConfig } from './CliConfigModal';
|
||||
|
||||
// ========== Types ==========
|
||||
@@ -84,14 +83,6 @@ type LaunchMode = 'default' | 'yolo';
|
||||
const CLI_TOOLS = ['claude', 'gemini', 'qwen', 'codex', 'opencode'] as const;
|
||||
type CliTool = (typeof CLI_TOOLS)[number];
|
||||
|
||||
const LAUNCH_COMMANDS: Record<CliTool, Record<LaunchMode, string>> = {
|
||||
claude: { default: 'claude', yolo: 'claude --permission-mode bypassPermissions' },
|
||||
gemini: { default: 'gemini', yolo: 'gemini --approval-mode yolo' },
|
||||
qwen: { default: 'qwen', yolo: 'qwen --approval-mode yolo' },
|
||||
codex: { default: 'codex', yolo: 'codex --full-auto' },
|
||||
opencode: { default: 'opencode', yolo: 'opencode' },
|
||||
};
|
||||
|
||||
// ========== Component ==========
|
||||
|
||||
export function DashboardToolbar({ activePanel, onTogglePanel, isFileSidebarOpen, onToggleFileSidebar, isSessionSidebarOpen, onToggleSessionSidebar, isFullscreen, onToggleFullscreen }: DashboardToolbarProps) {
|
||||
@@ -151,22 +142,12 @@ export function DashboardToolbar({ activePanel, onTogglePanel, isFileSidebarOpen
|
||||
const targetPaneId = getOrCreateFocusedPane();
|
||||
if (!targetPaneId) return;
|
||||
|
||||
const created = await createSessionAndAssign(targetPaneId, {
|
||||
await createSessionAndAssign(targetPaneId, {
|
||||
workingDir: projectPath,
|
||||
preferredShell: 'bash',
|
||||
tool: selectedTool,
|
||||
launchMode,
|
||||
}, projectPath);
|
||||
|
||||
if (created?.session?.sessionKey) {
|
||||
const command = LAUNCH_COMMANDS[selectedTool]?.[launchMode] ?? selectedTool;
|
||||
setTimeout(() => {
|
||||
sendCliSessionText(
|
||||
created.session.sessionKey,
|
||||
{ text: command, appendNewline: true },
|
||||
projectPath
|
||||
).catch((err) => console.error('[DashboardToolbar] auto-launch failed:', err));
|
||||
}, 300);
|
||||
}
|
||||
} finally {
|
||||
setIsCreating(false);
|
||||
}
|
||||
@@ -190,22 +171,12 @@ export function DashboardToolbar({ activePanel, onTogglePanel, isFileSidebarOpen
|
||||
preferredShell: config.preferredShell,
|
||||
tool: config.tool,
|
||||
model: config.model,
|
||||
launchMode: config.launchMode,
|
||||
},
|
||||
projectPath
|
||||
);
|
||||
|
||||
if (!created?.session?.sessionKey) throw new Error('createSessionAndAssign failed');
|
||||
|
||||
const tool = config.tool as CliTool;
|
||||
const mode = config.launchMode as LaunchMode;
|
||||
const command = LAUNCH_COMMANDS[tool]?.[mode] ?? tool;
|
||||
setTimeout(() => {
|
||||
sendCliSessionText(
|
||||
created.session.sessionKey,
|
||||
{ text: command, appendNewline: true },
|
||||
projectPath
|
||||
).catch((err) => console.error('[DashboardToolbar] auto-launch failed:', err));
|
||||
}, 300);
|
||||
} finally {
|
||||
setIsCreating(false);
|
||||
}
|
||||
|
||||
@@ -6336,6 +6336,8 @@ export interface CliSession {
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
isPaused: boolean;
|
||||
/** When set, this session is a native CLI interactive process. */
|
||||
cliTool?: string;
|
||||
}
|
||||
|
||||
export interface CreateCliSessionInput {
|
||||
@@ -6346,6 +6348,8 @@ export interface CreateCliSessionInput {
|
||||
tool?: string;
|
||||
model?: string;
|
||||
resumeKey?: string;
|
||||
/** Launch mode for native CLI sessions (default or yolo). */
|
||||
launchMode?: 'default' | 'yolo';
|
||||
}
|
||||
|
||||
function withPath(url: string, projectPath?: string): string {
|
||||
@@ -6397,6 +6401,8 @@ export interface ExecuteInCliSessionInput {
|
||||
category?: 'user' | 'internal' | 'insight';
|
||||
resumeKey?: string;
|
||||
resumeStrategy?: 'nativeResume' | 'promptConcat';
|
||||
instructionType?: 'prompt' | 'skill' | 'command';
|
||||
skillName?: string;
|
||||
}
|
||||
|
||||
export async function executeInCliSession(
|
||||
|
||||
@@ -159,6 +159,8 @@ export async function dispatch(
|
||||
category: options.category,
|
||||
resumeKey: options.resumeKey ?? step.resumeKey,
|
||||
resumeStrategy: options.resumeStrategy,
|
||||
instructionType: step.instructionType,
|
||||
skillName: step.skillName,
|
||||
};
|
||||
|
||||
// Step 3: Execute in the resolved session
|
||||
|
||||
@@ -104,13 +104,25 @@ export class OrchestrationPlanBuilder {
|
||||
if (nodeData.slashCommand) {
|
||||
executionType = 'slash-command';
|
||||
} else if (nodeData.tool && nodeData.mode) {
|
||||
// More sophisticated logic might be needed here to differentiate backend-flow
|
||||
// For now, if tool/mode are present, assume frontend-cli or backend-flow
|
||||
// depending on whether it's a direct CLI call or a backend orchestrator call.
|
||||
// Assuming CLI tools are frontend-cli for now unless specified otherwise.
|
||||
executionType = 'frontend-cli';
|
||||
}
|
||||
|
||||
// Resolve instructionType and skillName
|
||||
// Priority: explicit instructionType > slashCommand backward compat > default prompt
|
||||
let instructionType = nodeData.instructionType;
|
||||
let skillName = nodeData.skillName;
|
||||
if (!instructionType) {
|
||||
if (skillName) {
|
||||
instructionType = 'skill';
|
||||
} else if (nodeData.slashCommand) {
|
||||
// Backward compat: map slashCommand to skill type
|
||||
instructionType = 'skill';
|
||||
skillName = nodeData.slashCommand;
|
||||
} else {
|
||||
instructionType = 'prompt';
|
||||
}
|
||||
}
|
||||
|
||||
steps.push({
|
||||
id: node.id,
|
||||
name: nodeData.label || `Step ${node.id}`,
|
||||
@@ -128,6 +140,8 @@ export class OrchestrationPlanBuilder {
|
||||
errorHandling: undefined,
|
||||
executionType: executionType,
|
||||
sourceNodeId: node.id,
|
||||
instructionType,
|
||||
skillName,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1126,6 +1126,49 @@ function PromptTemplateProperties({ data, onChange }: PromptTemplatePropertiesPr
|
||||
{/* CLI Session Routing (tmux-like) */}
|
||||
{!isSlashCommandMode && (
|
||||
<>
|
||||
{/* Instruction Type for native CLI sessions */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-foreground mb-1">
|
||||
Instruction Type
|
||||
</label>
|
||||
<select
|
||||
value={data.instructionType || 'prompt'}
|
||||
onChange={(e) => {
|
||||
const next = e.target.value as 'prompt' | 'skill' | 'command';
|
||||
const updates: Partial<PromptTemplateNodeData> = { instructionType: next };
|
||||
if (next !== 'skill') {
|
||||
updates.skillName = undefined;
|
||||
}
|
||||
onChange(updates);
|
||||
}}
|
||||
className="w-full h-10 px-3 rounded-md border border-border bg-background text-foreground text-sm"
|
||||
>
|
||||
<option value="prompt">Prompt (direct text)</option>
|
||||
<option value="skill">Skill (CLI-specific prefix)</option>
|
||||
<option value="command">Command (CLI native)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Skill Name - shown when instructionType is 'skill' */}
|
||||
{(data.instructionType === 'skill') && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-foreground mb-1">
|
||||
Skill Name
|
||||
{data.tool && (
|
||||
<span className="ml-2 text-xs text-muted-foreground font-normal">
|
||||
{data.tool === 'claude' ? 'prefix: /' : data.tool === 'codex' ? 'prefix: $' : 'no prefix'}
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
<Input
|
||||
value={data.skillName || ''}
|
||||
onChange={(e) => onChange({ skillName: e.target.value || undefined })}
|
||||
placeholder={data.tool === 'claude' ? 'e.g. review-code' : data.tool === 'codex' ? 'e.g. fix' : 'skill name'}
|
||||
className="font-mono text-sm"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-foreground mb-1">
|
||||
{formatMessage({ id: 'orchestrator.propertyPanel.delivery' })}
|
||||
|
||||
@@ -24,7 +24,7 @@ export type ExecutionStatus = 'pending' | 'running' | 'completed' | 'failed';
|
||||
/**
|
||||
* Available CLI tools for execution
|
||||
*/
|
||||
export type CliTool = 'gemini' | 'qwen' | 'codex' | 'claude';
|
||||
export type CliTool = 'gemini' | 'qwen' | 'codex' | 'claude' | 'opencode';
|
||||
|
||||
/**
|
||||
* Execution modes for prompt templates
|
||||
@@ -117,6 +117,20 @@ export interface PromptTemplateNodeData {
|
||||
*/
|
||||
contextRefs?: string[];
|
||||
|
||||
/**
|
||||
* Instruction type for CLI session execution.
|
||||
* - prompt: raw text sent as conversation input
|
||||
* - skill: CLI-specific skill invocation (Claude: /name, Codex: $name)
|
||||
* - command: CLI native command
|
||||
*/
|
||||
instructionType?: 'prompt' | 'skill' | 'command';
|
||||
|
||||
/**
|
||||
* Skill name for instructionType='skill'.
|
||||
* The actual prefix (/ or $) is determined by the target CLI tool.
|
||||
*/
|
||||
skillName?: string;
|
||||
|
||||
/**
|
||||
* Selected slash command name (e.g., "workflow:plan", "review-code")
|
||||
* When set, overrides instruction during execution.
|
||||
|
||||
@@ -145,6 +145,20 @@ export interface OrchestrationStep {
|
||||
*/
|
||||
executionType: ExecutionType;
|
||||
|
||||
/**
|
||||
* Instruction type for native CLI session execution.
|
||||
* - prompt: raw text conversation input
|
||||
* - skill: CLI-specific skill invocation (prefix determined by CLI tool)
|
||||
* - command: CLI native command
|
||||
*/
|
||||
instructionType?: 'prompt' | 'skill' | 'command';
|
||||
|
||||
/**
|
||||
* Skill name for instructionType='skill'.
|
||||
* The actual prefix (/ or $) is assembled by the backend InstructionAssembler.
|
||||
*/
|
||||
skillName?: string;
|
||||
|
||||
/**
|
||||
* For flow-based plans, the ID of the source FlowNode.
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import type { RouteContext } from './types.js';
|
||||
import { getCliSessionManager } from '../services/cli-session-manager.js';
|
||||
import type { InstructionType } from '../services/cli-instruction-assembler.js';
|
||||
import path from 'path';
|
||||
import { getCliSessionPolicy } from '../services/cli-session-policy.js';
|
||||
import { RateLimiter } from '../services/rate-limiter.js';
|
||||
@@ -91,7 +92,8 @@ export async function handleCliSessionsRoutes(ctx: RouteContext): Promise<boolea
|
||||
preferredShell,
|
||||
tool,
|
||||
model,
|
||||
resumeKey
|
||||
resumeKey,
|
||||
launchMode
|
||||
} = (body || {}) as any;
|
||||
|
||||
if (tool && typeof tool === 'string') {
|
||||
@@ -115,7 +117,8 @@ export async function handleCliSessionsRoutes(ctx: RouteContext): Promise<boolea
|
||||
preferredShell: preferredShell === 'pwsh' ? 'pwsh' : 'bash',
|
||||
tool: typeof tool === 'string' ? tool.trim() : undefined,
|
||||
model,
|
||||
resumeKey
|
||||
resumeKey,
|
||||
launchMode: launchMode === 'yolo' ? 'yolo' : 'default',
|
||||
});
|
||||
|
||||
appendCliSessionAudit({
|
||||
@@ -353,7 +356,9 @@ export async function handleCliSessionsRoutes(ctx: RouteContext): Promise<boolea
|
||||
workingDir,
|
||||
category,
|
||||
resumeKey,
|
||||
resumeStrategy
|
||||
resumeStrategy,
|
||||
instructionType,
|
||||
skillName
|
||||
} = (body || {}) as any;
|
||||
|
||||
if (!tool || typeof tool !== 'string') {
|
||||
@@ -380,7 +385,9 @@ export async function handleCliSessionsRoutes(ctx: RouteContext): Promise<boolea
|
||||
workingDir,
|
||||
category,
|
||||
resumeKey,
|
||||
resumeStrategy: resumeStrategy === 'promptConcat' ? 'promptConcat' : 'nativeResume'
|
||||
resumeStrategy: resumeStrategy === 'promptConcat' ? 'promptConcat' : 'nativeResume',
|
||||
instructionType: typeof instructionType === 'string' ? instructionType as InstructionType : undefined,
|
||||
skillName: typeof skillName === 'string' ? skillName : undefined,
|
||||
});
|
||||
|
||||
appendCliSessionAudit({
|
||||
|
||||
37
ccw/src/core/services/cli-instruction-assembler.ts
Normal file
37
ccw/src/core/services/cli-instruction-assembler.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
// ========================================
|
||||
// CLI Instruction Assembler
|
||||
// ========================================
|
||||
// Assembles the final sendText string based on CLI type and instruction type.
|
||||
|
||||
export type InstructionType = 'prompt' | 'skill' | 'command';
|
||||
|
||||
/**
|
||||
* Assemble the text to send to a CLI interactive session.
|
||||
*
|
||||
* - prompt → raw content text
|
||||
* - skill → CLI-specific skill prefix (claude: /, codex: $, others: fallback to prompt)
|
||||
* - command → raw content text (CLI native command)
|
||||
*/
|
||||
export function assembleInstruction(
|
||||
cliTool: string,
|
||||
instructionType: InstructionType,
|
||||
content: string,
|
||||
skillName?: string,
|
||||
): string {
|
||||
if (instructionType === 'prompt' || instructionType === 'command') {
|
||||
return content;
|
||||
}
|
||||
|
||||
// instructionType === 'skill'
|
||||
const name = skillName ?? '';
|
||||
|
||||
switch (cliTool) {
|
||||
case 'claude':
|
||||
return `/${name} ${content}`;
|
||||
case 'codex':
|
||||
return `$${name} ${content}`;
|
||||
default:
|
||||
// Other CLIs don't support skill syntax — fallback to plain prompt
|
||||
return content;
|
||||
}
|
||||
}
|
||||
53
ccw/src/core/services/cli-launch-registry.ts
Normal file
53
ccw/src/core/services/cli-launch-registry.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
// ========================================
|
||||
// CLI Launch Registry
|
||||
// ========================================
|
||||
// Defines interactive-mode launch parameters for each CLI tool.
|
||||
// Supports 'default' and 'yolo' launch modes.
|
||||
|
||||
export type CliTool = 'claude' | 'gemini' | 'qwen' | 'codex' | 'opencode';
|
||||
|
||||
export type LaunchMode = 'default' | 'yolo';
|
||||
|
||||
export interface CliLaunchConfig {
|
||||
command: string;
|
||||
args: string[];
|
||||
env?: Record<string, string>;
|
||||
}
|
||||
|
||||
function parseCommand(raw: string): CliLaunchConfig {
|
||||
const parts = raw.split(/\s+/);
|
||||
return { command: parts[0], args: parts.slice(1) };
|
||||
}
|
||||
|
||||
const LAUNCH_CONFIGS: Record<CliTool, Record<LaunchMode, CliLaunchConfig>> = {
|
||||
claude: {
|
||||
default: parseCommand('claude'),
|
||||
yolo: parseCommand('claude --permission-mode bypassPermissions'),
|
||||
},
|
||||
gemini: {
|
||||
default: parseCommand('gemini'),
|
||||
yolo: parseCommand('gemini --approval-mode yolo'),
|
||||
},
|
||||
qwen: {
|
||||
default: parseCommand('qwen'),
|
||||
yolo: parseCommand('qwen --approval-mode yolo'),
|
||||
},
|
||||
codex: {
|
||||
default: parseCommand('codex'),
|
||||
yolo: parseCommand('codex --full-auto'),
|
||||
},
|
||||
opencode: {
|
||||
default: parseCommand('opencode'),
|
||||
yolo: parseCommand('opencode'),
|
||||
},
|
||||
};
|
||||
|
||||
const KNOWN_TOOLS = new Set<string>(Object.keys(LAUNCH_CONFIGS));
|
||||
|
||||
export function getLaunchConfig(tool: string, launchMode: LaunchMode): CliLaunchConfig {
|
||||
if (KNOWN_TOOLS.has(tool)) {
|
||||
return LAUNCH_CONFIGS[tool as CliTool][launchMode];
|
||||
}
|
||||
// Unknown tool: treat the tool name itself as the command
|
||||
return { command: tool, args: [] };
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
} from './cli-session-command-builder.js';
|
||||
import { getCliSessionPolicy } from './cli-session-policy.js';
|
||||
import { appendCliSessionAudit } from './cli-session-audit.js';
|
||||
import { getLaunchConfig } from './cli-launch-registry.js';
|
||||
import { assembleInstruction, type InstructionType } from './cli-instruction-assembler.js';
|
||||
|
||||
export interface CliSession {
|
||||
sessionKey: string;
|
||||
@@ -24,6 +26,8 @@ export interface CliSession {
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
isPaused: boolean;
|
||||
/** When set, this session is a native CLI interactive process (not a shell). */
|
||||
cliTool?: string;
|
||||
}
|
||||
|
||||
export interface CreateCliSessionOptions {
|
||||
@@ -34,6 +38,8 @@ export interface CreateCliSessionOptions {
|
||||
tool?: string;
|
||||
model?: string;
|
||||
resumeKey?: string;
|
||||
/** Launch mode for native CLI sessions. */
|
||||
launchMode?: 'default' | 'yolo';
|
||||
}
|
||||
|
||||
export interface ExecuteInCliSessionOptions {
|
||||
@@ -45,6 +51,10 @@ export interface ExecuteInCliSessionOptions {
|
||||
category?: 'user' | 'internal' | 'insight';
|
||||
resumeKey?: string;
|
||||
resumeStrategy?: CliSessionResumeStrategy;
|
||||
/** Instruction type for native CLI sessions. */
|
||||
instructionType?: InstructionType;
|
||||
/** Skill name for instructionType='skill'. */
|
||||
skillName?: string;
|
||||
}
|
||||
|
||||
export interface CliSessionOutputEvent {
|
||||
@@ -202,12 +212,31 @@ export class CliSessionManager {
|
||||
|
||||
createSession(options: CreateCliSessionOptions): CliSession {
|
||||
const workingDir = normalizeWorkingDir(options.workingDir);
|
||||
const preferredShell = options.preferredShell ?? 'bash';
|
||||
const { shellKind, file, args } = pickShell(preferredShell);
|
||||
|
||||
const sessionKey = createSessionKey();
|
||||
const createdAt = nowIso();
|
||||
|
||||
let shellKind: CliSessionShellKind;
|
||||
let file: string;
|
||||
let args: string[];
|
||||
let cliTool: string | undefined;
|
||||
|
||||
if (options.tool) {
|
||||
// Native CLI interactive session: spawn the CLI process directly
|
||||
const launchMode = options.launchMode ?? 'default';
|
||||
const config = getLaunchConfig(options.tool, launchMode);
|
||||
shellKind = 'git-bash'; // PTY shell kind label (not actually a shell)
|
||||
file = config.command;
|
||||
args = config.args;
|
||||
cliTool = options.tool;
|
||||
} else {
|
||||
// Legacy shell session: spawn bash/pwsh
|
||||
const preferredShell = options.preferredShell ?? 'bash';
|
||||
const picked = pickShell(preferredShell);
|
||||
shellKind = picked.shellKind;
|
||||
file = picked.file;
|
||||
args = picked.args;
|
||||
}
|
||||
|
||||
const pty = nodePty.spawn(file, args, {
|
||||
name: 'xterm-256color',
|
||||
cols: options.cols ?? 120,
|
||||
@@ -230,6 +259,7 @@ export class CliSessionManager {
|
||||
bufferBytes: 0,
|
||||
lastActivityAt: Date.now(),
|
||||
isPaused: false,
|
||||
cliTool,
|
||||
};
|
||||
|
||||
pty.onData((data) => {
|
||||
@@ -272,7 +302,8 @@ export class CliSessionManager {
|
||||
this.sessions.set(sessionKey, session);
|
||||
|
||||
// WSL often ignores Windows cwd; best-effort cd to mounted path.
|
||||
if (shellKind === 'wsl-bash') {
|
||||
// Only for legacy shell sessions, not native CLI sessions.
|
||||
if (!cliTool && shellKind === 'wsl-bash') {
|
||||
const wslCwd = toWslPath(workingDir.replace(/\\/g, '/'));
|
||||
this.sendText(sessionKey, `cd ${wslCwd}`, true);
|
||||
}
|
||||
@@ -388,27 +419,37 @@ export class CliSessionManager {
|
||||
? `${resumeKey}-${Date.now()}`
|
||||
: `exec-${Date.now()}-${randomBytes(3).toString('hex')}`;
|
||||
|
||||
const { command } = buildCliSessionExecuteCommand({
|
||||
projectRoot: this.projectRoot,
|
||||
shellKind: session.shellKind,
|
||||
tool: options.tool,
|
||||
prompt: options.prompt,
|
||||
mode: options.mode,
|
||||
model: options.model,
|
||||
workingDir: options.workingDir ?? session.workingDir,
|
||||
category: options.category,
|
||||
resumeStrategy: options.resumeStrategy,
|
||||
prevExecutionId,
|
||||
executionId
|
||||
});
|
||||
let command: string;
|
||||
|
||||
if (session.cliTool) {
|
||||
// Native CLI session: assemble instruction and sendText directly
|
||||
const instructionType = options.instructionType ?? 'prompt';
|
||||
command = assembleInstruction(session.cliTool, instructionType, options.prompt, options.skillName);
|
||||
this.sendText(sessionKey, command, true);
|
||||
} else {
|
||||
// Legacy shell session: build ccw cli pipe command
|
||||
const result = buildCliSessionExecuteCommand({
|
||||
projectRoot: this.projectRoot,
|
||||
shellKind: session.shellKind,
|
||||
tool: options.tool,
|
||||
prompt: options.prompt,
|
||||
mode: options.mode,
|
||||
model: options.model,
|
||||
workingDir: options.workingDir ?? session.workingDir,
|
||||
category: options.category,
|
||||
resumeStrategy: options.resumeStrategy,
|
||||
prevExecutionId,
|
||||
executionId
|
||||
});
|
||||
command = result.command;
|
||||
this.sendText(sessionKey, command, true);
|
||||
}
|
||||
|
||||
// Best-effort: preemptively update mapping so subsequent queue items can chain.
|
||||
if (resumeMapKey) {
|
||||
this.resumeKeyLastExecution.set(resumeMapKey, executionId);
|
||||
}
|
||||
|
||||
this.sendText(sessionKey, command, true);
|
||||
|
||||
broadcastToClients({
|
||||
type: 'CLI_SESSION_EXECUTE',
|
||||
payload: { sessionKey, executionId, command, timestamp: nowIso() }
|
||||
|
||||
@@ -21,6 +21,7 @@ import { broadcastToClients } from '../websocket.js';
|
||||
import { executeCliTool } from '../../tools/cli-executor-core.js';
|
||||
import { cliSessionMux } from './cli-session-mux.js';
|
||||
import { appendCliSessionAudit } from './cli-session-audit.js';
|
||||
import { assembleInstruction, type InstructionType } from './cli-instruction-assembler.js';
|
||||
import type {
|
||||
Flow,
|
||||
FlowNode,
|
||||
@@ -264,12 +265,30 @@ export class NodeRunner {
|
||||
error: `Target session not found: ${targetSessionKey}`
|
||||
};
|
||||
}
|
||||
|
||||
// Resolve instructionType and skillName for native CLI sessions
|
||||
let instructionType = data.instructionType;
|
||||
let skillName = data.skillName;
|
||||
if (!instructionType) {
|
||||
if (skillName) {
|
||||
instructionType = 'skill';
|
||||
} else if (data.slashCommand) {
|
||||
// Backward compat: map slashCommand to skill
|
||||
instructionType = 'skill';
|
||||
skillName = data.slashCommand;
|
||||
} else {
|
||||
instructionType = 'prompt';
|
||||
}
|
||||
}
|
||||
|
||||
const routed = manager.execute(targetSessionKey, {
|
||||
tool,
|
||||
prompt: instruction,
|
||||
mode,
|
||||
resumeKey: data.resumeKey,
|
||||
resumeStrategy: data.resumeStrategy === 'promptConcat' ? 'promptConcat' : 'nativeResume'
|
||||
resumeStrategy: data.resumeStrategy === 'promptConcat' ? 'promptConcat' : 'nativeResume',
|
||||
instructionType: instructionType as InstructionType,
|
||||
skillName,
|
||||
});
|
||||
|
||||
// Best-effort: record audit event so Observability panel includes orchestrator-routed executions.
|
||||
|
||||
Reference in New Issue
Block a user