@brainervirus/workit-cli 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1303 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
+
5
+ // src/index.tsx
6
+ import { render } from "ink";
7
+
8
+ // src/steps.tsx
9
+ import { Box, Text, useInput } from "ink";
10
+ import { ConfirmInput, MultiSelect, Select, TextInput } from "@inkjs/ui";
11
+ import { useState } from "react";
12
+
13
+ // node_modules/@brainervirus/workit-core/src/core/config.ts
14
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
15
+ import os from "node:os";
16
+ import path from "node:path";
17
+ var PRESETS = {
18
+ gitflow: { allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"], protected: ["main", "develop", "master", "prod", "production"] },
19
+ "github-flow": { allowed: ["*"], protected: ["main"] },
20
+ "trunk-based": { allowed: ["*"], protected: ["main"] },
21
+ custom: { allowed: [], protected: [] }
22
+ };
23
+ var configDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "workflow-toolkit");
24
+ var LOCALE_RE = /^[a-z]{2,3}(-[A-Z]{2})?$/;
25
+ var DEFAULTS = {
26
+ locale: "en",
27
+ localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
28
+ timezone: "America/Santiago",
29
+ branchPolicy: { preset: "gitflow", allowed: [...PRESETS.gitflow.allowed], protected: [...PRESETS.gitflow.protected] }
30
+ };
31
+ var readSafe = (p) => {
32
+ try {
33
+ return readFileSync(p, "utf8");
34
+ } catch {
35
+ return null;
36
+ }
37
+ };
38
+ var readConfig = () => {
39
+ const raw = readSafe(path.join(configDir(), "config.json"));
40
+ if (!raw)
41
+ return DEFAULTS;
42
+ try {
43
+ const parsed = JSON.parse(raw);
44
+ const locale = LOCALE_RE.test(String(parsed.locale ?? "")) ? parsed.locale : DEFAULTS.locale;
45
+ const preset = parsed.branchPolicy?.preset ?? "gitflow";
46
+ const presetOk = Object.hasOwn(PRESETS, preset) ? preset : "gitflow";
47
+ const presetDefs = PRESETS[presetOk];
48
+ return {
49
+ locale,
50
+ localeOptions: Array.isArray(parsed.localeOptions) ? parsed.localeOptions : DEFAULTS.localeOptions,
51
+ timezone: parsed.timezone ?? DEFAULTS.timezone,
52
+ branchPolicy: {
53
+ preset: presetOk,
54
+ allowed: Array.isArray(parsed.branchPolicy?.allowed) ? parsed.branchPolicy.allowed : presetDefs.allowed,
55
+ protected: Array.isArray(parsed.branchPolicy?.protected) ? parsed.branchPolicy.protected : presetDefs.protected
56
+ }
57
+ };
58
+ } catch {
59
+ return DEFAULTS;
60
+ }
61
+ };
62
+ var writeConfig = (config) => {
63
+ const dir = configDir();
64
+ mkdirSync(dir, { recursive: true });
65
+ writeFileSync(path.join(dir, "config.json"), JSON.stringify(config, null, 2) + `
66
+ `, "utf8");
67
+ };
68
+
69
+ // src/logic.ts
70
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, renameSync, writeFileSync as writeFileSync4 } from "node:fs";
71
+ import { isDeepStrictEqual } from "node:util";
72
+ import path7 from "node:path";
73
+
74
+ // node_modules/@brainervirus/workit-core/src/core/workspaces.ts
75
+ import path2 from "node:path";
76
+ var workspacesPath = () => path2.join(configDir(), "workspaces.json");
77
+
78
+ // node_modules/@brainervirus/workit-core/src/core/gitignore.ts
79
+ import { existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
80
+ import path3 from "node:path";
81
+ var GITIGNORE_ENTRIES = [
82
+ "# workit: SDD working state (never commit)",
83
+ "docs/*/sdd/",
84
+ "",
85
+ "# OS / editor cruft",
86
+ ".DS_Store",
87
+ "Thumbs.db",
88
+ "*.swp",
89
+ ".idea/",
90
+ ".vscode/",
91
+ ".env",
92
+ "node_modules/",
93
+ "dist/",
94
+ "*.log",
95
+ ".cache/"
96
+ ];
97
+ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
98
+ if (!confirmed)
99
+ return { ok: false, error: "confirmed: true required" };
100
+ const file = path3.join(workspaceRoot, ".gitignore");
101
+ const existing = existsSync(file) ? readFileSync2(file, "utf8") : "";
102
+ const existingLines = new Set(existing.split(`
103
+ `).map((l) => l.trim()).filter(Boolean));
104
+ const added = [];
105
+ const append = [];
106
+ for (const entry of GITIGNORE_ENTRIES) {
107
+ if (entry.trim() === "" || existingLines.has(entry.trim()))
108
+ continue;
109
+ append.push(entry);
110
+ added.push(entry);
111
+ }
112
+ if (append.length) {
113
+ const separator = existing && !existing.endsWith(`
114
+ `) ? `
115
+ ` : "";
116
+ writeFileSync2(file, existing + separator + (existing ? `
117
+ ` : "") + append.join(`
118
+ `) + `
119
+ `, "utf8");
120
+ } else if (!existsSync(file)) {
121
+ writeFileSync2(file, "", "utf8");
122
+ }
123
+ return { ok: true, path: file, added };
124
+ };
125
+
126
+ // node_modules/@brainervirus/workit-core/src/core/hygiene.ts
127
+ import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
128
+ import path6 from "node:path";
129
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
130
+
131
+ // node_modules/@brainervirus/workit-core/src/core/changelog.ts
132
+ import fs from "node:fs";
133
+ import path5 from "node:path";
134
+
135
+ // node_modules/@brainervirus/workit-core/src/core/scripts.ts
136
+ import path4 from "node:path";
137
+ import { fileURLToPath } from "node:url";
138
+ var dirname = path4.dirname(fileURLToPath(import.meta.url));
139
+ var PLUGIN_ROOT = path4.resolve(dirname, "../..");
140
+ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
141
+
142
+ // node_modules/@brainervirus/workit-core/src/core/changelog.ts
143
+ function changelogUnreleasedStats(workspace_root, changelogPath = "CHANGELOG.md") {
144
+ const cwd = resolveWorkspaceRoot(workspace_root);
145
+ const abs = path5.isAbsolute(changelogPath) ? changelogPath : path5.join(cwd, changelogPath);
146
+ if (!fs.existsSync(abs))
147
+ return { exists: false };
148
+ const text = fs.readFileSync(abs, "utf8");
149
+ const m = text.match(/##\s+\[Unreleased\]([\s\S]*?)(?=\n##\s+\[|$)/i);
150
+ if (!m)
151
+ return { exists: true, has_unreleased: false };
152
+ const body = m[1];
153
+ const headings = [...body.matchAll(/^###\s+(\w+)\s*$/gim)].map((x) => x[1]);
154
+ const dupes = headings.filter((h, i) => headings.slice(0, i).some((p) => p.toLowerCase() === h.toLowerCase()));
155
+ return {
156
+ exists: true,
157
+ has_unreleased: true,
158
+ category_headings: headings,
159
+ duplicate_category_headings: dupes,
160
+ needs_normalize: dupes.length > 0
161
+ };
162
+ }
163
+
164
+ // node_modules/@brainervirus/workit-core/src/core/hygiene.ts
165
+ var repoRoot = path6.resolve(path6.dirname(fileURLToPath2(import.meta.url)), "../..");
166
+ var templatesDir = () => path6.join(repoRoot, "templates", "hygiene");
167
+ var packageJson = (root) => {
168
+ if (!existsSync2(path6.join(root, "package.json")))
169
+ return null;
170
+ try {
171
+ return JSON.parse(readFileSync3(path6.join(root, "package.json"), "utf8"));
172
+ } catch {
173
+ return null;
174
+ }
175
+ };
176
+ var isOpenSource = (root) => {
177
+ if (existsSync2(path6.join(root, "LICENSE")))
178
+ return true;
179
+ const pkg = packageJson(root);
180
+ if (pkg && !pkg.private)
181
+ return true;
182
+ return path6.basename(root) === "workflow-toolkit";
183
+ };
184
+ var licenseHolder = (root) => {
185
+ const pkg = packageJson(root);
186
+ if (pkg) {
187
+ const author = pkg.author;
188
+ if (typeof author === "string" && author.trim())
189
+ return author.trim();
190
+ if (author && typeof author === "object") {
191
+ const name = author.name;
192
+ if (typeof name === "string" && name.trim())
193
+ return name.trim();
194
+ }
195
+ }
196
+ return "";
197
+ };
198
+ var hygieneFiles = (root) => {
199
+ const openSource = isOpenSource(root);
200
+ const state = {};
201
+ for (const file of ["CHANGELOG.md", "README.md", ".editorconfig", ".gitattributes", "LICENSE", "CONTRIBUTING.md"]) {
202
+ if (file === "LICENSE" || file === "CONTRIBUTING.md") {
203
+ state[file] = openSource ? existsSync2(path6.join(root, file)) ? "ok" : "missing" : "skip";
204
+ continue;
205
+ }
206
+ if (!existsSync2(path6.join(root, file))) {
207
+ state[file] = "missing";
208
+ continue;
209
+ }
210
+ if (file === "CHANGELOG.md") {
211
+ const stats = changelogUnreleasedStats(root);
212
+ state[file] = stats.exists && stats.has_unreleased ? "ok" : "invalid";
213
+ continue;
214
+ }
215
+ state[file] = "ok";
216
+ }
217
+ return { state, openSource };
218
+ };
219
+ var ensureHygieneFiles = (root, opts) => {
220
+ if (!opts.confirmed)
221
+ return { ok: false, error: "confirmed: true required" };
222
+ const files = ["CHANGELOG.md", "README.md", ".editorconfig", ".gitattributes"];
223
+ if (opts.includeOpenSource)
224
+ files.push("LICENSE", "CONTRIBUTING.md");
225
+ const created = [];
226
+ const tplDir = templatesDir();
227
+ for (const file of files) {
228
+ if (existsSync2(path6.join(root, file)))
229
+ continue;
230
+ const tpl = path6.join(tplDir, file);
231
+ if (!existsSync2(tpl))
232
+ continue;
233
+ const content = readFileSync3(tpl, "utf8").replace(/<PROJECT>/g, path6.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
234
+ writeFileSync3(path6.join(root, file), content, "utf8");
235
+ created.push(file);
236
+ }
237
+ return { ok: true, created };
238
+ };
239
+
240
+ // src/logic.ts
241
+ var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
242
+ function validateLocale(locale) {
243
+ if (!LOCALE_RE.test(locale)) {
244
+ return `invalid locale "${locale}" — expected BCP-47 like en or es-CL`;
245
+ }
246
+ return null;
247
+ }
248
+ var KNOWN_TIMEZONES = typeof Intl.supportedValuesOf === "function" ? Intl.supportedValuesOf("timeZone") : null;
249
+ function validateTimezone(timezone) {
250
+ const tz = timezone.trim();
251
+ if (!tz)
252
+ return "timezone is required";
253
+ if (KNOWN_TIMEZONES && !KNOWN_TIMEZONES.includes(tz)) {
254
+ return `unknown timezone "${tz}" — check the IANA name (e.g. America/Santiago)`;
255
+ }
256
+ return null;
257
+ }
258
+ function validateBaseUrl(url) {
259
+ let parsed;
260
+ try {
261
+ parsed = new URL(url.trim());
262
+ } catch {
263
+ return `invalid URL "${url}"`;
264
+ }
265
+ if (parsed.protocol !== "https:")
266
+ return "base URL must use https";
267
+ return null;
268
+ }
269
+ function parseList(raw) {
270
+ return raw.split(",").map((s) => s.trim()).filter(Boolean);
271
+ }
272
+ function collectConfigValues(input, current) {
273
+ const preset = input.preset ?? current.branchPolicy.preset;
274
+ const presetDefs = PRESETS[preset];
275
+ return {
276
+ locale: input.locale ?? current.locale,
277
+ localeOptions: current.localeOptions,
278
+ timezone: input.timezone ?? current.timezone,
279
+ branchPolicy: {
280
+ preset,
281
+ allowed: preset === "custom" ? input.allowed ?? current.branchPolicy.allowed : [...presetDefs.allowed],
282
+ protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...presetDefs.protected]
283
+ }
284
+ };
285
+ }
286
+ function runProjectSetup(root, opts = {}) {
287
+ const openSource = opts.includeOpenSource ?? hygieneFiles(root).openSource;
288
+ const gitignore = ensureProjectGitignore(root, true);
289
+ const hygiene = ensureHygieneFiles(root, { confirmed: true, includeOpenSource: openSource });
290
+ const created = [
291
+ ...gitignore.ok ? gitignore.added : [],
292
+ ...hygiene.ok ? hygiene.created : []
293
+ ];
294
+ return { gitignore, hygiene, openSource, created };
295
+ }
296
+ var DEFAULT_BASE_URL = "https://enghouseamg.youtrack.cloud";
297
+ function scaffoldYouTrack(dir, baseUrl, opts = {}) {
298
+ mkdirSync2(dir, { recursive: true });
299
+ const youtrackJson = path7.join(dir, "youtrack.json");
300
+ const tokenPath = path7.join(dir, "youtrack.token");
301
+ const config = {
302
+ baseUrl,
303
+ tokenFile: tokenPath,
304
+ timezone: opts.timezone ?? "America/Santiago",
305
+ locale: opts.locale ?? "es-CL",
306
+ defaultMention: "Alejandra.Flores",
307
+ greetings: { morning: "buenos días", afternoon: "buenas tardes" },
308
+ greetingCutoff: "12:00",
309
+ meetingIssue: "IRPT-12",
310
+ meetingIssues: {
311
+ general: {
312
+ issue: "IRPT-12",
313
+ label: "General meetings (Reuniones internas Team IRP)",
314
+ workItemText: "Reuniones"
315
+ },
316
+ web: {
317
+ issue: "NSXFT-21",
318
+ label: "Web meetings",
319
+ workItemText: "Reuniones web",
320
+ url: "https://enghouseamg.youtrack.cloud/projects/NSXFT/issues/NSXFT-21"
321
+ }
322
+ },
323
+ commentHeader: "# Actualización",
324
+ attachmentsHeaderImages: "## Adjunto capturas",
325
+ attachmentsHeaderFiles: "## Archivos adjuntos",
326
+ attachmentsHeaderMixed: "## Adjuntos",
327
+ tokenDefaults: {
328
+ name: "workit",
329
+ description: "OpenCode workit — /wk-issue-update and /wk-meetings",
330
+ scopes: ["YouTrack"],
331
+ profileTab: "account-security"
332
+ }
333
+ };
334
+ writeFileSync4(youtrackJson, JSON.stringify(config, null, 2) + `
335
+ `, "utf8");
336
+ writeFileSync4(tokenPath, TOKEN_PLACEHOLDER + `
337
+ `, { encoding: "utf8", mode: 384 });
338
+ const base = baseUrl.replace(/\/+$/, "");
339
+ return {
340
+ youtrackJson,
341
+ tokenPath,
342
+ tokenCreateUrl: `${base}/users/me?tab=account-security`
343
+ };
344
+ }
345
+ var TOKEN_DEFAULTS = {
346
+ name: "workit",
347
+ description: "OpenCode workit — /wk-pr and glab/gh",
348
+ gitlabScopes: ["api"],
349
+ githubPermissions: { pull_requests: "write", contents: "write", metadata: "read" },
350
+ githubClassicScopes: ["repo"]
351
+ };
352
+ function scaffoldVcs(dir, provider) {
353
+ mkdirSync2(dir, { recursive: true });
354
+ const vcsJson = path7.join(dir, "vcs.json");
355
+ const gitlabToken = path7.join(dir, "gitlab.token");
356
+ const githubToken = path7.join(dir, "github.token");
357
+ const config = {
358
+ provider,
359
+ defaultTargetBranch: "develop",
360
+ gitlab: { host: "gitlab.com", apiUrl: "https://gitlab.com/api/v4", tokenFile: gitlabToken },
361
+ github: { host: "github.com", tokenFile: githubToken },
362
+ pr: { squashOnMerge: true, removeSourceBranch: true, pushBranch: true, confirmSkip: true },
363
+ tokenDefaults: TOKEN_DEFAULTS
364
+ };
365
+ writeFileSync4(vcsJson, JSON.stringify(config, null, 2) + `
366
+ `, "utf8");
367
+ for (const p of [gitlabToken, githubToken]) {
368
+ writeFileSync4(p, TOKEN_PLACEHOLDER + `
369
+ `, { encoding: "utf8", mode: 384 });
370
+ }
371
+ const gitlabUrl = `https://gitlab.com/-/user_settings/personal_access_tokens?${new URLSearchParams({
372
+ name: TOKEN_DEFAULTS.name,
373
+ description: TOKEN_DEFAULTS.description,
374
+ scopes: "api"
375
+ })}`;
376
+ const githubUrl = `https://github.com/settings/personal-access-tokens/new?${new URLSearchParams({
377
+ name: TOKEN_DEFAULTS.name,
378
+ description: TOKEN_DEFAULTS.description,
379
+ pull_requests: "write",
380
+ contents: "write",
381
+ metadata: "read"
382
+ })}`;
383
+ return {
384
+ vcsJson,
385
+ tokenPaths: [gitlabToken, githubToken],
386
+ activeTokenPath: provider === "gitlab" ? gitlabToken : githubToken,
387
+ tokenCreateUrl: provider === "gitlab" ? gitlabUrl : githubUrl,
388
+ provider
389
+ };
390
+ }
391
+ function shouldWriteWorkspaces(loaded, current) {
392
+ return !isDeepStrictEqual(loaded, current);
393
+ }
394
+ function loadWorkspaces() {
395
+ let raw;
396
+ try {
397
+ raw = readFileSync4(workspacesPath(), "utf8");
398
+ } catch {
399
+ return [];
400
+ }
401
+ let parsed;
402
+ try {
403
+ parsed = JSON.parse(raw);
404
+ } catch {
405
+ return [];
406
+ }
407
+ if (!parsed || typeof parsed !== "object")
408
+ return [];
409
+ const list = parsed.workspaces;
410
+ return Array.isArray(list) ? list : [];
411
+ }
412
+ var VALID_PROVIDERS = ["gitlab", "github"];
413
+ function writeWorkspaces(entries) {
414
+ const file = workspacesPath();
415
+ for (const [i, entry] of entries.entries()) {
416
+ if (!entry || typeof entry !== "object") {
417
+ return { ok: false, error: `workspace #${i + 1} is null`, path: file };
418
+ }
419
+ if (typeof entry.name !== "string" || !entry.name.trim()) {
420
+ return { ok: false, error: `workspace #${i + 1} missing a name`, path: file };
421
+ }
422
+ if (typeof entry.glob !== "string" || !entry.glob.trim()) {
423
+ return { ok: false, error: `workspace "${entry.name}" missing a glob`, path: file };
424
+ }
425
+ const provider = entry.vcs?.provider;
426
+ if (provider && !VALID_PROVIDERS.includes(provider)) {
427
+ return { ok: false, error: `workspace "${entry.name}" has unknown provider "${provider}"`, path: file };
428
+ }
429
+ if (entry.youtrack && provider !== "gitlab") {
430
+ return { ok: false, error: `workspace "${entry.name}" links YouTrack issues but provider is "${provider ?? "unset"}" — youtrack linking requires the gitlab provider`, path: file };
431
+ }
432
+ if (entry.issues && provider !== "github") {
433
+ return { ok: false, error: `workspace "${entry.name}" links GitHub issues but provider is "${provider ?? "unset"}" — github issues require the github provider`, path: file };
434
+ }
435
+ }
436
+ mkdirSync2(path7.dirname(file), { recursive: true });
437
+ const tmp = `${file}.${process.pid}.tmp`;
438
+ try {
439
+ writeFileSync4(tmp, JSON.stringify({ workspaces: entries }, null, 2) + `
440
+ `, "utf8");
441
+ renameSync(tmp, file);
442
+ } catch (err) {
443
+ return { ok: false, error: `failed to write ${file}: ${err.message}`, path: file };
444
+ }
445
+ return { ok: true, path: file };
446
+ }
447
+
448
+ // src/steps.tsx
449
+ import { jsxDEV, Fragment } from "react/jsx-dev-runtime";
450
+ var PLATFORMS = [
451
+ { label: "OpenCode", value: "opencode" },
452
+ { label: "Cursor", value: "cursor" }
453
+ ];
454
+ var BRANCH_PRESETS = [
455
+ { label: "GitFlow", value: "gitflow" },
456
+ { label: "GitHub Flow", value: "github-flow" },
457
+ { label: "Trunk-based", value: "trunk-based" },
458
+ { label: "Custom", value: "custom" }
459
+ ];
460
+ var VCS_PROVIDERS = [
461
+ { label: "GitLab", value: "gitlab" },
462
+ { label: "GitHub", value: "github" }
463
+ ];
464
+ function continueLabel() {
465
+ return " y to continue · n to stay · Esc to exit";
466
+ }
467
+ function Wizard({ onExit }) {
468
+ const [step, setStep] = useState(0);
469
+ const [results, setResults] = useState(() => ({
470
+ platforms: [],
471
+ config: readConfig(),
472
+ workspaces: [],
473
+ youtrack: null,
474
+ vcs: null,
475
+ project: null
476
+ }));
477
+ useInput((input, key) => {
478
+ if (key.escape || key.ctrl && input.toLowerCase() === "c")
479
+ onExit();
480
+ });
481
+ const advance = () => setStep((s) => Math.min(s + 1, 6));
482
+ const props = { results, setResults, onDone: advance, onExit };
483
+ const Step = step === 6 ? SummaryStep : [PlatformStep, ConfigStep, YouTrackStep, VcsStep, WorkspacesStep, ProjectStep, SummaryStep][step];
484
+ return /* @__PURE__ */ jsxDEV(Box, {
485
+ flexDirection: "column",
486
+ gap: 1,
487
+ children: [
488
+ /* @__PURE__ */ jsxDEV(Text, {
489
+ bold: true,
490
+ color: "cyan",
491
+ children: "flowkit — workflow rails for agentic coding"
492
+ }, undefined, false, undefined, this),
493
+ /* @__PURE__ */ jsxDEV(Step, {
494
+ ...props
495
+ }, undefined, false, undefined, this)
496
+ ]
497
+ }, undefined, true, undefined, this);
498
+ }
499
+ function PlatformStep({ results, setResults, onDone }) {
500
+ const [error, setError] = useState(false);
501
+ return /* @__PURE__ */ jsxDEV(Box, {
502
+ flexDirection: "column",
503
+ gap: 1,
504
+ children: [
505
+ /* @__PURE__ */ jsxDEV(Text, {
506
+ bold: true,
507
+ children: "Step 1 — Platforms"
508
+ }, undefined, false, undefined, this),
509
+ /* @__PURE__ */ jsxDEV(Text, {
510
+ dimColor: true,
511
+ children: "Select the tools to configure (space to toggle):"
512
+ }, undefined, false, undefined, this),
513
+ /* @__PURE__ */ jsxDEV(MultiSelect, {
514
+ options: PLATFORMS,
515
+ defaultValue: results.platforms,
516
+ onSubmit: (values) => {
517
+ if (values.length === 0) {
518
+ setError(true);
519
+ return;
520
+ }
521
+ setResults((r) => ({ ...r, platforms: values }));
522
+ onDone();
523
+ }
524
+ }, undefined, false, undefined, this),
525
+ error && /* @__PURE__ */ jsxDEV(Text, {
526
+ color: "red",
527
+ children: "Select at least one platform to continue."
528
+ }, undefined, false, undefined, this),
529
+ /* @__PURE__ */ jsxDEV(Text, {
530
+ dimColor: true,
531
+ children: "Enter to continue · Esc to exit"
532
+ }, undefined, false, undefined, this)
533
+ ]
534
+ }, undefined, true, undefined, this);
535
+ }
536
+ function ConfigStep({ results, setResults, onDone }) {
537
+ const current = results.config;
538
+ const [locale, setLocale] = useState(current.locale);
539
+ const [localeOk, setLocaleOk] = useState(validateLocale(current.locale) === null);
540
+ const [localeError, setLocaleError] = useState(null);
541
+ const [timezone, setTimezone] = useState(current.timezone);
542
+ const [tzOk, setTzOk] = useState(validateTimezone(current.timezone) === null);
543
+ const [tzError, setTzError] = useState(null);
544
+ const [preset, setPreset] = useState(current.branchPolicy.preset);
545
+ const [allowed, setAllowed] = useState(current.branchPolicy.allowed.join(", "));
546
+ const [protectedNames, setProtectedNames] = useState(current.branchPolicy.protected.join(", "));
547
+ const save = () => {
548
+ const next = collectConfigValues({
549
+ locale: localeOk ? locale : undefined,
550
+ timezone: tzOk ? timezone : undefined,
551
+ preset,
552
+ allowed: preset === "custom" ? parseList(allowed) : undefined,
553
+ protectedNames: preset === "custom" ? parseList(protectedNames) : undefined
554
+ }, current);
555
+ writeConfig(next);
556
+ setResults((r) => ({ ...r, config: next }));
557
+ onDone();
558
+ };
559
+ return /* @__PURE__ */ jsxDEV(Box, {
560
+ flexDirection: "column",
561
+ gap: 1,
562
+ children: [
563
+ /* @__PURE__ */ jsxDEV(Text, {
564
+ bold: true,
565
+ children: "Step 2 — Global config"
566
+ }, undefined, false, undefined, this),
567
+ /* @__PURE__ */ jsxDEV(Text, {
568
+ dimColor: true,
569
+ children: "Locale (BCP-47, e.g. en or es-CL):"
570
+ }, undefined, false, undefined, this),
571
+ /* @__PURE__ */ jsxDEV(TextInput, {
572
+ defaultValue: locale,
573
+ onSubmit: (v) => {
574
+ const err = validateLocale(v);
575
+ if (err) {
576
+ setLocaleError(err);
577
+ setLocaleOk(false);
578
+ } else {
579
+ setLocaleError(null);
580
+ setLocale(v);
581
+ setLocaleOk(true);
582
+ }
583
+ }
584
+ }, undefined, false, undefined, this),
585
+ localeError && /* @__PURE__ */ jsxDEV(Text, {
586
+ color: "red",
587
+ children: localeError
588
+ }, undefined, false, undefined, this),
589
+ /* @__PURE__ */ jsxDEV(Text, {
590
+ dimColor: true,
591
+ children: "Timezone (IANA name, e.g. America/Santiago):"
592
+ }, undefined, false, undefined, this),
593
+ /* @__PURE__ */ jsxDEV(TextInput, {
594
+ defaultValue: timezone,
595
+ onSubmit: (v) => {
596
+ const err = validateTimezone(v);
597
+ if (err) {
598
+ setTzError(err);
599
+ setTzOk(false);
600
+ } else {
601
+ setTzError(null);
602
+ setTimezone(v);
603
+ setTzOk(true);
604
+ }
605
+ }
606
+ }, undefined, false, undefined, this),
607
+ tzError && /* @__PURE__ */ jsxDEV(Text, {
608
+ color: "red",
609
+ children: tzError
610
+ }, undefined, false, undefined, this),
611
+ /* @__PURE__ */ jsxDEV(Text, {
612
+ dimColor: true,
613
+ children: "Branch policy preset:"
614
+ }, undefined, false, undefined, this),
615
+ /* @__PURE__ */ jsxDEV(Select, {
616
+ options: BRANCH_PRESETS,
617
+ defaultValue: preset,
618
+ onChange: (v) => setPreset(v)
619
+ }, undefined, false, undefined, this),
620
+ preset === "custom" && /* @__PURE__ */ jsxDEV(Fragment, {
621
+ children: [
622
+ /* @__PURE__ */ jsxDEV(Text, {
623
+ dimColor: true,
624
+ children: "Allowed branch patterns (comma-separated):"
625
+ }, undefined, false, undefined, this),
626
+ /* @__PURE__ */ jsxDEV(TextInput, {
627
+ defaultValue: allowed,
628
+ onChange: setAllowed
629
+ }, undefined, false, undefined, this),
630
+ /* @__PURE__ */ jsxDEV(Text, {
631
+ dimColor: true,
632
+ children: "Protected branch names (comma-separated):"
633
+ }, undefined, false, undefined, this),
634
+ /* @__PURE__ */ jsxDEV(TextInput, {
635
+ defaultValue: protectedNames,
636
+ onChange: setProtectedNames
637
+ }, undefined, false, undefined, this)
638
+ ]
639
+ }, undefined, true, undefined, this),
640
+ /* @__PURE__ */ jsxDEV(ConfirmInput, {
641
+ isDisabled: !localeOk || !tzOk,
642
+ defaultChoice: "confirm",
643
+ submitOnEnter: false,
644
+ onConfirm: save,
645
+ onCancel: () => {}
646
+ }, undefined, false, undefined, this),
647
+ /* @__PURE__ */ jsxDEV(Text, {
648
+ dimColor: true,
649
+ children: continueLabel()
650
+ }, undefined, false, undefined, this)
651
+ ]
652
+ }, undefined, true, undefined, this);
653
+ }
654
+ function YouTrackStep({ results, setResults, onDone }) {
655
+ const [baseUrl, setBaseUrl] = useState(DEFAULT_BASE_URL);
656
+ const [urlError, setUrlError] = useState(null);
657
+ const [scaffold, setScaffold] = useState(results.youtrack);
658
+ return /* @__PURE__ */ jsxDEV(Box, {
659
+ flexDirection: "column",
660
+ gap: 1,
661
+ children: [
662
+ /* @__PURE__ */ jsxDEV(Text, {
663
+ bold: true,
664
+ children: "Step 3 — YouTrack"
665
+ }, undefined, false, undefined, this),
666
+ /* @__PURE__ */ jsxDEV(Text, {
667
+ dimColor: true,
668
+ children: "Base URL (https):"
669
+ }, undefined, false, undefined, this),
670
+ /* @__PURE__ */ jsxDEV(TextInput, {
671
+ defaultValue: baseUrl,
672
+ isDisabled: scaffold !== null,
673
+ onSubmit: (v) => {
674
+ const err = validateBaseUrl(v);
675
+ if (err) {
676
+ setUrlError(err);
677
+ return;
678
+ }
679
+ setUrlError(null);
680
+ setBaseUrl(v);
681
+ const s = scaffoldYouTrack(configDir(), v, {
682
+ locale: results.config.locale,
683
+ timezone: results.config.timezone
684
+ });
685
+ setScaffold(s);
686
+ setResults((r) => ({ ...r, youtrack: s }));
687
+ }
688
+ }, undefined, false, undefined, this),
689
+ urlError && /* @__PURE__ */ jsxDEV(Text, {
690
+ color: "red",
691
+ children: urlError
692
+ }, undefined, false, undefined, this),
693
+ scaffold ? /* @__PURE__ */ jsxDEV(Fragment, {
694
+ children: [
695
+ /* @__PURE__ */ jsxDEV(Box, {
696
+ flexDirection: "column",
697
+ gap: 0,
698
+ children: [
699
+ /* @__PURE__ */ jsxDEV(Text, {
700
+ color: "green",
701
+ children: [
702
+ "Scaffolded ",
703
+ scaffold.youtrackJson
704
+ ]
705
+ }, undefined, true, undefined, this),
706
+ /* @__PURE__ */ jsxDEV(Text, {
707
+ children: [
708
+ "Token placeholder: ",
709
+ scaffold.tokenPath
710
+ ]
711
+ }, undefined, true, undefined, this),
712
+ /* @__PURE__ */ jsxDEV(Text, {
713
+ children: [
714
+ "Create token: ",
715
+ scaffold.tokenCreateUrl
716
+ ]
717
+ }, undefined, true, undefined, this)
718
+ ]
719
+ }, undefined, true, undefined, this),
720
+ /* @__PURE__ */ jsxDEV(ConfirmInput, {
721
+ defaultChoice: "confirm",
722
+ submitOnEnter: false,
723
+ onConfirm: onDone,
724
+ onCancel: () => {}
725
+ }, undefined, false, undefined, this),
726
+ /* @__PURE__ */ jsxDEV(Text, {
727
+ dimColor: true,
728
+ children: continueLabel()
729
+ }, undefined, false, undefined, this)
730
+ ]
731
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV(Text, {
732
+ dimColor: true,
733
+ children: "Enter to submit the URL — then y to continue"
734
+ }, undefined, false, undefined, this)
735
+ ]
736
+ }, undefined, true, undefined, this);
737
+ }
738
+ function VcsStep({ results, setResults, onDone }) {
739
+ const [provider, setProvider] = useState(results.vcs?.provider ?? "gitlab");
740
+ const [scaffold, setScaffold] = useState(results.vcs);
741
+ const apply = (p) => {
742
+ const s = scaffoldVcs(configDir(), p);
743
+ setScaffold(s);
744
+ setResults((r) => ({ ...r, vcs: s }));
745
+ };
746
+ useInput((_input, key) => {
747
+ if (key.return)
748
+ apply(provider);
749
+ });
750
+ return /* @__PURE__ */ jsxDEV(Box, {
751
+ flexDirection: "column",
752
+ gap: 1,
753
+ children: [
754
+ /* @__PURE__ */ jsxDEV(Text, {
755
+ bold: true,
756
+ children: "Step 4 — Version control"
757
+ }, undefined, false, undefined, this),
758
+ /* @__PURE__ */ jsxDEV(Text, {
759
+ dimColor: true,
760
+ children: "Provider:"
761
+ }, undefined, false, undefined, this),
762
+ /* @__PURE__ */ jsxDEV(Select, {
763
+ options: VCS_PROVIDERS,
764
+ defaultValue: provider,
765
+ onChange: (v) => {
766
+ const p = v;
767
+ setProvider(p);
768
+ apply(p);
769
+ }
770
+ }, undefined, false, undefined, this),
771
+ scaffold ? /* @__PURE__ */ jsxDEV(Fragment, {
772
+ children: [
773
+ /* @__PURE__ */ jsxDEV(Box, {
774
+ flexDirection: "column",
775
+ gap: 0,
776
+ children: [
777
+ /* @__PURE__ */ jsxDEV(Text, {
778
+ color: "green",
779
+ children: [
780
+ "Scaffolded ",
781
+ scaffold.vcsJson,
782
+ " (provider: ",
783
+ scaffold.provider,
784
+ ")"
785
+ ]
786
+ }, undefined, true, undefined, this),
787
+ /* @__PURE__ */ jsxDEV(Text, {
788
+ children: [
789
+ "Token placeholder: ",
790
+ scaffold.activeTokenPath
791
+ ]
792
+ }, undefined, true, undefined, this),
793
+ /* @__PURE__ */ jsxDEV(Text, {
794
+ children: [
795
+ "Create token: ",
796
+ scaffold.tokenCreateUrl
797
+ ]
798
+ }, undefined, true, undefined, this)
799
+ ]
800
+ }, undefined, true, undefined, this),
801
+ /* @__PURE__ */ jsxDEV(ConfirmInput, {
802
+ defaultChoice: "confirm",
803
+ submitOnEnter: false,
804
+ onConfirm: onDone,
805
+ onCancel: () => {}
806
+ }, undefined, false, undefined, this),
807
+ /* @__PURE__ */ jsxDEV(Text, {
808
+ dimColor: true,
809
+ children: continueLabel()
810
+ }, undefined, false, undefined, this)
811
+ ]
812
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV(Text, {
813
+ dimColor: true,
814
+ children: "Enter to confirm the provider — then y to continue"
815
+ }, undefined, false, undefined, this)
816
+ ]
817
+ }, undefined, true, undefined, this);
818
+ }
819
+ var WS_LINKING = {
820
+ gitlab: [
821
+ { label: "YouTrack", value: "youtrack" },
822
+ { label: "None", value: "none" }
823
+ ],
824
+ github: [
825
+ { label: "GitHub issues", value: "github" },
826
+ { label: "None", value: "none" }
827
+ ]
828
+ };
829
+ function WorkspacesStep({ setResults, onDone }) {
830
+ const [loaded] = useState(() => loadWorkspaces());
831
+ const [entries, setEntries] = useState(loaded);
832
+ const [mode, setMode] = useState("list");
833
+ const [draft, setDraft] = useState({ name: "", glob: "", provider: "gitlab", branch: "develop", linking: "none" });
834
+ const [fieldError, setFieldError] = useState(null);
835
+ const [writeError, setWriteError] = useState(null);
836
+ const resetDraft = () => setDraft({ name: "", glob: "", provider: "gitlab", branch: "develop", linking: "none" });
837
+ const finish = () => {
838
+ setResults((r) => ({ ...r, workspaces: entries }));
839
+ if (!shouldWriteWorkspaces(loaded, entries)) {
840
+ onDone();
841
+ return;
842
+ }
843
+ const result = writeWorkspaces(entries);
844
+ if (result.ok) {
845
+ onDone();
846
+ } else {
847
+ setWriteError(result.error ?? "failed to write workspaces.json");
848
+ setMode("list");
849
+ }
850
+ };
851
+ const actions = [
852
+ { label: "Add workspace", value: "add" },
853
+ { label: "Remove workspace", value: "remove" },
854
+ { label: "Done", value: "done" }
855
+ ];
856
+ if (mode === "list") {
857
+ return /* @__PURE__ */ jsxDEV(Box, {
858
+ flexDirection: "column",
859
+ gap: 1,
860
+ children: [
861
+ /* @__PURE__ */ jsxDEV(Text, {
862
+ bold: true,
863
+ children: "Step 5 — Workspaces"
864
+ }, undefined, false, undefined, this),
865
+ entries.length === 0 && /* @__PURE__ */ jsxDEV(Text, {
866
+ dimColor: true,
867
+ children: "No workspaces configured yet."
868
+ }, undefined, false, undefined, this),
869
+ entries.map((e) => /* @__PURE__ */ jsxDEV(Text, {
870
+ children: [
871
+ "• ",
872
+ e.name,
873
+ " — ",
874
+ e.vcs?.provider ?? "?",
875
+ " — ",
876
+ e.glob
877
+ ]
878
+ }, `${e.name}|${e.glob}|${e.vcs?.provider ?? ""}`, true, undefined, this)),
879
+ /* @__PURE__ */ jsxDEV(Text, {
880
+ dimColor: true,
881
+ children: "Select an action:"
882
+ }, undefined, false, undefined, this),
883
+ /* @__PURE__ */ jsxDEV(Select, {
884
+ options: actions,
885
+ onChange: (v) => {
886
+ if (v === "add") {
887
+ setFieldError(null);
888
+ setWriteError(null);
889
+ setMode("name");
890
+ } else if (v === "remove" && entries.length > 0) {
891
+ setFieldError(null);
892
+ setWriteError(null);
893
+ setMode("remove");
894
+ } else if (v === "done") {
895
+ finish();
896
+ }
897
+ }
898
+ }, "actions", false, undefined, this),
899
+ writeError && /* @__PURE__ */ jsxDEV(Text, {
900
+ color: "red",
901
+ children: writeError
902
+ }, undefined, false, undefined, this),
903
+ /* @__PURE__ */ jsxDEV(Text, {
904
+ dimColor: true,
905
+ children: "Enter to pick · Esc to exit"
906
+ }, undefined, false, undefined, this)
907
+ ]
908
+ }, undefined, true, undefined, this);
909
+ }
910
+ if (mode === "name") {
911
+ return /* @__PURE__ */ jsxDEV(Box, {
912
+ flexDirection: "column",
913
+ gap: 1,
914
+ children: [
915
+ /* @__PURE__ */ jsxDEV(Text, {
916
+ bold: true,
917
+ children: "Step 5 — Workspaces · new workspace"
918
+ }, undefined, false, undefined, this),
919
+ /* @__PURE__ */ jsxDEV(Text, {
920
+ dimColor: true,
921
+ children: "Name (e.g. work):"
922
+ }, undefined, false, undefined, this),
923
+ /* @__PURE__ */ jsxDEV(TextInput, {
924
+ onSubmit: (v) => {
925
+ const name = v.trim();
926
+ if (!name) {
927
+ setFieldError("name is required");
928
+ return;
929
+ }
930
+ if (entries.some((e) => e.name === name)) {
931
+ setFieldError(`"${name}" already exists — pick a unique name`);
932
+ return;
933
+ }
934
+ setFieldError(null);
935
+ setDraft({ ...draft, name });
936
+ setMode("glob");
937
+ }
938
+ }, "name", false, undefined, this),
939
+ fieldError && /* @__PURE__ */ jsxDEV(Text, {
940
+ color: "red",
941
+ children: fieldError
942
+ }, undefined, false, undefined, this)
943
+ ]
944
+ }, undefined, true, undefined, this);
945
+ }
946
+ if (mode === "glob") {
947
+ return /* @__PURE__ */ jsxDEV(Box, {
948
+ flexDirection: "column",
949
+ gap: 1,
950
+ children: [
951
+ /* @__PURE__ */ jsxDEV(Text, {
952
+ bold: true,
953
+ children: [
954
+ "Step 5 — Workspaces · ",
955
+ draft.name
956
+ ]
957
+ }, undefined, true, undefined, this),
958
+ /* @__PURE__ */ jsxDEV(Text, {
959
+ dimColor: true,
960
+ children: "Path glob (e.g. /home/*/Documents/projects/work/**):"
961
+ }, undefined, false, undefined, this),
962
+ /* @__PURE__ */ jsxDEV(TextInput, {
963
+ onSubmit: (v) => {
964
+ if (!v.trim()) {
965
+ setFieldError("glob is required");
966
+ return;
967
+ }
968
+ setFieldError(null);
969
+ setDraft({ ...draft, glob: v.trim() });
970
+ setMode("provider");
971
+ }
972
+ }, "glob", false, undefined, this),
973
+ fieldError && /* @__PURE__ */ jsxDEV(Text, {
974
+ color: "red",
975
+ children: fieldError
976
+ }, undefined, false, undefined, this)
977
+ ]
978
+ }, undefined, true, undefined, this);
979
+ }
980
+ if (mode === "provider") {
981
+ return /* @__PURE__ */ jsxDEV(Box, {
982
+ flexDirection: "column",
983
+ gap: 1,
984
+ children: [
985
+ /* @__PURE__ */ jsxDEV(Text, {
986
+ bold: true,
987
+ children: [
988
+ "Step 5 — Workspaces · ",
989
+ draft.name
990
+ ]
991
+ }, undefined, true, undefined, this),
992
+ /* @__PURE__ */ jsxDEV(Text, {
993
+ dimColor: true,
994
+ children: "VCS provider:"
995
+ }, undefined, false, undefined, this),
996
+ /* @__PURE__ */ jsxDEV(Select, {
997
+ options: VCS_PROVIDERS,
998
+ onChange: (v) => {
999
+ const p = v;
1000
+ setDraft({ ...draft, provider: p, branch: p === "gitlab" ? "develop" : "main" });
1001
+ setMode("branch");
1002
+ }
1003
+ }, "provider", false, undefined, this)
1004
+ ]
1005
+ }, undefined, true, undefined, this);
1006
+ }
1007
+ if (mode === "branch") {
1008
+ return /* @__PURE__ */ jsxDEV(Box, {
1009
+ flexDirection: "column",
1010
+ gap: 1,
1011
+ children: [
1012
+ /* @__PURE__ */ jsxDEV(Text, {
1013
+ bold: true,
1014
+ children: [
1015
+ "Step 5 — Workspaces · ",
1016
+ draft.name
1017
+ ]
1018
+ }, undefined, true, undefined, this),
1019
+ /* @__PURE__ */ jsxDEV(Text, {
1020
+ dimColor: true,
1021
+ children: [
1022
+ 'Default target branch (Enter to keep "',
1023
+ draft.branch,
1024
+ '"):'
1025
+ ]
1026
+ }, undefined, true, undefined, this),
1027
+ /* @__PURE__ */ jsxDEV(TextInput, {
1028
+ defaultValue: draft.branch,
1029
+ onSubmit: (v) => {
1030
+ setDraft({ ...draft, branch: v.trim() });
1031
+ setMode("linking");
1032
+ }
1033
+ }, "branch", false, undefined, this)
1034
+ ]
1035
+ }, undefined, true, undefined, this);
1036
+ }
1037
+ if (mode === "linking") {
1038
+ return /* @__PURE__ */ jsxDEV(Box, {
1039
+ flexDirection: "column",
1040
+ gap: 1,
1041
+ children: [
1042
+ /* @__PURE__ */ jsxDEV(Text, {
1043
+ bold: true,
1044
+ children: [
1045
+ "Step 5 — Workspaces · ",
1046
+ draft.name
1047
+ ]
1048
+ }, undefined, true, undefined, this),
1049
+ /* @__PURE__ */ jsxDEV(Text, {
1050
+ dimColor: true,
1051
+ children: "Issue linking:"
1052
+ }, undefined, false, undefined, this),
1053
+ /* @__PURE__ */ jsxDEV(Select, {
1054
+ options: WS_LINKING[draft.provider],
1055
+ onChange: (v) => {
1056
+ const linking = v;
1057
+ const vcs = { provider: draft.provider, ...draft.branch ? { defaultTargetBranch: draft.branch } : {} };
1058
+ setEntries([
1059
+ ...entries,
1060
+ {
1061
+ name: draft.name,
1062
+ glob: draft.glob,
1063
+ vcs,
1064
+ ...linking === "youtrack" ? { youtrack: { link_issues: true } } : {},
1065
+ ...linking === "github" ? { issues: { provider: "github", link_on_pr: true } } : {}
1066
+ }
1067
+ ]);
1068
+ resetDraft();
1069
+ setWriteError(null);
1070
+ setMode("list");
1071
+ }
1072
+ }, "linking", false, undefined, this)
1073
+ ]
1074
+ }, undefined, true, undefined, this);
1075
+ }
1076
+ return /* @__PURE__ */ jsxDEV(Box, {
1077
+ flexDirection: "column",
1078
+ gap: 1,
1079
+ children: [
1080
+ /* @__PURE__ */ jsxDEV(Text, {
1081
+ bold: true,
1082
+ children: "Step 5 — Workspaces · remove"
1083
+ }, undefined, false, undefined, this),
1084
+ /* @__PURE__ */ jsxDEV(Text, {
1085
+ dimColor: true,
1086
+ children: "Select a workspace to remove:"
1087
+ }, undefined, false, undefined, this),
1088
+ /* @__PURE__ */ jsxDEV(Select, {
1089
+ options: entries.map((e) => ({ label: `${e.name} — ${e.vcs?.provider ?? "?"}`, value: e.name })),
1090
+ onChange: (v) => {
1091
+ setEntries(entries.filter((e) => e.name !== v));
1092
+ setWriteError(null);
1093
+ setMode("list");
1094
+ }
1095
+ }, "remove", false, undefined, this)
1096
+ ]
1097
+ }, undefined, true, undefined, this);
1098
+ }
1099
+ function ProjectStep({ setResults, onDone }) {
1100
+ const apply = () => {
1101
+ const result = runProjectSetup(process.cwd());
1102
+ setResults((r) => ({ ...r, project: result }));
1103
+ onDone();
1104
+ };
1105
+ return /* @__PURE__ */ jsxDEV(Box, {
1106
+ flexDirection: "column",
1107
+ gap: 1,
1108
+ children: [
1109
+ /* @__PURE__ */ jsxDEV(Text, {
1110
+ bold: true,
1111
+ children: "Step 6 — Project setup"
1112
+ }, undefined, false, undefined, this),
1113
+ /* @__PURE__ */ jsxDEV(Text, {
1114
+ dimColor: true,
1115
+ children: [
1116
+ "Will apply gitignore + hygiene in ",
1117
+ process.cwd(),
1118
+ " (existing files are never overwritten):"
1119
+ ]
1120
+ }, undefined, true, undefined, this),
1121
+ /* @__PURE__ */ jsxDEV(ConfirmInput, {
1122
+ defaultChoice: "confirm",
1123
+ submitOnEnter: false,
1124
+ onConfirm: apply,
1125
+ onCancel: () => {}
1126
+ }, undefined, false, undefined, this),
1127
+ /* @__PURE__ */ jsxDEV(Text, {
1128
+ dimColor: true,
1129
+ children: continueLabel()
1130
+ }, undefined, false, undefined, this)
1131
+ ]
1132
+ }, undefined, true, undefined, this);
1133
+ }
1134
+ function SummaryStep({ results, onExit }) {
1135
+ return /* @__PURE__ */ jsxDEV(Box, {
1136
+ flexDirection: "column",
1137
+ gap: 1,
1138
+ children: [
1139
+ /* @__PURE__ */ jsxDEV(Text, {
1140
+ bold: true,
1141
+ color: "cyan",
1142
+ children: "Setup complete"
1143
+ }, undefined, false, undefined, this),
1144
+ /* @__PURE__ */ jsxDEV(Text, {
1145
+ children: [
1146
+ "Platforms: ",
1147
+ /* @__PURE__ */ jsxDEV(Text, {
1148
+ color: "green",
1149
+ children: results.platforms.join(", ")
1150
+ }, undefined, false, undefined, this)
1151
+ ]
1152
+ }, undefined, true, undefined, this),
1153
+ /* @__PURE__ */ jsxDEV(Text, {
1154
+ children: [
1155
+ "Global config: ",
1156
+ /* @__PURE__ */ jsxDEV(Text, {
1157
+ color: "green",
1158
+ children: [
1159
+ configDir(),
1160
+ "/config.json"
1161
+ ]
1162
+ }, undefined, true, undefined, this)
1163
+ ]
1164
+ }, undefined, true, undefined, this),
1165
+ results.youtrack && /* @__PURE__ */ jsxDEV(Box, {
1166
+ flexDirection: "column",
1167
+ gap: 0,
1168
+ children: [
1169
+ /* @__PURE__ */ jsxDEV(Text, {
1170
+ children: [
1171
+ "YouTrack: ",
1172
+ /* @__PURE__ */ jsxDEV(Text, {
1173
+ color: "green",
1174
+ children: results.youtrack.youtrackJson
1175
+ }, undefined, false, undefined, this)
1176
+ ]
1177
+ }, undefined, true, undefined, this),
1178
+ /* @__PURE__ */ jsxDEV(Text, {
1179
+ children: [
1180
+ " token placeholder: ",
1181
+ results.youtrack.tokenPath
1182
+ ]
1183
+ }, undefined, true, undefined, this),
1184
+ /* @__PURE__ */ jsxDEV(Text, {
1185
+ children: [
1186
+ " create token: ",
1187
+ results.youtrack.tokenCreateUrl
1188
+ ]
1189
+ }, undefined, true, undefined, this)
1190
+ ]
1191
+ }, undefined, true, undefined, this),
1192
+ results.vcs && /* @__PURE__ */ jsxDEV(Box, {
1193
+ flexDirection: "column",
1194
+ gap: 0,
1195
+ children: [
1196
+ /* @__PURE__ */ jsxDEV(Text, {
1197
+ children: [
1198
+ "VCS: ",
1199
+ /* @__PURE__ */ jsxDEV(Text, {
1200
+ color: "green",
1201
+ children: results.vcs.vcsJson
1202
+ }, undefined, false, undefined, this),
1203
+ " (provider: ",
1204
+ results.vcs.provider,
1205
+ ")"
1206
+ ]
1207
+ }, undefined, true, undefined, this),
1208
+ /* @__PURE__ */ jsxDEV(Text, {
1209
+ children: [
1210
+ " token placeholder: ",
1211
+ results.vcs.activeTokenPath
1212
+ ]
1213
+ }, undefined, true, undefined, this),
1214
+ /* @__PURE__ */ jsxDEV(Text, {
1215
+ children: [
1216
+ " create token: ",
1217
+ results.vcs.tokenCreateUrl
1218
+ ]
1219
+ }, undefined, true, undefined, this)
1220
+ ]
1221
+ }, undefined, true, undefined, this),
1222
+ results.workspaces.length > 0 && /* @__PURE__ */ jsxDEV(Box, {
1223
+ flexDirection: "column",
1224
+ gap: 0,
1225
+ children: [
1226
+ /* @__PURE__ */ jsxDEV(Text, {
1227
+ children: "Workspaces:"
1228
+ }, undefined, false, undefined, this),
1229
+ results.workspaces.map((w) => /* @__PURE__ */ jsxDEV(Text, {
1230
+ children: [
1231
+ " ",
1232
+ w.name,
1233
+ " — ",
1234
+ w.vcs?.provider ?? "?"
1235
+ ]
1236
+ }, `${w.name}|${w.glob}|${w.vcs?.provider ?? ""}`, true, undefined, this))
1237
+ ]
1238
+ }, undefined, true, undefined, this),
1239
+ results.project && results.project.created.length > 0 && /* @__PURE__ */ jsxDEV(Box, {
1240
+ flexDirection: "column",
1241
+ gap: 0,
1242
+ children: [
1243
+ /* @__PURE__ */ jsxDEV(Text, {
1244
+ children: "Project files:"
1245
+ }, undefined, false, undefined, this),
1246
+ results.project.created.map((file) => /* @__PURE__ */ jsxDEV(Text, {
1247
+ children: [
1248
+ " + ",
1249
+ file
1250
+ ]
1251
+ }, file, true, undefined, this))
1252
+ ]
1253
+ }, undefined, true, undefined, this),
1254
+ /* @__PURE__ */ jsxDEV(Text, {
1255
+ dimColor: true,
1256
+ children: "Paste the token(s) into the placeholder files, then run /wf-status to verify."
1257
+ }, undefined, false, undefined, this),
1258
+ /* @__PURE__ */ jsxDEV(ConfirmInput, {
1259
+ defaultChoice: "confirm",
1260
+ submitOnEnter: false,
1261
+ onConfirm: onExit,
1262
+ onCancel: () => {}
1263
+ }, undefined, false, undefined, this),
1264
+ /* @__PURE__ */ jsxDEV(Text, {
1265
+ dimColor: true,
1266
+ children: continueLabel()
1267
+ }, undefined, false, undefined, this)
1268
+ ]
1269
+ }, undefined, true, undefined, this);
1270
+ }
1271
+
1272
+ // src/index.tsx
1273
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
1274
+ var HELP = `workit — workflow rails for agentic coding
1275
+
1276
+ Usage:
1277
+ workit init Run the interactive setup wizard
1278
+ workit Show this help
1279
+
1280
+ Run \`npx workit init\` to configure platforms, YouTrack, VCS and project hygiene.
1281
+ `;
1282
+ async function runInit() {
1283
+ if (process.stdin.isTTY !== true) {
1284
+ console.log("workit init requires an interactive terminal (TTY).");
1285
+ process.exit(0);
1286
+ }
1287
+ let done = () => {};
1288
+ const { waitUntilExit, unmount } = render(/* @__PURE__ */ jsxDEV2(Wizard, {
1289
+ onExit: () => done()
1290
+ }, undefined, false, undefined, this));
1291
+ done = unmount;
1292
+ await waitUntilExit();
1293
+ process.exit(0);
1294
+ }
1295
+ if (__require.main == __require.module) {
1296
+ const [subcommand] = process.argv.slice(2);
1297
+ if (subcommand === "init") {
1298
+ await runInit();
1299
+ } else {
1300
+ console.log(HELP);
1301
+ process.exit(0);
1302
+ }
1303
+ }