@monnet/mcp 0.7.1 → 0.8.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.
- package/README.md +19 -1
- package/assets/monnet-init.md +37 -0
- package/dist/cli/env.d.ts +13 -0
- package/dist/cli/env.js +44 -0
- package/dist/cli/env.js.map +1 -0
- package/dist/cli/install.d.ts +10 -0
- package/dist/cli/install.js +76 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/cli/io.d.ts +18 -0
- package/dist/cli/io.js +30 -0
- package/dist/cli/io.js.map +1 -0
- package/dist/cli/login.d.ts +14 -0
- package/dist/cli/login.js +77 -0
- package/dist/cli/login.js.map +1 -0
- package/dist/cli/onboarding.d.ts +41 -0
- package/dist/cli/onboarding.js +145 -0
- package/dist/cli/onboarding.js.map +1 -0
- package/dist/cli/run.d.ts +11 -0
- package/dist/cli/run.js +35 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/test-home.d.ts +8 -0
- package/dist/cli/test-home.js +17 -0
- package/dist/cli/test-home.js.map +1 -0
- package/dist/cli/test-io.d.ts +21 -0
- package/dist/cli/test-io.js +32 -0
- package/dist/cli/test-io.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +12 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +22 -1
- package/dist/client.js +56 -19
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +17 -0
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -0
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/test-env.d.ts +4 -7
- package/dist/test-env.js +4 -7
- package/dist/test-env.js.map +1 -1
- package/dist/tools/list-workspaces.js +26 -8
- package/dist/tools/list-workspaces.js.map +1 -1
- package/dist/tools/wiki/_common.d.ts +10 -0
- package/dist/tools/wiki/_common.js +10 -0
- package/dist/tools/wiki/_common.js.map +1 -1
- package/dist/tools/wiki/read.js +2 -2
- package/dist/tools/wiki/read.js.map +1 -1
- package/dist/tools/wiki/write.js +29 -17
- package/dist/tools/wiki/write.js.map +1 -1
- package/dist/version.d.ts +13 -0
- package/dist/version.js +26 -0
- package/dist/version.js.map +1 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -6,6 +6,24 @@ Exposes motions, plans, and approvals to MCP-compatible clients (Claude Desktop,
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
+
Install the Monnet MCP and the `/monnet-init` onboarding command for Codex and
|
|
10
|
+
Claude Code:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx -y @monnet/mcp install
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Start a fresh coding-agent session and run `/monnet-init`. The command opens a
|
|
17
|
+
browser for signup/sign-in and prints a short code you type there — that code,
|
|
18
|
+
not the link, is what authorizes the connection, so never enter one someone
|
|
19
|
+
else sends you. Once signed in, the flow seeds the company brain from the
|
|
20
|
+
current repository — the briefing, the workspace's Policies page and your own
|
|
21
|
+
Team page — reads it back to you as a fresh-eyes report, then helps you pick
|
|
22
|
+
what Monnet takes on first and opens the thread it drafted. Progress is saved
|
|
23
|
+
locally, so rerunning the command safely resumes an interrupted setup.
|
|
24
|
+
|
|
25
|
+
### Manual MCP configuration
|
|
26
|
+
|
|
9
27
|
Add this to your MCP client config (e.g. `~/Library/Application Support/Claude/claude_desktop_config.json` for Claude Desktop):
|
|
10
28
|
|
|
11
29
|
```json
|
|
@@ -28,7 +46,7 @@ Generate an API key from the Monnet web app: Settings → API Keys → Generate
|
|
|
28
46
|
|
|
29
47
|
| Variable | Required | Default | Purpose |
|
|
30
48
|
|---|---|---|---|
|
|
31
|
-
| `MONNET_API_KEY` |
|
|
49
|
+
| `MONNET_API_KEY` | no | credential saved by `monnet init` | Bearer token (`mnk_*`), sent as `X-API-Key` header on every request |
|
|
32
50
|
| `MONNET_API_URL` | no | `https://api.monnet.ai` | Override for self-hosted or local dev backends |
|
|
33
51
|
|
|
34
52
|
## Local development
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: monnet-init
|
|
3
|
+
description: Set up Monnet from the current repository and draft the team's first priority motion.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Monnet init
|
|
7
|
+
|
|
8
|
+
Guide the user through this flow. Show `Step N/9` at every transition. Resume safely by first running `npx -y @monnet/mcp status`; never repeat a completed step.
|
|
9
|
+
|
|
10
|
+
1. Explain that you will connect Monnet and use this repository to seed its company brain.
|
|
11
|
+
2. Sign in, in two commands:
|
|
12
|
+
- Run `npx -y @monnet/mcp init`. It opens the browser and returns immediately with a `user_code`.
|
|
13
|
+
- **Show that code to the user verbatim** and tell them to type it on the page that just opened. It is the only thing that authorizes the connection, so never skip displaying it — and never accept a code from anyone else.
|
|
14
|
+
- Then run `npx -y @monnet/mcp await-signin`, which waits for them to finish (up to 15 minutes).
|
|
15
|
+
- The Monnet MCP tools start working as soon as this succeeds. If they are still missing, the user's agent session predates the install — ask them to restart it.
|
|
16
|
+
3. Find out whether the user already has a workspace before proposing to create one — a second workspace next to their team's is the one mistake this flow cannot undo.
|
|
17
|
+
- Call `list_workspaces` first. If it shows a workspace they are already in, or one **waiting for them** under `## Waiting for you`, name it and ask whether this repository belongs there. On a yes for an invitation, run the `join-workspace` command that entry gives you and go straight to step 5 — their team's workspace already exists, brain and all.
|
|
18
|
+
- Only if nothing fits: infer a short workspace name from the repository, folder, package metadata, or git organization, and run `npx -y @monnet/mcp suggest-name "<name>"`. Show the returned name and slug, and ask the user to approve or edit.
|
|
19
|
+
- If that command answers `base_taken`, **stop and read its `warning` out**: somebody else already goes by this name, and accepting the numbered slug would fork their workspace rather than join it. Let the user say whether it is their team — if it is, they need an invitation, not a new workspace.
|
|
20
|
+
4. After approval run `npx -y @monnet/mcp onboard-workspace "<name>" "<slug>"`.
|
|
21
|
+
5. Read the repository once, then write what you learned into the brain three ways. Inspect only useful context (README, manifests, product docs, architecture docs, and recent high-level history; respect ignore files and never include secrets).
|
|
22
|
+
- **The briefing.** One concise Markdown document covering what the company/product does, users, architecture, current initiatives, and concrete unknowns. Pipe it to `npx -y @monnet/mcp push-knowledge`, which files it and asks the brain to digest it straight away rather than tonight.
|
|
23
|
+
- **The workspace's Policies page.** It already exists: the page at the root of the tree, named after the workspace, written from a template of `[bracketed placeholders]`. Find it with `get_wiki_tree`, `read_wiki_page` it, then `edit_wiki_page` once — match the placeholder text and replace it with what the repository actually shows. Keep its section headings exactly as they are; they are the ones Monnet reads.
|
|
24
|
+
- **The user's own page.** `Team/<their name>`, created empty with the workspace — `monnet_whoami` tells you which one is theirs. Write how to work with them: their role, what they own, how they work, what they would rather Monnet just did without asking. Its `old_string` is the `(this page is empty)` placeholder `read_wiki_page` shows in place of a body.
|
|
25
|
+
|
|
26
|
+
Never create a page — both already exist, and a second copy splits the brain. If one is genuinely absent, move on and say so. Skip a section rather than filling it with a guess, and tell the user what you wrote.
|
|
27
|
+
6. Give the user a fresh-eyes report on them and their company: an outsider's read of what you just learned, not a summary of the repository. Write it in the terminal under `## What I learned`, `## First impressions`, `## Motions to kick off`. Say what surprised you and what looks unresolved — a report that only flatters is worth nothing. Then offer to keep it: on a yes, pipe it to `npx -y @monnet/mcp push-knowledge` so it is theirs to share.
|
|
28
|
+
7. Ask what Monnet should take on first. Offer exactly these three, and let them skip:
|
|
29
|
+
- **a fresh-eyes report on the team** — Monnet interviews each teammate and builds, with them, the outside view you just sketched alone;
|
|
30
|
+
- **the top priority the repository points at** — propose the three strongest candidates, one line of rationale each, and let them pick one;
|
|
31
|
+
- **something else**, in their own words.
|
|
32
|
+
|
|
33
|
+
Do not choose for them.
|
|
34
|
+
8. Ask how to invite teammates: Slack (default), Teams, email, or skip. For a channel run `npx -y @monnet/mcp invite <slack|teams|email>`; it opens the UI that connects that channel — Slack and email land on the invite flow, Teams on the workspace's Integrations tab. Continue when they say they are done. If skipped, continue without the command.
|
|
35
|
+
9. Unless the selection was skipped, build a focused prompt containing the chosen outcome, why now, relevant repository evidence, constraints, and a request for an actionable draft with owners. Pipe it to `npx -y @monnet/mcp create-first-motion`. For the team report, pass the playbook Monnet already runs for it instead of describing the interviews yourself: `npx -y @monnet/mcp create-first-motion onboard-team`. It opens the thread ready to review and publish. Report the returned URL.
|
|
36
|
+
|
|
37
|
+
If any command is interrupted, rerun this skill: all server creates and local progress are resumable. Never print or ask for an API key — only ever the short sign-in code from step 2.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function apiUrl(): string;
|
|
2
|
+
export declare function appUrl(): string;
|
|
3
|
+
/** The credential this process acts as.
|
|
4
|
+
*
|
|
5
|
+
* Same precedence as the MCP tools in `client.ts`: the environment wins over
|
|
6
|
+
* the saved key. They used to disagree — the tools read `MONNET_API_KEY` and
|
|
7
|
+
* the CLI read only the file — so a session with that variable set listed one
|
|
8
|
+
* person's invitations and accepted them as somebody else.
|
|
9
|
+
*/
|
|
10
|
+
export declare function apiKey(): string | undefined;
|
|
11
|
+
/** One HTTP call against the Monnet API, with the saved credential unless the
|
|
12
|
+
* endpoint is part of the sign-in handshake itself. */
|
|
13
|
+
export declare function request<T>(path: string, init?: RequestInit, authenticated?: boolean): Promise<T>;
|
package/dist/cli/env.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readConfig } from "../config.js";
|
|
2
|
+
import { fail } from "./io.js";
|
|
3
|
+
const trimSlash = (url) => url.replace(/\/$/, "");
|
|
4
|
+
export function apiUrl() {
|
|
5
|
+
return trimSlash(process.env.MONNET_API_URL || readConfig().apiUrl || "https://api.monnet.ai");
|
|
6
|
+
}
|
|
7
|
+
export function appUrl() {
|
|
8
|
+
return trimSlash(process.env.MONNET_APP_URL || readConfig().appUrl || "https://app.monnet.ai");
|
|
9
|
+
}
|
|
10
|
+
/** The credential this process acts as.
|
|
11
|
+
*
|
|
12
|
+
* Same precedence as the MCP tools in `client.ts`: the environment wins over
|
|
13
|
+
* the saved key. They used to disagree — the tools read `MONNET_API_KEY` and
|
|
14
|
+
* the CLI read only the file — so a session with that variable set listed one
|
|
15
|
+
* person's invitations and accepted them as somebody else.
|
|
16
|
+
*/
|
|
17
|
+
export function apiKey() {
|
|
18
|
+
return process.env.MONNET_API_KEY || readConfig().apiKey;
|
|
19
|
+
}
|
|
20
|
+
/** One HTTP call against the Monnet API, with the saved credential unless the
|
|
21
|
+
* endpoint is part of the sign-in handshake itself. */
|
|
22
|
+
export async function request(path, init = {}, authenticated = true) {
|
|
23
|
+
const token = apiKey();
|
|
24
|
+
if (authenticated && !token)
|
|
25
|
+
fail("not connected yet; run `npx -y @monnet/mcp init` first");
|
|
26
|
+
const response = await fetch(`${apiUrl()}${path}`, {
|
|
27
|
+
...init,
|
|
28
|
+
headers: {
|
|
29
|
+
"Content-Type": "application/json",
|
|
30
|
+
...(authenticated ? { "X-API-Key": token } : {}),
|
|
31
|
+
...init.headers,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
let detail = `${response.status}`;
|
|
36
|
+
try {
|
|
37
|
+
detail = (await response.json()).detail || detail;
|
|
38
|
+
}
|
|
39
|
+
catch { /* empty or non-JSON body */ }
|
|
40
|
+
fail(detail);
|
|
41
|
+
}
|
|
42
|
+
return response.status === 204 ? undefined : (await response.json());
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/cli/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAE1D,MAAM,UAAU,MAAM;IACpB,OAAO,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,EAAE,CAAC,MAAM,IAAI,uBAAuB,CAAC,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,MAAM;IACpB,OAAO,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,EAAE,CAAC,MAAM,IAAI,uBAAuB,CAAC,CAAC;AACjG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,MAAM;IACpB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,EAAE,CAAC,MAAM,CAAC;AAC3D,CAAC;AAED;wDACwD;AACxD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAI,IAAY,EAAE,OAAoB,EAAE,EAAE,aAAa,GAAG,IAAI;IACzF,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC;IACvB,IAAI,aAAa,IAAI,CAAC,KAAK;QAAE,IAAI,CAAC,wDAAwD,CAAC,CAAC;IAE5F,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE;QACjD,GAAG,IAAI;QACP,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,GAAG,IAAI,CAAC,OAAO;SAChB;KACF,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,IAAI,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,GAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAyB,CAAC,MAAM,IAAI,MAAM,CAAC;QAC7E,CAAC;QAAC,MAAM,CAAC,CAAC,4BAA4B,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,CAAC;IACf,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAE,SAAe,CAAC,CAAC,CAAE,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAO,CAAC;AACrF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Installs the Monnet MCP server and the `/monnet-init` command into the
|
|
2
|
+
* coding agents present on this machine.
|
|
3
|
+
*
|
|
4
|
+
* The command file is written to each agent's own convention: Claude Code
|
|
5
|
+
* reads `~/.claude/commands/*.md`, Codex reads `~/.codex/prompts/*.md`. Both
|
|
6
|
+
* are overwritten on every install — they are ours, and a stale copy of the
|
|
7
|
+
* flow is worse than no copy.
|
|
8
|
+
*/
|
|
9
|
+
import type { Io } from "./io.js";
|
|
10
|
+
export declare function install(io: Io): Promise<void>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Installs the Monnet MCP server and the `/monnet-init` command into the
|
|
2
|
+
* coding agents present on this machine.
|
|
3
|
+
*
|
|
4
|
+
* The command file is written to each agent's own convention: Claude Code
|
|
5
|
+
* reads `~/.claude/commands/*.md`, Codex reads `~/.codex/prompts/*.md`. Both
|
|
6
|
+
* are overwritten on every install — they are ours, and a stale copy of the
|
|
7
|
+
* flow is worse than no copy.
|
|
8
|
+
*/
|
|
9
|
+
import { execFile } from "node:child_process";
|
|
10
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { readFile } from "node:fs/promises";
|
|
12
|
+
import { homedir, platform } from "node:os";
|
|
13
|
+
import { dirname, join } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
import { promisify } from "node:util";
|
|
16
|
+
const execFileAsync = promisify(execFile);
|
|
17
|
+
const FALLBACK_COMMAND = "Run `npx -y @monnet/mcp init`, then follow the JSON instructions it returns.\n";
|
|
18
|
+
function commandTargets() {
|
|
19
|
+
return [
|
|
20
|
+
join(homedir(), ".claude", "commands", "monnet-init.md"),
|
|
21
|
+
join(homedir(), ".codex", "prompts", "monnet-init.md"),
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
async function commandBody() {
|
|
25
|
+
const source = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "assets", "monnet-init.md");
|
|
26
|
+
try {
|
|
27
|
+
return await readFile(source, "utf8");
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Shipped package always has the asset ; a linked dev checkout may not.
|
|
31
|
+
return FALLBACK_COMMAND;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function isExecutable(name) {
|
|
35
|
+
try {
|
|
36
|
+
await execFileAsync(platform() === "win32" ? "where" : "which", [name]);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function registerMcp(agent) {
|
|
44
|
+
if (!(await isExecutable(agent)))
|
|
45
|
+
return false;
|
|
46
|
+
const scope = agent === "claude" ? ["--scope", "user"] : [];
|
|
47
|
+
try {
|
|
48
|
+
await execFileAsync(agent, ["mcp", "add", ...scope, "monnet", "--", "npx", "-y", "@monnet/mcp"]);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Already registered — an idempotent install, not a failure.
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
export async function install(io) {
|
|
56
|
+
const body = await commandBody();
|
|
57
|
+
const targets = commandTargets();
|
|
58
|
+
for (const target of targets) {
|
|
59
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
60
|
+
writeFileSync(target, body);
|
|
61
|
+
}
|
|
62
|
+
const agents = [];
|
|
63
|
+
for (const agent of ["claude", "codex"]) {
|
|
64
|
+
if (await registerMcp(agent))
|
|
65
|
+
agents.push(agent);
|
|
66
|
+
}
|
|
67
|
+
io.out({
|
|
68
|
+
installed: true,
|
|
69
|
+
commands: targets,
|
|
70
|
+
agents,
|
|
71
|
+
next: agents.length
|
|
72
|
+
? "Start a fresh coding-agent session and run /monnet-init."
|
|
73
|
+
: "No claude or codex CLI found on PATH — add the MCP server manually (see the README), then run /monnet-init.",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/cli/install.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/io.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Everything the commands do to the outside world, in one injectable shape.
|
|
2
|
+
* Tests pass a recording double ; nothing else in the CLI touches stdio,
|
|
3
|
+
* the clock or the browser directly. */
|
|
4
|
+
export interface Io {
|
|
5
|
+
/** Machine-readable result, for the coding agent parsing stdout. */
|
|
6
|
+
out(value: unknown): void;
|
|
7
|
+
/** Human-readable progress, for the terminal the user is looking at. */
|
|
8
|
+
err(text: string): void;
|
|
9
|
+
open(url: string): void;
|
|
10
|
+
sleep(ms: number): Promise<void>;
|
|
11
|
+
readStdin(): Promise<string>;
|
|
12
|
+
}
|
|
13
|
+
/** A message meant for the user, not a stack trace. The entry point turns it
|
|
14
|
+
* into a clean stderr line and exit code 1. */
|
|
15
|
+
export declare class CliFailure extends Error {
|
|
16
|
+
}
|
|
17
|
+
export declare function fail(message: string): never;
|
|
18
|
+
export declare function defaultIo(): Io;
|
package/dist/cli/io.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { platform } from "node:os";
|
|
3
|
+
/** A message meant for the user, not a stack trace. The entry point turns it
|
|
4
|
+
* into a clean stderr line and exit code 1. */
|
|
5
|
+
export class CliFailure extends Error {
|
|
6
|
+
}
|
|
7
|
+
export function fail(message) {
|
|
8
|
+
throw new CliFailure(message);
|
|
9
|
+
}
|
|
10
|
+
function openInBrowser(url) {
|
|
11
|
+
const [command, args] = platform() === "darwin" ? ["open", [url]]
|
|
12
|
+
: platform() === "win32" ? ["cmd", ["/c", "start", "", url]]
|
|
13
|
+
: ["xdg-open", [url]];
|
|
14
|
+
spawn(command, args, { detached: true, stdio: "ignore" }).unref();
|
|
15
|
+
}
|
|
16
|
+
export function defaultIo() {
|
|
17
|
+
return {
|
|
18
|
+
out: (value) => process.stdout.write(`${typeof value === "string" ? value : JSON.stringify(value)}\n`),
|
|
19
|
+
err: (text) => process.stderr.write(text),
|
|
20
|
+
open: openInBrowser,
|
|
21
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
22
|
+
readStdin: async () => {
|
|
23
|
+
const chunks = [];
|
|
24
|
+
for await (const chunk of process.stdin)
|
|
25
|
+
chunks.push(chunk);
|
|
26
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=io.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.js","sourceRoot":"","sources":["../../src/cli/io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAenC;gDACgD;AAChD,MAAM,OAAO,UAAW,SAAQ,KAAK;CAAG;AAExC,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,MAAM,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GACnB,QAAQ,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC,CAAC,QAAQ,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;YAC1D,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO;QACL,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;QACtG,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;QACzC,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAChE,SAAS,EAAE,KAAK,IAAI,EAAE;YACpB,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK;gBAAE,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC;YACtE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser sign-in, in two commands on purpose.
|
|
3
|
+
*
|
|
4
|
+
* `init` opens the browser and returns immediately with the code the user
|
|
5
|
+
* must type there, so the coding agent can show it before anything blocks.
|
|
6
|
+
* `await-signin` then waits for the browser step to complete.
|
|
7
|
+
*
|
|
8
|
+
* The session id stays local — it is what redeems the key, so it never goes
|
|
9
|
+
* into a URL. What the browser gets is the short code, which is useless to
|
|
10
|
+
* anyone who is not looking at this terminal.
|
|
11
|
+
*/
|
|
12
|
+
import { type Io } from "./io.js";
|
|
13
|
+
export declare function init(io: Io): Promise<void>;
|
|
14
|
+
export declare function awaitSignin(io: Io): Promise<void>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser sign-in, in two commands on purpose.
|
|
3
|
+
*
|
|
4
|
+
* `init` opens the browser and returns immediately with the code the user
|
|
5
|
+
* must type there, so the coding agent can show it before anything blocks.
|
|
6
|
+
* `await-signin` then waits for the browser step to complete.
|
|
7
|
+
*
|
|
8
|
+
* The session id stays local — it is what redeems the key, so it never goes
|
|
9
|
+
* into a URL. What the browser gets is the short code, which is useless to
|
|
10
|
+
* anyone who is not looking at this terminal.
|
|
11
|
+
*/
|
|
12
|
+
import { readConfig, updateOnboarding, writeConfig } from "../config.js";
|
|
13
|
+
import { apiKey, apiUrl, appUrl, request } from "./env.js";
|
|
14
|
+
import { fail } from "./io.js";
|
|
15
|
+
const POLL_INTERVAL_MS = 2000;
|
|
16
|
+
const DEFAULT_TTL_SECONDS = 15 * 60;
|
|
17
|
+
export async function init(io) {
|
|
18
|
+
if (apiKey()) {
|
|
19
|
+
io.out({ authenticated: true, resumed: true, next: "propose_workspace" });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const session = await request("/api/cli-auth/mcp/start", { method: "POST" }, false);
|
|
23
|
+
const url = `${appUrl()}/cli-auth?kind=mcp`;
|
|
24
|
+
updateOnboarding({ pendingDevice: session.device, step: 2 });
|
|
25
|
+
io.err(`\nMonnet sign-in code: ${session.user_code}\n` +
|
|
26
|
+
`Opened ${url} — sign in, then type that code.\n` +
|
|
27
|
+
"It expires in 15 minutes, and only this terminal can use it.\n\n");
|
|
28
|
+
io.open(url);
|
|
29
|
+
io.out({
|
|
30
|
+
authenticated: false,
|
|
31
|
+
user_code: session.user_code,
|
|
32
|
+
url,
|
|
33
|
+
expires_in: session.expires_in,
|
|
34
|
+
next: "await-signin",
|
|
35
|
+
show_user: `Sign in at ${url} and enter the code ${session.user_code}`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export async function awaitSignin(io) {
|
|
39
|
+
const config = readConfig();
|
|
40
|
+
if (apiKey()) {
|
|
41
|
+
io.out({ authenticated: true, resumed: true, next: "propose_workspace" });
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const device = config.onboarding?.pendingDevice;
|
|
45
|
+
if (!device)
|
|
46
|
+
fail("no sign-in in progress; run `npx -y @monnet/mcp init` first");
|
|
47
|
+
const attempts = Math.ceil((DEFAULT_TTL_SECONDS * 1000) / POLL_INTERVAL_MS);
|
|
48
|
+
io.err("Waiting for the browser");
|
|
49
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
50
|
+
await io.sleep(POLL_INTERVAL_MS);
|
|
51
|
+
const response = await fetch(`${apiUrl()}/api/cli-auth/mcp/poll`, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: { "Content-Type": "application/json" },
|
|
54
|
+
body: JSON.stringify({ device }),
|
|
55
|
+
});
|
|
56
|
+
if (response.status === 202) {
|
|
57
|
+
io.err(".");
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (!response.ok)
|
|
61
|
+
fail(`sign-in failed (${response.status}); run /monnet-init again to restart it`);
|
|
62
|
+
const { token } = (await response.json());
|
|
63
|
+
const current = readConfig();
|
|
64
|
+
writeConfig({
|
|
65
|
+
...current,
|
|
66
|
+
apiKey: token,
|
|
67
|
+
apiUrl: apiUrl(),
|
|
68
|
+
appUrl: appUrl(),
|
|
69
|
+
onboarding: { ...current.onboarding, pendingDevice: undefined, step: 3 },
|
|
70
|
+
});
|
|
71
|
+
io.err("\n");
|
|
72
|
+
io.out({ authenticated: true, next: "propose_workspace" });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
fail("sign-in timed out after 15 minutes; run /monnet-init again to resume");
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/cli/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** The workspace/knowledge/invite/motion steps of `/monnet-init`.
|
|
2
|
+
*
|
|
3
|
+
* Every one of them writes its progress to the local config, so an
|
|
4
|
+
* interrupted run resumes where it stopped instead of creating a second
|
|
5
|
+
* workspace.
|
|
6
|
+
*/
|
|
7
|
+
import { type Io } from "./io.js";
|
|
8
|
+
/** Where each channel actually connects teammates, as the web app has it. */
|
|
9
|
+
declare const INVITE_CHANNELS: {
|
|
10
|
+
readonly slack: (slug: string) => string;
|
|
11
|
+
readonly email: (slug: string) => string;
|
|
12
|
+
readonly teams: (slug: string) => string;
|
|
13
|
+
};
|
|
14
|
+
export type InviteChannel = keyof typeof INVITE_CHANNELS;
|
|
15
|
+
export declare function inviteUrl(channel: InviteChannel, slug: string): string;
|
|
16
|
+
export declare function suggestName(io: Io, args: string[], fallbackName: string): Promise<void>;
|
|
17
|
+
/** Accept an invitation and adopt that workspace as this run's workspace.
|
|
18
|
+
*
|
|
19
|
+
* The token comes from `list_workspaces`, which reads it off the sidebar's
|
|
20
|
+
* pending invitations. Joining is what a teammate needs instead of step 4:
|
|
21
|
+
* their team's workspace already exists, brain and all.
|
|
22
|
+
*/
|
|
23
|
+
export declare function joinWorkspace(io: Io, args: string[]): Promise<void>;
|
|
24
|
+
export declare function onboardWorkspace(io: Io, args: string[]): Promise<void>;
|
|
25
|
+
/** Queue the briefing, then ask the brain to digest it now.
|
|
26
|
+
*
|
|
27
|
+
* Pushing alone only files a SOURCE: the pages a person recognises as their
|
|
28
|
+
* company brain are written by the ingest job, which otherwise runs tonight —
|
|
29
|
+
* long after the user has closed the terminal and decided the wiki is empty.
|
|
30
|
+
* So we press the same "Run now" button the Brain header offers.
|
|
31
|
+
*/
|
|
32
|
+
export declare function pushKnowledge(io: Io): Promise<void>;
|
|
33
|
+
export declare function invite(io: Io, args: string[]): void;
|
|
34
|
+
/** `args[0]` is an optional workspace skill to pre-apply — the flow passes
|
|
35
|
+
* `onboard-team` when the user asked for a fresh-eyes report on the team, so
|
|
36
|
+
* the run opens with the playbook Monnet already uses for that, instead of a
|
|
37
|
+
* prompt reinventing it. An unknown name degrades to a plain thread server
|
|
38
|
+
* side, so it is never worth validating twice. */
|
|
39
|
+
export declare function createFirstMotion(io: Io, args?: string[]): Promise<void>;
|
|
40
|
+
export declare function status(io: Io): void;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/** The workspace/knowledge/invite/motion steps of `/monnet-init`.
|
|
2
|
+
*
|
|
3
|
+
* Every one of them writes its progress to the local config, so an
|
|
4
|
+
* interrupted run resumes where it stopped instead of creating a second
|
|
5
|
+
* workspace.
|
|
6
|
+
*/
|
|
7
|
+
import { configPath, readConfig, updateOnboarding } from "../config.js";
|
|
8
|
+
import { apiKey, appUrl, request } from "./env.js";
|
|
9
|
+
import { CliFailure, fail } from "./io.js";
|
|
10
|
+
/** Where each channel actually connects teammates, as the web app has it. */
|
|
11
|
+
const INVITE_CHANNELS = {
|
|
12
|
+
slack: (slug) => `/onboarding/invite?slug=${encodeURIComponent(slug)}`,
|
|
13
|
+
email: (slug) => `/onboarding/invite/email?slug=${encodeURIComponent(slug)}`,
|
|
14
|
+
// Teams is connected from the workspace's Integrations tab — the onboarding
|
|
15
|
+
// invite chooser only offers Slack and email.
|
|
16
|
+
teams: (slug) => `/${encodeURIComponent(slug)}/edit?tab=integrations`,
|
|
17
|
+
};
|
|
18
|
+
export function inviteUrl(channel, slug) {
|
|
19
|
+
return `${appUrl()}${INVITE_CHANNELS[channel](slug)}`;
|
|
20
|
+
}
|
|
21
|
+
function workspace() {
|
|
22
|
+
const state = readConfig().onboarding;
|
|
23
|
+
if (!state?.workspaceId || !state.workspaceSlug)
|
|
24
|
+
fail("create the workspace first");
|
|
25
|
+
return { workspaceId: state.workspaceId, workspaceSlug: state.workspaceSlug };
|
|
26
|
+
}
|
|
27
|
+
export async function suggestName(io, args, fallbackName) {
|
|
28
|
+
const name = args.join(" ").trim() || fallbackName;
|
|
29
|
+
const { slug, base, base_taken } = await request(`/workspaces/generate-slug?name=${encodeURIComponent(name)}`);
|
|
30
|
+
// The returned SLUG is free by construction — that is the one thing it can
|
|
31
|
+
// never tell you. `base_taken` can: somebody else already goes by this name,
|
|
32
|
+
// and taking `acme-2` would fork their workspace rather than join it. Say so
|
|
33
|
+
// instead of quietly reserving the next number.
|
|
34
|
+
const warning = base_taken
|
|
35
|
+
? `A workspace already goes by "${base}" and it is not yours. If that is your team, have ` +
|
|
36
|
+
"them invite you (Settings → Members) and run `join-workspace` instead of creating a " +
|
|
37
|
+
"second one. Otherwise pick a different name."
|
|
38
|
+
: undefined;
|
|
39
|
+
io.out({ name, slug, slug_available: true, base, base_taken, ...(warning ? { warning } : {}) });
|
|
40
|
+
}
|
|
41
|
+
/** Accept an invitation and adopt that workspace as this run's workspace.
|
|
42
|
+
*
|
|
43
|
+
* The token comes from `list_workspaces`, which reads it off the sidebar's
|
|
44
|
+
* pending invitations. Joining is what a teammate needs instead of step 4:
|
|
45
|
+
* their team's workspace already exists, brain and all.
|
|
46
|
+
*/
|
|
47
|
+
export async function joinWorkspace(io, args) {
|
|
48
|
+
const token = args[0]?.trim();
|
|
49
|
+
if (!token)
|
|
50
|
+
fail("usage: join-workspace <invitation-token>");
|
|
51
|
+
const accepted = await request(`/invitations/${encodeURIComponent(token)}/accept`, { method: "POST" });
|
|
52
|
+
// The rest of the flow addresses the workspace by id, and acceptance only
|
|
53
|
+
// hands back the slug.
|
|
54
|
+
const workspace = await request(`/workspaces/by-slug/${encodeURIComponent(accepted.workspace_slug)}`);
|
|
55
|
+
updateOnboarding({ workspaceId: workspace.id, workspaceSlug: workspace.slug, step: 5 });
|
|
56
|
+
io.out({ id: workspace.id, slug: workspace.slug, name: workspace.name, joined: true });
|
|
57
|
+
}
|
|
58
|
+
export async function onboardWorkspace(io, args) {
|
|
59
|
+
const [name, slug] = args;
|
|
60
|
+
if (!name || !slug)
|
|
61
|
+
fail("usage: onboard-workspace <name> <available-slug>");
|
|
62
|
+
const existing = readConfig().onboarding;
|
|
63
|
+
if (existing?.workspaceId && existing.workspaceSlug) {
|
|
64
|
+
// Resume only the workspace this config is actually about. The state lives
|
|
65
|
+
// in ~/.config/monnet, so it is per MACHINE, not per repository: without
|
|
66
|
+
// this check, running the flow in a second repo silently adopted the first
|
|
67
|
+
// one's workspace and ignored the name the user had just approved.
|
|
68
|
+
if (existing.workspaceSlug !== slug) {
|
|
69
|
+
fail(`this machine is already onboarding "${existing.workspaceSlug}", not "${slug}". ` +
|
|
70
|
+
`Finish that run, or start over by removing the "onboarding" block from ${configPath}.`);
|
|
71
|
+
}
|
|
72
|
+
io.out({ id: existing.workspaceId, slug: existing.workspaceSlug, resumed: true });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const created = await request("/workspaces", { method: "POST", body: JSON.stringify({ name, slug }) });
|
|
76
|
+
updateOnboarding({ workspaceId: created.id, workspaceSlug: created.slug, step: 5 });
|
|
77
|
+
io.out(created);
|
|
78
|
+
}
|
|
79
|
+
/** Queue the briefing, then ask the brain to digest it now.
|
|
80
|
+
*
|
|
81
|
+
* Pushing alone only files a SOURCE: the pages a person recognises as their
|
|
82
|
+
* company brain are written by the ingest job, which otherwise runs tonight —
|
|
83
|
+
* long after the user has closed the terminal and decided the wiki is empty.
|
|
84
|
+
* So we press the same "Run now" button the Brain header offers.
|
|
85
|
+
*/
|
|
86
|
+
export async function pushKnowledge(io) {
|
|
87
|
+
const { workspaceId } = workspace();
|
|
88
|
+
const content = await io.readStdin();
|
|
89
|
+
if (!content.trim())
|
|
90
|
+
fail("pipe the generated knowledge document on stdin");
|
|
91
|
+
const source = await request(`/workspaces/${workspaceId}/wiki/sources/text`, { method: "POST", body: JSON.stringify({ filename: "initial-repository-context.md", content }) });
|
|
92
|
+
updateOnboarding({ step: 6 });
|
|
93
|
+
io.out({ ...source, ...(await startIngest(io, workspaceId)), next: "fresh_eyes_report" });
|
|
94
|
+
}
|
|
95
|
+
/** Best-effort: the source is filed either way, and every refusal here is a
|
|
96
|
+
* reason to say "tonight" rather than to fail the step — the job is switched
|
|
97
|
+
* off, or a run is already going (409), or the network blipped. */
|
|
98
|
+
async function startIngest(io, workspaceId) {
|
|
99
|
+
try {
|
|
100
|
+
await request(`/workspaces/${workspaceId}/scheduled-tasks/brain_ingest/run`, { method: "POST" });
|
|
101
|
+
return {
|
|
102
|
+
ingest_started: true,
|
|
103
|
+
note: "The brain is digesting it now — pages appear over the next few minutes.",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const detail = error instanceof CliFailure ? error.message : String(error);
|
|
108
|
+
io.err(`could not start the brain ingest (${detail}); it will run tonight\n`);
|
|
109
|
+
return { ingest_started: false, note: "Filed as a source; the nightly ingest will turn it into pages." };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export function invite(io, args) {
|
|
113
|
+
const requested = (args[0] || "slack");
|
|
114
|
+
if (!(requested in INVITE_CHANNELS)) {
|
|
115
|
+
fail(`unknown invite channel "${requested}"; use one of ${Object.keys(INVITE_CHANNELS).join(", ")}`);
|
|
116
|
+
}
|
|
117
|
+
const { workspaceSlug } = workspace();
|
|
118
|
+
const url = inviteUrl(requested, workspaceSlug);
|
|
119
|
+
io.open(url);
|
|
120
|
+
updateOnboarding({ step: 7 });
|
|
121
|
+
io.out({ channel: requested, url, opened: true, can_skip: true });
|
|
122
|
+
}
|
|
123
|
+
/** `args[0]` is an optional workspace skill to pre-apply — the flow passes
|
|
124
|
+
* `onboard-team` when the user asked for a fresh-eyes report on the team, so
|
|
125
|
+
* the run opens with the playbook Monnet already uses for that, instead of a
|
|
126
|
+
* prompt reinventing it. An unknown name degrades to a plain thread server
|
|
127
|
+
* side, so it is never worth validating twice. */
|
|
128
|
+
export async function createFirstMotion(io, args = []) {
|
|
129
|
+
const { workspaceId, workspaceSlug } = workspace();
|
|
130
|
+
const playbook = args[0]?.trim() || undefined;
|
|
131
|
+
const prompt = await io.readStdin();
|
|
132
|
+
if (!prompt.trim())
|
|
133
|
+
fail("pipe the chosen priority and supporting context on stdin");
|
|
134
|
+
const motion = await request(`/workspaces/${workspaceId}/motions/generate`, { method: "POST", body: JSON.stringify({ prompt, ...(playbook ? { playbook } : {}) }) });
|
|
135
|
+
const shortId = motion.id.slice(0, 8);
|
|
136
|
+
const url = `${appUrl()}/${workspaceSlug}/motions/${shortId}`;
|
|
137
|
+
io.open(url);
|
|
138
|
+
updateOnboarding({ motionId: motion.id, step: 8 });
|
|
139
|
+
io.out({ id: motion.id, short_id: shortId, url, playbook: playbook ?? null, opened: true, complete: true });
|
|
140
|
+
}
|
|
141
|
+
export function status(io) {
|
|
142
|
+
const config = readConfig();
|
|
143
|
+
io.out({ connected: Boolean(apiKey()), ...(config.onboarding || { step: 1 }) });
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=onboarding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../../src/cli/onboarding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Command registry for the `monnet` CLI.
|
|
2
|
+
*
|
|
3
|
+
* Adding a step to /monnet-init means adding one entry here, never editing a
|
|
4
|
+
* dispatch chain.
|
|
5
|
+
*/
|
|
6
|
+
import { type Io } from "./io.js";
|
|
7
|
+
type Command = (io: Io, args: string[]) => void | Promise<void>;
|
|
8
|
+
export declare const COMMANDS: Record<string, Command>;
|
|
9
|
+
export declare const USAGE: string;
|
|
10
|
+
export declare function run(argv: string[], io?: Io): Promise<void>;
|
|
11
|
+
export {};
|
package/dist/cli/run.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Command registry for the `monnet` CLI.
|
|
2
|
+
*
|
|
3
|
+
* Adding a step to /monnet-init means adding one entry here, never editing a
|
|
4
|
+
* dispatch chain.
|
|
5
|
+
*/
|
|
6
|
+
import { hostname } from "node:os";
|
|
7
|
+
import { install } from "./install.js";
|
|
8
|
+
import { awaitSignin, init } from "./login.js";
|
|
9
|
+
import { defaultIo, fail } from "./io.js";
|
|
10
|
+
import { createFirstMotion, invite, joinWorkspace, onboardWorkspace, pushKnowledge, status, suggestName, } from "./onboarding.js";
|
|
11
|
+
export const COMMANDS = {
|
|
12
|
+
install: (io) => install(io),
|
|
13
|
+
init: (io) => init(io),
|
|
14
|
+
"await-signin": (io) => awaitSignin(io),
|
|
15
|
+
"suggest-name": (io, args) => suggestName(io, args, hostname().split(".")[0]),
|
|
16
|
+
"onboard-workspace": (io, args) => onboardWorkspace(io, args),
|
|
17
|
+
"join-workspace": (io, args) => joinWorkspace(io, args),
|
|
18
|
+
"push-knowledge": (io) => pushKnowledge(io),
|
|
19
|
+
invite: (io, args) => invite(io, args),
|
|
20
|
+
"create-first-motion": (io, args) => createFirstMotion(io, args),
|
|
21
|
+
status: (io) => status(io),
|
|
22
|
+
};
|
|
23
|
+
export const USAGE = `Usage: monnet <${Object.keys(COMMANDS).join("|")}>`;
|
|
24
|
+
export async function run(argv, io = defaultIo()) {
|
|
25
|
+
const [command, ...args] = argv;
|
|
26
|
+
if (!command || command === "help" || command === "--help") {
|
|
27
|
+
io.out(USAGE);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const handler = COMMANDS[command];
|
|
31
|
+
if (!handler)
|
|
32
|
+
fail(`unknown command "${command}"\n${USAGE}`);
|
|
33
|
+
await handler(io, args);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=run.js.map
|