@p11-core/cli 0.0.16 → 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/index.js CHANGED
@@ -3699,6 +3699,22 @@ Environment:
3699
3699
  );
3700
3700
  });
3701
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);
3702
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) => {
3703
3719
  await runCliAction(
3704
3720
  "delete",
@@ -3791,6 +3807,16 @@ function normalizeCommentsOptions(options) {
3791
3807
  apiUrl: optionString(options.apiUrl, "--api-url requires a URL.")
3792
3808
  };
3793
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
+ }
3794
3820
  function normalizeDeleteOptions(options) {
3795
3821
  return {
3796
3822
  json: options.json,
@@ -3938,6 +3964,57 @@ async function comments(target, options) {
3938
3964
  printComments(data);
3939
3965
  }
3940
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
+ }
3941
4018
  async function deleteDocument(target, options) {
3942
4019
  if (!target) {
3943
4020
  throw new Error("Usage: p11 delete <editUrl|editId> [--json] [--api-url URL]");
@@ -4270,10 +4347,10 @@ function printComments(data) {
4270
4347
  console.log(`[${comment.createdAt}]${target}${resolved} ${comment.name}`);
4271
4348
  if (comment.quote) console.log(`> ${comment.quote}`);
4272
4349
  console.log(comment.body);
4273
- for (const reply of comment.replies ?? []) {
4274
- const replyResolved = reply.resolvedAt ? " [resolved]" : "";
4275
- console.log(` [${reply.createdAt}] reply${replyResolved} ${reply.name}`);
4276
- 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}`);
4277
4354
  }
4278
4355
  console.log("");
4279
4356
  }
@@ -4642,6 +4719,14 @@ function commentsApiUrlForTarget(value, options = {}) {
4642
4719
  if (version !== void 0) url.searchParams.set("v", String(version));
4643
4720
  return url;
4644
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
+ }
4645
4730
  function deleteApiUrlForTarget(value, options = {}) {
4646
4731
  const target = parseEditTarget(value);
4647
4732
  const apiUrl = normalizeApiUrl(String(options.apiUrl ?? target.apiUrl ?? defaultApiUrl));
@@ -4682,6 +4767,9 @@ function validateAccessTarget(target, expected) {
4682
4767
  const prefix = target.kind === "read" ? "read" : "edit";
4683
4768
  if (!new RegExp(`^${prefix}_[A-Za-z0-9_-]{6,80}$`).test(target.id)) throw new Error(`Invalid ${expected}.`);
4684
4769
  }
4770
+ function validateCommentId(commentId) {
4771
+ if (!/^cmt_[A-Za-z0-9_-]{8,64}$/.test(commentId)) throw new Error("Invalid comment id.");
4772
+ }
4685
4773
  function normalizeApiUrl(value) {
4686
4774
  return value.endsWith("/") ? value.slice(0, -1) : value;
4687
4775
  }
@@ -4769,6 +4857,7 @@ export {
4769
4857
  parseReadId,
4770
4858
  publishOutputLines,
4771
4859
  readHistoryEntries,
4860
+ replyApiUrlForTarget,
4772
4861
  resolveBuildParentDir,
4773
4862
  runCli,
4774
4863
  validatePageSource
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@p11-core/cli",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "bin": {