@nexrall/code-core 1.4.76 → 1.4.78

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 (143) hide show
  1. package/dist/agent/effortCap.d.ts +24 -0
  2. package/dist/agent/effortCap.d.ts.map +1 -0
  3. package/dist/agent/effortCap.js +66 -0
  4. package/dist/agent/goal.d.ts +55 -0
  5. package/dist/agent/goal.d.ts.map +1 -0
  6. package/dist/agent/goal.js +120 -0
  7. package/dist/agent/handoffBrief.d.ts +82 -0
  8. package/dist/agent/handoffBrief.d.ts.map +1 -0
  9. package/dist/agent/handoffBrief.js +121 -0
  10. package/dist/agent/hooks.d.ts +10 -2
  11. package/dist/agent/hooks.d.ts.map +1 -1
  12. package/dist/agent/hooks.js +9 -3
  13. package/dist/agent/modelCatalogue.d.ts +23 -0
  14. package/dist/agent/modelCatalogue.d.ts.map +1 -1
  15. package/dist/agent/modelCatalogue.js +28 -0
  16. package/dist/agent/outputStyles.d.ts +27 -0
  17. package/dist/agent/outputStyles.d.ts.map +1 -0
  18. package/dist/agent/outputStyles.js +174 -0
  19. package/dist/agent/skills.d.ts.map +1 -1
  20. package/dist/agent/skills.js +21 -1
  21. package/dist/api/billingLines.d.ts +11 -0
  22. package/dist/api/billingLines.d.ts.map +1 -0
  23. package/dist/api/billingLines.js +56 -0
  24. package/dist/api/client.d.ts +2 -0
  25. package/dist/api/client.d.ts.map +1 -1
  26. package/dist/api/scheduleLines.d.ts +53 -0
  27. package/dist/api/scheduleLines.d.ts.map +1 -0
  28. package/dist/api/scheduleLines.js +150 -0
  29. package/dist/context/contextFiles.d.ts +81 -0
  30. package/dist/context/contextFiles.d.ts.map +1 -0
  31. package/dist/context/contextFiles.js +310 -0
  32. package/dist/diff/collect.d.ts +25 -0
  33. package/dist/diff/collect.d.ts.map +1 -0
  34. package/dist/diff/collect.js +169 -0
  35. package/dist/diff/diffView.d.ts +57 -0
  36. package/dist/diff/diffView.d.ts.map +1 -0
  37. package/dist/diff/diffView.js +231 -0
  38. package/dist/diff/reviewQueue.d.ts +53 -0
  39. package/dist/diff/reviewQueue.d.ts.map +1 -0
  40. package/dist/diff/reviewQueue.js +349 -0
  41. package/dist/events/recap.d.ts +54 -0
  42. package/dist/events/recap.d.ts.map +1 -0
  43. package/dist/events/recap.js +161 -0
  44. package/dist/events/sessionEvents.d.ts +54 -0
  45. package/dist/events/sessionEvents.d.ts.map +1 -0
  46. package/dist/events/sessionEvents.js +324 -0
  47. package/dist/events/sessionStats.d.ts +89 -0
  48. package/dist/events/sessionStats.d.ts.map +1 -0
  49. package/dist/events/sessionStats.js +264 -0
  50. package/dist/import/claudeImport.d.ts +44 -0
  51. package/dist/import/claudeImport.d.ts.map +1 -0
  52. package/dist/import/claudeImport.js +337 -0
  53. package/dist/index.d.ts +38 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +38 -0
  56. package/dist/mcp/manager.d.ts +8 -0
  57. package/dist/mcp/manager.d.ts.map +1 -1
  58. package/dist/mcp/manager.js +64 -57
  59. package/dist/permissions/destructiveTokens.d.ts +1 -0
  60. package/dist/permissions/destructiveTokens.d.ts.map +1 -1
  61. package/dist/permissions/destructiveTokens.js +93 -0
  62. package/dist/permissions/rules.d.ts +12 -0
  63. package/dist/permissions/rules.d.ts.map +1 -1
  64. package/dist/permissions/rules.js +89 -7
  65. package/dist/plugins/configStore.d.ts +43 -0
  66. package/dist/plugins/configStore.d.ts.map +1 -0
  67. package/dist/plugins/configStore.js +253 -0
  68. package/dist/plugins/files.d.ts +10 -0
  69. package/dist/plugins/files.d.ts.map +1 -0
  70. package/dist/plugins/files.js +85 -0
  71. package/dist/plugins/index.d.ts +3 -22
  72. package/dist/plugins/index.d.ts.map +1 -1
  73. package/dist/plugins/index.js +30 -40
  74. package/dist/plugins/resolve.d.ts +18 -0
  75. package/dist/plugins/resolve.d.ts.map +1 -0
  76. package/dist/plugins/resolve.js +152 -0
  77. package/dist/plugins/userConfig.d.ts +90 -0
  78. package/dist/plugins/userConfig.d.ts.map +1 -0
  79. package/dist/plugins/userConfig.js +332 -0
  80. package/dist/plugins/validate.d.ts +14 -0
  81. package/dist/plugins/validate.d.ts.map +1 -0
  82. package/dist/plugins/validate.js +222 -0
  83. package/dist/project/projectCommands.d.ts +33 -0
  84. package/dist/project/projectCommands.d.ts.map +1 -0
  85. package/dist/project/projectCommands.js +246 -0
  86. package/dist/search/conversationTurns.d.ts +53 -0
  87. package/dist/search/conversationTurns.d.ts.map +1 -0
  88. package/dist/search/conversationTurns.js +170 -0
  89. package/dist/search/globalSearch.d.ts +144 -0
  90. package/dist/search/globalSearch.d.ts.map +1 -0
  91. package/dist/search/globalSearch.js +350 -0
  92. package/dist/share/sessionChannel.d.ts +136 -0
  93. package/dist/share/sessionChannel.d.ts.map +1 -0
  94. package/dist/share/sessionChannel.js +373 -0
  95. package/dist/share/sessionShare.d.ts +104 -0
  96. package/dist/share/sessionShare.d.ts.map +1 -0
  97. package/dist/share/sessionShare.js +336 -0
  98. package/dist/ui/configCmd.d.ts +48 -0
  99. package/dist/ui/configCmd.d.ts.map +1 -0
  100. package/dist/ui/configCmd.js +146 -0
  101. package/dist/ui/deployPanel.d.ts +91 -0
  102. package/dist/ui/deployPanel.d.ts.map +1 -0
  103. package/dist/ui/deployPanel.js +442 -0
  104. package/dist/ui/mergeAssistant.d.ts +120 -0
  105. package/dist/ui/mergeAssistant.d.ts.map +1 -0
  106. package/dist/ui/mergeAssistant.js +391 -0
  107. package/dist/ui/notify.d.ts +114 -0
  108. package/dist/ui/notify.d.ts.map +1 -0
  109. package/dist/ui/notify.js +235 -0
  110. package/dist/ui/promptSuggest.d.ts +56 -0
  111. package/dist/ui/promptSuggest.d.ts.map +1 -0
  112. package/dist/ui/promptSuggest.js +160 -0
  113. package/dist/ui/taskGraph.d.ts +44 -0
  114. package/dist/ui/taskGraph.d.ts.map +1 -0
  115. package/dist/ui/taskGraph.js +289 -0
  116. package/dist/util/addDir.d.ts +31 -0
  117. package/dist/util/addDir.d.ts.map +1 -0
  118. package/dist/util/addDir.js +124 -0
  119. package/dist/util/batch.d.ts +16 -0
  120. package/dist/util/batch.d.ts.map +1 -0
  121. package/dist/util/batch.js +92 -0
  122. package/dist/util/cellWidth.d.ts +12 -0
  123. package/dist/util/cellWidth.d.ts.map +1 -0
  124. package/dist/util/cellWidth.js +161 -0
  125. package/dist/util/duration.d.ts +8 -0
  126. package/dist/util/duration.d.ts.map +1 -0
  127. package/dist/util/duration.js +50 -0
  128. package/dist/util/loopJobs.d.ts +67 -0
  129. package/dist/util/loopJobs.d.ts.map +1 -0
  130. package/dist/util/loopJobs.js +169 -0
  131. package/dist/util/settingsFile.d.ts +14 -0
  132. package/dist/util/settingsFile.d.ts.map +1 -0
  133. package/dist/util/settingsFile.js +99 -0
  134. package/dist/util/toolLabel.d.ts +2 -0
  135. package/dist/util/toolLabel.d.ts.map +1 -0
  136. package/dist/util/toolLabel.js +17 -0
  137. package/dist/verify/failureTimeline.d.ts +100 -0
  138. package/dist/verify/failureTimeline.d.ts.map +1 -0
  139. package/dist/verify/failureTimeline.js +253 -0
  140. package/dist/verify/verification.d.ts +75 -0
  141. package/dist/verify/verification.d.ts.map +1 -0
  142. package/dist/verify/verification.js +315 -0
  143. package/package.json +5 -4
@@ -0,0 +1,442 @@
1
+ "use strict";
2
+ // The pure half of /deploy + the confirm-word rules (spec §74). Moved to core
3
+ // from the CLI's ui/deployPanel.ts (2026-10-04) so the VS Code panel gates and
4
+ // runs deploys under the same rules; the CLI module keeps the ANSI formatters
5
+ // and re-exports everything else.
6
+ // Spec §74 — deployment UX: what `/deploy` shows, and what it refuses to do.
7
+ //
8
+ // WHY: a deploy is the one command where "which target, which environment, and did anyone
9
+ // check the tests?" all matter before anything runs — and the project already knows the
10
+ // answers (the script in package.json, the vercel.json, the checks this session ran). So
11
+ // `/deploy` reads them out instead of asking the user to remember them.
12
+ //
13
+ // Two rules run through the whole file:
14
+ // • We never GUESS an environment. When the evidence is silent the target is `unknown`,
15
+ // and `unknown` is gated exactly like production — being wrong the other way is a live
16
+ // deploy nobody approved.
17
+ // • An override can raise the gate but never lower it: `/deploy --env staging` on a
18
+ // production target is still a production deploy, and the panel says so.
19
+ //
20
+ // Detection only. The command that comes out goes through runUserCmd, i.e. the same
21
+ // permission rules, --readonly gate, output caps and transcript record as `!cmd`.
22
+ //
23
+ // Pure-ish (fs reads for detection only, no console, no Ink) — see test/deployPanel.test.ts.
24
+ var __importDefault = (this && this.__importDefault) || function (mod) {
25
+ return (mod && mod.__esModule) ? mod : { "default": mod };
26
+ };
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.DEPLOY_USAGE = exports.ENV_LABELS = exports.ENV_RANK = void 0;
29
+ exports.strictestEnv = strictestEnv;
30
+ exports.envFromFlags = envFromFlags;
31
+ exports.inferEnv = inferEnv;
32
+ exports.detectDeployTargets = detectDeployTargets;
33
+ exports.parseDeployArgs = parseDeployArgs;
34
+ exports.deployGate = deployGate;
35
+ exports.confirmPrompt = confirmPrompt;
36
+ exports.confirmMatches = confirmMatches;
37
+ const node_fs_1 = __importDefault(require("node:fs"));
38
+ const node_path_1 = __importDefault(require("node:path"));
39
+ const projectCommands_1 = require("../project/projectCommands");
40
+ /** How strict a gate is. `unknown` sits WITH production, not below it. */
41
+ exports.ENV_RANK = { local: 0, staging: 1, unknown: 2, production: 3 };
42
+ exports.ENV_LABELS = {
43
+ local: 'local',
44
+ staging: 'staging',
45
+ production: 'production',
46
+ unknown: 'unknown',
47
+ };
48
+ /** The strictest of two environments — the rule that stops an override lowering a gate. */
49
+ function strictestEnv(a, b) {
50
+ return exports.ENV_RANK[b] > exports.ENV_RANK[a] ? b : a;
51
+ }
52
+ // ─── environment inference ───────────────────────────────────────────────────
53
+ const PROD_WORDS = new Set(['prod', 'production', 'live', 'release']);
54
+ const STAGING_WORDS = new Set(['staging', 'stage', 'stg', 'preview', 'uat', 'qa', 'canary', 'beta']);
55
+ const LOCAL_WORDS = new Set(['local', 'dev', 'development', 'sandbox', 'emulator']);
56
+ const words = (s) => s
57
+ .toLowerCase()
58
+ .split(/[\s:._/\\-]+/)
59
+ .map((w) => w.replace(/^[^a-z0-9]+/, ''))
60
+ .filter(Boolean);
61
+ /**
62
+ * Every environment word in `text`, strictest first. Used on the script NAME (`deploy:prod`)
63
+ * and on explicit flags only — never on the command body, where `/dev/null` would read as
64
+ * "local" and `--release` on some tool would read as production.
65
+ */
66
+ function envWords(text) {
67
+ const found = [];
68
+ for (const w of words(text)) {
69
+ if (PROD_WORDS.has(w))
70
+ found.push('production');
71
+ else if (STAGING_WORDS.has(w))
72
+ found.push('staging');
73
+ else if (LOCAL_WORDS.has(w))
74
+ found.push('local');
75
+ }
76
+ return found.sort((a, b) => exports.ENV_RANK[b] - exports.ENV_RANK[a]);
77
+ }
78
+ /** `--env production`, `--environment=staging`, `--stage prod` — the tool's own flag. */
79
+ const ENV_FLAG_RE = /--(?:env|environment|stage|target)(?:[=\s]+)([A-Za-z][\w-]*)/g;
80
+ /** Bare `--prod` / `--production`: vercel, netlify and friends spell it with no value. */
81
+ const PROD_FLAG_RE = /(?:^|\s)--(?:prod|production|live)(?![\w-])/;
82
+ function envFromFlags(command) {
83
+ const hits = [];
84
+ for (const m of command.matchAll(ENV_FLAG_RE)) {
85
+ const hit = envWords(m[1])[0];
86
+ if (hit)
87
+ hits.push(hit);
88
+ }
89
+ if (PROD_FLAG_RE.test(command))
90
+ hits.push('production');
91
+ return hits.sort((a, b) => exports.ENV_RANK[b] - exports.ENV_RANK[a])[0];
92
+ }
93
+ /** The environment a target runs against, and the sentence that justifies it. */
94
+ function inferEnv(name, command) {
95
+ const fromName = envWords(name)[0];
96
+ const fromFlag = envFromFlags(command);
97
+ const picked = [fromName, fromFlag].filter(Boolean);
98
+ const env = picked.sort((a, b) => exports.ENV_RANK[b] - exports.ENV_RANK[a])[0];
99
+ if (!env) {
100
+ return { env: 'unknown', why: 'nothing in the name or the command says which environment — gated like production' };
101
+ }
102
+ const where = fromName && fromFlag && fromName !== fromFlag
103
+ ? `the name says ${exports.ENV_LABELS[fromName]}, the flags say ${exports.ENV_LABELS[fromFlag]} — the stricter one wins`
104
+ : fromName
105
+ ? `the name says ${exports.ENV_LABELS[fromName]}`
106
+ : `the command passes ${exports.ENV_LABELS[fromFlag]}`;
107
+ return { env, why: where };
108
+ }
109
+ // ─── detection ───────────────────────────────────────────────────────────────
110
+ /** Script names (or script body words) that mean "this ships something". */
111
+ const DEPLOY_WORDS = new Set(['deploy', 'deployment', 'deploys', 'release', 'publish', 'ship', 'upload', 'promote']);
112
+ /** Tools whose presence in a script body is enough to call it a deploy. */
113
+ const DEPLOY_TOOLS = new Set([
114
+ 'vercel',
115
+ 'netlify',
116
+ 'flyctl',
117
+ 'firebase',
118
+ 'wrangler',
119
+ 'sst',
120
+ 'serverless',
121
+ 'sls',
122
+ 'surge',
123
+ 'gh-pages',
124
+ 'pages',
125
+ 'gcloud',
126
+ 'kubectl',
127
+ 'helm',
128
+ 'terraform',
129
+ 'kamal',
130
+ 'cap',
131
+ 'eb',
132
+ 'ansible-playbook',
133
+ 'sam',
134
+ 'cdk',
135
+ 'pulumi',
136
+ 'ebctl',
137
+ 'now',
138
+ ]);
139
+ const MAX_READ_BYTES = 256 * 1024;
140
+ function readText(file) {
141
+ try {
142
+ const st = node_fs_1.default.statSync(file);
143
+ if (!st.isFile() || st.size > MAX_READ_BYTES)
144
+ return null;
145
+ return node_fs_1.default.readFileSync(file, 'utf-8');
146
+ }
147
+ catch {
148
+ return null;
149
+ }
150
+ }
151
+ function isFile(file) {
152
+ try {
153
+ return node_fs_1.default.statSync(file).isFile();
154
+ }
155
+ catch {
156
+ return false;
157
+ }
158
+ }
159
+ function isDir(file) {
160
+ try {
161
+ return node_fs_1.default.statSync(file).isDirectory();
162
+ }
163
+ catch {
164
+ return false;
165
+ }
166
+ }
167
+ /** package.json scripts that look like deploys, by name or by the tool they call. */
168
+ function scriptsFrom(workDir) {
169
+ const raw = readText(node_path_1.default.join(workDir, 'package.json'));
170
+ if (raw === null)
171
+ return [];
172
+ let json;
173
+ try {
174
+ json = JSON.parse(raw);
175
+ }
176
+ catch {
177
+ return [];
178
+ }
179
+ const scripts = json?.scripts;
180
+ if (!scripts || typeof scripts !== 'object')
181
+ return [];
182
+ const pm = (0, projectCommands_1.packageManager)(workDir);
183
+ const out = [];
184
+ for (const [name, body] of Object.entries(scripts)) {
185
+ if (typeof body !== 'string')
186
+ continue;
187
+ const nameHit = words(name).some((w) => DEPLOY_WORDS.has(w));
188
+ const bodyHit = words(body).some((w) => DEPLOY_TOOLS.has(w));
189
+ if (!nameHit && !bodyHit)
190
+ continue;
191
+ const { env, why } = inferEnv(name, body);
192
+ out.push({ name, command: `${pm} run ${name}`, source: 'package.json', env, why });
193
+ }
194
+ return out;
195
+ }
196
+ /** Column-0 `deploy…:` rules in a Makefile / justfile. */
197
+ function recipeTargets(file, kind) {
198
+ const raw = readText(file);
199
+ if (raw === null)
200
+ return [];
201
+ const out = [];
202
+ const seen = new Set();
203
+ for (const line of raw.split('\n')) {
204
+ const m = kind === 'make'
205
+ ? /^([A-Za-z0-9][\w.-]*)\s*:(?!=)/.exec(line)
206
+ : /^([A-Za-z_][\w-]*)(\s[^:]*)?\s*:(?!=)/.exec(line);
207
+ if (!m)
208
+ continue;
209
+ const name = m[1];
210
+ if (seen.has(name) || !words(name).some((w) => DEPLOY_WORDS.has(w)))
211
+ continue;
212
+ seen.add(name);
213
+ const command = `${kind} ${name}`;
214
+ const { env, why } = inferEnv(name, command);
215
+ out.push({ name, command, source: node_path_1.default.basename(file), env, why });
216
+ }
217
+ return out;
218
+ }
219
+ /**
220
+ * Config-file evidence: the tool is set up here, and its own default is what says which
221
+ * environment a bare invocation hits. Tools that default to a LIVE target are listed as
222
+ * production; the two that default to a preview build are listed as such, next to the
223
+ * `--prod` form a user would otherwise have to remember.
224
+ */
225
+ const CONFIG_TARGETS = [
226
+ {
227
+ files: ['vercel.json', '.vercel/project.json'],
228
+ name: 'vercel',
229
+ command: 'vercel deploy',
230
+ env: 'staging',
231
+ why: 'vercel deploy without --prod builds a preview deployment',
232
+ },
233
+ {
234
+ files: ['vercel.json', '.vercel/project.json'],
235
+ name: 'vercel-prod',
236
+ command: 'vercel deploy --prod',
237
+ env: 'production',
238
+ why: '--prod ships to the live domain',
239
+ },
240
+ {
241
+ files: ['netlify.toml', '.netlify/state.json'],
242
+ name: 'netlify',
243
+ command: 'netlify deploy',
244
+ env: 'staging',
245
+ why: 'netlify deploy without --prod publishes a draft',
246
+ },
247
+ {
248
+ files: ['netlify.toml', '.netlify/state.json'],
249
+ name: 'netlify-prod',
250
+ command: 'netlify deploy --prod',
251
+ env: 'production',
252
+ why: '--prod publishes the live site',
253
+ },
254
+ {
255
+ files: ['fly.toml'],
256
+ name: 'fly',
257
+ command: 'fly deploy',
258
+ env: 'production',
259
+ why: 'fly deploy updates the app\u2019s live machines',
260
+ },
261
+ {
262
+ files: ['firebase.json'],
263
+ name: 'firebase',
264
+ command: 'firebase deploy',
265
+ env: 'production',
266
+ why: 'firebase deploy targets the live project',
267
+ },
268
+ {
269
+ files: ['wrangler.toml', 'wrangler.jsonc', 'wrangler.json'],
270
+ name: 'workers',
271
+ command: 'wrangler deploy',
272
+ env: 'production',
273
+ why: 'wrangler deploy publishes the live Worker',
274
+ },
275
+ {
276
+ files: ['serverless.yml', 'serverless.yaml', 'serverless.ts'],
277
+ name: 'serverless',
278
+ command: 'serverless deploy',
279
+ env: 'unknown',
280
+ why: 'the stage comes from the config or --stage — check before running',
281
+ },
282
+ {
283
+ files: ['sst.config.ts', 'sst.config.js'],
284
+ name: 'sst',
285
+ command: 'sst deploy --stage production',
286
+ env: 'production',
287
+ why: 'the command asks for the production stage explicitly',
288
+ },
289
+ {
290
+ files: ['app.yaml'],
291
+ name: 'appengine',
292
+ command: 'gcloud app deploy',
293
+ env: 'production',
294
+ why: 'gcloud app deploy ships the default service',
295
+ },
296
+ ];
297
+ function configTargets(workDir) {
298
+ const out = [];
299
+ for (const t of CONFIG_TARGETS) {
300
+ if (!t.files.some((f) => isFile(node_path_1.default.join(workDir, f))))
301
+ continue;
302
+ out.push({
303
+ name: t.name,
304
+ command: t.command,
305
+ source: t.files.find((f) => isFile(node_path_1.default.join(workDir, f))),
306
+ env: t.env,
307
+ why: t.why,
308
+ });
309
+ }
310
+ return out;
311
+ }
312
+ /** `kubectl apply` / `terraform apply`: real deploys whose target lives OUTSIDE the repo. */
313
+ function infraTargets(workDir) {
314
+ const out = [];
315
+ for (const dir of ['k8s', 'kubernetes', 'manifests', 'deploy']) {
316
+ const abs = node_path_1.default.join(workDir, dir);
317
+ if (!isDir(abs))
318
+ continue;
319
+ let hasYaml;
320
+ try {
321
+ hasYaml = node_fs_1.default.readdirSync(abs).some((f) => /\.ya?ml$/.test(f));
322
+ }
323
+ catch {
324
+ hasYaml = false;
325
+ }
326
+ if (!hasYaml)
327
+ continue;
328
+ out.push({
329
+ name: dir,
330
+ command: `kubectl apply -f ${dir}/`,
331
+ source: `${dir}/`,
332
+ env: 'unknown',
333
+ why: 'applies to whatever cluster kubectl points at — check `kubectl config current-context` first',
334
+ });
335
+ }
336
+ try {
337
+ const hasTf = node_fs_1.default.readdirSync(workDir).some((f) => f.endsWith('.tf'));
338
+ if (hasTf) {
339
+ out.push({
340
+ name: 'terraform',
341
+ command: 'terraform apply',
342
+ source: '*.tf',
343
+ env: 'unknown',
344
+ why: 'applies to the selected workspace and state — `terraform plan` shows what changes',
345
+ });
346
+ }
347
+ }
348
+ catch {
349
+ /* unreadable dir — nothing to offer */
350
+ }
351
+ return out;
352
+ }
353
+ /**
354
+ * Every deploy target this folder can offer, in a stable order: package.json scripts, then
355
+ * recipes, then tool configs, then infrastructure. Duplicate commands are collapsed (a
356
+ * `deploy` script that merely calls `vercel deploy` is also matched by the vercel config).
357
+ */
358
+ function detectDeployTargets(workDir) {
359
+ const all = [
360
+ ...scriptsFrom(workDir),
361
+ ...recipeTargets(node_path_1.default.join(workDir, 'Makefile'), 'make'),
362
+ ...recipeTargets(node_path_1.default.join(workDir, 'justfile'), 'just'),
363
+ ...configTargets(workDir),
364
+ ...infraTargets(workDir),
365
+ ];
366
+ const byCommand = new Map();
367
+ for (const t of all)
368
+ if (!byCommand.has(t.command))
369
+ byCommand.set(t.command, t);
370
+ return [...byCommand.values()];
371
+ }
372
+ exports.DEPLOY_USAGE = '/deploy [target] [--env local|staging|production] [--yes] [--dry-run] · /deploy list';
373
+ const isEnv = (s) => s === 'local' || s === 'staging' || s === 'production' || s === 'unknown';
374
+ function parseDeployArgs(arg) {
375
+ const parts = arg.trim().split(/\s+/).filter(Boolean);
376
+ const out = { yes: false, dryRun: false, list: false };
377
+ for (let i = 0; i < parts.length; i++) {
378
+ const p = parts[i];
379
+ if (p === 'list' || p === '--list' || p === '-l') {
380
+ out.list = true;
381
+ continue;
382
+ }
383
+ if (p === '--yes' || p === '-y') {
384
+ out.yes = true;
385
+ continue;
386
+ }
387
+ if (p === '--dry-run' || p === '-n') {
388
+ out.dryRun = true;
389
+ continue;
390
+ }
391
+ if (p === '--env' || p === '--environment') {
392
+ const v = parts[i + 1];
393
+ if (!v)
394
+ return { error: '--env needs a value (local, staging, production).' };
395
+ if (!isEnv(v))
396
+ return { error: `Unknown environment "${v}" — use local, staging or production.` };
397
+ out.env = v;
398
+ i++;
399
+ continue;
400
+ }
401
+ if (p.startsWith('--env=')) {
402
+ const v = p.slice(6);
403
+ if (!isEnv(v))
404
+ return { error: `Unknown environment "${v}" — use local, staging or production.` };
405
+ out.env = v;
406
+ continue;
407
+ }
408
+ if (p.startsWith('-'))
409
+ return { error: `Unknown flag "${p}". ${exports.DEPLOY_USAGE}` };
410
+ if (out.name === undefined) {
411
+ out.name = p;
412
+ continue;
413
+ }
414
+ return { error: `Only one target at a time (got "${out.name}" and "${p}"). ${exports.DEPLOY_USAGE}` };
415
+ }
416
+ return out;
417
+ }
418
+ function deployGate(env, opts = {}) {
419
+ if (opts.readonly) {
420
+ return {
421
+ ok: false,
422
+ reason: 'This is a read-only session (--readonly) — a deploy changes things, so /deploy is refused. Restart without --readonly to deploy.',
423
+ };
424
+ }
425
+ if (env === 'production')
426
+ return { ok: true, confirmWord: 'production' };
427
+ if (env === 'unknown')
428
+ return { ok: true, confirmWord: 'deploy' };
429
+ return { ok: true, ask: !opts.yes };
430
+ }
431
+ /** The environment word the user must type, in one sentence they can act on. */
432
+ function confirmPrompt(env) {
433
+ if (env === 'production')
434
+ return 'Type "production" to ship to production';
435
+ return 'Type "deploy" to confirm \u2014 nothing here says which environment this hits';
436
+ }
437
+ /** True when `typed` is the confirmation word `env` asked for. */
438
+ function confirmMatches(env, typed) {
439
+ const want = env === 'production' ? 'production' : 'deploy';
440
+ return typed.trim().toLowerCase() === want;
441
+ }
442
+ //# sourceMappingURL=deployPanel.js.map
@@ -0,0 +1,120 @@
1
+ export type NodeStatus = 'running' | 'completed' | 'failed' | 'stopped';
2
+ export declare const MAX_NODE_FILES = 40;
3
+ export declare const STATUS_GLYPH: Record<NodeStatus | 'idle', string>;
4
+ /** The ref a worktree branch would be merged INTO: the branch name, or HEAD when detached. */
5
+ export declare function headRefFor(dir: string): string;
6
+ /**
7
+ * git with stderr captured, because `merge-tree` says "clean" and "I could not run" with
8
+ * different exit codes and treating the second as the first is exactly the lie this feature
9
+ * exists to avoid. execFileSync throws on a non-zero status, so the fields come off the error.
10
+ */
11
+ export declare const gitRunnerForMerge: GitRunner;
12
+ /** Cap on merge-tree invocations per /merge — one per branch plus every pair, all cheap. */
13
+ export declare const MAX_MERGE_TREE_CHECKS = 12;
14
+ /** Agent rows before the panel stops listing and counts the rest. */
15
+ export declare const MAX_WRITER_ROWS = 8;
16
+ /** The subset of `AgentNode` this module needs. Structural, so chat.ts passes board nodes. */
17
+ export interface WriterNode {
18
+ id: string;
19
+ name: string;
20
+ label: string;
21
+ status: NodeStatus;
22
+ files: string[];
23
+ report?: string;
24
+ }
25
+ export interface NoteWorktree {
26
+ path: string;
27
+ branch?: string;
28
+ }
29
+ export interface WriterInfo {
30
+ id: string;
31
+ name: string;
32
+ label: string;
33
+ status: NodeStatus;
34
+ /** Paths this agent wrote, first-seen order, as the board recorded them. */
35
+ files: string[];
36
+ /** True once the per-node cap is hit: `files.length` is then a floor, not a total. */
37
+ capped: boolean;
38
+ /** Present when the agent kept an isolated worktree (its writes are NOT in this checkout). */
39
+ worktree?: NoteWorktree;
40
+ }
41
+ export declare function parseWorktreeNote(text: string | undefined): NoteWorktree | null;
42
+ /** Agents that wrote at least one file, or that kept a worktree worth reconciling. */
43
+ export declare function writersFrom(nodes: readonly WriterNode[]): WriterInfo[];
44
+ export interface SharedFile {
45
+ path: string;
46
+ /** Writer ids, first-seen order. */
47
+ writers: string[];
48
+ }
49
+ /**
50
+ * Files written by more than one agent in the same checkout.
51
+ *
52
+ * An isolated agent is skipped: its writes landed in its own worktree, so the same relative
53
+ * path is a different file on disk and pairing them here would invent a clash.
54
+ */
55
+ export declare function sharedFiles(writers: readonly WriterInfo[]): SharedFile[];
56
+ /** Sub-agent writes that the main agent also made this session (same checkout only). */
57
+ export declare function alsoByMain(writers: readonly WriterInfo[], mainFiles: readonly string[]): string[];
58
+ export interface MergePair {
59
+ a: string;
60
+ b: string;
61
+ }
62
+ export interface MergeCheck extends MergePair {
63
+ /** Paths git reported as conflicting; empty means the merge is clean. */
64
+ conflicts: string[];
65
+ /** Set when the check could not run (missing branch, old git, …). */
66
+ error?: string;
67
+ /**
68
+ * Both refs resolve to ONE commit, so there is nothing between them: `merge-tree` can only
69
+ * answer "clean" for the trivial reason that the two sides are the same tree. This is the
70
+ * normal state of a worktree agent that has not committed yet, and "clean" must not be read
71
+ * as "these changes merge safely".
72
+ */
73
+ trivial?: boolean;
74
+ }
75
+ export type GitRunner = (cwd: string, args: string[]) => {
76
+ status: number;
77
+ stdout: string;
78
+ stderr: string;
79
+ };
80
+ /** A ref we are willing to hand to git — no leading dash, no whitespace. */
81
+ export declare function looksLikeRef(name: string): boolean;
82
+ /**
83
+ * Which merges to test, most useful first: every branch into the current checkout, then the
84
+ * branches against each other (merging A then B is exactly where two agents' overlapping work
85
+ * surfaces). Capped, and the number left out is reported rather than hidden.
86
+ */
87
+ export declare function mergePairs(writers: readonly WriterInfo[], head: string, max?: number): {
88
+ pairs: MergePair[];
89
+ skipped: number;
90
+ };
91
+ /** Conflicted paths from `merge-tree --write-tree`: the stage rows are the authoritative list. */
92
+ export declare function parseMergeTree(stdout: string): string[];
93
+ /**
94
+ * Run one dry-run merge per pair. Exit 0 = clean, 1 = conflicts (paths on stdout), anything
95
+ * else = the check did not happen, which is reported as such instead of as "no conflicts".
96
+ *
97
+ * Pairs whose two refs resolve to the same commit are marked `trivial` instead of being asked:
98
+ * the answer is fixed before git runs, and it is exactly the case a freshly spawned worktree
99
+ * agent is in (its writes are on disk in the worktree, not in any commit).
100
+ */
101
+ export declare function checkBranchMerges(cwd: string, pairs: readonly MergePair[], run: GitRunner): {
102
+ checks: MergeCheck[];
103
+ unsupported: boolean;
104
+ };
105
+ export interface MergeInput {
106
+ /** Sub-agents that wrote files (or kept a worktree). */
107
+ writers: readonly WriterInfo[];
108
+ /** Sub-agents spawned this session INCLUDING those that wrote nothing. */
109
+ totalAgents: number;
110
+ mainFiles?: readonly string[];
111
+ /** Result of `checkBranchMerges`, if the caller ran it. */
112
+ checks?: readonly MergeCheck[];
113
+ skippedChecks?: number;
114
+ /** git here has no `merge-tree --write-tree`: say so instead of implying "clean". */
115
+ unsupportedGit?: boolean;
116
+ /** Any sub-agent still running — conflicts are only as final as the work is. */
117
+ running?: boolean;
118
+ }
119
+ export declare function formatMergePanel(input: MergeInput, cols: number): string[];
120
+ //# sourceMappingURL=mergeAssistant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeAssistant.d.ts","sourceRoot":"","sources":["../../src/ui/mergeAssistant.ts"],"names":[],"mappings":"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"}