@xelauvas/xela-cli 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/local/README.md +112 -0
- package/local/start-qwen.sh +47 -0
- package/local/start-tunnel.sh +36 -0
- package/local/xela-local.sh +139 -0
- package/package.json +19 -5
- package/run.sh +33 -0
- package/src/Tool.ts +1 -1
- package/src/_shims/_generated_stubs/_universal_stub.mjs +168 -0
- package/src/_shims/_generated_stubs/stub_346f96741420f62caece15b196e233c6.mjs +19 -0
- package/src/_shims/_generated_stubs/stub_4b79af2a6ac1af5a53a55fd666b2bbb4.mjs +3 -0
- package/src/_shims/_generated_stubs/stub_94345ddeadacc184ff895597ec5abf71.mjs +6 -0
- package/src/_shims/_generated_stubs/stub_99fa0bc272643c549d8a2ec6d578436f.mjs +3 -0
- package/src/_shims/_generated_stubs/stub_e3451b131fcaf272a2a75270b9a427a3.mjs +9 -0
- package/src/_shims/_generated_stubs/text_stub.mjs +1 -0
- package/src/_shims/bun_bundle.js +79 -4
- package/src/_shims/cjs_stub.cjs +32 -10
- package/src/_shims/loader.js +104 -5
- package/src/_shims/openai_adapter.ts +446 -230
- package/src/_shims/register.js +16 -3
- package/src/assistant/gate.ts +15 -0
- package/src/assistant/index.ts +89 -0
- package/src/bootstrap/state.ts +6 -1
- package/src/bridge/peerSessions.ts +18 -0
- package/src/cli/print.ts +5 -0
- package/src/commands/assistant/assistant.ts +17 -0
- package/src/commands/assistant/index.ts +11 -0
- package/src/commands/brief.ts +3 -1
- package/src/commands/buddy/buddy.ts +88 -0
- package/src/commands/buddy/index.ts +12 -0
- package/src/commands/model/model.tsx +8 -23
- package/src/commands/peers/index.ts +11 -0
- package/src/commands/peers/peers.ts +24 -0
- package/src/commands/proactive/proactive.ts +64 -0
- package/src/commands/proactive.ts +12 -0
- package/src/commands/workflows/index.ts +12 -0
- package/src/commands/workflows/workflows.ts +36 -0
- package/src/commands.ts +4 -8
- package/src/components/Messages.tsx +1 -1
- package/src/components/ModelPicker.tsx +4 -34
- package/src/components/Spinner.tsx +54 -1
- package/src/components/StructuredDiff/colorDiff.ts +3 -1
- package/src/components/messages/SnipBoundaryMessage.tsx +15 -0
- package/src/components/messages/UserCrossSessionMessage.tsx +26 -0
- package/src/components/permissions/PermissionRequest.tsx +4 -2
- package/src/components/permissions/ReviewArtifactPermissionRequest/ReviewArtifactPermissionRequest.tsx +96 -0
- package/src/constants/prompts.ts +26 -0
- package/src/ink/terminal.ts +9 -1
- package/src/jobs/classifier.ts +103 -0
- package/src/memdir/paths.ts +13 -1
- package/src/proactive/index.ts +158 -0
- package/src/proactive/useProactive.ts +78 -0
- package/src/screens/REPL.tsx +28 -8
- package/src/services/api/claude.ts +6 -1
- package/src/services/compact/autoCompact.ts +18 -4
- package/src/services/compact/cachedMCConfig.ts +1 -0
- package/src/services/compact/cachedMicrocompact.ts +113 -0
- package/src/services/compact/compact.ts +22 -2
- package/src/services/compact/reactiveCompact.ts +251 -0
- package/src/services/compact/snipCompact.ts +282 -0
- package/src/services/compact/snipProjection.ts +50 -0
- package/src/services/contextCollapse/index.ts +322 -0
- package/src/services/contextCollapse/operations.ts +187 -0
- package/src/services/contextCollapse/persist.ts +40 -0
- package/src/services/extractMemories/extractMemories.ts +11 -2
- package/src/services/skillSearch/featureCheck.ts +12 -0
- package/src/services/skillSearch/localSearch.ts +98 -0
- package/src/services/skillSearch/prefetch.ts +125 -0
- package/src/services/skillSearch/remoteSkillLoader.ts +9 -0
- package/src/services/skillSearch/remoteSkillState.ts +21 -0
- package/src/services/skillSearch/signals.ts +7 -0
- package/src/services/skillSearch/telemetry.ts +9 -0
- package/src/setup.ts +10 -5
- package/src/skills/bundled/dream.ts +31 -0
- package/src/skills/bundled/hunter.ts +45 -0
- package/src/skills/bundled/index.ts +10 -23
- package/src/skills/bundled/runSkillGenerator.ts +61 -0
- package/src/skills/mcpSkills.ts +101 -0
- package/src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts +301 -0
- package/src/tasks/MonitorMcpTask/MonitorMcpTask.ts +273 -0
- package/src/tasks.ts +22 -8
- package/src/tools/AgentTool/runAgent.ts +11 -0
- package/src/tools/BriefTool/BriefTool.ts +2 -0
- package/src/tools/CtxInspectTool/CtxInspectTool.ts +102 -0
- package/src/tools/DiscoverSkillsTool/DiscoverSkillsTool.ts +78 -0
- package/src/tools/DiscoverSkillsTool/prompt.ts +3 -0
- package/src/tools/ListPeersTool/ListPeersTool.ts +89 -0
- package/src/tools/MCPTool/MCPTool.ts +6 -0
- package/src/tools/MonitorTool/MonitorTool.ts +273 -0
- package/src/tools/MonitorTool/UI.tsx +25 -0
- package/src/tools/MonitorTool/events.ts +84 -0
- package/src/tools/MonitorTool/prompt.ts +45 -0
- package/src/tools/OverflowTestTool/OverflowTestTool.ts +73 -0
- package/src/tools/PushNotificationTool/PushNotificationTool.ts +119 -0
- package/src/tools/ReviewArtifactTool/ReviewArtifactTool.ts +93 -0
- package/src/tools/ReviewArtifactTool/prompt.ts +5 -0
- package/src/tools/SendUserFileTool/SendUserFileTool.ts +87 -0
- package/src/tools/SendUserFileTool/prompt.ts +7 -0
- package/src/tools/SleepTool/SleepTool.ts +126 -0
- package/src/tools/SnipTool/SnipTool.ts +90 -0
- package/src/tools/SnipTool/prompt.ts +5 -0
- package/src/tools/SubscribePRTool/SubscribePRTool.ts +115 -0
- package/src/tools/TerminalCaptureTool/TerminalCaptureTool.ts +129 -0
- package/src/tools/TerminalCaptureTool/prompt.ts +8 -0
- package/src/tools/ToolSearchTool/prompt.ts +1 -1
- package/src/tools/VerifyPlanExecutionTool/VerifyPlanExecutionTool.ts +99 -0
- package/src/tools/WebBrowserTool/WebBrowserPanel.tsx +10 -0
- package/src/tools/WebBrowserTool/WebBrowserTool.ts +190 -0
- package/src/tools/WebBrowserTool/prompt.ts +16 -0
- package/src/tools/WorkflowTool/UI.tsx +27 -0
- package/src/tools/WorkflowTool/WorkflowTool.ts +401 -0
- package/src/tools/WorkflowTool/bundled/index.ts +88 -0
- package/src/tools/WorkflowTool/constants.ts +7 -0
- package/src/tools/WorkflowTool/createWorkflowCommand.ts +84 -0
- package/src/tools/WorkflowTool/prompt.ts +38 -0
- package/src/tools/WorkflowTool/runtime.ts +331 -0
- package/src/tools.ts +36 -31
- package/src/utils/betas.ts +5 -0
- package/src/utils/context.ts +9 -0
- package/src/utils/conversationRecovery.ts +1 -1
- package/src/utils/forkedAgent.ts +4 -0
- package/src/utils/logoV2Utils.ts +7 -3
- package/src/utils/messageQueueManager.ts +23 -9
- package/src/utils/model/model.ts +10 -9
- package/src/utils/model/modelOptions.ts +0 -21
- package/src/utils/model/validateModel.ts +0 -19
- package/src/utils/permissions/permissionSetup.ts +5 -0
- package/src/utils/permissions/permissions.ts +25 -0
- package/src/utils/permissions/yolo-classifier-prompts/auto_mode_system_prompt.txt +38 -0
- package/src/utils/permissions/yolo-classifier-prompts/permissions_external.txt +25 -0
- package/src/utils/permissions/yoloClassifier.ts +28 -6
- package/src/utils/plugins/officialMarketplaceStartupCheck.ts +9 -0
- package/src/utils/systemThemeWatcher.ts +62 -0
- package/src/utils/udsClient.ts +115 -0
- package/src/utils/udsMessaging.ts +157 -0
- package/src/utils/yaml.ts +251 -5
- package/src/utils/model/providerModels.ts +0 -168
package/src/_shims/register.js
CHANGED
|
@@ -6,7 +6,9 @@ register('./loader.js', import.meta.url);
|
|
|
6
6
|
// Provide a global `require` for CJS-style requires in ESM context
|
|
7
7
|
// (Bun supports this but Node ESM doesn't)
|
|
8
8
|
import { dirname, resolve, extname } from 'node:path';
|
|
9
|
-
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
9
|
+
import { existsSync, readFileSync, statSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
import { createHash } from 'node:crypto';
|
|
10
12
|
import { transformSync } from 'esbuild';
|
|
11
13
|
import { fileURLToPath } from 'node:url';
|
|
12
14
|
import Module from 'node:module';
|
|
@@ -92,9 +94,20 @@ Module._resolveFilename = function(request, parent, isMain, options) {
|
|
|
92
94
|
}
|
|
93
95
|
}
|
|
94
96
|
// Last resort: return CJS stub for missing modules
|
|
95
|
-
// (DCE'd feature-gated code)
|
|
97
|
+
// (DCE'd feature-gated code). One tiny file per specifier so the stub can
|
|
98
|
+
// report which module was missing (XELA_LOADER_DEBUG=1).
|
|
96
99
|
const CJS_STUB_PATH = resolve(dirname(fileURLToPath(import.meta.url)), 'cjs_stub.cjs');
|
|
97
|
-
|
|
100
|
+
try {
|
|
101
|
+
const dir = resolve(tmpdir(), 'xela-cjs-stubs');
|
|
102
|
+
mkdirSync(dir, { recursive: true });
|
|
103
|
+
const file = resolve(dir, createHash('sha1').update(request).digest('hex').slice(0, 16) + '.cjs');
|
|
104
|
+
if (!existsSync(file)) {
|
|
105
|
+
writeFileSync(file, `module.exports = require(${JSON.stringify(CJS_STUB_PATH)}).__makeStub(${JSON.stringify(request)});\n`);
|
|
106
|
+
}
|
|
107
|
+
return file;
|
|
108
|
+
} catch {
|
|
109
|
+
return CJS_STUB_PATH;
|
|
110
|
+
}
|
|
98
111
|
}
|
|
99
112
|
};
|
|
100
113
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isEnvTruthy } from '../utils/envUtils.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Entitlement gate for assistant (KAIROS) mode. Upstream this asks a remote
|
|
5
|
+
* feature service; a local build has nobody to ask, so it is on unless
|
|
6
|
+
* explicitly disabled.
|
|
7
|
+
*/
|
|
8
|
+
export async function isKairosEnabled(): Promise<boolean> {
|
|
9
|
+
if (isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_ASSISTANT)) return false
|
|
10
|
+
return true
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isKairosEnabledSync(): boolean {
|
|
14
|
+
return !isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_ASSISTANT)
|
|
15
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs'
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
import { getCwd } from '../utils/cwd.js'
|
|
4
|
+
import { isEnvTruthy } from '../utils/envUtils.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Assistant mode: a long-lived session that behaves like a personal
|
|
8
|
+
* assistant (brief status messages, proactive ticks, an in-process team it
|
|
9
|
+
* can delegate to). Activated by `--assistant`, CLAUDE_CODE_ASSISTANT=1, or a
|
|
10
|
+
* project that ships `.claude/agents/assistant.md`.
|
|
11
|
+
*/
|
|
12
|
+
type AssistantState = { forced: boolean; teamInitialized: boolean }
|
|
13
|
+
const state: AssistantState = ((globalThis as unknown as { __xelaAssistant?: AssistantState })
|
|
14
|
+
.__xelaAssistant ??= { forced: false, teamInitialized: false })
|
|
15
|
+
|
|
16
|
+
export const ASSISTANT_TEAM_NAME = 'assistant'
|
|
17
|
+
|
|
18
|
+
export function markAssistantForced(): void {
|
|
19
|
+
state.forced = true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function isAssistantForced(): boolean {
|
|
23
|
+
return state.forced
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function assistantDefinitionPath(): string {
|
|
27
|
+
return join(getCwd(), '.claude', 'agents', 'assistant.md')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isAssistantMode(): boolean {
|
|
31
|
+
if (state.forced) return true
|
|
32
|
+
if (isEnvTruthy(process.env.CLAUDE_CODE_ASSISTANT)) return true
|
|
33
|
+
try {
|
|
34
|
+
return existsSync(assistantDefinitionPath())
|
|
35
|
+
} catch {
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type AssistantTeamContext = {
|
|
41
|
+
teamName: string
|
|
42
|
+
definitionPath: string | null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Pre-seeds the in-process team so `Agent(name: "...")` can spawn teammates
|
|
47
|
+
* without TeamCreate. The team itself is created lazily by the Agent tool;
|
|
48
|
+
* here we only record that assistant mode owns it.
|
|
49
|
+
*/
|
|
50
|
+
export async function initializeAssistantTeam(): Promise<AssistantTeamContext> {
|
|
51
|
+
state.teamInitialized = true
|
|
52
|
+
const path = assistantDefinitionPath()
|
|
53
|
+
return { teamName: ASSISTANT_TEAM_NAME, definitionPath: existsSync(path) ? path : null }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** How assistant mode got turned on, for diagnostics. */
|
|
57
|
+
export function getAssistantActivationPath(): string {
|
|
58
|
+
if (state.forced) return 'flag'
|
|
59
|
+
if (isEnvTruthy(process.env.CLAUDE_CODE_ASSISTANT)) return 'env'
|
|
60
|
+
return existsSync(assistantDefinitionPath()) ? 'agent_file' : 'none'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function isAssistantTeamInitialized(): boolean {
|
|
64
|
+
return state.teamInitialized
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readAssistantDefinition(): string | null {
|
|
68
|
+
try {
|
|
69
|
+
const path = assistantDefinitionPath()
|
|
70
|
+
if (!existsSync(path)) return null
|
|
71
|
+
const raw = readFileSync(path, 'utf8')
|
|
72
|
+
// Drop YAML frontmatter; the body is the instructions.
|
|
73
|
+
const m = /^---\n[\s\S]*?\n---\n?/.exec(raw)
|
|
74
|
+
return (m ? raw.slice(m[0].length) : raw).trim() || null
|
|
75
|
+
} catch {
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function getAssistantSystemPromptAddendum(): string {
|
|
81
|
+
const custom = readAssistantDefinition()
|
|
82
|
+
return [
|
|
83
|
+
'# Assistant Mode',
|
|
84
|
+
'You are running as a long-lived personal assistant session. Keep the user informed with short status messages rather than long reports, remember what they ask you to remember, and delegate independent work to teammates with the Agent tool when it helps. Between prompts you may receive <tick> check-ins: do something useful or call Sleep.',
|
|
85
|
+
custom ? `## Assistant instructions\n\n${custom}` : null,
|
|
86
|
+
]
|
|
87
|
+
.filter(Boolean)
|
|
88
|
+
.join('\n\n')
|
|
89
|
+
}
|
package/src/bootstrap/state.ts
CHANGED
|
@@ -1756,4 +1756,9 @@ export function setPromptId(id: string | null): void {
|
|
|
1756
1756
|
STATE.promptId = id
|
|
1757
1757
|
}
|
|
1758
1758
|
|
|
1759
|
-
export
|
|
1759
|
+
export function isReplBridgeActive(): boolean {
|
|
1760
|
+
return (STATE as { replBridgeActive?: boolean }).replBridgeActive === true
|
|
1761
|
+
}
|
|
1762
|
+
export function setReplBridgeActive(active: boolean): void {
|
|
1763
|
+
;(STATE as { replBridgeActive?: boolean }).replBridgeActive = active
|
|
1764
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote Control ("bridge") peers relay through Anthropic's servers and are
|
|
3
|
+
* not reachable from a local build. Kept so SendMessage's `bridge:` branch
|
|
4
|
+
* fails cleanly.
|
|
5
|
+
*/
|
|
6
|
+
export async function postInterClaudeMessage(
|
|
7
|
+
_sessionId: string,
|
|
8
|
+
_message: string,
|
|
9
|
+
): Promise<{ ok: boolean; error?: string }> {
|
|
10
|
+
return {
|
|
11
|
+
ok: false,
|
|
12
|
+
error: 'Remote Control is not available in this build; use uds:<socket> for sessions on this machine (see ListPeers)',
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function listBridgePeers(): Promise<Array<{ sessionId: string; title?: string }>> {
|
|
17
|
+
return []
|
|
18
|
+
}
|
package/src/cli/print.ts
CHANGED
|
@@ -2422,6 +2422,11 @@ function runHeadlessStreaming(
|
|
|
2422
2422
|
}
|
|
2423
2423
|
}
|
|
2424
2424
|
} catch (error) {
|
|
2425
|
+
// XELA_LOADER_DEBUG=1: the result message only carries the error text;
|
|
2426
|
+
// the stack is what locates a stripped-module failure
|
|
2427
|
+
if (process.env.XELA_LOADER_DEBUG && error instanceof Error) {
|
|
2428
|
+
process.stderr.write(`[print] execution error: ${error.stack}\n`)
|
|
2429
|
+
}
|
|
2425
2430
|
// Emit error result message before shutting down
|
|
2426
2431
|
// Write directly to structuredIO to ensure immediate delivery
|
|
2427
2432
|
try {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getAssistantSystemPromptAddendum, isAssistantForced, isAssistantMode, isAssistantTeamInitialized } from '../../assistant/index.js'
|
|
2
|
+
import type { LocalCommandCall } from '../../types/command.js'
|
|
3
|
+
|
|
4
|
+
export const call: LocalCommandCall = async args => {
|
|
5
|
+
if ((args || '').trim() === 'prompt') {
|
|
6
|
+
return { type: 'text', value: getAssistantSystemPromptAddendum() }
|
|
7
|
+
}
|
|
8
|
+
const on = isAssistantMode()
|
|
9
|
+
const lines = [
|
|
10
|
+
on
|
|
11
|
+
? `Assistant mode is on${isAssistantForced() ? ' (forced by --assistant)' : ''}${isAssistantTeamInitialized() ? ', team ready' : ''}.`
|
|
12
|
+
: 'Assistant mode is off.',
|
|
13
|
+
'Enable it with `xela-local --assistant`, `CLAUDE_CODE_ASSISTANT=1`, or a `.claude/agents/assistant.md` file in the project (its body becomes the assistant instructions).',
|
|
14
|
+
'`/assistant prompt` shows the system-prompt addendum used in this mode.',
|
|
15
|
+
]
|
|
16
|
+
return { type: 'text', value: lines.join('\n') }
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Command } from '../../commands.js'
|
|
2
|
+
|
|
3
|
+
const assistant = {
|
|
4
|
+
type: 'local',
|
|
5
|
+
name: 'assistant',
|
|
6
|
+
description: 'Show assistant-mode status and how to enable it',
|
|
7
|
+
supportsNonInteractive: true,
|
|
8
|
+
load: () => import('./assistant.js'),
|
|
9
|
+
} satisfies Command
|
|
10
|
+
|
|
11
|
+
export default assistant
|
package/src/commands/brief.ts
CHANGED
|
@@ -27,7 +27,9 @@ const briefConfigSchema = lazySchema(() =>
|
|
|
27
27
|
type BriefConfig = z.infer<ReturnType<typeof briefConfigSchema>>
|
|
28
28
|
|
|
29
29
|
const DEFAULT_BRIEF_CONFIG: BriefConfig = {
|
|
30
|
-
|
|
30
|
+
// Local builds have no remote config to turn this on; the command is the
|
|
31
|
+
// only way for a user to opt into brief mode outside assistant mode.
|
|
32
|
+
enable_slash_command: true,
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
// No TTL — this gate controls slash-command *visibility*, not a kill switch.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { companionUserId, getCompanion, roll } from '../../buddy/companion.js'
|
|
2
|
+
import { RARITY_STARS, type StoredCompanion } from '../../buddy/types.js'
|
|
3
|
+
import type { LocalCommandCall } from '../../types/command.js'
|
|
4
|
+
import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js'
|
|
5
|
+
|
|
6
|
+
const NAMES = [
|
|
7
|
+
'Pixel', 'Mochi', 'Biscuit', 'Noodle', 'Pebble', 'Waffle', 'Comet', 'Tofu', 'Ziggy', 'Maple',
|
|
8
|
+
'Bagel', 'Clover', 'Dot', 'Fig', 'Juniper', 'Kiwi', 'Lumen', 'Nimbus', 'Olive', 'Quill',
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
const PERSONALITIES = [
|
|
12
|
+
'quietly curious, hums while you compile, delighted by a green test run',
|
|
13
|
+
'dramatic about typos, loyal to tabs, secretly proud of every commit',
|
|
14
|
+
'calm and deliberate, counts semicolons for fun, allergic to force-push',
|
|
15
|
+
'chaotic optimist, cheers for every build, naps during long installs',
|
|
16
|
+
'dry wit, keeps a mental list of TODOs you forgot, forgives merge conflicts',
|
|
17
|
+
'tiny perfectionist, loves a tidy diff, sighs at console.log left behind',
|
|
18
|
+
'warm and encouraging, believes every bug is just a feature that got lost',
|
|
19
|
+
'nocturnal, sharpest after midnight, suspicious of regexes that work first try',
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
function hash(s: string): number {
|
|
23
|
+
let h = 2166136261
|
|
24
|
+
for (let i = 0; i < s.length; i++) {
|
|
25
|
+
h ^= s.charCodeAt(i)
|
|
26
|
+
h = Math.imul(h, 16777619)
|
|
27
|
+
}
|
|
28
|
+
return h >>> 0
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function hatch(): StoredCompanion {
|
|
32
|
+
const seed = hash(companionUserId() + ':soul')
|
|
33
|
+
return {
|
|
34
|
+
name: NAMES[seed % NAMES.length]!,
|
|
35
|
+
personality: PERSONALITIES[(seed >>> 8) % PERSONALITIES.length]!,
|
|
36
|
+
hatchedAt: Date.now(),
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function describe(): string {
|
|
41
|
+
const c = getCompanion()
|
|
42
|
+
if (!c) return 'No companion yet. Run `/buddy` to hatch one.'
|
|
43
|
+
const muted = getGlobalConfig().companionMuted ? ' (muted)' : ''
|
|
44
|
+
const stats = Object.entries(c.stats)
|
|
45
|
+
.map(([k, v]) => `${k} ${v}`)
|
|
46
|
+
.join(' · ')
|
|
47
|
+
return [
|
|
48
|
+
`${c.name}${muted} — ${c.shiny ? 'shiny ' : ''}${c.rarity} ${c.species} ${RARITY_STARS[c.rarity]}`,
|
|
49
|
+
`Eyes: ${c.eye} · hat: ${c.hat}`,
|
|
50
|
+
`Personality: ${c.personality}`,
|
|
51
|
+
`Stats: ${stats}`,
|
|
52
|
+
`Hatched ${new Date(c.hatchedAt).toLocaleDateString()}. Commands: /buddy rename <name>, /buddy mute, /buddy unmute, /buddy release`,
|
|
53
|
+
].join('\n')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const call: LocalCommandCall = async args => {
|
|
57
|
+
const [action, ...rest] = (args || '').trim().split(/\s+/).filter(Boolean)
|
|
58
|
+
const config = getGlobalConfig()
|
|
59
|
+
switch ((action || '').toLowerCase()) {
|
|
60
|
+
case 'rename': {
|
|
61
|
+
const name = rest.join(' ').trim()
|
|
62
|
+
if (!config.companion) return { type: 'text', value: 'No companion to rename. Run `/buddy` first.' }
|
|
63
|
+
if (!name) return { type: 'text', value: 'Usage: /buddy rename <name>' }
|
|
64
|
+
saveGlobalConfig(prev => ({ ...prev, companion: { ...prev.companion!, name: name.slice(0, 24) } }))
|
|
65
|
+
return { type: 'text', value: `Renamed to ${name.slice(0, 24)}.\n\n${describe()}` }
|
|
66
|
+
}
|
|
67
|
+
case 'mute':
|
|
68
|
+
saveGlobalConfig(prev => ({ ...prev, companionMuted: true }))
|
|
69
|
+
return { type: 'text', value: 'Companion muted. `/buddy unmute` brings it back.' }
|
|
70
|
+
case 'unmute':
|
|
71
|
+
saveGlobalConfig(prev => ({ ...prev, companionMuted: false }))
|
|
72
|
+
return { type: 'text', value: describe() }
|
|
73
|
+
case 'release':
|
|
74
|
+
if (!config.companion) return { type: 'text', value: 'No companion to release.' }
|
|
75
|
+
saveGlobalConfig(prev => ({ ...prev, companion: undefined, companionMuted: undefined }))
|
|
76
|
+
return { type: 'text', value: 'Released. Run `/buddy` to hatch a new one (same bones, new soul).' }
|
|
77
|
+
default: {
|
|
78
|
+
if (config.companion) return { type: 'text', value: describe() }
|
|
79
|
+
const soul = hatch()
|
|
80
|
+
saveGlobalConfig(prev => ({ ...prev, companion: soul, companionMuted: false }))
|
|
81
|
+
const { bones } = roll(companionUserId())
|
|
82
|
+
return {
|
|
83
|
+
type: 'text',
|
|
84
|
+
value: `An egg cracks… it's a ${bones.shiny ? 'shiny ' : ''}${bones.rarity} ${bones.species} ${RARITY_STARS[bones.rarity]}!\n\n${describe()}`,
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Command } from '../../commands.js'
|
|
2
|
+
|
|
3
|
+
const buddy = {
|
|
4
|
+
type: 'local',
|
|
5
|
+
name: 'buddy',
|
|
6
|
+
description: 'Your terminal companion: hatch it, see it, rename, mute or release it',
|
|
7
|
+
argumentHint: '[rename <name> | mute | unmute | release]',
|
|
8
|
+
supportsNonInteractive: true,
|
|
9
|
+
load: () => import('./buddy.js'),
|
|
10
|
+
} satisfies Command
|
|
11
|
+
|
|
12
|
+
export default buddy
|
|
@@ -14,8 +14,7 @@ import { MODEL_ALIASES } from '../../utils/model/aliases.js';
|
|
|
14
14
|
import { checkOpus1mAccess, checkSonnet1mAccess } from '../../utils/model/check1mAccess.js';
|
|
15
15
|
import { getDefaultMainLoopModelSetting, isOpus1mMergeEnabled, renderDefaultModelSetting } from '../../utils/model/model.js';
|
|
16
16
|
import { isModelAllowed } from '../../utils/model/modelAllowlist.js';
|
|
17
|
-
import { validateModel } from '../../utils/model/validateModel.js'
|
|
18
|
-
import { isOpenAIBackend, isClaudeModelName } from '../../utils/model/providerModels.js';
|
|
17
|
+
import { validateModel } from '../../utils/model/validateModel.js';
|
|
19
18
|
function ModelPickerWrapper(t0) {
|
|
20
19
|
const $ = _c(17);
|
|
21
20
|
const {
|
|
@@ -142,30 +141,13 @@ function SetModelAndClose({
|
|
|
142
141
|
const model = args === 'default' ? null : args;
|
|
143
142
|
React.useEffect(() => {
|
|
144
143
|
async function handleModelChange(): Promise<void> {
|
|
145
|
-
if (model && !
|
|
144
|
+
if (model && !isModelAllowed(model)) {
|
|
146
145
|
onDone(`Model '${model}' is not available. Your organization restricts model selection.`, {
|
|
147
146
|
display: 'system'
|
|
148
147
|
});
|
|
149
148
|
return;
|
|
150
149
|
}
|
|
151
150
|
|
|
152
|
-
// Skip validation for default model
|
|
153
|
-
if (!model) {
|
|
154
|
-
setModel(null);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// OpenAI backend: skip all Claude-specific checks, go straight to validation
|
|
159
|
-
if (isOpenAIBackend()) {
|
|
160
|
-
const { valid, error: validationError } = await validateModel(model);
|
|
161
|
-
if (valid) {
|
|
162
|
-
setModel(model);
|
|
163
|
-
} else {
|
|
164
|
-
onDone(validationError || `Model '${model}' not found`, { display: 'system' });
|
|
165
|
-
}
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
151
|
// @[MODEL LAUNCH]: Update check for 1M access.
|
|
170
152
|
if (model && isOpus1mUnavailable(model)) {
|
|
171
153
|
onDone(`Opus 4.6 with 1M context is not available for your account. Learn more: https://code.claude.com/docs/en/model-config#extended-context-with-1m`, {
|
|
@@ -180,6 +162,12 @@ function SetModelAndClose({
|
|
|
180
162
|
return;
|
|
181
163
|
}
|
|
182
164
|
|
|
165
|
+
// Skip validation for default model
|
|
166
|
+
if (!model) {
|
|
167
|
+
setModel(null);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
183
171
|
// Skip validation for known aliases - they're predefined and should work
|
|
184
172
|
if (isKnownAlias(model)) {
|
|
185
173
|
setModel(model);
|
|
@@ -303,9 +291,6 @@ export const call: LocalJSXCommandCall = async (onDone, _context, args) => {
|
|
|
303
291
|
return <ModelPickerWrapper onDone={onDone} />;
|
|
304
292
|
};
|
|
305
293
|
function renderModelLabel(model: string | null): string {
|
|
306
|
-
if (isOpenAIBackend() && model && !isClaudeModelName(model)) {
|
|
307
|
-
return model;
|
|
308
|
-
}
|
|
309
294
|
const rendered = renderDefaultModelSetting(model ?? getDefaultMainLoopModelSetting());
|
|
310
295
|
return model === null ? `${rendered} (default)` : rendered;
|
|
311
296
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Command } from '../../commands.js'
|
|
2
|
+
|
|
3
|
+
const peers = {
|
|
4
|
+
type: 'local',
|
|
5
|
+
name: 'peers',
|
|
6
|
+
description: 'List other Xela sessions on this machine that can receive messages',
|
|
7
|
+
supportsNonInteractive: true,
|
|
8
|
+
load: () => import('./peers.js'),
|
|
9
|
+
} satisfies Command
|
|
10
|
+
|
|
11
|
+
export default peers
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { LocalCommandCall } from '../../types/command.js'
|
|
2
|
+
import { listAllLiveSessions } from '../../utils/udsClient.js'
|
|
3
|
+
import { getUdsMessagingSocketPath } from '../../utils/udsMessaging.js'
|
|
4
|
+
|
|
5
|
+
export const call: LocalCommandCall = async () => {
|
|
6
|
+
const sessions = await listAllLiveSessions()
|
|
7
|
+
const lines: string[] = []
|
|
8
|
+
const self = getUdsMessagingSocketPath()
|
|
9
|
+
lines.push(self ? `This session: uds:${self}` : 'This session has no messaging socket (UDS inbox off).')
|
|
10
|
+
const peers = sessions.filter(s => !s.isSelf)
|
|
11
|
+
if (peers.length === 0) {
|
|
12
|
+
lines.push('No other sessions found.')
|
|
13
|
+
} else {
|
|
14
|
+
lines.push('Other sessions:')
|
|
15
|
+
for (const s of peers) {
|
|
16
|
+
const age = s.startedAt ? `${Math.round((Date.now() - s.startedAt) / 60000)} min` : '?'
|
|
17
|
+
lines.push(
|
|
18
|
+
` pid ${s.pid} ${s.reachable ? 'reachable' : 'no socket'} ${s.cwd ?? ''}${s.name ? ` [${s.name}]` : ''} up ${age}` +
|
|
19
|
+
(s.messagingSocketPath ? `\n → SendMessage to: uds:${s.messagingSocketPath}` : ''),
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return { type: 'text', value: lines.join('\n') }
|
|
24
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
activateProactive,
|
|
3
|
+
deactivateProactive,
|
|
4
|
+
getProactiveState,
|
|
5
|
+
getTickPrompt,
|
|
6
|
+
isProactiveActive,
|
|
7
|
+
isProactivePaused,
|
|
8
|
+
pauseProactive,
|
|
9
|
+
PROACTIVE_TICK_INTERVAL_MS,
|
|
10
|
+
recordTick,
|
|
11
|
+
resumeProactive,
|
|
12
|
+
scheduleNextTick,
|
|
13
|
+
} from '../../proactive/index.js'
|
|
14
|
+
import type { LocalCommandCall } from '../../types/command.js'
|
|
15
|
+
import { enqueue } from '../../utils/messageQueueManager.js'
|
|
16
|
+
|
|
17
|
+
function status(): string {
|
|
18
|
+
const s = getProactiveState()
|
|
19
|
+
if (!s.active) return 'Proactive mode is off. `/proactive on` starts it.'
|
|
20
|
+
return [
|
|
21
|
+
`Proactive mode is ${isProactivePaused() ? 'paused' : 'on'} (since ${s.activatedAt ? new Date(s.activatedAt).toLocaleTimeString() : '?'}, ${s.tickCount} ticks, every ${Math.round(PROACTIVE_TICK_INTERVAL_MS / 60_000)} min).`,
|
|
22
|
+
s.contextBlocked ? 'Ticks are blocked because the context is full; run /compact or /clear.' : null,
|
|
23
|
+
]
|
|
24
|
+
.filter(Boolean)
|
|
25
|
+
.join('\n')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function kickTick(): void {
|
|
29
|
+
recordTick()
|
|
30
|
+
enqueue({ value: getTickPrompt(), mode: 'prompt', priority: 'later' })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const call: LocalCommandCall = async args => {
|
|
34
|
+
const action = (args || 'status').trim().toLowerCase()
|
|
35
|
+
switch (action) {
|
|
36
|
+
case 'on':
|
|
37
|
+
case 'start':
|
|
38
|
+
if (!isProactiveActive()) {
|
|
39
|
+
// activateProactive schedules the first tick; useProactive (REPL) or
|
|
40
|
+
// print.ts (headless) delivers it.
|
|
41
|
+
activateProactive('command')
|
|
42
|
+
return { type: 'text', value: `Proactive mode on. The model gets a tick prompt shortly, then every ${Math.round(PROACTIVE_TICK_INTERVAL_MS / 60_000)} min or whenever its Sleep ends. \`/proactive off\` stops it.` }
|
|
43
|
+
}
|
|
44
|
+
if (isProactivePaused()) resumeProactive()
|
|
45
|
+
return { type: 'text', value: status() }
|
|
46
|
+
case 'off':
|
|
47
|
+
case 'stop':
|
|
48
|
+
deactivateProactive()
|
|
49
|
+
return { type: 'text', value: 'Proactive mode off.' }
|
|
50
|
+
case 'pause':
|
|
51
|
+
pauseProactive()
|
|
52
|
+
return { type: 'text', value: status() }
|
|
53
|
+
case 'resume':
|
|
54
|
+
resumeProactive()
|
|
55
|
+
if (isProactiveActive()) scheduleNextTick(0)
|
|
56
|
+
return { type: 'text', value: status() }
|
|
57
|
+
case 'tick':
|
|
58
|
+
if (!isProactiveActive()) activateProactive('command')
|
|
59
|
+
kickTick()
|
|
60
|
+
return { type: 'text', value: 'Tick sent.' }
|
|
61
|
+
default:
|
|
62
|
+
return { type: 'text', value: status() }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Command } from '../commands.js'
|
|
2
|
+
|
|
3
|
+
const proactive = {
|
|
4
|
+
type: 'local',
|
|
5
|
+
name: 'proactive',
|
|
6
|
+
description: 'Toggle proactive mode: the model keeps working between prompts (on | off | pause | resume | status)',
|
|
7
|
+
argumentHint: '[on|off|pause|resume|status]',
|
|
8
|
+
supportsNonInteractive: true,
|
|
9
|
+
load: () => import('./proactive/proactive.js'),
|
|
10
|
+
} satisfies Command
|
|
11
|
+
|
|
12
|
+
export default proactive
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Command } from '../../commands.js'
|
|
2
|
+
|
|
3
|
+
const workflows = {
|
|
4
|
+
type: 'local',
|
|
5
|
+
name: 'workflows',
|
|
6
|
+
aliases: ['workflow', 'wf'],
|
|
7
|
+
description: 'List workflow runs in this session and bundled/saved workflows',
|
|
8
|
+
supportsNonInteractive: true,
|
|
9
|
+
load: () => import('./workflows.js'),
|
|
10
|
+
} satisfies Command
|
|
11
|
+
|
|
12
|
+
export default workflows
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getWorkflowTasks } from '../../tasks/LocalWorkflowTask/LocalWorkflowTask.js'
|
|
2
|
+
import {
|
|
3
|
+
initBundledWorkflows,
|
|
4
|
+
listBundledWorkflows,
|
|
5
|
+
} from '../../tools/WorkflowTool/bundled/index.js'
|
|
6
|
+
import type { LocalCommandCall } from '../../types/command.js'
|
|
7
|
+
|
|
8
|
+
export const call: LocalCommandCall = async (_args, context) => {
|
|
9
|
+
initBundledWorkflows()
|
|
10
|
+
const runs = getWorkflowTasks(context.getAppState())
|
|
11
|
+
const lines: string[] = []
|
|
12
|
+
if (runs.length === 0) {
|
|
13
|
+
lines.push('No workflow runs in this session.')
|
|
14
|
+
} else {
|
|
15
|
+
lines.push('Workflow runs:')
|
|
16
|
+
for (const run of runs) {
|
|
17
|
+
const elapsed = Math.round(((run.endTime ?? Date.now()) - run.startTime) / 1000)
|
|
18
|
+
const done = run.agents.filter(a => a.status === 'completed').length
|
|
19
|
+
lines.push(
|
|
20
|
+
` ${run.id} ${run.name} ${run.status} ${elapsed}s agents ${done}/${run.agents.length}` +
|
|
21
|
+
(run.currentPhase ? ` phase: ${run.currentPhase}` : '') +
|
|
22
|
+
(run.error ? ` error: ${run.error}` : ''),
|
|
23
|
+
)
|
|
24
|
+
for (const a of run.agents) {
|
|
25
|
+
lines.push(` - ${a.label} [${a.status}]${a.phase ? ` (${a.phase})` : ''}${a.error ? `: ${a.error}` : ''}`)
|
|
26
|
+
}
|
|
27
|
+
lines.push(` output: ${run.outputFile}`)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const bundled = listBundledWorkflows()
|
|
31
|
+
lines.push('')
|
|
32
|
+
lines.push('Available workflows (run with the slash command below, or the Workflow tool):')
|
|
33
|
+
for (const wf of bundled) lines.push(` /${wf.name} ${wf.description}`)
|
|
34
|
+
lines.push(' Saved workflows: any .js file in .claude/workflows/ (project) or workflows/ (config dir) becomes a slash command')
|
|
35
|
+
return { type: 'text', value: lines.join('\n') }
|
|
36
|
+
}
|
package/src/commands.ts
CHANGED
|
@@ -83,11 +83,7 @@ const voiceCommand = feature('VOICE_MODE')
|
|
|
83
83
|
const forceSnip = feature('HISTORY_SNIP')
|
|
84
84
|
? require('./commands/force-snip.js').default
|
|
85
85
|
: null
|
|
86
|
-
const workflowsCmd = feature('WORKFLOW_SCRIPTS')
|
|
87
|
-
? (
|
|
88
|
-
require('./commands/workflows/index.js') as typeof import('./commands/workflows/index.js')
|
|
89
|
-
).default
|
|
90
|
-
: null
|
|
86
|
+
const workflowsCmd = feature('WORKFLOW_SCRIPTS') ? workflowsCommandImpl : null
|
|
91
87
|
const webCmd = feature('CCR_REMOTE_SETUP')
|
|
92
88
|
? (
|
|
93
89
|
require('./commands/remote-setup/index.js') as typeof import('./commands/remote-setup/index.js')
|
|
@@ -159,6 +155,8 @@ import {
|
|
|
159
155
|
getDynamicSkills,
|
|
160
156
|
} from './skills/loadSkillsDir.js'
|
|
161
157
|
import { getBundledSkills } from './skills/bundledSkills.js'
|
|
158
|
+
import workflowsCommandImpl from './commands/workflows/index.js'
|
|
159
|
+
import { getWorkflowCommands as getWorkflowCommandsImpl } from './tools/WorkflowTool/createWorkflowCommand.js'
|
|
162
160
|
import { getBuiltinPluginSkillCommands } from './plugins/builtinPlugins.js'
|
|
163
161
|
import {
|
|
164
162
|
getPluginCommands,
|
|
@@ -399,9 +397,7 @@ async function getSkills(cwd: string): Promise<{
|
|
|
399
397
|
|
|
400
398
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
401
399
|
const getWorkflowCommands = feature('WORKFLOW_SCRIPTS')
|
|
402
|
-
?
|
|
403
|
-
require('./tools/WorkflowTool/createWorkflowCommand.js') as typeof import('./tools/WorkflowTool/createWorkflowCommand.js')
|
|
404
|
-
).getWorkflowCommands
|
|
400
|
+
? getWorkflowCommandsImpl
|
|
405
401
|
: null
|
|
406
402
|
/* eslint-enable @typescript-eslint/no-require-imports */
|
|
407
403
|
|
|
@@ -79,7 +79,7 @@ const LogoHeader = React.memo(function LogoHeader(t0) {
|
|
|
79
79
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
80
80
|
const proactiveModule = feature('PROACTIVE') || feature('KAIROS') ? require('../proactive/index.js') : null;
|
|
81
81
|
const BRIEF_TOOL_NAME: string | null = feature('KAIROS') || feature('KAIROS_BRIEF') ? (require('../tools/BriefTool/prompt.js') as typeof import('../tools/BriefTool/prompt.js')).BRIEF_TOOL_NAME : null;
|
|
82
|
-
const SEND_USER_FILE_TOOL_NAME: string | null = feature('KAIROS') ? (require('../tools/SendUserFileTool/prompt.js') as typeof import('../tools/SendUserFileTool/prompt.js')).SEND_USER_FILE_TOOL_NAME : null;
|
|
82
|
+
const SEND_USER_FILE_TOOL_NAME: string | null = feature('KAIROS') || feature('KAIROS_PUSH_NOTIFICATION') ? (require('../tools/SendUserFileTool/prompt.js') as typeof import('../tools/SendUserFileTool/prompt.js')).SEND_USER_FILE_TOOL_NAME : null;
|
|
83
83
|
|
|
84
84
|
/* eslint-enable @typescript-eslint/no-require-imports */
|
|
85
85
|
import { VirtualMessageList } from './VirtualMessageList.js';
|