@counterfact-labs/cli 0.1.1 → 0.1.2

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.
@@ -35,9 +35,11 @@ export async function prepareCheck(path, ctx) {
35
35
  throw new Error("No agent files were found in this folder.");
36
36
  ctx.write(fileText(path, wanted));
37
37
  const upload = await readUpload(root, files, plan.wanted);
38
- const response = await awaitQuote(await ctx.api.request("checks", { ...facts, files: upload }), ctx);
38
+ const created = await ctx.api.request("checks", { ...facts, files: upload });
39
+ ctx.write(`Follow it in the app: ${created.url}\n`);
40
+ const response = await awaitQuote(created, ctx);
39
41
  ctx.write(quoteText(response.quote));
40
- return { root, response, files: upload };
42
+ return { root, response, files: upload, url: created.url };
41
43
  }
42
44
  /** A confirmed check can keep running and charge while the connection is down. */
43
45
  async function pollConfirmed(id, ctx) {
@@ -1,7 +1,8 @@
1
1
  import { saveCredentials } from "../credentials.js";
2
2
  export async function login(ctx) {
3
3
  const device = await ctx.api.request("device", {}, false);
4
- ctx.write(`To sign in, open ${device.verificationUri} and enter ${device.userCode}\n`);
4
+ ctx.write(`1. Open ${device.verificationUri}. If asked, sign in with the code we email you.\n`
5
+ + `2. Then enter this code on that page: ${device.userCode}\n`);
5
6
  const until = ctx.now() + device.expiresIn * 1000;
6
7
  while (ctx.now() < until) {
7
8
  await ctx.sleep(device.interval * 1000);
package/dist/files.js CHANGED
@@ -2,10 +2,10 @@ import { constants } from "node:fs";
2
2
  import { lstat, open, readdir, realpath } from "node:fs/promises";
3
3
  import { dirname, join, relative, resolve, sep } from "node:path";
4
4
  import { ignored, parseIgnore } from "./gitignore.js";
5
- import { SECRET_FILE_RE } from "./secret-files.js";
5
+ import { isReadableRepoPath } from "./secret-files.js";
6
6
  const slash = (path) => path.split(sep).join("/");
7
7
  function safe(path) {
8
- return !!path && !path.includes("\\") && !path.includes("\0") && !path.startsWith("/") && !SECRET_FILE_RE.test(path)
8
+ return !!path && !path.includes("\\") && !path.includes("\0") && !path.startsWith("/") && isReadableRepoPath(path)
9
9
  && path.split("/").every(part => !["", ".", "..", ".git", "node_modules"].includes(part));
10
10
  }
11
11
  async function ignoreFile(directory, base) {
package/dist/mcp.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createInterface } from "node:readline";
2
2
  import { once } from "node:events";
3
+ import { readFile } from "node:fs/promises";
3
4
  import { resolve } from "node:path";
4
5
  import { errorCode, errorText } from "./api.js";
5
6
  import { commandContext } from "./context.js";
@@ -27,6 +28,7 @@ class ProtocolError extends Error {
27
28
  }
28
29
  function object(value) { return value !== null && typeof value === "object" && !Array.isArray(value); }
29
30
  export async function serveMcp(input, output, options = {}) {
31
+ const { version } = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
30
32
  const previews = new Map();
31
33
  const requests = new Map();
32
34
  let queue = Promise.resolve();
@@ -47,6 +49,7 @@ export async function serveMcp(input, output, options = {}) {
47
49
  throw new ProtocolError(-32602, "Provide a finding id.");
48
50
  let text = "", exitCode = 0;
49
51
  let findingIds;
52
+ let url;
50
53
  try {
51
54
  const ctx = commandContext({ ...options, signal, write: chunk => {
52
55
  // Progress is transient terminal text; only the final lines belong in a tool response.
@@ -64,11 +67,16 @@ export async function serveMcp(input, output, options = {}) {
64
67
  previews.delete(path);
65
68
  if (!prepared)
66
69
  throw new Error("Get a preview first. Show the user the price and get their agreement before confirming.");
67
- findingIds = (await confirmPrepared(prepared, ctx)).findingIds;
70
+ url = prepared.url;
71
+ const result = await confirmPrepared(prepared, ctx);
72
+ findingIds = result.findingIds;
73
+ url = result.taste.url;
68
74
  }
69
75
  else {
70
76
  previews.delete(path);
71
- previews.set(path, await prepareCheck(args.path, ctx));
77
+ const prepared = await prepareCheck(args.path, ctx);
78
+ previews.set(path, prepared);
79
+ url = prepared.url;
72
80
  }
73
81
  }
74
82
  }
@@ -79,7 +87,7 @@ export async function serveMcp(input, output, options = {}) {
79
87
  // Some clients (Claude Code among them) give the model only the structured result when one is present, so it
80
88
  // repeats the text: without it the model sees no files, price or results.
81
89
  return { content: [{ type: "text", text }], isError: exitCode !== 0,
82
- structuredContent: { text, exitCode, ...(findingIds ? { findingIds } : {}) } };
90
+ structuredContent: { text, exitCode, ...(findingIds ? { findingIds } : {}), ...(url ? { url } : {}) } };
83
91
  }
84
92
  async function respond(message, id, signal) {
85
93
  if (signal.aborted)
@@ -90,7 +98,7 @@ export async function serveMcp(input, output, options = {}) {
90
98
  if (message.method === "initialize") {
91
99
  const requested = object(message.params) ? message.params.protocolVersion : undefined;
92
100
  result = { protocolVersion: ["2024-11-05", "2025-03-26", "2025-06-18"].includes(requested) ? requested : "2025-06-18",
93
- capabilities: { tools: {} }, serverInfo: { name: "counterfact-labs", version: "0.1.1" } };
101
+ capabilities: { tools: {} }, serverInfo: { name: "counterfact-labs", version } };
94
102
  }
95
103
  else if (message.method === "ping")
96
104
  result = {};
@@ -1,2 +1,5 @@
1
- // Copied from src/lib/conventions/file-patterns.ts by the build.
1
+ // Copied from src/lib/conventions/file-admission.ts by the build.
2
2
  export const SECRET_FILE_RE = /(^|\/)(\.env(?:\.[^/]*)?|\.(?:npmrc|pypirc|netrc)|(?:credentials|secrets)\.(?:json|ya?ml)|secrets\.toml|[^/]+-credentials\.json|service-account[^/]*\.json|id_(?:rsa|dsa|ecdsa|ed25519)|[^/]+\.(?:pem|key|p12|pfx))$/i;
3
+ export function isReadableRepoPath(path) {
4
+ return !SECRET_FILE_RE.test(path.replace(/\\/g, "/"));
5
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@counterfact-labs/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "license": "UNLICENSED",
5
5
  "description": "Check an agent from your terminal or coding agent",
6
6
  "type": "module",