@illuminis/comprism 0.1.4 → 0.1.6

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 (116) hide show
  1. package/out/agent/command.js +5 -5
  2. package/out/agent/render.d.ts +39 -6
  3. package/out/agent/render.js +287 -28
  4. package/out/agent/session.d.ts +21 -1
  5. package/out/agent/session.js +81 -17
  6. package/out/commands/agents.js +3 -3
  7. package/out/commands/ask.js +20 -11
  8. package/out/commands/codemap.d.ts +1 -1
  9. package/out/commands/codemap.js +3 -3
  10. package/out/commands/commands-thin.js +4 -4
  11. package/out/commands/config.js +1 -1
  12. package/out/commands/cost.js +4 -4
  13. package/out/commands/hooks.js +1 -1
  14. package/out/commands/install.d.ts +1 -1
  15. package/out/commands/install.js +3 -2
  16. package/out/commands/instructions.js +1 -1
  17. package/out/commands/integrations.js +4 -4
  18. package/out/commands/keys.js +6 -6
  19. package/out/commands/login.js +97 -27
  20. package/out/commands/permissions.js +2 -2
  21. package/out/commands/plugins.js +3 -3
  22. package/out/commands/privacy.js +2 -2
  23. package/out/commands/repl.d.ts +4 -0
  24. package/out/commands/repl.js +563 -101
  25. package/out/commands/review.js +6 -6
  26. package/out/commands/settings.js +30 -123
  27. package/out/commands/skills.js +2 -2
  28. package/out/commands/unattended.js +6 -6
  29. package/out/commands/update.js +1 -1
  30. package/out/commands/welcome.d.ts +25 -1
  31. package/out/commands/welcome.js +65 -38
  32. package/out/commands/worktrees.d.ts +1 -1
  33. package/out/graph/sync.d.ts +1 -1
  34. package/out/graph/sync.js +6 -6
  35. package/out/lib/attach.js +4 -3
  36. package/out/lib/commandlist.d.ts +1 -1
  37. package/out/lib/commandlist.js +2 -2
  38. package/out/lib/config.d.ts +3 -2
  39. package/out/lib/config.js +7 -8
  40. package/out/lib/connection.js +1 -1
  41. package/out/lib/gateway.d.ts +9 -5
  42. package/out/lib/keyboard.d.ts +3 -0
  43. package/out/lib/keyboard.js +7 -0
  44. package/out/lib/lastlogin.d.ts +14 -0
  45. package/out/lib/lastlogin.js +105 -0
  46. package/out/lib/machine.js +1 -1
  47. package/out/lib/picker.d.ts +1 -0
  48. package/out/lib/picker.js +158 -0
  49. package/out/lib/project-ops.d.ts +1 -1
  50. package/out/lib/project-ops.js +4 -4
  51. package/out/lib/prompt.d.ts +3 -0
  52. package/out/lib/prompt.js +21 -0
  53. package/out/lib/queue.js +1 -1
  54. package/out/lib/readiness.d.ts +3 -30
  55. package/out/lib/readiness.js +13 -82
  56. package/out/lib/servicecommand.d.ts +14 -0
  57. package/out/lib/servicecommand.js +74 -0
  58. package/out/lib/sessions.js +3 -3
  59. package/out/lib/startcache.d.ts +13 -0
  60. package/out/lib/startcache.js +78 -0
  61. package/out/lib/ui.d.ts +11 -0
  62. package/out/lib/ui.js +28 -0
  63. package/out/lib/voice.d.ts +36 -0
  64. package/out/lib/voice.js +137 -2
  65. package/out/lib/words.d.ts +20 -0
  66. package/out/lib/words.js +27 -0
  67. package/out/machine/codemap/build.d.ts +45 -0
  68. package/out/machine/codemap/build.js +91 -0
  69. package/out/machine/codemap/facts.d.ts +47 -0
  70. package/out/machine/codemap/facts.js +12 -0
  71. package/out/machine/codemap/files.d.ts +45 -0
  72. package/out/machine/codemap/files.js +207 -0
  73. package/out/machine/codemap/read-locales.d.ts +29 -0
  74. package/out/machine/codemap/read-locales.js +246 -0
  75. package/out/machine/codemap/read-python.d.ts +11 -0
  76. package/out/machine/codemap/read-python.js +116 -0
  77. package/out/machine/codemap/read-typescript.d.ts +16 -0
  78. package/out/machine/codemap/read-typescript.js +292 -0
  79. package/out/machine/executor/browser.d.ts +14 -0
  80. package/out/machine/executor/browser.js +270 -0
  81. package/out/machine/executor/diagnostics.d.ts +2 -0
  82. package/out/machine/executor/diagnostics.js +181 -0
  83. package/out/machine/executor/documents.d.ts +40 -0
  84. package/out/machine/executor/documents.js +170 -0
  85. package/out/machine/executor/files.d.ts +2 -0
  86. package/out/machine/executor/files.js +590 -0
  87. package/out/machine/executor/git.d.ts +48 -0
  88. package/out/machine/executor/git.js +145 -0
  89. package/out/machine/executor/hooks.d.ts +51 -0
  90. package/out/machine/executor/hooks.js +154 -0
  91. package/out/machine/executor/index.d.ts +45 -0
  92. package/out/machine/executor/index.js +367 -0
  93. package/out/machine/executor/notebook.d.ts +2 -0
  94. package/out/machine/executor/notebook.js +147 -0
  95. package/out/machine/executor/paths.d.ts +20 -0
  96. package/out/machine/executor/paths.js +154 -0
  97. package/out/machine/executor/sandbox.d.ts +40 -0
  98. package/out/machine/executor/sandbox.js +299 -0
  99. package/out/machine/executor/shell.d.ts +86 -0
  100. package/out/machine/executor/shell.js +582 -0
  101. package/out/machine/executor/toolservers.d.ts +20 -0
  102. package/out/machine/executor/toolservers.js +189 -0
  103. package/out/machine/executor/worktree.d.ts +9 -0
  104. package/out/machine/executor/worktree.js +119 -0
  105. package/out/machine/folder/project.d.ts +28 -0
  106. package/out/machine/folder/project.js +114 -0
  107. package/out/machine/runtime/home.d.ts +2 -0
  108. package/out/machine/runtime/home.js +48 -0
  109. package/out/machine/runtime/needs.d.ts +30 -0
  110. package/out/machine/runtime/needs.js +89 -0
  111. package/out/machine/runtime/self.d.ts +23 -0
  112. package/out/machine/runtime/self.js +124 -0
  113. package/out/native/dictate +0 -0
  114. package/out/providers/index.js +2 -1
  115. package/out/thin.js +13 -12
  116. package/package.json +5 -4
package/out/lib/config.js CHANGED
@@ -3,8 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.HOME_DIR_NAME = void 0;
7
- exports.homeDir = homeDir;
6
+ exports.homeDir = exports.HOME_DIR_NAME = void 0;
8
7
  exports.ensureHome = ensureHome;
9
8
  exports.configPath = configPath;
10
9
  exports.defaultConfig = defaultConfig;
@@ -41,10 +40,10 @@ exports.envVarName = envVarName;
41
40
  const node_fs_1 = __importDefault(require("node:fs"));
42
41
  const node_os_1 = __importDefault(require("node:os"));
43
42
  const node_path_1 = __importDefault(require("node:path"));
44
- exports.HOME_DIR_NAME = '.comprism';
45
- function homeDir() {
46
- return process.env.COMPRISM_HOME || node_path_1.default.join(node_os_1.default.homedir(), exports.HOME_DIR_NAME);
47
- }
43
+ var home_1 = require("../machine/runtime/home");
44
+ Object.defineProperty(exports, "HOME_DIR_NAME", { enumerable: true, get: function () { return home_1.HOME_DIR_NAME; } });
45
+ const home_2 = require("../machine/runtime/home");
46
+ Object.defineProperty(exports, "homeDir", { enumerable: true, get: function () { return home_2.homeDir; } });
48
47
  /**
49
48
  * Create the home directory at mode 700 if it is not there.
50
49
  *
@@ -53,7 +52,7 @@ function homeDir() {
53
52
  * never noticed afterwards.
54
53
  */
55
54
  function ensureHome() {
56
- const dir = homeDir();
55
+ const dir = (0, home_2.homeDir)();
57
56
  node_fs_1.default.mkdirSync(dir, { recursive: true, mode: 0o700 });
58
57
  try {
59
58
  node_fs_1.default.chmodSync(dir, 0o700);
@@ -64,7 +63,7 @@ function ensureHome() {
64
63
  return dir;
65
64
  }
66
65
  function configPath() {
67
- return node_path_1.default.join(homeDir(), 'config.json');
66
+ return node_path_1.default.join((0, home_2.homeDir)(), 'config.json');
68
67
  }
69
68
  function defaultConfig() {
70
69
  const now = new Date().toISOString();
@@ -141,7 +141,7 @@ function isLocalAddress(url) {
141
141
  function writeConnection(connection, typed = false) {
142
142
  if (!typed && isLocalAddress(connection.url)) {
143
143
  throw new Error(`refusing to point this machine at ${connection.url}, which is this computer `
144
- + 'rather than a workspace. Pass it with --workspace if that is genuinely what you want.');
144
+ + 'rather than an organization. Pass it with --workspace if that is genuinely what you want.');
145
145
  }
146
146
  (0, config_1.ensureHome)();
147
147
  node_fs_1.default.writeFileSync(connectionPath(), `${JSON.stringify(connection, null, 2)}\n`, { mode: 0o600 });
@@ -62,12 +62,14 @@ export interface SpendReport {
62
62
  export interface GatewayReply {
63
63
  /** Local transport metadata, not an authentication decision. */
64
64
  httpStatus?: number;
65
- intent: 'decide' | 'answer' | 'catalog' | 'store_key' | 'session' | 'job_start' | 'resolve_workspace' | 'sign_in' | 'sign_out' | 'attach' | 'transcribe' | 'graph' | 'spend' | 'project' | 'commands' | 'transcript' | 'context' | 'compact' | 'sessions' | 'permissions' | 'jobs' | 'agents' | 'integrations' | 'skills' | 'plugins' | 'schema' | 'usage' | 'instructions' | 'config' | 'memory' | 'worktrees' | 'privacy' | 'hooks' | 'queue' | 'review' | 'release';
65
+ intent: 'decide' | 'answer' | 'catalog' | 'store_key' | 'session' | 'job_start' | 'resolve_workspace' | 'sign_in' | 'sign_out' | 'attach' | 'transcribe' | 'graph' | 'spend' | 'project' | 'commands' | 'transcript' | 'context' | 'compact' | 'sessions' | 'permissions' | 'jobs' | 'agents' | 'integrations' | 'skills' | 'plugins' | 'schema' | 'usage' | 'instructions' | 'config' | 'memory' | 'worktrees' | 'privacy' | 'hooks' | 'queue' | 'review' | 'release' | 'command';
66
66
  client: string;
67
67
  decision: Decision;
68
68
  answer: string | null;
69
69
  /** The receipt, already worded by the server. Clients never compose one. */
70
70
  receipt: string | null;
71
+ /** The receipt card, worded by the server for every client. */
72
+ card?: import('../agent/render').ReceiptCard | null;
71
73
  /** The model that actually answered. */
72
74
  ran_on: string | null;
73
75
  /** What this job cost to finish, on the model we ran. */
@@ -92,7 +94,7 @@ export interface GatewayReply {
92
94
  /**
93
95
  * Did this workspace's build serve the intent that was asked for.
94
96
  *
95
- * False is a COMPLETE answer meaning "your workspace is behind", never an
97
+ * False is a COMPLETE answer meaning "your organization is behind", never an
96
98
  * error and never an absence. A caller that reads it as an absence repeats
97
99
  * the 17 September fault: a tool asked a workspace for its vendor list, the
98
100
  * build did not carry that question, and the refusal was reported to the
@@ -244,8 +246,8 @@ export interface GatewayReply {
244
246
  *
245
247
  * False means we never heard from it, so EVERY other field here is this
246
248
  * client's own placeholder rather than anything the workspace said. Without
247
- * this a caller could not tell "the workspace says you have no provider key"
248
- * from "the workspace did not answer", and it read the second as the first:
249
+ * this a caller could not tell "the organization says you have no provider key"
250
+ * from "the organization did not answer", and it read the second as the first:
249
251
  * somebody whose keys were all present and correct was told none was stored
250
252
  * and walked into a form asking them to paste one.
251
253
  */
@@ -262,6 +264,8 @@ export interface SessionRecord {
262
264
  conversation_kept?: boolean;
263
265
  cost_usd: number;
264
266
  jobs: number;
267
+ /** The session as a list entry, in the service's words: name, then where and when. */
268
+ card?: string[];
265
269
  }
266
270
  export interface SessionsReply extends Partial<SessionRecord> {
267
271
  lines: string[];
@@ -338,7 +342,7 @@ export interface SessionOut {
338
342
  can_write: boolean;
339
343
  }
340
344
  export interface Ask {
341
- intent: 'decide' | 'answer' | 'catalog' | 'store_key' | 'session' | 'job_start' | 'resolve_workspace' | 'sign_in' | 'sign_out' | 'attach' | 'transcribe' | 'graph' | 'spend' | 'project' | 'commands' | 'transcript' | 'context' | 'compact' | 'sessions' | 'permissions' | 'jobs' | 'agents' | 'integrations' | 'skills' | 'plugins' | 'schema' | 'usage' | 'instructions' | 'config' | 'memory' | 'worktrees' | 'privacy' | 'hooks' | 'queue' | 'review' | 'release';
345
+ intent: 'decide' | 'answer' | 'catalog' | 'store_key' | 'session' | 'job_start' | 'resolve_workspace' | 'sign_in' | 'sign_out' | 'attach' | 'transcribe' | 'graph' | 'spend' | 'project' | 'commands' | 'transcript' | 'context' | 'compact' | 'sessions' | 'permissions' | 'jobs' | 'agents' | 'integrations' | 'skills' | 'plugins' | 'schema' | 'usage' | 'instructions' | 'config' | 'memory' | 'worktrees' | 'privacy' | 'hooks' | 'queue' | 'review' | 'release' | 'command';
342
346
  prompt: string;
343
347
  callingModel?: string;
344
348
  contextTokens?: number;
@@ -47,6 +47,9 @@ export declare class Keyboard {
47
47
  /** Esc on its own (manual 6.7). Returns true when it was used, so the key
48
48
  * goes no further; false passes it to the reader as before. */
49
49
  onEscape: (() => boolean) | null;
50
+ /** The command list under the prompt (manual 6.4): Up, Down, Enter and Tab
51
+ * go to it while it is showing. Returns true when it used the key. */
52
+ onMenuKey: ((key: 'up' | 'down' | 'enter' | 'tab') => boolean) | null;
50
53
  /** Ctrl+O, the short or full view (manual 6.9). */
51
54
  onFullView: (() => void) | null;
52
55
  /** Ctrl+G, the request in your own editor (manual 6.6). */
@@ -57,6 +57,9 @@ class Keyboard {
57
57
  /** Esc on its own (manual 6.7). Returns true when it was used, so the key
58
58
  * goes no further; false passes it to the reader as before. */
59
59
  onEscape = null;
60
+ /** The command list under the prompt (manual 6.4): Up, Down, Enter and Tab
61
+ * go to it while it is showing. Returns true when it used the key. */
62
+ onMenuKey = null;
60
63
  /** Ctrl+O, the short or full view (manual 6.9). */
61
64
  onFullView = null;
62
65
  /** Ctrl+G, the request in your own editor (manual 6.6). */
@@ -167,6 +170,10 @@ class Keyboard {
167
170
  // arrow or another sequence and goes to the reader.
168
171
  if (s === '\x1b' && this.onEscape?.())
169
172
  return;
173
+ const menu = { '\x1b[A': 'up', '\x1bOA': 'up', '\x1b[B': 'down', '\x1bOB': 'down',
174
+ '\r': 'enter', '\t': 'tab' }[s];
175
+ if (menu && this.onMenuKey?.(menu))
176
+ return;
170
177
  if (this.vi) {
171
178
  if (s === '\x1b') {
172
179
  this.normal = true;
@@ -0,0 +1,14 @@
1
+ export interface LastLogin {
2
+ email: string;
3
+ /** Exactly what was typed or found as the workspace, so a local one stays local. */
4
+ workspace: string;
5
+ /** Whether that workspace was typed by the person (only then may it be local). */
6
+ typed: boolean;
7
+ }
8
+ export declare function readLast(): LastLogin | null;
9
+ export declare function saveLast(last: LastLogin): void;
10
+ export declare function forgetLast(): void;
11
+ export declare const keychainAvailable: () => boolean;
12
+ export declare function savedPassword(email: string, workspace: string): string | null;
13
+ export declare function savePassword(email: string, workspace: string, password: string): boolean;
14
+ export declare function forgetPassword(email: string, workspace: string): void;
@@ -0,0 +1,105 @@
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.keychainAvailable = void 0;
37
+ exports.readLast = readLast;
38
+ exports.saveLast = saveLast;
39
+ exports.forgetLast = forgetLast;
40
+ exports.savedPassword = savedPassword;
41
+ exports.savePassword = savePassword;
42
+ exports.forgetPassword = forgetPassword;
43
+ /**
44
+ * Who signed in last on this machine, so signing in again is one key.
45
+ *
46
+ * The email and the workspace address are kept in a small file. The password
47
+ * is never written to that file or anywhere else this tool controls: on a Mac,
48
+ * and only when the person says yes, it goes into their own login keychain,
49
+ * the same place Safari and git keep theirs, and it is read back from there.
50
+ * Signing out leaves both, so the next `login` is Enter; `login --forget`
51
+ * removes both.
52
+ */
53
+ const node_child_process_1 = require("node:child_process");
54
+ const fs = __importStar(require("node:fs"));
55
+ const path = __importStar(require("node:path"));
56
+ const config_1 = require("./config");
57
+ function file() {
58
+ return path.join((0, config_1.ensureHome)(), 'last-login.json');
59
+ }
60
+ function readLast() {
61
+ try {
62
+ const got = JSON.parse(fs.readFileSync(file(), 'utf8'));
63
+ return got.email && got.workspace ? got : null;
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ }
69
+ function saveLast(last) {
70
+ try {
71
+ fs.writeFileSync(file(), JSON.stringify(last), { mode: 0o600 });
72
+ }
73
+ catch { /* next login asks */ }
74
+ }
75
+ function forgetLast() {
76
+ const last = readLast();
77
+ if (last)
78
+ forgetPassword(last.email, last.workspace);
79
+ try {
80
+ fs.rmSync(file(), { force: true });
81
+ }
82
+ catch { /* nothing to remove */ }
83
+ }
84
+ /** The keychain entry's name: one per workspace, so two workspaces never mix. */
85
+ const service = (workspace) => `CompletionPrism ${workspace}`;
86
+ const keychainAvailable = () => process.platform === 'darwin';
87
+ exports.keychainAvailable = keychainAvailable;
88
+ function savedPassword(email, workspace) {
89
+ if (!(0, exports.keychainAvailable)() || !email || !workspace)
90
+ return null;
91
+ const r = (0, node_child_process_1.spawnSync)('security', ['find-generic-password', '-a', email, '-s', service(workspace), '-w'], { encoding: 'utf8' });
92
+ return r.status === 0 ? r.stdout.replace(/\n$/, '') : null;
93
+ }
94
+ function savePassword(email, workspace, password) {
95
+ if (!(0, exports.keychainAvailable)())
96
+ return false;
97
+ const r = (0, node_child_process_1.spawnSync)('security', ['add-generic-password', '-U', '-a', email, '-s', service(workspace),
98
+ '-l', service(workspace), '-w', password], { stdio: 'ignore' });
99
+ return r.status === 0;
100
+ }
101
+ function forgetPassword(email, workspace) {
102
+ if (!(0, exports.keychainAvailable)())
103
+ return;
104
+ (0, node_child_process_1.spawnSync)('security', ['delete-generic-password', '-a', email, '-s', service(workspace)], { stdio: 'ignore' });
105
+ }
@@ -15,7 +15,7 @@ exports.isPlain = isPlain;
15
15
  * Kept in the tool's own `config.json`, never on the service, because none of
16
16
  * them changes what the agent does: two people sharing a project can each have
17
17
  * their own theme. Settings that do change what the agent does are the
18
- * service's and the project's (manual 2.10), and are not written here.
18
+ * service's and the workspace's (manual 2.10), and are not written here.
19
19
  */
20
20
  const config_1 = require("./config");
21
21
  /** Every machine setting, its allowed values, and its default. `editor` takes
@@ -0,0 +1 @@
1
+ export declare function pick(entries: Array<string | string[]>, hint: string, start?: number, numbered?: boolean): Promise<number | null>;
@@ -0,0 +1,158 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.pick = pick;
40
+ /**
41
+ * Choosing one thing from a list with the arrow keys.
42
+ *
43
+ * Each entry is one or more lines: the first is its name, the rest are quieter
44
+ * detail. Entries are separated by a blank line so each reads as its own item.
45
+ * Up and Down move, Enter picks, Esc (or Ctrl+C) leaves without picking.
46
+ *
47
+ * Only as many entries as fit the window are drawn, and the view follows the
48
+ * marker, so a long list scrolls instead of running off the screen (which would
49
+ * leave the redraw writing over lines it can no longer reach). Every line is
50
+ * cut to the window's width for the same reason: a wrapped line throws the
51
+ * redraw off by one. Off a terminal there is nothing to move through, so it
52
+ * returns null and the caller falls back to its typed form.
53
+ *
54
+ * With `numbered`, every entry carries its number, and typing a number moves
55
+ * the marker straight to it. A list of two hundred sessions is not something
56
+ * anybody should have to arrow through to reach the one they can already see.
57
+ */
58
+ const node_readline_1 = __importDefault(require("node:readline"));
59
+ const ui = __importStar(require("./ui"));
60
+ const CSI = `${String.fromCharCode(27)}[`;
61
+ function pick(entries, hint, start = 0, numbered = false) {
62
+ if (!process.stdin.isTTY || !process.stdout.isTTY || entries.length === 0)
63
+ return Promise.resolve(null);
64
+ const items = entries.map((e) => (Array.isArray(e) ? e : [e]));
65
+ const width = Math.max(20, (process.stdout.columns || 100) - 6);
66
+ const cut = (s) => (s.length > width ? `${s.slice(0, width - 3)}...` : s);
67
+ const tall = Math.max(...items.map((i) => i.length)) + 1;
68
+ // Room for the entries, the hint and a line either side.
69
+ const fits = Math.max(1, Math.floor(((process.stdout.rows || 24) - 4) / tall));
70
+ let at = Math.min(Math.max(0, start), items.length - 1);
71
+ let top = 0;
72
+ let drawn = 0;
73
+ // The number typed so far, when the entries are numbered.
74
+ let typed = '';
75
+ const digits = String(items.length).length;
76
+ const label = (i, line) => (numbered ? `${String(i + 1).padStart(digits)}. ${line}` : line);
77
+ const draw = () => {
78
+ if (at < top)
79
+ top = at;
80
+ if (at >= top + fits)
81
+ top = at - fits + 1;
82
+ if (drawn)
83
+ process.stdout.write(`${CSI}${drawn}A\r`);
84
+ const lines = [];
85
+ if (top > 0)
86
+ lines.push(ui.c.dim(` ${top} more above`));
87
+ items.slice(top, top + fits).forEach((item, k) => {
88
+ const i = top + k;
89
+ const chosen = i === at;
90
+ item.forEach((line, n) => {
91
+ if (n === 0) {
92
+ const name = cut(label(i, line));
93
+ lines.push(chosen ? ` ${ui.c.bold(ui.c.blue(`▸ ${name}`))}` : ` ${ui.c.bold(name)}`);
94
+ }
95
+ else
96
+ lines.push(` ${ui.c.dim(cut(numbered ? `${' '.repeat(digits + 2)}${line}` : line))}`);
97
+ });
98
+ lines.push('');
99
+ });
100
+ const below = items.length - (top + fits);
101
+ if (below > 0)
102
+ lines.push(ui.c.dim(` ${below} more below`));
103
+ lines.push(` ${ui.c.dim(hint)}${typed ? ` ${ui.c.bold(`number ${typed}`)}` : ''}`);
104
+ // The same height every time, so the next redraw lands exactly on this one.
105
+ while (lines.length < drawn)
106
+ lines.push('');
107
+ process.stdout.write(`${lines.map((l) => `${CSI}2K${l}`).join('\n')}\n`);
108
+ drawn = lines.length;
109
+ };
110
+ return new Promise((resolve) => {
111
+ node_readline_1.default.emitKeypressEvents(process.stdin);
112
+ const wasRaw = process.stdin.isRaw;
113
+ process.stdin.setRawMode(true);
114
+ process.stdin.resume();
115
+ const done = (choice) => {
116
+ process.stdin.off('keypress', onKey);
117
+ process.stdin.setRawMode(wasRaw);
118
+ process.stdin.pause();
119
+ resolve(choice);
120
+ };
121
+ const onKey = (s, key) => {
122
+ if (!key && !(numbered && /^[0-9]$/.test(s ?? '')))
123
+ return;
124
+ if (numbered && /^[0-9]$/.test(s ?? '')) {
125
+ // A number past the end starts again from this digit, so a mistyped
126
+ // number is corrected by typing the right one, not by clearing first.
127
+ const next = Number(typed + s) <= items.length ? typed + s : s;
128
+ const n = Number(next);
129
+ typed = n >= 1 && n <= items.length ? next : '';
130
+ if (typed)
131
+ at = n - 1;
132
+ draw();
133
+ }
134
+ else if (numbered && key.name === 'backspace' && typed) {
135
+ typed = typed.slice(0, -1);
136
+ if (typed)
137
+ at = Number(typed) - 1;
138
+ draw();
139
+ }
140
+ else if (key.name === 'up' || key.name === 'k') {
141
+ typed = '';
142
+ at = Math.max(0, at - 1);
143
+ draw();
144
+ }
145
+ else if (key.name === 'down' || key.name === 'j') {
146
+ typed = '';
147
+ at = Math.min(items.length - 1, at + 1);
148
+ draw();
149
+ }
150
+ else if (key.name === 'return')
151
+ done(at);
152
+ else if (key.name === 'escape' || key.name === 'q' || (key.ctrl && key.name === 'c'))
153
+ done(null);
154
+ };
155
+ process.stdin.on('keypress', onKey);
156
+ draw();
157
+ });
158
+ }
@@ -1,5 +1,5 @@
1
1
  import { type GatewayReply } from "./gateway";
2
- import { type ProjectFacts } from "./project";
2
+ import { type ProjectFacts } from "../machine/folder/project";
3
3
  export type Intent = "instructions" | "config" | "memory" | "worktrees" | "hooks";
4
4
  export declare function useSessionFolder(folder: string | null): void;
5
5
  /** The project for this command: the session's folder, the saved default, or
@@ -50,8 +50,8 @@ exports.openEditor = openEditor;
50
50
  const fs = __importStar(require("node:fs"));
51
51
  const path = __importStar(require("node:path"));
52
52
  const gateway_1 = require("./gateway");
53
- const project_1 = require("./project");
54
- const paths_1 = require("../executor/paths");
53
+ const project_1 = require("../machine/folder/project");
54
+ const paths_1 = require("../machine/executor/paths");
55
55
  const ui = __importStar(require("./ui"));
56
56
  const out = (s = "") => process.stdout.write(s + "\n");
57
57
  /** The largest file sent. An instruction or settings file is a page, not a book. */
@@ -104,8 +104,8 @@ async function call(intent, op, payload, facts, quiet = false) {
104
104
  });
105
105
  if (!reply.ok || reply.served === false) {
106
106
  out(ui.fail(reply.served === false
107
- ? "Your workspace is running an older build that does not answer this yet."
108
- : `Your workspace could not be reached: ${reply.message || "no answer"}`));
107
+ ? "Your organization is running an older build that does not answer this yet."
108
+ : `Your organization could not be reached: ${reply.message || "no answer"}`));
109
109
  return null;
110
110
  }
111
111
  if (reply.granted === false) {
@@ -2,6 +2,9 @@
2
2
  export declare function closePrompt(): void;
3
3
  /** Ask a question and wait for the answer. */
4
4
  export declare function ask(prompt: string): Promise<string>;
5
+ /** Ask, telling a cancel (Ctrl+C, input closed) apart from an empty answer:
6
+ * null is "never mind", "" is Enter. */
7
+ export declare function askOrCancel(prompt: string): Promise<string | null>;
5
8
  /**
6
9
  * Ask for a secret, and do not put it on the screen.
7
10
  *
package/out/lib/prompt.js CHANGED
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.closePrompt = closePrompt;
7
7
  exports.ask = ask;
8
+ exports.askOrCancel = askOrCancel;
8
9
  exports.askHidden = askHidden;
9
10
  exports.freeText = freeText;
10
11
  exports.subject = subject;
@@ -24,6 +25,11 @@ function open() {
24
25
  output: process.stdout,
25
26
  terminal: process.stdin.isTTY === true,
26
27
  });
28
+ // Ctrl+C closes it. The next question then opens a new one instead of
29
+ // asking a closed reader, which threw and ended the whole program.
30
+ const mine = reader;
31
+ mine.once('close', () => { if (reader === mine)
32
+ reader = null; });
27
33
  }
28
34
  return reader;
29
35
  }
@@ -51,6 +57,19 @@ function ask(prompt) {
51
57
  });
52
58
  });
53
59
  }
60
+ /** Ask, telling a cancel (Ctrl+C, input closed) apart from an empty answer:
61
+ * null is "never mind", "" is Enter. */
62
+ function askOrCancel(prompt) {
63
+ return new Promise((resolve) => {
64
+ const r = open();
65
+ const closed = () => resolve(null);
66
+ r.once('close', closed);
67
+ r.question(prompt, (answer) => {
68
+ r.off('close', closed);
69
+ resolve(answer.trim());
70
+ });
71
+ });
72
+ }
54
73
  /**
55
74
  * Ask for a secret, and do not put it on the screen.
56
75
  *
@@ -86,6 +105,8 @@ function askHidden(prompt) {
86
105
  process.stdout.write(`\r${prompt}${"\u2022".repeat(inner.line.length)}`);
87
106
  };
88
107
  return new Promise((resolve) => {
108
+ // Ctrl+C at the password is "never mind", answered as nothing, never a crash.
109
+ reader.once('close', () => { inner._writeToOutput = asItWas; resolve(''); });
89
110
  reader.question(prompt, (answer) => {
90
111
  // Put readline back the way it was, whatever happened, or every later
91
112
  // question on this reader would come out as dots.
package/out/lib/queue.js CHANGED
@@ -51,7 +51,7 @@ const path = __importStar(require("path"));
51
51
  const child_process_1 = require("child_process");
52
52
  const gateway_1 = require("./gateway");
53
53
  const config_1 = require("./config");
54
- const self_1 = require("./self");
54
+ const self_1 = require("../machine/runtime/self");
55
55
  async function queue(op, payload = {}) {
56
56
  return (0, gateway_1.ask)({
57
57
  intent: "queue", prompt: "", op,
@@ -1,33 +1,6 @@
1
- export interface Requirement {
2
- /** What it is for, said as the work it makes possible. */
3
- purpose: string;
4
- /** Any one of these being present is enough. */
5
- anyOf: string[];
6
- /** Work that cannot be done without it, for the message. */
7
- without: string;
8
- /** True when the tool cannot function at all without this. */
9
- essential: boolean;
10
- }
11
- export declare const REQUIREMENTS: Requirement[];
12
- /** Is this program on the machine, by name, without running anything of theirs. */
13
- export declare function present(program: string): boolean;
14
- /**
15
- * The one command that installs what is missing, for the machine it is on.
16
- *
17
- * Named per platform rather than guessed: telling somebody on Ubuntu to run a
18
- * Homebrew command is worse than saying nothing, because they now have to work
19
- * out that the instruction was wrong before they can work out what is right.
20
- */
21
- export declare function howToInstall(missing: string[]): string;
22
- export interface Readiness {
23
- ready: boolean;
24
- /** One line per requirement, ready to print. */
25
- lines: string[];
26
- /** The command to run, empty when nothing is missing. */
27
- fix: string;
28
- }
29
- /** What this machine can and cannot do, and how to close the gap. */
30
- export declare function check(): Readiness;
1
+ import { REQUIREMENTS, check, howToInstall, present } from '../machine/runtime/needs';
2
+ export type { Readiness, Requirement } from '../machine/runtime/needs';
3
+ export { REQUIREMENTS, check, howToInstall, present };
31
4
  /** One checked item: its line, whether it is required, and the fix. */
32
5
  export interface Finding {
33
6
  ok: boolean;