@amdahl/cli 0.3.5 → 0.3.6
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/README.md +7 -4
- package/dist/amdahl.js +368 -362
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -140,8 +140,9 @@ Run codex, paste an email and say: "Use Amdahl to make this sound less like AI"
|
|
|
140
140
|
Enter runs `codex mcp login amdahl` on your terminal (it prints the link and
|
|
141
141
|
opens the browser). Ctrl+C there skips it and names the command to run
|
|
142
142
|
later; if the sign-in does not finish, it says so and names the same
|
|
143
|
-
command. Without `codex` on your PATH,
|
|
144
|
-
|
|
143
|
+
command. Without `codex` on your PATH, and always on Windows (where `codex`
|
|
144
|
+
is a `.cmd` file the CLI cannot start without a shell), the screen lists
|
|
145
|
+
`codex mcp login amdahl` as step 1. With more than one tool, the screens follow one another
|
|
145
146
|
and the "New to Amdahl?" line is printed once. A tool that fails (a broken
|
|
146
147
|
`~/.cursor/mcp.json` or `~/.claude.json`, say) prints its error, the rest
|
|
147
148
|
still run, and the exit code is the first failure's. In a terminal narrower
|
|
@@ -320,8 +321,10 @@ message, or bare `amdahl` to add Amdahl to Claude Code, Cursor or Codex.
|
|
|
320
321
|
`amdahl optimize` with no file, at a terminal, asks you to paste the message
|
|
321
322
|
(as the setup's "This terminal" does) and signs you in through the browser
|
|
322
323
|
first if you are not signed in ("Sign in first, then paste your message."). With stdin piped, or `-`, it reads stdin
|
|
323
|
-
whole as before.
|
|
324
|
-
|
|
324
|
+
whole as before. At a terminal it still says to give a file (exit 2) when
|
|
325
|
+
an agent runs it (`CLAUDECODE` or `CURSOR_AGENT` set), in CI (the same
|
|
326
|
+
variables bare `amdahl` checks) or with `AMDAHL_NO_PROMPT=1`: none of them
|
|
327
|
+
can paste.
|
|
325
328
|
|
|
326
329
|
On a server or in CI, skip `login` and set an API key in `AMDAHL_KEY`.
|
|
327
330
|
|
package/dist/amdahl.js
CHANGED
|
@@ -9527,276 +9527,14 @@ async function keysRevoke(ctx, args) {
|
|
|
9527
9527
|
}
|
|
9528
9528
|
|
|
9529
9529
|
// src/commands/optimize.ts
|
|
9530
|
-
import { readFileSync as
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
var
|
|
9534
|
-
|
|
9535
|
-
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
if (!allowed.includes(value)) throw usageError(`--${flag} must be one of: ${allowed.join(", ")}.`);
|
|
9539
|
-
return value;
|
|
9540
|
-
}
|
|
9541
|
-
function readContext(path) {
|
|
9542
|
-
let parsed;
|
|
9543
|
-
try {
|
|
9544
|
-
parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
9545
|
-
} catch (err) {
|
|
9546
|
-
throw usageError(`Could not read --context ${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
9547
|
-
}
|
|
9548
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9549
|
-
throw usageError("--context must be a JSON object with rules, voice_examples and facts.");
|
|
9550
|
-
}
|
|
9551
|
-
return parsed;
|
|
9552
|
-
}
|
|
9553
|
-
function withTimeout(promise, ms, file) {
|
|
9554
|
-
let timer;
|
|
9555
|
-
const timeout = new Promise((_, reject) => {
|
|
9556
|
-
timer = setTimeout(
|
|
9557
|
-
() => reject(new CliError("timeout", `Optimizing ${file} timed out after ${ms / 1e3} s.`, EXIT.network)),
|
|
9558
|
-
ms
|
|
9559
|
-
);
|
|
9560
|
-
});
|
|
9561
|
-
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
9562
|
-
}
|
|
9563
|
-
var WEAKEST = {
|
|
9564
|
-
rules: "how it follows your rules",
|
|
9565
|
-
ask: "the ask",
|
|
9566
|
-
opener: "the opener",
|
|
9567
|
-
fit: "how it fits the recipient",
|
|
9568
|
-
proof: "its proof points",
|
|
9569
|
-
length: "the length",
|
|
9570
|
-
style: "the tone",
|
|
9571
|
-
preference: "how it matches your preferences",
|
|
9572
|
-
voice: "how much it sounds like you"
|
|
9573
|
-
};
|
|
9574
|
-
var CONCERNS = {
|
|
9575
|
-
conflicts_with_callers_ask: "it asks for a different length or time than your ask",
|
|
9576
|
-
drops_ask: "it leaves out your ask",
|
|
9577
|
-
adds_claim: "it states something neither your draft nor your facts support",
|
|
9578
|
-
drops_entity: "it drops a name, link or placeholder you had",
|
|
9579
|
-
inflates: "it is longer than you asked for",
|
|
9580
|
-
no_gain: "it changed little beyond the wording",
|
|
9581
|
-
breaks_rules: "it breaks one of your rules"
|
|
9582
|
-
};
|
|
9583
|
-
var EVIDENCE_MISSED = {
|
|
9584
|
-
corpus_empty: "the workspace has none yet",
|
|
9585
|
-
evidence_unavailable: "this environment does not read them",
|
|
9586
|
-
evidence_read_failed: "the read failed, so a retry may work"
|
|
9587
|
-
};
|
|
9588
|
-
var MAX_LINES = 3;
|
|
9589
|
-
function list(value) {
|
|
9590
|
-
return Array.isArray(value) ? value.filter((v) => !!v && typeof v === "object") : null;
|
|
9591
|
-
}
|
|
9592
|
-
function record(value) {
|
|
9593
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
9594
|
-
}
|
|
9595
|
-
function text(value) {
|
|
9596
|
-
return typeof value === "string" ? printable(value).trim() : "";
|
|
9597
|
-
}
|
|
9598
|
-
function sentence(value) {
|
|
9599
|
-
return /[.!?]$/.test(value) ? value : `${value}.`;
|
|
9600
|
-
}
|
|
9601
|
-
function plural(n, one, many = `${one}s`) {
|
|
9602
|
-
return `${n} ${n === 1 ? one : many}`;
|
|
9603
|
-
}
|
|
9604
|
-
function readSteps(steps) {
|
|
9605
|
-
const reasonOf = (node) => text(steps.find((s) => s.node === node)?.reason);
|
|
9606
|
-
const changed = (node) => steps.some((s) => s.node === node && s.changed === true);
|
|
9607
|
-
const counts = new Map(
|
|
9608
|
-
reasonOf("w3").split(",").map((part) => part.split(":")).filter(([, n]) => /^\d+$/.test(n ?? "")).map(([code, n]) => [code, Number(n)])
|
|
9609
|
-
);
|
|
9610
|
-
const did = [];
|
|
9611
|
-
const repaired = counts.get("repaired") ?? 0;
|
|
9612
|
-
const deleted = counts.get("deleted") ?? 0;
|
|
9613
|
-
if (repaired && deleted) did.push(`Rewrote ${plural(repaired, "sentence")} and cut ${deleted} that nothing backed.`);
|
|
9614
|
-
else if (repaired) did.push(`Rewrote ${plural(repaired, "sentence")} that did not hold up.`);
|
|
9615
|
-
else if (deleted) did.push(`Cut ${plural(deleted, "sentence")} that nothing backed.`);
|
|
9616
|
-
const repair = reasonOf("w5");
|
|
9617
|
-
if (changed("w5") && repair.startsWith("repaired:")) {
|
|
9618
|
-
const weakest = WEAKEST[repair.slice("repaired:".length)];
|
|
9619
|
-
did.push(weakest ? `Reworked ${weakest}.` : "Reworked its weakest part.");
|
|
9620
|
-
}
|
|
9621
|
-
const flagged = [];
|
|
9622
|
-
const open = counts.get("open");
|
|
9623
|
-
if (open) flagged.push(`${plural(open, "sentence problem")} it could not fix`);
|
|
9624
|
-
const second = reasonOf("w6");
|
|
9625
|
-
if (changed("w6") && !second.startsWith("kept_before_repair")) {
|
|
9626
|
-
const concern = CONCERNS[second];
|
|
9627
|
-
flagged.push(concern ? `a second-check concern (${concern})` : "a second-check concern");
|
|
9628
|
-
}
|
|
9629
|
-
return { did: did.join(" "), flagged };
|
|
9630
|
-
}
|
|
9631
|
-
function and(parts) {
|
|
9632
|
-
return parts.length < 2 ? parts[0] ?? "" : `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
|
|
9633
|
-
}
|
|
9634
|
-
function whatAmdahlDid(result) {
|
|
9635
|
-
const steps = result.unchanged === true ? { did: "", flagged: [] } : readSteps(list(result.steps) ?? []);
|
|
9636
|
-
const flagged = [...steps.flagged];
|
|
9637
|
-
let checked = "";
|
|
9638
|
-
let removed = "";
|
|
9639
|
-
const evidence = record(result.evidence);
|
|
9640
|
-
const claims = list(result.unsupported_claims);
|
|
9641
|
-
if (evidence?.used === true) {
|
|
9642
|
-
const quotes = record(result.lift)?.quotes;
|
|
9643
|
-
const read = typeof quotes === "number" && quotes > 0 ? ` (${plural(quotes, "quote")})` : "";
|
|
9644
|
-
if (claims) {
|
|
9645
|
-
const still = claims.filter((c) => c.status !== "removed").length;
|
|
9646
|
-
const cut = claims.length - still;
|
|
9647
|
-
if (cut) removed = `Removed ${plural(cut, "claim")} your calls don't back.`;
|
|
9648
|
-
if (still) flagged.unshift(`${plural(still, "claim")} your calls don't back`);
|
|
9649
|
-
checked = `Checked every claim against your calls${read}${claims.length ? "" : " and found none they don't back"}.`;
|
|
9650
|
-
} else checked = `Checked the message against your calls${read}.`;
|
|
9651
|
-
} else if (evidence?.requested === "workspace" && EVIDENCE_MISSED[text(evidence.reason)]) {
|
|
9652
|
-
checked = `Skipped checking claims against your calls: ${EVIDENCE_MISSED[text(evidence.reason)]}.`;
|
|
9653
|
-
}
|
|
9654
|
-
const used = (list(record(result.context_use)?.rows) ?? []).filter((r) => r.verdict === "pass");
|
|
9655
|
-
const sources = [
|
|
9656
|
-
used.some((r) => r.bucket === "internal") ? "Amdahl's own records" : "",
|
|
9657
|
-
used.some((r) => r.bucket === "external") ? "what their company or market published" : ""
|
|
9658
|
-
].filter(Boolean);
|
|
9659
|
-
const usedLine = sources.length ? `Used ${plural(sources.length, "source")}: ${and(sources)}.` : "";
|
|
9660
|
-
const omitted = list(result.omitted) ?? [];
|
|
9661
|
-
const whys = [...new Set(omitted.map((o) => text(o.why)).filter(Boolean))];
|
|
9662
|
-
const more = whys.length > 1 ? ` (${plural(whys.length - 1, "more reason")} in --json)` : "";
|
|
9663
|
-
const leftOut = omitted.length ? `Left out ${plural(omitted.length, "thing")}${whys[0] ? `: ${sentence(whys[0])}` : "."}${more}` : "";
|
|
9664
|
-
const lines = [
|
|
9665
|
-
{ rank: 2, line: sentence(text(result.what_changed)) },
|
|
9666
|
-
{ rank: 5, line: checked },
|
|
9667
|
-
{ rank: 3, line: removed },
|
|
9668
|
-
{ rank: 6, line: usedLine },
|
|
9669
|
-
{ rank: 7, line: steps.did },
|
|
9670
|
-
{ rank: 4, line: leftOut },
|
|
9671
|
-
{ rank: 1, line: flagged.length ? `Flagged ${and(flagged)}; check before sending.` : "" }
|
|
9672
|
-
].filter((l) => l.line && l.line !== ".");
|
|
9673
|
-
const kept = new Set([...lines].sort((a, b) => a.rank - b.rank).slice(0, MAX_LINES));
|
|
9674
|
-
return lines.filter((l) => kept.has(l)).map((l) => l.line);
|
|
9675
|
-
}
|
|
9676
|
-
var PASTE_PROMPT = "Paste an email or LinkedIn message, then press Enter on an empty line:";
|
|
9677
|
-
var VERSION_RULE = "\u2500\u2500 Amdahl's version \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500";
|
|
9678
|
-
function nextTimeLine(ctx) {
|
|
9679
|
-
const file = amdahlCommand("optimize draft.txt");
|
|
9680
|
-
return ctx.io.platform === "darwin" ? `Next time: ${file},
|
|
9681
|
-
or pbpaste | ${amdahlCommand("optimize -")}` : `Next time: ${file}`;
|
|
9682
|
-
}
|
|
9683
|
-
function requestOptions(ctx) {
|
|
9684
|
-
const { flags } = ctx;
|
|
9685
|
-
const base = {};
|
|
9686
|
-
const channel = oneOf("channel", str(flags.channel), CHANNELS);
|
|
9687
|
-
const evidence = oneOf("evidence", str(flags.evidence), EVIDENCE);
|
|
9688
|
-
const onUnsupported = oneOf("on-unsupported", str(flags["on-unsupported"]), ON_UNSUPPORTED);
|
|
9689
|
-
if (channel) base.channel = channel;
|
|
9690
|
-
if (evidence) base.evidence = evidence;
|
|
9691
|
-
if (onUnsupported) base.on_unsupported = onUnsupported;
|
|
9692
|
-
if (flags.tries === true) base.include_tries = true;
|
|
9693
|
-
const contextPath = str(flags.context);
|
|
9694
|
-
if (contextPath) base.context = readContext(contextPath);
|
|
9695
|
-
return base;
|
|
9696
|
-
}
|
|
9697
|
-
async function optimizeOne(ctx, credential, message2, base, file) {
|
|
9698
|
-
try {
|
|
9699
|
-
return await withTimeout(
|
|
9700
|
-
apiClient(ctx, credential).request("/api/platform/v1/messages/optimize", {
|
|
9701
|
-
method: "POST",
|
|
9702
|
-
body: { message: message2, ...base }
|
|
9703
|
-
}),
|
|
9704
|
-
OPTIMIZE_TIMEOUT_MS,
|
|
9705
|
-
file
|
|
9706
|
-
);
|
|
9707
|
-
} catch (err) {
|
|
9708
|
-
throw toCliError(err);
|
|
9709
|
-
}
|
|
9710
|
-
}
|
|
9711
|
-
async function credentialOrSignIn(ctx) {
|
|
9712
|
-
try {
|
|
9713
|
-
return await resolveCredential(ctx);
|
|
9714
|
-
} catch (err) {
|
|
9715
|
-
if (!(err instanceof CliError && err.exit === EXIT.unauthenticated)) throw err;
|
|
9716
|
-
}
|
|
9717
|
-
ctx.out.info(`Sign in first, then paste your message.
|
|
9718
|
-
${NEW_USER}`);
|
|
9719
|
-
const signedIn = await browserSignIn(ctx);
|
|
9720
|
-
ctx.out.info(`\u2713 Signed in \xB7 ${printable(signedIn.workspace.name)}`);
|
|
9721
|
-
return resolveCredential(ctx);
|
|
9722
|
-
}
|
|
9723
|
-
async function optimizePasted(ctx) {
|
|
9724
|
-
const { io, out } = ctx;
|
|
9725
|
-
const base = requestOptions(ctx);
|
|
9726
|
-
const credential = await credentialOrSignIn(ctx);
|
|
9727
|
-
const message2 = await io.readPaste(PASTE_PROMPT);
|
|
9728
|
-
if (!message2.trim()) {
|
|
9729
|
-
out.info(`Nothing pasted, so nothing was sent.
|
|
9730
|
-
${nextTimeLine(ctx)}`);
|
|
9731
|
-
return EXIT.ok;
|
|
9732
|
-
}
|
|
9733
|
-
out.info("Amdahl is rewriting it. This can take a minute.");
|
|
9734
|
-
const result = await optimizeOne(ctx, credential, message2, base, "your message");
|
|
9735
|
-
if (result.ok === false) {
|
|
9736
|
-
if (out.json) out.result({ ok: false, file: "-", result });
|
|
9737
|
-
out.info(`Not optimized (${String(result.reason ?? "unknown")}): ${String(result.detail ?? "")}`);
|
|
9738
|
-
return EXIT.general;
|
|
9739
|
-
}
|
|
9740
|
-
if (out.json) {
|
|
9741
|
-
out.result({ ok: true, file: "-", result });
|
|
9742
|
-
return EXIT.ok;
|
|
9743
|
-
}
|
|
9744
|
-
out.info(VERSION_RULE);
|
|
9745
|
-
if (result.unchanged === true) out.info("No rewrite beat your draft; it is returned unchanged.");
|
|
9746
|
-
out.result({}, String(result.message ?? ""));
|
|
9747
|
-
const did = whatAmdahlDid(result);
|
|
9748
|
-
out.info([...did, nextTimeLine(ctx)].join("\n"));
|
|
9749
|
-
return EXIT.ok;
|
|
9750
|
-
}
|
|
9751
|
-
async function optimize(ctx, args) {
|
|
9752
|
-
const { io, out } = ctx;
|
|
9753
|
-
let files = args;
|
|
9754
|
-
if (files.length === 0) {
|
|
9755
|
-
if (io.stdinIsTTY && !ctx.nonInteractive) return optimizePasted(ctx);
|
|
9756
|
-
if (io.stdinIsTTY) {
|
|
9757
|
-
throw usageError(
|
|
9758
|
-
"Give a file to optimize, or pipe the message on stdin.",
|
|
9759
|
-
"Usage: amdahl optimize <file>... | - [--channel email|linkedin]"
|
|
9760
|
-
);
|
|
9761
|
-
}
|
|
9762
|
-
files = ["-"];
|
|
9763
|
-
}
|
|
9764
|
-
if (files.filter((f) => f === "-").length > 1) throw usageError("Stdin (-) can be read only once.");
|
|
9765
|
-
const base = requestOptions(ctx);
|
|
9766
|
-
const inputs = [];
|
|
9767
|
-
for (const file of files) {
|
|
9768
|
-
let message2;
|
|
9769
|
-
try {
|
|
9770
|
-
message2 = file === "-" ? await io.readStdin() : readFileSync4(file, "utf8");
|
|
9771
|
-
} catch (err) {
|
|
9772
|
-
throw usageError(`Could not read ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
9773
|
-
}
|
|
9774
|
-
if (!message2.trim()) throw usageError(`${file === "-" ? "Stdin" : file} is empty.`);
|
|
9775
|
-
inputs.push({ file, message: message2 });
|
|
9776
|
-
}
|
|
9777
|
-
const credential = await resolveCredential(ctx);
|
|
9778
|
-
const results = [];
|
|
9779
|
-
let exit = EXIT.ok;
|
|
9780
|
-
for (const { file, message: message2 } of inputs) {
|
|
9781
|
-
const result = await optimizeOne(ctx, credential, message2, base, file);
|
|
9782
|
-
const ok = result.ok !== false;
|
|
9783
|
-
results.push({ ok, file, result });
|
|
9784
|
-
if (!ok) {
|
|
9785
|
-
exit = EXIT.general;
|
|
9786
|
-
out.info(`${file}: not optimized (${String(result.reason ?? "unknown")}): ${String(result.detail ?? "")}`);
|
|
9787
|
-
continue;
|
|
9788
|
-
}
|
|
9789
|
-
if (!out.json) {
|
|
9790
|
-
if (inputs.length > 1) out.info(`== ${file} ==`);
|
|
9791
|
-
if (result.unchanged === true) out.info("No rewrite beat your draft; it is returned unchanged.");
|
|
9792
|
-
else if (typeof result.summary === "string") out.info(result.summary);
|
|
9793
|
-
out.result({}, String(result.message ?? ""));
|
|
9794
|
-
const did = whatAmdahlDid(result);
|
|
9795
|
-
if (did.length) out.info(did.join("\n"));
|
|
9796
|
-
}
|
|
9797
|
-
}
|
|
9798
|
-
if (out.json) out.result(results.length === 1 ? results[0] : results);
|
|
9799
|
-
return exit;
|
|
9530
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
9531
|
+
|
|
9532
|
+
// src/ci.ts
|
|
9533
|
+
var isSet = (value) => Boolean(value) && value !== "false" && value !== "0";
|
|
9534
|
+
function ciVariable(env) {
|
|
9535
|
+
const named = ["CI", "JENKINS_URL", "TF_BUILD", "CODEBUILD_BUILD_ID", "TEAMCITY_VERSION", "GITHUB_ACTIONS", "GITLAB_CI", "BUILDKITE", "CIRCLECI"].find((name) => isSet(env[name]));
|
|
9536
|
+
if (named) return named;
|
|
9537
|
+
return isSet(env.BUILD_NUMBER) && isSet(env.JENKINS_HOME) ? "BUILD_NUMBER with JENKINS_HOME" : null;
|
|
9800
9538
|
}
|
|
9801
9539
|
|
|
9802
9540
|
// src/commands/skills-guide.ts
|
|
@@ -9805,7 +9543,7 @@ import { dirname as dirname3, join as join6, relative, sep } from "path";
|
|
|
9805
9543
|
|
|
9806
9544
|
// src/commands/skills-install.ts
|
|
9807
9545
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
9808
|
-
import { chmodSync as chmodSync4, lstatSync as lstatSync2, mkdirSync as mkdirSync3, mkdtempSync, readdirSync, readFileSync as
|
|
9546
|
+
import { chmodSync as chmodSync4, lstatSync as lstatSync2, mkdirSync as mkdirSync3, mkdtempSync, readdirSync, readFileSync as readFileSync4, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
9809
9547
|
import { dirname as dirname2, join as join5 } from "path";
|
|
9810
9548
|
function skillDirs(targets, name) {
|
|
9811
9549
|
const dirs = [];
|
|
@@ -9852,7 +9590,7 @@ function sameFolder(dir, archive) {
|
|
|
9852
9590
|
if (!file) return false;
|
|
9853
9591
|
const full = join5(dir, path);
|
|
9854
9592
|
if (process.platform !== "win32" && (lstatSync2(full).mode & 73 ? 493 : 420) !== file.mode) return false;
|
|
9855
|
-
if (!
|
|
9593
|
+
if (!readFileSync4(full).equals(file.bytes)) return false;
|
|
9856
9594
|
} else {
|
|
9857
9595
|
return false;
|
|
9858
9596
|
}
|
|
@@ -9897,7 +9635,7 @@ function planInstall(dirs, payload) {
|
|
|
9897
9635
|
refuseLinks(dir, path);
|
|
9898
9636
|
let current;
|
|
9899
9637
|
try {
|
|
9900
|
-
current = lstatSync2(path, { throwIfNoEntry: false }) ?
|
|
9638
|
+
current = lstatSync2(path, { throwIfNoEntry: false }) ? readFileSync4(path) : null;
|
|
9901
9639
|
} catch (err) {
|
|
9902
9640
|
throw writeFailure(path, err);
|
|
9903
9641
|
}
|
|
@@ -9985,96 +9723,368 @@ function writeFiles(plan, body) {
|
|
|
9985
9723
|
written.push(p.path);
|
|
9986
9724
|
}
|
|
9987
9725
|
}
|
|
9988
|
-
function writeInstall(io, plan, name, payload) {
|
|
9989
|
-
io.uninterruptible(() => payload.type === "archive" ? writeFolders(plan, name, payload.archive) : writeFiles(plan, payload.body));
|
|
9990
|
-
}
|
|
9991
|
-
|
|
9992
|
-
// src/commands/skills-guide.ts
|
|
9993
|
-
function shouldGuide(ctx) {
|
|
9994
|
-
const f = ctx.flags;
|
|
9995
|
-
return !ctx.nonInteractive && ctx.io.stdoutIsTTY && !ctx.out.json && f.global !== true && f.agents !== true && f.yes !== true;
|
|
9996
|
-
}
|
|
9997
|
-
function shownPath(ctx, path) {
|
|
9998
|
-
const under = (base) => {
|
|
9999
|
-
const rel = relative(base, path);
|
|
10000
|
-
return rel === "" || !rel.startsWith("..") && !rel.startsWith(sep) && !/^[a-zA-Z]:/.test(rel) ? rel : null;
|
|
10001
|
-
};
|
|
10002
|
-
const inCwd = under(ctx.io.cwd);
|
|
10003
|
-
if (inCwd !== null) return inCwd ? `./${inCwd.split(sep).join("/")}` : ".";
|
|
10004
|
-
const inHome = under(ctx.io.homedir);
|
|
10005
|
-
if (inHome !== null) return inHome ? `~/${inHome.split(sep).join("/")}` : "~";
|
|
10006
|
-
return path;
|
|
9726
|
+
function writeInstall(io, plan, name, payload) {
|
|
9727
|
+
io.uninterruptible(() => payload.type === "archive" ? writeFolders(plan, name, payload.archive) : writeFiles(plan, payload.body));
|
|
9728
|
+
}
|
|
9729
|
+
|
|
9730
|
+
// src/commands/skills-guide.ts
|
|
9731
|
+
function shouldGuide(ctx) {
|
|
9732
|
+
const f = ctx.flags;
|
|
9733
|
+
return !ctx.nonInteractive && ctx.io.stdoutIsTTY && !ctx.out.json && f.global !== true && f.agents !== true && f.yes !== true;
|
|
9734
|
+
}
|
|
9735
|
+
function shownPath(ctx, path) {
|
|
9736
|
+
const under = (base) => {
|
|
9737
|
+
const rel = relative(base, path);
|
|
9738
|
+
return rel === "" || !rel.startsWith("..") && !rel.startsWith(sep) && !/^[a-zA-Z]:/.test(rel) ? rel : null;
|
|
9739
|
+
};
|
|
9740
|
+
const inCwd = under(ctx.io.cwd);
|
|
9741
|
+
if (inCwd !== null) return inCwd ? `./${inCwd.split(sep).join("/")}` : ".";
|
|
9742
|
+
const inHome = under(ctx.io.homedir);
|
|
9743
|
+
if (inHome !== null) return inHome ? `~/${inHome.split(sep).join("/")}` : "~";
|
|
9744
|
+
return path;
|
|
9745
|
+
}
|
|
9746
|
+
function agentsInUse(ctx) {
|
|
9747
|
+
const bases = [ctx.io.cwd, ctx.io.homedir];
|
|
9748
|
+
const seen = (dir) => bases.some((base) => existsSync4(join6(base, dir)));
|
|
9749
|
+
const claude = seen(".claude") || Boolean(ctx.io.env.CLAUDECODE);
|
|
9750
|
+
const codex = seen(".codex");
|
|
9751
|
+
const cursor = seen(".cursor") || Boolean(ctx.io.env.CURSOR_AGENT);
|
|
9752
|
+
const others = codex || cursor || seen(".agents");
|
|
9753
|
+
if (!claude && !others) return { claude: true, agents: true, names: ["Claude Code", "Codex", "Cursor"] };
|
|
9754
|
+
const names = [];
|
|
9755
|
+
if (claude) names.push("Claude Code");
|
|
9756
|
+
if (codex) names.push("Codex");
|
|
9757
|
+
if (cursor) names.push("Cursor");
|
|
9758
|
+
if (others && !codex && !cursor) names.push("other agents");
|
|
9759
|
+
return { claude, agents: others, names };
|
|
9760
|
+
}
|
|
9761
|
+
function runningAgent(ctx) {
|
|
9762
|
+
if (ctx.io.env.CLAUDECODE) return "claude";
|
|
9763
|
+
if (ctx.io.env.CURSOR_AGENT) return "cursor";
|
|
9764
|
+
return null;
|
|
9765
|
+
}
|
|
9766
|
+
function joinNames(names, word = "and") {
|
|
9767
|
+
if (names.length <= 1) return names[0] ?? "";
|
|
9768
|
+
return `${names.slice(0, -1).join(", ")} ${word} ${names[names.length - 1]}`;
|
|
9769
|
+
}
|
|
9770
|
+
function readable(slug) {
|
|
9771
|
+
const words = slug.replace(/-/g, " ");
|
|
9772
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
9773
|
+
}
|
|
9774
|
+
async function guide(ctx, skill, payload) {
|
|
9775
|
+
try {
|
|
9776
|
+
return await ask(ctx, skill, payload);
|
|
9777
|
+
} catch (err) {
|
|
9778
|
+
if (err instanceof PromptCancelled) throw new CliError("cancelled", "Stopped. Nothing was written.", EXIT.cancelled);
|
|
9779
|
+
throw err;
|
|
9780
|
+
}
|
|
9781
|
+
}
|
|
9782
|
+
async function ask(ctx, skill, payload) {
|
|
9783
|
+
const { io, out } = ctx;
|
|
9784
|
+
const color = out.color;
|
|
9785
|
+
const used = agentsInUse(ctx);
|
|
9786
|
+
const count = payload.type === "archive" ? payload.archive.files.length : 1;
|
|
9787
|
+
const version = skill.version ? `${skill.version.startsWith("v") ? "" : "v"}${skill.version}, ` : "";
|
|
9788
|
+
out.info(`${readable(skill.name)}, by ${readable(skill.publisher)} (${version}${count} file${count === 1 ? "" : "s"}), for ${joinNames(used.names)}.`);
|
|
9789
|
+
let base = io.homedir;
|
|
9790
|
+
if (io.cwd !== io.homedir) {
|
|
9791
|
+
const here = io.cwd.startsWith(io.homedir + sep) ? `~${io.cwd.slice(io.homedir.length).split(sep).join("/")}` : io.cwd;
|
|
9792
|
+
const pick2 = await io.select("Install it in:", [`This project (${here})`, "Every project on this computer"], { initial: 0, color });
|
|
9793
|
+
base = pick2 === 1 ? io.homedir : io.cwd;
|
|
9794
|
+
}
|
|
9795
|
+
const targets = { base, claude: used.claude, agents: used.agents };
|
|
9796
|
+
const plan = planInstall(skillDirs(targets, skill.name), payload);
|
|
9797
|
+
const clashes = plan.filter((p) => p.action === "replaced");
|
|
9798
|
+
if (clashes.length === 0 || ctx.flags.force === true) return { kind: "install", targets, plan, force: true };
|
|
9799
|
+
out.info(
|
|
9800
|
+
[
|
|
9801
|
+
"",
|
|
9802
|
+
`A different copy of ${skill.name} is already at ${joinNames(clashes.map((p) => shownPath(ctx, p.path)))}.`,
|
|
9803
|
+
"Replacing it deletes that copy, with any changes you made to it."
|
|
9804
|
+
].join("\n")
|
|
9805
|
+
);
|
|
9806
|
+
const pick = await io.select("Keep or replace it?", ["Keep your copy", "Replace it"], { initial: 0, color });
|
|
9807
|
+
return pick === 1 ? { kind: "install", targets, plan, force: true } : { kind: "kept", targets, plan };
|
|
9808
|
+
}
|
|
9809
|
+
function skillsFolders(ctx, plan) {
|
|
9810
|
+
return joinNames(plan.map((p) => shownPath(ctx, dirname3(p.path.endsWith("SKILL.md") ? dirname3(p.path) : p.path))));
|
|
9811
|
+
}
|
|
9812
|
+
function nextStep(ctx, targets, seen) {
|
|
9813
|
+
const where = targets.base === ctx.io.homedir ? "in any folder" : "in this folder";
|
|
9814
|
+
const names = seen.map((n) => n === "other agents" ? "other agent" : n);
|
|
9815
|
+
return `Open a new ${joinNames(names, "or")} chat ${where} to use it.`;
|
|
9816
|
+
}
|
|
9817
|
+
|
|
9818
|
+
// src/commands/optimize.ts
|
|
9819
|
+
var NEW_USER = "New to Amdahl? The sign-in sets up your free workspace.";
|
|
9820
|
+
var OPTIMIZE_TIMEOUT_MS = 18e4;
|
|
9821
|
+
var CHANNELS = ["email", "linkedin"];
|
|
9822
|
+
var EVIDENCE = ["off", "workspace"];
|
|
9823
|
+
var ON_UNSUPPORTED = ["flag", "remove"];
|
|
9824
|
+
function oneOf(flag, value, allowed) {
|
|
9825
|
+
if (value === void 0) return void 0;
|
|
9826
|
+
if (!allowed.includes(value)) throw usageError(`--${flag} must be one of: ${allowed.join(", ")}.`);
|
|
9827
|
+
return value;
|
|
9828
|
+
}
|
|
9829
|
+
function readContext(path) {
|
|
9830
|
+
let parsed;
|
|
9831
|
+
try {
|
|
9832
|
+
parsed = JSON.parse(readFileSync5(path, "utf8"));
|
|
9833
|
+
} catch (err) {
|
|
9834
|
+
throw usageError(`Could not read --context ${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
9835
|
+
}
|
|
9836
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9837
|
+
throw usageError("--context must be a JSON object with rules, voice_examples and facts.");
|
|
9838
|
+
}
|
|
9839
|
+
return parsed;
|
|
9840
|
+
}
|
|
9841
|
+
function withTimeout(promise, ms, file) {
|
|
9842
|
+
let timer;
|
|
9843
|
+
const timeout = new Promise((_, reject) => {
|
|
9844
|
+
timer = setTimeout(
|
|
9845
|
+
() => reject(new CliError("timeout", `Optimizing ${file} timed out after ${ms / 1e3} s.`, EXIT.network)),
|
|
9846
|
+
ms
|
|
9847
|
+
);
|
|
9848
|
+
});
|
|
9849
|
+
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
9850
|
+
}
|
|
9851
|
+
var WEAKEST = {
|
|
9852
|
+
rules: "how it follows your rules",
|
|
9853
|
+
ask: "the ask",
|
|
9854
|
+
opener: "the opener",
|
|
9855
|
+
fit: "how it fits the recipient",
|
|
9856
|
+
proof: "its proof points",
|
|
9857
|
+
length: "the length",
|
|
9858
|
+
style: "the tone",
|
|
9859
|
+
preference: "how it matches your preferences",
|
|
9860
|
+
voice: "how much it sounds like you"
|
|
9861
|
+
};
|
|
9862
|
+
var CONCERNS = {
|
|
9863
|
+
conflicts_with_callers_ask: "it asks for a different length or time than your ask",
|
|
9864
|
+
drops_ask: "it leaves out your ask",
|
|
9865
|
+
adds_claim: "it states something neither your draft nor your facts support",
|
|
9866
|
+
drops_entity: "it drops a name, link or placeholder you had",
|
|
9867
|
+
inflates: "it is longer than you asked for",
|
|
9868
|
+
no_gain: "it changed little beyond the wording",
|
|
9869
|
+
breaks_rules: "it breaks one of your rules"
|
|
9870
|
+
};
|
|
9871
|
+
var EVIDENCE_MISSED = {
|
|
9872
|
+
corpus_empty: "the workspace has none yet",
|
|
9873
|
+
evidence_unavailable: "this environment does not read them",
|
|
9874
|
+
evidence_read_failed: "the read failed, so a retry may work"
|
|
9875
|
+
};
|
|
9876
|
+
var MAX_LINES = 3;
|
|
9877
|
+
function list(value) {
|
|
9878
|
+
return Array.isArray(value) ? value.filter((v) => !!v && typeof v === "object") : null;
|
|
9879
|
+
}
|
|
9880
|
+
function record(value) {
|
|
9881
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
9882
|
+
}
|
|
9883
|
+
function text(value) {
|
|
9884
|
+
return typeof value === "string" ? printable(value).trim() : "";
|
|
9885
|
+
}
|
|
9886
|
+
function sentence(value) {
|
|
9887
|
+
return /[.!?]$/.test(value) ? value : `${value}.`;
|
|
9888
|
+
}
|
|
9889
|
+
function plural(n, one, many = `${one}s`) {
|
|
9890
|
+
return `${n} ${n === 1 ? one : many}`;
|
|
9891
|
+
}
|
|
9892
|
+
function readSteps(steps) {
|
|
9893
|
+
const reasonOf = (node) => text(steps.find((s) => s.node === node)?.reason);
|
|
9894
|
+
const changed = (node) => steps.some((s) => s.node === node && s.changed === true);
|
|
9895
|
+
const counts = new Map(
|
|
9896
|
+
reasonOf("w3").split(",").map((part) => part.split(":")).filter(([, n]) => /^\d+$/.test(n ?? "")).map(([code, n]) => [code, Number(n)])
|
|
9897
|
+
);
|
|
9898
|
+
const did = [];
|
|
9899
|
+
const repaired = counts.get("repaired") ?? 0;
|
|
9900
|
+
const deleted = counts.get("deleted") ?? 0;
|
|
9901
|
+
if (repaired && deleted) did.push(`Rewrote ${plural(repaired, "sentence")} and cut ${deleted} that nothing backed.`);
|
|
9902
|
+
else if (repaired) did.push(`Rewrote ${plural(repaired, "sentence")} that did not hold up.`);
|
|
9903
|
+
else if (deleted) did.push(`Cut ${plural(deleted, "sentence")} that nothing backed.`);
|
|
9904
|
+
const repair = reasonOf("w5");
|
|
9905
|
+
if (changed("w5") && repair.startsWith("repaired:")) {
|
|
9906
|
+
const weakest = WEAKEST[repair.slice("repaired:".length)];
|
|
9907
|
+
did.push(weakest ? `Reworked ${weakest}.` : "Reworked its weakest part.");
|
|
9908
|
+
}
|
|
9909
|
+
const flagged = [];
|
|
9910
|
+
const open = counts.get("open");
|
|
9911
|
+
if (open) flagged.push(`${plural(open, "sentence problem")} it could not fix`);
|
|
9912
|
+
const second = reasonOf("w6");
|
|
9913
|
+
if (changed("w6") && !second.startsWith("kept_before_repair")) {
|
|
9914
|
+
const concern = CONCERNS[second];
|
|
9915
|
+
flagged.push(concern ? `a second-check concern (${concern})` : "a second-check concern");
|
|
9916
|
+
}
|
|
9917
|
+
return { did: did.join(" "), flagged };
|
|
9918
|
+
}
|
|
9919
|
+
function and(parts) {
|
|
9920
|
+
return parts.length < 2 ? parts[0] ?? "" : `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
|
|
10007
9921
|
}
|
|
10008
|
-
function
|
|
10009
|
-
const
|
|
10010
|
-
const
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
const
|
|
10014
|
-
const
|
|
10015
|
-
if (
|
|
10016
|
-
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10021
|
-
|
|
9922
|
+
function whatAmdahlDid(result) {
|
|
9923
|
+
const steps = result.unchanged === true ? { did: "", flagged: [] } : readSteps(list(result.steps) ?? []);
|
|
9924
|
+
const flagged = [...steps.flagged];
|
|
9925
|
+
let checked = "";
|
|
9926
|
+
let removed = "";
|
|
9927
|
+
const evidence = record(result.evidence);
|
|
9928
|
+
const claims = list(result.unsupported_claims);
|
|
9929
|
+
if (evidence?.used === true) {
|
|
9930
|
+
const quotes = record(result.lift)?.quotes;
|
|
9931
|
+
const read = typeof quotes === "number" && quotes > 0 ? ` (${plural(quotes, "quote")})` : "";
|
|
9932
|
+
if (claims) {
|
|
9933
|
+
const still = claims.filter((c) => c.status !== "removed").length;
|
|
9934
|
+
const cut = claims.length - still;
|
|
9935
|
+
if (cut) removed = `Removed ${plural(cut, "claim")} your calls don't back.`;
|
|
9936
|
+
if (still) flagged.unshift(`${plural(still, "claim")} your calls don't back`);
|
|
9937
|
+
checked = `Checked every claim against your calls${read}${claims.length ? "" : " and found none they don't back"}.`;
|
|
9938
|
+
} else checked = `Checked the message against your calls${read}.`;
|
|
9939
|
+
} else if (evidence?.requested === "workspace" && EVIDENCE_MISSED[text(evidence.reason)]) {
|
|
9940
|
+
checked = `Skipped checking claims against your calls: ${EVIDENCE_MISSED[text(evidence.reason)]}.`;
|
|
9941
|
+
}
|
|
9942
|
+
const used = (list(record(result.context_use)?.rows) ?? []).filter((r) => r.verdict === "pass");
|
|
9943
|
+
const sources = [
|
|
9944
|
+
used.some((r) => r.bucket === "internal") ? "Amdahl's own records" : "",
|
|
9945
|
+
used.some((r) => r.bucket === "external") ? "what their company or market published" : ""
|
|
9946
|
+
].filter(Boolean);
|
|
9947
|
+
const usedLine = sources.length ? `Used ${plural(sources.length, "source")}: ${and(sources)}.` : "";
|
|
9948
|
+
const omitted = list(result.omitted) ?? [];
|
|
9949
|
+
const whys = [...new Set(omitted.map((o) => text(o.why)).filter(Boolean))];
|
|
9950
|
+
const more = whys.length > 1 ? ` (${plural(whys.length - 1, "more reason")} in --json)` : "";
|
|
9951
|
+
const leftOut = omitted.length ? `Left out ${plural(omitted.length, "thing")}${whys[0] ? `: ${sentence(whys[0])}` : "."}${more}` : "";
|
|
9952
|
+
const lines = [
|
|
9953
|
+
{ rank: 2, line: sentence(text(result.what_changed)) },
|
|
9954
|
+
{ rank: 5, line: checked },
|
|
9955
|
+
{ rank: 3, line: removed },
|
|
9956
|
+
{ rank: 6, line: usedLine },
|
|
9957
|
+
{ rank: 7, line: steps.did },
|
|
9958
|
+
{ rank: 4, line: leftOut },
|
|
9959
|
+
{ rank: 1, line: flagged.length ? `Flagged ${and(flagged)}; check before sending.` : "" }
|
|
9960
|
+
].filter((l) => l.line && l.line !== ".");
|
|
9961
|
+
const kept = new Set([...lines].sort((a, b) => a.rank - b.rank).slice(0, MAX_LINES));
|
|
9962
|
+
return lines.filter((l) => kept.has(l)).map((l) => l.line);
|
|
10022
9963
|
}
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
9964
|
+
var PASTE_PROMPT = "Paste an email or LinkedIn message, then press Enter on an empty line:";
|
|
9965
|
+
var VERSION_RULE = "\u2500\u2500 Amdahl's version \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500";
|
|
9966
|
+
function nextTimeLine(ctx) {
|
|
9967
|
+
const file = amdahlCommand("optimize draft.txt");
|
|
9968
|
+
return ctx.io.platform === "darwin" ? `Next time: ${file},
|
|
9969
|
+
or pbpaste | ${amdahlCommand("optimize -")}` : `Next time: ${file}`;
|
|
10027
9970
|
}
|
|
10028
|
-
function
|
|
10029
|
-
|
|
10030
|
-
|
|
9971
|
+
function requestOptions(ctx) {
|
|
9972
|
+
const { flags } = ctx;
|
|
9973
|
+
const base = {};
|
|
9974
|
+
const channel = oneOf("channel", str(flags.channel), CHANNELS);
|
|
9975
|
+
const evidence = oneOf("evidence", str(flags.evidence), EVIDENCE);
|
|
9976
|
+
const onUnsupported = oneOf("on-unsupported", str(flags["on-unsupported"]), ON_UNSUPPORTED);
|
|
9977
|
+
if (channel) base.channel = channel;
|
|
9978
|
+
if (evidence) base.evidence = evidence;
|
|
9979
|
+
if (onUnsupported) base.on_unsupported = onUnsupported;
|
|
9980
|
+
if (flags.tries === true) base.include_tries = true;
|
|
9981
|
+
const contextPath = str(flags.context);
|
|
9982
|
+
if (contextPath) base.context = readContext(contextPath);
|
|
9983
|
+
return base;
|
|
10031
9984
|
}
|
|
10032
|
-
function
|
|
10033
|
-
|
|
10034
|
-
|
|
9985
|
+
async function optimizeOne(ctx, credential, message2, base, file) {
|
|
9986
|
+
try {
|
|
9987
|
+
return await withTimeout(
|
|
9988
|
+
apiClient(ctx, credential).request("/api/platform/v1/messages/optimize", {
|
|
9989
|
+
method: "POST",
|
|
9990
|
+
body: { message: message2, ...base }
|
|
9991
|
+
}),
|
|
9992
|
+
OPTIMIZE_TIMEOUT_MS,
|
|
9993
|
+
file
|
|
9994
|
+
);
|
|
9995
|
+
} catch (err) {
|
|
9996
|
+
throw toCliError(err);
|
|
9997
|
+
}
|
|
10035
9998
|
}
|
|
10036
|
-
async function
|
|
9999
|
+
async function credentialOrSignIn(ctx) {
|
|
10037
10000
|
try {
|
|
10038
|
-
return await
|
|
10001
|
+
return await resolveCredential(ctx);
|
|
10039
10002
|
} catch (err) {
|
|
10040
|
-
if (err instanceof
|
|
10041
|
-
throw err;
|
|
10003
|
+
if (!(err instanceof CliError && err.exit === EXIT.unauthenticated)) throw err;
|
|
10042
10004
|
}
|
|
10005
|
+
ctx.out.info(`Sign in first, then paste your message.
|
|
10006
|
+
${NEW_USER}`);
|
|
10007
|
+
const signedIn = await browserSignIn(ctx);
|
|
10008
|
+
ctx.out.info(`\u2713 Signed in \xB7 ${printable(signedIn.workspace.name)}`);
|
|
10009
|
+
return resolveCredential(ctx);
|
|
10043
10010
|
}
|
|
10044
|
-
async function
|
|
10011
|
+
async function optimizePasted(ctx) {
|
|
10045
10012
|
const { io, out } = ctx;
|
|
10046
|
-
const
|
|
10047
|
-
const
|
|
10048
|
-
const
|
|
10049
|
-
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
const here = io.cwd.startsWith(io.homedir + sep) ? `~${io.cwd.slice(io.homedir.length).split(sep).join("/")}` : io.cwd;
|
|
10054
|
-
const pick2 = await io.select("Install it in:", [`This project (${here})`, "Every project on this computer"], { initial: 0, color });
|
|
10055
|
-
base = pick2 === 1 ? io.homedir : io.cwd;
|
|
10013
|
+
const base = requestOptions(ctx);
|
|
10014
|
+
const credential = await credentialOrSignIn(ctx);
|
|
10015
|
+
const message2 = await io.readPaste(PASTE_PROMPT);
|
|
10016
|
+
if (!message2.trim()) {
|
|
10017
|
+
out.info(`Nothing pasted, so nothing was sent.
|
|
10018
|
+
${nextTimeLine(ctx)}`);
|
|
10019
|
+
return EXIT.ok;
|
|
10056
10020
|
}
|
|
10057
|
-
|
|
10058
|
-
const
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
}
|
|
10071
|
-
|
|
10072
|
-
|
|
10021
|
+
out.info("Amdahl is rewriting it. This can take a minute.");
|
|
10022
|
+
const result = await optimizeOne(ctx, credential, message2, base, "your message");
|
|
10023
|
+
if (result.ok === false) {
|
|
10024
|
+
if (out.json) out.result({ ok: false, file: "-", result });
|
|
10025
|
+
out.info(`Not optimized (${String(result.reason ?? "unknown")}): ${String(result.detail ?? "")}`);
|
|
10026
|
+
return EXIT.general;
|
|
10027
|
+
}
|
|
10028
|
+
if (out.json) {
|
|
10029
|
+
out.result({ ok: true, file: "-", result });
|
|
10030
|
+
return EXIT.ok;
|
|
10031
|
+
}
|
|
10032
|
+
out.info(VERSION_RULE);
|
|
10033
|
+
if (result.unchanged === true) out.info("No rewrite beat your draft; it is returned unchanged.");
|
|
10034
|
+
out.result({}, String(result.message ?? ""));
|
|
10035
|
+
const did = whatAmdahlDid(result);
|
|
10036
|
+
out.info([...did, nextTimeLine(ctx)].join("\n"));
|
|
10037
|
+
return EXIT.ok;
|
|
10073
10038
|
}
|
|
10074
|
-
function
|
|
10075
|
-
const
|
|
10076
|
-
|
|
10077
|
-
|
|
10039
|
+
async function optimize(ctx, args) {
|
|
10040
|
+
const { io, out } = ctx;
|
|
10041
|
+
let files = args;
|
|
10042
|
+
if (files.length === 0) {
|
|
10043
|
+
if (io.stdinIsTTY && !ctx.nonInteractive && runningAgent(ctx) === null && !ciVariable(io.env)) return optimizePasted(ctx);
|
|
10044
|
+
if (io.stdinIsTTY) {
|
|
10045
|
+
throw usageError(
|
|
10046
|
+
"Give a file to optimize, or pipe the message on stdin.",
|
|
10047
|
+
"Usage: amdahl optimize <file>... | - [--channel email|linkedin]"
|
|
10048
|
+
);
|
|
10049
|
+
}
|
|
10050
|
+
files = ["-"];
|
|
10051
|
+
}
|
|
10052
|
+
if (files.filter((f) => f === "-").length > 1) throw usageError("Stdin (-) can be read only once.");
|
|
10053
|
+
const base = requestOptions(ctx);
|
|
10054
|
+
const inputs = [];
|
|
10055
|
+
for (const file of files) {
|
|
10056
|
+
let message2;
|
|
10057
|
+
try {
|
|
10058
|
+
message2 = file === "-" ? await io.readStdin() : readFileSync5(file, "utf8");
|
|
10059
|
+
} catch (err) {
|
|
10060
|
+
throw usageError(`Could not read ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
10061
|
+
}
|
|
10062
|
+
if (!message2.trim()) throw usageError(`${file === "-" ? "Stdin" : file} is empty.`);
|
|
10063
|
+
inputs.push({ file, message: message2 });
|
|
10064
|
+
}
|
|
10065
|
+
const credential = await resolveCredential(ctx);
|
|
10066
|
+
const results = [];
|
|
10067
|
+
let exit = EXIT.ok;
|
|
10068
|
+
for (const { file, message: message2 } of inputs) {
|
|
10069
|
+
const result = await optimizeOne(ctx, credential, message2, base, file);
|
|
10070
|
+
const ok = result.ok !== false;
|
|
10071
|
+
results.push({ ok, file, result });
|
|
10072
|
+
if (!ok) {
|
|
10073
|
+
exit = EXIT.general;
|
|
10074
|
+
out.info(`${file}: not optimized (${String(result.reason ?? "unknown")}): ${String(result.detail ?? "")}`);
|
|
10075
|
+
continue;
|
|
10076
|
+
}
|
|
10077
|
+
if (!out.json) {
|
|
10078
|
+
if (inputs.length > 1) out.info(`== ${file} ==`);
|
|
10079
|
+
if (result.unchanged === true) out.info("No rewrite beat your draft; it is returned unchanged.");
|
|
10080
|
+
else if (typeof result.summary === "string") out.info(result.summary);
|
|
10081
|
+
out.result({}, String(result.message ?? ""));
|
|
10082
|
+
const did = whatAmdahlDid(result);
|
|
10083
|
+
if (did.length) out.info(did.join("\n"));
|
|
10084
|
+
}
|
|
10085
|
+
}
|
|
10086
|
+
if (out.json) out.result(results.length === 1 ? results[0] : results);
|
|
10087
|
+
return exit;
|
|
10078
10088
|
}
|
|
10079
10089
|
|
|
10080
10090
|
// src/commands/setup.ts
|
|
@@ -10179,12 +10189,6 @@ function finishLines(names, inClaudeCode = false) {
|
|
|
10179
10189
|
};
|
|
10180
10190
|
return [`Try it: ${AGENT_ASK} in any of them.`, "Before that, connect each one once:", ...names.map((n) => ` ${n}: ${step[n] ?? ""}`)];
|
|
10181
10191
|
}
|
|
10182
|
-
var isSet = (value) => Boolean(value) && value !== "false" && value !== "0";
|
|
10183
|
-
function ciVariable(env) {
|
|
10184
|
-
const named = ["CI", "JENKINS_URL", "TF_BUILD", "CODEBUILD_BUILD_ID", "TEAMCITY_VERSION", "GITHUB_ACTIONS", "GITLAB_CI", "BUILDKITE", "CIRCLECI"].find((name) => isSet(env[name]));
|
|
10185
|
-
if (named) return named;
|
|
10186
|
-
return isSet(env.BUILD_NUMBER) && isSet(env.JENKINS_HOME) ? "BUILD_NUMBER with JENKINS_HOME" : null;
|
|
10187
|
-
}
|
|
10188
10192
|
function setupMode(io) {
|
|
10189
10193
|
const { env } = io;
|
|
10190
10194
|
const ci = ciVariable(env);
|
|
@@ -10256,7 +10260,7 @@ async function codexScreen(ctx, tool, say, detail, lead) {
|
|
|
10256
10260
|
say(tool.already ? "\u2713 Codex already has Amdahl." : "\u2713 Added Amdahl to Codex.");
|
|
10257
10261
|
detail(tool.detail);
|
|
10258
10262
|
for (const line of lead) say(line);
|
|
10259
|
-
if (!await onPath(ctx, "codex")) {
|
|
10263
|
+
if (ctx.io.platform === "win32" || !await onPath(ctx, "codex")) {
|
|
10260
10264
|
say("One step left, in your terminal:");
|
|
10261
10265
|
say(` 1. Run: ${CODEX_LOGIN}`);
|
|
10262
10266
|
say(` 2. ${TRY_IN.Codex ?? ""}`);
|
|
@@ -10310,13 +10314,15 @@ ${installLine()}
|
|
|
10310
10314
|
if (detected.length > 0) {
|
|
10311
10315
|
const there = await Promise.all(detected.map((c) => hasAmdahl(ctx, c.client)));
|
|
10312
10316
|
if (there.every(Boolean)) {
|
|
10317
|
+
let exit2 = EXIT.ok;
|
|
10313
10318
|
for (const tool of detected) {
|
|
10314
10319
|
if (await isInstalled(ctx, tool.client)) continue;
|
|
10315
|
-
const
|
|
10316
|
-
for (const r of ready) detail(r.detail);
|
|
10320
|
+
const refreshed = await installEach(ctx, [tool]);
|
|
10321
|
+
for (const r of refreshed.ready) detail(r.detail);
|
|
10322
|
+
if (exit2 === EXIT.ok) exit2 = refreshed.exit;
|
|
10317
10323
|
}
|
|
10318
10324
|
for (const line of allSetLines(detected.map((c) => c.name))) say(line);
|
|
10319
|
-
return
|
|
10325
|
+
return exit2;
|
|
10320
10326
|
}
|
|
10321
10327
|
}
|
|
10322
10328
|
const rows = [...SHOWN.map((c) => c.name), TERMINAL];
|
|
@@ -11003,7 +11009,7 @@ async function workspaceUse(ctx, args) {
|
|
|
11003
11009
|
}
|
|
11004
11010
|
|
|
11005
11011
|
// src/version.ts
|
|
11006
|
-
var VERSION = "0.3.
|
|
11012
|
+
var VERSION = "0.3.6";
|
|
11007
11013
|
|
|
11008
11014
|
// src/main.ts
|
|
11009
11015
|
var COMMANDS = {
|
package/package.json
CHANGED