@p11-core/cli 0.0.15 → 0.0.17

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/dist/bin.js ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __p11CreateRequire } from "node:module"; const require = __p11CreateRequire(import.meta.url);
3
+
4
+ // src/bin.ts
5
+ var minimumNodeVersion = "22.12.0";
6
+ if (!isSupportedNodeVersion(process.versions.node)) {
7
+ console.error(`p11 requires Node.js >=${minimumNodeVersion}.`);
8
+ console.error(`Current Node.js: v${process.versions.node}.`);
9
+ console.error("Upgrade Node.js, then run p11 again.");
10
+ process.exit(1);
11
+ }
12
+ try {
13
+ const cliEntryUrl = new URL("./index.js", import.meta.url).href;
14
+ const { runCli } = await import(cliEntryUrl);
15
+ await runCli();
16
+ } catch (error) {
17
+ console.error(error instanceof Error ? error.message : String(error));
18
+ process.exit(1);
19
+ }
20
+ function isSupportedNodeVersion(version) {
21
+ const [major = 0, minor = 0, patch = 0] = version.split(".").map((part) => Number(part));
22
+ if (major > 22) return true;
23
+ if (major < 22) return false;
24
+ if (minor > 12) return true;
25
+ if (minor < 12) return false;
26
+ return patch >= 0;
27
+ }
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  import { createRequire as __p11CreateRequire } from "node:module"; const require = __p11CreateRequire(import.meta.url);
3
2
  var __create = Object.create;
4
3
  var __defProp = Object.defineProperty;
@@ -3619,7 +3618,7 @@ var P11_CODE_SOURCE_LINE_ATTRIBUTE = "data-p11-code-source-line";
3619
3618
  var P11_SOURCE_FILE_ATTRIBUTE = "data-p11-source-file";
3620
3619
  var P11_SOURCE_LINE_ATTRIBUTE = "data-p11-source-line";
3621
3620
  if (isCliEntrypoint()) {
3622
- main().catch((error) => {
3621
+ runCli().catch((error) => {
3623
3622
  console.error(error instanceof Error ? error.message : String(error));
3624
3623
  process.exit(1);
3625
3624
  });
@@ -3633,7 +3632,7 @@ function isCliEntrypoint() {
3633
3632
  return path.resolve(process.argv[1]) === modulePath;
3634
3633
  }
3635
3634
  }
3636
- async function main() {
3635
+ async function runCli() {
3637
3636
  const program2 = createCliProgram();
3638
3637
  if (process.argv.length <= 2) {
3639
3638
  program2.outputHelp();
@@ -3700,6 +3699,22 @@ Environment:
3700
3699
  );
3701
3700
  });
3702
3701
  allowLegacyParserBehavior(commentsCommand);
3702
+ const replyCommand = program2.command("reply").description("Reply to a comment on a read URL or id.").argument("[readUrl|readId]").argument("[commentId]").option("--name [name]", "Reply author name. Defaults to AI Agent.").option("--body [text]", "Plain-text reply body. Newlines are preserved.").option("--body-file [file]", "Read plain-text reply body from a file, or '-' for stdin.").option("--json", "Print the API response as JSON.").option("--version [n]", "Reply on a specific page version.").option("--api-url [url]", "Override the p11 API URL.").action(async (target, commentId, options2) => {
3703
+ await runCliAction(
3704
+ "reply",
3705
+ {
3706
+ has_target: Boolean(target),
3707
+ has_comment_id: Boolean(commentId),
3708
+ json: Boolean(options2.json),
3709
+ body: typeof options2.body === "string",
3710
+ body_file: typeof options2.bodyFile === "string",
3711
+ version: typeof options2.version === "string",
3712
+ api_url_override: typeof options2.apiUrl === "string"
3713
+ },
3714
+ () => reply(target, commentId, normalizeReplyOptions(options2))
3715
+ );
3716
+ });
3717
+ allowLegacyParserBehavior(replyCommand);
3703
3718
  const deleteCommand = program2.command("delete").description("Delete a document and all of its versions and comments.").argument("[editUrl|editId]").option("--json", "Print the API response as JSON.").option("--api-url [url]", "Override the p11 API URL.").action(async (target, options2) => {
3704
3719
  await runCliAction(
3705
3720
  "delete",
@@ -3792,6 +3807,16 @@ function normalizeCommentsOptions(options) {
3792
3807
  apiUrl: optionString(options.apiUrl, "--api-url requires a URL.")
3793
3808
  };
3794
3809
  }
3810
+ function normalizeReplyOptions(options) {
3811
+ return {
3812
+ json: options.json,
3813
+ name: optionString(options.name, "--name requires a value."),
3814
+ body: optionString(options.body, "--body requires reply text."),
3815
+ bodyFile: optionString(options.bodyFile, "--body-file requires a file path or '-'."),
3816
+ version: optionString(options.version, "--version requires a positive integer."),
3817
+ apiUrl: optionString(options.apiUrl, "--api-url requires a URL.")
3818
+ };
3819
+ }
3795
3820
  function normalizeDeleteOptions(options) {
3796
3821
  return {
3797
3822
  json: options.json,
@@ -3939,6 +3964,57 @@ async function comments(target, options) {
3939
3964
  printComments(data);
3940
3965
  }
3941
3966
  }
3967
+ async function reply(target, commentId, options) {
3968
+ if (!target || !commentId) {
3969
+ throw new Error("Usage: p11 reply <readUrl|readId> <commentId> --body TEXT [--name NAME] [--json] [--version N]");
3970
+ }
3971
+ validateCommentId(commentId);
3972
+ const body = await replyBody(options);
3973
+ if (!body.trim()) throw new Error("Reply body is required. Use --body TEXT or --body-file FILE.");
3974
+ const version = options.version === void 0 ? void 0 : parseVersionFlag(String(options.version));
3975
+ const url = replyApiUrlForTarget(target, {
3976
+ apiUrl: options.apiUrl,
3977
+ version
3978
+ });
3979
+ const response = await fetchForOperation("Reply", url, {
3980
+ method: "POST",
3981
+ headers: {
3982
+ "content-type": "application/json"
3983
+ },
3984
+ body: JSON.stringify({
3985
+ name: options.name ?? "AI Agent",
3986
+ body,
3987
+ parentId: commentId
3988
+ })
3989
+ });
3990
+ const text = await response.text();
3991
+ if (!response.ok) {
3992
+ throw new Error(`Reply failed (${response.status}): ${responseErrorMessage(text)}`);
3993
+ }
3994
+ const data = JSON.parse(text);
3995
+ if (options.json) {
3996
+ console.log(JSON.stringify(data, null, 2));
3997
+ return;
3998
+ }
3999
+ console.log(`Replied ${data.comment?.id ?? "comment"}`);
4000
+ if (data.comment?.parentId) console.log(`parentId: ${data.comment.parentId}`);
4001
+ if (data.comment?.createdAt) console.log(`createdAt: ${data.comment.createdAt}`);
4002
+ }
4003
+ async function replyBody(options) {
4004
+ if (options.body !== void 0 && options.bodyFile !== void 0) {
4005
+ throw new Error("Use either --body or --body-file, not both.");
4006
+ }
4007
+ if (options.body !== void 0) return options.body;
4008
+ if (options.bodyFile === "-") return readStdin();
4009
+ if (options.bodyFile !== void 0) return readFile(path.resolve(options.bodyFile), "utf8");
4010
+ return "";
4011
+ }
4012
+ async function readStdin() {
4013
+ let body = "";
4014
+ process.stdin.setEncoding("utf8");
4015
+ for await (const chunk of process.stdin) body += chunk;
4016
+ return body;
4017
+ }
3942
4018
  async function deleteDocument(target, options) {
3943
4019
  if (!target) {
3944
4020
  throw new Error("Usage: p11 delete <editUrl|editId> [--json] [--api-url URL]");
@@ -4271,10 +4347,10 @@ function printComments(data) {
4271
4347
  console.log(`[${comment.createdAt}]${target}${resolved} ${comment.name}`);
4272
4348
  if (comment.quote) console.log(`> ${comment.quote}`);
4273
4349
  console.log(comment.body);
4274
- for (const reply of comment.replies ?? []) {
4275
- const replyResolved = reply.resolvedAt ? " [resolved]" : "";
4276
- console.log(` [${reply.createdAt}] reply${replyResolved} ${reply.name}`);
4277
- console.log(` ${reply.body}`);
4350
+ for (const reply2 of comment.replies ?? []) {
4351
+ const replyResolved = reply2.resolvedAt ? " [resolved]" : "";
4352
+ console.log(` [${reply2.createdAt}] reply${replyResolved} ${reply2.name}`);
4353
+ console.log(` ${reply2.body}`);
4278
4354
  }
4279
4355
  console.log("");
4280
4356
  }
@@ -4643,6 +4719,14 @@ function commentsApiUrlForTarget(value, options = {}) {
4643
4719
  if (version !== void 0) url.searchParams.set("v", String(version));
4644
4720
  return url;
4645
4721
  }
4722
+ function replyApiUrlForTarget(value, options = {}) {
4723
+ const target = parseAccessTarget(value, ["read"], "a read URL with /r/<readId> or a read id", { parseVersion: true });
4724
+ const apiUrl = normalizeApiUrl(String(options.apiUrl ?? target.apiUrl ?? defaultApiUrl));
4725
+ const url = new URL(`/api/read/${encodeURIComponent(target.id)}/comments`, apiUrl);
4726
+ const version = options.version ?? target.version;
4727
+ if (version !== void 0) url.searchParams.set("v", String(version));
4728
+ return url;
4729
+ }
4646
4730
  function deleteApiUrlForTarget(value, options = {}) {
4647
4731
  const target = parseEditTarget(value);
4648
4732
  const apiUrl = normalizeApiUrl(String(options.apiUrl ?? target.apiUrl ?? defaultApiUrl));
@@ -4683,6 +4767,9 @@ function validateAccessTarget(target, expected) {
4683
4767
  const prefix = target.kind === "read" ? "read" : "edit";
4684
4768
  if (!new RegExp(`^${prefix}_[A-Za-z0-9_-]{6,80}$`).test(target.id)) throw new Error(`Invalid ${expected}.`);
4685
4769
  }
4770
+ function validateCommentId(commentId) {
4771
+ if (!/^cmt_[A-Za-z0-9_-]{8,64}$/.test(commentId)) throw new Error("Invalid comment id.");
4772
+ }
4686
4773
  function normalizeApiUrl(value) {
4687
4774
  return value.endsWith("/") ? value.slice(0, -1) : value;
4688
4775
  }
@@ -4770,6 +4857,8 @@ export {
4770
4857
  parseReadId,
4771
4858
  publishOutputLines,
4772
4859
  readHistoryEntries,
4860
+ replyApiUrlForTarget,
4773
4861
  resolveBuildParentDir,
4862
+ runCli,
4774
4863
  validatePageSource
4775
4864
  };
package/docs/index.md CHANGED
@@ -11,10 +11,13 @@ p11 share <page.tsx>
11
11
  p11 share <page.tsx> --edit-url <editUrl>
12
12
  p11 history
13
13
  p11 comments <readUrl|editUrl|readId|editId>
14
+ p11 reply <readUrl|readId> <commentId> --body "Reply text"
14
15
  p11 delete <editUrl|editId>
15
16
  ```
16
17
 
17
- For `share`, `history`, `comments`, and `delete`, add `--json` when scripting or when exact structured fields are needed.
18
+ Replies are plain text; newlines are preserved. Markdown and HTML are not rendered.
19
+
20
+ For `share`, `history`, `comments`, `reply`, and `delete`, add `--json` when scripting or when exact structured fields are needed.
18
21
 
19
22
  ## Docs Topics
20
23
 
@@ -32,4 +35,4 @@ p11 example all
32
35
  p11 example all --output ./all.tsx
33
36
  ```
34
37
 
35
- Use `p11 share --help`, `p11 comments --help`, `p11 delete --help`, and `p11 history --help` for command-specific flags.
38
+ Use `p11 share --help`, `p11 comments --help`, `p11 reply --help`, `p11 delete --help`, and `p11 history --help` for command-specific flags.
package/docs/sharing.md CHANGED
@@ -26,6 +26,16 @@ p11 comments <readUrl|editUrl|readId|editId> --version 1
26
26
  p11 comments <readUrl|editUrl|readId|editId> --output comments.json
27
27
  ```
28
28
 
29
+ Reply to a top-level comment:
30
+
31
+ ```bash
32
+ p11 reply <readUrl|readId> <commentId> --body "Reply text"
33
+ p11 reply <readUrl|readId> <commentId> --body-file reply.txt
34
+ p11 reply <readUrl|readId> <commentId> --body-file - --json
35
+ ```
36
+
37
+ Replies are plain text; newlines are preserved. Markdown and HTML are not rendered.
38
+
29
39
  Delete a shared document and all of its versions, comments, and replies:
30
40
 
31
41
  ```bash
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@p11-core/cli",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "bin": {
7
- "p11": "dist/index.js"
7
+ "p11": "dist/bin.js"
8
+ },
9
+ "engines": {
10
+ "node": ">=22.12.0"
8
11
  },
9
12
  "files": [
10
13
  "dist",