@parall/cli 1.16.0 → 1.18.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.
@@ -1,19 +1,17 @@
1
1
  import { Command } from 'commander';
2
2
  /**
3
- * `parall no-reply` — explicit "I am not replying this turn" signal.
3
+ * `parall no-reply` — explicit "I am not replying this turn" audit signal.
4
4
  *
5
- * Runs entirely client-side: no API call, no server state. The agent bridge
6
- * (agent-core) sniffs the invocation in the Bash tool_call stream and
7
- * drops all subsequent text projections for this dispatch, so the agent
8
- * can silence a turn without leaking a polite "No response needed" message
9
- * into the chat.
5
+ * Runs entirely client-side: no API call, no server state. Under the unified
6
+ * runtime output contract (see `docs/engineering-design/agent-dm-loop-prevention.md`
7
+ * § Layer 0), plain text is never auto-projected as a chat message — agents
8
+ * produce visible messages only by explicitly invoking `parall messages send`
9
+ * / `dm`. So silence is the default when the agent simply does nothing.
10
10
  *
11
- * Usage pattern in the agent's workspace CLAUDE.md:
12
- *
13
- * When a dispatched event has `[Hint: no_reply]` or you decide the turn
14
- * does not need a visible reply, run this command BEFORE emitting any
15
- * plain text — anything you say after it is dropped, but anything before
16
- * is already projected as a chat message.
11
+ * What this command adds: a deterministic, auditable marker in the agent's
12
+ * session tool-call log saying "this turn was intentionally silent, here's
13
+ * the optional reason," which helps operators distinguish deliberate silence
14
+ * from a crashed or timed-out dispatch.
17
15
  */
18
16
  export declare function registerNoReplyCommands(program: Command): void;
19
17
  //# sourceMappingURL=no-reply.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-reply.d.ts","sourceRoot":"","sources":["../../src/commands/no-reply.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QAevD"}
1
+ {"version":3,"file":"no-reply.d.ts","sourceRoot":"","sources":["../../src/commands/no-reply.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QAevD"}
@@ -1,28 +1,26 @@
1
1
  /**
2
- * `parall no-reply` — explicit "I am not replying this turn" signal.
2
+ * `parall no-reply` — explicit "I am not replying this turn" audit signal.
3
3
  *
4
- * Runs entirely client-side: no API call, no server state. The agent bridge
5
- * (agent-core) sniffs the invocation in the Bash tool_call stream and
6
- * drops all subsequent text projections for this dispatch, so the agent
7
- * can silence a turn without leaking a polite "No response needed" message
8
- * into the chat.
4
+ * Runs entirely client-side: no API call, no server state. Under the unified
5
+ * runtime output contract (see `docs/engineering-design/agent-dm-loop-prevention.md`
6
+ * § Layer 0), plain text is never auto-projected as a chat message — agents
7
+ * produce visible messages only by explicitly invoking `parall messages send`
8
+ * / `dm`. So silence is the default when the agent simply does nothing.
9
9
  *
10
- * Usage pattern in the agent's workspace CLAUDE.md:
11
- *
12
- * When a dispatched event has `[Hint: no_reply]` or you decide the turn
13
- * does not need a visible reply, run this command BEFORE emitting any
14
- * plain text — anything you say after it is dropped, but anything before
15
- * is already projected as a chat message.
10
+ * What this command adds: a deterministic, auditable marker in the agent's
11
+ * session tool-call log saying "this turn was intentionally silent, here's
12
+ * the optional reason," which helps operators distinguish deliberate silence
13
+ * from a crashed or timed-out dispatch.
16
14
  */
17
15
  export function registerNoReplyCommands(program) {
18
16
  program
19
17
  .command('no-reply')
20
- .description('Signal to the bridge that this dispatch turn should produce no visible reply. ' +
21
- 'Subsequent text output is dropped.')
18
+ .description('Record an explicit "no visible reply this turn" marker in the session log. ' +
19
+ 'Purely informational silence is already the default when no `messages send` runs.')
22
20
  .option('--reason <reason>', 'Optional human-readable reason, logged for debugging')
23
21
  .action((opts) => {
24
- // Print a deterministic marker so the bridge (and operators reading
25
- // tool_use logs) can confirm the suppression was requested.
22
+ // Print a deterministic marker so operators reading tool_use logs can
23
+ // confirm the agent deliberately declared silence this turn.
26
24
  const payload = { ack: 'no-reply' };
27
25
  if (opts.reason)
28
26
  payload.reason = opts.reason;
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/commands/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAgPpD"}
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/commands/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QA4RpD"}
@@ -165,7 +165,8 @@ export function registerTaskCommands(program) {
165
165
  cursor: opts.cursor,
166
166
  order: opts.order,
167
167
  });
168
- const result = await client.getTaskComments(orgId, stripPrllScheme(taskId), params);
168
+ const { COMMENT_TARGET } = await import('@parall/sdk');
169
+ const result = await client.getComments(orgId, { target_uri: COMMENT_TARGET.task(stripPrllScheme(taskId)), ...params });
169
170
  printJson(result);
170
171
  }
171
172
  catch (err) {
@@ -180,7 +181,8 @@ export function registerTaskCommands(program) {
180
181
  .action(async (taskId, opts) => {
181
182
  try {
182
183
  const { client, orgId } = resolveCredentials();
183
- const result = await client.createTaskComment(orgId, stripPrllScheme(taskId), { body: opts.body });
184
+ const { COMMENT_TARGET } = await import('@parall/sdk');
185
+ const result = await client.createComment(orgId, { target_uri: COMMENT_TARGET.task(stripPrllScheme(taskId)), body: opts.body });
184
186
  printJson(result);
185
187
  printRefHint(result.id);
186
188
  }
@@ -191,13 +193,12 @@ export function registerTaskCommands(program) {
191
193
  comments
192
194
  .command('update')
193
195
  .description('Update a task comment')
194
- .argument('<taskId>', 'Task ID')
195
196
  .argument('<commentId>', 'Comment ID')
196
197
  .requiredOption('--body <text>', 'New comment body')
197
- .action(async (taskId, commentId, opts) => {
198
+ .action(async (commentId, opts) => {
198
199
  try {
199
200
  const { client, orgId } = resolveCredentials();
200
- const result = await client.updateTaskComment(orgId, stripPrllScheme(taskId), stripPrllScheme(commentId), { body: opts.body });
201
+ const result = await client.updateComment(orgId, stripPrllScheme(commentId), { body: opts.body });
201
202
  printJson(result);
202
203
  }
203
204
  catch (err) {
@@ -207,12 +208,11 @@ export function registerTaskCommands(program) {
207
208
  comments
208
209
  .command('delete')
209
210
  .description('Delete a task comment')
210
- .argument('<taskId>', 'Task ID')
211
211
  .argument('<commentId>', 'Comment ID')
212
- .action(async (taskId, commentId) => {
212
+ .action(async (commentId) => {
213
213
  try {
214
214
  const { client, orgId } = resolveCredentials();
215
- await client.deleteTaskComment(orgId, stripPrllScheme(taskId), stripPrllScheme(commentId));
215
+ await client.deleteComment(orgId, stripPrllScheme(commentId));
216
216
  printJson({ ok: true });
217
217
  }
218
218
  catch (err) {
@@ -240,4 +240,47 @@ export function registerTaskCommands(program) {
240
240
  printError(err);
241
241
  }
242
242
  });
243
+ // ---- Watchers ----
244
+ tasks
245
+ .command('watch')
246
+ .description('Watch a task (subscribe to comment notifications)')
247
+ .argument('<taskId>', 'Task ID')
248
+ .action(async (taskId) => {
249
+ try {
250
+ const { client, orgId } = resolveCredentials();
251
+ await client.watchTask(orgId, stripPrllScheme(taskId));
252
+ printJson({ ok: true });
253
+ }
254
+ catch (err) {
255
+ printError(err);
256
+ }
257
+ });
258
+ tasks
259
+ .command('unwatch')
260
+ .description('Unwatch a task (stop receiving comment notifications)')
261
+ .argument('<taskId>', 'Task ID')
262
+ .action(async (taskId) => {
263
+ try {
264
+ const { client, orgId } = resolveCredentials();
265
+ await client.unwatchTask(orgId, stripPrllScheme(taskId));
266
+ printJson({ ok: true });
267
+ }
268
+ catch (err) {
269
+ printError(err);
270
+ }
271
+ });
272
+ tasks
273
+ .command('watchers')
274
+ .description('List watchers of a task')
275
+ .argument('<taskId>', 'Task ID')
276
+ .action(async (taskId) => {
277
+ try {
278
+ const { client, orgId } = resolveCredentials();
279
+ const result = await client.getTaskWatchers(orgId, stripPrllScheme(taskId));
280
+ printJson(result);
281
+ }
282
+ catch (err) {
283
+ printError(err);
284
+ }
285
+ });
243
286
  }
@@ -1 +1 @@
1
- {"version":3,"file":"wiki.d.ts","sourceRoot":"","sources":["../../src/commands/wiki.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAyqBpD"}
1
+ {"version":3,"file":"wiki.d.ts","sourceRoot":"","sources":["../../src/commands/wiki.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAwwBpD"}
@@ -630,6 +630,99 @@ export function registerWikiCommands(program) {
630
630
  printError(error);
631
631
  }
632
632
  });
633
+ // ---- Comments ----
634
+ const comment = wiki.command('comment').description('Manage wiki comments');
635
+ comment
636
+ .command('list')
637
+ .description('List comments on a wiki target')
638
+ .argument('[wiki]', 'Wiki ID or slug')
639
+ .requiredOption('--target-type <type>', 'Target type (changeset or page)')
640
+ .requiredOption('--target-id <id>', 'Target ID (changeset ID or page path)')
641
+ .option('--limit <n>', 'Maximum number of comments to return', '50')
642
+ .option('--cursor <cursor>', 'Pagination cursor')
643
+ .option('--order <order>', 'Sort order (asc or desc)')
644
+ .action(async (wikiRef, opts) => {
645
+ try {
646
+ const ctx = resolveCredentials();
647
+ const { resolveWikiRefOrDefault } = await import('../lib/wiki.js');
648
+ const { COMMENT_TARGET } = await import('@parall/sdk');
649
+ const w = await resolveWikiRefOrDefault(ctx, wikiRef);
650
+ if (opts.targetType !== 'changeset' && opts.targetType !== 'page') {
651
+ throw new Error('--target-type must be "changeset" or "page"');
652
+ }
653
+ const targetUri = opts.targetType === 'changeset'
654
+ ? COMMENT_TARGET.changeset(opts.targetId, w.id)
655
+ : COMMENT_TARGET.wikiPage(w.id, opts.targetId);
656
+ const result = await ctx.client.getComments(ctx.orgId, {
657
+ target_uri: targetUri,
658
+ limit: Number(opts.limit),
659
+ cursor: opts.cursor,
660
+ order: opts.order,
661
+ });
662
+ printJson(result);
663
+ }
664
+ catch (err) {
665
+ printError(err);
666
+ }
667
+ });
668
+ comment
669
+ .command('add')
670
+ .description('Add a comment to a wiki target')
671
+ .argument('[wiki]', 'Wiki ID or slug')
672
+ .requiredOption('--target-type <type>', 'Target type (changeset or page)')
673
+ .requiredOption('--target-id <id>', 'Target ID (changeset ID or page path)')
674
+ .requiredOption('--body <text>', 'Comment body')
675
+ .action(async (wikiRef, opts) => {
676
+ try {
677
+ const ctx = resolveCredentials();
678
+ const { resolveWikiRefOrDefault } = await import('../lib/wiki.js');
679
+ const { COMMENT_TARGET } = await import('@parall/sdk');
680
+ const w = await resolveWikiRefOrDefault(ctx, wikiRef);
681
+ if (opts.targetType !== 'changeset' && opts.targetType !== 'page') {
682
+ throw new Error('--target-type must be "changeset" or "page"');
683
+ }
684
+ const targetUri = opts.targetType === 'changeset'
685
+ ? COMMENT_TARGET.changeset(opts.targetId, w.id)
686
+ : COMMENT_TARGET.wikiPage(w.id, opts.targetId);
687
+ const result = await ctx.client.createComment(ctx.orgId, {
688
+ target_uri: targetUri,
689
+ body: opts.body,
690
+ });
691
+ printJson(result);
692
+ }
693
+ catch (err) {
694
+ printError(err);
695
+ }
696
+ });
697
+ comment
698
+ .command('update')
699
+ .description('Update a wiki comment')
700
+ .argument('<commentId>', 'Comment ID')
701
+ .requiredOption('--body <text>', 'New comment body')
702
+ .action(async (commentId, opts) => {
703
+ try {
704
+ const ctx = resolveCredentials();
705
+ const result = await ctx.client.updateComment(ctx.orgId, commentId, { body: opts.body });
706
+ printJson(result);
707
+ }
708
+ catch (err) {
709
+ printError(err);
710
+ }
711
+ });
712
+ comment
713
+ .command('delete')
714
+ .description('Delete a wiki comment')
715
+ .argument('<commentId>', 'Comment ID')
716
+ .action(async (commentId) => {
717
+ try {
718
+ const ctx = resolveCredentials();
719
+ await ctx.client.deleteComment(ctx.orgId, commentId);
720
+ printJson({ ok: true });
721
+ }
722
+ catch (err) {
723
+ printError(err);
724
+ }
725
+ });
633
726
  // ---- Utility (not for direct agent use) ----
634
727
  wiki
635
728
  .command('list')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/cli",
3
- "version": "1.16.0",
3
+ "version": "1.18.0",
4
4
  "description": "CLI client for Parall — universal agent & human access to Parall API",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -30,7 +30,7 @@
30
30
  "@modelcontextprotocol/sdk": "1.27.1",
31
31
  "commander": "^13.0.0",
32
32
  "zod": "^4.3.6",
33
- "@parall/sdk": "1.16.0"
33
+ "@parall/sdk": "1.18.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^22.0.0",