quoxcli 0.2.0 → 0.3.1
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/LICENSE +1 -1
- package/README.md +2 -0
- package/bin/quox.js +99 -126
- package/lib/api/endpoints/admin.js +26 -0
- package/lib/api/endpoints/agent-schedules.js +26 -0
- package/lib/api/endpoints/agents.js +7 -0
- package/lib/api/endpoints/assistants.js +12 -0
- package/lib/api/endpoints/away.js +15 -0
- package/lib/api/endpoints/chat.js +7 -0
- package/lib/api/endpoints/contactsq.js +2 -0
- package/lib/api/endpoints/conversations.js +14 -0
- package/lib/api/endpoints/executions.js +13 -0
- package/lib/api/endpoints/fetchAllowlist.js +27 -0
- package/lib/api/endpoints/files.js +14 -0
- package/lib/api/endpoints/hat.js +30 -0
- package/lib/api/endpoints/inbox.js +20 -0
- package/lib/api/endpoints/index.js +14 -0
- package/lib/api/endpoints/memory.js +19 -0
- package/lib/api/endpoints/mind.js +13 -0
- package/lib/api/endpoints/mon.js +6 -0
- package/lib/api/endpoints/objectives.js +6 -0
- package/lib/api/endpoints/plugins.js +18 -0
- package/lib/api/endpoints/presence.js +33 -0
- package/lib/api/endpoints/quoxplan.js +7 -0
- package/lib/api/endpoints/quoxseo.js +6 -0
- package/lib/api/endpoints/router.js +17 -0
- package/lib/api/endpoints/safety.js +7 -0
- package/lib/api/endpoints/skills.js +20 -0
- package/lib/api/endpoints/stepup.js +20 -0
- package/lib/api/endpoints/teams.js +2 -2
- package/lib/api/endpoints/triggers.js +26 -0
- package/lib/api/endpoints/v1.js +86 -0
- package/lib/api/endpoints/ward.js +7 -0
- package/lib/api/endpoints/workflows.js +7 -0
- package/lib/api/endpoints/x.js +8 -0
- package/lib/commands/actions.js +61 -0
- package/lib/commands/admin.js +16 -0
- package/lib/commands/agent.js +163 -27
- package/lib/commands/agentAttach.js +94 -6
- package/lib/commands/approvals.js +73 -1
- package/lib/commands/aside.js +24 -0
- package/lib/commands/ask.js +144 -0
- package/lib/commands/assistant.js +59 -4
- package/lib/commands/auth.js +8 -2
- package/lib/commands/backup.js +8 -4
- package/lib/commands/brain.js +105 -14
- package/lib/commands/build.js +19 -5
- package/lib/commands/chat.js +57 -2
- package/lib/commands/claude.js +34 -8
- package/lib/commands/contacts.js +49 -0
- package/lib/commands/conversation.js +35 -4
- package/lib/commands/dashboard.js +43 -0
- package/lib/commands/department.js +28 -7
- package/lib/commands/devResident.js +15 -3
- package/lib/commands/devSupervisor.js +13 -1
- package/lib/commands/discord.js +75 -39
- package/lib/commands/done.js +98 -28
- package/lib/commands/engagement.js +17 -6
- package/lib/commands/fetchAllowlist.js +71 -0
- package/lib/commands/file.js +38 -1
- package/lib/commands/fleet.js +311 -4
- package/lib/commands/governance.js +12 -3
- package/lib/commands/gripe.js +44 -13
- package/lib/commands/hat.js +178 -9
- package/lib/commands/helpers.js +18 -1
- package/lib/commands/hyperion.js +58 -0
- package/lib/commands/inbox.js +74 -23
- package/lib/commands/licence.js +14 -155
- package/lib/commands/lucid.js +10 -4
- package/lib/commands/memory.js +85 -8
- package/lib/commands/mind.js +71 -0
- package/lib/commands/mon.js +69 -2
- package/lib/commands/monitoring.js +1 -1
- package/lib/commands/motion.js +2 -2
- package/lib/commands/nucleus.js +48 -17
- package/lib/commands/objective.js +11 -0
- package/lib/commands/offload.js +89 -0
- package/lib/commands/onboard.js +2 -1
- package/lib/commands/org.js +11 -1
- package/lib/commands/peek.js +28 -0
- package/lib/commands/plan.js +38 -0
- package/lib/commands/plugin.js +110 -0
- package/lib/commands/presence.js +89 -0
- package/lib/commands/provider.js +159 -0
- package/lib/commands/qlarity.js +19 -3
- package/lib/commands/quoxchat.js +13 -4
- package/lib/commands/quoxseo.js +38 -0
- package/lib/commands/reach.js +2 -1
- package/lib/commands/router.js +77 -0
- package/lib/commands/rows.js +358 -0
- package/lib/commands/run.js +64 -9
- package/lib/commands/server.js +292 -0
- package/lib/commands/setup.js +33 -7
- package/lib/commands/skills.js +48 -5
- package/lib/commands/slot.js +260 -7
- package/lib/commands/status.js +1 -1
- package/lib/commands/team.js +3 -1
- package/lib/commands/tips.js +23 -0
- package/lib/commands/tool.js +133 -45
- package/lib/commands/trace.js +12 -7
- package/lib/commands/trigger.js +95 -0
- package/lib/commands/update.js +46 -0
- package/lib/commands/vault.js +31 -8
- package/lib/commands/vm.js +237 -0
- package/lib/commands/voice.js +34 -3
- package/lib/commands/volt.js +45 -3
- package/lib/commands/walkthrough.js +32 -0
- package/lib/commands/watch.js +73 -21
- package/lib/commands/welcome.js +14 -0
- package/lib/commands/workflow.js +189 -32
- package/lib/commands/x.js +13 -4
- package/lib/core/actingAs.js +2 -2
- package/lib/core/activeSummary.js +186 -0
- package/lib/core/aside.js +54 -0
- package/lib/core/askDecision.js +60 -0
- package/lib/core/auth.js +164 -13
- package/lib/core/callerSource.js +1 -1
- package/lib/core/cliBuild.js +62 -0
- package/lib/core/client.js +144 -35
- package/lib/core/commandRegistrars.js +121 -0
- package/lib/core/config.js +142 -24
- package/lib/core/customHats.js +168 -0
- package/lib/core/doneEngineFetch.js +8 -0
- package/lib/core/errors.js +329 -20
- package/lib/core/global-flags.js +105 -5
- package/lib/core/hyperion.js +102 -0
- package/lib/core/inboxResolution.js +65 -0
- package/lib/core/lazyCommands.js +4 -1
- package/lib/core/offload.js +504 -0
- package/lib/core/org-override.js +1 -1
- package/lib/core/orgResolve.js +70 -0
- package/lib/core/output.js +4 -4
- package/lib/core/peek.js +126 -0
- package/lib/core/profileUsers.js +51 -0
- package/lib/core/promptCancel.js +18 -0
- package/lib/core/repoPath.js +11 -0
- package/lib/core/residentSeed.js +3 -1
- package/lib/core/rootOptions.js +39 -0
- package/lib/core/router.js +168 -0
- package/lib/core/selfUpdate.js +188 -0
- package/lib/core/sessionsOverview.js +64 -0
- package/lib/core/slotKeeper.js +248 -3
- package/lib/core/slotWake.js +120 -0
- package/lib/core/supervisorEngine.js +71 -2
- package/lib/core/version.js +1 -1
- package/lib/core/vm.js +361 -0
- package/lib/core/vmTui.js +86 -0
- package/lib/core/walkthrough.js +35 -0
- package/lib/hyperion/deck.js +29 -0
- package/lib/lucid/LucidCard.jsx +68 -0
- package/lib/lucid/LucidIntro.jsx +45 -58
- package/lib/lucid/cards.js +86 -0
- package/lib/lucid/markdown.js +81 -0
- package/lib/lucid/motion.js +56 -0
- package/lib/quoxon/activity.js +14 -2
- package/lib/quoxon/aocl.js +117 -0
- package/lib/quoxon/api.js +504 -25
- package/lib/quoxon/app.js +3000 -230
- package/lib/quoxon/approvalFacts.js +94 -0
- package/lib/quoxon/approvalsView.js +325 -0
- package/lib/quoxon/asideView.js +40 -0
- package/lib/quoxon/ask.js +121 -0
- package/lib/quoxon/away.js +52 -0
- package/lib/quoxon/bang.js +224 -0
- package/lib/quoxon/bulkRisk.js +17 -0
- package/lib/quoxon/capabilities.js +13 -6
- package/lib/quoxon/cells.js +53 -0
- package/lib/quoxon/commandShort.js +112 -0
- package/lib/quoxon/commands.js +363 -38
- package/lib/quoxon/connect.js +210 -23
- package/lib/quoxon/connectCard.js +19 -2
- package/lib/quoxon/consent.js +86 -0
- package/lib/quoxon/copy.js +4 -3
- package/lib/quoxon/countdownLine.js +11 -0
- package/lib/quoxon/demo.autoplay.js +29 -0
- package/lib/quoxon/demo.fixtures.js +1 -1
- package/lib/quoxon/demo.js +44 -3
- package/lib/quoxon/devObserver.js +20 -0
- package/lib/quoxon/dividerAocl.js +191 -0
- package/lib/quoxon/dividerRule.js +57 -0
- package/lib/quoxon/dragonModes.js +150 -0
- package/lib/quoxon/dragonView.js +41 -0
- package/lib/quoxon/earlyInput.js +40 -0
- package/lib/quoxon/echo.js +15 -0
- package/lib/quoxon/egg.js +2 -2
- package/lib/quoxon/events.js +244 -25
- package/lib/quoxon/feedback.js +50 -0
- package/lib/quoxon/gallery.js +21 -7
- package/lib/quoxon/handoffOffer.js +78 -0
- package/lib/quoxon/hatSwitch.js +37 -0
- package/lib/quoxon/headless.js +161 -0
- package/lib/quoxon/hostUrls.js +41 -0
- package/lib/quoxon/indicator.js +6 -2
- package/lib/quoxon/intent.js +12 -1
- package/lib/quoxon/keys.js +49 -6
- package/lib/quoxon/launcher.js +103 -0
- package/lib/quoxon/main.js +92 -160
- package/lib/quoxon/markdown.js +45 -21
- package/lib/quoxon/markdownSettle.js +19 -0
- package/lib/quoxon/matrixDomains.json +46 -0
- package/lib/quoxon/mentions.js +90 -0
- package/lib/quoxon/mind.js +103 -0
- package/lib/quoxon/mode.js +46 -0
- package/lib/quoxon/model.js +132 -26
- package/lib/quoxon/modelPrefs.js +345 -0
- package/lib/quoxon/motion/clock.js +3 -2
- package/lib/quoxon/motion/level.js +91 -0
- package/lib/quoxon/mouse.js +211 -0
- package/lib/quoxon/noai.js +57 -0
- package/lib/quoxon/objects/away.js +72 -0
- package/lib/quoxon/objects/fetch.js +17 -5
- package/lib/quoxon/objects/fleet.js +149 -0
- package/lib/quoxon/objects/health.js +186 -0
- package/lib/quoxon/objects/index.js +24 -3
- package/lib/quoxon/objects/mirror.js +137 -0
- package/lib/quoxon/objects/peek.js +17 -0
- package/lib/quoxon/objects/prove.js +130 -0
- package/lib/quoxon/objects/replay.js +125 -0
- package/lib/quoxon/objects/sitrep.js +58 -0
- package/lib/quoxon/objects/view.js +11 -0
- package/lib/quoxon/onboarding/copy.js +32 -4
- package/lib/quoxon/onboarding/credential.js +17 -4
- package/lib/quoxon/onboarding/dockerOffer.js +45 -0
- package/lib/quoxon/onboarding/failureCause.js +33 -0
- package/lib/quoxon/onboarding/finish.js +15 -0
- package/lib/quoxon/onboarding/index.js +34 -7
- package/lib/quoxon/onboarding/installRunner.js +64 -5
- package/lib/quoxon/onboarding/preflight.js +17 -3
- package/lib/quoxon/onboarding/probe.js +65 -0
- package/lib/quoxon/onboarding/view.js +76 -19
- package/lib/quoxon/orgLabel.js +10 -0
- package/lib/quoxon/orgSwitch.js +33 -0
- package/lib/quoxon/ownBroadcast.js +22 -0
- package/lib/quoxon/palette.js +43 -0
- package/lib/quoxon/print.js +9 -5
- package/lib/quoxon/profileSwitch.js +53 -0
- package/lib/quoxon/promptHistory.js +41 -0
- package/lib/quoxon/providers.js +48 -0
- package/lib/quoxon/qart.js +20 -51
- package/lib/quoxon/qdragon.js +410 -0
- package/lib/quoxon/qdragonFly.js +61 -0
- package/lib/quoxon/qdragonScenes.js +282 -0
- package/lib/quoxon/qeyes.js +213 -0
- package/lib/quoxon/qmotion.js +58 -37
- package/lib/quoxon/render.js +638 -119
- package/lib/quoxon/resend.js +72 -0
- package/lib/quoxon/roster.js +95 -9
- package/lib/quoxon/route.js +6 -5
- package/lib/quoxon/rows.js +711 -0
- package/lib/quoxon/rowsStore.js +80 -0
- package/lib/quoxon/screenContext.js +150 -0
- package/lib/quoxon/screenFrames.js +254 -0
- package/lib/quoxon/session.js +172 -2
- package/lib/quoxon/sessionDead.js +17 -0
- package/lib/quoxon/signin.js +125 -0
- package/lib/quoxon/slash/agents.js +112 -0
- package/lib/quoxon/slash/argSources.js +65 -0
- package/lib/quoxon/slash/args.js +65 -0
- package/lib/quoxon/slash/automate.js +89 -0
- package/lib/quoxon/slash/broadcast.js +140 -0
- package/lib/quoxon/slash/broadcastRun.js +108 -0
- package/lib/quoxon/slash/card.js +30 -0
- package/lib/quoxon/slash/challenge.js +114 -0
- package/lib/quoxon/slash/cliCommand.js +53 -0
- package/lib/quoxon/slash/cliLeaves.json +948 -0
- package/lib/quoxon/slash/compact.js +1 -1
- package/lib/quoxon/slash/confirm.js +30 -0
- package/lib/quoxon/slash/crystallize.js +69 -0
- package/lib/quoxon/slash/dashboard.js +136 -0
- package/lib/quoxon/slash/draft.js +54 -0
- package/lib/quoxon/slash/hatWalk.js +206 -0
- package/lib/quoxon/slash/inverses.js +77 -0
- package/lib/quoxon/slash/killer.js +24 -0
- package/lib/quoxon/slash/killerApi.js +16 -0
- package/lib/quoxon/slash/ledger.js +13 -8
- package/lib/quoxon/slash/listView.js +3 -1
- package/lib/quoxon/slash/modelCmd.js +261 -0
- package/lib/quoxon/slash/palette.js +21 -7
- package/lib/quoxon/slash/paletteController.js +117 -7
- package/lib/quoxon/slash/panels.js +21 -6
- package/lib/quoxon/slash/picker.js +20 -2
- package/lib/quoxon/slash/restart.js +71 -13
- package/lib/quoxon/slash/rowCmd.js +157 -0
- package/lib/quoxon/slash/rowWalk.js +175 -0
- package/lib/quoxon/slash/settings.js +16 -6
- package/lib/quoxon/slash/spawn.js +177 -0
- package/lib/quoxon/slash/stepUp.js +274 -0
- package/lib/quoxon/slash/stepUpCard.js +60 -0
- package/lib/quoxon/slash/stepUpCmds.js +78 -0
- package/lib/quoxon/slash/undoApi.js +38 -0
- package/lib/quoxon/slash/undoCmd.js +49 -0
- package/lib/quoxon/slash/undoWish.js +15 -0
- package/lib/quoxon/slash/watch.js +164 -0
- package/lib/quoxon/slash/wish.js +23 -0
- package/lib/quoxon/slash/wishCmd.js +32 -0
- package/lib/quoxon/slotChat.js +300 -0
- package/lib/quoxon/slotFormat.js +195 -0
- package/lib/quoxon/slotRun.js +131 -0
- package/lib/quoxon/sound/mapping.js +2 -1
- package/lib/quoxon/stray.js +22 -0
- package/lib/quoxon/suggestCommand.js +59 -0
- package/lib/quoxon/term.js +46 -11
- package/lib/quoxon/textBuffer.js +176 -2
- package/lib/quoxon/theme.js +58 -25
- package/lib/quoxon/tips.js +433 -0
- package/lib/quoxon/toolError.js +41 -0
- package/lib/quoxon/transcript.js +42 -4
- package/lib/quoxon/web/qface.js +18 -10
- package/lib/quoxon/welcome.js +1 -1
- package/lib/quoxon/whatsnew.js +136 -0
- package/lib/quoxon/whatsnew.jsonl +89 -0
- package/lib/quoxon/windowOrg.js +113 -0
- package/lib/server/diag.js +129 -0
- package/lib/server/doctor.js +71 -0
- package/lib/server/install.js +149 -0
- package/lib/server/pluginEnable.js +161 -0
- package/lib/server/skew.js +56 -0
- package/lib/server/snapshot.js +131 -0
- package/lib/server/status.js +128 -0
- package/lib/server/sys.js +65 -0
- package/lib/server/uninstall.js +63 -0
- package/lib/server/update.js +234 -0
- package/lib/toolOutputShape.js +315 -0
- package/lib/tui/views/HyperionDeck.jsx +79 -0
- package/lib/tui/views/LucidDeck.jsx +209 -217
- package/package.json +4 -1
- package/lib/core/websiteSession.js +0 -148
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QUOXON-FW8: "!" commands. A line that starts with `!` runs on THIS machine, in the session cwd, and its output
|
|
3
|
+
* is shown inline. It never reaches the model (no turn, no server call) and needs no approval card: it is the
|
|
4
|
+
* user's own action on their own machine. Pure parsing plus one process runner; app.js owns the display.
|
|
5
|
+
*
|
|
6
|
+
* !<shell command> run via $SHELL -lc
|
|
7
|
+
* !quox <args> / !q <args> run THIS quox binary (not whatever is on PATH) with the current profile injected
|
|
8
|
+
* !! reserved (refused with a note)
|
|
9
|
+
*
|
|
10
|
+
* BANG-TTY (owner 2026-10-16): capture stays the default (so /share works), but a known interactive command
|
|
11
|
+
* (needsTerminal, e.g. quox lucid deck, vim, top) or a captured run that failed for want of a terminal (ttyFailure)
|
|
12
|
+
* is handed the terminal, the same way a slot run request is.
|
|
13
|
+
*/
|
|
14
|
+
import { spawn } from 'node:child_process';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
import { dirname, join } from 'node:path';
|
|
17
|
+
|
|
18
|
+
export const MAX_LINES = 2000;
|
|
19
|
+
export const MAX_BYTES = 256 * 1024;
|
|
20
|
+
export const SHARE_MAX_BYTES = 16 * 1024;
|
|
21
|
+
export const DEFAULT_TIMEOUT_MS = 120000;
|
|
22
|
+
export const KILL_GRACE_MS = 2000;
|
|
23
|
+
export const QUOX_ENTRY = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'bin', 'quox.js');
|
|
24
|
+
|
|
25
|
+
/** -> null (not a bang line) | { kind: 'reserved' | 'empty' | 'shell' | 'quox', cmd?, args? } */
|
|
26
|
+
export function parseBang(line) {
|
|
27
|
+
const t = String(line ?? '').replace(/^\s+/, '');
|
|
28
|
+
if (!t.startsWith('!')) return null;
|
|
29
|
+
if (t.startsWith('!!')) return { kind: 'reserved' };
|
|
30
|
+
const rest = t.slice(1).trim();
|
|
31
|
+
if (!rest) return { kind: 'empty' };
|
|
32
|
+
const m = /^(quox|q)(?:\s+([\s\S]*))?$/.exec(rest);
|
|
33
|
+
if (m) return { kind: 'quox', args: tokenize(m[2] || ''), cmd: rest };
|
|
34
|
+
return { kind: 'shell', cmd: rest };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Minimal shell-ish word split: whitespace, 'single' and "double" quotes, backslash escapes outside single quotes. */
|
|
38
|
+
export function tokenize(s) {
|
|
39
|
+
const out = []; let cur = ''; let has = false; let q = null;
|
|
40
|
+
for (let i = 0; i < s.length; i++) {
|
|
41
|
+
const ch = s[i];
|
|
42
|
+
if (q === "'") { if (ch === "'") q = null; else cur += ch; continue; }
|
|
43
|
+
if (q === '"') {
|
|
44
|
+
if (ch === '"') q = null;
|
|
45
|
+
else if (ch === '\\' && i + 1 < s.length && /["\\$`]/.test(s[i + 1])) cur += s[++i];
|
|
46
|
+
else cur += ch;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (ch === "'" || ch === '"') { q = ch; has = true; continue; }
|
|
50
|
+
if (ch === '\\' && i + 1 < s.length) { cur += s[++i]; has = true; continue; }
|
|
51
|
+
if (/\s/.test(ch)) { if (cur || has) out.push(cur); cur = ''; has = false; continue; }
|
|
52
|
+
cur += ch; has = true;
|
|
53
|
+
}
|
|
54
|
+
if (cur || has) out.push(cur);
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const hasProfile = (args) => args.some(a => a === '-p' || a === '--profile' || a.startsWith('--profile=') || /^-p.+/.test(a));
|
|
59
|
+
const INTERACTIVE = new Set(['tui', 'quoxon']);
|
|
60
|
+
// quox leaves that draw a full-screen view (first two positional words); `--interactive` anywhere also counts
|
|
61
|
+
const INTERACTIVE_QUOX = new Set(['lucid deck']);
|
|
62
|
+
const INTERACTIVE_FLAG = (a) => a === '--interactive'; // not -i: some leaves use it for other things
|
|
63
|
+
// shell programs that need a terminal whatever their arguments; ssh only when it has no remote command
|
|
64
|
+
const INTERACTIVE_SHELL = new Set(['vim', 'vi', 'nvim', 'nano', 'emacs', 'less', 'more', 'most', 'man', 'top', 'htop', 'btop', 'atop', 'watch', 'tmux', 'screen', 'mc', 'ranger']);
|
|
65
|
+
const SSH_VALUE_FLAGS = new Set(['-b', '-c', '-D', '-E', '-e', '-F', '-I', '-i', '-J', '-L', '-l', '-m', '-O', '-o', '-p', '-Q', '-R', '-S', '-W', '-w', '-B']);
|
|
66
|
+
|
|
67
|
+
const quoxPositional = (args) => {
|
|
68
|
+
const pos = [];
|
|
69
|
+
for (let i = 0; i < args.length; i++) {
|
|
70
|
+
const a = args[i];
|
|
71
|
+
if (a === '-p' || a === '--profile') { i++; continue; }
|
|
72
|
+
if (a.startsWith('-')) continue;
|
|
73
|
+
pos.push(a);
|
|
74
|
+
}
|
|
75
|
+
return pos;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/** -> a short reason when this ! command needs a real terminal (hand it over), else null. */
|
|
79
|
+
export function needsTerminal(bang) {
|
|
80
|
+
if (!bang || (bang.kind !== 'shell' && bang.kind !== 'quox')) return null;
|
|
81
|
+
if (bang.kind === 'quox') {
|
|
82
|
+
const args = bang.args || [];
|
|
83
|
+
const pos = quoxPositional(args);
|
|
84
|
+
const info = args.some(a => a === '--help' || a === '-h' || a === '--version' || a === '-V');
|
|
85
|
+
if (!pos.length || info || INTERACTIVE.has(pos[0])) return null; // bare quox and tui stay refused (quoxArgv): this window IS that app
|
|
86
|
+
if (INTERACTIVE_QUOX.has(pos.slice(0, 2).join(' '))) return `quox ${pos.slice(0, 2).join(' ')} is interactive`;
|
|
87
|
+
if (args.some(INTERACTIVE_FLAG)) return 'it asked for --interactive';
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const words = tokenize(bang.cmd);
|
|
91
|
+
let i = 0;
|
|
92
|
+
while (i < words.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(words[i])) i++; // FOO=1 less x
|
|
93
|
+
if (/[|;&<>]/.test(bang.cmd)) return null; // a pipeline or redirect is the user's own plumbing: leave it captured
|
|
94
|
+
const prog = (words[i] || '').split('/').pop();
|
|
95
|
+
if (INTERACTIVE_SHELL.has(prog)) return `${prog} is interactive`;
|
|
96
|
+
if (prog === 'ssh') {
|
|
97
|
+
const rest = words.slice(i + 1); let host = -1;
|
|
98
|
+
for (let k = 0; k < rest.length; k++) {
|
|
99
|
+
if (SSH_VALUE_FLAGS.has(rest[k])) { k++; continue; }
|
|
100
|
+
if (rest[k].startsWith('-')) continue;
|
|
101
|
+
host = k; break;
|
|
102
|
+
}
|
|
103
|
+
if (host >= 0 && host === rest.length - 1) return 'ssh with no command opens a shell';
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const TTY_FAIL = /needs an interactive terminal|not a tty|inappropriate ioctl|must be run (from|in) a terminal|(input|stdin|stdout) is not a terminal|requires a tty|no tty present/i;
|
|
109
|
+
/** -> true when a captured run failed because it had no terminal (re-run it handed over). */
|
|
110
|
+
export function ttyFailure(r) {
|
|
111
|
+
if (!r || r.code === 0 || r.code == null || r.timedOut || r.killed) return false;
|
|
112
|
+
return TTY_FAIL.test(String(r.text || '').slice(-2000));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** -> { refuse: 'one-line hint' } | { argv: [...] } for process.execPath. */
|
|
116
|
+
export function quoxArgv(args, { profile, entry = QUOX_ENTRY } = {}) {
|
|
117
|
+
// positional words: skip flags and the value that follows -p/--profile
|
|
118
|
+
const pos = quoxPositional(args);
|
|
119
|
+
const info = args.some(a => a === '--help' || a === '-h' || a === '--version' || a === '-V');
|
|
120
|
+
const inject = profile && !hasProfile(args) ? ['-p', profile] : [];
|
|
121
|
+
const argv = [entry, ...inject, ...args];
|
|
122
|
+
// refuse = it cannot run captured inline; tty = the argv to hand the terminal to instead (BANG-TTY)
|
|
123
|
+
if (!pos.length && !info) return { refuse: 'bare quox opens the full-screen app, which needs the terminal. Give it a subcommand, e.g. !quox inbox list (or !quox --help).' };
|
|
124
|
+
if (INTERACTIVE.has(pos[0])) return { refuse: `quox ${pos[0]} is interactive and needs the terminal, so it cannot run inline. Open it in another terminal.` };
|
|
125
|
+
if (needsTerminal({ kind: 'quox', args })) return { refuse: `quox ${pos.slice(0, 2).join(' ')} is interactive and needs the terminal, so it cannot run inline.`, tty: { argv } };
|
|
126
|
+
return { argv };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const STDIN_HINT = /stdin|end of file|\bEOF\b|password|passphrase|\[y\/n\]|\(y\/n\)|continue\?|press (any )?key|enter .*:\s*$/i;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Run one command. -> { kill(), done: Promise<result> }.
|
|
133
|
+
* result: { code, signal, ms, timedOut, killed, lines, truncated, text, stdinNote }
|
|
134
|
+
* onData(text) is called with accepted output only (capped at MAX_LINES / MAX_BYTES); `text` is the same capped capture.
|
|
135
|
+
*/
|
|
136
|
+
export function runBang(plan, { cwd = process.cwd(), env = process.env, timeoutMs, onData = () => {}, graceMs = KILL_GRACE_MS, now = Date.now, shell } = {}) {
|
|
137
|
+
const tmo = Number(timeoutMs ?? env.QUOX_BANG_TIMEOUT_MS ?? DEFAULT_TIMEOUT_MS) || DEFAULT_TIMEOUT_MS;
|
|
138
|
+
const t0 = now();
|
|
139
|
+
let file, args;
|
|
140
|
+
if (plan.argv) { file = process.execPath; args = plan.argv; }
|
|
141
|
+
else { file = shell || env.SHELL || '/bin/sh'; args = ['-lc', plan.cmd]; }
|
|
142
|
+
const st = { timedOut: false, killed: false, lines: 0, acc: 0, bytes: 0, truncated: false, text: '', partial: false };
|
|
143
|
+
let child, timer, killTimer;
|
|
144
|
+
const done = new Promise((resolve) => {
|
|
145
|
+
const finish = (code, signal) => {
|
|
146
|
+
clearTimeout(timer); clearTimeout(killTimer);
|
|
147
|
+
const res = { code, signal, ms: now() - t0, timedOut: st.timedOut, killed: st.killed && !st.timedOut, lines: st.lines, truncated: st.truncated, text: st.text };
|
|
148
|
+
if (code !== 0 && !st.timedOut && !st.killed && (!st.text.trim() || STDIN_HINT.test(st.text.slice(-400)))) res.stdinNote = 'This command got end-of-file on stdin (input is not available here), so it may have been waiting for input.';
|
|
149
|
+
resolve(res);
|
|
150
|
+
};
|
|
151
|
+
try {
|
|
152
|
+
child = spawn(file, args, { cwd, env, detached: true, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
153
|
+
} catch (e) { st.text = String(e.message); return finish(127, null); }
|
|
154
|
+
child.on('error', (e) => { st.text += `${e.message}\n`; onData(`${e.message}\n`); finish(127, null); });
|
|
155
|
+
child.stdin.on('error', () => {});
|
|
156
|
+
child.stdin.end(); // EOF immediately: nothing here can answer a prompt
|
|
157
|
+
const take = (buf) => {
|
|
158
|
+
let s = buf.toString('utf8');
|
|
159
|
+
if (!s) return;
|
|
160
|
+
for (const ch of s) if (ch === '\n') st.lines++;
|
|
161
|
+
if (st.truncated) return;
|
|
162
|
+
if (st.acc >= MAX_LINES) { st.truncated = true; return; }
|
|
163
|
+
const room = MAX_BYTES - st.bytes;
|
|
164
|
+
if (Buffer.byteLength(s) > room) { s = Buffer.from(s).subarray(0, Math.max(0, room)).toString('utf8'); st.truncated = true; }
|
|
165
|
+
let nl = 0;
|
|
166
|
+
for (let i = 0; i < s.length; i++) {
|
|
167
|
+
if (s[i] === '\n' && st.acc + (++nl) >= MAX_LINES) { if (i + 1 < s.length) st.truncated = true; s = s.slice(0, i + 1); break; }
|
|
168
|
+
}
|
|
169
|
+
st.acc += (s.match(/\n/g) || []).length; st.bytes += Buffer.byteLength(s); st.text += s;
|
|
170
|
+
if (s) onData(s);
|
|
171
|
+
};
|
|
172
|
+
const onOut = (b) => take(b);
|
|
173
|
+
child.stdout.on('data', onOut); child.stderr.on('data', onOut);
|
|
174
|
+
child.on('close', (code, signal) => finish(code, signal));
|
|
175
|
+
timer = setTimeout(() => { st.timedOut = true; kill(); }, tmo);
|
|
176
|
+
});
|
|
177
|
+
function kill() {
|
|
178
|
+
if (!child || child.exitCode !== null || child.signalCode) return;
|
|
179
|
+
st.killed = true;
|
|
180
|
+
const sig = (s) => { try { process.kill(-child.pid, s); } catch { try { child.kill(s); } catch { /* gone */ } } };
|
|
181
|
+
sig('SIGTERM');
|
|
182
|
+
killTimer = setTimeout(() => sig('SIGKILL'), graceMs);
|
|
183
|
+
}
|
|
184
|
+
return { kill, done };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The "exit N · 1.2s" footer, plus the plain-words reasons. */
|
|
188
|
+
export function footer(r, { timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
|
|
189
|
+
const secs = `${(r.ms / 1000).toFixed(1)}s`;
|
|
190
|
+
if (r.timedOut) return `timed out after ${Math.round(timeoutMs / 1000)}s and was stopped · ${secs} (QUOX_BANG_TIMEOUT_MS changes the limit)`;
|
|
191
|
+
if (r.killed) return `stopped (Ctrl+C) · ${secs}`;
|
|
192
|
+
if (r.signal) return `killed by ${r.signal} · ${secs}`;
|
|
193
|
+
return `exit ${r.code} · ${secs}`;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export const truncNote = (r) => `(output truncated, ${r.lines} lines)`;
|
|
197
|
+
|
|
198
|
+
/** Text to attach for /share: capped at SHARE_MAX_BYTES. -> { text, truncated } */
|
|
199
|
+
export function shareText(text, max = SHARE_MAX_BYTES) {
|
|
200
|
+
const b = Buffer.from(String(text ?? ''), 'utf8');
|
|
201
|
+
if (b.length <= max) return { text: String(text ?? ''), truncated: false };
|
|
202
|
+
return { text: b.subarray(0, max).toString('utf8'), truncated: true };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** The message sent to the model: the attached output, clearly framed, then the user's words. */
|
|
206
|
+
export function withShared(message, shared) {
|
|
207
|
+
if (!shared?.length) return message;
|
|
208
|
+
const blocks = shared.map(s => `[output of \`${s.cmd}\`${s.truncated ? ' (truncated)' : ''}]\n${s.text}`).join('\n\n');
|
|
209
|
+
return `${message}\n\n${blocks}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Make command output safe for Ink: keep SGR colour codes, drop every other escape sequence (cursor moves, OSC titles,
|
|
214
|
+
* clears), resolve carriage-return rewrites (progress bars) to their last state, expand tabs, drop other control bytes.
|
|
215
|
+
*/
|
|
216
|
+
export function cleanOutput(s) {
|
|
217
|
+
let t = String(s ?? '');
|
|
218
|
+
t = t.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)?/g, ''); // OSC
|
|
219
|
+
t = t.replace(/\x1b\[([0-9;:]*)([\x20-\x2f]*)([\x40-\x7e])/g, (m, p, i, f) => (f === 'm' ? m : '')); // CSI: keep only colour
|
|
220
|
+
t = t.replace(/\x1b[PX^_][^\x1b]*(?:\x1b\\)?/g, '').replace(/\x1b[@-Z\\-_]/g, '');
|
|
221
|
+
t = t.replace(/\r\n/g, '\n').split('\n').map(l => { const i = l.lastIndexOf('\r'); return i >= 0 && i < l.length - 1 ? l.slice(i + 1) : l.replace(/\r/g, ''); }).join('\n');
|
|
222
|
+
t = t.replace(/\t/g, ' ');
|
|
223
|
+
return t.replace(/[\x00-\x08\x0b-\x1a\x1c-\x1f\x7f]/g, '');
|
|
224
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// APPROVALS-AUDIT finding 1: a bulk yes (/yestoall, /approve all, /answer 1-3 y) never covers a risky ask.
|
|
2
|
+
// A held decision is walked one card at a time, with its full text and its own explicit yes.
|
|
3
|
+
// Mirror of services/auth/lib/bulkGuard.js (the server refuses the same set); a test pins the two together.
|
|
4
|
+
const LOOSEN = /loosen|\.test\.[a-z]+|\.spec\.[a-z]+|\/tests?\/|__tests__|test[- ]guard|\bguard\b|skip(ping)? (a |the )?test|re-?baseline/i;
|
|
5
|
+
const PRIV = /restart|daemon|privileged|elevated|sudo|\bgrant\b.*\btools?\b/i;
|
|
6
|
+
|
|
7
|
+
export function heldReason(it) {
|
|
8
|
+
const p = it?.payload || {};
|
|
9
|
+
if (p.high_risk === true) return 'high risk';
|
|
10
|
+
if (p.privileged === true) return 'privileged';
|
|
11
|
+
if (p.category === 'loosening' || p.category === 'test_change') return p.category;
|
|
12
|
+
const text = [it?.title, p.why, p.category, ...(Array.isArray(p.paths) ? p.paths : []), p.approval_context?.reason].filter(Boolean).join(' ');
|
|
13
|
+
if (LOOSEN.test(text)) return 'changes a test or guard';
|
|
14
|
+
if (PRIV.test(String(it?.title || ''))) return 'privileged or restart';
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
export const isHeldFromBulk = (it) => heldReason(it) !== null;
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* `res` is { ok:true, capabilities } or { ok:false, status?, missing?, error? }.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { capabilityName, statusUnreadable } from './providers.js';
|
|
11
|
+
|
|
10
12
|
const norm = (s) => String(s || '').trim().toLowerCase();
|
|
11
13
|
|
|
12
14
|
/** -> the capability matching an id, a provider short name, or a display name; else null. */
|
|
@@ -27,7 +29,7 @@ export function findCapability(query, res) {
|
|
|
27
29
|
export function offerFor(capabilityId, res) {
|
|
28
30
|
const c = findCapability(capabilityId, res);
|
|
29
31
|
if (!c) return null;
|
|
30
|
-
const name = c
|
|
32
|
+
const name = capabilityName(c);
|
|
31
33
|
switch (c.availability) {
|
|
32
34
|
case 'installed': return null;
|
|
33
35
|
case 'planned': return `Ⓠ ${name} is planned, not built yet.`;
|
|
@@ -53,22 +55,27 @@ export function capabilityLines(res) {
|
|
|
53
55
|
const of = (a) => caps.filter(c => c.availability === a);
|
|
54
56
|
const out = [['What this Quox can do here:']];
|
|
55
57
|
const installed = of('installed');
|
|
56
|
-
for (const c of installed) out.push([`● ${c
|
|
58
|
+
for (const c of installed) out.push([`● ${capabilityName(c)} ${c.intent}`, 'ok']);
|
|
57
59
|
if (!installed.length) out.push([' nothing is ready yet']);
|
|
58
60
|
const setup = of('requires_setup');
|
|
59
61
|
if (setup.length) {
|
|
60
62
|
out.push(['Needs setup:']);
|
|
61
|
-
for (const c of setup) out.push([`◐ ${c
|
|
63
|
+
for (const c of setup) out.push([`◐ ${capabilityName(c)} ${c.reason || 'needs setup'}${c.setup ? ` · ${c.setup}` : ''}`, 'warn']);
|
|
64
|
+
}
|
|
65
|
+
const unk = caps.filter(statusUnreadable);
|
|
66
|
+
if (unk.length) {
|
|
67
|
+
out.push(['Unknown (status could not be read):']);
|
|
68
|
+
for (const c of unk) out.push([`? ${capabilityName(c)} ${c.reason}`, 'warn']);
|
|
62
69
|
}
|
|
63
|
-
const off = of('unavailable');
|
|
70
|
+
const off = of('unavailable').filter(c => !statusUnreadable(c));
|
|
64
71
|
if (off.length) {
|
|
65
72
|
out.push(['Not available here:']);
|
|
66
|
-
for (const c of off) out.push([`○ ${c
|
|
73
|
+
for (const c of off) out.push([`○ ${capabilityName(c)} ${c.reason || 'unavailable'}${c.setup ? ` · ${c.setup}` : ''}`]);
|
|
67
74
|
}
|
|
68
75
|
const planned = of('planned');
|
|
69
76
|
if (planned.length) {
|
|
70
77
|
out.push(['Planned (not built yet):']);
|
|
71
|
-
for (const c of planned) out.push([`· ${c
|
|
78
|
+
for (const c of planned) out.push([`· ${capabilityName(c)} ${c.intent}`]);
|
|
72
79
|
}
|
|
73
80
|
out.push([' /capabilities <name> for one, e.g. /capabilities workflows']);
|
|
74
81
|
return out;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI-FOOTER: terminal cell widths for the bottom chrome (divider, custom rows, status rows).
|
|
3
|
+
*
|
|
4
|
+
* string-width counts East Asian Ambiguous characters as one cell. Several of ours are Ambiguous: the ' · '
|
|
5
|
+
* separator, ● Ⓠ │ ─ ▁ █ ▲ ▼ ▶. A terminal set to draw them wide (a CJK locale, or the "ambiguous characters
|
|
6
|
+
* are wide" option in iTerm2, VTE or Windows Terminal) gives each two cells, so a row measured to fit overflows
|
|
7
|
+
* and the terminal wraps it mid-label. Policy, in order: QUOX_AMBIGUOUS=wide|narrow; else a ja/zh/ko locale
|
|
8
|
+
* (LC_ALL, LC_CTYPE, LANG) means wide; else narrow (what nearly every Western terminal does).
|
|
9
|
+
* Pure apart from the env default; no extra dependency (the ranges below are Unicode EastAsianWidth=A, U+00A0..U+2FFF,
|
|
10
|
+
* combining marks excluded since they take no cell).
|
|
11
|
+
*/
|
|
12
|
+
import stringWidth from 'string-width';
|
|
13
|
+
|
|
14
|
+
const RANGES = ('a1 a4 a7-a8 aa ad-ae b0-b4 b6-ba bc-bf c6 d0 d7-d8 de-e1 e6 e8-ea ec-ed f0 f2-f3 f7-fa fc fe 101 111 113 11b ' +
|
|
15
|
+
'126-127 12b 131-133 138 13f-142 144 148-14b 14d 152-153 166-167 16b 1ce 1d0 1d2 1d4 1d6 1d8 1da 1dc 251 261 2c4 2c7 2c9-2cb ' +
|
|
16
|
+
'2cd 2d0 2d8-2db 2dd 2df 391-3a1 3a3-3a9 3b1-3c1 3c3-3c9 401 410-44f 451 2010 2013-2016 2018-2019 201c-201d 2020-2022 ' +
|
|
17
|
+
'2024-2027 2030 2032-2033 2035 203b 203e 2074 207f 2081-2084 20ac 2103 2105 2109 2113 2116 2121-2122 2126 212b 2153-2154 ' +
|
|
18
|
+
'215b-215e 2160-216b 2170-2179 2189 2190-2199 21b8-21b9 21d2 21d4 21e7 2200 2202-2203 2207-2208 220b 220f 2211 2215 221a ' +
|
|
19
|
+
'221d-2220 2223 2225 2227-222c 222e 2234-2237 223c-223d 2248 224c 2252 2260-2261 2264-2267 226a-226b 226e-226f 2282-2283 ' +
|
|
20
|
+
'2286-2287 2295 2299 22a5 22bf 2312 2460-24e9 24eb-254b 2550-2573 2580-258f 2592-2595 25a0-25a1 25a3-25a9 25b2-25b3 ' +
|
|
21
|
+
'25b6-25b7 25bc-25bd 25c0-25c1 25c6-25c8 25cb 25ce-25d1 25e2-25e5 25ef 2605-2606 2609 260e-260f 261c 261e 2640 2642 ' +
|
|
22
|
+
'2660-2661 2663-2665 2667-266a 266c-266d 266f 269e-269f 26bf 26c6-26cd 26cf-26d3 26d5-26e1 26e3 26e8-26e9 26eb-26f1 26f4 ' +
|
|
23
|
+
'26f6-26f9 26fb-26fc 26fe-26ff 273d 2776-277f 2b56-2b59')
|
|
24
|
+
.split(' ').map((r) => { const [a, b = a] = r.split('-'); return [parseInt(a, 16), parseInt(b, 16)]; });
|
|
25
|
+
|
|
26
|
+
/** Is this code point East Asian Ambiguous (drawn one or two cells depending on the terminal)? */
|
|
27
|
+
export function isAmbiguous(cp) {
|
|
28
|
+
let lo = 0, hi = RANGES.length - 1;
|
|
29
|
+
while (lo <= hi) {
|
|
30
|
+
const m = (lo + hi) >> 1;
|
|
31
|
+
if (cp < RANGES[m][0]) hi = m - 1; else if (cp > RANGES[m][1]) lo = m + 1; else return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The ambiguous-width policy for this terminal: true when Ambiguous characters take two cells. */
|
|
37
|
+
export function ambiguousWide(env = process.env) {
|
|
38
|
+
const o = String(env.QUOX_AMBIGUOUS || '').toLowerCase();
|
|
39
|
+
if (o === 'wide') return true;
|
|
40
|
+
if (o === 'narrow') return false;
|
|
41
|
+
return /^(ja|zh|ko)([_.@-]|$)/i.test(env.LC_ALL || env.LC_CTYPE || env.LANG || '');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ANSI = /\x1b\[[0-9;]*m/g;
|
|
45
|
+
/** Cells `text` takes in a terminal with the given ambiguous-width policy (escapes take none). */
|
|
46
|
+
export function cellWidth(text, { wide = ambiguousWide() } = {}) {
|
|
47
|
+
const t = String(text ?? '');
|
|
48
|
+
let w = stringWidth(t);
|
|
49
|
+
if (!wide) return w;
|
|
50
|
+
for (const ch of t.replace(ANSI, '')) if (isAmbiguous(ch.codePointAt(0))) w += 1;
|
|
51
|
+
return w;
|
|
52
|
+
}
|
|
53
|
+
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The palette's one-line description of each slash command: plain words, the job not the mechanism, at most 54
|
|
3
|
+
* characters so a row never clips at 79 columns. The long wording stays in the registry for /help.
|
|
4
|
+
* Rule: lowercase first word unless it is an acronym or a command name. A command with no entry here uses its
|
|
5
|
+
* registry desc, which a test requires to be short already.
|
|
6
|
+
*/
|
|
7
|
+
export const SHORT_MAX = 54;
|
|
8
|
+
export const SHORT = {
|
|
9
|
+
walkthrough: 'a guided pass: what it does, why, my advice',
|
|
10
|
+
approvals: 'the questions waiting for you, one at a time',
|
|
11
|
+
peek: 'look up an envelope, item, run or receipt by id',
|
|
12
|
+
aside: 'a read-only side question, kept out of the chat',
|
|
13
|
+
yestoall: 'approve everything waiting (same as /approve all)',
|
|
14
|
+
approve: 'approve item N, or everything one agent asked',
|
|
15
|
+
deny: 'deny item N (or all of one agent), with a reason',
|
|
16
|
+
answer: 'answer a question a session asked you',
|
|
17
|
+
authenticate: 'enter your PIN once, good for 10 minutes',
|
|
18
|
+
override: 'override one blocked action (PIN, witnessed)',
|
|
19
|
+
skills: 'list your org\'s governed skills, or show one',
|
|
20
|
+
skill: 'ask Quox to run a skill (approvals still apply)',
|
|
21
|
+
rules: 'which rules win: QUOX.md, dev pack, server policy',
|
|
22
|
+
clear: 'fresh conversation and a clear screen',
|
|
23
|
+
resume: 'pick a recent conversation and carry on',
|
|
24
|
+
compact: 'summarise the conversation, keeping decisions',
|
|
25
|
+
challenge: 'a second opinion on the last answer (one extra turn)',
|
|
26
|
+
spawn: 'ask HYPERION for a disposable VM (max 24h)',
|
|
27
|
+
consent: 'AI data sharing: see where you stand, or agree',
|
|
28
|
+
help: 'this reference, or detail for one command',
|
|
29
|
+
note: 'jot a note for the dev observer (also // text)',
|
|
30
|
+
share: 'attach your last ! command\'s output to your message',
|
|
31
|
+
copy: 'copy the last answer to the clipboard',
|
|
32
|
+
dragon: 'the Q comes alive; explain, ask, sleep',
|
|
33
|
+
'dragon-fly': 'the dragon flies across the screen',
|
|
34
|
+
sound: 'earcons: Silent, Minimal, Operational or Full',
|
|
35
|
+
mouse: 'click to reply and answer cards (off by default)',
|
|
36
|
+
tip: 'a "did you know" tip about something Quox can do',
|
|
37
|
+
tips: 'search the tips, or list the topics',
|
|
38
|
+
'tip-settings': 'tips: off, core (free features) or all',
|
|
39
|
+
whatsnew: 'new commands and release notes since you last looked',
|
|
40
|
+
welcome: 'show the welcome screen again',
|
|
41
|
+
about: 'who we are, terms, privacy, licence and docs',
|
|
42
|
+
quit: 'leave; finished work is kept, /resume picks it up',
|
|
43
|
+
restart: 'restart into the latest quox and resume',
|
|
44
|
+
update: 'update quox to the latest version, then restart',
|
|
45
|
+
demo: 'play the simulated demo; nothing real runs',
|
|
46
|
+
feedback: 'tell the Quox team something (you see it first)',
|
|
47
|
+
gripe: 'file a GitHub issue; /gripe! files at once',
|
|
48
|
+
sessions: 'everything live in your org, and where it runs',
|
|
49
|
+
active: 'who is doing what, one line per worker',
|
|
50
|
+
dashboard: 'open the web dashboard already signed in',
|
|
51
|
+
rename: 'name this session (alone shows the current name)',
|
|
52
|
+
slot: 'give this session a slot address',
|
|
53
|
+
say: 'message one or more slots directly',
|
|
54
|
+
join: 'join a slot as a group room',
|
|
55
|
+
leave: 'leave a slot room you joined',
|
|
56
|
+
rooms: 'rooms this window has joined, with unread counts',
|
|
57
|
+
broadcast: 'tell every held slot in this org at once',
|
|
58
|
+
page: 'urgent message to one slot, with acknowledgement',
|
|
59
|
+
ack: 'acknowledge a broadcast',
|
|
60
|
+
send: 'send a local text file into a slot (64 KB cap)',
|
|
61
|
+
attach: 'send your turns to a specific agent',
|
|
62
|
+
login: 'sign in again without leaving quox',
|
|
63
|
+
profile: 'switch this window to another signed-in profile',
|
|
64
|
+
connect: 'see what is connected; /connect openai adds a key',
|
|
65
|
+
hosts: 'add a host to your fleet; /hosts lists them',
|
|
66
|
+
capabilities: 'what this Quox can do here, and what needs setup',
|
|
67
|
+
verbose: 'protocol lines: off, on (compact) or full',
|
|
68
|
+
proof: 'the evidence chain for the last tool call',
|
|
69
|
+
prove: 'check the last turn\'s tool calls did what they said',
|
|
70
|
+
replay: 'rebuild an earlier run from the audit trail',
|
|
71
|
+
aocl: 'every AOCL layer of the last turn',
|
|
72
|
+
reconcile: 'check tool calls that ended with effect unknown',
|
|
73
|
+
roster: 'show or hide the live roster under the status line',
|
|
74
|
+
workflow: 'the step flow of a workflow\'s latest run',
|
|
75
|
+
crystal: 'could this workflow become a Crystal?',
|
|
76
|
+
undo: 'reverse the last thing Quox did, if it knows how',
|
|
77
|
+
wish: 'describe something Quox cannot do yet',
|
|
78
|
+
agents: 'pick an agent, or show one\'s card',
|
|
79
|
+
which: 'what this window is using: login, org, agent, model',
|
|
80
|
+
crystallize: 'turn what was just done into a reusable skill',
|
|
81
|
+
automate: 'turn this conversation or a task into a workflow',
|
|
82
|
+
watch: 'turn a site, repo, server or metric into an observer',
|
|
83
|
+
fleet: 'computers, agents, missions and SubFleets, with health',
|
|
84
|
+
memory: 'facts Quox remembers: source, age, trust, conflicts',
|
|
85
|
+
sitrep: 'the situation now: waiting, running, unseen',
|
|
86
|
+
away: 'what happened while you were away',
|
|
87
|
+
vm: 'VMs on Proxmox via HYPERION: list, start, stop',
|
|
88
|
+
hyperion: 'which VMs HYPERION has out, and who holds each',
|
|
89
|
+
observers: 'LUCID: the situation and what changed since',
|
|
90
|
+
mind: 'QuoxMind implants: status and last activity',
|
|
91
|
+
hat: 'list hats, switch hat, or make your own',
|
|
92
|
+
org: 'list your orgs, switch to one, or go back',
|
|
93
|
+
r: 'reply to whoever last messaged you (also /reply)',
|
|
94
|
+
poke: 'wake a session with an urgent page (also /wake)',
|
|
95
|
+
afk: 'tell sessions you are away; approvals still queue',
|
|
96
|
+
back: 'mark yourself present and show what you missed',
|
|
97
|
+
observe: 'post a note to the session watching your window',
|
|
98
|
+
huh: 'the dragon explains what just happened',
|
|
99
|
+
sleep: 'mark yourself sleeping; the dragon curls up',
|
|
100
|
+
mode: 'ask first (default) or plan only',
|
|
101
|
+
slots: 'held slots: who holds them and where',
|
|
102
|
+
mirror: 'what CommanderQ sees about this org right now',
|
|
103
|
+
health: 'dashboard summary: services, agents, approvals',
|
|
104
|
+
status: 'profile, org, server health, model, agent, slot',
|
|
105
|
+
cost: 'tokens and cost per model for this session',
|
|
106
|
+
context: 'model, tokens, rules, skills, memory, agent',
|
|
107
|
+
doctor: 'the quox doctor checks plus chat checks',
|
|
108
|
+
server: 'your self-hosted install: version, health, disk',
|
|
109
|
+
config: 'settings: theme, motion, verbose, model',
|
|
110
|
+
model: 'choose the provider and model for your next turns',
|
|
111
|
+
row: 'your own live lines above the status bar',
|
|
112
|
+
};
|