@magic-markdown/cli 0.3.5 → 0.3.7
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 +8 -6
- package/dist/index.js +147 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,19 +22,21 @@ The package is a single self-contained bundle with no runtime dependencies. It r
|
|
|
22
22
|
## Quick start for agents
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
+
npx --yes --package=@magic-markdown/cli@latest mdocs version --json
|
|
26
|
+
|
|
25
27
|
# Join a shared document and announce presence
|
|
26
|
-
mdocs join <share-url> --doc <docId> --name "Claude Code" --json
|
|
28
|
+
npx --yes --package=@magic-markdown/cli@latest mdocs join <share-url> --doc <docId> --name "Claude Code" --json
|
|
27
29
|
|
|
28
30
|
# Orient without dumping the whole document
|
|
29
|
-
mdocs remote context --summary --json
|
|
31
|
+
npx --yes --package=@magic-markdown/cli@latest mdocs remote context --summary --json
|
|
30
32
|
|
|
31
33
|
# Page document text separately from review state
|
|
32
|
-
mdocs remote context --start-line 1 --end-line 120 --no-review --json
|
|
33
|
-
mdocs remote review --json
|
|
34
|
+
npx --yes --package=@magic-markdown/cli@latest mdocs remote context --start-line 1 --end-line 120 --no-review --json
|
|
35
|
+
npx --yes --package=@magic-markdown/cli@latest mdocs remote review --json
|
|
34
36
|
|
|
35
37
|
# Leave a comment or propose an edit (ranges are 1-based, inclusive)
|
|
36
|
-
mdocs remote comment --range 12:14 --body "Tighten this paragraph." --json
|
|
37
|
-
mdocs remote suggest --range 12:14 --with-file replacement.md --message "Clearer phrasing" --json
|
|
38
|
+
npx --yes --package=@magic-markdown/cli@latest mdocs remote comment --range 12:14 --body "Tighten this paragraph." --json
|
|
39
|
+
npx --yes --package=@magic-markdown/cli@latest mdocs remote suggest --range 12:14 --with-file replacement.md --message "Clearer phrasing" --json
|
|
38
40
|
```
|
|
39
41
|
|
|
40
42
|
Run `mdocs agent guide` for the full agent workflow, `mdocs help <command>` for any command, or `mdocs agent commands --json` for the machine-readable command contract.
|
package/dist/index.js
CHANGED
|
@@ -10990,7 +10990,7 @@ var RemoteDocumentIO = class {
|
|
|
10990
10990
|
};
|
|
10991
10991
|
|
|
10992
10992
|
// src/agent.ts
|
|
10993
|
-
var CLI_VERSION = "0.3.
|
|
10993
|
+
var CLI_VERSION = "0.3.7";
|
|
10994
10994
|
var CLI_PACKAGE_NAME = "@magic-markdown/cli";
|
|
10995
10995
|
var AGENT_COMMANDS = [
|
|
10996
10996
|
{
|
|
@@ -11009,6 +11009,14 @@ var AGENT_COMMANDS = [
|
|
|
11009
11009
|
mutates: false,
|
|
11010
11010
|
examples: ["mdocs agent commands --json"]
|
|
11011
11011
|
},
|
|
11012
|
+
{
|
|
11013
|
+
name: "agent-guide",
|
|
11014
|
+
summary: "Alias for mdocs agent guide.",
|
|
11015
|
+
usage: "mdocs agent-guide [--json]",
|
|
11016
|
+
output: "text",
|
|
11017
|
+
mutates: false,
|
|
11018
|
+
examples: ["mdocs agent-guide", "mdocs agent-guide --json"]
|
|
11019
|
+
},
|
|
11012
11020
|
{
|
|
11013
11021
|
name: "init",
|
|
11014
11022
|
summary: "Create .mdocs metadata and index Markdown documents.",
|
|
@@ -11249,14 +11257,33 @@ var AGENT_COMMANDS = [
|
|
|
11249
11257
|
"mdocs checkpoint restore checkpoint_abc123 --json"
|
|
11250
11258
|
]
|
|
11251
11259
|
},
|
|
11260
|
+
{
|
|
11261
|
+
name: "bridge setup",
|
|
11262
|
+
summary: "Initialize, validate, request approval for, and start an agent filesystem bridge.",
|
|
11263
|
+
usage: "mdocs bridge setup --workspace <id> --root <path> --root-id <id> --url <base-url> --actor-name <agent-name> --request-token",
|
|
11264
|
+
output: "long-running",
|
|
11265
|
+
mutates: true,
|
|
11266
|
+
examples: [
|
|
11267
|
+
"mdocs bridge setup --workspace workspace_abc --root . --root-id agentfs-hermes --url https://magic.example.com --actor-name Hermes --request-token"
|
|
11268
|
+
],
|
|
11269
|
+
notes: [
|
|
11270
|
+
"Runs init and doctor before starting the bridge.",
|
|
11271
|
+
"--request-token opens a human approval URL and avoids pasting bridge secrets into an agent session.",
|
|
11272
|
+
"--token (or MDOCS_BRIDGE_TOKEN) still works when a scoped bridge token is already available."
|
|
11273
|
+
]
|
|
11274
|
+
},
|
|
11252
11275
|
{
|
|
11253
11276
|
name: "bridge",
|
|
11254
11277
|
summary: "Sync a local Markdown root with a Magic workspace over WebSocket (long-running).",
|
|
11255
|
-
usage: "mdocs bridge --workspace <id> --root <path> --url <base-url>",
|
|
11278
|
+
usage: "mdocs bridge --workspace <id> --root <path> --url <base-url> --request-token",
|
|
11256
11279
|
output: "long-running",
|
|
11257
11280
|
mutates: true,
|
|
11258
|
-
examples: ["mdocs bridge --workspace workspace_abc --root . --root-id root_abc --url https://magic.example.com"],
|
|
11259
|
-
notes: [
|
|
11281
|
+
examples: ["mdocs bridge --workspace workspace_abc --root . --root-id root_abc --url https://magic.example.com --request-token"],
|
|
11282
|
+
notes: [
|
|
11283
|
+
"--request-token opens a human approval URL and avoids pasting bridge secrets into an agent session.",
|
|
11284
|
+
"--token (or MDOCS_BRIDGE_TOKEN) still works when a scoped bridge token is already available.",
|
|
11285
|
+
"--url (or MDOCS_BASE_URL) is required; the bridge never assumes a localhost server."
|
|
11286
|
+
]
|
|
11260
11287
|
},
|
|
11261
11288
|
{
|
|
11262
11289
|
name: "serve-mcp",
|
|
@@ -11296,14 +11323,10 @@ Magic Markdown keeps Markdown files clean and stores collaboration metadata in .
|
|
|
11296
11323
|
|
|
11297
11324
|
## Install / Bootstrap
|
|
11298
11325
|
|
|
11299
|
-
The only official package is \`@magic-markdown/cli\` (bins \`mdocs\` and \`magic-markdown\`). A different, unrelated package is published as \`@mdocs/cli\` \u2014 do not install it.
|
|
11326
|
+
The only official package is \`@magic-markdown/cli\` (bins \`mdocs\` and \`magic-markdown\`). A different, unrelated package is published as \`@mdocs/cli\` \u2014 do not install it. Agent sessions should bootstrap through the latest official package so they do not reuse a stale global \`mdocs\`:
|
|
11300
11327
|
|
|
11301
11328
|
\`\`\`bash
|
|
11302
|
-
|
|
11303
|
-
if ! command -v mdocs >/dev/null 2>&1 || ! mdocs version --json 2>/dev/null | grep -qF '"name": "@magic-markdown/cli"'; then
|
|
11304
|
-
MDOCS="npx --yes --package=@magic-markdown/cli@latest mdocs"
|
|
11305
|
-
fi
|
|
11306
|
-
$MDOCS version --json
|
|
11329
|
+
npx --yes --package=@magic-markdown/cli@latest mdocs version --json
|
|
11307
11330
|
\`\`\`
|
|
11308
11331
|
|
|
11309
11332
|
Run \`mdocs help <command>\` (or \`mdocs <command> --help\`) for usage, examples, and notes on any command, or \`mdocs agent commands --json\` for the full machine-readable contract.
|
|
@@ -13363,10 +13386,38 @@ function optionalFolderTarget(value) {
|
|
|
13363
13386
|
// src/bridge.ts
|
|
13364
13387
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
13365
13388
|
import { basename as basename2, resolve as resolve4 } from "node:path";
|
|
13389
|
+
function bridgeSetupIdentity(input) {
|
|
13390
|
+
const actorName = input.actorName?.trim() || "Agent";
|
|
13391
|
+
return {
|
|
13392
|
+
actorId: input.actorId?.trim() || `agent:${agentSlug(actorName)}`,
|
|
13393
|
+
actorName,
|
|
13394
|
+
sourceName: input.sourceName?.trim() || agentFilesystemDisplayName(actorName)
|
|
13395
|
+
};
|
|
13396
|
+
}
|
|
13397
|
+
async function runBridgeSetup(options) {
|
|
13398
|
+
const root = resolve4(options.root);
|
|
13399
|
+
const identity = bridgeSetupIdentity(options);
|
|
13400
|
+
const io = new NodeWorkspaceIO(root);
|
|
13401
|
+
const manifest = await indexWorkspace(io);
|
|
13402
|
+
process.stdout.write(`mdocs bridge setup indexed ${manifest.docs.length} Markdown document${manifest.docs.length === 1 ? "" : "s"} in ${root}
|
|
13403
|
+
`);
|
|
13404
|
+
const doctor = await runDoctor(io, root);
|
|
13405
|
+
process.stdout.write(`mdocs doctor ${doctor.ok ? "ok" : "has warnings"}: ${doctor.checks.map((check) => check.details).join(" ")}
|
|
13406
|
+
`);
|
|
13407
|
+
await runBridge({
|
|
13408
|
+
...options,
|
|
13409
|
+
root,
|
|
13410
|
+
actorId: identity.actorId,
|
|
13411
|
+
actorName: identity.actorName,
|
|
13412
|
+
sourceName: identity.sourceName,
|
|
13413
|
+
requestToken: options.requestToken || !options.token
|
|
13414
|
+
});
|
|
13415
|
+
}
|
|
13366
13416
|
async function runBridge(options) {
|
|
13367
13417
|
const root = resolve4(options.root);
|
|
13368
13418
|
const localManifestSource = await readLocalSource(root);
|
|
13369
13419
|
const rootId = options.rootId ?? options.sourceId ?? localManifestSource?.sourceId ?? rootIdForPath(root);
|
|
13420
|
+
const token = options.token ?? (options.requestToken ? await requestBridgeToken(options, rootId) : void 0);
|
|
13370
13421
|
const replicaId = `replica_${createHash2("sha256").update(`${root}:${options.actorId}`).digest("hex").slice(0, 12)}`;
|
|
13371
13422
|
const replicaKind = actorKindForBridge(options.actorId) === "agent" ? "agent_runtime" : "local";
|
|
13372
13423
|
const mapping = createSourceMapping({
|
|
@@ -13389,7 +13440,7 @@ async function runBridge(options) {
|
|
|
13389
13440
|
const claimMode = Boolean(options.claimToken || localManifestSource?.canonicalHead);
|
|
13390
13441
|
let lastAppliedHead = localManifestSource?.canonicalHead;
|
|
13391
13442
|
let socket;
|
|
13392
|
-
const registeredRoot =
|
|
13443
|
+
const registeredRoot = token ? await fetchScopedRoot({ ...options, token }, rootId) : await registerRoot(options, root, rootId, mapping);
|
|
13393
13444
|
lastAppliedHead = lastAppliedHead ?? registeredRoot?.canonical.head;
|
|
13394
13445
|
await writeSourceState(lastAppliedHead);
|
|
13395
13446
|
connect();
|
|
@@ -13412,7 +13463,7 @@ async function runBridge(options) {
|
|
|
13412
13463
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
13413
13464
|
url.searchParams.set("actorId", options.actorId);
|
|
13414
13465
|
url.searchParams.set("sourceId", sourceId);
|
|
13415
|
-
if (
|
|
13466
|
+
if (token) url.searchParams.set("token", token);
|
|
13416
13467
|
socket = new WebSocket(url);
|
|
13417
13468
|
socket.addEventListener("open", () => {
|
|
13418
13469
|
process.stdout.write(`mdocs bridge connected ${root} -> ${options.workspaceId}/${rootId}
|
|
@@ -13574,7 +13625,7 @@ async function runBridge(options) {
|
|
|
13574
13625
|
`/api/workspaces/${encodeURIComponent(options.workspaceId)}/roots/${encodeURIComponent(rootId)}/documents/${encodeURIComponent(docId)}`,
|
|
13575
13626
|
options.baseUrl
|
|
13576
13627
|
),
|
|
13577
|
-
{ headers: authHeaders(
|
|
13628
|
+
{ headers: authHeaders(token) }
|
|
13578
13629
|
);
|
|
13579
13630
|
if (!response.ok) return void 0;
|
|
13580
13631
|
const document = await response.json();
|
|
@@ -13633,6 +13684,60 @@ async function runBridge(options) {
|
|
|
13633
13684
|
});
|
|
13634
13685
|
}
|
|
13635
13686
|
}
|
|
13687
|
+
async function requestBridgeToken(options, rootId) {
|
|
13688
|
+
const response = await fetch(new URL("/api/bridge-requests", options.baseUrl), {
|
|
13689
|
+
method: "POST",
|
|
13690
|
+
headers: { "Content-Type": "application/json" },
|
|
13691
|
+
body: JSON.stringify({
|
|
13692
|
+
workspaceId: options.workspaceId,
|
|
13693
|
+
rootId,
|
|
13694
|
+
actorId: options.actorId,
|
|
13695
|
+
actorName: options.actorName,
|
|
13696
|
+
sourceName: options.sourceName,
|
|
13697
|
+
role: "edit"
|
|
13698
|
+
})
|
|
13699
|
+
}).catch((error) => {
|
|
13700
|
+
throw new Error(`Failed to create bridge approval request: ${error instanceof Error ? error.message : String(error)}`);
|
|
13701
|
+
});
|
|
13702
|
+
if (!response.ok) {
|
|
13703
|
+
throw new Error(`Failed to create bridge approval request: ${response.status} ${await response.text()}`);
|
|
13704
|
+
}
|
|
13705
|
+
const created = await response.json();
|
|
13706
|
+
if (!created.requestId || !created.pollToken || !created.approveUrl) {
|
|
13707
|
+
throw new Error("Bridge approval request response was missing requestId, pollToken, or approveUrl.");
|
|
13708
|
+
}
|
|
13709
|
+
process.stdout.write(`mdocs bridge approval needed: ${created.approveUrl}
|
|
13710
|
+
`);
|
|
13711
|
+
if (created.expiresAt) process.stdout.write(`Waiting for approval until ${created.expiresAt}.
|
|
13712
|
+
`);
|
|
13713
|
+
const deadline = Date.now() + (options.pairingTimeoutMs ?? 1e3 * 60 * 15);
|
|
13714
|
+
while (Date.now() < deadline) {
|
|
13715
|
+
await delay(2e3);
|
|
13716
|
+
const pollResponse = await fetch(new URL(`/api/bridge-requests/${encodeURIComponent(created.requestId)}/token`, options.baseUrl), {
|
|
13717
|
+
headers: { Authorization: `Bearer ${created.pollToken}` }
|
|
13718
|
+
}).catch((error) => {
|
|
13719
|
+
throw new Error(`Bridge approval polling failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
13720
|
+
});
|
|
13721
|
+
const payload = await pollResponse.json().catch(() => ({}));
|
|
13722
|
+
if (pollResponse.status === 202) continue;
|
|
13723
|
+
if (pollResponse.ok && payload.token) {
|
|
13724
|
+
process.stdout.write(`mdocs bridge approved${payload.expiresAt ? `; token expires ${payload.expiresAt}` : ""}.
|
|
13725
|
+
`);
|
|
13726
|
+
return payload.token;
|
|
13727
|
+
}
|
|
13728
|
+
if (pollResponse.status === 409 && payload.status === "rejected") {
|
|
13729
|
+
throw new Error("Bridge approval request was rejected.");
|
|
13730
|
+
}
|
|
13731
|
+
if (pollResponse.status === 410 || payload.status === "expired") {
|
|
13732
|
+
throw new Error("Bridge approval request expired before it was approved.");
|
|
13733
|
+
}
|
|
13734
|
+
throw new Error(`Bridge approval polling failed with status ${pollResponse.status}.`);
|
|
13735
|
+
}
|
|
13736
|
+
throw new Error("Bridge approval timed out before a token was issued.");
|
|
13737
|
+
}
|
|
13738
|
+
function delay(ms) {
|
|
13739
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
13740
|
+
}
|
|
13636
13741
|
function authHeaders(token) {
|
|
13637
13742
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
13638
13743
|
}
|
|
@@ -13717,6 +13822,15 @@ function agentNameFromActorId(actorId, rootName) {
|
|
|
13717
13822
|
if (nameFromId) return nameFromId;
|
|
13718
13823
|
return "Agent";
|
|
13719
13824
|
}
|
|
13825
|
+
function agentFilesystemDisplayName(agentName) {
|
|
13826
|
+
const trimmed = agentName.trim();
|
|
13827
|
+
if (!trimmed) return "Agent Filesystem";
|
|
13828
|
+
return /\bagent$/i.test(trimmed) ? `${trimmed} Filesystem` : `${trimmed} Agent Filesystem`;
|
|
13829
|
+
}
|
|
13830
|
+
function agentSlug(agentName) {
|
|
13831
|
+
const slug = agentName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
13832
|
+
return slug || "agent";
|
|
13833
|
+
}
|
|
13720
13834
|
function mergePermissions(permissions, permission) {
|
|
13721
13835
|
return [...permissions.filter((candidate) => candidate.actorId !== permission.actorId), permission];
|
|
13722
13836
|
}
|
|
@@ -13915,8 +14029,12 @@ async function main() {
|
|
|
13915
14029
|
await runAgentCommand(io, cwd, subcommand, parsed);
|
|
13916
14030
|
return;
|
|
13917
14031
|
}
|
|
14032
|
+
case "agent-guide": {
|
|
14033
|
+
print(parsed.flags.json ? getAgentGuidePayload() : getAgentSkillMarkdown(), parsed.flags);
|
|
14034
|
+
return;
|
|
14035
|
+
}
|
|
13918
14036
|
case "bridge": {
|
|
13919
|
-
|
|
14037
|
+
const options = {
|
|
13920
14038
|
root: resolve5(String(parsed.flags.root ?? cwd)),
|
|
13921
14039
|
workspaceId: String(parsed.flags.workspace ?? "workspace_demo"),
|
|
13922
14040
|
rootId: typeof parsed.flags["root-id"] === "string" ? parsed.flags["root-id"] : void 0,
|
|
@@ -13925,12 +14043,16 @@ async function main() {
|
|
|
13925
14043
|
canonicalPrefix: typeof parsed.flags["canonical-prefix"] === "string" ? parsed.flags["canonical-prefix"] : void 0,
|
|
13926
14044
|
replicaPrefix: typeof parsed.flags["replica-prefix"] === "string" ? parsed.flags["replica-prefix"] : void 0,
|
|
13927
14045
|
claimToken: typeof parsed.flags.claim === "string" ? parsed.flags.claim : void 0,
|
|
13928
|
-
actorId:
|
|
14046
|
+
actorId: typeof parsed.flags.actor === "string" ? parsed.flags.actor : void 0,
|
|
13929
14047
|
actorName: typeof parsed.flags["actor-name"] === "string" ? parsed.flags["actor-name"] : void 0,
|
|
13930
14048
|
baseUrl: bridgeBaseUrl(parsed.flags),
|
|
13931
14049
|
intervalMs: Number(parsed.flags.interval ?? 1e3),
|
|
13932
|
-
token: typeof parsed.flags.token === "string" ? parsed.flags.token : process.env.MDOCS_BRIDGE_TOKEN || void 0
|
|
13933
|
-
|
|
14050
|
+
token: typeof parsed.flags.token === "string" ? parsed.flags.token : process.env.MDOCS_BRIDGE_TOKEN || void 0,
|
|
14051
|
+
requestToken: Boolean(parsed.flags["request-token"] || parsed.flags.pair),
|
|
14052
|
+
pairingTimeoutMs: typeof parsed.flags["pairing-timeout-ms"] === "string" ? Number(parsed.flags["pairing-timeout-ms"]) : void 0
|
|
14053
|
+
};
|
|
14054
|
+
if (subcommand === "setup") await runBridgeSetup(options);
|
|
14055
|
+
else await runBridge({ ...options, actorId: options.actorId ?? "actor_local" });
|
|
13934
14056
|
return;
|
|
13935
14057
|
}
|
|
13936
14058
|
case "doctor": {
|
|
@@ -14086,6 +14208,7 @@ function help() {
|
|
|
14086
14208
|
|
|
14087
14209
|
Commands:
|
|
14088
14210
|
agent guide Print agent SKILL.md workflow guidance
|
|
14211
|
+
agent-guide Alias for agent guide
|
|
14089
14212
|
agent commands --json Print machine-readable command metadata
|
|
14090
14213
|
init [path] Initialize .mdocs and index Markdown files
|
|
14091
14214
|
map --json Print workspace map
|
|
@@ -14118,10 +14241,15 @@ Commands:
|
|
|
14118
14241
|
remote events|history|restore Poll events, list commits, restore snapshots
|
|
14119
14242
|
remote library|create-folder|update-folder|move-root|invite-folder
|
|
14120
14243
|
Organize the joined project library
|
|
14244
|
+
bridge setup --workspace <id> --root . --url <base-url> --request-token
|
|
14245
|
+
Initialize, validate, request approval,
|
|
14246
|
+
and start an agent filesystem bridge
|
|
14247
|
+
bridge --workspace <id> --root . --url <base-url> --request-token
|
|
14248
|
+
Request human approval, then sync an
|
|
14249
|
+
approved local root with the workspace
|
|
14121
14250
|
bridge --workspace <id> --root . --url <base-url> --token <bridge-token>
|
|
14122
14251
|
Sync an approved local root with the workspace
|
|
14123
|
-
(
|
|
14124
|
-
dialog, or MDOCS_BRIDGE_TOKEN)
|
|
14252
|
+
(or set MDOCS_BRIDGE_TOKEN)
|
|
14125
14253
|
bridge --claim <token> --root . --canonical-prefix prompts --replica-prefix packages/agent/prompts
|
|
14126
14254
|
Claim an existing repo path as a Magic-canonical source mapping
|
|
14127
14255
|
doctor --json Validate sidecar mappings
|
package/package.json
CHANGED