@k-leumas/queque-cli 0.4.2 → 0.5.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/dist/cli/main.js CHANGED
@@ -1,1761 +1,47 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __quequeCreateRequire } from 'node:module';
3
+ const require = __quequeCreateRequire(import.meta.url);
2
4
 
3
- // src/cli/main.ts
4
- import * as fs2 from "fs";
5
- import { cac } from "cac";
6
-
7
- // src/context/providers/filesystem-context.ts
8
- var FILENAME_RE = /\b[\w.-]+\.\w{2,6}\b/g;
9
- var filesystemContextProvider = {
10
- id: "filesystem-context",
11
- intents: ["filesystem"],
12
- async gather(input) {
13
- const match = input.base.queryText.match(FILENAME_RE);
14
- return {
15
- kind: "filesystem",
16
- payload: {
17
- cwd: input.base.cwd,
18
- apparentFilename: match?.[0] ?? null
19
- }
20
- };
21
- }
22
- };
23
-
24
- // src/context/providers/git-context.ts
25
- import { execFile as execFile2 } from "child_process";
26
- import { statSync } from "fs";
27
- import { promisify as promisify2 } from "util";
28
-
29
- // src/shared/debug-log.ts
30
- import * as fsp from "fs/promises";
31
-
32
- // src/shared/qq-config.ts
33
- import { readFileSync } from "fs";
5
+ // src/cli/launcher.ts
6
+ import { lstatSync, mkdirSync } from "fs";
7
+ import * as nodeModule from "module";
34
8
  import { homedir } from "os";
35
- import { join } from "path";
36
- import { z } from "zod";
37
- var DEFAULT_SENSITIVE_PATH_PATTERNS = [
38
- "\\.env",
39
- "credentials",
40
- "\\.pem$",
41
- "id_rsa",
42
- "\\.ssh/",
43
- "\\.aws/",
44
- "secret"
45
- ];
46
- var DEFAULT_REDACT_LOG_KEYS = ["lbuffer", "rbuffer", "queryText", "request"];
47
- var DEFAULT_DESTRUCTIVE_COMMAND_PATTERNS = [
48
- "\\brm\\s+(-?-?\\w+)?\\b",
49
- "\\bsudo\\b",
50
- "\\bchmod\\s+-R\\b",
51
- ">\\s*/dev/",
52
- "\\bcurl\\b[^\\n|]*\\|\\s*sh\\b",
53
- "\\bwget\\b[^\\n|]*\\|\\s*sh\\b"
54
- ];
55
- var privacyConfigSchema = z.object({
56
- sensitivePathPatterns: z.array(z.string()).optional(),
57
- redactLogKeys: z.array(z.string()).optional(),
58
- allowFileRead: z.boolean().optional(),
59
- useGitignore: z.boolean().optional().describe(
60
- "Not yet implemented \u2014 this config option will enable gitignore-based filtering in the future. Placeholder only."
61
- )
62
- }).optional();
63
- var safetyConfigSchema = z.object({
64
- destructiveCommandPatterns: z.array(z.string()).optional()
65
- }).optional();
66
- var qqConfigFileSchema = z.object({
67
- privacy: privacyConfigSchema,
68
- safety: safetyConfigSchema
69
- });
70
- var cachedConfig = null;
71
- function defaultConfigPath() {
72
- return process.env.QQ_CONFIG_FILE ?? join(homedir(), ".config", "qq", "config.json");
73
- }
74
- var MAX_PATTERN_LENGTH = 256;
75
- function compilePatterns(patterns) {
76
- const compiled = [];
77
- for (const source of patterns) {
78
- if (source.length > MAX_PATTERN_LENGTH) {
79
- continue;
80
- }
81
- try {
82
- compiled.push(new RegExp(source, "i"));
83
- } catch {
84
- }
85
- }
86
- return compiled;
87
- }
88
- function envFlagEnabled(name) {
89
- const value = process.env[name];
90
- return value === "1" || value === "true";
91
- }
92
- function readConfigFile() {
93
- const configPath = defaultConfigPath();
94
- let raw;
95
- try {
96
- raw = readFileSync(configPath, "utf8");
97
- } catch (error) {
98
- const code = error.code;
99
- if (code === "ENOENT") {
100
- return {};
101
- }
102
- return {};
103
- }
104
- try {
105
- return qqConfigFileSchema.parse(JSON.parse(raw));
106
- } catch (error) {
107
- const message = error instanceof Error ? error.message : String(error);
108
- console.warn(`queque: ignoring invalid config at ${configPath}: ${message}`);
109
- return {};
110
- }
111
- }
112
- function loadQqConfig() {
113
- if (cachedConfig) {
114
- return cachedConfig;
115
- }
116
- const file = readConfigFile();
117
- const sensitivePatterns = [
118
- ...DEFAULT_SENSITIVE_PATH_PATTERNS,
119
- ...file.privacy?.sensitivePathPatterns ?? []
120
- ];
121
- const redactKeys = [...DEFAULT_REDACT_LOG_KEYS, ...file.privacy?.redactLogKeys ?? []];
122
- const destructivePatterns = [
123
- ...DEFAULT_DESTRUCTIVE_COMMAND_PATTERNS,
124
- ...file.safety?.destructiveCommandPatterns ?? []
125
- ];
126
- const allowFileRead = envFlagEnabled("QQ_ALLOW_FILE_READ") || (file.privacy?.allowFileRead ?? false);
127
- cachedConfig = {
128
- sensitivePathRegexes: compilePatterns(sensitivePatterns),
129
- redactLogKeys: new Set(redactKeys),
130
- allowFileRead,
131
- destructiveCommandRegexes: compilePatterns(destructivePatterns)
132
- };
133
- return cachedConfig;
134
- }
135
-
136
- // src/shared/privacy-filter.ts
137
- function isSensitivePath(path2) {
138
- const { sensitivePathRegexes } = loadQqConfig();
139
- return sensitivePathRegexes.some((pattern) => pattern.test(path2));
140
- }
141
- function filterContextEnvelope(envelope) {
142
- return {
143
- ...envelope,
144
- extras: envelope.extras.map((chunk) => {
145
- if (chunk.kind !== "git") {
146
- return chunk;
147
- }
148
- return {
149
- ...chunk,
150
- payload: {
151
- ...chunk.payload,
152
- changedFiles: chunk.payload.changedFiles.filter((filePath) => !isSensitivePath(filePath))
153
- }
154
- };
155
- })
156
- };
157
- }
158
- function redactForLog(details) {
159
- if (process.env.QQ_DEBUG_VERBOSE === "1" || details === void 0) {
160
- return details;
161
- }
162
- if (details === null || typeof details !== "object") {
163
- return details;
164
- }
165
- if (Array.isArray(details)) {
166
- return details.map((item) => redactForLog(item));
167
- }
168
- const { redactLogKeys } = loadQqConfig();
169
- const input = details;
170
- const output = {};
171
- for (const [key, value] of Object.entries(input)) {
172
- if (redactLogKeys.has(key)) {
173
- if (typeof value === "string") {
174
- output[key] = `[redacted:${value.length}chars]`;
175
- } else {
176
- output[key] = redactForLog(value);
177
- }
178
- continue;
179
- }
180
- output[key] = redactForLog(value);
181
- }
182
- return output;
183
- }
184
- function isDestructiveCommand(command) {
185
- const { destructiveCommandRegexes } = loadQqConfig();
186
- return destructiveCommandRegexes.some((pattern) => pattern.test(command));
187
- }
188
-
189
- // src/shared/debug-log.ts
190
- var debugLogPath = process.env.QQ_DEBUG_LOG_FILE ?? `/tmp/qq-${process.getuid?.() ?? "unknown"}-debug.log`;
191
- function formatDetails(details) {
192
- if (details === void 0) return "";
193
- try {
194
- return ` ${JSON.stringify(details)}`;
195
- } catch {
196
- return " [unserializable-details]";
197
- }
198
- }
199
- async function appendDebugLog(scope, message, details) {
200
- const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${scope}] ${message}${formatDetails(redactForLog(details))}
201
- `;
202
- try {
203
- await fsp.appendFile(debugLogPath, line, { encoding: "utf-8", mode: 384 });
204
- } catch {
205
- }
206
- }
207
-
208
- // src/shared/vcs-context.ts
209
- import { execFile } from "child_process";
210
- import { promisify } from "util";
211
- var execFileAsync = promisify(execFile);
212
- async function runGit(cwd, args) {
213
- try {
214
- const { stdout } = await execFileAsync("git", ["-C", cwd, ...args], { encoding: "utf8" });
215
- return stdout.trim();
216
- } catch {
217
- return null;
218
- }
219
- }
220
- async function detectVcsContext(cwd) {
221
- const insideWorkTree = await runGit(cwd, ["rev-parse", "--is-inside-work-tree"]);
222
- if (insideWorkTree !== "true") {
223
- return { kind: "none", cwd };
224
- }
225
- const root = await runGit(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd;
226
- const branch = await runGit(cwd, ["branch", "--show-current"]) || null;
227
- const dirty = Boolean((await runGit(cwd, ["status", "--porcelain=v1"]))?.length);
228
- return {
229
- kind: "git",
230
- cwd,
231
- root,
232
- branch,
233
- dirty
234
- };
235
- }
236
-
237
- // src/context/providers/git-context.ts
238
- var execFileAsync2 = promisify2(execFile2);
239
- var GIT_TIMEOUT_MS = 5e3;
240
- function assertSafeCwd(cwd) {
241
- let st;
242
- try {
243
- st = statSync(cwd);
244
- } catch {
245
- throw new Error(`cwd not accessible: ${cwd}`);
246
- }
247
- if (!st.isDirectory()) throw new Error(`cwd is not a directory: ${cwd}`);
248
- }
249
- function unescapeGitPath(s) {
250
- return s.replace(/\\([0-7]{3})/g, (_, oct) => String.fromCodePoint(parseInt(oct, 8))).replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\\\/g, "\\");
251
- }
252
- function stripQuotes(value) {
253
- if (value.startsWith('"') && value.endsWith('"')) {
254
- return unescapeGitPath(value.slice(1, -1));
255
- }
256
- return value;
257
- }
258
- function parsePorcelainLine(line) {
259
- if (line.length < 4) {
260
- return null;
261
- }
262
- const rest = line.slice(3);
263
- const renameSeparator = " -> ";
264
- const renameIndex = rest.indexOf(renameSeparator);
265
- if (renameIndex !== -1) {
266
- return stripQuotes(rest.slice(renameIndex + renameSeparator.length));
267
- }
268
- return stripQuotes(rest);
269
- }
270
- async function getChangedFiles(cwd) {
271
- try {
272
- assertSafeCwd(cwd);
273
- const { stdout } = await execFileAsync2("git", ["-C", cwd, "status", "--porcelain"], {
274
- encoding: "utf8",
275
- timeout: GIT_TIMEOUT_MS
276
- });
277
- return stdout.split("\n").filter(Boolean).map(parsePorcelainLine).filter((path2) => path2 !== null);
278
- } catch {
279
- return [];
280
- }
281
- }
282
- var gitContextProvider = {
283
- id: "git-context",
284
- intents: ["codebase", "shell-command"],
285
- async gather(input) {
286
- if (input.decision.intent === "shell-command" && !input.decision.signals.includes("git-prefix")) {
287
- return null;
288
- }
289
- try {
290
- const vcs = await detectVcsContext(input.base.cwd);
291
- if (vcs.kind === "none") {
292
- return null;
293
- }
294
- const changedFiles = await getChangedFiles(input.base.cwd);
295
- void appendDebugLog("context", "git provider gathered", {
296
- cwd: input.base.cwd,
297
- branch: vcs.branch,
298
- dirty: vcs.dirty,
299
- changedFiles: changedFiles.length
300
- });
301
- return {
302
- kind: "git",
303
- payload: {
304
- cwd: vcs.cwd,
305
- root: vcs.root,
306
- branch: vcs.branch,
307
- dirty: vcs.dirty,
308
- changedFiles
309
- }
310
- };
311
- } catch (error) {
312
- void appendDebugLog("context", "git provider failed", {
313
- cwd: input.base.cwd,
314
- message: error instanceof Error ? error.message : String(error)
315
- });
316
- return null;
317
- }
318
- }
319
- };
320
-
321
- // src/providers/claude.ts
322
- import Anthropic from "@anthropic-ai/sdk";
323
-
324
- // src/contracts/candidates.ts
325
- import { z as z2 } from "zod";
326
- var commandCandidateSchema = z2.object({
327
- command: z2.string().trim().min(1),
328
- explanation: z2.string()
329
- });
330
- var candidateListSchema = z2.array(commandCandidateSchema).min(1).max(5);
331
-
332
- // src/shared/env-file.ts
333
- import { existsSync, readFileSync as readFileSync2 } from "fs";
334
- import { dirname, join as join2 } from "path";
335
- function findUp(startDir, filename) {
336
- let current = startDir;
337
- while (true) {
338
- const candidate = join2(current, filename);
339
- if (existsSync(candidate)) {
340
- return candidate;
341
- }
342
- const parent = dirname(current);
343
- if (parent === current) {
344
- return null;
345
- }
346
- current = parent;
347
- }
348
- }
349
- function parseEnvFile(content) {
350
- const values = /* @__PURE__ */ new Map();
351
- for (const rawLine of content.split(/\r?\n/)) {
352
- const line = rawLine.trim();
353
- if (!line || line.startsWith("#")) {
354
- continue;
355
- }
356
- const match = line.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
357
- if (!match) {
358
- continue;
359
- }
360
- const [, key, rawValue] = match;
361
- let value = rawValue.trim();
362
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
363
- value = value.slice(1, -1);
364
- }
365
- values.set(key, value);
366
- }
367
- return values;
368
- }
369
- var envCache = /* @__PURE__ */ new Map();
370
- function readEnvValueFromDotEnvLocal(key, startDir = process.cwd()) {
371
- const cacheKey = `${key}\0${startDir}`;
372
- if (envCache.has(cacheKey)) {
373
- return envCache.get(cacheKey) ?? null;
374
- }
375
- const envPath = findUp(startDir, ".env.local");
376
- if (!envPath) {
377
- envCache.set(cacheKey, null);
378
- return null;
379
- }
380
- const values = parseEnvFile(readFileSync2(envPath, "utf8"));
381
- const result = values.get(key) ?? null;
382
- envCache.set(cacheKey, result);
383
- return result;
384
- }
385
-
386
- // src/providers/claude.ts
387
- var DEFAULT_MODEL = "claude-sonnet-5";
388
- function resolveModel() {
389
- return process.env.QQ_MODEL ?? readEnvValueFromDotEnvLocal("QQ_MODEL") ?? DEFAULT_MODEL;
390
- }
391
- function extractText(content) {
392
- return content.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text ?? "").join("").trim();
9
+ import { isAbsolute, join } from "path";
10
+ var SHARED_WRITE_BITS = 18;
11
+ function isPrivateDirectory(path, uid) {
12
+ const stats = lstatSync(path);
13
+ return stats.isDirectory() && !stats.isSymbolicLink() && stats.uid === uid && (stats.mode & SHARED_WRITE_BITS) === 0;
393
14
  }
394
- function stripCodeFence(raw) {
395
- const m = raw.trim().match(/^```(?:json)?\s*\n?([\s\S]*?)\n?```\s*$/);
396
- return m ? m[1].trim() : raw.trim();
397
- }
398
- function parseCandidates(text) {
399
- let parsed;
400
- try {
401
- parsed = JSON.parse(stripCodeFence(text));
402
- } catch {
403
- const trimmed = text.trim();
404
- return [{ command: trimmed || 'echo ""', explanation: "" }];
405
- }
406
- try {
407
- return candidateListSchema.parse(parsed);
408
- } catch {
409
- return [{ command: 'echo ""', explanation: "QueQue: unexpected response format" }];
410
- }
411
- }
412
- function shouldForceSelector() {
413
- const configured = process.env.QQ_FORCE_SELECTOR ?? readEnvValueFromDotEnvLocal("QQ_FORCE_SELECTOR");
414
- return configured === "1" || configured === "true";
415
- }
416
- function ensureSelectableCandidates(candidates) {
417
- if (!shouldForceSelector() || candidates.length >= 2) {
418
- return candidates;
419
- }
420
- const [first] = candidates;
421
- return [
422
- first,
423
- {
424
- command: first.command,
425
- explanation: first.explanation.length > 0 ? `${first.explanation} (forced duplicate for selector testing)` : "Forced duplicate for selector testing."
426
- }
427
- ];
428
- }
429
- function buildPrompt(envelope) {
430
- const filtered = filterContextEnvelope(envelope);
431
- const gitChunk = filtered.extras.find((chunk) => chunk.kind === "git");
432
- const filesystemChunk = filtered.extras.find((chunk) => chunk.kind === "filesystem");
433
- return [
434
- "Return ONLY a JSON array of 1-3 shell command candidates, most likely first.",
435
- 'Each item must have exactly these keys: "command" and "explanation".',
436
- "No markdown, no prose, no code fences. Raw JSON array only.",
437
- "",
438
- "The command should be safe to place back into a shell buffer.",
439
- "",
440
- "Shell context:",
441
- JSON.stringify(
442
- {
443
- cwd: filtered.base.cwd,
444
- queryText: filtered.base.queryText,
445
- platform: filtered.base.platform,
446
- shellName: filtered.base.shellName,
447
- ...gitChunk ? { versionControl: gitChunk.payload } : {},
448
- ...filesystemChunk ? { filesystem: filesystemChunk.payload } : {}
449
- },
450
- null,
451
- 2
452
- )
453
- ].join("\n");
454
- }
455
- async function fetchCandidates(envelope, rbuffer = "") {
456
- const apiKey = process.env.ANTHROPIC_API_KEY ?? readEnvValueFromDotEnvLocal("ANTHROPIC_API_KEY");
457
- if (!apiKey) {
458
- throw new Error("ANTHROPIC_API_KEY is required in the environment or .env.local");
459
- }
460
- const client = new Anthropic({ apiKey });
461
- const prompt = buildPrompt(envelope);
462
- const model = resolveModel();
463
- void appendDebugLog("provider", "request start", {
464
- model,
465
- cwd: envelope.base.cwd,
466
- rbufferLength: rbuffer.length,
467
- extras: envelope.extras.map((chunk) => chunk.kind)
468
- });
15
+ function enablePerUserCompileCache() {
469
16
  try {
470
- const response = await client.messages.create(
471
- {
472
- model,
473
- max_tokens: 1024,
474
- system: "You are QueQue, a terminal shell assistant. Return ONLY a JSON array of command candidates, ranked with the most correct/direct command first. No prose, no markdown, no code fences. When a command requires a value the user must supply (hostname, filename, branch name, etc.), wrap it in angle brackets: <placeholder>. Use descriptive names like <user@host>, <filename>, <branch-name>. Do not use angle brackets for optional flags or known values.",
475
- messages: [{ role: "user", content: prompt }]
476
- },
477
- {
478
- timeout: 25e3
479
- // 25s — slightly under the zsh 30s FIFO timeout
480
- }
481
- );
482
- const text = extractText(response.content);
483
- const candidates = ensureSelectableCandidates(parseCandidates(text));
484
- void appendDebugLog("provider", "response parsed", {
485
- model,
486
- candidateCount: candidates.length,
487
- forceSelector: shouldForceSelector()
488
- });
489
- return candidates;
490
- } catch (error) {
491
- void appendDebugLog("provider", "request failed", {
492
- model,
493
- message: error instanceof Error ? error.message : String(error)
494
- });
495
- throw error;
496
- }
497
- }
498
- var claudeAdapter = {
499
- fetchCandidates
500
- };
501
-
502
- // src/registry/context-providers.ts
503
- var registry = /* @__PURE__ */ new Map();
504
- function registerContextProvider(descriptor) {
505
- if (registry.has(descriptor.id)) {
506
- throw new Error(`Context provider already registered: "${descriptor.id}"`);
507
- }
508
- registry.set(descriptor.id, descriptor);
509
- }
510
- function listContextProviders() {
511
- return Array.from(registry.values());
512
- }
513
-
514
- // src/registry/provider-backends.ts
515
- var registry2 = /* @__PURE__ */ new Map();
516
- function registerProviderBackend(registration) {
517
- if (registry2.has(registration.id)) {
518
- throw new Error(`Provider backend already registered: "${registration.id}"`);
519
- }
520
- registry2.set(registration.id, registration);
521
- }
522
- function getProviderAdapter(id) {
523
- return registry2.get(id)?.adapter;
524
- }
525
-
526
- // src/registry/shell-adapters.ts
527
- var registry3 = /* @__PURE__ */ new Map();
528
- function registerShellAdapter(descriptor) {
529
- if (registry3.has(descriptor.id)) {
530
- throw new Error(`Shell adapter already registered: "${descriptor.id}"`);
531
- }
532
- registry3.set(descriptor.id, descriptor);
533
- }
534
- function listShellAdapters() {
535
- return Array.from(registry3.values());
536
- }
537
-
538
- // src/registry/storage-hooks.ts
539
- var registry4 = /* @__PURE__ */ new Map();
540
- function registerStorageHook(descriptor) {
541
- if (registry4.has(descriptor.id)) {
542
- throw new Error(`Storage hook already registered: "${descriptor.id}"`);
543
- }
544
- registry4.set(descriptor.id, descriptor);
545
- }
546
-
547
- // src/registry/bootstrap.ts
548
- var bootstrapped = false;
549
- function bootstrapBuiltins() {
550
- if (bootstrapped) {
551
- return;
552
- }
553
- bootstrapped = true;
554
- registerContextProvider(gitContextProvider);
555
- registerContextProvider(filesystemContextProvider);
556
- registerShellAdapter({
557
- id: "zsh",
558
- shell: "zsh",
559
- description: "zsh ZLE widget adapter (Phase 2 built-in)"
560
- });
561
- registerStorageHook({
562
- id: "noop",
563
- description: "No-op storage hook (default \u2014 no persistence)"
564
- });
565
- registerStorageHook({
566
- id: "memory",
567
- description: "In-memory storage hook (test and ephemeral use)"
568
- });
569
- registerProviderBackend({
570
- id: "claude",
571
- name: "Claude (Anthropic)",
572
- description: "Anthropic Claude adapter \u2014 default LLM backend",
573
- adapter: claudeAdapter
574
- });
575
- }
576
-
577
- // src/client/run-foreground.ts
578
- import * as fsp4 from "fs/promises";
579
- import * as tty from "tty";
580
- import { render } from "ink";
581
- import React from "react";
582
-
583
- // src/contracts/request.ts
584
- import { z as z4 } from "zod";
585
-
586
- // src/contracts/shell.ts
587
- import { z as z3 } from "zod";
588
- var shellRequestSchema = z3.object({
589
- version: z3.literal(1),
590
- ttyPath: z3.string(),
591
- cwd: z3.string().regex(/^\/[^\0]*$/, "cwd must be an absolute POSIX path"),
592
- shellPid: z3.number().int().positive(),
593
- lbuffer: z3.string(),
594
- rbuffer: z3.string()
595
- });
596
- var shellResultSchema = z3.discriminatedUnion("kind", [
597
- z3.object({
598
- kind: z3.literal("cancel")
599
- }).strict(),
600
- z3.object({
601
- kind: z3.literal("replace-buffer"),
602
- lbuffer: z3.string(),
603
- rbuffer: z3.string(),
604
- query: z3.string().optional()
605
- }),
606
- z3.object({
607
- kind: z3.literal("error"),
608
- message: z3.string()
609
- })
610
- ]);
611
-
612
- // src/contracts/request.ts
613
- var requestIntentSchema = z4.enum([
614
- "shell-command",
615
- "codebase",
616
- "filesystem",
617
- "general",
618
- "unknown"
619
- ]);
620
- var normalizedRequestSchema = shellRequestSchema.extend({
621
- intent: requestIntentSchema,
622
- confidence: z4.number().min(0).max(1)
623
- });
624
- var baseContextSchema = z4.object({
625
- queryText: z4.string(),
626
- cwd: z4.string(),
627
- ttyPath: z4.string(),
628
- shellPid: z4.number().int().positive(),
629
- /** Hardcoded 'zsh' for Phase 2 — cross-shell support is v2 scope. */
630
- shellName: z4.literal("zsh"),
631
- platform: z4.string(),
632
- timestamp: z4.string()
633
- });
634
- var intentDecisionSchema = z4.object({
635
- intent: requestIntentSchema,
636
- confidence: z4.number().min(0).max(1),
637
- signals: z4.array(z4.string())
638
- });
639
- var contextChunkSchema = z4.discriminatedUnion("kind", [
640
- z4.object({
641
- kind: z4.literal("git"),
642
- payload: z4.object({
643
- cwd: z4.string(),
644
- root: z4.string(),
645
- branch: z4.string().nullable(),
646
- dirty: z4.boolean(),
647
- /** File paths only (from git status --porcelain). No diff content, no file bytes. */
648
- changedFiles: z4.array(z4.string())
649
- })
650
- }),
651
- z4.object({
652
- kind: z4.literal("filesystem"),
653
- payload: z4.object({
654
- cwd: z4.string(),
655
- /** Apparent filename extracted from query text. No file bytes read. */
656
- apparentFilename: z4.string().nullable()
657
- })
658
- })
659
- ]);
660
- var contextEnvelopeSchema = z4.object({
661
- base: baseContextSchema,
662
- extras: z4.array(contextChunkSchema)
663
- });
664
-
665
- // src/intent/router.ts
666
- var GIT_PREFIX_RE = /^git\s/;
667
- var PKG_MANAGER_RE = /^(npm|pnpm|yarn)\s+(test|build|lint|run|start|dev|check|format|typecheck)\b/i;
668
- var FILESYSTEM_KEYWORDS_RE = /\b(?:rename|move(?:\s+file)?|find\s+files?|copy|delete|list\s+files?|image|folder|directory)\b/i;
669
- var SHELL_COMMAND_RE = /^(ls|echo|grep|awk|sed|curl|wget|ssh|cat|head|tail|cd|pwd|mkdir|touch|chmod|cp|mv|rm|which|man|ps|kill|sudo|command)\s|\|/;
670
- var FILE_PATH_RE = /(?:\/|\.\/|~\/|\.[a-zA-Z][\w-]*(?:\.\w+)?(?=\s|$)|\b[\w-]+\.(?:ts|tsx|js|jsx|mjs|cjs|py|go|rs|rb|java|sh|md|json|yaml|yml|toml|env|html|css|scss|txt|xml|svg|png|jpg|jpeg|gif|pdf|zip|tar|gz|lock|config)(?:\b|$))/;
671
- function classifyIntent(request) {
672
- const query = request.lbuffer.trim();
673
- if (query.length === 0) {
674
- return {
675
- intent: requestIntentSchema.enum.unknown,
676
- confidence: 1,
677
- signals: ["empty-query"]
678
- };
679
- }
680
- if (GIT_PREFIX_RE.test(query)) {
681
- return {
682
- intent: requestIntentSchema.enum["shell-command"],
683
- confidence: 1,
684
- signals: ["git-prefix"]
685
- };
686
- }
687
- if (PKG_MANAGER_RE.test(query)) {
688
- return {
689
- intent: requestIntentSchema.enum.codebase,
690
- confidence: 1,
691
- signals: ["pkg-manager-script"]
692
- };
693
- }
694
- if (FILESYSTEM_KEYWORDS_RE.test(query)) {
695
- return {
696
- intent: requestIntentSchema.enum.filesystem,
697
- confidence: 0.85,
698
- signals: ["filesystem-keyword"]
699
- };
700
- }
701
- if (SHELL_COMMAND_RE.test(query)) {
702
- return {
703
- intent: requestIntentSchema.enum["shell-command"],
704
- confidence: 0.8,
705
- signals: ["shell-command-signal"]
706
- };
707
- }
708
- if (FILE_PATH_RE.test(query)) {
709
- return {
710
- intent: requestIntentSchema.enum.codebase,
711
- confidence: 0.9,
712
- signals: ["file-path-signal"]
713
- };
714
- }
715
- return {
716
- intent: requestIntentSchema.enum.general,
717
- confidence: 0.5,
718
- signals: ["no-strong-signal"]
719
- };
720
- }
721
-
722
- // src/context/base-context.ts
723
- function buildBaseContext(request) {
724
- return {
725
- queryText: request.lbuffer.trim(),
726
- cwd: request.cwd,
727
- ttyPath: request.ttyPath,
728
- shellPid: request.shellPid,
729
- shellName: "zsh",
730
- platform: process.platform,
731
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
732
- };
733
- }
734
-
735
- // src/context/pipeline.ts
736
- async function gatherContext(request) {
737
- void appendDebugLog("context", "pipeline start", {
738
- queryLength: request.lbuffer.length,
739
- intent: request.intent
740
- });
741
- const base = buildBaseContext(request);
742
- const decision = classifyIntent(request);
743
- void appendDebugLog("context", "intent decision", {
744
- intent: decision.intent,
745
- signals: decision.signals
746
- });
747
- const extras = [];
748
- const providers = listContextProviders();
749
- for (const provider of providers) {
750
- const supportsAllIntents = provider.intents.includes("*");
751
- const supportsIntent = provider.intents.includes(decision.intent);
752
- if (!supportsAllIntents && !supportsIntent) {
753
- continue;
754
- }
755
- try {
756
- const chunk = await provider.gather({ base, decision });
757
- if (chunk !== null) {
758
- extras.push(chunk);
759
- void appendDebugLog("context", "provider chunk gathered", {
760
- providerId: provider.id,
761
- kind: chunk.kind
762
- });
763
- }
764
- } catch (error) {
765
- void appendDebugLog("context", "provider error", {
766
- providerId: provider.id,
767
- message: error instanceof Error ? error.message : String(error)
768
- });
769
- }
770
- }
771
- const envelope = filterContextEnvelope({ base, extras });
772
- void appendDebugLog("context", "envelope assembled", { extraCount: extras.length });
773
- return envelope;
774
- }
775
-
776
- // src/daemon/bootstrap.ts
777
- import { spawn } from "child_process";
778
- import * as fs from "fs";
779
- import * as net from "net";
780
- import * as path from "path";
781
- import { fileURLToPath } from "url";
782
- var CONNECT_TIMEOUT_MS = 500;
783
- var POLL_INTERVAL_MS = 50;
784
- var POLL_MAX_ATTEMPTS = 40;
785
- function tryConnect(socketPath2) {
786
- return new Promise((resolve3) => {
787
- const timer = setTimeout(() => {
788
- client.destroy();
789
- resolve3(false);
790
- }, CONNECT_TIMEOUT_MS);
791
- const client = net.createConnection(socketPath2, () => {
792
- clearTimeout(timer);
793
- client.destroy();
794
- resolve3(true);
795
- });
796
- client.on("error", () => {
797
- clearTimeout(timer);
798
- resolve3(false);
799
- });
800
- });
801
- }
802
- async function waitForSocket(socketPath2, maxAttempts = POLL_MAX_ATTEMPTS) {
803
- for (let i = 0; i < maxAttempts; i++) {
804
- if (await tryConnect(socketPath2)) return true;
805
- await new Promise((res) => setTimeout(res, POLL_INTERVAL_MS));
806
- }
807
- return false;
808
- }
809
- function assertSafeSocketPath(socketPath2) {
810
- const resolved = path.resolve(socketPath2);
811
- const base = path.basename(resolved);
812
- const dir = path.dirname(resolved);
813
- const tmpRoots = ["/tmp", "/private/tmp"];
814
- if (!tmpRoots.includes(dir) || !base.startsWith("qq-") || !base.endsWith(".sock")) {
815
- throw new Error(`unsafe socket path rejected: ${socketPath2}`);
816
- }
817
- }
818
- async function ensureDaemon(socketPath2) {
819
- assertSafeSocketPath(socketPath2);
820
- void appendDebugLog("daemon", "ensure start", { socketPath: socketPath2 });
821
- if (await tryConnect(socketPath2)) {
822
- void appendDebugLog("daemon", "already running", { socketPath: socketPath2 });
823
- return;
824
- }
825
- try {
826
- fs.unlinkSync(socketPath2);
827
- } catch {
828
- }
829
- const qqBin = process.execPath;
830
- const qqScript = fileURLToPath(import.meta.url);
831
- if (!fs.existsSync(qqScript)) {
832
- void appendDebugLog("daemon", "script missing", { qqScript });
833
- throw new Error(`qq daemon script not found at: ${qqScript}. Run 'pnpm build' first.`);
834
- }
835
- void appendDebugLog("daemon", "spawning daemon", { qqScript, socketPath: socketPath2 });
836
- const child = spawn(qqBin, [qqScript, "daemon", "--socket", socketPath2], {
837
- detached: true,
838
- stdio: "ignore"
839
- });
840
- child.unref();
841
- const started = await waitForSocket(socketPath2);
842
- if (!started) {
843
- void appendDebugLog("daemon", "startup timeout", { socketPath: socketPath2 });
844
- throw new Error(`qq daemon did not start within the expected window (${socketPath2})`);
845
- }
846
- void appendDebugLog("daemon", "daemon ready", { socketPath: socketPath2 });
847
- }
848
-
849
- // src/providers/detect.ts
850
- import { execSync } from "child_process";
851
- import * as fsp2 from "fs/promises";
852
- import * as os from "os";
853
- function claudeOnPath() {
854
- try {
855
- execSync("which claude", { stdio: "pipe" });
856
- return true;
857
- } catch {
858
- return false;
859
- }
860
- }
861
- async function claudeAuthFileExists() {
862
- const home = os.homedir();
863
- const candidates = [`${home}/.claude/.credentials.json`, `${home}/.claude/credentials.json`];
864
- for (const p of candidates) {
865
- try {
866
- await fsp2.stat(p);
867
- return true;
868
- } catch {
869
- }
870
- }
871
- return false;
872
- }
873
- async function ollamaHealthy() {
874
- try {
875
- const res = await fetch("http://localhost:11434/api/tags", {
876
- signal: AbortSignal.timeout(300)
877
- });
878
- return res.ok;
879
- } catch {
880
- return false;
881
- }
882
- }
883
- async function detectProvider() {
884
- if (process.env.ANTHROPIC_API_KEY || readEnvValueFromDotEnvLocal("ANTHROPIC_API_KEY")) {
885
- return { kind: "anthropic-key" };
886
- }
887
- if (claudeOnPath() && await claudeAuthFileExists()) {
888
- return { kind: "claude-cli" };
889
- }
890
- if (await ollamaHealthy()) {
891
- return { kind: "ollama", baseUrl: "http://localhost:11434" };
892
- }
893
- if (process.env.OPENAI_API_KEY) {
894
- return { kind: "openai-key" };
895
- }
896
- return {
897
- kind: "none",
898
- message: "queque: no AI provider configured\nChecked (in order):\n 1. ANTHROPIC_API_KEY env var or .env.local\n 2. Claude CLI (claude on PATH + credentials file)\n 3. Ollama at http://localhost:11434\n 4. OPENAI_API_KEY env var\nSet one of the above and re-trigger ??"
899
- };
900
- }
901
-
902
- // src/providers/resolver.ts
903
- function resolveAdapter(detected) {
904
- switch (detected.kind) {
905
- case "anthropic-key": {
906
- const adapter = getProviderAdapter("claude");
907
- if (!adapter) {
908
- throw new Error(
909
- "QueQue: Claude provider is not registered \u2014 was bootstrapBuiltins() called?"
910
- );
911
- }
912
- return adapter;
913
- }
914
- case "claude-cli":
915
- throw new Error(
916
- "QueQue: Claude CLI detected but subprocess adapter is not wired yet (Phase 8). Set ANTHROPIC_API_KEY or use .env.local."
917
- );
918
- case "ollama":
919
- throw new Error(
920
- "QueQue: Ollama detected but local adapter is not wired yet (Phase 8). Set ANTHROPIC_API_KEY or use .env.local."
921
- );
922
- case "openai-key":
923
- throw new Error(
924
- "QueQue: OPENAI_API_KEY detected but OpenAI adapter is not wired yet (Phase 8). Set ANTHROPIC_API_KEY for Claude."
925
- );
926
- case "none":
927
- throw new Error(detected.message);
928
- }
929
- }
930
-
931
- // src/shared/socket-path.ts
932
- function socketPathForUid(uid) {
933
- return `/tmp/qq-${uid}.sock`;
934
- }
935
- function socketPath() {
936
- return socketPathForUid(process.getuid?.() ?? 0);
937
- }
938
-
939
- // src/ui/CandidateSelect.tsx
940
- import { Box as Box4, Text as Text4, useInput, useStdin, useStdout as useStdout2 } from "ink";
941
- import { useEffect as useEffect2, useState as useState2 } from "react";
942
-
943
- // src/ui/ControlsLine.tsx
944
- import { Box, Text } from "ink";
945
- import { jsx, jsxs } from "react/jsx-runtime";
946
- function ControlsLine() {
947
- return /* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
948
- /* @__PURE__ */ jsx(Text, { backgroundColor: "ansi256(238)", color: "white", bold: true, children: " \u2191\u2193 " }),
949
- /* @__PURE__ */ jsx(Text, { backgroundColor: "ansi256(245)", color: "black", children: " select " }),
950
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
951
- /* @__PURE__ */ jsx(Text, { backgroundColor: "ansi256(238)", color: "white", bold: true, children: " enter " }),
952
- /* @__PURE__ */ jsx(Text, { backgroundColor: "ansi256(245)", color: "black", children: " accept " }),
953
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
954
- /* @__PURE__ */ jsx(Text, { backgroundColor: "ansi256(238)", color: "white", bold: true, children: " esc " }),
955
- /* @__PURE__ */ jsx(Text, { backgroundColor: "ansi256(245)", color: "black", children: " cancel " })
956
- ] });
957
- }
958
-
959
- // src/ui/LoadingSpinner.tsx
960
- import { Text as Text2 } from "ink";
961
- import { useEffect, useState } from "react";
962
- import { jsx as jsx2 } from "react/jsx-runtime";
963
- var FRAMES = ["thinking\u2026", "thinking.", "thinking..", "thinking\u2026"];
964
- function LoadingSpinner() {
965
- const [frame, setFrame] = useState(0);
966
- useEffect(() => {
967
- const id = setInterval(() => {
968
- setFrame((f) => (f + 1) % FRAMES.length);
969
- }, 200);
970
- return () => clearInterval(id);
971
- }, []);
972
- return /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: FRAMES[frame] });
973
- }
974
-
975
- // src/ui/Modal.tsx
976
- import { Box as Box2, useStdout } from "ink";
977
- import { jsx as jsx3 } from "react/jsx-runtime";
978
- function Modal({ children }) {
979
- const { stdout } = useStdout();
980
- const width = Math.min(80, stdout.columns || 80);
981
- return /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", paddingX: 1, width, children });
982
- }
983
-
984
- // src/ui/modal-layout.ts
985
- function wrapText(text, maxWidth) {
986
- if (maxWidth < 1) {
987
- return [text];
988
- }
989
- const words = text.split(/\s+/).filter((word) => word.length > 0);
990
- if (words.length === 0) {
991
- return [""];
992
- }
993
- const lines = [];
994
- let line = "";
995
- for (const word of words) {
996
- let remaining = word;
997
- while (remaining.length > 0) {
998
- const candidate = line ? `${line} ${remaining}` : remaining;
999
- if (candidate.length <= maxWidth) {
1000
- line = candidate;
1001
- remaining = "";
1002
- break;
1003
- }
1004
- if (line) {
1005
- lines.push(line);
1006
- line = "";
1007
- continue;
1008
- }
1009
- lines.push(remaining.slice(0, maxWidth));
1010
- remaining = remaining.slice(maxWidth);
1011
- }
1012
- }
1013
- if (line) {
1014
- lines.push(line);
1015
- }
1016
- return lines;
1017
- }
1018
- function normalizeCommand(command) {
1019
- return command.trim();
1020
- }
1021
- function filterCandidates(candidates, query) {
1022
- if (!query) return candidates;
1023
- const lower = query.toLowerCase();
1024
- return candidates.filter((c) => normalizeCommand(c.command).toLowerCase().includes(lower));
1025
- }
1026
- function estimateCandidateSelectLines(input) {
1027
- let lines = 0;
1028
- if (input.initialQuery && input.initialQuery.length > 0) {
1029
- lines += 1;
1030
- }
1031
- lines += 1;
1032
- lines += 1;
1033
- if (input.error === true) {
1034
- lines += 1;
1035
- } else if (input.candidates === null) {
1036
- lines += 1;
1037
- } else {
1038
- const visible2 = filterCandidates(input.candidates, input.query);
1039
- if (visible2.length === 0) {
1040
- lines += 1;
1041
- } else {
1042
- for (const candidate of visible2) {
1043
- const command = normalizeCommand(candidate.command);
1044
- const commandLines = wrapText(command, input.contentWidth);
1045
- const explanationLines = candidate.explanation.length > 0 ? wrapText(candidate.explanation, input.contentWidth) : [];
1046
- lines += commandLines.length + explanationLines.length;
1047
- lines += 1;
1048
- }
1049
- }
1050
- }
1051
- const visible = input.candidates !== null && input.error !== true ? filterCandidates(input.candidates, input.query) : [];
1052
- const selectedCommand = visible[input.selectedIndex]?.command ? normalizeCommand(visible[input.selectedIndex].command) : void 0;
1053
- const showDestructiveWarning = selectedCommand !== void 0 && isDestructiveCommand(selectedCommand);
1054
- if (showDestructiveWarning) {
1055
- lines += 1;
1056
- lines += 1;
1057
- }
1058
- lines += 1;
1059
- lines += 1;
1060
- if (input.includeDevFooter) {
1061
- lines += 1;
1062
- }
1063
- return lines;
1064
- }
1065
-
1066
- // src/ui/SearchInput.tsx
1067
- import { Box as Box3, Text as Text3 } from "ink";
1068
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1069
- function SearchInput({ query }) {
1070
- return /* @__PURE__ */ jsxs2(Box3, { children: [
1071
- /* @__PURE__ */ jsx4(Text3, { color: "cyan", bold: true, children: "FILTER: " }),
1072
- query ? /* @__PURE__ */ jsx4(Text3, { children: query }) : /* @__PURE__ */ jsx4(Text3, { dimColor: true, children: "type to filter\u2026" })
1073
- ] });
1074
- }
1075
-
1076
- // src/ui/CandidateSelect.tsx
1077
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1078
- var ROW_GUTTER = " ";
1079
- var ACTIVE_HEAD = "\u250C> ";
1080
- var ACTIVE_TAIL = "\u2502 ";
1081
- var DESTRUCTIVE_WARNING_MARK = "\u26A0";
1082
- var DESTRUCTIVE_WARNING_TEXT = `${DESTRUCTIVE_WARNING_MARK} review carefully \u2014 this command may be destructive`;
1083
- var MODAL_MAX_WIDTH = 80;
1084
- var MODAL_PADDING_X = 2;
1085
- function linePrefix(active, lineIndex) {
1086
- if (!active) {
1087
- return ROW_GUTTER;
1088
- }
1089
- return lineIndex === 0 ? ACTIVE_HEAD : ACTIVE_TAIL;
1090
- }
1091
- function normalizeCommand2(command) {
1092
- return command.trim();
1093
- }
1094
- function CandidateSelect({
1095
- candidates,
1096
- initialQuery,
1097
- onSelect,
1098
- onCancel,
1099
- error,
1100
- onLayoutLinesChange
1101
- }) {
1102
- const { isRawModeSupported } = useStdin();
1103
- const { stdout } = useStdout2();
1104
- const modalWidth = Math.min(MODAL_MAX_WIDTH, stdout.columns || MODAL_MAX_WIDTH);
1105
- const contentWidth = modalWidth - MODAL_PADDING_X - ROW_GUTTER.length;
1106
- const [selectedIndex, setSelectedIndex] = useState2(0);
1107
- const [query, setQuery] = useState2(initialQuery ?? "");
1108
- useEffect2(() => {
1109
- if (!isRawModeSupported) {
1110
- onCancel();
1111
- }
1112
- }, [isRawModeSupported, onCancel]);
1113
- useEffect2(() => {
1114
- setSelectedIndex(0);
1115
- if (candidates && candidates.length > 0 && filterCandidates(candidates, query).length === 0) {
1116
- setQuery("");
1117
- }
1118
- }, [candidates]);
1119
- useEffect2(() => {
1120
- setSelectedIndex(0);
1121
- }, [query]);
1122
- const includeDevFooter = process.env.QQ_DEV_ROOT != null;
1123
- useEffect2(() => {
1124
- if (!onLayoutLinesChange) {
17
+ if (process.env.NODE_COMPILE_CACHE !== void 0 || process.env.NODE_DISABLE_COMPILE_CACHE !== void 0) {
1125
18
  return;
1126
19
  }
1127
- if (candidates === null && error !== true) {
20
+ const enableCompileCache2 = nodeModule.enableCompileCache;
21
+ if (typeof enableCompileCache2 !== "function" || typeof process.getuid !== "function") {
1128
22
  return;
1129
23
  }
1130
- onLayoutLinesChange(
1131
- estimateCandidateSelectLines({
1132
- candidates,
1133
- initialQuery,
1134
- query,
1135
- selectedIndex,
1136
- error,
1137
- contentWidth,
1138
- includeDevFooter
1139
- })
1140
- );
1141
- }, [
1142
- candidates,
1143
- initialQuery,
1144
- query,
1145
- selectedIndex,
1146
- error,
1147
- contentWidth,
1148
- includeDevFooter,
1149
- onLayoutLinesChange
1150
- ]);
1151
- useInput(
1152
- (input, key) => {
1153
- if (key.escape) {
1154
- onCancel();
1155
- return;
1156
- }
1157
- if (key.backspace) {
1158
- setQuery((q) => q.slice(0, -1));
1159
- return;
1160
- }
1161
- if (key.return && candidates) {
1162
- const visible = filterCandidates(candidates, query);
1163
- if (visible.length === 0) return;
1164
- const selected = visible[selectedIndex] ?? visible[0];
1165
- const command = normalizeCommand2(selected.command);
1166
- const explanation = selected.explanation || `see man ${command.split(" ")[0]}`;
1167
- onSelect(command, explanation);
1168
- return;
1169
- }
1170
- if (input && !key.ctrl && !key.meta) {
1171
- setQuery((q) => q + input);
1172
- return;
1173
- }
1174
- if (key.upArrow && candidates) {
1175
- const visible = filterCandidates(candidates, query);
1176
- setSelectedIndex((i) => i === 0 ? visible.length - 1 : i - 1);
1177
- return;
1178
- }
1179
- if (key.downArrow && candidates) {
1180
- const visible = filterCandidates(candidates, query);
1181
- setSelectedIndex((i) => (i + 1) % visible.length);
1182
- return;
1183
- }
1184
- },
1185
- { isActive: isRawModeSupported }
1186
- );
1187
- let content;
1188
- let showDestructiveWarning = false;
1189
- if (error === true) {
1190
- content = /* @__PURE__ */ jsx5(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text4, { dimColor: true, children: "provider error \u2014 press esc to return" }) });
1191
- } else if (candidates === null) {
1192
- content = /* @__PURE__ */ jsx5(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx5(LoadingSpinner, {}) });
1193
- } else {
1194
- const visible = filterCandidates(candidates, query);
1195
- const selectedCommand = visible[selectedIndex]?.command ? normalizeCommand2(visible[selectedIndex].command) : void 0;
1196
- showDestructiveWarning = selectedCommand !== void 0 && isDestructiveCommand(selectedCommand);
1197
- if (visible.length === 0) {
1198
- content = /* @__PURE__ */ jsx5(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text4, { dimColor: true, children: "no matches \u2014 try a different query" }) });
1199
- } else {
1200
- content = /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", marginTop: 1, children: visible.map((candidate, index) => {
1201
- const active = index === selectedIndex;
1202
- const command = normalizeCommand2(candidate.command);
1203
- const destructive = isDestructiveCommand(command);
1204
- const commandLines = wrapText(command, contentWidth);
1205
- const explanationLines = candidate.explanation.length > 0 ? wrapText(candidate.explanation, contentWidth) : [];
1206
- const blockLines = [...commandLines, ...explanationLines];
1207
- return /* @__PURE__ */ jsx5(
1208
- Box4,
1209
- {
1210
- flexDirection: "column",
1211
- marginBottom: 1,
1212
- children: blockLines.map((line, lineIndex) => {
1213
- const isCommandLine = lineIndex < commandLines.length;
1214
- const isLastCommandLine = isCommandLine && lineIndex === commandLines.length - 1;
1215
- const prefix = linePrefix(active, lineIndex);
1216
- return /* @__PURE__ */ jsxs3(Box4, { children: [
1217
- /* @__PURE__ */ jsx5(Text4, { color: active ? "ansi256(166)" : void 0, children: prefix }),
1218
- /* @__PURE__ */ jsx5(
1219
- Text4,
1220
- {
1221
- bold: active && isCommandLine,
1222
- color: active && isCommandLine ? "white" : void 0,
1223
- dimColor: !active || !isCommandLine,
1224
- children: line
1225
- }
1226
- ),
1227
- isLastCommandLine && destructive && /* @__PURE__ */ jsx5(Text4, { color: "yellow", children: ` ${DESTRUCTIVE_WARNING_MARK}` })
1228
- ] }, `${prefix}${line}`);
1229
- })
1230
- },
1231
- `${command}::${candidate.explanation}`
1232
- );
1233
- }) });
1234
- }
1235
- }
1236
- return /* @__PURE__ */ jsxs3(Modal, { children: [
1237
- initialQuery && initialQuery.length > 0 && /* @__PURE__ */ jsx5(Box4, { flexGrow: 0, height: 1, children: /* @__PURE__ */ jsx5(Text4, { dimColor: true, wrap: "truncate", children: `queque \u203A ${initialQuery}` }) }),
1238
- /* @__PURE__ */ jsx5(SearchInput, { query }),
1239
- content,
1240
- showDestructiveWarning && /* @__PURE__ */ jsx5(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "yellow", children: DESTRUCTIVE_WARNING_TEXT }) }),
1241
- /* @__PURE__ */ jsx5(ControlsLine, {}),
1242
- process.env.QQ_DEV_ROOT != null && /* @__PURE__ */ jsx5(Box4, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx5(Text4, { color: "yellow", children: `dev v${"0.4.2"} ${"bad1456"}` }) })
1243
- ] });
1244
- }
1245
-
1246
- // src/client/result-writer.ts
1247
- import * as fsp3 from "fs/promises";
1248
- async function writeShellResult(resultFile, result) {
1249
- const parsed = shellResultSchema.parse(result);
1250
- const line = `${JSON.stringify(parsed)}
1251
- `;
1252
- let isFifo = false;
1253
- try {
1254
- const st = await fsp3.stat(resultFile);
1255
- isFifo = st.isFIFO();
1256
- } catch {
1257
- }
1258
- if (isFifo) {
1259
- await fsp3.writeFile(resultFile, line, { encoding: "utf-8" });
1260
- } else {
1261
- const tmpFile = `${resultFile}.tmp`;
1262
- await fsp3.writeFile(tmpFile, line, { encoding: "utf-8" });
1263
- await fsp3.rename(tmpFile, resultFile);
1264
- }
1265
- }
1266
-
1267
- // src/client/zellij-pane-resize.ts
1268
- import { spawn as spawn2 } from "child_process";
1269
- var DEFAULT_MAX_PANE_HEIGHT = 24;
1270
- var MIN_PANE_HEIGHT = 6;
1271
- var ZELLIJ_FLOATING_FRAME_LINES = 2;
1272
- var lastSyncedHeight;
1273
- function readMaxZellijPaneHeight() {
1274
- const parsed = Number.parseInt(process.env.QQ_PANE_HEIGHT ?? "", 10);
1275
- if (Number.isFinite(parsed) && parsed >= MIN_PANE_HEIGHT) {
1276
- return parsed;
1277
- }
1278
- return DEFAULT_MAX_PANE_HEIGHT;
1279
- }
1280
- function resolveZellijPaneHeight(contentLines, maxHeight = readMaxZellijPaneHeight()) {
1281
- const withFrame = contentLines + ZELLIJ_FLOATING_FRAME_LINES;
1282
- return Math.min(Math.max(withFrame, MIN_PANE_HEIGHT), maxHeight);
1283
- }
1284
- function formatZellijPaneId(rawPaneId) {
1285
- if (rawPaneId.startsWith("terminal_") || rawPaneId.startsWith("plugin_")) {
1286
- return rawPaneId;
1287
- }
1288
- return `terminal_${rawPaneId}`;
1289
- }
1290
- function syncZellijFloatingPaneHeight(contentLines) {
1291
- if (process.env.ZELLIJ === void 0) {
1292
- return;
1293
- }
1294
- const rawPaneId = process.env.ZELLIJ_PANE_ID;
1295
- if (rawPaneId === void 0 || rawPaneId.length === 0) {
1296
- return;
1297
- }
1298
- const height = resolveZellijPaneHeight(contentLines);
1299
- if (height === lastSyncedHeight) {
1300
- return;
1301
- }
1302
- lastSyncedHeight = height;
1303
- const paneId = formatZellijPaneId(rawPaneId);
1304
- const child = spawn2(
1305
- "zellij",
1306
- ["action", "change-floating-pane-coordinates", "--pane-id", paneId, "--height", String(height)],
1307
- { stdio: "ignore" }
1308
- );
1309
- child.unref();
1310
- }
1311
-
1312
- // src/client/run-foreground.ts
1313
- function buildShellBuffers(command, explanation) {
1314
- const comment = ` # ${explanation}`;
1315
- const stripped = command.replace(/<([^>]+)>/g, "$1");
1316
- const firstAngle = command.indexOf("<");
1317
- if (firstAngle >= 0) {
1318
- return {
1319
- lbuffer: stripped.slice(0, firstAngle),
1320
- rbuffer: stripped.slice(firstAngle) + comment
1321
- };
1322
- }
1323
- return { lbuffer: command, rbuffer: comment };
1324
- }
1325
- async function runForegroundClient(args) {
1326
- const { requestFile, resultFile, resultMode } = args;
1327
- const uid = typeof process.getuid === "function" ? process.getuid() : 0;
1328
- const socketPath2 = socketPathForUid(uid);
1329
- const inZellij = process.env.ZELLIJ !== void 0;
1330
- void appendDebugLog("client", "foreground start", {
1331
- requestFile,
1332
- resultFile,
1333
- resultMode,
1334
- socketPath: socketPath2,
1335
- inZellij
1336
- });
1337
- const ttyHandle = await fsp4.open("/dev/tty", "r+").catch(() => null);
1338
- try {
1339
- const raw = await fsp4.readFile(requestFile, "utf-8");
1340
- const request = shellRequestSchema.parse(JSON.parse(raw.trim()));
1341
- void appendDebugLog("client", "request parsed", {
1342
- lbufferLength: request.lbuffer.length,
1343
- rbufferLength: request.rbuffer.length,
1344
- cwd: request.cwd,
1345
- shellPid: request.shellPid
1346
- });
1347
- const detectedProvider = await detectProvider();
1348
- void appendDebugLog("client", "provider detected", { kind: detectedProvider.kind });
1349
- if (detectedProvider.kind === "none") {
1350
- if (ttyHandle && !inZellij) {
1351
- try {
1352
- await ttyHandle.write(`\r
1353
- ${detectedProvider.message}\r
1354
- `);
1355
- } catch {
1356
- }
1357
- }
1358
- await writeShellResult(resultFile, { kind: "error", message: detectedProvider.message });
24
+ const xdgCacheHome = process.env.XDG_CACHE_HOME;
25
+ const cacheRoot = xdgCacheHome !== void 0 && isAbsolute(xdgCacheHome) ? xdgCacheHome : join(homedir(), ".cache");
26
+ const appDir = join(cacheRoot, "queque");
27
+ const cacheDir = join(appDir, "node-compile-cache");
28
+ mkdirSync(cacheDir, { recursive: true, mode: 448 });
29
+ const uid = process.getuid();
30
+ if (!isPrivateDirectory(appDir, uid) || !isPrivateDirectory(cacheDir, uid)) {
1359
31
  return;
1360
32
  }
1361
- await ensureDaemon(socketPath2);
1362
- void appendDebugLog("client", "daemon ensured", { socketPath: socketPath2 });
1363
- if (resultMode === "replace-buffer-fixture") {
1364
- const fixtureLbuffer = request.lbuffer || "echo hello";
1365
- await writeShellResult(resultFile, {
1366
- kind: "replace-buffer",
1367
- lbuffer: fixtureLbuffer,
1368
- rbuffer: request.rbuffer
1369
- });
1370
- void appendDebugLog("client", "wrote replace-buffer result", {
1371
- resultFile,
1372
- lbufferLength: fixtureLbuffer.length,
1373
- rbufferLength: request.rbuffer.length
1374
- });
1375
- } else if (resultMode === "llm") {
1376
- let ttyReadStream;
1377
- let ttyWriteStream;
1378
- try {
1379
- const decision = classifyIntent({ ...request, intent: "unknown", confidence: 0 });
1380
- void appendDebugLog("client", "intent classified", {
1381
- intent: decision.intent,
1382
- signals: decision.signals
1383
- });
1384
- const normalized = {
1385
- ...request,
1386
- intent: decision.intent,
1387
- confidence: decision.confidence
1388
- };
1389
- const envelope = await gatherContext(normalized);
1390
- void appendDebugLog("client", "context gathered", {
1391
- extraCount: envelope.extras.length
1392
- });
1393
- const adapter = resolveAdapter(detectedProvider);
1394
- void appendDebugLog("client", "provider adapter resolved", { kind: detectedProvider.kind });
1395
- if (ttyHandle) {
1396
- try {
1397
- ttyReadStream = new tty.ReadStream(ttyHandle.fd);
1398
- ttyWriteStream = new tty.WriteStream(ttyHandle.fd);
1399
- } catch {
1400
- }
1401
- }
1402
- if (!ttyReadStream || !ttyWriteStream) {
1403
- const msg = "\r\nqueque: could not open terminal for interactive input\r\n";
1404
- if (ttyHandle) {
1405
- try {
1406
- await ttyHandle.write(msg);
1407
- } catch {
1408
- }
1409
- }
1410
- await writeShellResult(resultFile, { kind: "error", message: msg.trim() });
1411
- return;
1412
- }
1413
- try {
1414
- ttyReadStream.setRawMode(true);
1415
- ttyReadStream.setRawMode(false);
1416
- } catch {
1417
- const msg = "\r\nqueque: terminal does not support interactive mode\r\nRun ?? from an interactive ZSH session.\r\n";
1418
- ttyWriteStream.write(msg);
1419
- await writeShellResult(resultFile, {
1420
- kind: "error",
1421
- message: "queque: terminal does not support interactive mode \u2014 run ?? from an interactive shell"
1422
- });
1423
- return;
1424
- }
1425
- const MODAL_VIEWPORT_LINES = 16;
1426
- if (ttyWriteStream && !inZellij) {
1427
- ttyWriteStream.write("\n".repeat(MODAL_VIEWPORT_LINES));
1428
- ttyWriteStream.write(`\x1B[${MODAL_VIEWPORT_LINES}A`);
1429
- ttyWriteStream.write("\x1B7");
1430
- }
1431
- await new Promise((resolve3) => {
1432
- let unmount;
1433
- let resolved = false;
1434
- const buildCandidateElement = (candidates, errorState) => React.createElement(CandidateSelect, {
1435
- candidates,
1436
- initialQuery: request.lbuffer,
1437
- error: errorState,
1438
- onSelect: async (command, explanation) => {
1439
- if (resolved) return;
1440
- resolved = true;
1441
- const { lbuffer, rbuffer } = buildShellBuffers(command, explanation);
1442
- await writeShellResult(resultFile, {
1443
- kind: "replace-buffer",
1444
- lbuffer,
1445
- rbuffer,
1446
- query: request.lbuffer
1447
- });
1448
- unmount?.();
1449
- },
1450
- onCancel: async () => {
1451
- if (resolved) return;
1452
- resolved = true;
1453
- await writeShellResult(resultFile, { kind: "cancel" });
1454
- unmount?.();
1455
- },
1456
- onLayoutLinesChange: inZellij ? syncZellijFloatingPaneHeight : void 0
1457
- });
1458
- const renderOptions = ttyReadStream && ttyWriteStream ? { stdin: ttyReadStream, stdout: ttyWriteStream, interactive: true } : { interactive: true };
1459
- const app = render(buildCandidateElement(null), renderOptions);
1460
- const clearScrollReserve = () => {
1461
- if (ttyWriteStream && !inZellij) {
1462
- try {
1463
- ttyWriteStream.write("\x1B8\x1B[J");
1464
- } catch {
1465
- }
1466
- }
1467
- };
1468
- const cleanupOnSignal = () => {
1469
- app.unmount();
1470
- clearScrollReserve();
1471
- process.exit(0);
1472
- };
1473
- process.once("SIGHUP", cleanupOnSignal);
1474
- process.once("SIGTERM", cleanupOnSignal);
1475
- unmount = (skipClear = false) => {
1476
- process.off("SIGHUP", cleanupOnSignal);
1477
- process.off("SIGTERM", cleanupOnSignal);
1478
- app.unmount();
1479
- if (!skipClear) clearScrollReserve();
1480
- resolve3();
1481
- };
1482
- adapter.fetchCandidates(envelope).then((candidates) => {
1483
- void appendDebugLog("client", "candidates received", { count: candidates.length });
1484
- app.rerender(buildCandidateElement(candidates));
1485
- }).catch(async (err) => {
1486
- const message = err instanceof Error ? err.message : String(err);
1487
- void appendDebugLog("client", "llm request failed", { message });
1488
- if (resolved) return;
1489
- resolved = true;
1490
- const errorMsg = `QueQue: ${message}`;
1491
- await writeShellResult(resultFile, { kind: "error", message: errorMsg });
1492
- unmount?.(true);
1493
- });
1494
- });
1495
- void appendDebugLog("client", "wrote llm result", { resultFile });
1496
- } catch (error) {
1497
- const message = error instanceof Error ? error.message : String(error);
1498
- void appendDebugLog("client", "llm request failed", { message });
1499
- const errorMsg = `QueQue: ${message}`;
1500
- await writeShellResult(resultFile, { kind: "error", message: errorMsg });
1501
- }
1502
- } else {
1503
- await writeShellResult(resultFile, { kind: "cancel" });
1504
- void appendDebugLog("client", "wrote cancel result", { resultFile });
1505
- }
1506
- } finally {
1507
- await ttyHandle?.close();
1508
- }
1509
- }
1510
-
1511
- // src/cli/commands/client.ts
1512
- function parseResultMode(mode) {
1513
- if (mode === void 0 || mode === "llm") return "llm";
1514
- if (mode === "cancel") return "cancel";
1515
- if (mode === "replace-buffer-fixture") return "replace-buffer-fixture";
1516
- void appendDebugLog("client", `unknown --result-mode "${mode}", defaulting to cancel`);
1517
- return "cancel";
1518
- }
1519
- async function clientCommand(options) {
1520
- const resultMode = parseResultMode(options.resultMode);
1521
- void appendDebugLog("client", "command start", {
1522
- requestFile: options.requestFile,
1523
- resultFile: options.resultFile,
1524
- resultMode
1525
- });
1526
- await runForegroundClient({
1527
- requestFile: options.requestFile,
1528
- resultFile: options.resultFile,
1529
- resultMode
1530
- });
1531
- }
1532
-
1533
- // src/daemon/server.ts
1534
- import * as net2 from "net";
1535
-
1536
- // src/contracts/ipc.ts
1537
- import { z as z5 } from "zod";
1538
- var ipcRequestSchema = z5.discriminatedUnion("kind", [
1539
- z5.object({
1540
- kind: z5.literal("ping")
1541
- }),
1542
- z5.object({
1543
- kind: z5.literal("ensure-session"),
1544
- shellPid: z5.number().int().positive(),
1545
- ttyPath: z5.string()
1546
- }),
1547
- z5.object({
1548
- kind: z5.literal("run-query"),
1549
- request: shellRequestSchema
1550
- })
1551
- ]);
1552
- var ipcResponseSchema = z5.discriminatedUnion("kind", [
1553
- z5.object({
1554
- kind: z5.literal("pong")
1555
- }),
1556
- z5.object({
1557
- kind: z5.literal("session-ready"),
1558
- socketPath: z5.string()
1559
- }),
1560
- z5.object({
1561
- kind: z5.literal("query-accepted"),
1562
- requestId: z5.string()
1563
- })
1564
- ]);
1565
-
1566
- // src/daemon/server.ts
1567
- function startDaemonServer(socketPath2) {
1568
- return new Promise((resolve3, reject) => {
1569
- const MAX_BUF_BYTES = 64 * 1024;
1570
- const server = net2.createServer((socket) => {
1571
- let buf = "";
1572
- void appendDebugLog("daemon", "socket connected", { socketPath: socketPath2 });
1573
- socket.on("data", (chunk) => {
1574
- if (buf.length + chunk.length > MAX_BUF_BYTES) {
1575
- socket.destroy(new Error("IPC message too large"));
1576
- return;
1577
- }
1578
- buf += chunk.toString();
1579
- let nl = buf.indexOf("\n");
1580
- while (nl !== -1) {
1581
- const line = buf.slice(0, nl).trim();
1582
- buf = buf.slice(nl + 1);
1583
- nl = buf.indexOf("\n");
1584
- if (!line) continue;
1585
- let parsed;
1586
- try {
1587
- parsed = JSON.parse(line);
1588
- } catch {
1589
- continue;
1590
- }
1591
- const result = ipcRequestSchema.safeParse(parsed);
1592
- if (!result.success) {
1593
- void appendDebugLog("daemon", "ignored unknown request", { line });
1594
- continue;
1595
- }
1596
- const req = result.data;
1597
- void appendDebugLog("daemon", "request received", req);
1598
- switch (req.kind) {
1599
- case "ping": {
1600
- socket.write(`${JSON.stringify({ kind: "pong" })}
1601
- `);
1602
- void appendDebugLog("daemon", "replied pong");
1603
- break;
1604
- }
1605
- case "ensure-session": {
1606
- socket.write(`${JSON.stringify({ kind: "session-ready", socketPath: socketPath2 })}
1607
- `);
1608
- void appendDebugLog("daemon", "replied session-ready", { socketPath: socketPath2 });
1609
- break;
1610
- }
1611
- case "run-query": {
1612
- const requestId = Math.random().toString(36).slice(2);
1613
- socket.write(`${JSON.stringify({ kind: "query-accepted", requestId })}
1614
- `);
1615
- void appendDebugLog("daemon", "replied query-accepted", { requestId });
1616
- break;
1617
- }
1618
- }
1619
- }
1620
- });
1621
- socket.on("error", () => {
1622
- });
1623
- });
1624
- server.on("error", reject);
1625
- server.listen(socketPath2, () => {
1626
- void appendDebugLog("daemon", "listening", { socketPath: socketPath2 });
1627
- resolve3(server);
1628
- });
1629
- });
1630
- }
1631
-
1632
- // src/cli/commands/daemon.ts
1633
- async function daemonCommand(options) {
1634
- const sock = options.socket ?? socketPath();
1635
- void appendDebugLog("daemon", "command start", { socket: sock, ensure: options.ensure });
1636
- if (options.ensure) {
1637
- await ensureDaemon(sock);
1638
- void appendDebugLog("daemon", "ensure command complete", { socket: sock });
1639
- return;
1640
- }
1641
- const server = await startDaemonServer(sock);
1642
- void appendDebugLog("daemon", "server started", { socket: sock });
1643
- const shutdown = () => {
1644
- void appendDebugLog("daemon", "shutdown", { socket: sock });
1645
- server.close(() => {
1646
- process.exit(0);
1647
- });
1648
- };
1649
- process.once("SIGTERM", shutdown);
1650
- process.once("SIGINT", shutdown);
1651
- await new Promise(() => {
1652
- });
1653
- }
1654
-
1655
- // src/cli/commands/init.ts
1656
- import { appendFileSync, existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
1657
- import { homedir as homedir3 } from "os";
1658
- import { dirname as dirname3, join as join3, resolve as resolve2 } from "path";
1659
- import { fileURLToPath as fileURLToPath2 } from "url";
1660
- var __dirname = dirname3(fileURLToPath2(import.meta.url));
1661
- var HOMEBREW_PREFIXES = ["/opt/homebrew", "/usr/local", "/home/linuxbrew/.linuxbrew"];
1662
- function _resolveScriptPath(shell) {
1663
- for (const prefix of HOMEBREW_PREFIXES) {
1664
- const p = resolve2(prefix, "share", "queque", `queque.${shell}`);
1665
- if (existsSync3(p)) return p;
1666
- }
1667
- return resolve2(__dirname, "../../shell", shell, `queque.${shell}`);
1668
- }
1669
- function hasShellIntegration(existing, shell) {
1670
- if (/# queque shell integration/.test(existing)) {
1671
- return true;
33
+ enableCompileCache2(cacheDir);
34
+ } catch {
1672
35
  }
1673
- const scriptPattern = new RegExp(`source\\s+["'][^"']*queque\\.${shell.replace(".", "\\.")}["']`);
1674
- return scriptPattern.test(existing);
1675
36
  }
1676
- function initCommand(shell) {
1677
- bootstrapBuiltins();
1678
- const supportedShells = listShellAdapters().map((adapter) => adapter.shell);
1679
- if (!supportedShells.includes(shell)) {
1680
- console.error(`Unsupported shell: '${shell}'. Supported: ${supportedShells.join(", ")}`);
1681
- process.exit(1);
1682
- }
1683
- const zshrc = join3(homedir3(), ".zshrc");
1684
- const scriptPath = _resolveScriptPath(shell);
1685
- const snippet = `source "${scriptPath}"`;
1686
- const existing = existsSync3(zshrc) ? readFileSync3(zshrc, "utf8") : "";
1687
- if (hasShellIntegration(existing, shell)) {
1688
- console.error("queque: shell integration already present in ~/.zshrc");
1689
- process.exit(0);
1690
- }
1691
- appendFileSync(zshrc, `
1692
- # queque shell integration
1693
- ${snippet}
37
+ enablePerUserCompileCache();
38
+ var appUrl = new URL("./app.js", import.meta.url).href;
39
+ try {
40
+ const app = await import(appUrl);
41
+ await app.main();
42
+ } catch (error) {
43
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}
1694
44
  `);
1695
- console.log("queque: added shell integration to ~/.zshrc \u2014 run: source ~/.zshrc");
1696
- }
1697
-
1698
- // src/cli/main.ts
1699
- function requiredOption(name, value) {
1700
- if (!value) {
1701
- throw new Error(`Missing required option: ${name}`);
1702
- }
1703
- return value;
1704
- }
1705
- var QQ_RESULT_FILE_PATTERN = /^\/tmp\/qq-sess\.[A-Za-z0-9]+\//;
1706
- process.on("uncaughtException", (err) => {
1707
- console.error("QueQue: uncaught exception:", err.message);
1708
- const resultFile = process.env.QQ_RESULT_FILE;
1709
- if (resultFile && QQ_RESULT_FILE_PATTERN.test(resultFile)) {
1710
- try {
1711
- fs2.writeFileSync(resultFile, '{"kind":"cancel"}\n');
1712
- } catch {
1713
- }
1714
- }
1715
- process.exit(1);
1716
- });
1717
- process.on("unhandledRejection", (reason) => {
1718
- const message = reason instanceof Error ? reason.message : String(reason);
1719
- console.error("QueQue: unhandled rejection:", message);
1720
- const resultFile = process.env.QQ_RESULT_FILE;
1721
- if (resultFile && QQ_RESULT_FILE_PATTERN.test(resultFile)) {
1722
- try {
1723
- fs2.writeFileSync(resultFile, '{"kind":"cancel"}\n');
1724
- } catch {
1725
- }
1726
- }
1727
- process.exit(1);
1728
- });
1729
- async function main(argv = process.argv.slice(2)) {
1730
- bootstrapBuiltins();
1731
- const cli = cac("qq");
1732
- cli.command("client").option("--request-file <path>", "Path to the serialized shell request").option("--result-file <path>", "Path to write the serialized shell result").option("--result-mode <mode>", "Result mode: llm | cancel | replace-buffer-fixture").action(async (options) => {
1733
- const requestFile = requiredOption("--request-file", options.requestFile);
1734
- const resultFile = requiredOption("--result-file", options.resultFile);
1735
- await clientCommand({ requestFile, resultFile, resultMode: options.resultMode });
1736
- });
1737
- cli.command(
1738
- "init <shell>",
1739
- "Print a source line for shell integration (append to ~/.zshrc with `qq init zsh >> ~/.zshrc`)"
1740
- ).action((shell) => {
1741
- initCommand(shell);
1742
- });
1743
- cli.command("daemon").option("--socket <path>", "Unix socket path for the daemon").option("--ensure", "Ensure the daemon is running before returning").action(async (options) => {
1744
- await daemonCommand(options);
1745
- });
1746
- cli.help();
1747
- cli.parse(["node", "qq", ...argv], { run: false });
1748
- await cli.runMatchedCommand();
1749
- }
1750
- var isDirectRun = process.argv[1] ? import.meta.url === new URL(`file://${fs2.realpathSync(process.argv[1])}`).href : false;
1751
- if (isDirectRun) {
1752
- void main().catch((error) => {
1753
- const message = error instanceof Error ? error.message : String(error);
1754
- console.error(message);
1755
- process.exitCode = 1;
1756
- });
45
+ process.exitCode = 1;
1757
46
  }
1758
- export {
1759
- main
1760
- };
1761
47
  //# sourceMappingURL=main.js.map