shipr-agent 0.1.5 → 0.1.7

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 +306 -85
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -101201,12 +101201,92 @@ var init_mission_runner = __esm(async () => {
101201
101201
  await init_renderer3();
101202
101202
  });
101203
101203
 
101204
+ // package.json
101205
+ var require_package3 = __commonJS((exports, module) => {
101206
+ module.exports = {
101207
+ name: "shipr-agent",
101208
+ version: "0.1.7",
101209
+ description: "Fully autonomous terminal-based coding agent",
101210
+ type: "module",
101211
+ bin: {
101212
+ shipr: "./dist/index.js"
101213
+ },
101214
+ main: "./dist/index.js",
101215
+ files: [
101216
+ "dist/",
101217
+ "README.md",
101218
+ "LICENSE"
101219
+ ],
101220
+ scripts: {
101221
+ dev: "bun src/index.ts",
101222
+ build: `bun build src/index.ts --outdir dist --target node --format esm && node -e "const fs=require('fs');const f='dist/index.js';const pkg=JSON.parse(fs.readFileSync('package.json','utf8'));let c=fs.readFileSync(f,'utf8');c=c.replace('#!/usr/bin/env bun','#!/usr/bin/env node');c=c.replace('0.1.7',pkg.version);fs.writeFileSync(f,c)"`,
101223
+ "build:bin": "bun build src/index.ts --compile --outfile shipr",
101224
+ typecheck: "bunx tsc --noEmit",
101225
+ lint: "bunx biome check src/",
101226
+ "lint:fix": "bunx biome check src/ --write",
101227
+ test: "bun test",
101228
+ check: "bun run typecheck && bun run lint && bun test",
101229
+ prepublishOnly: "bun run typecheck && bun run build",
101230
+ postpublish: "npm install -g shipr-agent@latest"
101231
+ },
101232
+ keywords: [
101233
+ "ai",
101234
+ "coding-agent",
101235
+ "cli",
101236
+ "autonomous",
101237
+ "llm",
101238
+ "copilot",
101239
+ "openai",
101240
+ "anthropic",
101241
+ "terminal"
101242
+ ],
101243
+ author: "praveenmalik",
101244
+ license: "MIT",
101245
+ engines: {
101246
+ node: ">=18",
101247
+ bun: ">=1.0"
101248
+ },
101249
+ repository: {
101250
+ type: "git",
101251
+ url: "https://github.com/praveenmalik/shipr"
101252
+ },
101253
+ homepage: "https://github.com/praveenmalik/shipr#readme",
101254
+ dependencies: {
101255
+ "@aws-sdk/client-bedrock-runtime": "^3.1016.0",
101256
+ chalk: "^5.6.2",
101257
+ commander: "^14.0.3",
101258
+ ink: "^6.8.0",
101259
+ "js-yaml": "^4.1.1",
101260
+ ora: "^9.3.0",
101261
+ react: "^19.2.4",
101262
+ "simple-git": "^3.33.0",
101263
+ zod: "^4.3.6"
101264
+ },
101265
+ devDependencies: {
101266
+ "@biomejs/biome": "^2.4.8",
101267
+ "@types/js-yaml": "^4.0.9",
101268
+ "@types/react": "^19.2.14",
101269
+ "bun-types": "^1.3.11",
101270
+ "react-devtools-core": "^7.0.1",
101271
+ typescript: "^6.0.2"
101272
+ }
101273
+ };
101274
+ });
101275
+
101204
101276
  // src/cli/repl.ts
101205
101277
  var exports_repl = {};
101206
101278
  __export(exports_repl, {
101207
101279
  startRepl: () => startRepl
101208
101280
  });
101209
101281
  import { createInterface as createInterface3 } from "node:readline";
101282
+ function getVersion() {
101283
+ try {
101284
+ const pkg = require_package3();
101285
+ return pkg.version;
101286
+ } catch {
101287
+ return "__SHIPR_VERSION__";
101288
+ }
101289
+ }
101210
101290
  async function detectActiveProvider() {
101211
101291
  const providers = listProviders();
101212
101292
  for (const name of providers) {
@@ -101234,17 +101314,138 @@ function getAllModelsForProvider(providerName) {
101234
101314
  const p = factory();
101235
101315
  return p.getModels().map((m) => m.id);
101236
101316
  }
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("");
101317
+ function commandPicker(initial, rl) {
101318
+ return new Promise((resolve) => {
101319
+ let query = initial;
101320
+ let selectedIdx = 0;
101321
+ let scrollTop = 0;
101322
+ let drawnLines = 0;
101323
+ const stdin = process.stdin;
101324
+ const savedData = stdin.rawListeners("data").slice();
101325
+ const savedKeypress = stdin.rawListeners("keypress").slice();
101326
+ stdin.removeAllListeners("data");
101327
+ stdin.removeAllListeners("keypress");
101328
+ stdin.resume();
101329
+ stdin.setRawMode(true);
101330
+ function filtered() {
101331
+ const q = query.slice(1).toLowerCase();
101332
+ if (!q)
101333
+ return COMMANDS;
101334
+ return COMMANDS.filter((c) => c.name.slice(1).startsWith(q) || c.name.slice(1).includes(q));
101335
+ }
101336
+ function clamp() {
101337
+ const matches = filtered();
101338
+ if (selectedIdx >= matches.length)
101339
+ selectedIdx = Math.max(0, matches.length - 1);
101340
+ if (selectedIdx < scrollTop)
101341
+ scrollTop = selectedIdx;
101342
+ if (selectedIdx >= scrollTop + PICKER_VISIBLE)
101343
+ scrollTop = selectedIdx - PICKER_VISIBLE + 1;
101344
+ }
101345
+ function draw() {
101346
+ const matches = filtered();
101347
+ const visible = matches.slice(scrollTop, scrollTop + PICKER_VISIBLE);
101348
+ const total = matches.length;
101349
+ const lines = [];
101350
+ lines.push(` ${DIM2(">")} ${source_default.bold(query)}`);
101351
+ lines.push("");
101352
+ if (visible.length === 0) {
101353
+ lines.push(DIM2(" No matching commands."));
101354
+ } else {
101355
+ for (let i2 = 0;i2 < visible.length; i2++) {
101356
+ const item = visible[i2];
101357
+ const gi = scrollTop + i2;
101358
+ const sel = gi === selectedIdx;
101359
+ const pointer = sel ? BRAND8("❯ ") : " ";
101360
+ const name = sel ? BRAND_BOLD(item.name.padEnd(14)) : BRAND8(item.name.padEnd(14));
101361
+ const desc = sel ? item.description : DIM2(item.description);
101362
+ lines.push(` ${pointer}${name} ${desc}`);
101363
+ }
101364
+ }
101365
+ lines.push("");
101366
+ const from = total === 0 ? 0 : scrollTop + 1;
101367
+ const to = Math.min(scrollTop + PICKER_VISIBLE, total);
101368
+ lines.push(DIM2(` Use ↑↓ to navigate, Tab/Enter to select, Esc to cancel • Showing ${from}–${to} of ${total}`));
101369
+ if (drawnLines > 0) {
101370
+ process.stdout.write(`\x1B[${drawnLines}A\x1B[0J`);
101371
+ }
101372
+ for (const line of lines) {
101373
+ process.stdout.write(line + `
101374
+ `);
101375
+ }
101376
+ drawnLines = lines.length;
101377
+ }
101378
+ function cleanup() {
101379
+ stdin.removeListener("data", onKey);
101380
+ for (const l of savedData)
101381
+ stdin.addListener("data", l);
101382
+ for (const l of savedKeypress)
101383
+ stdin.addListener("keypress", l);
101384
+ }
101385
+ function cancel() {
101386
+ cleanup();
101387
+ if (drawnLines > 0)
101388
+ process.stdout.write(`\x1B[${drawnLines}A\x1B[0J`);
101389
+ resolve(null);
101390
+ }
101391
+ function select() {
101392
+ const matches = filtered();
101393
+ const chosen = matches[selectedIdx]?.name ?? null;
101394
+ cleanup();
101395
+ if (drawnLines > 0)
101396
+ process.stdout.write(`\x1B[${drawnLines}A\x1B[0J`);
101397
+ resolve(chosen);
101398
+ }
101399
+ function onKey(data) {
101400
+ const key = data.toString();
101401
+ if (key === "\x1B" || key === "\x03") {
101402
+ cancel();
101403
+ return;
101404
+ }
101405
+ if (key === "\r" || key === `
101406
+ ` || key === "\t") {
101407
+ select();
101408
+ return;
101409
+ }
101410
+ if (key === "" || key === "\b") {
101411
+ if (query.length > 1) {
101412
+ query = query.slice(0, -1);
101413
+ selectedIdx = 0;
101414
+ scrollTop = 0;
101415
+ draw();
101416
+ } else {
101417
+ cancel();
101418
+ }
101419
+ return;
101420
+ }
101421
+ if (key === "\x1B[A") {
101422
+ if (selectedIdx > 0) {
101423
+ selectedIdx--;
101424
+ clamp();
101425
+ draw();
101426
+ }
101427
+ return;
101428
+ }
101429
+ if (key === "\x1B[B") {
101430
+ if (selectedIdx < filtered().length - 1) {
101431
+ selectedIdx++;
101432
+ clamp();
101433
+ draw();
101434
+ }
101435
+ return;
101436
+ }
101437
+ if (key.length === 1 && key >= " ") {
101438
+ query += key;
101439
+ selectedIdx = 0;
101440
+ scrollTop = 0;
101441
+ draw();
101442
+ return;
101443
+ }
101444
+ }
101445
+ stdin.on("data", onKey);
101446
+ drawnLines = 0;
101447
+ draw();
101448
+ });
101248
101449
  }
101249
101450
  async function handleRun(args, state2) {
101250
101451
  if (!args.trim()) {
@@ -101309,13 +101510,18 @@ async function handleSimulate(args, state2) {
101309
101510
  process.stdout.write(PROMPT);
101310
101511
  });
101311
101512
  }
101312
- function interactivePick(title, items) {
101513
+ function interactivePick(title, items, rl) {
101313
101514
  return new Promise((resolve) => {
101314
101515
  let cursor = items.findIndex((i2) => i2.active) ?? 0;
101315
101516
  if (cursor < 0)
101316
101517
  cursor = 0;
101317
101518
  const stdin = process.stdin;
101318
- const wasRaw = stdin.isRaw;
101519
+ const savedData = stdin.rawListeners("data").slice();
101520
+ const savedKeypress = stdin.rawListeners("keypress").slice();
101521
+ stdin.removeAllListeners("data");
101522
+ stdin.removeAllListeners("keypress");
101523
+ stdin.resume();
101524
+ stdin.setRawMode(true);
101319
101525
  function render2() {
101320
101526
  const totalLines = items.length + 4;
101321
101527
  process.stdout.write(`\x1B[${totalLines}A\x1B[0J`);
@@ -101344,10 +101550,10 @@ function interactivePick(title, items) {
101344
101550
  }
101345
101551
  function cleanup() {
101346
101552
  stdin.removeListener("data", onKey);
101347
- stdin.setRawMode(wasRaw ?? false);
101348
- if (!stdin.isRaw) {
101349
- stdin.resume();
101350
- }
101553
+ for (const l of savedData)
101554
+ stdin.addListener("data", l);
101555
+ for (const l of savedKeypress)
101556
+ stdin.addListener("keypress", l);
101351
101557
  }
101352
101558
  function onKey(data) {
101353
101559
  const key = data.toString();
@@ -101376,8 +101582,6 @@ function interactivePick(title, items) {
101376
101582
  return;
101377
101583
  }
101378
101584
  }
101379
- stdin.setRawMode(true);
101380
- stdin.resume();
101381
101585
  stdin.on("data", onKey);
101382
101586
  draw();
101383
101587
  });
@@ -101421,7 +101625,7 @@ async function handleModel(args, state2, rl) {
101421
101625
  active: name === state2.activeProvider
101422
101626
  };
101423
101627
  });
101424
- const chosenProvider = await interactivePick("Select Provider", providerItems);
101628
+ const chosenProvider = await interactivePick("Select Provider", providerItems, rl);
101425
101629
  if (!chosenProvider) {
101426
101630
  rl.resume();
101427
101631
  return;
@@ -101449,7 +101653,7 @@ async function handleModel(args, state2, rl) {
101449
101653
  active: id === state2.activeModel && chosenProvider === state2.activeProvider
101450
101654
  };
101451
101655
  });
101452
- const chosenModel = await interactivePick("Select Model", modelItems);
101656
+ const chosenModel = await interactivePick("Select Model", modelItems, rl);
101453
101657
  if (!chosenModel) {
101454
101658
  state2.activeModel = defaultModel;
101455
101659
  console.log(source_default.green(" ✓"), `Provider: ${source_default.bold(state2.activeProvider)} Model: ${source_default.bold(state2.activeModel)}`);
@@ -101776,7 +101980,7 @@ async function startRepl() {
101776
101980
  }
101777
101981
  console.log("");
101778
101982
  console.log(DIM2(" Type /help for commands, /run <mission> to start."));
101779
- console.log(DIM2(` Press Tab to autocomplete commands.
101983
+ console.log(DIM2(` Type / to browse all commands interactively.
101780
101984
  `));
101781
101985
  const state2 = {
101782
101986
  activeProvider,
@@ -101794,17 +101998,62 @@ async function startRepl() {
101794
101998
  historySize: 200
101795
101999
  });
101796
102000
  rl.prompt();
102001
+ const NO_ARG_COMMANDS = new Set(["/resume", "/status", "/cost", "/providers", "/missions", "/help", "/exit", "/quit", "/q", "/model"]);
102002
+ async function dispatchCommand(cmd, args) {
102003
+ switch (cmd) {
102004
+ case "/run":
102005
+ await handleRun(args, state2);
102006
+ break;
102007
+ case "/simulate":
102008
+ await handleSimulate(args, state2);
102009
+ break;
102010
+ case "/resume":
102011
+ await handleResume(state2);
102012
+ break;
102013
+ case "/status":
102014
+ await handleStatus();
102015
+ break;
102016
+ case "/steer":
102017
+ await handleSteer(args);
102018
+ break;
102019
+ case "/cost":
102020
+ await handleCost();
102021
+ break;
102022
+ case "/model":
102023
+ await handleModel(args, state2, rl);
102024
+ break;
102025
+ case "/auth":
102026
+ await handleAuth(args);
102027
+ break;
102028
+ case "/providers":
102029
+ handleProviders();
102030
+ break;
102031
+ case "/missions":
102032
+ await handleMissions();
102033
+ break;
102034
+ case "/help":
102035
+ handleHelp();
102036
+ break;
102037
+ case "/exit":
102038
+ case "/quit":
102039
+ case "/q":
102040
+ if (state2.missionRunning) {
102041
+ console.log(source_default.yellow(" ⚠"), "A mission is still running. It will be interrupted.");
102042
+ }
102043
+ console.log(DIM2(" Goodbye."));
102044
+ rl.close();
102045
+ process.exit(0);
102046
+ break;
102047
+ default:
102048
+ console.log(DIM2(` Unknown command: ${cmd}. Type ${BRAND8("/")} to see all commands.`));
102049
+ }
102050
+ }
101797
102051
  rl.on("line", (line) => {
101798
102052
  const trimmed2 = line.trim();
101799
102053
  if (!trimmed2) {
101800
102054
  rl.prompt();
101801
102055
  return;
101802
102056
  }
101803
- if (trimmed2 === "/") {
101804
- showCommandHints("/");
101805
- rl.prompt();
101806
- return;
101807
- }
101808
102057
  if (!trimmed2.startsWith("/")) {
101809
102058
  console.log(DIM2(` hint: use ${BRAND8("/run")} <your mission> to start. Type ${BRAND8("/")} to see all commands.`));
101810
102059
  rl.prompt();
@@ -101813,61 +102062,33 @@ async function startRepl() {
101813
102062
  const spaceIdx = trimmed2.indexOf(" ");
101814
102063
  const cmd = spaceIdx === -1 ? trimmed2 : trimmed2.slice(0, spaceIdx);
101815
102064
  const args = spaceIdx === -1 ? "" : trimmed2.slice(spaceIdx + 1);
101816
- if (spaceIdx === -1 && !COMMAND_NAMES.includes(cmd)) {
101817
- showCommandHints(cmd);
101818
- rl.prompt();
102065
+ if (trimmed2 === "/" || spaceIdx === -1 && !COMMAND_NAMES.includes(cmd)) {
102066
+ rl.pause();
102067
+ let skipFinalPrompt = false;
102068
+ commandPicker(trimmed2, rl).then(async (chosen) => {
102069
+ if (chosen) {
102070
+ if (NO_ARG_COMMANDS.has(chosen)) {
102071
+ await dispatchCommand(chosen, "");
102072
+ rl.resume();
102073
+ } else {
102074
+ skipFinalPrompt = true;
102075
+ rl.resume();
102076
+ rl.prompt();
102077
+ rl.write(chosen + " ");
102078
+ }
102079
+ } else {
102080
+ rl.resume();
102081
+ }
102082
+ }).catch((err) => {
102083
+ rl.resume();
102084
+ console.log(source_default.red(" ✗"), err.message);
102085
+ }).finally(() => {
102086
+ if (!skipFinalPrompt)
102087
+ rl.prompt();
102088
+ });
101819
102089
  return;
101820
102090
  }
101821
- const dispatch = async () => {
101822
- switch (cmd) {
101823
- case "/run":
101824
- await handleRun(args, state2);
101825
- break;
101826
- case "/simulate":
101827
- await handleSimulate(args, state2);
101828
- break;
101829
- case "/resume":
101830
- await handleResume(state2);
101831
- break;
101832
- case "/status":
101833
- await handleStatus();
101834
- break;
101835
- case "/steer":
101836
- await handleSteer(args);
101837
- break;
101838
- case "/cost":
101839
- await handleCost();
101840
- break;
101841
- case "/model":
101842
- await handleModel(args, state2, rl);
101843
- break;
101844
- case "/auth":
101845
- await handleAuth(args);
101846
- break;
101847
- case "/providers":
101848
- handleProviders();
101849
- break;
101850
- case "/missions":
101851
- await handleMissions();
101852
- break;
101853
- case "/help":
101854
- handleHelp();
101855
- break;
101856
- case "/exit":
101857
- case "/quit":
101858
- case "/q":
101859
- if (state2.missionRunning) {
101860
- console.log(source_default.yellow(" ⚠"), "A mission is still running. It will be interrupted.");
101861
- }
101862
- console.log(DIM2(" Goodbye."));
101863
- rl.close();
101864
- process.exit(0);
101865
- break;
101866
- default:
101867
- console.log(DIM2(` Unknown command: ${cmd}. Type ${BRAND8("/")} to see all commands.`));
101868
- }
101869
- };
101870
- dispatch().catch((err) => {
102091
+ dispatchCommand(cmd, args).catch((err) => {
101871
102092
  console.log(source_default.red(" ✗"), err.message);
101872
102093
  }).finally(() => {
101873
102094
  rl.prompt();
@@ -101879,7 +102100,7 @@ async function startRepl() {
101879
102100
  process.exit(0);
101880
102101
  });
101881
102102
  }
101882
- var BRAND8, BRAND_BOLD, DIM2, PROMPT, COMMANDS, COMMAND_NAMES, BANNER;
102103
+ var BRAND8, BRAND_BOLD, DIM2, PROMPT, COMMANDS, COMMAND_NAMES, BANNER, PICKER_VISIBLE = 6;
101883
102104
  var init_repl = __esm(async () => {
101884
102105
  init_source();
101885
102106
  init_keychain();
@@ -101911,7 +102132,7 @@ ${BRAND_BOLD(" ██╔════╝██║ ██║██║██╔
101911
102132
  ${BRAND_BOLD(" ███████╗███████║██║██████╔╝██████╔╝")}
101912
102133
  ${BRAND_BOLD(" ╚════██║██╔══██║██║██╔═══╝ ██╔══██╗")}
101913
102134
  ${BRAND_BOLD(" ███████║██║ ██║██║██║ ██║ ██║")}
101914
- ${BRAND_BOLD(" ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═╝")}
102135
+ ${BRAND_BOLD(" ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═╝")} ${DIM2(`v${getVersion()}`)}
101915
102136
  `;
101916
102137
  });
101917
102138
 
@@ -103367,13 +103588,13 @@ async function findActiveMission(cwd2) {
103367
103588
  }
103368
103589
 
103369
103590
  // src/index.ts
103370
- function getVersion() {
103591
+ function getVersion2() {
103371
103592
  try {
103372
103593
  const require2 = createRequire2(import.meta.url);
103373
103594
  const pkg = require2("../package.json");
103374
103595
  return pkg.version;
103375
103596
  } catch {
103376
- return "0.1.5";
103597
+ return "__SHIPR_VERSION__";
103377
103598
  }
103378
103599
  }
103379
103600
  var hasArgs = process.argv.length > 2;
@@ -103382,7 +103603,7 @@ if (!hasArgs) {
103382
103603
  await startRepl2();
103383
103604
  } else {
103384
103605
  const program2 = new Command;
103385
- program2.name("shipr").version(getVersion()).description("Fully autonomous terminal-based coding agent");
103606
+ program2.name("shipr").version(getVersion2()).description("Fully autonomous terminal-based coding agent");
103386
103607
  registerRunCommand(program2);
103387
103608
  registerNewCommand(program2);
103388
103609
  registerResumeCommand(program2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shipr-agent",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Fully autonomous terminal-based coding agent",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,7 +21,8 @@
21
21
  "lint:fix": "bunx biome check src/ --write",
22
22
  "test": "bun test",
23
23
  "check": "bun run typecheck && bun run lint && bun test",
24
- "prepublishOnly": "bun run typecheck && bun run build"
24
+ "prepublishOnly": "bun run typecheck && bun run build",
25
+ "postpublish": "npm install -g shipr-agent@latest"
25
26
  },
26
27
  "keywords": [
27
28
  "ai",