dirpush 0.1.2 → 0.1.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/package.json CHANGED
@@ -37,7 +37,7 @@
37
37
  "url": "https://github.com/baronunread/dirpush.git"
38
38
  },
39
39
  "homepage": "https://dirpush.com",
40
- "version": "0.1.2",
40
+ "version": "0.1.4",
41
41
  "keywords": [
42
42
  "directories",
43
43
  "startup",
package/src/checks.ts CHANGED
@@ -9,6 +9,7 @@ import { and, eq, gt, inArray } from "drizzle-orm";
9
9
  import { HOME, PKG, type Db } from "./db";
10
10
  import { jobs } from "./db/schema";
11
11
  import * as J from "./jobs";
12
+ import { loadCapabilities } from "./capabilities";
12
13
  import { loadDirectory } from "./registry";
13
14
 
14
15
  const color = process.stdout.isTTY && !process.env.NO_COLOR;
@@ -56,12 +57,12 @@ export async function checks(db: Db, opts: { product?: string; types: string[] }
56
57
  const handedBack: string[] = [], byYou: string[] = [], skipped: string[] = [];
57
58
  let signedIn = false;
58
59
 
59
- const summary = () => {
60
+ const summary = async () => {
60
61
  console.log(`\n${bold("Done for now.")}`);
61
- if (handedBack.length) console.log(`${green("✓")} Back to the agents: ${handedBack.join(", ")}`);
62
+ if (handedBack.length) console.log(`${green("✓")} Ready for the agents to finish: ${handedBack.join(", ")}`);
62
63
  if (byYou.length) console.log(`${green("✓")} Submitted by you: ${byYou.join(", ")}`);
63
64
  if (skipped.length) console.log(`${dim("·")} Skipped, still waiting: ${skipped.join(", ")}`);
64
- if (handedBack.length) console.log(`\nNext: ask your agent to ${bold("“continue my launch”")}.`);
65
+ if (handedBack.length) await startAgent(handedBack.length);
65
66
  };
66
67
 
67
68
  const googleSignIn = async () => {
@@ -81,13 +82,13 @@ export async function checks(db: Db, opts: { product?: string; types: string[] }
81
82
 
82
83
  console.log(`${bold("dirpush checks")} ${dim(`${google.length} need Google sign-in · ${queue.length} CAPTCHAs or bot checks`)}`);
83
84
  if (!google.length && !queue.length) return console.log("Nothing is waiting on you.");
84
- console.log(dim("Agents never solve these; you do, and each job goes back to them."));
85
+ console.log(dim("Agents never solve these. You clear each one, then an agent finishes the submission (at the end I can start yours)."));
85
86
 
86
87
  if (google.length) {
87
88
  console.log(`\n${bold("1. Sign into Google")} ${dim(google.map((j) => j.subject).join(", "))}`);
88
89
  console.log("These directories use “Sign in with Google”. Sign in once and the agents can use it from then on.");
89
90
  const k = await key(`${cyan("[enter]")} sign in now ${cyan("[n]")} later ${cyan("[q]")} quit `, "\rnq");
90
- if (k === "q") return summary();
91
+ if (k === "q") return await summary();
91
92
  if (k === "\r" && (await googleSignIn())) {
92
93
  for (const j of google) { J.unblock(db, j.id, "human signed into Google in the agents' Chrome profile"); handedBack.push(j.subject); }
93
94
  } else skipped.push(...google.map((j) => j.subject));
@@ -109,10 +110,10 @@ export async function checks(db: Db, opts: { product?: string; types: string[] }
109
110
  console.log(dim("Check never passes there? Submit it from your own browser instead, then press s."));
110
111
  open();
111
112
  for (;;) {
112
- const k = await key(`${cyan("enter")} done ${cyan("s")} I submitted it ${cyan("o")} reopen ${cyan("g")} Google sign-in ${cyan("n")} skip ${cyan("q")} quit › `, "\rsognq");
113
+ const k = await key(`${cyan("enter")} cleared, agent finishes it ${cyan("s")} I submitted it ${cyan("o")} reopen ${cyan("g")} Google sign-in ${cyan("n")} skip ${cyan("q")} quit › `, "\rsognq");
113
114
  if (k === "o") { open(); continue; }
114
115
  if (k === "g") { if (!signedIn) await googleSignIn(); open(); continue; }
115
- if (k === "q") return summary();
116
+ if (k === "q") return await summary();
116
117
  if (k === "n") skipped.push(job.subject);
117
118
  else if (k === "\r") {
118
119
  J.unblock(db, job.id, "human cleared the check in the agents' Chrome; continue in the same browser session");
@@ -125,6 +126,28 @@ export async function checks(db: Db, opts: { product?: string; types: string[] }
125
126
  break;
126
127
  }
127
128
  }
128
- summary();
129
+ await summary();
129
130
  }
130
131
 
132
+
133
+ /** The agent CLIs we can open on "continue my launch", most likely first: the one that recorded capabilities, then any on PATH. */
134
+ async function startAgent(n: number) {
135
+ const caps = await loadCapabilities().catch(() => null);
136
+ const known = [
137
+ { id: "claude-code", name: "Claude Code", bin: "claude" },
138
+ { id: "codex", name: "Codex", bin: "codex" },
139
+ ].filter((a) => Bun.which(a.bin));
140
+ const agent = known.find((a) => caps?.checked_by?.startsWith(a.id)) ?? known[0];
141
+ const prompt = "continue my launch";
142
+ if (!agent || process.platform !== "darwin") {
143
+ console.log(`\nNext: open your agent and say ${bold(`“${prompt}”`)}; it finishes the ${n} job(s) above.`);
144
+ return;
145
+ }
146
+ console.log(`\nThe ${n} job(s) above are waiting for an agent to finish them.`);
147
+ const k = await key(`${cyan("[enter]")} open ${agent.name} on “${prompt}” now ${cyan("[n]")} I'll do it later `, "\rn");
148
+ if (k === "n") return console.log(dim(`Whenever you're ready: open your agent and say “${prompt}”.`));
149
+ // A new Terminal window, so the human watches it work and approves what it asks for. Nothing runs unattended.
150
+ const cmd = `cd ${JSON.stringify(process.cwd())} && ${agent.bin} ${JSON.stringify(prompt)}`;
151
+ const ok = Bun.spawnSync(["osascript", "-e", `tell application "Terminal" to do script ${JSON.stringify(cmd)}`, "-e", `tell application "Terminal" to activate`], { stdin: "ignore", stdout: "ignore", stderr: "ignore" }).exitCode === 0;
152
+ console.log(ok ? `${green("✓")} Opened ${agent.name} in a new Terminal window.` : `Couldn't open Terminal. Run this yourself: ${cmd}`);
153
+ }
package/src/cli.ts CHANGED
@@ -36,6 +36,17 @@ if (first === "--version" || first === "-v") {
36
36
  process.exit(0);
37
37
  }
38
38
  if (first === "--help" || first === "-h") process.argv.splice(2, 1, "help");
39
+ // Names people type for the human-facing commands.
40
+ const ALIASES: Record<string, string> = { check: "checks", captchas: "checks", needs: "actions", todos: "actions" };
41
+ if (first && ALIASES[first]) process.argv.splice(2, 1, ALIASES[first]);
42
+ /** Agents read JSON errors; a person at a terminal gets a plain sentence. */
43
+ function fail(error: string, extra: { didYouMean?: string[] } = {}): never {
44
+ if (process.stderr.isTTY) {
45
+ const alts = extra.didYouMean?.map((c) => `dirpush ${c}`).join(", ");
46
+ console.error(`dirpush: ${error}${alts ? `\nDid you mean: ${alts}?` : ""}\nRun 'dirpush help' for every command.`);
47
+ } else console.error(JSON.stringify({ error, ...extra, help: "dirpush help" }));
48
+ process.exit(1);
49
+ }
39
50
 
40
51
  const { values: flags, positionals } = parseArgs({
41
52
  allowPositionals: true,
@@ -733,14 +744,22 @@ if (!cmd || cmd === "help") {
733
744
  const run = commands[`${cmd} ${sub}`] ?? commands[cmd ?? ""];
734
745
  if (!run) {
735
746
  const tried = sub ? `${cmd} ${sub}` : cmd;
736
- const close = Object.keys(commands).filter((c) => c.startsWith(cmd) || c.includes(tried) || (sub && c.endsWith(sub)));
737
- console.error(JSON.stringify({ error: `unknown command: ${tried}`, ...(close.length && { didYouMean: close.slice(0, 5) }), help: "dirpush help" }));
738
- process.exit(1);
747
+ // Typos too: "chek" → checks (edit distance on the first word).
748
+ const dist = (x: string, y: string) => {
749
+ let prev = Array.from({ length: y.length + 1 }, (_, j) => j);
750
+ for (let i = 1; i <= x.length; i++) {
751
+ const row = [i];
752
+ for (let j = 1; j <= y.length; j++) row[j] = Math.min(prev[j]! + 1, row[j - 1]! + 1, prev[j - 1]! + (x[i - 1] === y[j - 1] ? 0 : 1));
753
+ prev = row;
754
+ }
755
+ return prev[y.length]!;
756
+ };
757
+ const close = Object.keys(commands).filter((c) => c.startsWith(cmd) || c.includes(tried) || (sub && c.endsWith(sub)) || dist(c.split(" ")[0] ?? c, cmd ?? "") <= 2);
758
+ fail(`unknown command: ${tried}`, close.length ? { didYouMean: close.slice(0, 5) } : {});
739
759
  }
740
760
  try {
741
761
  const result = await run();
742
762
  if (result !== undefined) out(result);
743
763
  } catch (e) {
744
- console.error(JSON.stringify({ error: e instanceof z.ZodError ? z.prettifyError(e) : (e as Error).message }));
745
- process.exit(1);
764
+ fail(e instanceof z.ZodError ? z.prettifyError(e) : (e as Error).message);
746
765
  }