@agentgrant.cash/cli 1.3.0 → 1.4.0

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.
@@ -0,0 +1,119 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync, } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join, dirname } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { buildContext, emit, ui } from "../../lib/index.js";
7
+ /**
8
+ * `grant onboard` — one-step setup for a fresh agent.
9
+ *
10
+ * This is the command the hosted onboarding skill (`/skill.md`) tells an agent
11
+ * to run after a user pastes "Set up https://<host>/skill.md". It:
12
+ * 1. installs the FULL Grant Cash skill (bundled in this package) into
13
+ * whatever coding agent it detects (Claude Code / Cursor / Codex /
14
+ * Windsurf / Continue), so the agent gains the complete command map, and
15
+ * 2. connects the account by running the real `login` flow, which prints the
16
+ * sign-in link and waits for the user to approve.
17
+ *
18
+ * Net effect: the agent goes from "knows nothing" to "skill installed + link
19
+ * in front of the user" from a single command, with no `curl | sh`.
20
+ */
21
+ const SKILL_NAME = "grant-cash";
22
+ /** The full operational skill shipped inside this package. */
23
+ function readBundledSkill() {
24
+ const p = fileURLToPath(new URL("../../../skills/grant-cash/SKILL.md", import.meta.url));
25
+ return readFileSync(p, "utf8");
26
+ }
27
+ /** Detect installed agents and write the skill into each one's convention. */
28
+ function installSkill(skill) {
29
+ const home = homedir();
30
+ const installed = [];
31
+ const has = (rel) => existsSync(join(home, rel));
32
+ const writeFresh = (file) => {
33
+ mkdirSync(dirname(file), { recursive: true });
34
+ writeFileSync(file, skill);
35
+ };
36
+ if (has(".claude")) {
37
+ const file = join(home, ".claude", "skills", SKILL_NAME, "SKILL.md");
38
+ writeFresh(file);
39
+ installed.push({ agent: "Claude Code", path: file });
40
+ }
41
+ if (has(".cursor")) {
42
+ const file = join(home, ".cursor", "rules", `${SKILL_NAME}.mdc`);
43
+ writeFresh(file);
44
+ installed.push({ agent: "Cursor", path: file });
45
+ }
46
+ if (has(".codex")) {
47
+ // Codex reads one AGENTS.md — append (deduped) rather than overwrite.
48
+ const file = join(home, ".codex", "AGENTS.md");
49
+ const existing = existsSync(file) ? readFileSync(file, "utf8") : "";
50
+ if (!/^name: grant-cash$/m.test(existing)) {
51
+ mkdirSync(dirname(file), { recursive: true });
52
+ appendFileSync(file, (existing.trim() ? "\n\n---\n\n" : "") + skill);
53
+ }
54
+ installed.push({ agent: "Codex", path: file });
55
+ }
56
+ if (has(join(".codeium", "windsurf"))) {
57
+ const file = join(home, ".codeium", "windsurf", "memories", `${SKILL_NAME}.md`);
58
+ writeFresh(file);
59
+ installed.push({ agent: "Windsurf", path: file });
60
+ }
61
+ if (has(".continue")) {
62
+ const file = join(home, ".continue", "rules", `${SKILL_NAME}.md`);
63
+ writeFresh(file);
64
+ installed.push({ agent: "Continue", path: file });
65
+ }
66
+ // No known agent detected → default to the Claude Code layout so the skill
67
+ // still lands somewhere sane.
68
+ if (installed.length === 0) {
69
+ const file = join(home, ".claude", "skills", SKILL_NAME, "SKILL.md");
70
+ writeFresh(file);
71
+ installed.push({ agent: "Claude Code", path: file });
72
+ }
73
+ return installed;
74
+ }
75
+ export function registerOnboard(program) {
76
+ program
77
+ .command("onboard")
78
+ .description("Set up Grant Cash: install the skill into your agent and connect (one step)")
79
+ .option("--no-open", "do not auto-open the browser during sign-in; just print the link")
80
+ .option("--no-login", "only install the skill; skip sign-in")
81
+ .option("--timeout <seconds>", "how long to wait for the browser", "300")
82
+ .action(async (opts, cmd) => {
83
+ const ctx = buildContext(cmd);
84
+ const installed = installSkill(readBundledSkill());
85
+ if (!ctx.json) {
86
+ process.stdout.write(`\n${ui.title("Grant Cash — set up")}\n` +
87
+ installed
88
+ .map((t) => ` ${ui.green("✓")} ${t.agent.padEnd(12)} ${ui.dim(t.path)}`)
89
+ .join("\n") +
90
+ "\n");
91
+ }
92
+ // Install-only mode (e.g. CI, or re-installing the skill).
93
+ if (opts.login === false) {
94
+ emit(ctx, { installed, connected: false }, () => `\n${ui.green("✓ Skill installed.")} ${ui.dim("Run `grant login` to connect.")}\n`);
95
+ return;
96
+ }
97
+ // Connect by re-running the real `login` command with inherited stdio, so
98
+ // the sign-in link prints, the browser opens, and polling completes
99
+ // exactly as `grant login` would. Forward the relevant flags.
100
+ const passthrough = ["login"];
101
+ if (ctx.json)
102
+ passthrough.push("--json");
103
+ if (opts.open === false)
104
+ passthrough.push("--no-open");
105
+ if (opts.timeout)
106
+ passthrough.push("--timeout", String(opts.timeout));
107
+ const globals = cmd.optsWithGlobals();
108
+ if (globals.credsFile)
109
+ passthrough.push("--creds-file", globals.credsFile);
110
+ if (!ctx.json) {
111
+ process.stdout.write(`\n${ui.dim("Connecting your account…")}\n`);
112
+ }
113
+ const res = spawnSync(process.execPath, [process.argv[1], ...passthrough], {
114
+ stdio: "inherit",
115
+ });
116
+ if (res.status && res.status !== 0)
117
+ process.exitCode = res.status;
118
+ });
119
+ }
package/dist/cli/index.js CHANGED
@@ -25,6 +25,7 @@ import { registerPortfolio } from "./commands/portfolio.js";
25
25
  import { registerMoney } from "./commands/money.js";
26
26
  import { registerAgent } from "./commands/agent.js";
27
27
  import { registerMeta } from "./commands/meta.js";
28
+ import { registerOnboard } from "./commands/onboard.js";
28
29
  // ── full investing surface ported from the standalone Perfolio CLI ──
29
30
  import { registerTrade } from "./perfolio-commands/trade.js";
30
31
  import { registerMarket } from "./perfolio-commands/market.js";
@@ -55,6 +56,7 @@ program
55
56
  .option("--json", "machine-readable JSON output")
56
57
  .option("--creds-file <path>", "override ~/.grant-cash/credentials.json");
57
58
  registerAuth(program);
59
+ registerOnboard(program);
58
60
  registerPortfolio(program);
59
61
  registerMoney(program);
60
62
  registerAgent(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentgrant.cash/cli",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Grant Cash — one CLI for your money (gold) and your agent (pay-per-use services). Routes to the Perfolio backend and the Agent-mode backend behind a single, plain-language surface.",
5
5
  "type": "module",
6
6
  "bin": {