shipr-agent 0.1.2 → 0.1.3

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.
Files changed (2) hide show
  1. package/dist/index.js +182 -39
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -101218,6 +101218,34 @@ async function detectActiveProvider() {
101218
101218
  }
101219
101219
  return "none";
101220
101220
  }
101221
+ function getDefaultModel(providerName) {
101222
+ const factory = PROVIDER_MAP[providerName];
101223
+ if (!factory)
101224
+ return "default";
101225
+ const p = factory();
101226
+ const models = p.getModels();
101227
+ const standard = models.find((m) => m.defaultForComplexity?.includes("standard"));
101228
+ return standard?.id ?? models[0]?.id ?? "default";
101229
+ }
101230
+ function getAllModelsForProvider(providerName) {
101231
+ const factory = PROVIDER_MAP[providerName];
101232
+ if (!factory)
101233
+ return [];
101234
+ const p = factory();
101235
+ return p.getModels().map((m) => m.id);
101236
+ }
101237
+ function showCommandHints(partial2) {
101238
+ const query = partial2.toLowerCase();
101239
+ const matches = COMMANDS.filter((c) => c.name.startsWith(query));
101240
+ if (matches.length === 0)
101241
+ return;
101242
+ console.log("");
101243
+ for (const cmd of matches) {
101244
+ const args = cmd.args ? ` ${DIM2(cmd.args)}` : "";
101245
+ console.log(` ${BRAND8(cmd.name)}${args} ${DIM2("—")} ${DIM2(cmd.description)}`);
101246
+ }
101247
+ console.log("");
101248
+ }
101221
101249
  async function handleRun(args, state2) {
101222
101250
  if (!args.trim()) {
101223
101251
  console.log(BRAND8(" usage:"), "/run <mission description>");
@@ -101228,11 +101256,13 @@ async function handleRun(args, state2) {
101228
101256
  return;
101229
101257
  }
101230
101258
  const runner = new MissionRunner({
101231
- provider: state2.activeProvider !== "none" ? state2.activeProvider : undefined
101259
+ provider: state2.activeProvider !== "none" ? state2.activeProvider : undefined,
101260
+ model: state2.activeModel !== "default" ? state2.activeModel : undefined
101232
101261
  });
101233
101262
  state2.missionRunner = runner;
101234
101263
  state2.missionRunning = true;
101235
101264
  console.log(BRAND8(" ▶"), `Starting mission: ${source_default.bold(args.trim())}`);
101265
+ console.log(DIM2(` Provider: ${state2.activeProvider} Model: ${state2.activeModel}`));
101236
101266
  console.log(DIM2(` Use /status to check progress, /steer to adjust direction.
101237
101267
  `));
101238
101268
  state2.missionPromise = runner.run(args.trim()).then(() => {
@@ -101258,11 +101288,13 @@ async function handleSimulate(args, state2) {
101258
101288
  }
101259
101289
  const runner = new MissionRunner({
101260
101290
  provider: state2.activeProvider !== "none" ? state2.activeProvider : undefined,
101291
+ model: state2.activeModel !== "default" ? state2.activeModel : undefined,
101261
101292
  simulate: true
101262
101293
  });
101263
101294
  state2.missionRunner = runner;
101264
101295
  state2.missionRunning = true;
101265
101296
  console.log(BRAND8(" ▶"), `Simulating: ${source_default.bold(args.trim())}`);
101297
+ console.log(DIM2(` Provider: ${state2.activeProvider} Model: ${state2.activeModel}`));
101266
101298
  console.log(DIM2(` Dry-run — no files will be written.
101267
101299
  `));
101268
101300
  state2.missionPromise = runner.simulate(args.trim()).then(() => {
@@ -101277,6 +101309,71 @@ async function handleSimulate(args, state2) {
101277
101309
  process.stdout.write(PROMPT);
101278
101310
  });
101279
101311
  }
101312
+ function handleModel(args, state2) {
101313
+ const parts = args.trim().split(/\s+/).filter(Boolean);
101314
+ if (parts.length === 0) {
101315
+ console.log("");
101316
+ console.log(` ${BRAND8("Provider:")} ${source_default.bold(state2.activeProvider)}`);
101317
+ console.log(` ${BRAND8("Model:")} ${source_default.bold(state2.activeModel)}`);
101318
+ console.log("");
101319
+ console.log(DIM2(" Usage:"));
101320
+ console.log(DIM2(" /model <provider> Switch provider (uses default model)"));
101321
+ console.log(DIM2(" /model <provider> <model> Switch provider and model"));
101322
+ console.log("");
101323
+ console.log(DIM2(" Available:"));
101324
+ const providers = listProviders();
101325
+ for (const name of providers) {
101326
+ const factory = PROVIDER_MAP[name];
101327
+ if (!factory)
101328
+ continue;
101329
+ const p = factory();
101330
+ const models = p.getModels();
101331
+ const active = name === state2.activeProvider ? BRAND8(" ◀ active") : "";
101332
+ console.log(` ${source_default.bold(name)}${active}`);
101333
+ for (const m of models) {
101334
+ const isActive = name === state2.activeProvider && m.id === state2.activeModel;
101335
+ const marker = isActive ? source_default.green(" ● ") : " ";
101336
+ const ctx = `${(m.contextWindow / 1000).toFixed(0)}k`;
101337
+ console.log(`${marker}${m.id} ${DIM2(ctx)}`);
101338
+ }
101339
+ }
101340
+ console.log("");
101341
+ return;
101342
+ }
101343
+ const providerName = parts[0];
101344
+ const allProviders = listProviders();
101345
+ if (!allProviders.includes(providerName)) {
101346
+ const match = allProviders.find((p) => p.startsWith(providerName));
101347
+ if (match) {
101348
+ state2.activeProvider = match;
101349
+ state2.activeModel = getDefaultModel(match);
101350
+ console.log(source_default.green(" ✓"), `Provider: ${source_default.bold(match)} Model: ${source_default.bold(state2.activeModel)}`);
101351
+ return;
101352
+ }
101353
+ console.log(source_default.red(" ✗"), `Unknown provider: ${providerName}`);
101354
+ console.log(DIM2(` Available: ${allProviders.join(", ")}`));
101355
+ return;
101356
+ }
101357
+ state2.activeProvider = providerName;
101358
+ if (parts.length >= 2) {
101359
+ const modelId = parts[1];
101360
+ const available = getAllModelsForProvider(providerName);
101361
+ const exactMatch = available.find((m) => m === modelId);
101362
+ const fuzzyMatch = available.find((m) => m.includes(modelId));
101363
+ if (exactMatch) {
101364
+ state2.activeModel = exactMatch;
101365
+ } else if (fuzzyMatch) {
101366
+ state2.activeModel = fuzzyMatch;
101367
+ } else {
101368
+ console.log(source_default.yellow(" ⚠"), `Model "${modelId}" not found for ${providerName}. Using default.`);
101369
+ console.log(DIM2(` Available: ${available.join(", ")}`));
101370
+ state2.activeModel = getDefaultModel(providerName);
101371
+ }
101372
+ } else {
101373
+ state2.activeModel = getDefaultModel(providerName);
101374
+ }
101375
+ console.log(source_default.green(" ✓"), `Provider: ${source_default.bold(state2.activeProvider)} Model: ${source_default.bold(state2.activeModel)}`);
101376
+ }
101280
101377
  async function handleStatus() {
101281
101378
  const { readdir: readdir8, readFile: readFile21 } = await import("node:fs/promises");
101282
101379
  const { join: join28 } = await import("node:path");
@@ -101362,7 +101459,8 @@ async function handleResume(state2) {
101362
101459
  }
101363
101460
  console.log(BRAND8(" ⏯"), `Resuming: ${source_default.bold(target.title)} (was ${target.state})`);
101364
101461
  const runner = new MissionRunner({
101365
- provider: state2.activeProvider !== "none" ? state2.activeProvider : undefined
101462
+ provider: state2.activeProvider !== "none" ? state2.activeProvider : undefined,
101463
+ model: state2.activeModel !== "default" ? state2.activeModel : undefined
101366
101464
  });
101367
101465
  state2.missionRunner = runner;
101368
101466
  state2.missionRunning = true;
@@ -101383,9 +101481,8 @@ async function handleSteer(args) {
101383
101481
  console.log(BRAND8(" usage:"), "/steer <instruction>");
101384
101482
  return;
101385
101483
  }
101386
- const { mkdir: mkdir11, readFile: readFile21, writeFile: writeFile14 } = await import("node:fs/promises");
101484
+ const { mkdir: mkdir11, readFile: readFile21, writeFile: writeFile14, readdir: readdir8 } = await import("node:fs/promises");
101387
101485
  const { join: join28 } = await import("node:path");
101388
- const { readdir: readdir8 } = await import("node:fs/promises");
101389
101486
  const cwd2 = process.cwd();
101390
101487
  const missionsPath = join28(cwd2, ".shipr/missions");
101391
101488
  let dirs;
@@ -101449,7 +101546,7 @@ async function handleCost() {
101449
101546
  }
101450
101547
  if (totalCalls === 0) {
101451
101548
  console.log(DIM2(" No cost data."));
101452
- } else if (totalCalls > 0) {
101549
+ } else {
101453
101550
  console.log(DIM2(` ──────────────────────────────────────`));
101454
101551
  console.log(` ${source_default.bold("Total")} ${source_default.green(`$${totalCost.toFixed(4)}`)} ${DIM2(`${totalCalls} calls`)}`);
101455
101552
  }
@@ -101501,10 +101598,14 @@ function handleProviders() {
101501
101598
  continue;
101502
101599
  const p = factory();
101503
101600
  const models = p.getModels();
101504
- const modelNames = models.map((m) => m.id).join(", ");
101505
- console.log(` ${source_default.bold(name)} ${DIM2(modelNames)}`);
101601
+ console.log(` ${source_default.bold(name)} ${DIM2(`(${p.authMethod})`)}`);
101602
+ for (const m of models) {
101603
+ const price = m.inputPricePer1M === 0 ? DIM2("free") : DIM2(`$${m.inputPricePer1M.toFixed(2)}/$${m.outputPricePer1M.toFixed(2)} per 1M`);
101604
+ const ctx = DIM2(`${(m.contextWindow / 1000).toFixed(0)}k ctx`);
101605
+ console.log(` ${m.id} ${price} ${ctx}`);
101606
+ }
101607
+ console.log("");
101506
101608
  }
101507
- console.log("");
101508
101609
  }
101509
101610
  async function handleMissions() {
101510
101611
  const { readdir: readdir8, readFile: readFile21 } = await import("node:fs/promises");
@@ -101536,37 +101637,64 @@ function handleHelp() {
101536
101637
  console.log("");
101537
101638
  console.log(BRAND_BOLD(" Commands"));
101538
101639
  console.log("");
101539
- console.log(` ${BRAND8("/run")} <mission> Start an autonomous coding mission`);
101540
- console.log(` ${BRAND8("/simulate")} <mission> Dry-run — plan without writing files`);
101541
- console.log(` ${BRAND8("/resume")} Resume the last interrupted mission`);
101542
- console.log(` ${BRAND8("/status")} Show current mission progress`);
101543
- console.log(` ${BRAND8("/steer")} <text> Inject a steering directive mid-mission`);
101544
- console.log(` ${BRAND8("/cost")} Show token ledger and spend`);
101545
- console.log(` ${BRAND8("/auth")} list|add|revoke Manage provider credentials`);
101546
- console.log(` ${BRAND8("/providers")} List all 16 supported providers`);
101547
- console.log(` ${BRAND8("/missions")} Show mission history`);
101548
- console.log(` ${BRAND8("/help")} Show this help`);
101549
- console.log(` ${BRAND8("/exit")} Quit`);
101640
+ for (const cmd of COMMANDS) {
101641
+ const args = cmd.args ? ` ${DIM2(cmd.args)}` : "";
101642
+ const pad = " ".repeat(Math.max(1, 28 - cmd.name.length - (cmd.args ? cmd.args.length + 1 : 0)));
101643
+ console.log(` ${BRAND8(cmd.name)}${args}${pad}${DIM2(cmd.description)}`);
101644
+ }
101550
101645
  console.log("");
101551
101646
  }
101552
- function completer(line) {
101647
+ function completer(line, state2) {
101553
101648
  const trimmed2 = line.trimStart();
101554
101649
  if (!trimmed2.startsWith("/")) {
101555
101650
  return [[], line];
101556
101651
  }
101557
- const matches = SLASH_COMMANDS.filter((cmd) => cmd.startsWith(trimmed2.split(/\s/)[0] ?? ""));
101558
- return [matches.map((m) => m + " "), trimmed2];
101652
+ const parts = trimmed2.split(/\s+/);
101653
+ const cmd = parts[0] ?? "";
101654
+ if (parts.length === 1) {
101655
+ const matches = COMMAND_NAMES.filter((c) => c.startsWith(cmd));
101656
+ return [matches.map((m) => m + " "), cmd];
101657
+ }
101658
+ const arg = parts[parts.length - 1] ?? "";
101659
+ if (cmd === "/model") {
101660
+ if (parts.length === 2) {
101661
+ const providers = listProviders().filter((p) => p.startsWith(arg));
101662
+ return [providers, arg];
101663
+ }
101664
+ if (parts.length === 3) {
101665
+ const provider2 = parts[1] ?? "";
101666
+ const models = getAllModelsForProvider(provider2).filter((m) => m.startsWith(arg));
101667
+ return [models, arg];
101668
+ }
101669
+ }
101670
+ if (cmd === "/auth") {
101671
+ if (parts.length === 2) {
101672
+ const subs = ["list", "add", "revoke"].filter((s) => s.startsWith(arg));
101673
+ return [subs, arg];
101674
+ }
101675
+ if (parts.length === 3 && (parts[1] === "add" || parts[1] === "revoke")) {
101676
+ const providers = listProviders().filter((p) => p.startsWith(arg));
101677
+ return [providers, arg];
101678
+ }
101679
+ }
101680
+ return [[], line];
101559
101681
  }
101560
101682
  async function startRepl() {
101561
101683
  const activeProvider = await detectActiveProvider();
101684
+ const activeModel = activeProvider !== "none" ? getDefaultModel(activeProvider) : "default";
101562
101685
  console.log(BANNER);
101563
101686
  console.log(DIM2(` ${process.cwd()}`));
101564
101687
  console.log(` Provider: ${activeProvider !== "none" ? source_default.green(activeProvider) : source_default.yellow("none — run /auth add <provider>")}`);
101688
+ if (activeProvider !== "none") {
101689
+ console.log(` Model: ${source_default.green(activeModel)}`);
101690
+ }
101565
101691
  console.log("");
101566
- console.log(DIM2(` Type /help for commands, /run <mission> to start.
101692
+ console.log(DIM2(" Type /help for commands, /run <mission> to start."));
101693
+ console.log(DIM2(` Press Tab to autocomplete commands.
101567
101694
  `));
101568
101695
  const state2 = {
101569
101696
  activeProvider,
101697
+ activeModel,
101570
101698
  missionRunner: null,
101571
101699
  missionPromise: null,
101572
101700
  missionRunning: false
@@ -101575,7 +101703,7 @@ async function startRepl() {
101575
101703
  input: process.stdin,
101576
101704
  output: process.stdout,
101577
101705
  prompt: PROMPT,
101578
- completer,
101706
+ completer: (line) => completer(line, state2),
101579
101707
  terminal: true,
101580
101708
  historySize: 200
101581
101709
  });
@@ -101586,14 +101714,24 @@ async function startRepl() {
101586
101714
  rl.prompt();
101587
101715
  return;
101588
101716
  }
101717
+ if (trimmed2 === "/") {
101718
+ showCommandHints("/");
101719
+ rl.prompt();
101720
+ return;
101721
+ }
101589
101722
  if (!trimmed2.startsWith("/")) {
101590
- console.log(DIM2(` hint: use ${BRAND8("/run")} <your mission> to start`));
101723
+ console.log(DIM2(` hint: use ${BRAND8("/run")} <your mission> to start. Type ${BRAND8("/")} to see all commands.`));
101591
101724
  rl.prompt();
101592
101725
  return;
101593
101726
  }
101594
101727
  const spaceIdx = trimmed2.indexOf(" ");
101595
101728
  const cmd = spaceIdx === -1 ? trimmed2 : trimmed2.slice(0, spaceIdx);
101596
101729
  const args = spaceIdx === -1 ? "" : trimmed2.slice(spaceIdx + 1);
101730
+ if (spaceIdx === -1 && !COMMAND_NAMES.includes(cmd)) {
101731
+ showCommandHints(cmd);
101732
+ rl.prompt();
101733
+ return;
101734
+ }
101597
101735
  const dispatch = async () => {
101598
101736
  switch (cmd) {
101599
101737
  case "/run":
@@ -101614,6 +101752,9 @@ async function startRepl() {
101614
101752
  case "/cost":
101615
101753
  await handleCost();
101616
101754
  break;
101755
+ case "/model":
101756
+ handleModel(args, state2);
101757
+ break;
101617
101758
  case "/auth":
101618
101759
  await handleAuth(args);
101619
101760
  break;
@@ -101637,7 +101778,7 @@ async function startRepl() {
101637
101778
  process.exit(0);
101638
101779
  break;
101639
101780
  default:
101640
- console.log(DIM2(` Unknown command: ${cmd}. Type /help for available commands.`));
101781
+ console.log(DIM2(` Unknown command: ${cmd}. Type ${BRAND8("/")} to see all commands.`));
101641
101782
  }
101642
101783
  };
101643
101784
  dispatch().catch((err) => {
@@ -101652,7 +101793,7 @@ async function startRepl() {
101652
101793
  process.exit(0);
101653
101794
  });
101654
101795
  }
101655
- var BRAND8, BRAND_BOLD, DIM2, PROMPT, SLASH_COMMANDS, BANNER;
101796
+ var BRAND8, BRAND_BOLD, DIM2, PROMPT, COMMANDS, COMMAND_NAMES, BANNER;
101656
101797
  var init_repl = __esm(async () => {
101657
101798
  init_source();
101658
101799
  init_keychain();
@@ -101663,19 +101804,21 @@ var init_repl = __esm(async () => {
101663
101804
  BRAND_BOLD = BRAND8.bold;
101664
101805
  DIM2 = source_default.dim;
101665
101806
  PROMPT = `${BRAND8("shipr")} ${BRAND8("▶")} `;
101666
- SLASH_COMMANDS = [
101667
- "/run",
101668
- "/simulate",
101669
- "/resume",
101670
- "/status",
101671
- "/steer",
101672
- "/cost",
101673
- "/auth",
101674
- "/providers",
101675
- "/missions",
101676
- "/help",
101677
- "/exit"
101807
+ COMMANDS = [
101808
+ { name: "/run", args: "<mission>", description: "Start an autonomous coding mission" },
101809
+ { name: "/simulate", args: "<mission>", description: "Dry-run — plan without writing files" },
101810
+ { name: "/resume", args: "", description: "Resume the last interrupted mission" },
101811
+ { name: "/status", args: "", description: "Show current mission progress" },
101812
+ { name: "/steer", args: "<text>", description: "Inject a steering directive mid-mission" },
101813
+ { name: "/cost", args: "", description: "Show token ledger and spend" },
101814
+ { name: "/model", args: "[provider] [model]", description: "Set or show active provider and model" },
101815
+ { name: "/auth", args: "list|add|revoke", description: "Manage provider credentials" },
101816
+ { name: "/providers", args: "", description: "List all 16 supported providers" },
101817
+ { name: "/missions", args: "", description: "Show mission history" },
101818
+ { name: "/help", args: "", description: "Show this help" },
101819
+ { name: "/exit", args: "", description: "Quit" }
101678
101820
  ];
101821
+ COMMAND_NAMES = COMMANDS.map((c) => c.name);
101679
101822
  BANNER = `
101680
101823
  ${BRAND_BOLD(" ███████╗██╗ ██╗██╗██████╗ ██████╗ ")}
101681
101824
  ${BRAND_BOLD(" ██╔════╝██║ ██║██║██╔══██╗██╔══██╗")}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shipr-agent",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Fully autonomous terminal-based coding agent",
5
5
  "type": "module",
6
6
  "bin": {