oh-my-githubcopilot 1.5.7 → 1.8.0-alpha.f50f59a

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/bin/omp.mjs CHANGED
@@ -73,22 +73,22 @@ var init_renderer = __esm({
73
73
 
74
74
  // src/hud/statusline.mts
75
75
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
76
- import { homedir } from "os";
77
- import { dirname, join } from "path";
76
+ import { homedir as homedir2 } from "os";
77
+ import { dirname as dirname2, join as join2 } from "path";
78
78
  import { fileURLToPath } from "url";
79
- function getStatuslinePaths(home = process.env["HOME"] || homedir()) {
80
- const ompDir = join(home, ".omp");
81
- const hudDir = join(ompDir, "hud");
79
+ function getStatuslinePaths(home = process.env["HOME"] || homedir2()) {
80
+ const ompDir = join2(home, ".omp");
81
+ const hudDir = join2(ompDir, "hud");
82
82
  return {
83
- legacyLinePath: join(ompDir, "hud.line"),
83
+ legacyLinePath: join2(ompDir, "hud.line"),
84
84
  hudDir,
85
- statusJsonPath: join(hudDir, "status.json"),
86
- displayPath: join(hudDir, "display.txt"),
87
- tmuxSegmentPath: join(hudDir, "tmux-segment.sh")
85
+ statusJsonPath: join2(hudDir, "status.json"),
86
+ displayPath: join2(hudDir, "display.txt"),
87
+ tmuxSegmentPath: join2(hudDir, "tmux-segment.sh")
88
88
  };
89
89
  }
90
90
  function ensureParent(filePath) {
91
- mkdirSync(dirname(filePath), { recursive: true });
91
+ mkdirSync(dirname2(filePath), { recursive: true });
92
92
  }
93
93
  function writeAtomic(filePath, content, mode) {
94
94
  ensureParent(filePath);
@@ -228,8 +228,8 @@ __export(watch_exports, {
228
228
  runHudWatch: () => runHudWatch
229
229
  });
230
230
  import { readFileSync as readFileSync2 } from "fs";
231
- import { homedir as homedir2 } from "os";
232
- import { join as join2 } from "path";
231
+ import { homedir as homedir3 } from "os";
232
+ import { join as join3 } from "path";
233
233
  function readSnapshot() {
234
234
  try {
235
235
  const raw = readFileSync2(STATE_PATH, "utf-8");
@@ -279,13 +279,376 @@ var init_watch = __esm({
279
279
  init_statusline();
280
280
  init_renderer();
281
281
  DEFAULT_INTERVAL_MS = 2e3;
282
- STATE_PATH = join2(homedir2(), ".omp", "state", "session.json");
282
+ STATE_PATH = join3(homedir3(), ".omp", "state", "session.json");
283
+ }
284
+ });
285
+
286
+ // src/hooks/keyword-detector.mts
287
+ var keyword_detector_exports = {};
288
+ __export(keyword_detector_exports, {
289
+ processHook: () => processHook
290
+ });
291
+ function detectKeyword(prompt) {
292
+ const trimmed = prompt.trimStart();
293
+ for (const [keyword, skillId] of KEYWORD_ENTRIES) {
294
+ if (trimmed.startsWith(keyword)) {
295
+ return {
296
+ keyword,
297
+ skillId,
298
+ position: 0
299
+ };
300
+ }
301
+ }
302
+ const slashPattern = /^\/((?:omp:)?[a-zA-Z][a-zA-Z0-9-]*)\b/;
303
+ const slashMatch = trimmed.match(slashPattern);
304
+ if (slashMatch) {
305
+ const cmd = slashMatch[1].toLowerCase();
306
+ const skillId = KEYWORD_MAP[`/${cmd}`] ?? KEYWORD_MAP[`${cmd}:`];
307
+ if (skillId) {
308
+ return {
309
+ keyword: slashMatch[0],
310
+ skillId,
311
+ position: 0
312
+ };
313
+ }
314
+ }
315
+ const longNamespacePattern = /^\/?oh-my-githubcopilot:([a-zA-Z][a-zA-Z0-9-]*)\b/i;
316
+ const longNamespaceMatch = trimmed.match(longNamespacePattern);
317
+ if (longNamespaceMatch) {
318
+ const cmd = longNamespaceMatch[1].toLowerCase();
319
+ const skillId = KEYWORD_MAP[`/omp:${cmd}`] ?? KEYWORD_MAP[`/${cmd}`] ?? KEYWORD_MAP[`${cmd}:`];
320
+ if (skillId) {
321
+ return {
322
+ keyword: longNamespaceMatch[0],
323
+ skillId,
324
+ position: 0
325
+ };
326
+ }
327
+ }
328
+ return null;
329
+ }
330
+ function getCanonicalCommand(skillId) {
331
+ return CANONICAL_COMMAND_MAP[skillId] ?? `/omp:${skillId}`;
332
+ }
333
+ function processHook(input) {
334
+ const start = Date.now();
335
+ const log = [];
336
+ try {
337
+ if (input.hook_type !== "UserPromptSubmitted") {
338
+ return {
339
+ status: "skip",
340
+ latencyMs: Date.now() - start,
341
+ mutations: [],
342
+ log: ["Not a UserPromptSubmitted hook"]
343
+ };
344
+ }
345
+ const match = detectKeyword(input.prompt);
346
+ if (!match) {
347
+ return {
348
+ status: "ok",
349
+ latencyMs: Date.now() - start,
350
+ mutations: [],
351
+ log: []
352
+ };
353
+ }
354
+ const taskPart = input.prompt.slice(match.position + match.keyword.length).trim();
355
+ const rewritten = `${getCanonicalCommand(match.skillId)}${taskPart ? ` ${taskPart}` : ""}`;
356
+ log.push(`Keyword detected: "${match.keyword}" \u2192 skill: ${match.skillId}`);
357
+ log.push(`Rewritten: "${rewritten}"`);
358
+ return {
359
+ status: "ok",
360
+ latencyMs: Date.now() - start,
361
+ modifiedPrompt: rewritten,
362
+ mutations: [
363
+ { type: "set_mode", mode: match.skillId },
364
+ { type: "log", level: "info", message: `Skill activated: ${match.skillId}` }
365
+ ],
366
+ log
367
+ };
368
+ } catch (err) {
369
+ return {
370
+ status: "error",
371
+ latencyMs: Date.now() - start,
372
+ mutations: [],
373
+ log: [`Error: ${err}`]
374
+ };
375
+ }
376
+ }
377
+ async function readStdin() {
378
+ const chunks = [];
379
+ for await (const chunk of process.stdin) {
380
+ chunks.push(chunk);
381
+ }
382
+ return chunks.join("");
383
+ }
384
+ var KEYWORD_MAP, KEYWORD_ENTRIES, CANONICAL_COMMAND_MAP;
385
+ var init_keyword_detector = __esm({
386
+ async "src/hooks/keyword-detector.mts"() {
387
+ "use strict";
388
+ KEYWORD_MAP = {
389
+ "autopilot:": "autopilot",
390
+ "/autopilot": "autopilot",
391
+ "/omp:autopilot": "autopilot",
392
+ "ralph:": "ralph",
393
+ "/ralph": "ralph",
394
+ "/omp:ralph": "ralph",
395
+ "ulw:": "ultrawork",
396
+ "ultrawork:": "ultrawork",
397
+ "/ulw": "ultrawork",
398
+ "/ultrawork": "ultrawork",
399
+ "/omp:ulw": "ultrawork",
400
+ "/omp:ultrawork": "ultrawork",
401
+ "team:": "team",
402
+ "/team": "team",
403
+ "/omp:team": "team",
404
+ "eco:": "ecomode",
405
+ "ecomode:": "ecomode",
406
+ "/eco": "ecomode",
407
+ "/ecomode": "ecomode",
408
+ "/omp:eco": "ecomode",
409
+ "/omp:ecomode": "ecomode",
410
+ "swarm:": "swarm",
411
+ "/swarm": "swarm",
412
+ "/omp:swarm": "swarm",
413
+ "pipeline:": "pipeline",
414
+ "/pipeline": "pipeline",
415
+ "/omp:pipeline": "pipeline",
416
+ "deep interview:": "deep-interview",
417
+ "/deep-interview": "deep-interview",
418
+ "/omp:deep-interview": "deep-interview",
419
+ "plan:": "omp-plan",
420
+ "/plan": "omp-plan",
421
+ "/omp-plan": "omp-plan",
422
+ "/omp:plan": "omp-plan",
423
+ "setup:": "omp-setup",
424
+ "/setup": "omp-setup",
425
+ "/omp-setup": "omp-setup",
426
+ "/omp:setup": "omp-setup",
427
+ "mcp:": "mcp-setup",
428
+ "mcp-setup:": "mcp-setup",
429
+ "/mcp": "mcp-setup",
430
+ "/mcp-setup": "mcp-setup",
431
+ "/omp:mcp-setup": "mcp-setup",
432
+ "/hud": "hud",
433
+ "hud:": "hud",
434
+ "/omp:hud": "hud",
435
+ "/wiki": "wiki",
436
+ "wiki:": "wiki",
437
+ "/omp:wiki": "wiki",
438
+ "/learner": "learner",
439
+ "learner:": "learner",
440
+ "/omp:learner": "learner",
441
+ "/note": "note",
442
+ "note:": "note",
443
+ "/omp:note": "note",
444
+ "/trace": "trace",
445
+ "trace:": "trace",
446
+ "/omp:trace": "trace",
447
+ "/release": "release",
448
+ "release:": "release",
449
+ "/omp:release": "release",
450
+ "/configure-notifications": "configure-notifications",
451
+ "configure-notifications:": "configure-notifications",
452
+ "/omp:configure-notifications": "configure-notifications",
453
+ "/psm": "psm",
454
+ "psm:": "psm",
455
+ "/omp:psm": "psm",
456
+ "/swe-bench": "swe-bench",
457
+ "swe-bench:": "swe-bench",
458
+ "/omp:swe-bench": "swe-bench",
459
+ "graphify:": "graphify",
460
+ "graph build": "graphify",
461
+ "build graph": "graphify",
462
+ "graphwiki:": "graphwiki",
463
+ "graph:": "graph-provider",
464
+ "spending:": "spending",
465
+ "/graphify": "graphify",
466
+ "/omp:graphify": "graphify",
467
+ "/graphwiki": "graphwiki",
468
+ "/omp:graphwiki": "graphwiki",
469
+ "/graph-provider": "graph-provider",
470
+ "/omp:graph-provider": "graph-provider",
471
+ "/spending": "spending",
472
+ "/omp:spending": "spending",
473
+ "--consensus": "omp-plan",
474
+ "/omp:omp-doctor": "omp-doctor",
475
+ "/omp:ralplan": "ralplan",
476
+ "/omp:research": "research",
477
+ "doctor:": "doctor",
478
+ "/doctor": "doctor",
479
+ "/omp:doctor": "doctor",
480
+ "interview:": "interview",
481
+ "/interview": "interview",
482
+ "/omp:interview": "interview",
483
+ "notifications:": "notifications",
484
+ "/notifications": "notifications",
485
+ "/omp:notifications": "notifications",
486
+ "session:": "session",
487
+ "/session": "session",
488
+ "/omp:session": "session"
489
+ };
490
+ KEYWORD_ENTRIES = Object.entries(KEYWORD_MAP).sort(([a], [b]) => b.length - a.length);
491
+ CANONICAL_COMMAND_MAP = {
492
+ "omp-plan": "/omp:plan",
493
+ "omp-setup": "/setup",
494
+ "mcp-setup": "/mcp"
495
+ };
496
+ if (process.argv[1]?.endsWith("keyword-detector.mjs") || process.argv[1]?.endsWith("keyword-detector.mts")) {
497
+ const input = JSON.parse(await readStdin());
498
+ const output = processHook(input);
499
+ console.log(JSON.stringify(output));
500
+ }
283
501
  }
284
502
  });
285
503
 
286
504
  // src/index.mts
287
505
  import { parseArgs } from "util";
288
506
  import { createRequire } from "module";
507
+
508
+ // src/cli/update.mts
509
+ import { spawnSync } from "child_process";
510
+ import { mkdir, readFile, writeFile } from "fs/promises";
511
+ import { homedir } from "os";
512
+ import { dirname, join } from "path";
513
+ import { createInterface } from "node:readline/promises";
514
+ var CHECK_INTERVAL_MS = 12 * 60 * 60 * 1e3;
515
+ var PROMPTABLE_SUBCOMMANDS = /* @__PURE__ */ new Set(["hud", "psm", "bench"]);
516
+ var DISABLED_AUTO_UPDATE_VALUES = /* @__PURE__ */ new Set(["0", "false", "no", "off"]);
517
+ var ENABLED_DISABLE_FLAG_VALUES = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
518
+ function parseSemver(version) {
519
+ const match = version.trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/);
520
+ if (!match) return null;
521
+ return {
522
+ major: Number(match[1]),
523
+ minor: Number(match[2]),
524
+ patch: Number(match[3]),
525
+ prerelease: match[4] ?? null
526
+ };
527
+ }
528
+ function isNewerVersion(current, latest) {
529
+ const currentVersion = parseSemver(current);
530
+ const latestVersion = parseSemver(latest);
531
+ if (!currentVersion || !latestVersion) return false;
532
+ if (latestVersion.major !== currentVersion.major) return latestVersion.major > currentVersion.major;
533
+ if (latestVersion.minor !== currentVersion.minor) return latestVersion.minor > currentVersion.minor;
534
+ if (latestVersion.patch !== currentVersion.patch) return latestVersion.patch > currentVersion.patch;
535
+ if (currentVersion.prerelease && !latestVersion.prerelease) return true;
536
+ return false;
537
+ }
538
+ function shouldCheckForUpdates(nowMs, state, intervalMs = CHECK_INTERVAL_MS) {
539
+ if (!state?.last_checked_at) return true;
540
+ const lastCheckedAt = Date.parse(state.last_checked_at);
541
+ if (!Number.isFinite(lastCheckedAt)) return true;
542
+ return nowMs - lastCheckedAt >= intervalMs;
543
+ }
544
+ function isAutoUpdateDisabled(env = process.env) {
545
+ const autoUpdate = env["OMP_AUTO_UPDATE"]?.trim().toLowerCase();
546
+ if (autoUpdate && DISABLED_AUTO_UPDATE_VALUES.has(autoUpdate)) return true;
547
+ const disableCheck = env["OMP_DISABLE_UPDATE_CHECK"]?.trim().toLowerCase();
548
+ if (disableCheck && ENABLED_DISABLE_FLAG_VALUES.has(disableCheck)) return true;
549
+ return false;
550
+ }
551
+ function shouldSkipUpdatePrompt(subcommand2, flags2 = {}) {
552
+ if (flags2.help || flags2.version) return true;
553
+ return !PROMPTABLE_SUBCOMMANDS.has(subcommand2);
554
+ }
555
+ function updateStatePath(homeDir) {
556
+ return join(homeDir, ".omp", "state", "update-check.json");
557
+ }
558
+ async function readCachedUpdateState(statePath) {
559
+ try {
560
+ const raw = await readFile(statePath, "utf-8");
561
+ return JSON.parse(raw);
562
+ } catch {
563
+ return null;
564
+ }
565
+ }
566
+ async function writeCachedUpdateState(statePath, state) {
567
+ await mkdir(dirname(statePath), { recursive: true });
568
+ await writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
569
+ }
570
+ async function fetchLatestVersionFromNpm(packageName, timeoutMs = 3500) {
571
+ const controller = new AbortController();
572
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
573
+ try {
574
+ const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`;
575
+ const response = await fetch(url, { signal: controller.signal });
576
+ if (!response.ok) return null;
577
+ const payload = await response.json();
578
+ return typeof payload.version === "string" ? payload.version : null;
579
+ } catch {
580
+ return null;
581
+ } finally {
582
+ clearTimeout(timeout);
583
+ }
584
+ }
585
+ function runNpmGlobalUpdate(packageName, cwd) {
586
+ const result = spawnSync("npm", ["install", "-g", `${packageName}@latest`], {
587
+ encoding: "utf-8",
588
+ stdio: ["ignore", "ignore", "pipe"],
589
+ timeout: 12e4,
590
+ windowsHide: true,
591
+ cwd
592
+ });
593
+ if (result.error) return { ok: false, stderr: result.error.message };
594
+ if (result.status !== 0) {
595
+ return { ok: false, stderr: (result.stderr || "").trim() || `npm exited ${result.status}` };
596
+ }
597
+ return { ok: true, stderr: "" };
598
+ }
599
+ async function askYesNo(question) {
600
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
601
+ const readline = createInterface({ input: process.stdin, output: process.stdout });
602
+ try {
603
+ const answer = (await readline.question(question)).trim().toLowerCase();
604
+ return answer === "" || answer === "y" || answer === "yes";
605
+ } finally {
606
+ readline.close();
607
+ }
608
+ }
609
+ var defaultDependencies = {
610
+ nowMs: () => Date.now(),
611
+ readUpdateState: readCachedUpdateState,
612
+ writeUpdateState: writeCachedUpdateState,
613
+ fetchLatestVersion: fetchLatestVersionFromNpm,
614
+ askYesNo,
615
+ runGlobalUpdate: runNpmGlobalUpdate
616
+ };
617
+ async function maybeCheckAndPromptUpdate(context, dependencies = {}) {
618
+ const updateDependencies = {
619
+ ...defaultDependencies,
620
+ ...dependencies
621
+ };
622
+ try {
623
+ if (isAutoUpdateDisabled()) return;
624
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return;
625
+ if (shouldSkipUpdatePrompt(context.subcommand, context.flags)) return;
626
+ const statePath = updateStatePath(process.env["HOME"] || homedir());
627
+ const now = updateDependencies.nowMs();
628
+ const state = await updateDependencies.readUpdateState(statePath);
629
+ if (!shouldCheckForUpdates(now, state)) return;
630
+ const latestVersion = await updateDependencies.fetchLatestVersion(context.packageName);
631
+ await updateDependencies.writeUpdateState(statePath, {
632
+ last_checked_at: new Date(now).toISOString(),
633
+ last_seen_latest: latestVersion || state?.last_seen_latest
634
+ });
635
+ if (!latestVersion || !isNewerVersion(context.currentVersion, latestVersion)) return;
636
+ const approved = await updateDependencies.askYesNo(
637
+ `[omp] Update available: v${context.currentVersion} \u2192 v${latestVersion}. Update now? [Y/n] `
638
+ );
639
+ if (!approved) return;
640
+ console.log(`[omp] Running: npm install -g ${context.packageName}@latest`);
641
+ const result = updateDependencies.runGlobalUpdate(context.packageName, context.cwd);
642
+ if (result.ok) {
643
+ console.log(`[omp] Updated to v${latestVersion}. Restart this shell to load the new CLI.`);
644
+ } else {
645
+ console.log(`[omp] Update failed. Run manually: npm install -g ${context.packageName}@latest`);
646
+ }
647
+ } catch {
648
+ }
649
+ }
650
+
651
+ // src/index.mts
289
652
  var _require = createRequire(import.meta.url);
290
653
  var { version: PKG_VERSION, name: PKG_NAME } = _require("../package.json");
291
654
  var { positionals, values: flags } = parseArgs({
@@ -298,8 +661,23 @@ var { positionals, values: flags } = parseArgs({
298
661
  allowPositionals: true
299
662
  });
300
663
  var subcommand = positionals[0] || "hud";
664
+ var resolvedSubcommand = flags.version && !positionals[0] ? "version" : subcommand;
301
665
  async function main() {
302
- switch (subcommand) {
666
+ if (flags.help) {
667
+ printUsage();
668
+ return;
669
+ }
670
+ await maybeCheckAndPromptUpdate({
671
+ cwd: process.cwd(),
672
+ packageName: PKG_NAME,
673
+ currentVersion: PKG_VERSION,
674
+ subcommand: resolvedSubcommand,
675
+ flags: {
676
+ help: flags.help,
677
+ version: flags.version
678
+ }
679
+ });
680
+ switch (resolvedSubcommand) {
303
681
  case "hud":
304
682
  if (flags.watch) {
305
683
  const { runHudWatch: runHudWatch2 } = await Promise.resolve().then(() => (init_watch(), watch_exports));
@@ -317,18 +695,25 @@ async function main() {
317
695
  case "bench":
318
696
  await runBench(positionals.slice(1));
319
697
  break;
698
+ case "hook":
699
+ await runHook(positionals.slice(1));
700
+ break;
320
701
  default:
321
- console.error(`Unknown subcommand: ${subcommand}`);
322
- console.error("Usage: omp [hud|version|psm|bench]");
702
+ console.error(`Unknown subcommand: ${resolvedSubcommand}`);
703
+ printUsage(true);
323
704
  process.exit(1);
324
705
  }
325
706
  }
707
+ function printUsage(stderr = false) {
708
+ const output = stderr ? console.error : console.log;
709
+ output("Usage: omp [hud|version|psm|bench|hook] [--watch]");
710
+ }
326
711
  async function printHud() {
327
712
  try {
328
713
  const { readFileSync: readFileSync3 } = await import("fs");
329
- const { join: join3 } = await import("path");
330
- const { homedir: homedir3 } = await import("os");
331
- const hudPath = join3(homedir3(), ".omp", "hud.line");
714
+ const { join: join4 } = await import("path");
715
+ const { homedir: homedir4 } = await import("os");
716
+ const hudPath = join4(homedir4(), ".omp", "hud.line");
332
717
  const line = readFileSync3(hudPath, "utf-8").trim();
333
718
  console.log(line);
334
719
  } catch {
@@ -342,6 +727,25 @@ async function runPsm(_args) {
342
727
  console.log(" /omp:psm switch <name> Switch to session");
343
728
  console.log(" /omp:psm destroy <name> Destroy session");
344
729
  }
730
+ async function runHook(args) {
731
+ const hookId = args[0];
732
+ if (hookId !== "keyword-detector") {
733
+ console.error("Usage: omp hook keyword-detector");
734
+ process.exit(1);
735
+ }
736
+ const { processHook: processHook2 } = await init_keyword_detector().then(() => keyword_detector_exports);
737
+ const inputText = await readStdin2();
738
+ const input = JSON.parse(inputText || "{}");
739
+ const output = processHook2(input);
740
+ console.log(JSON.stringify(output));
741
+ }
742
+ async function readStdin2() {
743
+ const chunks = [];
744
+ for await (const chunk of process.stdin) {
745
+ chunks.push(String(chunk));
746
+ }
747
+ return chunks.join("");
748
+ }
345
749
  async function runBench(_args) {
346
750
  console.log("SWE-bench requires Node.js subprocess with Python evaluation harness.");
347
751
  console.log("Usage: /omp:swe-bench --suite lite --compare baseline");