@illuminis/comprism 0.1.2 → 0.1.4

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.
Files changed (131) hide show
  1. package/README.md +98 -36
  2. package/out/agent/command.d.ts +75 -4
  3. package/out/agent/command.js +220 -25
  4. package/out/agent/render.d.ts +17 -2
  5. package/out/agent/render.js +157 -14
  6. package/out/agent/session.d.ts +141 -2
  7. package/out/agent/session.js +735 -149
  8. package/out/commands/agents.d.ts +2 -0
  9. package/out/commands/agents.js +79 -0
  10. package/out/commands/ask.d.ts +1 -1
  11. package/out/commands/ask.js +78 -11
  12. package/out/commands/commands-thin.js +79 -17
  13. package/out/commands/config.d.ts +1 -0
  14. package/out/commands/config.js +138 -0
  15. package/out/commands/cost.d.ts +1 -0
  16. package/out/commands/cost.js +167 -0
  17. package/out/commands/hooks.d.ts +1 -0
  18. package/out/commands/hooks.js +83 -0
  19. package/out/commands/install.d.ts +44 -1
  20. package/out/commands/install.js +198 -4
  21. package/out/commands/instructions.d.ts +1 -0
  22. package/out/commands/instructions.js +113 -0
  23. package/out/commands/integrations.d.ts +3 -0
  24. package/out/commands/integrations.js +215 -0
  25. package/out/commands/jobs.d.ts +5 -0
  26. package/out/commands/jobs.js +157 -0
  27. package/out/commands/login.js +188 -36
  28. package/out/commands/memory.d.ts +3 -0
  29. package/out/commands/memory.js +113 -0
  30. package/out/commands/permissions.d.ts +1 -0
  31. package/out/commands/permissions.js +94 -0
  32. package/out/commands/plugins.d.ts +4 -0
  33. package/out/commands/plugins.js +192 -0
  34. package/out/commands/privacy.d.ts +1 -0
  35. package/out/commands/privacy.js +57 -0
  36. package/out/commands/providerKey.d.ts +32 -0
  37. package/out/commands/providerKey.js +108 -0
  38. package/out/commands/repl.d.ts +8 -1
  39. package/out/commands/repl.js +1207 -118
  40. package/out/commands/report.d.ts +39 -0
  41. package/out/commands/report.js +115 -0
  42. package/out/commands/review.d.ts +5 -0
  43. package/out/commands/review.js +223 -0
  44. package/out/commands/sessions.d.ts +23 -0
  45. package/out/commands/sessions.js +115 -0
  46. package/out/commands/settings.d.ts +3 -1
  47. package/out/commands/settings.js +18 -16
  48. package/out/commands/skills.d.ts +21 -0
  49. package/out/commands/skills.js +207 -0
  50. package/out/commands/unattended.d.ts +7 -0
  51. package/out/commands/unattended.js +351 -0
  52. package/out/commands/update.d.ts +1 -0
  53. package/out/commands/update.js +123 -0
  54. package/out/commands/worktrees.d.ts +5 -0
  55. package/out/commands/worktrees.js +186 -0
  56. package/out/executor/browser.d.ts +14 -0
  57. package/out/executor/browser.js +270 -0
  58. package/out/executor/diagnostics.d.ts +2 -0
  59. package/out/executor/diagnostics.js +181 -0
  60. package/out/executor/files.js +270 -40
  61. package/out/executor/git.js +42 -29
  62. package/out/executor/hooks.d.ts +42 -58
  63. package/out/executor/hooks.js +89 -182
  64. package/out/executor/index.d.ts +21 -5
  65. package/out/executor/index.js +160 -14
  66. package/out/executor/paths.d.ts +6 -1
  67. package/out/executor/paths.js +34 -6
  68. package/out/executor/sandbox.d.ts +40 -0
  69. package/out/executor/sandbox.js +299 -0
  70. package/out/executor/shell.d.ts +49 -4
  71. package/out/executor/shell.js +302 -56
  72. package/out/executor/toolservers.d.ts +20 -0
  73. package/out/executor/toolservers.js +189 -0
  74. package/out/executor/worktree.d.ts +9 -0
  75. package/out/executor/worktree.js +119 -0
  76. package/out/graph/read-python.js +2 -1
  77. package/out/lib/attach.d.ts +56 -12
  78. package/out/lib/attach.js +230 -63
  79. package/out/lib/clipboard.d.ts +23 -0
  80. package/out/lib/clipboard.js +182 -0
  81. package/out/lib/commandlist.d.ts +20 -0
  82. package/out/lib/commandlist.js +58 -0
  83. package/out/lib/decision.d.ts +22 -0
  84. package/out/lib/decision.js +50 -0
  85. package/out/lib/fingerprint.d.ts +25 -0
  86. package/out/lib/fingerprint.js +58 -0
  87. package/out/lib/gateway.d.ts +186 -2
  88. package/out/lib/gateway.js +59 -4
  89. package/out/lib/history.d.ts +24 -0
  90. package/out/lib/history.js +137 -0
  91. package/out/lib/ide.d.ts +19 -0
  92. package/out/lib/ide.js +131 -0
  93. package/out/lib/keyboard.d.ts +95 -0
  94. package/out/lib/keyboard.js +383 -0
  95. package/out/lib/machine.d.ts +21 -0
  96. package/out/lib/machine.js +91 -0
  97. package/out/lib/notify.d.ts +4 -0
  98. package/out/lib/notify.js +52 -0
  99. package/out/lib/output.d.ts +48 -0
  100. package/out/lib/output.js +108 -0
  101. package/out/lib/project-ops.d.ts +19 -0
  102. package/out/lib/project-ops.js +146 -0
  103. package/out/lib/project.d.ts +28 -0
  104. package/out/lib/project.js +114 -0
  105. package/out/lib/prompt.js +15 -2
  106. package/out/lib/queue.d.ts +13 -0
  107. package/out/lib/queue.js +116 -0
  108. package/out/lib/readiness.d.ts +19 -0
  109. package/out/lib/readiness.js +170 -1
  110. package/out/lib/self.d.ts +23 -0
  111. package/out/lib/self.js +124 -0
  112. package/out/lib/sessions.d.ts +19 -0
  113. package/out/lib/sessions.js +221 -0
  114. package/out/lib/stdin.d.ts +32 -0
  115. package/out/lib/stdin.js +117 -0
  116. package/out/lib/store.d.ts +40 -0
  117. package/out/lib/store.js +138 -0
  118. package/out/lib/sync.d.ts +18 -0
  119. package/out/lib/sync.js +81 -0
  120. package/out/lib/ui.d.ts +2 -4
  121. package/out/lib/ui.js +31 -25
  122. package/out/lib/voice.js +24 -0
  123. package/out/postinstall.js +42 -14
  124. package/out/providers/anthropic.d.ts +22 -0
  125. package/out/providers/anthropic.js +80 -0
  126. package/out/providers/index.d.ts +8 -0
  127. package/out/providers/index.js +83 -0
  128. package/out/providers/openai.d.ts +11 -0
  129. package/out/providers/openai.js +57 -0
  130. package/out/thin.js +609 -21
  131. package/package.json +9 -49
@@ -127,7 +127,113 @@ async function post(url, path, body, tenant) {
127
127
  catch { /* non-JSON body */ }
128
128
  return { status: res.status, json };
129
129
  }
130
+ const DEVICE_GRANT = 'urn:ietf:params:oauth:grant-type:device_code';
131
+ /** The built in public client the portal accepts for this product's CLI. */
132
+ const CLIENT_ID = 'completionprism-cli';
133
+ function headersFor(tenant) {
134
+ return tenant ? { 'X-Tenant-ID': tenant } : {};
135
+ }
136
+ /** Whether this workspace's company signs in through its own identity provider. */
137
+ async function usesCompanyLogin(url, tenant) {
138
+ try {
139
+ const res = await fetch(`${url}/api/v1/auth/sso/providers`, { headers: headersFor(tenant) });
140
+ if (!res.ok)
141
+ return false;
142
+ const body = await res.json();
143
+ return (body.providers?.length ?? 0) > 0 && body.password_enabled === false;
144
+ }
145
+ catch {
146
+ return false;
147
+ }
148
+ }
149
+ function openInBrowser(address) {
150
+ const { spawn } = require('node:child_process');
151
+ const [cmd, argv] = process.platform === 'darwin' ? ['open', [address]]
152
+ : process.platform === 'win32' ? ['cmd', ['/c', 'start', '', address]]
153
+ : ['xdg-open', [address]];
154
+ try {
155
+ spawn(cmd, argv, { stdio: 'ignore', detached: true }).on('error', () => { }).unref();
156
+ }
157
+ catch { /* the address is printed as well */ }
158
+ }
159
+ /** The portal's own device sign in: approve in a browser, get a token back. */
160
+ async function approveInBrowser(url, tenant, noBrowser) {
161
+ const form = (fields) => ({
162
+ method: 'POST',
163
+ headers: { ...headersFor(tenant), 'content-type': 'application/x-www-form-urlencoded' },
164
+ body: new URLSearchParams(fields).toString(),
165
+ });
166
+ let started;
167
+ try {
168
+ const res = await fetch(`${url}/oauth/device_authorization`, form({ client_id: CLIENT_ID, scope: 'api:read api:write' }));
169
+ if (!res.ok) {
170
+ out(ui.fail(res.status === 404 || res.status === 405
171
+ ? 'That workspace is running a build without browser sign in.'
172
+ : 'The workspace would not start a browser sign in.'));
173
+ return '';
174
+ }
175
+ started = await res.json();
176
+ }
177
+ catch {
178
+ out(ui.fail(`Could not reach ${url}. Check your connection and try again.`));
179
+ return '';
180
+ }
181
+ out();
182
+ if (noBrowser) {
183
+ out(` On any device, open ${ui.c.bold(started.verification_uri)}`);
184
+ out(` and enter the code ${ui.c.bold(started.user_code)}`);
185
+ }
186
+ else {
187
+ out(ui.info('Your company signs in through its own login page. Opening your browser.'));
188
+ out(` If it does not open, go to ${started.verification_uri_complete}`);
189
+ out(` and confirm the code ${ui.c.bold(started.user_code)}`);
190
+ openInBrowser(started.verification_uri_complete);
191
+ }
192
+ out(ui.c.dim(' waiting for you to approve it there...'));
193
+ const deadline = Date.now() + Math.max(30, started.expires_in || 600) * 1000;
194
+ let wait = Math.max(1, started.interval || 5);
195
+ while (Date.now() < deadline) {
196
+ await new Promise((r) => setTimeout(r, wait * 1000));
197
+ let res;
198
+ try {
199
+ res = await fetch(`${url}/oauth/token`, form({ grant_type: DEVICE_GRANT, device_code: started.device_code, client_id: CLIENT_ID }));
200
+ }
201
+ catch {
202
+ continue;
203
+ }
204
+ const body = await res.json().catch(() => ({}));
205
+ if (res.ok && body.access_token)
206
+ return body.access_token;
207
+ if (body.error === 'authorization_pending')
208
+ continue;
209
+ if (body.error === 'slow_down') {
210
+ wait += 5;
211
+ continue;
212
+ }
213
+ out(ui.fail(body.error === 'access_denied' ? 'The sign in was declined in the browser.'
214
+ : 'That sign in was not approved.'));
215
+ return '';
216
+ }
217
+ out(ui.fail('That code expired before it was approved. Run comprism login again.'));
218
+ return '';
219
+ }
130
220
  async function runLogin(args) {
221
+ // SIGN IN WITH AN INSTALL KEY (web manual 12.1). The key made on the
222
+ // Install and setup page is this machine's credential, so setup is the one
223
+ // command the page shows and nothing is typed. It needs no terminal, because
224
+ // the install script runs it on a pipe.
225
+ const keyAt = args.indexOf('--key');
226
+ if (keyAt >= 0)
227
+ return loginWithKey(args[keyAt + 1], args);
228
+ // Password and second factor entry require a person at a terminal. Do not
229
+ // open readline on a pipe: it can wait forever, or exit successfully at EOF
230
+ // without signing in. Check before workspace lookup or reading credentials.
231
+ if (process.stdin.isTTY !== true) {
232
+ out(ui.fail('Signing in requires an interactive terminal.'));
233
+ out(' Run comprism login in a terminal, then run your command again.');
234
+ process.exitCode = 1;
235
+ return;
236
+ }
131
237
  out(ui.banner('Sign in'));
132
238
  out();
133
239
  // Flags still work, for a script or a second machine. A person gets asked.
@@ -151,8 +257,12 @@ async function runLogin(args) {
151
257
  if (!typedWorkspace) {
152
258
  const live = new ui.LiveIndicator();
153
259
  live.start('finding your workspace');
154
- typedWorkspace = (await resolveWorkspace(username)) || undefined;
155
- live.stop();
260
+ try {
261
+ typedWorkspace = (await resolveWorkspace(username)) || undefined;
262
+ }
263
+ finally {
264
+ live.stop();
265
+ }
156
266
  // The indicator clears its own line but leaves the cursor mid-row, so the
157
267
  // next prompt landed after a run of blanks.
158
268
  process.stdout.write('\r');
@@ -167,8 +277,23 @@ async function runLogin(args) {
167
277
  }
168
278
  const url = workspaceUrl(typedWorkspace);
169
279
  const tenant = tenantFor(url, typedWorkspace, flag('--tenant'));
170
- const password = await (0, prompt_1.askHidden)(' password: ');
171
- if (!password) {
280
+ // Said before the password is asked for, so a person can see which company
281
+ // they are about to sign in to (manual 1.3).
282
+ out(` workspace: ${url}`);
283
+ // COMPANY LOGIN (manual 1.10). A workspace whose company signs in through
284
+ // its own identity provider is signed in to in a browser; this tool never
285
+ // sees that password. `--device` is the same thing for a machine with no
286
+ // browser: a short code to approve from any other device.
287
+ const byCompany = args.includes('--device') || args.includes('--browser')
288
+ || await usesCompanyLogin(url, tenant);
289
+ const deviceToken = byCompany ? await approveInBrowser(url, tenant, args.includes('--device')) : '';
290
+ if (byCompany && !deviceToken) {
291
+ out();
292
+ process.exitCode = 1;
293
+ return;
294
+ }
295
+ const password = byCompany ? '' : await (0, prompt_1.askHidden)(' password: ');
296
+ if (!byCompany && !password) {
172
297
  out(ui.fail('A password is needed.'));
173
298
  out();
174
299
  process.exitCode = 1;
@@ -185,7 +310,7 @@ async function runLogin(args) {
185
310
  let signInMessage = '';
186
311
  {
187
312
  let reply = await (0, gateway_1.askAt)(url, {
188
- intent: 'sign_in', prompt: username, password,
313
+ intent: 'sign_in', prompt: username, password, deviceToken,
189
314
  machineLabel: `comprism on ${node_os_1.default.hostname()}`,
190
315
  }, tenant);
191
316
  if (reply.served === false) {
@@ -198,8 +323,9 @@ async function runLogin(args) {
198
323
  }
199
324
  if (!reply.ok) {
200
325
  out();
201
- out(ui.fail(`Could not reach ${url}.`));
202
- out(` ${ui.c.dim('check the workspace name, and that you are on the network')}`);
326
+ out(ui.fail(reply.httpStatus
327
+ ? (reply.message || 'The service refused the sign in request.')
328
+ : `Could not reach ${url}. Check your connection and try again.`));
203
329
  out();
204
330
  process.exitCode = 1;
205
331
  return;
@@ -210,7 +336,7 @@ async function runLogin(args) {
210
336
  if (reply.sign_in?.needs_second_factor && reply.sign_in.challenge) {
211
337
  out();
212
338
  out(ui.info('This account uses a second step. Check your authenticator app or your email.'));
213
- const code = await (0, prompt_1.ask)(' code: ');
339
+ const code = await (0, prompt_1.askHidden)(' code from your authenticator app: ');
214
340
  if (!code) {
215
341
  out(ui.fail('A code is needed to finish signing in.'));
216
342
  out();
@@ -254,31 +380,10 @@ async function runLogin(args) {
254
380
  process.exitCode = 1;
255
381
  return;
256
382
  }
257
- // Enrol the machine while we still hold the session, so signing in is one
258
- // step rather than a sign in followed by something else to remember.
259
- // The machine credential came back WITH the sign in, from the one service,
260
- // which is why there is no second call here any more. An existing one is
261
- // kept: signing in twice must not leave two entries in the workspace's own
262
- // list of machines.
383
+ // A previous credential may belong to another account or workspace, or
384
+ // have been revoked during this sign in. Only retain the newly issued key.
263
385
  const existing = (0, connection_1.readConnection)();
264
- // The key this sign in just issued wins.
265
- //
266
- // This used to prefer whatever was already on the machine, so that signing
267
- // in twice did not leave two entries in the workspace's own list of
268
- // machines. The rule was right for the same person signing in again and
269
- // wrong for every other case: sign in as a different account, or into a
270
- // different workspace, and the machine carried on holding the OLD key. The
271
- // sign in reported success, the header then said the workspace could not be
272
- // reached, and nothing anywhere said that the credential belonged to
273
- // somebody else. A revoked key behaved the same way.
274
- //
275
- // The server already ends the previous credential for this machine when it
276
- // issues one, so taking the new key does not leave two live. The old one is
277
- // kept only when the server issued nothing at all, which is the case where
278
- // there is nothing better to hold.
279
- const workspaceKey = signedIn?.machine_credential
280
- || existing?.workspaceKey
281
- || undefined;
386
+ const workspaceKey = signedIn?.machine_credential || undefined;
282
387
  // `typedByHand` is the second half of the fence in `writeConnection`. It is
283
388
  // true only when the person put the address on this command line, in this
284
389
  // run. A workspace resolved from their email domain, or an address a server
@@ -312,16 +417,20 @@ async function runLogin(args) {
312
417
  process.exitCode = 1;
313
418
  return;
314
419
  }
420
+ // So a new terminal presents the key just issued, not the last one or none.
421
+ for (const profile of (await Promise.resolve().then(() => __importStar(require('./install')))).finishSetup())
422
+ out(` Finishing setup: added one line to ${profile}`);
315
423
  out();
316
- out(ui.ok(`Signed in to ${ui.c.bold(url)}`));
424
+ out(ui.ok(`Signed in as ${ui.c.bold(username)} at ${ui.c.bold(signedIn?.workspace_url || url)}.`));
317
425
  if (workspaceKey) {
318
- out(` ${ui.c.dim('this machine is enrolled and revocable on its own')}`);
426
+ out(` ${ui.c.dim('This machine is enrolled and can be revoked from Administration.')}`);
319
427
  // Straight into the welcome screen, so the first thing after signing in is
320
428
  // where they are rather than another instruction.
321
429
  const { showWelcome } = await Promise.resolve().then(() => __importStar(require('./welcome')));
322
430
  await showWelcome();
323
431
  }
324
432
  else {
433
+ process.exitCode = 1;
325
434
  out(ui.warn('Signed in, but this machine could not be enrolled.'));
326
435
  // The workspace's OWN reason, carried back with the sign in. It used to
327
436
  // come from a local variable set by a separate call, which meant a blank
@@ -331,6 +440,48 @@ async function runLogin(args) {
331
440
  }
332
441
  out();
333
442
  }
443
+ /** Enrol this machine with an install key: checked with the service, then kept. */
444
+ async function loginWithKey(key, args) {
445
+ const flag = (name) => {
446
+ const i = args.indexOf(name);
447
+ return i >= 0 ? args[i + 1] : undefined;
448
+ };
449
+ const typed = flag('--workspace');
450
+ if (!key || !typed) {
451
+ out(ui.fail('Signing in with a key needs the key and the workspace: comprism login --key <key> --workspace <address>'));
452
+ process.exitCode = 1;
453
+ return;
454
+ }
455
+ const url = workspaceUrl(typed);
456
+ const tenant = tenantFor(url, typed, flag('--tenant'));
457
+ const { ask: askWith } = await Promise.resolve().then(() => __importStar(require('../lib/gateway')));
458
+ const existing = (0, connection_1.readConnection)();
459
+ try {
460
+ (0, connection_1.writeConnection)({
461
+ url, token: '', workspaceKey: key, ...(tenant ? { tenant } : {}),
462
+ everyMinutes: existing?.everyMinutes ?? 5,
463
+ connectedAt: new Date().toISOString(),
464
+ }, true);
465
+ }
466
+ catch (err) {
467
+ out(ui.fail(err instanceof Error ? err.message : 'the workspace address was refused'));
468
+ process.exitCode = 1;
469
+ return;
470
+ }
471
+ const reply = await askWith({ intent: 'catalog', prompt: '' });
472
+ if (!reply.ok) {
473
+ if (existing)
474
+ (0, connection_1.writeConnection)(existing, true);
475
+ out(ui.fail(reply.message || `That key was not accepted by ${url}.`));
476
+ out(` ${ui.c.dim('make a new one on the Install and setup page')}`);
477
+ process.exitCode = 1;
478
+ return;
479
+ }
480
+ for (const profile of (await Promise.resolve().then(() => __importStar(require('./install')))).finishSetup())
481
+ out(` Finishing setup: added one line to ${profile}`);
482
+ out(ui.ok(`This machine is enrolled at ${ui.c.bold(url)}.`));
483
+ out(` ${ui.c.dim('It can be revoked from Developers, Install and setup.')}`);
484
+ }
334
485
  /**
335
486
  * Which workspace an email belongs to.
336
487
  *
@@ -359,8 +510,9 @@ async function resolveWorkspace(email) {
359
510
  // because a person looking for their workspace does not have one yet.
360
511
  const root = `https://${WORKSPACE_DOMAIN}`;
361
512
  const reply = await (0, gateway_1.askAt)(root, { intent: 'resolve_workspace', prompt: email });
362
- if (!reply.ok || reply.served === false)
363
- return null;
513
+ if (!reply.ok || reply.served === false || reply.complete === false) {
514
+ throw new Error(reply.message || 'Your workspace could not be looked up. Try again later.');
515
+ }
364
516
  const url = reply.session?.workspace_url;
365
517
  if (!url)
366
518
  return null;
@@ -0,0 +1,3 @@
1
+ /** `comprism memory add` and the session word `remember`. */
2
+ export declare function addNote(text: string): Promise<number>;
3
+ export declare function cmdMemory(args: string[]): Promise<number>;
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.addNote = addNote;
37
+ exports.cmdMemory = cmdMemory;
38
+ /**
39
+ * `comprism memory` (manual 2.9): this project's notes. They are files under
40
+ * `.completionprism/memory/`; this command lists them, and adds, removes and
41
+ * opens them as the service says. Which notes are used, numbering and every
42
+ * word shown are the service's.
43
+ */
44
+ const fs = __importStar(require("node:fs"));
45
+ const path = __importStar(require("node:path"));
46
+ const project_ops_1 = require("../lib/project-ops");
47
+ const ui = __importStar(require("../lib/ui"));
48
+ const out = (s = "") => process.stdout.write(s + "\n");
49
+ const FOLDER = ".completionprism/memory";
50
+ const SETTINGS_FILES = [".completionprism/settings.local.json", ".completionprism/settings.json"];
51
+ function notes(root) {
52
+ let names = [];
53
+ try {
54
+ names = fs.readdirSync(path.join(root, FOLDER)).filter((n) => n.endsWith(".md")).sort();
55
+ }
56
+ catch { /* none yet */ }
57
+ return names
58
+ .map((n) => ({ file: `${FOLDER}/${n}`, text: (0, project_ops_1.readInside)(root, `${FOLDER}/${n}`) ?? "" }))
59
+ .filter((n) => n.text.trim());
60
+ }
61
+ /** Carry out what the service said to write or delete, inside the project. */
62
+ function apply(facts, data) {
63
+ const write = data?.write;
64
+ if (write?.key)
65
+ (0, project_ops_1.writeSetting)(facts.root, write.file, write.key, write.value);
66
+ else if (write?.file && typeof write.content === "string") {
67
+ const full = path.join(facts.root, write.file);
68
+ fs.mkdirSync(path.dirname(full), { recursive: true });
69
+ fs.writeFileSync(full, write.content);
70
+ }
71
+ const gone = data?.delete;
72
+ if (typeof gone === "string" && gone.startsWith(`${FOLDER}/`))
73
+ fs.rmSync(path.join(facts.root, gone), { force: true });
74
+ }
75
+ /** `comprism memory add` and the session word `remember`. */
76
+ async function addNote(text) {
77
+ const facts = await (0, project_ops_1.currentProject)();
78
+ const reply = await (0, project_ops_1.call)("memory", "add", { text }, facts);
79
+ if (!reply)
80
+ return 1;
81
+ apply(facts, reply.data);
82
+ return 0;
83
+ }
84
+ async function cmdMemory(args) {
85
+ const [verb, ...rest] = args;
86
+ const facts = await (0, project_ops_1.currentProject)();
87
+ const base = { notes: notes(facts.root), files: (0, project_ops_1.readSome)(facts.root, SETTINGS_FILES) };
88
+ if (verb === "add")
89
+ return addNote(rest.join(" "));
90
+ if (verb === "edit") {
91
+ const files = base.notes.map((n) => path.join(facts.root, n.file));
92
+ if (!files.length) {
93
+ out(ui.info('No notes yet. Add one with: comprism memory add "<text>"'));
94
+ return 0;
95
+ }
96
+ return (await (0, project_ops_1.openEditor)(files)) ? 0 : 1;
97
+ }
98
+ if (verb === "forget" || verb === "on" || verb === "off") {
99
+ const reply = await (0, project_ops_1.call)("memory", verb, { ...base, number: rest[0] }, facts);
100
+ if (!reply)
101
+ return 1;
102
+ apply(facts, reply.data);
103
+ return 0;
104
+ }
105
+ if (verb) {
106
+ out(ui.fail(`\`memory ${verb}\` is not a command. Use add, edit, forget N, on or off.`));
107
+ return 2;
108
+ }
109
+ out();
110
+ const reply = await (0, project_ops_1.call)("memory", "list", base, facts);
111
+ out();
112
+ return reply ? 0 : 1;
113
+ }
@@ -0,0 +1 @@
1
+ export declare function cmdPermissions(args: string[]): Promise<number>;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.cmdPermissions = cmdPermissions;
37
+ /**
38
+ * `comprism permissions` and `comprism permissions why <action> "<target>"`
39
+ * (manual 4.14).
40
+ *
41
+ * This reads the project's settings files and sends their text; the service
42
+ * decides and writes every line printed, with the same decision the agent's
43
+ * jobs use, so `why` and a real job can never disagree.
44
+ */
45
+ const fs = __importStar(require("node:fs"));
46
+ const path = __importStar(require("node:path"));
47
+ const gateway_1 = require("../lib/gateway");
48
+ const ui = __importStar(require("../lib/ui"));
49
+ const sessions_1 = require("../lib/sessions");
50
+ /** The settings files that can hold rules, relative to the project root. The
51
+ * service decides which of them count and how. */
52
+ const RULE_FILES = [
53
+ '.completionprism/settings.json', '.completionprism/settings.local.json',
54
+ '.comprism/settings.json', '.comprism/settings.local.json',
55
+ ];
56
+ const USAGE = 'Usage: comprism permissions, or comprism permissions why <action> "<target>"';
57
+ async function cmdPermissions(args) {
58
+ const [op, action, ...rest] = args;
59
+ if (op && op !== 'why') {
60
+ process.stderr.write(` ${USAGE}\n`);
61
+ return 2;
62
+ }
63
+ if (op === 'why' && (!action || rest.length === 0)) {
64
+ process.stderr.write(` ${USAGE}\n`);
65
+ return 2;
66
+ }
67
+ const { resolveProject, asWire } = await Promise.resolve().then(() => __importStar(require('../lib/project')));
68
+ const { currentWorkspace } = await Promise.resolve().then(() => __importStar(require('./settings')));
69
+ const facts = resolveProject(currentWorkspace().path, []);
70
+ const files = {};
71
+ for (const rel of RULE_FILES) {
72
+ try {
73
+ files[rel] = fs.readFileSync(path.join(facts.root, rel), 'utf8');
74
+ }
75
+ catch { /* none */ }
76
+ }
77
+ const reply = await (0, gateway_1.ask)({
78
+ intent: 'permissions',
79
+ prompt: '',
80
+ project: asWire(facts),
81
+ permissionsPayload: {
82
+ op: op === 'why' ? 'why' : 'list', action, target: rest.join(' '), files,
83
+ },
84
+ });
85
+ const lines = reply.permissions?.lines;
86
+ if (!reply.ok || !Array.isArray(lines)) {
87
+ process.stderr.write(ui.fail(reply.served === false
88
+ ? 'Your workspace is running a build without permission answers yet.'
89
+ : (0, sessions_1.failure)(reply)) + '\n');
90
+ return (0, sessions_1.exitFor)(reply);
91
+ }
92
+ process.stdout.write(`${lines.join('\n')}\n`);
93
+ return 0;
94
+ }
@@ -0,0 +1,4 @@
1
+ /** Every text file in the folder, by path inside it. Never runs anything. */
2
+ export declare function readFolder(dir: string): Record<string, string>;
3
+ export declare const PLUGINS_HELP = " comprism plugins installed plugins and their state\n comprism plugins install <name|folder> install from your company's catalog or a folder\n --scope project|personal skip the question\n comprism plugins enable <name> comprism plugins disable <name>\n comprism plugins update <name> shows what changes, then asks\n comprism plugins remove <name>";
4
+ export declare function cmdPlugins(args: string[]): Promise<number>;