@dugleelabs/copair 1.4.2 → 1.4.4

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/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { join as join15 } from "path";
5
5
  import { existsSync as existsSync18, readFileSync as readFileSync10 } from "fs";
6
6
  import { createRequire as createRequire3 } from "module";
7
- import { resolve as resolve10, dirname as dirname7 } from "path";
7
+ import { resolve as resolve9, dirname as dirname7 } from "path";
8
8
  import { fileURLToPath as fileURLToPath3 } from "url";
9
9
 
10
10
  // src/cli/args.ts
@@ -2573,7 +2573,7 @@ var bashTool = {
2573
2573
  encoding: "utf-8",
2574
2574
  maxBuffer: 5 * 1024 * 1024,
2575
2575
  timeout,
2576
- shell: "/bin/bash"
2576
+ shell: process.platform === "win32" ? "cmd.exe" : "/bin/bash"
2577
2577
  });
2578
2578
  return { content: result };
2579
2579
  } catch (err) {
@@ -3103,7 +3103,8 @@ var commandsCommand = {
3103
3103
  // src/core/session.ts
3104
3104
  import { writeFile, rename, appendFile, readFile, readdir, rm, mkdir, stat } from "fs/promises";
3105
3105
  import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
3106
- import { join, resolve as resolve4 } from "path";
3106
+ import { join } from "path";
3107
+ import { homedir as homedir2 } from "os";
3107
3108
  import { execSync as execSync5 } from "child_process";
3108
3109
  import { randomUUID } from "crypto";
3109
3110
  import { createInterface } from "readline";
@@ -3134,8 +3135,7 @@ function resolveSessionsDir(cwd) {
3134
3135
  mkdirSync2(dir2, { recursive: true });
3135
3136
  return dir2;
3136
3137
  }
3137
- const home = process.env["HOME"] ?? "~";
3138
- const dir = join(resolve4(home), ".copair", "sessions");
3138
+ const dir = join(homedir2(), ".copair", "sessions");
3139
3139
  mkdirSync2(dir, { recursive: true });
3140
3140
  return dir;
3141
3141
  }
@@ -3191,18 +3191,18 @@ async function presentSessionPicker(sessions) {
3191
3191
  console.log(` ${sessions.length + 1}. Start fresh`);
3192
3192
  process.stdout.write(`
3193
3193
  Select [1-${sessions.length + 1}]: `);
3194
- return new Promise((resolve11) => {
3194
+ return new Promise((resolve10) => {
3195
3195
  const rl = createInterface({ input: process.stdin, terminal: false });
3196
3196
  rl.once("line", (line) => {
3197
3197
  rl.close();
3198
3198
  const choice = parseInt(line.trim(), 10);
3199
3199
  if (choice >= 1 && choice <= sessions.length) {
3200
- resolve11(sessions[choice - 1].id);
3200
+ resolve10(sessions[choice - 1].id);
3201
3201
  } else {
3202
- resolve11(null);
3202
+ resolve10(null);
3203
3203
  }
3204
3204
  });
3205
- rl.once("close", () => resolve11(null));
3205
+ rl.once("close", () => resolve10(null));
3206
3206
  });
3207
3207
  }
3208
3208
  var SessionManager = class _SessionManager {
@@ -3374,8 +3374,7 @@ var SessionManager = class _SessionManager {
3374
3374
  }
3375
3375
  // -- Migration ------------------------------------------------------------
3376
3376
  static async migrateGlobalRecovery(sessionsDir, projectRoot) {
3377
- const home = process.env["HOME"] ?? "~";
3378
- const recoveryFile = join(resolve4(home), ".copair", "sessions", "recovery.json");
3377
+ const recoveryFile = join(homedir2(), ".copair", "sessions", "recovery.json");
3379
3378
  if (!existsSync5(recoveryFile)) return null;
3380
3379
  try {
3381
3380
  const raw = await readFile(recoveryFile, "utf8");
@@ -3559,13 +3558,13 @@ Session: ${meta.identifier}`);
3559
3558
 
3560
3559
  // src/commands/loader.ts
3561
3560
  import { readdir as readdir2, readFile as readFile2, stat as stat2 } from "fs/promises";
3562
- import { join as join2, resolve as resolve5, relative } from "path";
3561
+ import { join as join2, resolve as resolve4, relative } from "path";
3563
3562
  import { existsSync as existsSync6 } from "fs";
3564
3563
 
3565
3564
  // src/commands/interpolate.ts
3566
3565
  import { execSync as execSync6 } from "child_process";
3567
3566
  async function interpolate(template, args, context) {
3568
- const resolve11 = (key) => {
3567
+ const resolve10 = (key) => {
3569
3568
  if (key.startsWith("env.")) {
3570
3569
  return process.env[key.slice(4)] ?? "";
3571
3570
  }
@@ -3576,10 +3575,10 @@ async function interpolate(template, args, context) {
3576
3575
  return null;
3577
3576
  };
3578
3577
  let result = template.replace(/\{\{([^}]+)\}\}/g, (_match, key) => {
3579
- return resolve11(key.trim()) ?? _match;
3578
+ return resolve10(key.trim()) ?? _match;
3580
3579
  });
3581
3580
  result = result.replace(/\$([A-Z][A-Z0-9_]*)/g, (_match, key) => {
3582
- return resolve11(key) ?? _match;
3581
+ return resolve10(key) ?? _match;
3583
3582
  });
3584
3583
  return result;
3585
3584
  }
@@ -3678,8 +3677,8 @@ async function loadCommandsFromDir(dir, source) {
3678
3677
  return commands;
3679
3678
  }
3680
3679
  async function loadCustomCommands() {
3681
- const globalDir = resolve5(process.env["HOME"] ?? "~", ".copair", "commands");
3682
- const projectDir = resolve5(process.cwd(), ".copair", "commands");
3680
+ const globalDir = resolve4(process.env["HOME"] ?? "~", ".copair", "commands");
3681
+ const projectDir = resolve4(process.cwd(), ".copair", "commands");
3683
3682
  const globalCommands = await loadCommandsFromDir(globalDir, "global");
3684
3683
  const projectCommands = await loadCommandsFromDir(projectDir, "project");
3685
3684
  return [...globalCommands, ...projectCommands];
@@ -3789,7 +3788,7 @@ var CommandRegistry = class {
3789
3788
 
3790
3789
  // src/workflows/loader.ts
3791
3790
  import { readdir as readdir3, readFile as readFile3 } from "fs/promises";
3792
- import { join as join3, resolve as resolve6 } from "path";
3791
+ import { join as join3, resolve as resolve5 } from "path";
3793
3792
  import { existsSync as existsSync7 } from "fs";
3794
3793
  import { parse as parseYaml2 } from "yaml";
3795
3794
  import { z as z11 } from "zod";
@@ -3845,8 +3844,8 @@ async function loadWorkflowsFromDir(dir) {
3845
3844
  return workflows;
3846
3845
  }
3847
3846
  async function loadWorkflows() {
3848
- const globalDir = resolve6(process.env["HOME"] ?? "~", ".copair", "workflows");
3849
- const projectDir = resolve6(process.cwd(), ".copair", "workflows");
3847
+ const globalDir = resolve5(process.env["HOME"] ?? "~", ".copair", "workflows");
3848
+ const projectDir = resolve5(process.cwd(), ".copair", "workflows");
3850
3849
  const globalWorkflows = await loadWorkflowsFromDir(globalDir);
3851
3850
  const projectWorkflows = await loadWorkflowsFromDir(projectDir);
3852
3851
  const map = /* @__PURE__ */ new Map();
@@ -4189,10 +4188,10 @@ function extractFileWords(messages) {
4189
4188
  for (const key of ["file_path", "path", "filePath"]) {
4190
4189
  const val = input[key];
4191
4190
  if (typeof val === "string") {
4192
- const basename2 = val.split("/").pop()?.replace(/\.[^.]+$/, "") ?? "";
4193
- if (basename2) {
4191
+ const basename3 = val.split("/").pop()?.replace(/\.[^.]+$/, "") ?? "";
4192
+ if (basename3) {
4194
4193
  words.push(
4195
- ...basename2.split(/[^a-z0-9]+/i).map((w) => w.toLowerCase()).filter((w) => w.length > 2 && !STOP_WORDS.has(w))
4194
+ ...basename3.split(/[^a-z0-9]+/i).map((w) => w.toLowerCase()).filter((w) => w.length > 2 && !STOP_WORDS.has(w))
4196
4195
  );
4197
4196
  }
4198
4197
  }
@@ -4363,8 +4362,8 @@ var SessionSummarizer = class {
4363
4362
  return text.trim();
4364
4363
  }
4365
4364
  timeout() {
4366
- return new Promise((resolve11) => {
4367
- setTimeout(() => resolve11(null), this.timeoutMs);
4365
+ return new Promise((resolve10) => {
4366
+ setTimeout(() => resolve10(null), this.timeoutMs);
4368
4367
  });
4369
4368
  }
4370
4369
  };
@@ -4397,7 +4396,7 @@ async function resolveSummarizationModel(configModel, activeModel) {
4397
4396
  // src/core/version-check.ts
4398
4397
  import { readFile as readFile5, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
4399
4398
  import { existsSync as existsSync9 } from "fs";
4400
- import { join as join5, resolve as resolve7, dirname as dirname3 } from "path";
4399
+ import { join as join5, resolve as resolve6, dirname as dirname3 } from "path";
4401
4400
  import { createRequire as createRequire2 } from "module";
4402
4401
  import { fileURLToPath as fileURLToPath2 } from "url";
4403
4402
  var _dir2 = dirname3(fileURLToPath2(import.meta.url));
@@ -4405,13 +4404,13 @@ var _require = createRequire2(import.meta.url);
4405
4404
  var pkg2 = (() => {
4406
4405
  for (const rel of ["../package.json", "../../package.json"]) {
4407
4406
  try {
4408
- return _require(resolve7(_dir2, rel));
4407
+ return _require(resolve6(_dir2, rel));
4409
4408
  } catch {
4410
4409
  }
4411
4410
  }
4412
4411
  return { name: "copair", version: process.env["COPAIR_VERSION"] ?? "0.0.0-dev" };
4413
4412
  })();
4414
- var CACHE_DIR = resolve7(process.env["HOME"] ?? "~", ".copair");
4413
+ var CACHE_DIR = resolve6(process.env["HOME"] ?? "~", ".copair");
4415
4414
  var CACHE_FILE = join5(CACHE_DIR, "version-check.json");
4416
4415
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
4417
4416
  async function fetchLatestVersion() {
@@ -4480,7 +4479,7 @@ Update available: ${pkg2.version} \u2192 ${latest} (npm i -g ${pkg2.name})
4480
4479
  }
4481
4480
 
4482
4481
  // src/core/approval-gate.ts
4483
- import { resolve as resolvePath } from "path";
4482
+ import { resolve as resolvePath, sep } from "path";
4484
4483
  import chalk5 from "chalk";
4485
4484
 
4486
4485
  // src/cli/tty-prompt.ts
@@ -4588,8 +4587,8 @@ var ApprovalGate = class {
4588
4587
  if (typeof filePath !== "string") return false;
4589
4588
  const abs = resolvePath(filePath);
4590
4589
  for (const trusted of this.trustedPaths) {
4591
- if (abs === trusted || abs.startsWith(trusted + "/")) {
4592
- if (PERMISSION_SENSITIVE_FILES.some((name) => abs.endsWith("/" + name))) {
4590
+ if (abs === trusted || abs.startsWith(trusted + sep)) {
4591
+ if (PERMISSION_SENSITIVE_FILES.some((name) => abs.endsWith(sep + name))) {
4593
4592
  return false;
4594
4593
  }
4595
4594
  return true;
@@ -4641,7 +4640,7 @@ var ApprovalGate = class {
4641
4640
  }
4642
4641
  /** Bridge-based approval: emit event and await response from ink UI. */
4643
4642
  bridgePrompt(toolName, input, key) {
4644
- return new Promise((resolve11) => {
4643
+ return new Promise((resolve10) => {
4645
4644
  const summary = formatSummary(toolName, input);
4646
4645
  const warning = typeof input._sensitivePathWarning === "string" ? input._sensitivePathWarning : void 0;
4647
4646
  this.bridge.emit("approval-request", {
@@ -4655,29 +4654,29 @@ var ApprovalGate = class {
4655
4654
  switch (answer) {
4656
4655
  case "allow":
4657
4656
  void this.auditLog?.append({ event: "approval", tool: toolName, approved_by: "user", outcome: "allowed" });
4658
- resolve11(true);
4657
+ resolve10(true);
4659
4658
  break;
4660
4659
  case "always":
4661
4660
  this.alwaysAllow.add(key);
4662
4661
  void this.auditLog?.append({ event: "approval", tool: toolName, approved_by: "user", outcome: "allowed", detail: "always" });
4663
- resolve11(true);
4662
+ resolve10(true);
4664
4663
  break;
4665
4664
  case "all":
4666
4665
  this.bridge.approveAllForTurn = true;
4667
4666
  void this.auditLog?.append({ event: "approval", tool: toolName, approved_by: "user", outcome: "allowed", detail: "approve-all" });
4668
- resolve11(true);
4667
+ resolve10(true);
4669
4668
  break;
4670
4669
  case "similar": {
4671
4670
  const similarKey = similarSessionKey(toolName, input);
4672
4671
  this.alwaysAllow.add(similarKey);
4673
4672
  void this.auditLog?.append({ event: "approval", tool: toolName, approved_by: "user", outcome: "allowed", detail: "similar" });
4674
- resolve11(true);
4673
+ resolve10(true);
4675
4674
  break;
4676
4675
  }
4677
4676
  case "deny":
4678
4677
  default:
4679
4678
  void this.auditLog?.append({ event: "denial", tool: toolName, outcome: "denied", detail: "user denied" });
4680
- resolve11(false);
4679
+ resolve10(false);
4681
4680
  break;
4682
4681
  }
4683
4682
  });
@@ -4751,7 +4750,7 @@ function sessionKey(toolName, input) {
4751
4750
  function similarSessionKey(toolName, input) {
4752
4751
  const filePath = input.file_path ?? input.path;
4753
4752
  if (typeof filePath === "string") {
4754
- const dir = filePath.replace(/\/[^/]*$/, "/");
4753
+ const dir = filePath.replace(/[/\\][^/\\]*$/, sep);
4755
4754
  return `${toolName}:${dir}`;
4756
4755
  }
4757
4756
  return sessionKey(toolName, input);
@@ -6068,8 +6067,8 @@ function renderApp(bridge, model, options) {
6068
6067
 
6069
6068
  // src/core/path-guard.ts
6070
6069
  import { realpathSync, existsSync as existsSync10 } from "fs";
6071
- import { resolve as resolve8, dirname as dirname4 } from "path";
6072
- import { homedir as homedir2 } from "os";
6070
+ import { resolve as resolve7, dirname as dirname4, basename, sep as sep2 } from "path";
6071
+ import { homedir as homedir3 } from "os";
6073
6072
  import { execSync as execSync8 } from "child_process";
6074
6073
  import { minimatch } from "minimatch";
6075
6074
  var BUILTIN_DENY = [
@@ -6087,8 +6086,8 @@ var BUILTIN_DENY = [
6087
6086
  "**/.env.local"
6088
6087
  ];
6089
6088
  function expandHome(pattern) {
6090
- if (pattern === "~") return homedir2();
6091
- if (pattern.startsWith("~/")) return homedir2() + pattern.slice(1);
6089
+ if (pattern === "~") return homedir3();
6090
+ if (pattern.startsWith("~/")) return resolve7(homedir3(), pattern.slice(2));
6092
6091
  return pattern;
6093
6092
  }
6094
6093
  var PathGuard = class _PathGuard {
@@ -6118,15 +6117,14 @@ var PathGuard = class _PathGuard {
6118
6117
  }
6119
6118
  resolved = realpathSync(rawPath);
6120
6119
  } else {
6121
- const parentRaw = dirname4(resolve8(rawPath));
6120
+ const parentRaw = dirname4(resolve7(rawPath));
6122
6121
  if (!existsSync10(parentRaw)) {
6123
6122
  return { allowed: false, reason: "parent-missing" };
6124
6123
  }
6125
6124
  const resolvedParent = realpathSync(parentRaw);
6126
- const filename = rawPath.split("/").at(-1);
6127
- resolved = resolve8(resolvedParent, filename);
6125
+ resolved = resolve7(resolvedParent, basename(rawPath));
6128
6126
  }
6129
- const inside = resolved.startsWith(this.projectRoot + "/") || resolved === this.projectRoot;
6127
+ const inside = resolved.startsWith(this.projectRoot + sep2) || resolved === this.projectRoot;
6130
6128
  if (inside) {
6131
6129
  return { allowed: true, resolvedPath: resolved };
6132
6130
  }
@@ -6143,12 +6141,12 @@ var PathGuard = class _PathGuard {
6143
6141
  }
6144
6142
  isDenied(resolved) {
6145
6143
  return this.expandedDenyPatterns.some(
6146
- (pattern) => minimatch(resolved, pattern, { dot: true })
6144
+ (pattern) => minimatch(resolved, pattern, { dot: true, windowsPathsNoEscape: true })
6147
6145
  );
6148
6146
  }
6149
6147
  isAllowed(resolved) {
6150
6148
  return this.expandedAllowPatterns.some(
6151
- (pattern) => minimatch(resolved, pattern, { dot: true })
6149
+ (pattern) => minimatch(resolved, pattern, { dot: true, windowsPathsNoEscape: true })
6152
6150
  );
6153
6151
  }
6154
6152
  /**
@@ -6159,7 +6157,7 @@ var PathGuard = class _PathGuard {
6159
6157
  */
6160
6158
  static findProjectRoot(cwd) {
6161
6159
  try {
6162
- return execSync8("git rev-parse --show-toplevel", { cwd, encoding: "utf8" }).trim();
6160
+ return resolve7(execSync8("git rev-parse --show-toplevel", { cwd, encoding: "utf8" }).trim());
6163
6161
  } catch {
6164
6162
  return cwd;
6165
6163
  }
@@ -6283,8 +6281,8 @@ var ToolExecutor = class {
6283
6281
 
6284
6282
  // src/core/allow-list.ts
6285
6283
  import { readFileSync as readFileSync5, existsSync as existsSync11 } from "fs";
6286
- import { resolve as resolve9 } from "path";
6287
- import { homedir as homedir3 } from "os";
6284
+ import { resolve as resolve8 } from "path";
6285
+ import { homedir as homedir4 } from "os";
6288
6286
  import { parse as parseYaml3 } from "yaml";
6289
6287
  var AllowList = class {
6290
6288
  rules;
@@ -6339,8 +6337,8 @@ var AllowList = class {
6339
6337
  };
6340
6338
  var ALLOW_FILE = "allow.yaml";
6341
6339
  function loadAllowList(projectDir) {
6342
- const globalPath = resolve9(homedir3(), ".copair", ALLOW_FILE);
6343
- const projectPath = resolve9(projectDir ?? process.cwd(), ".copair", ALLOW_FILE);
6340
+ const globalPath = resolve8(homedir4(), ".copair", ALLOW_FILE);
6341
+ const projectPath = resolve8(projectDir ?? process.cwd(), ".copair", ALLOW_FILE);
6344
6342
  const global = readAllowFile(globalPath);
6345
6343
  const project = readAllowFile(projectPath);
6346
6344
  return new AllowList({
@@ -6401,7 +6399,7 @@ import chalk6 from "chalk";
6401
6399
  // package.json
6402
6400
  var package_default = {
6403
6401
  name: "@dugleelabs/copair",
6404
- version: "1.4.2",
6402
+ version: "1.4.4",
6405
6403
  description: "Model-agnostic AI coding agent for the terminal",
6406
6404
  type: "module",
6407
6405
  main: "dist/api.js",
@@ -6574,14 +6572,14 @@ var DEFAULT_PRICING = /* @__PURE__ */ new Map([
6574
6572
  // src/cli/ui/input-history.ts
6575
6573
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync12 } from "fs";
6576
6574
  import { join as join6, dirname as dirname5 } from "path";
6577
- import { homedir as homedir4 } from "os";
6575
+ import { homedir as homedir5 } from "os";
6578
6576
  var MAX_HISTORY = 500;
6579
6577
  function resolveHistoryPath(cwd) {
6580
6578
  const projectPath = join6(cwd, ".copair", "history");
6581
6579
  if (existsSync12(join6(cwd, ".copair"))) {
6582
6580
  return projectPath;
6583
6581
  }
6584
- return join6(homedir4(), ".copair", "history");
6582
+ return join6(homedir5(), ".copair", "history");
6585
6583
  }
6586
6584
  function loadHistory(historyPath) {
6587
6585
  try {
@@ -6609,7 +6607,7 @@ function appendHistory(historyPath, entry) {
6609
6607
 
6610
6608
  // src/cli/ui/completion-providers.ts
6611
6609
  import { readdirSync } from "fs";
6612
- import { join as join7, dirname as dirname6, basename } from "path";
6610
+ import { join as join7, dirname as dirname6, basename as basename2 } from "path";
6613
6611
  var SlashCommandProvider = class {
6614
6612
  id = "slash-commands";
6615
6613
  commands;
@@ -6648,7 +6646,7 @@ var FilePathProvider = class {
6648
6646
  const lastToken = input.split(/\s+/).pop() ?? "";
6649
6647
  try {
6650
6648
  const dir = lastToken.endsWith("/") ? join7(this.cwd, lastToken) : join7(this.cwd, dirname6(lastToken));
6651
- const prefix = lastToken.endsWith("/") ? "" : basename(lastToken);
6649
+ const prefix = lastToken.endsWith("/") ? "" : basename2(lastToken);
6652
6650
  const beforeToken = input.slice(0, input.length - lastToken.length);
6653
6651
  const entries = readdirSync(dir, { withFileTypes: true });
6654
6652
  const items = [];
@@ -6702,7 +6700,7 @@ var CompletionEngine = class {
6702
6700
  // src/init/GlobalInitManager.ts
6703
6701
  import { existsSync as existsSync13, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
6704
6702
  import { join as join8 } from "path";
6705
- import { homedir as homedir5 } from "os";
6703
+ import { homedir as homedir6 } from "os";
6706
6704
  var GLOBAL_CONFIG_TEMPLATE = `# Copair global configuration
6707
6705
  # Generated by Copair on first run \u2014 edit as needed
6708
6706
 
@@ -6732,7 +6730,7 @@ var GLOBAL_CONFIG_TEMPLATE = `# Copair global configuration
6732
6730
  var GlobalInitManager = class {
6733
6731
  globalDir;
6734
6732
  constructor(homeDir) {
6735
- this.globalDir = join8(homeDir ?? homedir5(), ".copair");
6733
+ this.globalDir = join8(homeDir ?? homedir6(), ".copair");
6736
6734
  }
6737
6735
  async check(options = { ci: false }) {
6738
6736
  if (existsSync13(this.globalDir)) {
@@ -7401,7 +7399,7 @@ var _require2 = createRequire3(import.meta.url);
7401
7399
  var _pkg = (() => {
7402
7400
  for (const rel of ["../package.json", "../../package.json"]) {
7403
7401
  try {
7404
- return _require2(resolve10(_dir3, rel));
7402
+ return _require2(resolve9(_dir3, rel));
7405
7403
  } catch {
7406
7404
  }
7407
7405
  }