@spekn/cli 1.0.0 → 1.0.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.
Files changed (154) hide show
  1. package/README.md +58 -0
  2. package/dist/main.js +40540 -32176
  3. package/dist/prompts/governance-analysis.prompt.md +109 -0
  4. package/dist/resources/prompts/repo-analysis.prompt.md +28 -136
  5. package/dist/resources/prompts/repo-sync-analysis.prompt.md +31 -68
  6. package/dist/tui/chunk-4WEASLXY.mjs +3444 -0
  7. package/dist/tui/chunk-755CADEG.mjs +3401 -0
  8. package/dist/tui/chunk-BUJQVTY5.mjs +3409 -0
  9. package/dist/tui/chunk-BZKKMGFB.mjs +1959 -0
  10. package/dist/tui/chunk-DJYOBCNM.mjs +3159 -0
  11. package/dist/tui/chunk-GTFTFDY4.mjs +3417 -0
  12. package/dist/tui/chunk-IMEBD2KA.mjs +3444 -0
  13. package/dist/tui/chunk-IX6DR5SW.mjs +3433 -0
  14. package/dist/tui/chunk-JKFOY4IF.mjs +2003 -0
  15. package/dist/tui/chunk-OXXZ3O5L.mjs +3378 -0
  16. package/dist/tui/chunk-SHJNIAAJ.mjs +1697 -0
  17. package/dist/tui/chunk-V4SNDRUS.mjs +1666 -0
  18. package/dist/tui/chunk-VXVHNZST.mjs +1666 -0
  19. package/dist/tui/chunk-WCTSFKTA.mjs +3459 -0
  20. package/dist/tui/chunk-X2XP5ACW.mjs +3443 -0
  21. package/dist/tui/chunk-YUYJ7VBG.mjs +2029 -0
  22. package/dist/tui/chunk-ZM3EI5IA.mjs +3384 -0
  23. package/dist/tui/chunk-ZYOX64HP.mjs +1653 -0
  24. package/dist/tui/index.mjs +6999 -6938
  25. package/dist/tui/prompts/spec-creation-system.prompt.md +47 -0
  26. package/dist/tui/prompts/spec-refinement-system.prompt.md +72 -0
  27. package/dist/tui/use-session-store-63YUGUFA.mjs +8 -0
  28. package/dist/tui/use-session-store-ACO2SMJC.mjs +8 -0
  29. package/dist/tui/use-session-store-BVFDAWOB.mjs +8 -0
  30. package/dist/tui/use-session-store-DJIZ3FQZ.mjs +9 -0
  31. package/dist/tui/use-session-store-EAIQA4UG.mjs +9 -0
  32. package/dist/tui/use-session-store-EFBAXC3G.mjs +8 -0
  33. package/dist/tui/use-session-store-FJOR4KTG.mjs +8 -0
  34. package/dist/tui/use-session-store-IJE5KVOC.mjs +8 -0
  35. package/dist/tui/use-session-store-KGAFXCKI.mjs +8 -0
  36. package/dist/tui/use-session-store-KS4DPNDY.mjs +8 -0
  37. package/dist/tui/use-session-store-MMHJENNL.mjs +8 -0
  38. package/dist/tui/use-session-store-OZ6HC4I2.mjs +9 -0
  39. package/dist/tui/use-session-store-PTMWISNJ.mjs +8 -0
  40. package/dist/tui/use-session-store-VCDECQMW.mjs +8 -0
  41. package/dist/tui/use-session-store-VOK5ML5J.mjs +9 -0
  42. package/package.json +33 -13
  43. package/dist/__tests__/export-cli.test.d.ts +0 -1
  44. package/dist/__tests__/export-cli.test.js +0 -70
  45. package/dist/__tests__/tui-args-policy.test.d.ts +0 -1
  46. package/dist/__tests__/tui-args-policy.test.js +0 -50
  47. package/dist/acp-S2MHZOAD.mjs +0 -23
  48. package/dist/acp-UCCI44JY.mjs +0 -25
  49. package/dist/auth/credentials-store.d.ts +0 -2
  50. package/dist/auth/credentials-store.js +0 -5
  51. package/dist/auth/device-flow.d.ts +0 -36
  52. package/dist/auth/device-flow.js +0 -189
  53. package/dist/auth/jwt.d.ts +0 -1
  54. package/dist/auth/jwt.js +0 -6
  55. package/dist/auth/session.d.ts +0 -67
  56. package/dist/auth/session.js +0 -86
  57. package/dist/auth-login.d.ts +0 -34
  58. package/dist/auth-login.js +0 -202
  59. package/dist/auth-logout.d.ts +0 -25
  60. package/dist/auth-logout.js +0 -115
  61. package/dist/auth-status.d.ts +0 -24
  62. package/dist/auth-status.js +0 -109
  63. package/dist/backlog-generate.d.ts +0 -11
  64. package/dist/backlog-generate.js +0 -308
  65. package/dist/backlog-health.d.ts +0 -11
  66. package/dist/backlog-health.js +0 -287
  67. package/dist/bridge-login.d.ts +0 -40
  68. package/dist/bridge-login.js +0 -277
  69. package/dist/chunk-3PAYRI4G.mjs +0 -2428
  70. package/dist/chunk-M4CS3A25.mjs +0 -2426
  71. package/dist/commands/auth/login.d.ts +0 -30
  72. package/dist/commands/auth/login.js +0 -164
  73. package/dist/commands/auth/logout.d.ts +0 -25
  74. package/dist/commands/auth/logout.js +0 -115
  75. package/dist/commands/auth/status.d.ts +0 -24
  76. package/dist/commands/auth/status.js +0 -109
  77. package/dist/commands/backlog/generate.d.ts +0 -11
  78. package/dist/commands/backlog/generate.js +0 -308
  79. package/dist/commands/backlog/health.d.ts +0 -11
  80. package/dist/commands/backlog/health.js +0 -287
  81. package/dist/commands/bridge/login.d.ts +0 -36
  82. package/dist/commands/bridge/login.js +0 -258
  83. package/dist/commands/export.d.ts +0 -35
  84. package/dist/commands/export.js +0 -485
  85. package/dist/commands/marketplace-export.d.ts +0 -21
  86. package/dist/commands/marketplace-export.js +0 -214
  87. package/dist/commands/project-clean.d.ts +0 -1
  88. package/dist/commands/project-clean.js +0 -126
  89. package/dist/commands/repo/common.d.ts +0 -105
  90. package/dist/commands/repo/common.js +0 -775
  91. package/dist/commands/repo/detach.d.ts +0 -2
  92. package/dist/commands/repo/detach.js +0 -120
  93. package/dist/commands/repo/register.d.ts +0 -21
  94. package/dist/commands/repo/register.js +0 -175
  95. package/dist/commands/repo/sync.d.ts +0 -22
  96. package/dist/commands/repo/sync.js +0 -873
  97. package/dist/commands/skills-import-local.d.ts +0 -16
  98. package/dist/commands/skills-import-local.js +0 -352
  99. package/dist/commands/spec/drift-check.d.ts +0 -3
  100. package/dist/commands/spec/drift-check.js +0 -186
  101. package/dist/commands/spec/frontmatter.d.ts +0 -11
  102. package/dist/commands/spec/frontmatter.js +0 -219
  103. package/dist/commands/spec/lint.d.ts +0 -11
  104. package/dist/commands/spec/lint.js +0 -499
  105. package/dist/commands/spec/parse.d.ts +0 -11
  106. package/dist/commands/spec/parse.js +0 -162
  107. package/dist/export.d.ts +0 -35
  108. package/dist/export.js +0 -485
  109. package/dist/main.d.ts +0 -1
  110. package/dist/marketplace-export.d.ts +0 -21
  111. package/dist/marketplace-export.js +0 -214
  112. package/dist/project-clean.d.ts +0 -1
  113. package/dist/project-clean.js +0 -126
  114. package/dist/project-context.d.ts +0 -99
  115. package/dist/project-context.js +0 -376
  116. package/dist/repo-common.d.ts +0 -101
  117. package/dist/repo-common.js +0 -671
  118. package/dist/repo-detach.d.ts +0 -2
  119. package/dist/repo-detach.js +0 -102
  120. package/dist/repo-ingest.d.ts +0 -29
  121. package/dist/repo-ingest.js +0 -305
  122. package/dist/repo-register.d.ts +0 -21
  123. package/dist/repo-register.js +0 -175
  124. package/dist/repo-sync.d.ts +0 -16
  125. package/dist/repo-sync.js +0 -152
  126. package/dist/resources/prompt-loader.d.ts +0 -1
  127. package/dist/resources/prompt-loader.js +0 -62
  128. package/dist/skills-import-local.d.ts +0 -16
  129. package/dist/skills-import-local.js +0 -352
  130. package/dist/spec-drift-check.d.ts +0 -3
  131. package/dist/spec-drift-check.js +0 -186
  132. package/dist/spec-frontmatter.d.ts +0 -11
  133. package/dist/spec-frontmatter.js +0 -219
  134. package/dist/spec-lint.d.ts +0 -11
  135. package/dist/spec-lint.js +0 -499
  136. package/dist/spec-parse.d.ts +0 -11
  137. package/dist/spec-parse.js +0 -162
  138. package/dist/stubs/dotenv.d.ts +0 -5
  139. package/dist/stubs/dotenv.js +0 -6
  140. package/dist/stubs/typeorm.d.ts +0 -22
  141. package/dist/stubs/typeorm.js +0 -28
  142. package/dist/tui-bundle.d.ts +0 -1
  143. package/dist/tui-bundle.js +0 -5
  144. package/dist/tui-entry.mjs +0 -1407
  145. package/dist/utils/cli-runtime.d.ts +0 -5
  146. package/dist/utils/cli-runtime.js +0 -22
  147. package/dist/utils/help-error.d.ts +0 -7
  148. package/dist/utils/help-error.js +0 -14
  149. package/dist/utils/interaction.d.ts +0 -19
  150. package/dist/utils/interaction.js +0 -93
  151. package/dist/utils/structured-log.d.ts +0 -7
  152. package/dist/utils/structured-log.js +0 -112
  153. package/dist/utils/trpc-url.d.ts +0 -4
  154. package/dist/utils/trpc-url.js +0 -15
@@ -0,0 +1,3459 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/store/use-session-store.ts
5
+ import { create as create6 } from "zustand";
6
+ import { subscribeWithSelector as subscribeWithSelector6 } from "zustand/middleware";
7
+
8
+ // src/utils/project-context.ts
9
+ import fs from "fs";
10
+ import os from "os";
11
+ import path from "path";
12
+ var LOCAL_CONTEXT_FILE = ".spekn";
13
+ var GLOBAL_CONTEXT_PATH = path.join(os.homedir(), ".spekn", "context.json");
14
+ var GLOBAL_CONFIG_PATH = path.join(os.homedir(), ".spekn", "config.json");
15
+ var GLOBAL_ERROR_LOG_PATH = "~/.spekn/error.log";
16
+ var DEFAULT_GLOBAL_CONFIG = {
17
+ logging: {
18
+ enabled: false,
19
+ path: GLOBAL_ERROR_LOG_PATH,
20
+ level: "info"
21
+ }
22
+ };
23
+ var LOG_LEVEL_RANK = {
24
+ debug: 10,
25
+ info: 20,
26
+ warn: 30,
27
+ error: 40
28
+ };
29
+ function loadContextFile(filePath) {
30
+ try {
31
+ const raw = fs.readFileSync(filePath, "utf-8");
32
+ const parsed = JSON.parse(raw);
33
+ if (!parsed || typeof parsed !== "object") return null;
34
+ return parsed;
35
+ } catch {
36
+ return null;
37
+ }
38
+ }
39
+ __name(loadContextFile, "loadContextFile");
40
+ function loadGlobalContextData() {
41
+ try {
42
+ const raw = fs.readFileSync(GLOBAL_CONTEXT_PATH, "utf-8");
43
+ const parsed = JSON.parse(raw);
44
+ if (!parsed || typeof parsed !== "object") return null;
45
+ return parsed;
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+ __name(loadGlobalContextData, "loadGlobalContextData");
51
+ function getGlobalProjects(global) {
52
+ if (!global) return [];
53
+ return Array.isArray(global.projects) ? global.projects : [];
54
+ }
55
+ __name(getGlobalProjects, "getGlobalProjects");
56
+ function isRoot(dirPath) {
57
+ return path.dirname(dirPath) === dirPath;
58
+ }
59
+ __name(isRoot, "isRoot");
60
+ function findNearestLocalContextFile(startDir = process.cwd()) {
61
+ let current = path.resolve(startDir);
62
+ while (true) {
63
+ const candidate = path.join(current, LOCAL_CONTEXT_FILE);
64
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
65
+ return candidate;
66
+ }
67
+ if (isRoot(current)) return null;
68
+ current = path.dirname(current);
69
+ }
70
+ }
71
+ __name(findNearestLocalContextFile, "findNearestLocalContextFile");
72
+ function resolveContextWorkspaceRoot(input) {
73
+ const fallback = path.resolve(input?.fallbackDir ?? process.cwd());
74
+ try {
75
+ const raw = fs.readFileSync(GLOBAL_CONTEXT_PATH, "utf-8");
76
+ const global = JSON.parse(raw);
77
+ const projects = getGlobalProjects(global);
78
+ const selectedProjectId = input?.projectId ?? global.lastUsedProjectId;
79
+ const selected = projects.find((entry) => entry?.id === selectedProjectId) ?? projects.find((entry) => Array.isArray(entry?.repoPaths) && entry.repoPaths.length > 0);
80
+ const repoPaths = Array.isArray(selected?.repoPaths) ? selected.repoPaths : [];
81
+ const existingPath = repoPaths.find((repoPath) => {
82
+ try {
83
+ return fs.existsSync(repoPath);
84
+ } catch {
85
+ return false;
86
+ }
87
+ });
88
+ const resolvedPath = existingPath ?? repoPaths[0];
89
+ if (typeof resolvedPath === "string" && resolvedPath.trim().length > 0) {
90
+ return path.resolve(resolvedPath);
91
+ }
92
+ } catch {
93
+ }
94
+ return fallback;
95
+ }
96
+ __name(resolveContextWorkspaceRoot, "resolveContextWorkspaceRoot");
97
+ function resolveDeclaredContext(input) {
98
+ const localPath = input.repoPath ? path.join(path.resolve(input.repoPath), LOCAL_CONTEXT_FILE) : findNearestLocalContextFile();
99
+ const local = localPath ? loadContextFile(localPath) : null;
100
+ const global = loadGlobalContextData();
101
+ const globalProjectId = global?.lastUsedProjectId;
102
+ const globalProjectOrganizationId = (globalProjectId ? getGlobalProjects(global).find((project) => project.id === globalProjectId)?.organizationId : void 0) ?? global?.defaultOrganizationId;
103
+ const projectId = input.explicitProjectId ?? local?.projectId ?? globalProjectId;
104
+ const organizationId = input.explicitOrganizationId ?? local?.organizationId ?? (projectId ? getGlobalProjects(global).find((project) => project.id === projectId)?.organizationId : void 0) ?? globalProjectOrganizationId ?? input.credentialsOrganizationId;
105
+ return {
106
+ projectId,
107
+ organizationId
108
+ };
109
+ }
110
+ __name(resolveDeclaredContext, "resolveDeclaredContext");
111
+ function saveLocalContext(repoPath, context) {
112
+ const filePath = path.join(path.resolve(repoPath), LOCAL_CONTEXT_FILE);
113
+ const content = {
114
+ projectId: context.projectId,
115
+ organizationId: context.organizationId,
116
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
117
+ };
118
+ fs.writeFileSync(filePath, JSON.stringify(content, null, 2) + "\n", "utf-8");
119
+ }
120
+ __name(saveLocalContext, "saveLocalContext");
121
+ function saveGlobalContext(repoPathInput, context) {
122
+ const dirPath = path.dirname(GLOBAL_CONTEXT_PATH);
123
+ fs.mkdirSync(dirPath, {
124
+ recursive: true,
125
+ mode: 448
126
+ });
127
+ const existing = loadGlobalContextData();
128
+ const now = (/* @__PURE__ */ new Date()).toISOString();
129
+ const repoPath = path.resolve(repoPathInput);
130
+ const organizationId = context.organizationId ?? existing?.defaultOrganizationId ?? "";
131
+ const previousProjects = getGlobalProjects(existing);
132
+ const updatedProjects = previousProjects.map((project) => project.id === context.projectId ? {
133
+ ...project,
134
+ organizationId: organizationId || project.organizationId,
135
+ lastUsed: now,
136
+ repoPaths: Array.from(/* @__PURE__ */ new Set([
137
+ ...project.repoPaths ?? [],
138
+ repoPath
139
+ ]))
140
+ } : project);
141
+ if (!updatedProjects.some((project) => project.id === context.projectId) && organizationId) {
142
+ updatedProjects.unshift({
143
+ id: context.projectId,
144
+ organizationId,
145
+ lastUsed: now,
146
+ repoPaths: [
147
+ repoPath
148
+ ]
149
+ });
150
+ }
151
+ const content = {
152
+ defaultOrganizationId: organizationId || existing?.defaultOrganizationId,
153
+ projects: updatedProjects.slice(0, 10),
154
+ lastUsedProjectId: context.projectId,
155
+ preferences: existing?.preferences,
156
+ repoSync: existing?.repoSync,
157
+ updatedAt: now
158
+ };
159
+ fs.writeFileSync(GLOBAL_CONTEXT_PATH, JSON.stringify(content, null, 2) + "\n", {
160
+ encoding: "utf-8",
161
+ mode: 384
162
+ });
163
+ }
164
+ __name(saveGlobalContext, "saveGlobalContext");
165
+ function saveGlobalContextSelectionOnly(context) {
166
+ const dirPath = path.dirname(GLOBAL_CONTEXT_PATH);
167
+ fs.mkdirSync(dirPath, {
168
+ recursive: true,
169
+ mode: 448
170
+ });
171
+ const existing = loadGlobalContextData();
172
+ const now = (/* @__PURE__ */ new Date()).toISOString();
173
+ const organizationId = context.organizationId ?? existing?.defaultOrganizationId ?? "";
174
+ const previousProjects = getGlobalProjects(existing);
175
+ const updatedProjects = previousProjects.map((project) => project.id === context.projectId ? {
176
+ ...project,
177
+ organizationId: organizationId || project.organizationId,
178
+ lastUsed: now
179
+ } : project);
180
+ if (!updatedProjects.some((project) => project.id === context.projectId) && organizationId) {
181
+ updatedProjects.unshift({
182
+ id: context.projectId,
183
+ organizationId,
184
+ lastUsed: now,
185
+ repoPaths: []
186
+ });
187
+ }
188
+ const content = {
189
+ defaultOrganizationId: organizationId || existing?.defaultOrganizationId,
190
+ projects: updatedProjects.slice(0, 10),
191
+ lastUsedProjectId: context.projectId,
192
+ preferences: existing?.preferences,
193
+ repoSync: existing?.repoSync,
194
+ updatedAt: now
195
+ };
196
+ fs.writeFileSync(GLOBAL_CONTEXT_PATH, JSON.stringify(content, null, 2) + "\n", {
197
+ encoding: "utf-8",
198
+ mode: 384
199
+ });
200
+ }
201
+ __name(saveGlobalContextSelectionOnly, "saveGlobalContextSelectionOnly");
202
+ function ensureGitignoreHasSpekn(repoPath) {
203
+ const gitignorePath = path.join(path.resolve(repoPath), ".gitignore");
204
+ const entry = ".spekn";
205
+ if (!fs.existsSync(gitignorePath)) {
206
+ fs.writeFileSync(gitignorePath, `${entry}
207
+ `, "utf-8");
208
+ return;
209
+ }
210
+ const content = fs.readFileSync(gitignorePath, "utf-8");
211
+ const lines = content.split(/\r?\n/);
212
+ if (lines.some((line) => line.trim() === entry)) return;
213
+ const needsNewline = content.length > 0 && !content.endsWith("\n");
214
+ const toAppend = `${needsNewline ? "\n" : ""}${entry}
215
+ `;
216
+ fs.appendFileSync(gitignorePath, toAppend, "utf-8");
217
+ }
218
+ __name(ensureGitignoreHasSpekn, "ensureGitignoreHasSpekn");
219
+ function hasLocalContext(repoPath) {
220
+ const startDir = repoPath ? path.resolve(repoPath) : process.cwd();
221
+ return findNearestLocalContextFile(startDir) !== null;
222
+ }
223
+ __name(hasLocalContext, "hasLocalContext");
224
+ function persistProjectContext(repoPath, context) {
225
+ saveLocalContext(repoPath, context);
226
+ saveGlobalContext(repoPath, context);
227
+ ensureGitignoreHasSpekn(repoPath);
228
+ }
229
+ __name(persistProjectContext, "persistProjectContext");
230
+ function persistSelectedProjectContext(context) {
231
+ saveGlobalContextSelectionOnly(context);
232
+ }
233
+ __name(persistSelectedProjectContext, "persistSelectedProjectContext");
234
+ function persistProjectContextWithoutRepoPath(repoPath, context) {
235
+ saveLocalContext(repoPath, context);
236
+ saveGlobalContextSelectionOnly(context);
237
+ ensureGitignoreHasSpekn(repoPath);
238
+ }
239
+ __name(persistProjectContextWithoutRepoPath, "persistProjectContextWithoutRepoPath");
240
+ function loadGlobalContextConfig() {
241
+ try {
242
+ const raw = fs.readFileSync(GLOBAL_CONFIG_PATH, "utf-8");
243
+ const parsed = JSON.parse(raw);
244
+ const levelCandidate = parsed?.logging?.level;
245
+ const level = levelCandidate === "debug" || levelCandidate === "info" || levelCandidate === "warn" || levelCandidate === "error" ? levelCandidate : DEFAULT_GLOBAL_CONFIG.logging.level;
246
+ return {
247
+ logging: {
248
+ enabled: typeof parsed?.logging?.enabled === "boolean" ? parsed.logging.enabled : DEFAULT_GLOBAL_CONFIG.logging.enabled,
249
+ path: typeof parsed?.logging?.path === "string" && parsed.logging.path.trim().length > 0 ? parsed.logging.path : DEFAULT_GLOBAL_CONFIG.logging.path,
250
+ level
251
+ }
252
+ };
253
+ } catch {
254
+ return DEFAULT_GLOBAL_CONFIG;
255
+ }
256
+ }
257
+ __name(loadGlobalContextConfig, "loadGlobalContextConfig");
258
+ function saveGlobalContextConfig(config) {
259
+ const dirPath = path.dirname(GLOBAL_CONFIG_PATH);
260
+ fs.mkdirSync(dirPath, {
261
+ recursive: true,
262
+ mode: 448
263
+ });
264
+ fs.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", {
265
+ encoding: "utf-8",
266
+ mode: 384
267
+ });
268
+ }
269
+ __name(saveGlobalContextConfig, "saveGlobalContextConfig");
270
+ function ensureGlobalContextConfig() {
271
+ const config = loadGlobalContextConfig();
272
+ if (!fs.existsSync(GLOBAL_CONFIG_PATH)) {
273
+ saveGlobalContextConfig(config);
274
+ }
275
+ return config;
276
+ }
277
+ __name(ensureGlobalContextConfig, "ensureGlobalContextConfig");
278
+ function expandHomePath(filePath) {
279
+ if (filePath === "~") return os.homedir();
280
+ if (filePath.startsWith("~/")) {
281
+ return path.join(os.homedir(), filePath.slice(2));
282
+ }
283
+ return filePath;
284
+ }
285
+ __name(expandHomePath, "expandHomePath");
286
+ function appendGlobalStructuredLog(input) {
287
+ try {
288
+ const config = ensureGlobalContextConfig();
289
+ if (!config.logging.enabled) return;
290
+ if (LOG_LEVEL_RANK[input.level] < LOG_LEVEL_RANK[config.logging.level]) return;
291
+ const logPath = path.resolve(expandHomePath(config.logging.path));
292
+ const dirPath = path.dirname(logPath);
293
+ fs.mkdirSync(dirPath, {
294
+ recursive: true,
295
+ mode: 448
296
+ });
297
+ const payload = {
298
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
299
+ source: input.source,
300
+ level: input.level,
301
+ message: input.message,
302
+ details: input.details ?? {}
303
+ };
304
+ fs.appendFileSync(logPath, `${JSON.stringify(payload)}
305
+ `, {
306
+ encoding: "utf-8",
307
+ mode: 384
308
+ });
309
+ } catch {
310
+ }
311
+ }
312
+ __name(appendGlobalStructuredLog, "appendGlobalStructuredLog");
313
+ function appendGlobalErrorLog(input) {
314
+ appendGlobalStructuredLog({
315
+ source: input.source,
316
+ level: "error",
317
+ message: input.message,
318
+ details: input.details
319
+ });
320
+ }
321
+ __name(appendGlobalErrorLog, "appendGlobalErrorLog");
322
+
323
+ // src/auth/credentials-store.ts
324
+ import * as fs2 from "fs";
325
+ import * as os2 from "os";
326
+ import * as path2 from "path";
327
+ import { z } from "zod";
328
+ var CliCredentialsSchema = z.object({
329
+ accessToken: z.string(),
330
+ refreshToken: z.string(),
331
+ expiresAt: z.number(),
332
+ keycloakUrl: z.string(),
333
+ realm: z.string(),
334
+ organizationId: z.string().optional(),
335
+ user: z.object({
336
+ sub: z.string(),
337
+ email: z.string(),
338
+ name: z.string().optional()
339
+ }).optional()
340
+ });
341
+ var TokenResponseSchema = z.object({
342
+ access_token: z.string(),
343
+ refresh_token: z.string(),
344
+ expires_in: z.number()
345
+ });
346
+ var CredentialsStore = class {
347
+ static {
348
+ __name(this, "CredentialsStore");
349
+ }
350
+ configDir;
351
+ credentialsPath;
352
+ constructor(configDir) {
353
+ this.configDir = configDir ?? path2.join(os2.homedir(), ".spekn");
354
+ this.credentialsPath = path2.join(this.configDir, "credentials.json");
355
+ }
356
+ load() {
357
+ try {
358
+ const raw = fs2.readFileSync(this.credentialsPath, "utf-8");
359
+ return CliCredentialsSchema.parse(JSON.parse(raw));
360
+ } catch {
361
+ return null;
362
+ }
363
+ }
364
+ save(creds) {
365
+ fs2.mkdirSync(this.configDir, {
366
+ recursive: true,
367
+ mode: 448
368
+ });
369
+ const json = JSON.stringify(creds, null, 2);
370
+ fs2.writeFileSync(this.credentialsPath, json, {
371
+ encoding: "utf-8",
372
+ mode: 384
373
+ });
374
+ }
375
+ clear() {
376
+ try {
377
+ fs2.rmSync(this.credentialsPath);
378
+ } catch (err) {
379
+ if (err.code !== "ENOENT") {
380
+ throw err;
381
+ }
382
+ }
383
+ }
384
+ async getValidToken() {
385
+ const creds = this.load();
386
+ if (creds === null) {
387
+ return null;
388
+ }
389
+ const bufferMs = 3e4;
390
+ if (Date.now() + bufferMs < creds.expiresAt) {
391
+ return creds.accessToken;
392
+ }
393
+ try {
394
+ const tokenUrl = `${creds.keycloakUrl}/realms/${creds.realm}/protocol/openid-connect/token`;
395
+ const body = new URLSearchParams({
396
+ grant_type: "refresh_token",
397
+ client_id: "spekn-cli",
398
+ refresh_token: creds.refreshToken
399
+ });
400
+ const res = await fetch(tokenUrl, {
401
+ method: "POST",
402
+ body,
403
+ headers: {
404
+ "Content-Type": "application/x-www-form-urlencoded"
405
+ }
406
+ });
407
+ if (!res.ok) {
408
+ return null;
409
+ }
410
+ const data = TokenResponseSchema.parse(await res.json());
411
+ const updated = {
412
+ ...creds,
413
+ accessToken: data.access_token,
414
+ refreshToken: data.refresh_token,
415
+ expiresAt: Date.now() + data.expires_in * 1e3
416
+ };
417
+ this.save(updated);
418
+ return updated.accessToken;
419
+ } catch {
420
+ return null;
421
+ }
422
+ }
423
+ };
424
+
425
+ // src/auth/jwt.ts
426
+ function decodeJwtPayload(token) {
427
+ try {
428
+ const parts = token.split(".");
429
+ if (parts.length !== 3) {
430
+ return null;
431
+ }
432
+ const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
433
+ const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
434
+ const json = Buffer.from(padded, "base64").toString("utf-8");
435
+ return JSON.parse(json);
436
+ } catch {
437
+ return null;
438
+ }
439
+ }
440
+ __name(decodeJwtPayload, "decodeJwtPayload");
441
+
442
+ // src/services/cli-runner.ts
443
+ import { spawn } from "child_process";
444
+ var CliRunner = class {
445
+ static {
446
+ __name(this, "CliRunner");
447
+ }
448
+ apiUrl;
449
+ credentialsStore = new CredentialsStore();
450
+ constructor(apiUrl) {
451
+ this.apiUrl = apiUrl;
452
+ }
453
+ resolveCliEntry() {
454
+ const cliEntry = process.argv[1];
455
+ return typeof cliEntry === "string" && cliEntry.length > 0 ? cliEntry : null;
456
+ }
457
+ buildCliEnv(options) {
458
+ const env = {
459
+ ...process.env
460
+ };
461
+ delete env.SPEKN_ORGANIZATION_ID;
462
+ if (options?.organizationId) {
463
+ env.SPEKN_ORGANIZATION_ID = options.organizationId;
464
+ }
465
+ if (options?.interactionEnabled) {
466
+ env.SPEKN_INTERACTION_MODE = "json-stdio";
467
+ } else {
468
+ delete env.SPEKN_INTERACTION_MODE;
469
+ }
470
+ return env;
471
+ }
472
+ async respondToInteraction(child, request, onProgress, requestInteraction) {
473
+ const requestPromise = requestInteraction?.(request);
474
+ const value = await Promise.race([
475
+ requestPromise,
476
+ new Promise((resolve) => setTimeout(() => resolve(void 0), 12e4))
477
+ ]);
478
+ if (!child.stdin || child.stdin.destroyed) return;
479
+ if (value === "skip") {
480
+ child.stdin.write(`${JSON.stringify({
481
+ id: request.id,
482
+ skip: true
483
+ })}
484
+ `);
485
+ return;
486
+ }
487
+ const allowed = new Set(request.options.map((option) => option?.value).filter((optionValue) => typeof optionValue === "string"));
488
+ if (typeof value === "string" && allowed.has(value)) {
489
+ child.stdin.write(`${JSON.stringify({
490
+ id: request.id,
491
+ value
492
+ })}
493
+ `);
494
+ return;
495
+ }
496
+ const fallbackValue = request.options[0]?.value;
497
+ if (typeof fallbackValue === "string") {
498
+ onProgress?.(`[interaction] Auto-selecting default: ${fallbackValue}`);
499
+ child.stdin.write(`${JSON.stringify({
500
+ id: request.id,
501
+ value: fallbackValue
502
+ })}
503
+ `);
504
+ return;
505
+ }
506
+ child.stdin.write(`${JSON.stringify({
507
+ id: request.id
508
+ })}
509
+ `);
510
+ }
511
+ async runCliCommand(args, options) {
512
+ const cliEntry = this.resolveCliEntry();
513
+ if (!cliEntry) {
514
+ return {
515
+ success: false,
516
+ exitCode: 1,
517
+ stdoutLines: [],
518
+ stderrLines: [],
519
+ outputLines: [],
520
+ output: "Could not resolve CLI entrypoint.",
521
+ error: "Could not resolve CLI entrypoint."
522
+ };
523
+ }
524
+ const includeAuthToken = options?.includeAuthToken !== false;
525
+ const token = includeAuthToken ? await this.credentialsStore.getValidToken().catch(() => null) : null;
526
+ const env = this.buildCliEnv({
527
+ organizationId: options?.organizationId,
528
+ interactionEnabled: Boolean(options?.requestInteraction)
529
+ });
530
+ if (includeAuthToken && token) {
531
+ env.SPEKN_AUTH_TOKEN = token;
532
+ } else if (!includeAuthToken) {
533
+ delete env.SPEKN_AUTH_TOKEN;
534
+ }
535
+ const finalArgs = [
536
+ cliEntry,
537
+ ...args
538
+ ];
539
+ const stdoutLines = [];
540
+ const stderrLines = [];
541
+ const outputLines = [];
542
+ const maxLines = options?.maxOutputLines ?? 250;
543
+ let stdoutBuffer = "";
544
+ let stderrBuffer = "";
545
+ const pushLine = /* @__PURE__ */ __name((line, stream) => {
546
+ const target = stream === "stdout" ? stdoutLines : stderrLines;
547
+ target.push(line);
548
+ outputLines.push(line);
549
+ if (target.length > maxLines) target.shift();
550
+ if (outputLines.length > maxLines) outputLines.shift();
551
+ options?.onProgress?.(line);
552
+ }, "pushLine");
553
+ const processLine = /* @__PURE__ */ __name((line, stream, child) => {
554
+ const trimmed = line.trim();
555
+ if (!trimmed) return;
556
+ const activityMarker = "[spekn-activity] ";
557
+ const activityIdx = trimmed.indexOf(activityMarker);
558
+ if (activityIdx >= 0) {
559
+ try {
560
+ const parsed = JSON.parse(trimmed.slice(activityIdx + activityMarker.length));
561
+ if (parsed.type === "spekn.activity") {
562
+ options?.onActivity?.(parsed);
563
+ return;
564
+ }
565
+ } catch {
566
+ }
567
+ }
568
+ const marker = "[spekn-interaction] ";
569
+ const markerIndex = trimmed.indexOf(marker);
570
+ if (options?.requestInteraction && markerIndex >= 0) {
571
+ const payloadRaw = trimmed.slice(markerIndex + marker.length);
572
+ let payload = null;
573
+ try {
574
+ payload = JSON.parse(payloadRaw);
575
+ } catch {
576
+ payload = null;
577
+ }
578
+ if (payload?.type === "spekn.interaction.request" && typeof payload.id === "string" && Array.isArray(payload.options)) {
579
+ options.onProgress?.(`[interaction] ${payload.title ?? "Selection required"}`);
580
+ const request = {
581
+ id: payload.id,
582
+ title: payload.title ?? "Selection required",
583
+ message: payload.message ?? "Choose an option",
584
+ options: payload.options,
585
+ allowSkip: payload.allowSkip === true
586
+ };
587
+ void this.respondToInteraction(child, request, options.onProgress, options.requestInteraction).catch(() => {
588
+ if (!child.stdin || child.stdin.destroyed) return;
589
+ child.stdin.write(`${JSON.stringify({
590
+ id: request.id
591
+ })}
592
+ `);
593
+ });
594
+ return;
595
+ }
596
+ }
597
+ pushLine(trimmed, stream);
598
+ }, "processLine");
599
+ try {
600
+ const child = spawn(process.execPath, finalArgs, {
601
+ cwd: options?.cwd ?? process.cwd(),
602
+ env,
603
+ stdio: [
604
+ "pipe",
605
+ "pipe",
606
+ "pipe"
607
+ ]
608
+ });
609
+ const onData = /* @__PURE__ */ __name((stream, data) => {
610
+ const chunk = String(data);
611
+ if (stream === "stdout") {
612
+ stdoutBuffer += chunk;
613
+ } else {
614
+ stderrBuffer += chunk;
615
+ }
616
+ const buffer = stream === "stdout" ? stdoutBuffer : stderrBuffer;
617
+ const lines = buffer.split(/\r?\n|\r/);
618
+ const remainder = lines.pop() ?? "";
619
+ if (stream === "stdout") {
620
+ stdoutBuffer = remainder;
621
+ } else {
622
+ stderrBuffer = remainder;
623
+ }
624
+ for (const line of lines) {
625
+ processLine(line, stream, child);
626
+ }
627
+ }, "onData");
628
+ child.stdout?.on("data", (data) => onData("stdout", data));
629
+ child.stderr?.on("data", (data) => onData("stderr", data));
630
+ if (options?.signal) {
631
+ if (options.signal.aborted) {
632
+ child.kill("SIGTERM");
633
+ } else {
634
+ const onAbort = /* @__PURE__ */ __name(() => {
635
+ child.kill("SIGTERM");
636
+ }, "onAbort");
637
+ options.signal.addEventListener("abort", onAbort, {
638
+ once: true
639
+ });
640
+ child.on("close", () => options.signal?.removeEventListener("abort", onAbort));
641
+ }
642
+ }
643
+ const exitCode = await new Promise((resolve, reject) => {
644
+ child.on("error", reject);
645
+ child.on("close", (code) => resolve(code ?? 1));
646
+ });
647
+ if (stdoutBuffer.trim().length > 0) {
648
+ processLine(stdoutBuffer, "stdout", child);
649
+ }
650
+ if (stderrBuffer.trim().length > 0) {
651
+ processLine(stderrBuffer, "stderr", child);
652
+ }
653
+ return {
654
+ success: exitCode === 0,
655
+ exitCode,
656
+ stdoutLines,
657
+ stderrLines,
658
+ outputLines,
659
+ output: outputLines.join("\n")
660
+ };
661
+ } catch (error) {
662
+ const message = error instanceof Error ? error.message : String(error);
663
+ return {
664
+ success: false,
665
+ exitCode: 1,
666
+ stdoutLines,
667
+ stderrLines,
668
+ outputLines,
669
+ output: message,
670
+ error: message
671
+ };
672
+ }
673
+ }
674
+ async runCliJson(args, cwd, organizationId) {
675
+ const result = await this.runCliCommand(args, {
676
+ cwd,
677
+ organizationId,
678
+ includeAuthToken: true
679
+ });
680
+ if (!result.success && result.output.trim().length === 0) {
681
+ return {
682
+ ok: false,
683
+ error: result.error ?? "CLI command failed."
684
+ };
685
+ }
686
+ const output = result.stdoutLines.join("\n").trim() || result.output.trim();
687
+ const jsonStart = output.indexOf("{");
688
+ if (jsonStart === -1) {
689
+ return {
690
+ ok: false,
691
+ error: output || "Command produced no JSON output."
692
+ };
693
+ }
694
+ try {
695
+ const parsed = JSON.parse(output.slice(jsonStart));
696
+ return {
697
+ ok: true,
698
+ value: parsed
699
+ };
700
+ } catch (error) {
701
+ const message = error instanceof Error ? error.message : String(error);
702
+ return {
703
+ ok: false,
704
+ error: message
705
+ };
706
+ }
707
+ }
708
+ };
709
+
710
+ // src/services/auth-service.ts
711
+ var AuthService = class {
712
+ static {
713
+ __name(this, "AuthService");
714
+ }
715
+ credentialsStore = new CredentialsStore();
716
+ cliRunner;
717
+ constructor(apiUrl) {
718
+ this.cliRunner = new CliRunner(apiUrl);
719
+ }
720
+ async checkAuthentication() {
721
+ try {
722
+ const token = await this.credentialsStore.getValidToken();
723
+ return token || null;
724
+ } catch {
725
+ return null;
726
+ }
727
+ }
728
+ async authenticateViaCli(onProgress) {
729
+ const result = await this.cliRunner.runCliCommand([
730
+ "auth",
731
+ "login"
732
+ ], {
733
+ cwd: process.cwd(),
734
+ includeAuthToken: false,
735
+ onProgress
736
+ });
737
+ return result.success;
738
+ }
739
+ extractUserEmail(token) {
740
+ try {
741
+ const claims = decodeJwtPayload(token);
742
+ return typeof claims?.["email"] === "string" ? claims["email"] : void 0;
743
+ } catch {
744
+ return void 0;
745
+ }
746
+ }
747
+ extractTokenExpiry(token) {
748
+ try {
749
+ const claims = decodeJwtPayload(token);
750
+ if (typeof claims?.["exp"] === "number") {
751
+ return claims["exp"] * 1e3;
752
+ }
753
+ return void 0;
754
+ } catch {
755
+ return void 0;
756
+ }
757
+ }
758
+ extractPermissions(token) {
759
+ const claims = decodeJwtPayload(token);
760
+ return Array.isArray(claims?.permissions) ? claims.permissions.filter((item) => typeof item === "string") : [];
761
+ }
762
+ };
763
+
764
+ // src/shared-enums.ts
765
+ var OrganizationPlan = {
766
+ FREE: "free",
767
+ PRO: "pro",
768
+ TEAM: "team",
769
+ ENTERPRISE: "enterprise"
770
+ };
771
+ var WorkflowPhase = {
772
+ SPECIFY: "specify",
773
+ CLARIFY: "clarify",
774
+ PLAN: "plan",
775
+ IMPLEMENT: "implement",
776
+ VERIFY: "verify",
777
+ COMPLETE: "complete"
778
+ };
779
+
780
+ // src/services/client.ts
781
+ import { createTRPCProxyClient, httpBatchLink } from "@trpc/client";
782
+
783
+ // src/utils/trpc-url.ts
784
+ function normalizeTrpcUrl(apiUrl) {
785
+ if (apiUrl.endsWith("/trpc")) {
786
+ return apiUrl;
787
+ }
788
+ if (apiUrl.endsWith("/")) {
789
+ return `${apiUrl}trpc`;
790
+ }
791
+ return `${apiUrl}/trpc`;
792
+ }
793
+ __name(normalizeTrpcUrl, "normalizeTrpcUrl");
794
+
795
+ // src/services/client.ts
796
+ function createApiClient(apiUrl, token, organizationId) {
797
+ return createTRPCProxyClient({
798
+ links: [
799
+ httpBatchLink({
800
+ url: normalizeTrpcUrl(apiUrl),
801
+ headers: {
802
+ authorization: token ? `Bearer ${token}` : "",
803
+ "x-organization-id": organizationId
804
+ }
805
+ })
806
+ ]
807
+ });
808
+ }
809
+ __name(createApiClient, "createApiClient");
810
+
811
+ // src/services/bootstrap-service.ts
812
+ function normalizePlan(raw) {
813
+ if (raw === OrganizationPlan.PRO) return OrganizationPlan.PRO;
814
+ if (raw === OrganizationPlan.TEAM) return OrganizationPlan.TEAM;
815
+ if (raw === OrganizationPlan.ENTERPRISE) return OrganizationPlan.ENTERPRISE;
816
+ return OrganizationPlan.FREE;
817
+ }
818
+ __name(normalizePlan, "normalizePlan");
819
+ function normalizeRole(raw) {
820
+ if (raw === "owner" || raw === "admin" || raw === "member" || raw === "viewer") {
821
+ return raw;
822
+ }
823
+ return "member";
824
+ }
825
+ __name(normalizeRole, "normalizeRole");
826
+ var BootstrapService = class {
827
+ static {
828
+ __name(this, "BootstrapService");
829
+ }
830
+ apiUrl;
831
+ credentialsStore = new CredentialsStore();
832
+ authService;
833
+ constructor(apiUrl) {
834
+ this.apiUrl = apiUrl;
835
+ this.authService = new AuthService(apiUrl);
836
+ }
837
+ async bootstrap(projectIdArg) {
838
+ const token = await this.credentialsStore.getValidToken();
839
+ if (!token) {
840
+ throw new Error("No valid credentials. Run `spekn auth login` first.");
841
+ }
842
+ const permissions = this.authService.extractPermissions(token);
843
+ const stored = this.credentialsStore.load();
844
+ const declared = resolveDeclaredContext({
845
+ explicitProjectId: projectIdArg,
846
+ repoPath: process.cwd(),
847
+ credentialsOrganizationId: stored?.organizationId,
848
+ envOrganizationId: process.env.SPEKN_ORGANIZATION_ID
849
+ });
850
+ if (!declared.projectId) {
851
+ throw new Error("PROJECT_CONTEXT_REQUIRED");
852
+ }
853
+ const fallbackOrg = declared.organizationId ?? "";
854
+ const bootstrapClient = createApiClient(this.apiUrl, token, fallbackOrg);
855
+ const orgs = await bootstrapClient.organization.list.query();
856
+ if (orgs.length === 0) {
857
+ throw new Error("No organization membership found for this account.");
858
+ }
859
+ const org = orgs.find((candidate) => candidate.id === fallbackOrg) ?? orgs[0];
860
+ const organizationId = org.id;
861
+ const client2 = createApiClient(this.apiUrl, token, organizationId);
862
+ const projects = await client2.project.list.query({
863
+ limit: 20,
864
+ offset: 0
865
+ });
866
+ if (projects.length === 0) {
867
+ throw new Error("ONBOARDING_REQUIRED");
868
+ }
869
+ const project = projects.find((candidate) => candidate.id === declared.projectId);
870
+ if (!project) {
871
+ throw new Error("ONBOARDING_REQUIRED");
872
+ }
873
+ const repoPath = resolveContextWorkspaceRoot({
874
+ projectId: project.id,
875
+ fallbackDir: process.cwd()
876
+ });
877
+ return {
878
+ boot: {
879
+ apiUrl: this.apiUrl,
880
+ organizationId,
881
+ organizationName: org.name,
882
+ role: normalizeRole(org.role),
883
+ plan: normalizePlan(org.plan),
884
+ projectId: project.id,
885
+ projectName: project.name,
886
+ repoPath,
887
+ permissions
888
+ },
889
+ client: client2
890
+ };
891
+ }
892
+ hasDeclaredProjectContext(projectIdArg) {
893
+ const stored = this.credentialsStore.load();
894
+ const declared = resolveDeclaredContext({
895
+ explicitProjectId: projectIdArg,
896
+ repoPath: process.cwd(),
897
+ credentialsOrganizationId: stored?.organizationId,
898
+ envOrganizationId: process.env.SPEKN_ORGANIZATION_ID
899
+ });
900
+ return Boolean(declared.projectId);
901
+ }
902
+ async loadWorkflowSummary(client2, projectId) {
903
+ const states = await client2.workflowState.listByProject.query({
904
+ projectId
905
+ });
906
+ const first = Array.isArray(states) && states.length > 0 ? states[0] : null;
907
+ const currentPhase = first?.currentPhase ?? null;
908
+ const blockedCount = Array.isArray(states) ? states.filter((state) => state.specificationLockStatus === "locked").length : 0;
909
+ return {
910
+ currentPhase,
911
+ blockedCount,
912
+ hasVerificationEvidence: Boolean(first?.hasVerificationEvidence),
913
+ hasPlanningArtifacts: Boolean(first?.hasPlanningArtifacts)
914
+ };
915
+ }
916
+ hasLocalProjectContext(repoPath) {
917
+ return hasLocalContext(repoPath);
918
+ }
919
+ persistContext(organizationId, projectId) {
920
+ persistSelectedProjectContext({
921
+ organizationId,
922
+ projectId
923
+ });
924
+ }
925
+ };
926
+
927
+ // src/services/bridge-service.ts
928
+ import fs3 from "fs";
929
+ import os3 from "os";
930
+ import path3 from "path";
931
+ import { spawn as spawn2 } from "child_process";
932
+ var DEFAULT_BRIDGE_CONFIG = {
933
+ port: 19550,
934
+ pairing: null
935
+ };
936
+ function loadLocalBridgeConfig() {
937
+ const configPath = path3.join(os3.homedir(), ".spekn", "bridge", "config.json");
938
+ try {
939
+ const raw = fs3.readFileSync(configPath, "utf-8");
940
+ const parsed = JSON.parse(raw);
941
+ return {
942
+ port: typeof parsed.port === "number" ? parsed.port : DEFAULT_BRIDGE_CONFIG.port,
943
+ pairing: parsed.pairing ?? DEFAULT_BRIDGE_CONFIG.pairing
944
+ };
945
+ } catch {
946
+ return DEFAULT_BRIDGE_CONFIG;
947
+ }
948
+ }
949
+ __name(loadLocalBridgeConfig, "loadLocalBridgeConfig");
950
+ async function loadWebMcpChannels(port) {
951
+ try {
952
+ const res = await fetch(`http://127.0.0.1:${port}/webmcp/channels`);
953
+ if (!res.ok) return [];
954
+ const data = await res.json();
955
+ return data.channels ?? [];
956
+ } catch {
957
+ return [];
958
+ }
959
+ }
960
+ __name(loadWebMcpChannels, "loadWebMcpChannels");
961
+ var BridgeService = class {
962
+ static {
963
+ __name(this, "BridgeService");
964
+ }
965
+ cliRunner;
966
+ constructor(apiUrl) {
967
+ this.cliRunner = new CliRunner(apiUrl);
968
+ }
969
+ async loadBridgeSummary(client2) {
970
+ const [flag, devices, metrics] = await Promise.all([
971
+ client2.bridge.getFeatureFlag.query().catch(() => ({
972
+ enabled: false
973
+ })),
974
+ client2.bridge.listDevices.query().catch(() => []),
975
+ client2.bridge.getMetrics.query().catch(() => ({
976
+ connectedDevices: 0,
977
+ authFailures: 0
978
+ }))
979
+ ]);
980
+ return {
981
+ featureEnabled: Boolean(flag.enabled),
982
+ devices: Array.isArray(devices) ? devices.map((device) => ({
983
+ id: device.id,
984
+ name: device.name,
985
+ status: device.status,
986
+ isDefault: Boolean(device.isDefault),
987
+ lastSeenAt: device.lastSeenAt
988
+ })) : [],
989
+ connectedDevices: Number(metrics.connectedDevices ?? 0),
990
+ authFailures: Number(metrics.authFailures ?? 0)
991
+ };
992
+ }
993
+ async loadLocalBridgeSummary() {
994
+ const config = loadLocalBridgeConfig();
995
+ let running = false;
996
+ let uptimeSec;
997
+ try {
998
+ const response = await fetch(`http://127.0.0.1:${config.port}/health`);
999
+ if (response.ok) {
1000
+ const payload = await response.json();
1001
+ running = true;
1002
+ uptimeSec = Number(payload.uptime ?? 0);
1003
+ }
1004
+ } catch {
1005
+ running = false;
1006
+ }
1007
+ return {
1008
+ paired: config.pairing !== null,
1009
+ deviceId: config.pairing?.deviceId,
1010
+ deviceName: config.pairing?.deviceName,
1011
+ port: config.port,
1012
+ running,
1013
+ uptimeSec
1014
+ };
1015
+ }
1016
+ startLocalBridgeDetached() {
1017
+ const cliEntry = this.cliRunner.resolveCliEntry();
1018
+ if (!cliEntry) return;
1019
+ const args = [
1020
+ cliEntry,
1021
+ "bridge",
1022
+ "start"
1023
+ ];
1024
+ const child = spawn2(process.execPath, args, {
1025
+ detached: true,
1026
+ stdio: "ignore"
1027
+ });
1028
+ child.unref();
1029
+ }
1030
+ async stopLocalBridge(configPort) {
1031
+ const port = configPort ?? loadLocalBridgeConfig().port;
1032
+ try {
1033
+ await fetch(`http://127.0.0.1:${port}/shutdown`, {
1034
+ method: "POST"
1035
+ });
1036
+ } catch {
1037
+ }
1038
+ }
1039
+ async loadBridgeLogs(port, since) {
1040
+ const p = port ?? loadLocalBridgeConfig().port;
1041
+ try {
1042
+ const url = since ? `http://127.0.0.1:${p}/logs?since=${since}` : `http://127.0.0.1:${p}/logs`;
1043
+ const res = await fetch(url);
1044
+ if (!res.ok) return [];
1045
+ const data = await res.json();
1046
+ return data.logs;
1047
+ } catch {
1048
+ return [];
1049
+ }
1050
+ }
1051
+ };
1052
+
1053
+ // src/services/decision-service.ts
1054
+ var DecisionService = class {
1055
+ static {
1056
+ __name(this, "DecisionService");
1057
+ }
1058
+ async loadDecisions(client2, projectId) {
1059
+ const result = await client2.decision.getAll.query({
1060
+ projectId,
1061
+ limit: 50,
1062
+ offset: 0
1063
+ });
1064
+ const decisions = Array.isArray(result?.decisions) ? result.decisions : [];
1065
+ return decisions.map((decision) => ({
1066
+ id: decision.id,
1067
+ title: decision.title,
1068
+ status: decision.status,
1069
+ decisionType: decision.decisionType,
1070
+ specAnchor: decision.specAnchor,
1071
+ createdAt: decision.createdAt
1072
+ }));
1073
+ }
1074
+ async resolveDecision(client2, projectId, decisionId, status, reason, existingRationale) {
1075
+ await client2.decision.update.mutate({
1076
+ projectId,
1077
+ id: decisionId,
1078
+ data: {
1079
+ status,
1080
+ rationale: reason || existingRationale
1081
+ }
1082
+ });
1083
+ }
1084
+ async deleteDecision(client2, projectId, decisionId, mode = "archive") {
1085
+ const deleteMutation = client2?.decision?.delete?.mutate;
1086
+ if (typeof deleteMutation !== "function") {
1087
+ throw new Error("Decision delete route is unavailable.");
1088
+ }
1089
+ await deleteMutation({
1090
+ projectId,
1091
+ id: decisionId,
1092
+ mode
1093
+ });
1094
+ }
1095
+ };
1096
+
1097
+ // src/services/export-service.ts
1098
+ var ExportService = class {
1099
+ static {
1100
+ __name(this, "ExportService");
1101
+ }
1102
+ apiUrl;
1103
+ cliRunner;
1104
+ constructor(apiUrl) {
1105
+ this.apiUrl = apiUrl;
1106
+ this.cliRunner = new CliRunner(apiUrl);
1107
+ }
1108
+ async previewExport(client2, projectId, format) {
1109
+ const result = await client2.export.preview.query({
1110
+ projectId,
1111
+ formatId: format
1112
+ });
1113
+ return {
1114
+ content: String(result.content ?? ""),
1115
+ anchorCount: Number(result.anchorCount ?? 0),
1116
+ specGeneration: typeof result.specGeneration === "number" ? result.specGeneration : void 0,
1117
+ warnings: Array.isArray(result.warnings) ? result.warnings.filter((warning) => typeof warning === "string") : void 0
1118
+ };
1119
+ }
1120
+ async generateExport(client2, projectId, format) {
1121
+ const result = await client2.export.generate.mutate({
1122
+ projectId,
1123
+ formatId: format
1124
+ });
1125
+ return {
1126
+ content: String(result.content ?? ""),
1127
+ anchorCount: Number(result.anchorCount ?? 0),
1128
+ specGeneration: typeof result.specGeneration === "number" ? result.specGeneration : void 0,
1129
+ warnings: Array.isArray(result.warnings) ? result.warnings.filter((warning) => typeof warning === "string") : void 0
1130
+ };
1131
+ }
1132
+ async discoverExportCapabilities(projectId, organizationId) {
1133
+ const fallback = {
1134
+ plan: "free",
1135
+ modes: [
1136
+ "global"
1137
+ ],
1138
+ formats: [
1139
+ {
1140
+ id: "agents-md"
1141
+ },
1142
+ {
1143
+ id: "claude-md"
1144
+ },
1145
+ {
1146
+ id: "cursorrules"
1147
+ },
1148
+ {
1149
+ id: "gemini-md"
1150
+ }
1151
+ ]
1152
+ };
1153
+ const cliResult = await this.cliRunner.runCliJson([
1154
+ "export",
1155
+ "discover",
1156
+ "--project",
1157
+ projectId,
1158
+ "--api-url",
1159
+ this.apiUrl,
1160
+ "--json"
1161
+ ], void 0, organizationId);
1162
+ if (!cliResult.ok) {
1163
+ return fallback;
1164
+ }
1165
+ const result = cliResult.value;
1166
+ const modes = Array.isArray(result?.modes) ? result.modes.filter((mode) => mode === "global" || mode === "scoped") : fallback.modes;
1167
+ const formats = Array.isArray(result?.formats) ? result.formats.map((format) => {
1168
+ if (format?.id !== "agents-md" && format?.id !== "claude-md" && format?.id !== "cursorrules" && format?.id !== "gemini-md") {
1169
+ return null;
1170
+ }
1171
+ const formatModes = Array.isArray(format?.modes) ? format.modes.filter((mode) => mode === "global" || mode === "scoped") : void 0;
1172
+ return {
1173
+ id: format.id,
1174
+ name: typeof format?.name === "string" ? format.name : void 0,
1175
+ filename: typeof format?.filename === "string" ? format.filename : void 0,
1176
+ modes: formatModes
1177
+ };
1178
+ }).filter((format) => format !== null) : fallback.formats;
1179
+ return {
1180
+ plan: result?.plan === "pro" || result?.plan === "team" || result?.plan === "enterprise" ? result.plan : "free",
1181
+ modes: modes.length > 0 ? modes : fallback.modes,
1182
+ formats: formats.length > 0 ? formats : fallback.formats
1183
+ };
1184
+ }
1185
+ async exportStatus(projectId, format, options) {
1186
+ const args = [
1187
+ "export",
1188
+ "status",
1189
+ "--project",
1190
+ projectId,
1191
+ "--format",
1192
+ format,
1193
+ "--api-url",
1194
+ this.apiUrl,
1195
+ "--json"
1196
+ ];
1197
+ if (options?.mode) args.push("--mode", options.mode);
1198
+ if (options?.scopePath) args.push("--scope", options.scopePath);
1199
+ const cliResult = await this.cliRunner.runCliJson(args, void 0, options?.organizationId);
1200
+ if (!cliResult.ok) {
1201
+ return {
1202
+ overall: "missing",
1203
+ targets: []
1204
+ };
1205
+ }
1206
+ const result = cliResult.value;
1207
+ const overall = result?.overall === "diverged" || result?.overall === "outdated" || result?.overall === "up-to-date" ? result.overall : "missing";
1208
+ const targets = Array.isArray(result?.targets) ? result.targets.map((target) => {
1209
+ const status = target?.status === "diverged" || target?.status === "outdated" || target?.status === "up-to-date" ? target.status : "missing";
1210
+ const kind = target?.kind === "canonical" ? "canonical" : "entrypoint";
1211
+ if (typeof target?.path !== "string") return null;
1212
+ return {
1213
+ path: target.path,
1214
+ kind,
1215
+ status
1216
+ };
1217
+ }).filter((target) => target !== null) : [];
1218
+ return {
1219
+ overall,
1220
+ targets
1221
+ };
1222
+ }
1223
+ async deliverExport(projectId, format, delivery, options) {
1224
+ const args = [
1225
+ "export",
1226
+ "--project",
1227
+ projectId,
1228
+ "--format",
1229
+ format,
1230
+ "--delivery",
1231
+ delivery,
1232
+ "--api-url",
1233
+ this.apiUrl,
1234
+ "--json"
1235
+ ];
1236
+ if (options?.mode) args.push("--mode", options.mode);
1237
+ if (options?.scopePath) args.push("--scope", options.scopePath);
1238
+ const exportCwd = delivery === "download" ? resolveContextWorkspaceRoot({
1239
+ projectId,
1240
+ fallbackDir: process.cwd()
1241
+ }) : process.cwd();
1242
+ const cliResult = await this.cliRunner.runCliJson(args, exportCwd, options?.organizationId);
1243
+ if (!cliResult.ok) {
1244
+ throw new Error(cliResult.error);
1245
+ }
1246
+ const result = cliResult.value;
1247
+ if (delivery === "commit") {
1248
+ return {
1249
+ message: typeof result?.commitSha === "string" ? `Committed to ${result.branch ?? "branch"}: ${result.commitSha}` : "Export commit completed."
1250
+ };
1251
+ }
1252
+ if (delivery === "pr") {
1253
+ return {
1254
+ message: typeof result?.prUrl === "string" ? `PR #${result.prNumber ?? "?"}: ${result.prUrl}` : "Export PR created."
1255
+ };
1256
+ }
1257
+ if (delivery === "download") {
1258
+ const targets = Array.isArray(result?.targets) ? result.targets.map((target) => {
1259
+ if (typeof target?.path !== "string") return null;
1260
+ return {
1261
+ path: target.path,
1262
+ kind: target?.kind === "canonical" ? "canonical" : "entrypoint",
1263
+ status: "up-to-date"
1264
+ };
1265
+ }).filter((target) => target !== null) : [];
1266
+ const count = targets.length;
1267
+ return {
1268
+ message: `Wrote ${count} export file(s) to ${exportCwd}.`,
1269
+ localStatus: {
1270
+ overall: "up-to-date",
1271
+ targets
1272
+ }
1273
+ };
1274
+ }
1275
+ return {
1276
+ message: "Export copy payload generated."
1277
+ };
1278
+ }
1279
+ };
1280
+
1281
+ // src/services/organization-service.ts
1282
+ var OrganizationService = class {
1283
+ static {
1284
+ __name(this, "OrganizationService");
1285
+ }
1286
+ apiUrl;
1287
+ credentialsStore = new CredentialsStore();
1288
+ constructor(apiUrl) {
1289
+ this.apiUrl = apiUrl;
1290
+ }
1291
+ async listOrganizations() {
1292
+ const token = await this.credentialsStore.getValidToken();
1293
+ if (!token) {
1294
+ throw new Error("No valid credentials. Run `spekn auth login` first.");
1295
+ }
1296
+ const stored = this.credentialsStore.load();
1297
+ const fallbackOrg = stored?.organizationId ?? process.env.SPEKN_ORGANIZATION_ID ?? "";
1298
+ const bootstrapClient = createApiClient(this.apiUrl, token, fallbackOrg);
1299
+ const orgs = await bootstrapClient.organization.list.query();
1300
+ return orgs.map((org) => ({
1301
+ id: org.id,
1302
+ name: org.name,
1303
+ plan: org.plan,
1304
+ role: org.role
1305
+ }));
1306
+ }
1307
+ async createOrganization(input) {
1308
+ const token = await this.credentialsStore.getValidToken();
1309
+ if (!token) {
1310
+ throw new Error("No valid credentials. Run `spekn auth login` first.");
1311
+ }
1312
+ const stored = this.credentialsStore.load();
1313
+ const fallbackOrg = stored?.organizationId ?? process.env.SPEKN_ORGANIZATION_ID ?? "";
1314
+ const bootstrapClient = createApiClient(this.apiUrl, token, fallbackOrg);
1315
+ const createdOrg = await bootstrapClient.organization.create.mutate(input);
1316
+ return {
1317
+ id: createdOrg.id,
1318
+ name: createdOrg.name,
1319
+ plan: createdOrg.plan,
1320
+ role: "owner"
1321
+ };
1322
+ }
1323
+ async listProjects(organizationId) {
1324
+ const token = await this.credentialsStore.getValidToken();
1325
+ if (!token) {
1326
+ throw new Error("No valid credentials. Run `spekn auth login` first.");
1327
+ }
1328
+ const client2 = createApiClient(this.apiUrl, token, organizationId);
1329
+ const projects = await client2.project.list.query({
1330
+ limit: 100,
1331
+ offset: 0
1332
+ });
1333
+ return projects.map((project) => ({
1334
+ id: project.id,
1335
+ name: project.name
1336
+ }));
1337
+ }
1338
+ async createProject(organizationId, name) {
1339
+ try {
1340
+ const token = await this.credentialsStore.getValidToken();
1341
+ if (!token) {
1342
+ throw new Error("No valid credentials. Run `spekn auth login` first.");
1343
+ }
1344
+ const client2 = createApiClient(this.apiUrl, token, organizationId);
1345
+ const created = await client2.project.create.mutate({
1346
+ name: name.trim()
1347
+ });
1348
+ return {
1349
+ id: created.id,
1350
+ name: String(created.name ?? name.trim())
1351
+ };
1352
+ } catch (error) {
1353
+ const message = error instanceof Error ? error.message : String(error);
1354
+ appendGlobalErrorLog({
1355
+ source: "wizard.createProject",
1356
+ message,
1357
+ details: {
1358
+ apiUrl: this.apiUrl,
1359
+ organizationId,
1360
+ projectName: name.trim(),
1361
+ cwd: process.cwd()
1362
+ }
1363
+ });
1364
+ throw error;
1365
+ }
1366
+ }
1367
+ canCreateProject(org) {
1368
+ const role = String(org?.role ?? "").toLowerCase();
1369
+ return role === "owner" || role === "admin";
1370
+ }
1371
+ };
1372
+
1373
+ // src/services/repo-service.ts
1374
+ import { execFileSync } from "child_process";
1375
+ import path4 from "path";
1376
+ var RepoService = class {
1377
+ static {
1378
+ __name(this, "RepoService");
1379
+ }
1380
+ apiUrl;
1381
+ credentialsStore = new CredentialsStore();
1382
+ cliRunner;
1383
+ constructor(apiUrl) {
1384
+ this.apiUrl = apiUrl;
1385
+ this.cliRunner = new CliRunner(apiUrl);
1386
+ }
1387
+ attachOrSyncCurrentRepository = /* @__PURE__ */ __name(async (organizationId, projectId, onProgress, _agentType, requestInteraction, onActivity) => {
1388
+ const repoPath = process.cwd();
1389
+ let args = [];
1390
+ let lastOutputLines = [];
1391
+ try {
1392
+ const remoteUrl = this.execGit(repoPath, [
1393
+ "remote",
1394
+ "get-url",
1395
+ "origin"
1396
+ ]);
1397
+ const token = await this.credentialsStore.getValidToken();
1398
+ if (!token) {
1399
+ throw new Error("No valid credentials. Run `spekn auth login` first.");
1400
+ }
1401
+ const client2 = createApiClient(this.apiUrl, token, organizationId);
1402
+ const repos = await client2.gitRepository.list.query({
1403
+ projectId,
1404
+ limit: 100,
1405
+ offset: 0
1406
+ });
1407
+ const isAlreadyAttached = repos.some((repo) => repo.repositoryUrl === remoteUrl);
1408
+ const cliEntry = this.cliRunner.resolveCliEntry();
1409
+ if (!cliEntry) {
1410
+ throw new Error("Could not resolve CLI entrypoint for repo register.");
1411
+ }
1412
+ const runAnalyze = !isAlreadyAttached;
1413
+ args = isAlreadyAttached ? [
1414
+ cliEntry,
1415
+ "repo",
1416
+ "sync",
1417
+ "--project-id",
1418
+ projectId,
1419
+ "--path",
1420
+ repoPath,
1421
+ "--api-url",
1422
+ this.apiUrl,
1423
+ "--no-analyze"
1424
+ ] : [
1425
+ cliEntry,
1426
+ "repo",
1427
+ "register",
1428
+ "--project-id",
1429
+ projectId,
1430
+ "--path",
1431
+ repoPath,
1432
+ "--api-url",
1433
+ this.apiUrl,
1434
+ "--analyze"
1435
+ ];
1436
+ if (isAlreadyAttached) {
1437
+ onProgress?.("Repository already attached to this project. Syncing metadata only (no analysis)...");
1438
+ } else {
1439
+ onProgress?.("Repository not attached yet. Registering and running analysis...");
1440
+ }
1441
+ const runResult = await this.cliRunner.runCliCommand(args.slice(1), {
1442
+ cwd: repoPath,
1443
+ organizationId,
1444
+ onProgress,
1445
+ requestInteraction,
1446
+ onActivity
1447
+ });
1448
+ lastOutputLines = runResult.outputLines;
1449
+ const exitCode = runResult.exitCode;
1450
+ if (exitCode !== 0) {
1451
+ appendGlobalErrorLog({
1452
+ source: "wizard.registerCurrentRepository",
1453
+ message: `Repository registration/analysis failed (exit ${exitCode}).`,
1454
+ details: {
1455
+ apiUrl: this.apiUrl,
1456
+ organizationId,
1457
+ projectId,
1458
+ repoPath,
1459
+ command: process.execPath,
1460
+ args,
1461
+ lastOutputLines: lastOutputLines.slice(-80)
1462
+ }
1463
+ });
1464
+ throw new Error(`Repository registration/analysis failed (exit ${exitCode}).`);
1465
+ }
1466
+ if (runAnalyze) {
1467
+ persistProjectContext(repoPath, {
1468
+ projectId,
1469
+ organizationId
1470
+ });
1471
+ } else {
1472
+ persistProjectContextWithoutRepoPath(repoPath, {
1473
+ projectId,
1474
+ organizationId
1475
+ });
1476
+ }
1477
+ return {
1478
+ success: true,
1479
+ analyzed: runAnalyze
1480
+ };
1481
+ } catch (error) {
1482
+ const message = error instanceof Error ? error.message : String(error);
1483
+ appendGlobalErrorLog({
1484
+ source: "wizard.registerCurrentRepository",
1485
+ message,
1486
+ details: {
1487
+ apiUrl: this.apiUrl,
1488
+ organizationId,
1489
+ projectId,
1490
+ repoPath,
1491
+ command: process.execPath,
1492
+ args,
1493
+ lastOutputLines: lastOutputLines.slice(-80)
1494
+ }
1495
+ });
1496
+ throw error;
1497
+ }
1498
+ }, "attachOrSyncCurrentRepository");
1499
+ async syncRepositoryViaCli(organizationId, projectId, repoPathInput, options) {
1500
+ const repoPath = path4.resolve(repoPathInput || process.cwd());
1501
+ if (!this.cliRunner.resolveCliEntry()) {
1502
+ return {
1503
+ success: false,
1504
+ output: "Could not resolve CLI entrypoint.",
1505
+ exitCode: 1
1506
+ };
1507
+ }
1508
+ const token = await this.credentialsStore.getValidToken();
1509
+ if (!token) {
1510
+ return {
1511
+ success: false,
1512
+ output: "No valid credentials. Run `spekn auth login` first.",
1513
+ exitCode: 1
1514
+ };
1515
+ }
1516
+ const args = [
1517
+ "repo",
1518
+ "sync",
1519
+ "--project-id",
1520
+ projectId,
1521
+ "--path",
1522
+ repoPath,
1523
+ "--api-url",
1524
+ this.apiUrl
1525
+ ];
1526
+ if (options?.analyze === false) {
1527
+ args.push("--no-analyze");
1528
+ }
1529
+ if (options?.importToProject) {
1530
+ args.push("--import-to-project");
1531
+ }
1532
+ if (typeof options?.maxFiles === "number" && Number.isFinite(options.maxFiles)) {
1533
+ args.push("--max-files", String(Math.max(1, Math.floor(options.maxFiles))));
1534
+ }
1535
+ if (options?.analysisEngine) {
1536
+ args.push("--analysis-engine", options.analysisEngine);
1537
+ }
1538
+ if (options?.agent && options.agent.trim().length > 0) {
1539
+ args.push("--agent", options.agent.trim());
1540
+ }
1541
+ try {
1542
+ const runResult = await this.cliRunner.runCliCommand(args, {
1543
+ cwd: repoPath,
1544
+ organizationId,
1545
+ onProgress: options?.onProgress,
1546
+ requestInteraction: options?.requestInteraction,
1547
+ onActivity: options?.onActivity,
1548
+ signal: options?.signal
1549
+ });
1550
+ return {
1551
+ success: runResult.success,
1552
+ output: runResult.output,
1553
+ exitCode: runResult.exitCode
1554
+ };
1555
+ } catch (error) {
1556
+ const message = error instanceof Error ? error.message : String(error);
1557
+ return {
1558
+ success: false,
1559
+ output: message,
1560
+ exitCode: 1
1561
+ };
1562
+ }
1563
+ }
1564
+ async detachContextViaCli(_projectId, repoPath = process.cwd()) {
1565
+ const result = await this.cliRunner.runCliCommand([
1566
+ "repo",
1567
+ "detach",
1568
+ "--path",
1569
+ repoPath
1570
+ ], {
1571
+ cwd: repoPath,
1572
+ includeAuthToken: false
1573
+ });
1574
+ return {
1575
+ success: result.success,
1576
+ output: result.output || result.error || "Unknown detach error"
1577
+ };
1578
+ }
1579
+ execGit(repoPath, args) {
1580
+ try {
1581
+ return execFileSync("git", [
1582
+ "-C",
1583
+ repoPath,
1584
+ ...args
1585
+ ], {
1586
+ encoding: "utf-8",
1587
+ stdio: [
1588
+ "pipe",
1589
+ "pipe",
1590
+ "pipe"
1591
+ ]
1592
+ }).trim();
1593
+ } catch {
1594
+ throw new Error("Could not read git metadata. Run TUI from a git repository with an 'origin' remote.");
1595
+ }
1596
+ }
1597
+ };
1598
+
1599
+ // src/services/spec-service.ts
1600
+ var SpecService = class {
1601
+ static {
1602
+ __name(this, "SpecService");
1603
+ }
1604
+ async loadSpecs(client2, projectId) {
1605
+ const specs = await client2.specification.list.query({
1606
+ projectId,
1607
+ limit: 50,
1608
+ offset: 0
1609
+ });
1610
+ return (Array.isArray(specs) ? specs : []).map((spec) => {
1611
+ const frontmatter = spec.frontmatter ?? {};
1612
+ const hints = frontmatter.hints ?? {};
1613
+ const aiContext = frontmatter.aiContext ?? {};
1614
+ const acp = frontmatter.acp ?? {};
1615
+ const tags = Array.isArray(frontmatter.tags) ? frontmatter.tags.filter((tag) => typeof tag === "string") : [];
1616
+ const aiFocusAreas = Array.isArray(aiContext.focusAreas) ? aiContext.focusAreas.filter((area) => typeof area === "string") : [];
1617
+ const acpAllowedAgents = Array.isArray(acp.allowedAgents) ? acp.allowedAgents.filter((agent) => typeof agent === "string") : [];
1618
+ const countArray = /* @__PURE__ */ __name((value) => Array.isArray(value) ? value.length : 0, "countArray");
1619
+ return {
1620
+ id: spec.id,
1621
+ specRef: typeof frontmatter.specRef === "string" ? frontmatter.specRef : typeof spec.specRef === "string" ? spec.specRef : void 0,
1622
+ frontmatter,
1623
+ title: spec.title,
1624
+ status: spec.status,
1625
+ generationNumber: typeof spec.generationNumber === "number" ? spec.generationNumber : 1,
1626
+ generationStatus: typeof spec.generationStatus === "string" ? spec.generationStatus : spec.status,
1627
+ updatedAt: spec.updatedAt,
1628
+ type: typeof frontmatter.type === "string" ? frontmatter.type : void 0,
1629
+ content: typeof spec.content === "string" ? spec.content : void 0,
1630
+ tags: tags.length > 0 ? tags : void 0,
1631
+ author: typeof frontmatter.author === "string" ? frontmatter.author : void 0,
1632
+ hintCounts: {
1633
+ constraints: countArray(hints.constraints),
1634
+ requirements: countArray(hints.requirements),
1635
+ technical: countArray(hints.technical),
1636
+ guidance: countArray(hints.guidance)
1637
+ },
1638
+ relationCounts: {
1639
+ dependsOn: countArray(frontmatter.dependsOn),
1640
+ conflictsWith: countArray(frontmatter.conflictsWith),
1641
+ compatibleWith: countArray(frontmatter.compatibleWith)
1642
+ },
1643
+ acpPolicyMode: typeof acp.policyMode === "string" ? acp.policyMode : void 0,
1644
+ acpAllowedAgents: acpAllowedAgents.length > 0 ? acpAllowedAgents : void 0,
1645
+ aiTokenBudget: typeof aiContext.tokenBudget === "number" ? aiContext.tokenBudget : void 0,
1646
+ aiFocusAreas: aiFocusAreas.length > 0 ? aiFocusAreas : void 0
1647
+ };
1648
+ });
1649
+ }
1650
+ async updateSpecificationContent(client2, projectId, specificationId, content) {
1651
+ await client2.specification.update.mutate({
1652
+ projectId,
1653
+ id: specificationId,
1654
+ data: {
1655
+ content,
1656
+ changeType: "patch",
1657
+ changeDescription: "Edited from TUI editor"
1658
+ }
1659
+ });
1660
+ }
1661
+ async updateSpecificationStatus(client2, projectId, specificationId, status) {
1662
+ await client2.specification.update.mutate({
1663
+ projectId,
1664
+ id: specificationId,
1665
+ data: {
1666
+ status,
1667
+ changeType: "metadata",
1668
+ changeDescription: `Status changed to ${status} from TUI`
1669
+ }
1670
+ });
1671
+ }
1672
+ async deleteSpecification(client2, projectId, specificationId, mode = "archive") {
1673
+ const deleteMutation = client2?.specification?.delete?.mutate;
1674
+ if (typeof deleteMutation !== "function") {
1675
+ throw new Error("Specification delete route is unavailable.");
1676
+ }
1677
+ await deleteMutation({
1678
+ projectId,
1679
+ id: specificationId,
1680
+ mode
1681
+ });
1682
+ }
1683
+ async refineSpecificationWithAi(client2, input) {
1684
+ const refineMutation = client2?.specification?.refine?.mutate;
1685
+ if (typeof refineMutation !== "function") {
1686
+ throw new Error("Specification refine route is unavailable.");
1687
+ }
1688
+ const result = await refineMutation({
1689
+ projectId: input.projectId,
1690
+ specificationContent: input.specContent,
1691
+ userMessage: input.userMessage,
1692
+ agentType: input.agentType
1693
+ });
1694
+ if (typeof result?.content !== "string") {
1695
+ throw new Error("AI refinement response was missing content.");
1696
+ }
1697
+ return result.content;
1698
+ }
1699
+ };
1700
+
1701
+ // src/services/context-service.ts
1702
+ var TuiContextService = class {
1703
+ static {
1704
+ __name(this, "TuiContextService");
1705
+ }
1706
+ apiUrl;
1707
+ authService;
1708
+ bootstrapService;
1709
+ bridgeService;
1710
+ decisionService;
1711
+ exportService;
1712
+ organizationService;
1713
+ repoService;
1714
+ specService;
1715
+ constructor(apiUrl) {
1716
+ this.apiUrl = apiUrl;
1717
+ this.authService = new AuthService(apiUrl);
1718
+ this.bootstrapService = new BootstrapService(apiUrl);
1719
+ this.bridgeService = new BridgeService(apiUrl);
1720
+ this.decisionService = new DecisionService();
1721
+ this.exportService = new ExportService(apiUrl);
1722
+ this.organizationService = new OrganizationService(apiUrl);
1723
+ this.repoService = new RepoService(apiUrl);
1724
+ this.specService = new SpecService();
1725
+ }
1726
+ // ── Bootstrap ──────────────────────────────────────────────────────
1727
+ async bootstrap(projectIdArg) {
1728
+ return this.bootstrapService.bootstrap(projectIdArg);
1729
+ }
1730
+ hasDeclaredProjectContext(projectIdArg) {
1731
+ return this.bootstrapService.hasDeclaredProjectContext(projectIdArg);
1732
+ }
1733
+ hasLocalProjectContext(repoPath) {
1734
+ return this.bootstrapService.hasLocalProjectContext(repoPath);
1735
+ }
1736
+ async loadWorkflowSummary(client2, projectId) {
1737
+ return this.bootstrapService.loadWorkflowSummary(client2, projectId);
1738
+ }
1739
+ persistContext(organizationId, projectId) {
1740
+ this.bootstrapService.persistContext(organizationId, projectId);
1741
+ }
1742
+ // ── Auth ───────────────────────────────────────────────────────────
1743
+ async checkAuthentication() {
1744
+ return this.authService.checkAuthentication();
1745
+ }
1746
+ async authenticateViaCli(onProgress) {
1747
+ return this.authService.authenticateViaCli(onProgress);
1748
+ }
1749
+ extractUserEmail(token) {
1750
+ return this.authService.extractUserEmail(token);
1751
+ }
1752
+ extractTokenExpiry(token) {
1753
+ return this.authService.extractTokenExpiry(token);
1754
+ }
1755
+ // ── Organization & Project ─────────────────────────────────────────
1756
+ async listOrganizations() {
1757
+ return this.organizationService.listOrganizations();
1758
+ }
1759
+ async createOrganization(input) {
1760
+ return this.organizationService.createOrganization(input);
1761
+ }
1762
+ async listProjects(organizationId) {
1763
+ return this.organizationService.listProjects(organizationId);
1764
+ }
1765
+ async createProject(organizationId, name) {
1766
+ return this.organizationService.createProject(organizationId, name);
1767
+ }
1768
+ canCreateProject(org) {
1769
+ return this.organizationService.canCreateProject(org);
1770
+ }
1771
+ // ── Specs ──────────────────────────────────────────────────────────
1772
+ async loadSpecs(client2, projectId) {
1773
+ return this.specService.loadSpecs(client2, projectId);
1774
+ }
1775
+ async updateSpecificationContent(client2, projectId, specificationId, content) {
1776
+ return this.specService.updateSpecificationContent(client2, projectId, specificationId, content);
1777
+ }
1778
+ async updateSpecificationStatus(client2, projectId, specificationId, status) {
1779
+ return this.specService.updateSpecificationStatus(client2, projectId, specificationId, status);
1780
+ }
1781
+ async deleteSpecification(client2, projectId, specificationId, mode = "archive") {
1782
+ return this.specService.deleteSpecification(client2, projectId, specificationId, mode);
1783
+ }
1784
+ async refineSpecificationWithAi(client2, input) {
1785
+ return this.specService.refineSpecificationWithAi(client2, input);
1786
+ }
1787
+ // ── Decisions ──────────────────────────────────────────────────────
1788
+ async loadDecisions(client2, projectId) {
1789
+ return this.decisionService.loadDecisions(client2, projectId);
1790
+ }
1791
+ async resolveDecision(client2, projectId, decisionId, status, reason, existingRationale) {
1792
+ return this.decisionService.resolveDecision(client2, projectId, decisionId, status, reason, existingRationale);
1793
+ }
1794
+ async deleteDecision(client2, projectId, decisionId, mode = "archive") {
1795
+ return this.decisionService.deleteDecision(client2, projectId, decisionId, mode);
1796
+ }
1797
+ // ── Export ─────────────────────────────────────────────────────────
1798
+ async previewExport(client2, projectId, format) {
1799
+ return this.exportService.previewExport(client2, projectId, format);
1800
+ }
1801
+ async generateExport(client2, projectId, format) {
1802
+ return this.exportService.generateExport(client2, projectId, format);
1803
+ }
1804
+ async discoverExportCapabilities(projectId, organizationId) {
1805
+ return this.exportService.discoverExportCapabilities(projectId, organizationId);
1806
+ }
1807
+ async exportStatus(projectId, format, options) {
1808
+ return this.exportService.exportStatus(projectId, format, options);
1809
+ }
1810
+ async deliverExport(projectId, format, delivery, options) {
1811
+ return this.exportService.deliverExport(projectId, format, delivery, options);
1812
+ }
1813
+ // ── Bridge ─────────────────────────────────────────────────────────
1814
+ async loadBridgeSummary(client2) {
1815
+ return this.bridgeService.loadBridgeSummary(client2);
1816
+ }
1817
+ async loadLocalBridgeSummary() {
1818
+ return this.bridgeService.loadLocalBridgeSummary();
1819
+ }
1820
+ startLocalBridgeDetached() {
1821
+ this.bridgeService.startLocalBridgeDetached();
1822
+ }
1823
+ async stopLocalBridge(configPort) {
1824
+ return this.bridgeService.stopLocalBridge(configPort);
1825
+ }
1826
+ async loadBridgeLogs(port, since) {
1827
+ return this.bridgeService.loadBridgeLogs(port, since);
1828
+ }
1829
+ // ── Repository ─────────────────────────────────────────────────────
1830
+ attachOrSyncCurrentRepository = /* @__PURE__ */ __name(async (organizationId, projectId, onProgress, _agentType, requestInteraction, onActivity) => {
1831
+ return this.repoService.attachOrSyncCurrentRepository(organizationId, projectId, onProgress, _agentType, requestInteraction, onActivity);
1832
+ }, "attachOrSyncCurrentRepository");
1833
+ async syncRepositoryViaCli(organizationId, projectId, repoPathInput, options) {
1834
+ return this.repoService.syncRepositoryViaCli(organizationId, projectId, repoPathInput, options);
1835
+ }
1836
+ async detachContextViaCli(_projectId, repoPath = process.cwd()) {
1837
+ return this.repoService.detachContextViaCli(_projectId, repoPath);
1838
+ }
1839
+ };
1840
+
1841
+ // src/store/service-bridge.ts
1842
+ var service = null;
1843
+ var client = null;
1844
+ function initServiceBridge(apiUrl) {
1845
+ if (!service) {
1846
+ service = new TuiContextService(apiUrl);
1847
+ }
1848
+ return service;
1849
+ }
1850
+ __name(initServiceBridge, "initServiceBridge");
1851
+ function getService() {
1852
+ if (!service) {
1853
+ throw new Error("Service bridge not initialized. Call initServiceBridge() first.");
1854
+ }
1855
+ return service;
1856
+ }
1857
+ __name(getService, "getService");
1858
+ function getClient() {
1859
+ return client;
1860
+ }
1861
+ __name(getClient, "getClient");
1862
+ function setClient(c) {
1863
+ client = c;
1864
+ }
1865
+ __name(setClient, "setClient");
1866
+
1867
+ // src/store/use-tui-ui-store.ts
1868
+ import { create } from "zustand";
1869
+ import { subscribeWithSelector } from "zustand/middleware";
1870
+ var _initialScreen = "home";
1871
+ function setInitialScreen(screen) {
1872
+ _initialScreen = screen;
1873
+ }
1874
+ __name(setInitialScreen, "setInitialScreen");
1875
+ var initialState = {
1876
+ screen: _initialScreen,
1877
+ showHelp: false,
1878
+ commandMode: false,
1879
+ searchQuery: "",
1880
+ navMode: "content",
1881
+ cursorSpec: null,
1882
+ cursorDecision: null,
1883
+ openedSpecId: void 0,
1884
+ editorMenuOpen: false,
1885
+ statusMenuOpen: false,
1886
+ aiRefineMenuOpen: false,
1887
+ editorLaunching: false,
1888
+ selectedEditor: "",
1889
+ selectedOrganizationIndex: void 0,
1890
+ selectedProjectIndex: void 0,
1891
+ newProjectName: void 0,
1892
+ selectedAnalysisAgent: "auto"
1893
+ };
1894
+ var useTuiUiStore = create()(subscribeWithSelector((set) => ({
1895
+ ...initialState,
1896
+ screen: _initialScreen,
1897
+ setScreen: /* @__PURE__ */ __name((screen) => set({
1898
+ screen
1899
+ }), "setScreen"),
1900
+ toggleHelp: /* @__PURE__ */ __name(() => set((prev) => ({
1901
+ showHelp: !prev.showHelp
1902
+ })), "toggleHelp"),
1903
+ setShowHelp: /* @__PURE__ */ __name((show) => set({
1904
+ showHelp: show
1905
+ }), "setShowHelp"),
1906
+ setCommandMode: /* @__PURE__ */ __name((enabled) => set({
1907
+ commandMode: enabled
1908
+ }), "setCommandMode"),
1909
+ setSearchQuery: /* @__PURE__ */ __name((query) => set({
1910
+ searchQuery: query
1911
+ }), "setSearchQuery"),
1912
+ setNavMode: /* @__PURE__ */ __name((mode) => set({
1913
+ navMode: mode
1914
+ }), "setNavMode"),
1915
+ setCursorSpec: /* @__PURE__ */ __name((spec) => set({
1916
+ cursorSpec: spec
1917
+ }), "setCursorSpec"),
1918
+ setCursorDecision: /* @__PURE__ */ __name((decision) => set({
1919
+ cursorDecision: decision
1920
+ }), "setCursorDecision"),
1921
+ setOpenedSpecId: /* @__PURE__ */ __name((specId) => set({
1922
+ openedSpecId: specId
1923
+ }), "setOpenedSpecId"),
1924
+ setSelectedEditor: /* @__PURE__ */ __name((editor) => set({
1925
+ selectedEditor: editor
1926
+ }), "setSelectedEditor"),
1927
+ setEditorMenuOpen: /* @__PURE__ */ __name((open) => set({
1928
+ editorMenuOpen: open
1929
+ }), "setEditorMenuOpen"),
1930
+ setStatusMenuOpen: /* @__PURE__ */ __name((open) => set({
1931
+ statusMenuOpen: open
1932
+ }), "setStatusMenuOpen"),
1933
+ setAiRefineMenuOpen: /* @__PURE__ */ __name((open) => set({
1934
+ aiRefineMenuOpen: open
1935
+ }), "setAiRefineMenuOpen"),
1936
+ setEditorLaunching: /* @__PURE__ */ __name((launching) => set({
1937
+ editorLaunching: launching
1938
+ }), "setEditorLaunching"),
1939
+ openEditorMenuForSpec: /* @__PURE__ */ __name((specId, editor) => set({
1940
+ openedSpecId: specId,
1941
+ selectedEditor: editor,
1942
+ editorMenuOpen: true
1943
+ }), "openEditorMenuForSpec"),
1944
+ resetSpecsUi: /* @__PURE__ */ __name(() => set({
1945
+ openedSpecId: void 0,
1946
+ cursorSpec: null,
1947
+ editorMenuOpen: false,
1948
+ statusMenuOpen: false,
1949
+ aiRefineMenuOpen: false,
1950
+ editorLaunching: false
1951
+ }), "resetSpecsUi"),
1952
+ setSelectedOrganizationIndex: /* @__PURE__ */ __name((index) => set({
1953
+ selectedOrganizationIndex: index
1954
+ }), "setSelectedOrganizationIndex"),
1955
+ setSelectedProjectIndex: /* @__PURE__ */ __name((index) => set({
1956
+ selectedProjectIndex: index
1957
+ }), "setSelectedProjectIndex"),
1958
+ setNewProjectName: /* @__PURE__ */ __name((name) => set({
1959
+ newProjectName: name
1960
+ }), "setNewProjectName"),
1961
+ setSelectedAnalysisAgent: /* @__PURE__ */ __name((agent) => set({
1962
+ selectedAnalysisAgent: agent
1963
+ }), "setSelectedAnalysisAgent"),
1964
+ resetOnboardingUi: /* @__PURE__ */ __name(() => set({
1965
+ selectedOrganizationIndex: void 0,
1966
+ selectedProjectIndex: void 0,
1967
+ newProjectName: void 0,
1968
+ selectedAnalysisAgent: "auto"
1969
+ }), "resetOnboardingUi"),
1970
+ showTooltip: /* @__PURE__ */ __name((message, duration = 3e3) => set((state) => {
1971
+ if (state.tooltipTimeout) {
1972
+ clearTimeout(state.tooltipTimeout);
1973
+ }
1974
+ const timeout = setTimeout(() => {
1975
+ useTuiUiStore.getState().clearTooltip();
1976
+ }, duration);
1977
+ return {
1978
+ tooltip: message,
1979
+ tooltipTimeout: timeout
1980
+ };
1981
+ }), "showTooltip"),
1982
+ clearTooltip: /* @__PURE__ */ __name(() => set((state) => {
1983
+ if (state.tooltipTimeout) {
1984
+ clearTimeout(state.tooltipTimeout);
1985
+ }
1986
+ return {
1987
+ tooltip: void 0,
1988
+ tooltipTimeout: void 0
1989
+ };
1990
+ }), "clearTooltip")
1991
+ })));
1992
+
1993
+ // src/capabilities/policy.ts
1994
+ var TIER_ORDER = {
1995
+ [OrganizationPlan.FREE]: 0,
1996
+ [OrganizationPlan.PRO]: 1,
1997
+ [OrganizationPlan.TEAM]: 2,
1998
+ [OrganizationPlan.ENTERPRISE]: 3
1999
+ };
2000
+ var NAV_DEFINITIONS = [
2001
+ {
2002
+ id: "home",
2003
+ label: "Home",
2004
+ description: "Next actions and workflow pulse",
2005
+ requiredPlan: OrganizationPlan.FREE
2006
+ },
2007
+ {
2008
+ id: "specs",
2009
+ label: "Specs",
2010
+ description: "Manage governed specifications",
2011
+ requiredPlan: OrganizationPlan.FREE
2012
+ },
2013
+ {
2014
+ id: "decisions",
2015
+ label: "Decision Log",
2016
+ description: "Review decisions and rationale",
2017
+ requiredPlan: OrganizationPlan.FREE
2018
+ },
2019
+ {
2020
+ id: "export",
2021
+ label: "Export",
2022
+ description: "Generate AGENTS.md / CLAUDE.md / .cursorrules",
2023
+ requiredPlan: OrganizationPlan.FREE
2024
+ },
2025
+ {
2026
+ id: "bridge",
2027
+ label: "Local Bridge",
2028
+ description: "Bridge status and controls",
2029
+ requiredPlan: OrganizationPlan.FREE
2030
+ },
2031
+ {
2032
+ id: "active-runs",
2033
+ label: "Active Runs",
2034
+ description: "Realtime orchestration dashboard",
2035
+ requiredPlan: OrganizationPlan.TEAM
2036
+ },
2037
+ {
2038
+ id: "phase-gates",
2039
+ label: "Phase Gates",
2040
+ description: "Approve and unblock workflow phases",
2041
+ requiredPlan: OrganizationPlan.TEAM
2042
+ },
2043
+ {
2044
+ id: "skills-marketplace",
2045
+ label: "Skills Marketplace",
2046
+ description: "Manage shared managed skills",
2047
+ requiredPlan: OrganizationPlan.TEAM
2048
+ },
2049
+ {
2050
+ id: "org-governance",
2051
+ label: "Org Governance",
2052
+ description: "Compliance, policy, deployment gates",
2053
+ requiredPlan: OrganizationPlan.ENTERPRISE
2054
+ }
2055
+ ];
2056
+ var GATE_DISABLED_PHASES = [
2057
+ WorkflowPhase.SPECIFY,
2058
+ WorkflowPhase.CLARIFY
2059
+ ];
2060
+ function meetsMinimumTier(current, required) {
2061
+ return TIER_ORDER[current] >= TIER_ORDER[required];
2062
+ }
2063
+ __name(meetsMinimumTier, "meetsMinimumTier");
2064
+ function resolveNavPolicy(ctx) {
2065
+ return NAV_DEFINITIONS.map((item) => {
2066
+ if (!meetsMinimumTier(ctx.plan, item.requiredPlan)) {
2067
+ return {
2068
+ ...item,
2069
+ state: "locked",
2070
+ reason: `Requires ${item.requiredPlan.toUpperCase()} tier`
2071
+ };
2072
+ }
2073
+ if (item.id === "phase-gates" && ctx.role === "viewer") {
2074
+ return {
2075
+ ...item,
2076
+ state: "disabled",
2077
+ reason: "Viewer role cannot approve gates"
2078
+ };
2079
+ }
2080
+ if (item.id === "phase-gates" && ctx.workflowPhase && GATE_DISABLED_PHASES.includes(ctx.workflowPhase)) {
2081
+ return {
2082
+ ...item,
2083
+ state: "disabled",
2084
+ reason: `Gate approvals are unavailable in ${ctx.workflowPhase} phase`
2085
+ };
2086
+ }
2087
+ return {
2088
+ ...item,
2089
+ state: "enabled"
2090
+ };
2091
+ });
2092
+ }
2093
+ __name(resolveNavPolicy, "resolveNavPolicy");
2094
+
2095
+ // src/state/onboarding-utils.ts
2096
+ function logOnboardingAttachResult(appendLog, result) {
2097
+ if (result.warning) {
2098
+ appendLog(`[warn] ${result.warning}`);
2099
+ }
2100
+ if (result.analyzed) {
2101
+ appendLog("[setup] Repository attached and analysis complete.");
2102
+ } else if (result.warning) {
2103
+ appendLog("[setup] Repository attached; analysis did not fully complete.");
2104
+ } else {
2105
+ appendLog("[setup] Repository already attached. Metadata sync complete (analysis skipped).");
2106
+ }
2107
+ }
2108
+ __name(logOnboardingAttachResult, "logOnboardingAttachResult");
2109
+
2110
+ // src/store/windows/use-onboarding-window.ts
2111
+ import { create as create2 } from "zustand";
2112
+ import { subscribeWithSelector as subscribeWithSelector2 } from "zustand/middleware";
2113
+ var useOnboardingWindow = create2()(subscribeWithSelector2((set) => ({
2114
+ mode: "initial-setup",
2115
+ step: "organization",
2116
+ attachCurrentFolder: true,
2117
+ runLines: [],
2118
+ runStatus: "running",
2119
+ startedAt: null,
2120
+ availableAgents: [],
2121
+ canCreateProject: false,
2122
+ toolCalls: {},
2123
+ setMode: /* @__PURE__ */ __name((mode) => set({
2124
+ mode
2125
+ }), "setMode"),
2126
+ setStep: /* @__PURE__ */ __name((step) => set({
2127
+ step
2128
+ }), "setStep"),
2129
+ setSelectedOrganizationIndex: /* @__PURE__ */ __name((index) => set({
2130
+ selectedOrganizationIndex: index
2131
+ }), "setSelectedOrganizationIndex"),
2132
+ setSelectedProjectIndex: /* @__PURE__ */ __name((index) => set({
2133
+ selectedProjectIndex: index
2134
+ }), "setSelectedProjectIndex"),
2135
+ setNewProjectName: /* @__PURE__ */ __name((name) => set({
2136
+ newProjectName: name
2137
+ }), "setNewProjectName"),
2138
+ setAttachCurrentFolder: /* @__PURE__ */ __name((attach) => set({
2139
+ attachCurrentFolder: attach
2140
+ }), "setAttachCurrentFolder"),
2141
+ setRunLines: /* @__PURE__ */ __name((lines) => set({
2142
+ runLines: lines
2143
+ }), "setRunLines"),
2144
+ addRunLine: /* @__PURE__ */ __name((line) => set((state) => ({
2145
+ runLines: [
2146
+ ...state.runLines,
2147
+ line
2148
+ ].slice(-400)
2149
+ })), "addRunLine"),
2150
+ setRunStatus: /* @__PURE__ */ __name((status) => set({
2151
+ runStatus: status
2152
+ }), "setRunStatus"),
2153
+ setStartedAt: /* @__PURE__ */ __name((ts) => set({
2154
+ startedAt: ts
2155
+ }), "setStartedAt"),
2156
+ setInteraction: /* @__PURE__ */ __name((interaction) => set({
2157
+ interaction
2158
+ }), "setInteraction"),
2159
+ setError: /* @__PURE__ */ __name((error) => set({
2160
+ error
2161
+ }), "setError"),
2162
+ setSelectedOrganizationId: /* @__PURE__ */ __name((id) => set({
2163
+ selectedOrganizationId: id
2164
+ }), "setSelectedOrganizationId"),
2165
+ setSelectedProjectId: /* @__PURE__ */ __name((id) => set({
2166
+ selectedProjectId: id
2167
+ }), "setSelectedProjectId"),
2168
+ setAvailableAgents: /* @__PURE__ */ __name((agents) => set({
2169
+ availableAgents: agents
2170
+ }), "setAvailableAgents"),
2171
+ setCanCreateProject: /* @__PURE__ */ __name((canCreate) => set({
2172
+ canCreateProject: canCreate
2173
+ }), "setCanCreateProject"),
2174
+ upsertToolCall: /* @__PURE__ */ __name((event) => set((state) => {
2175
+ const id = event.toolCallId ?? `tc-${Date.now()}`;
2176
+ const existing = state.toolCalls[id];
2177
+ const updated = existing ? {
2178
+ ...existing,
2179
+ status: event.status ?? existing.status,
2180
+ title: event.title ?? existing.title,
2181
+ locations: event.locations ?? existing.locations,
2182
+ completedAt: event.status === "completed" || event.status === "failed" ? Date.now() : existing.completedAt
2183
+ } : {
2184
+ toolCallId: id,
2185
+ title: event.title ?? event.toolName ?? "Tool call",
2186
+ toolName: event.toolName,
2187
+ kind: event.kind ?? "other",
2188
+ status: event.status ?? "in_progress",
2189
+ locations: event.locations ?? [],
2190
+ startedAt: Date.now()
2191
+ };
2192
+ return {
2193
+ toolCalls: {
2194
+ ...state.toolCalls,
2195
+ [id]: updated
2196
+ }
2197
+ };
2198
+ }), "upsertToolCall"),
2199
+ setActiveThought: /* @__PURE__ */ __name((text) => set({
2200
+ activeThought: text
2201
+ }), "setActiveThought"),
2202
+ reset: /* @__PURE__ */ __name(() => set({
2203
+ mode: "initial-setup",
2204
+ step: "organization",
2205
+ selectedOrganizationIndex: void 0,
2206
+ selectedProjectIndex: void 0,
2207
+ newProjectName: void 0,
2208
+ attachCurrentFolder: true,
2209
+ runLines: [],
2210
+ runStatus: "running",
2211
+ startedAt: null,
2212
+ interaction: void 0,
2213
+ error: void 0,
2214
+ selectedOrganizationId: void 0,
2215
+ selectedProjectId: void 0,
2216
+ availableAgents: [],
2217
+ canCreateProject: false,
2218
+ toolCalls: {},
2219
+ activeThought: void 0
2220
+ }), "reset")
2221
+ })));
2222
+
2223
+ // src/store/auth-utils.ts
2224
+ function isAuthenticationError(message) {
2225
+ const normalized = message.toLowerCase();
2226
+ return normalized.includes("authentication required") || normalized.includes("no valid credentials") || normalized.includes("unauthorized") || normalized.includes("forbidden") || normalized.includes("invalid token") || normalized.includes("token expired") || normalized.includes("auth");
2227
+ }
2228
+ __name(isAuthenticationError, "isAuthenticationError");
2229
+
2230
+ // src/store/use-project-data-store.ts
2231
+ import { create as create3 } from "zustand";
2232
+ import { subscribeWithSelector as subscribeWithSelector3 } from "zustand/middleware";
2233
+ var EMPTY_WORKFLOW = {
2234
+ currentPhase: null,
2235
+ blockedCount: 0,
2236
+ hasPlanningArtifacts: false,
2237
+ hasVerificationEvidence: false
2238
+ };
2239
+ var useProjectDataStore = create3()(subscribeWithSelector3((set, get) => ({
2240
+ specs: [],
2241
+ decisions: [],
2242
+ workflow: EMPTY_WORKFLOW,
2243
+ navPolicy: [],
2244
+ setProjectData: /* @__PURE__ */ __name((specs, decisions, workflow, navPolicy) => set({
2245
+ specs,
2246
+ decisions,
2247
+ workflow,
2248
+ navPolicy
2249
+ }), "setProjectData"),
2250
+ setPollingData: /* @__PURE__ */ __name((workflow, navPolicy) => set({
2251
+ workflow,
2252
+ navPolicy
2253
+ }), "setPollingData"),
2254
+ clearProjectData: /* @__PURE__ */ __name(() => set({
2255
+ specs: [],
2256
+ decisions: [],
2257
+ workflow: EMPTY_WORKFLOW,
2258
+ navPolicy: []
2259
+ }), "clearProjectData"),
2260
+ updateSpecificationContent: /* @__PURE__ */ __name(async (specificationId, content) => {
2261
+ const client2 = getClient();
2262
+ const boot = getBootContext();
2263
+ if (!client2 || !boot) return;
2264
+ await withSessionFeedback({
2265
+ statusLine: "Saving specification...",
2266
+ action: /* @__PURE__ */ __name(() => getService().updateSpecificationContent(client2, boot.projectId, specificationId, content), "action"),
2267
+ successLog: "[specs] saved specification changes",
2268
+ failureLog: "[error] save failed",
2269
+ failureStatusLine: "Save failed"
2270
+ });
2271
+ }, "updateSpecificationContent"),
2272
+ updateSpecificationStatus: /* @__PURE__ */ __name(async (specificationId, status, options) => {
2273
+ const client2 = getClient();
2274
+ const boot = getBootContext();
2275
+ if (!client2 || !boot) return;
2276
+ setSessionStatusLine(`Updating status to ${status}...`);
2277
+ try {
2278
+ await getService().updateSpecificationStatus(client2, boot.projectId, specificationId, status);
2279
+ appendSessionLog(`[specs] status updated to ${status}`);
2280
+ if (options?.refresh !== false) {
2281
+ const { useSessionStore: useSessionStore2 } = await import("./use-session-store-PTMWISNJ.mjs");
2282
+ await useSessionStore2.getState().refresh();
2283
+ }
2284
+ } catch (error) {
2285
+ const message = error instanceof Error ? error.message : String(error);
2286
+ appendSessionLog(`[error] status update failed: ${message}`);
2287
+ setSessionStatusLine("Status update failed");
2288
+ refreshOnAuthenticationError(message);
2289
+ }
2290
+ }, "updateSpecificationStatus"),
2291
+ deleteSpecification: /* @__PURE__ */ __name(async (specificationId, mode = "archive", options) => {
2292
+ const client2 = getClient();
2293
+ const boot = getBootContext();
2294
+ if (!client2 || !boot) return;
2295
+ setSessionStatusLine(mode === "delete" ? "Deleting specification permanently..." : "Archiving specification...");
2296
+ try {
2297
+ await getService().deleteSpecification(client2, boot.projectId, specificationId, mode);
2298
+ appendSessionLog(mode === "delete" ? "[specs] specification deleted permanently" : "[specs] specification archived");
2299
+ if (options?.refresh !== false) {
2300
+ const { useSessionStore: useSessionStore2 } = await import("./use-session-store-PTMWISNJ.mjs");
2301
+ await useSessionStore2.getState().refresh();
2302
+ }
2303
+ } catch (error) {
2304
+ const message = error instanceof Error ? error.message : String(error);
2305
+ appendSessionLog(`[error] specification ${mode === "delete" ? "delete" : "archive"} failed: ${message}`);
2306
+ setSessionStatusLine(mode === "delete" ? "Permanent delete failed" : "Archive failed");
2307
+ refreshOnAuthenticationError(message);
2308
+ }
2309
+ }, "deleteSpecification"),
2310
+ refineSpecificationWithAi: /* @__PURE__ */ __name(async (specificationId, userMessage) => {
2311
+ const client2 = getClient();
2312
+ const boot = getBootContext();
2313
+ const { specs } = get();
2314
+ if (!client2 || !boot) return;
2315
+ const spec = specs.find((item) => item.id === specificationId);
2316
+ if (!spec) {
2317
+ appendSessionLog("[error] refine failed: specification not found in current list");
2318
+ setSessionStatusLine("Refinement failed");
2319
+ return;
2320
+ }
2321
+ const specContent = (spec.content ?? "").trim();
2322
+ if (!specContent) {
2323
+ appendSessionLog("[error] refine failed: selected specification has no content");
2324
+ setSessionStatusLine("Refinement failed");
2325
+ return;
2326
+ }
2327
+ setSessionStatusLine("Refining specification with AI...");
2328
+ const service2 = getService();
2329
+ try {
2330
+ const refinedContent = await service2.refineSpecificationWithAi(client2, {
2331
+ projectId: boot.projectId,
2332
+ specContent,
2333
+ userMessage,
2334
+ agentType: "codex"
2335
+ });
2336
+ await service2.updateSpecificationContent(client2, boot.projectId, specificationId, refinedContent);
2337
+ appendSessionLog("[specs] AI refinement applied and saved");
2338
+ const { useSessionStore: useSessionStore2 } = await import("./use-session-store-PTMWISNJ.mjs");
2339
+ await useSessionStore2.getState().refresh();
2340
+ } catch (error) {
2341
+ const message = error instanceof Error ? error.message : String(error);
2342
+ appendSessionLog(`[error] AI refinement failed: ${message}`);
2343
+ setSessionStatusLine("AI refinement failed");
2344
+ refreshOnAuthenticationError(message);
2345
+ }
2346
+ }, "refineSpecificationWithAi"),
2347
+ resolveDecision: /* @__PURE__ */ __name(async (decisionId, status, reason, existingRationale, options) => {
2348
+ const client2 = getClient();
2349
+ const boot = getBootContext();
2350
+ if (!client2 || !boot) {
2351
+ throw new Error("Decision update unavailable: project context is not initialized.");
2352
+ }
2353
+ setSessionStatusLine(`Resolving decision as ${status}...`);
2354
+ try {
2355
+ await getService().resolveDecision(client2, boot.projectId, decisionId, status, reason, existingRationale);
2356
+ appendSessionLog(`[decisions] ${decisionId} -> ${status}`);
2357
+ if (options?.refresh !== false) {
2358
+ const { useSessionStore: useSessionStore2 } = await import("./use-session-store-PTMWISNJ.mjs");
2359
+ await useSessionStore2.getState().refresh();
2360
+ }
2361
+ } catch (error) {
2362
+ const message = error instanceof Error ? error.message : String(error);
2363
+ appendSessionLog(`[error] decision resolution failed: ${message}`);
2364
+ setSessionStatusLine("Decision resolution failed");
2365
+ refreshOnAuthenticationError(message);
2366
+ throw error;
2367
+ }
2368
+ }, "resolveDecision"),
2369
+ deleteDecision: /* @__PURE__ */ __name(async (decisionId, mode = "archive", options) => {
2370
+ const client2 = getClient();
2371
+ const boot = getBootContext();
2372
+ if (!client2 || !boot) {
2373
+ throw new Error("Decision delete unavailable: project context is not initialized.");
2374
+ }
2375
+ setSessionStatusLine(mode === "delete" ? "Deleting decision permanently..." : "Archiving decision...");
2376
+ try {
2377
+ await getService().deleteDecision(client2, boot.projectId, decisionId, mode);
2378
+ appendSessionLog(mode === "delete" ? `[decisions] ${decisionId} deleted permanently` : `[decisions] ${decisionId} archived`);
2379
+ if (options?.refresh !== false) {
2380
+ const { useSessionStore: useSessionStore2 } = await import("./use-session-store-PTMWISNJ.mjs");
2381
+ await useSessionStore2.getState().refresh();
2382
+ }
2383
+ } catch (error) {
2384
+ const message = error instanceof Error ? error.message : String(error);
2385
+ appendSessionLog(`[error] decision ${mode === "delete" ? "delete" : "archive"} failed: ${message}`);
2386
+ setSessionStatusLine(mode === "delete" ? "Permanent delete failed" : "Archive failed");
2387
+ refreshOnAuthenticationError(message);
2388
+ throw error;
2389
+ }
2390
+ }, "deleteDecision")
2391
+ })));
2392
+
2393
+ // src/store/use-export-store.ts
2394
+ import { create as create4 } from "zustand";
2395
+ import { subscribeWithSelector as subscribeWithSelector4 } from "zustand/middleware";
2396
+
2397
+ // src/utils/clipboard.ts
2398
+ import { spawnSync } from "child_process";
2399
+ function clipboardAttempts() {
2400
+ if (process.platform === "darwin") {
2401
+ return [
2402
+ {
2403
+ cmd: "pbcopy",
2404
+ args: []
2405
+ }
2406
+ ];
2407
+ }
2408
+ if (process.platform === "win32") {
2409
+ return [
2410
+ {
2411
+ cmd: "clip",
2412
+ args: []
2413
+ }
2414
+ ];
2415
+ }
2416
+ return [
2417
+ {
2418
+ cmd: "wl-copy",
2419
+ args: []
2420
+ },
2421
+ {
2422
+ cmd: "xclip",
2423
+ args: [
2424
+ "-selection",
2425
+ "clipboard"
2426
+ ]
2427
+ },
2428
+ {
2429
+ cmd: "xsel",
2430
+ args: [
2431
+ "--clipboard",
2432
+ "--input"
2433
+ ]
2434
+ }
2435
+ ];
2436
+ }
2437
+ __name(clipboardAttempts, "clipboardAttempts");
2438
+ function copyTextToClipboard(text) {
2439
+ const attempts = clipboardAttempts();
2440
+ let lastError = "No clipboard command available.";
2441
+ for (const attempt of attempts) {
2442
+ const result = spawnSync(attempt.cmd, attempt.args, {
2443
+ input: text,
2444
+ encoding: "utf8",
2445
+ stdio: [
2446
+ "pipe",
2447
+ "ignore",
2448
+ "pipe"
2449
+ ]
2450
+ });
2451
+ if (!result.error && result.status === 0) {
2452
+ return {
2453
+ ok: true
2454
+ };
2455
+ }
2456
+ lastError = (result.error?.message ?? result.stderr?.trim()) || `${attempt.cmd} exited with status ${result.status}`;
2457
+ }
2458
+ return {
2459
+ ok: false,
2460
+ error: lastError
2461
+ };
2462
+ }
2463
+ __name(copyTextToClipboard, "copyTextToClipboard");
2464
+
2465
+ // src/store/use-export-store.ts
2466
+ var EMPTY_EXPORT_JOB = {
2467
+ action: null,
2468
+ status: "idle"
2469
+ };
2470
+ async function runExport(action) {
2471
+ const state = useExportStore.getState();
2472
+ const boot = getBootContext();
2473
+ if (!boot) return;
2474
+ if (state.exportInFlight) {
2475
+ appendSessionLog("[warn] Export already in progress");
2476
+ return;
2477
+ }
2478
+ const format = state.exportFormat;
2479
+ useExportStore.setState({
2480
+ exportInFlight: true,
2481
+ exportJob: {
2482
+ action,
2483
+ status: "running",
2484
+ format
2485
+ }
2486
+ });
2487
+ setSessionStatusLine(action === "preview" ? "Previewing export..." : "Generating export...");
2488
+ const service2 = getService();
2489
+ const client2 = getClient();
2490
+ try {
2491
+ if (action === "status") {
2492
+ const status = await service2.exportStatus(boot.projectId, format, {
2493
+ mode: state.exportMode,
2494
+ scopePath: state.exportScopePath || void 0,
2495
+ organizationId: boot.organizationId
2496
+ });
2497
+ useExportStore.setState({
2498
+ exportStatus: status,
2499
+ exportJob: {
2500
+ action,
2501
+ status: "success",
2502
+ format
2503
+ }
2504
+ });
2505
+ setSessionStatusLine(`Export status: ${status.overall}`);
2506
+ appendSessionLog(`[export] Status ${format}: ${status.overall}`);
2507
+ return;
2508
+ }
2509
+ if (action === "commit" || action === "pr") {
2510
+ const delivery = action === "commit" ? "commit" : "pr";
2511
+ const result = await service2.deliverExport(boot.projectId, format, delivery, {
2512
+ mode: state.exportMode,
2513
+ scopePath: state.exportScopePath || void 0,
2514
+ organizationId: boot.organizationId
2515
+ });
2516
+ useExportStore.setState({
2517
+ exportJob: {
2518
+ action,
2519
+ status: "success",
2520
+ format
2521
+ }
2522
+ });
2523
+ setSessionStatusLine(result.message);
2524
+ appendSessionLog(`[export] ${result.message}`);
2525
+ return;
2526
+ }
2527
+ const output = action === "preview" ? await service2.previewExport(client2, boot.projectId, format) : await service2.generateExport(client2, boot.projectId, format);
2528
+ useExportStore.setState({
2529
+ exportPreview: output,
2530
+ exportJob: {
2531
+ action,
2532
+ status: "success",
2533
+ format
2534
+ }
2535
+ });
2536
+ setSessionStatusLine(action === "preview" ? "Export preview ready" : "Export generated");
2537
+ const verb = action === "preview" ? "Previewed" : "Generated";
2538
+ appendSessionLog(`[export] ${verb} ${format} (${output.anchorCount} anchors)`);
2539
+ } catch (error) {
2540
+ const message = error instanceof Error ? error.message : String(error);
2541
+ useExportStore.setState({
2542
+ exportJob: {
2543
+ action,
2544
+ status: "error",
2545
+ format,
2546
+ error: message
2547
+ }
2548
+ });
2549
+ setSessionStatusLine(action === "preview" ? "Export preview failed" : "Export generation failed");
2550
+ appendSessionLog(`[error] Export ${action} failed: ${message}`);
2551
+ refreshOnAuthenticationError(message);
2552
+ } finally {
2553
+ useExportStore.setState({
2554
+ exportInFlight: false
2555
+ });
2556
+ }
2557
+ }
2558
+ __name(runExport, "runExport");
2559
+ var useExportStore = create4()(subscribeWithSelector4((set, get) => ({
2560
+ exportFormat: "agents-md",
2561
+ exportMode: "global",
2562
+ exportScopePath: "",
2563
+ exportDelivery: "download",
2564
+ exportCapabilities: null,
2565
+ exportPreview: null,
2566
+ exportStatus: null,
2567
+ exportValidation: {
2568
+ open: false
2569
+ },
2570
+ exportJob: EMPTY_EXPORT_JOB,
2571
+ exportInFlight: false,
2572
+ setCapabilities: /* @__PURE__ */ __name((capabilities) => {
2573
+ set((prev) => {
2574
+ const discoveredFormats = capabilities?.formats?.map((f) => f.id) ?? [];
2575
+ const discoveredModes = capabilities?.modes ?? [];
2576
+ const resolvedFormat = discoveredFormats.length > 0 ? discoveredFormats.includes(prev.exportFormat) ? prev.exportFormat : discoveredFormats[0] : prev.exportFormat;
2577
+ const resolvedMode = discoveredModes.length > 0 ? discoveredModes.includes(prev.exportMode) ? prev.exportMode : discoveredModes[0] : prev.exportMode;
2578
+ return {
2579
+ exportCapabilities: capabilities,
2580
+ exportFormat: resolvedFormat,
2581
+ exportMode: resolvedMode
2582
+ };
2583
+ });
2584
+ }, "setCapabilities"),
2585
+ setExportFormat: /* @__PURE__ */ __name((format) => {
2586
+ set((prev) => {
2587
+ const allowedFormats = prev.exportCapabilities?.formats.map((f) => f.id) ?? [];
2588
+ if (allowedFormats.length > 0 && !allowedFormats.includes(format)) {
2589
+ setSessionStatusLine(`Export format unavailable: ${format}`);
2590
+ return prev;
2591
+ }
2592
+ if (prev.exportFormat === format) return prev;
2593
+ setSessionStatusLine(`Export format set to ${format}`);
2594
+ return {
2595
+ exportFormat: format,
2596
+ exportPreview: null,
2597
+ exportStatus: null,
2598
+ exportValidation: {
2599
+ open: false
2600
+ },
2601
+ exportJob: EMPTY_EXPORT_JOB
2602
+ };
2603
+ });
2604
+ }, "setExportFormat"),
2605
+ setExportMode: /* @__PURE__ */ __name((mode) => {
2606
+ set((prev) => {
2607
+ const allowedModes = prev.exportCapabilities?.modes ?? [];
2608
+ if (allowedModes.length > 0 && !allowedModes.includes(mode)) {
2609
+ setSessionStatusLine(`Export mode unavailable: ${mode}`);
2610
+ return prev;
2611
+ }
2612
+ setSessionStatusLine(`Export mode set to ${mode}`);
2613
+ return {
2614
+ exportMode: mode
2615
+ };
2616
+ });
2617
+ }, "setExportMode"),
2618
+ setExportScopePath: /* @__PURE__ */ __name((scopePath) => {
2619
+ setSessionStatusLine("Updated export scope path");
2620
+ set({
2621
+ exportScopePath: scopePath
2622
+ });
2623
+ }, "setExportScopePath"),
2624
+ setExportDelivery: /* @__PURE__ */ __name((delivery) => {
2625
+ setSessionStatusLine(`Export delivery set to ${delivery}`);
2626
+ set({
2627
+ exportDelivery: delivery
2628
+ });
2629
+ }, "setExportDelivery"),
2630
+ previewExport: /* @__PURE__ */ __name(async () => {
2631
+ await runExport("preview");
2632
+ }, "previewExport"),
2633
+ generateExport: /* @__PURE__ */ __name(async () => {
2634
+ setSessionStatusLine("Confirm export validation to write files to disk");
2635
+ set({
2636
+ exportValidation: {
2637
+ open: true
2638
+ }
2639
+ });
2640
+ }, "generateExport"),
2641
+ validateAndWriteExport: /* @__PURE__ */ __name(async () => {
2642
+ const state = get();
2643
+ const boot = getBootContext();
2644
+ if (!boot) return;
2645
+ if (state.exportInFlight) {
2646
+ appendSessionLog("[warn] Export already in progress");
2647
+ return;
2648
+ }
2649
+ const format = state.exportFormat;
2650
+ set({
2651
+ exportInFlight: true,
2652
+ exportValidation: {
2653
+ open: false
2654
+ },
2655
+ exportJob: {
2656
+ action: "generate",
2657
+ status: "running",
2658
+ format
2659
+ }
2660
+ });
2661
+ setSessionStatusLine("Validating export and writing files...");
2662
+ const service2 = getService();
2663
+ try {
2664
+ const result = await service2.deliverExport(boot.projectId, format, "download", {
2665
+ mode: state.exportMode,
2666
+ scopePath: state.exportScopePath || void 0,
2667
+ organizationId: boot.organizationId
2668
+ });
2669
+ set((prev) => ({
2670
+ exportStatus: result.localStatus ?? prev.exportStatus,
2671
+ exportJob: {
2672
+ action: "generate",
2673
+ status: "success",
2674
+ format
2675
+ }
2676
+ }));
2677
+ setSessionStatusLine(result.message);
2678
+ appendSessionLog(`[export] ${result.message}`);
2679
+ } catch (error) {
2680
+ const message = error instanceof Error ? error.message : String(error);
2681
+ set({
2682
+ exportJob: {
2683
+ action: "generate",
2684
+ status: "error",
2685
+ format,
2686
+ error: message
2687
+ }
2688
+ });
2689
+ setSessionStatusLine("Export validation/write failed");
2690
+ appendSessionLog(`[error] Export validate/write failed: ${message}`);
2691
+ refreshOnAuthenticationError(message);
2692
+ } finally {
2693
+ set({
2694
+ exportInFlight: false
2695
+ });
2696
+ }
2697
+ }, "validateAndWriteExport"),
2698
+ cancelExportValidation: /* @__PURE__ */ __name(() => {
2699
+ setSessionStatusLine("Export validation cancelled");
2700
+ set({
2701
+ exportValidation: {
2702
+ open: false
2703
+ }
2704
+ });
2705
+ }, "cancelExportValidation"),
2706
+ statusExport: /* @__PURE__ */ __name(async () => {
2707
+ await runExport("status");
2708
+ }, "statusExport"),
2709
+ commitExport: /* @__PURE__ */ __name(async () => {
2710
+ await runExport("commit");
2711
+ }, "commitExport"),
2712
+ createPrExport: /* @__PURE__ */ __name(async () => {
2713
+ await runExport("pr");
2714
+ }, "createPrExport"),
2715
+ copyExportPreview: /* @__PURE__ */ __name(() => {
2716
+ const { exportPreview, exportJob } = get();
2717
+ const hasGeneratedPreview = exportJob.status === "success" && (exportJob.action === "preview" || exportJob.action === "generate");
2718
+ if (!exportPreview?.content || !hasGeneratedPreview) {
2719
+ setSessionStatusLine("No generated preview to copy. Run p or g first.");
2720
+ appendSessionLog("[warn] export copy requested before preview/generate completed");
2721
+ return;
2722
+ }
2723
+ const copied = copyTextToClipboard(exportPreview.content);
2724
+ if (copied.ok) {
2725
+ setSessionStatusLine("Copied preview to clipboard");
2726
+ appendSessionLog("[export] copied preview to clipboard");
2727
+ return;
2728
+ }
2729
+ setSessionStatusLine("Clipboard copy failed");
2730
+ appendSessionLog(`[error] clipboard copy failed: ${copied.error}`);
2731
+ }, "copyExportPreview")
2732
+ })));
2733
+
2734
+ // src/store/use-bridge-store.ts
2735
+ import { create as create5 } from "zustand";
2736
+ import { subscribeWithSelector as subscribeWithSelector5 } from "zustand/middleware";
2737
+ var useBridgeStore = create5()(subscribeWithSelector5((set, get) => ({
2738
+ bridge: null,
2739
+ localBridge: null,
2740
+ bridgeLogs: [],
2741
+ bridgeLogsSince: 0,
2742
+ startedByTui: false,
2743
+ webmcpChannels: [],
2744
+ setBridgeData: /* @__PURE__ */ __name((bridge, localBridge) => set({
2745
+ bridge,
2746
+ localBridge
2747
+ }), "setBridgeData"),
2748
+ setLocalBridge: /* @__PURE__ */ __name((localBridge) => set({
2749
+ localBridge
2750
+ }), "setLocalBridge"),
2751
+ setBridgeLogs: /* @__PURE__ */ __name((logs) => {
2752
+ if (logs.length === 0) return;
2753
+ const lastTimestamp = logs[logs.length - 1].timestamp;
2754
+ set((prev) => ({
2755
+ bridgeLogs: [
2756
+ ...prev.bridgeLogs,
2757
+ ...logs
2758
+ ].slice(-200),
2759
+ bridgeLogsSince: lastTimestamp
2760
+ }));
2761
+ }, "setBridgeLogs"),
2762
+ clearBridgeLogs: /* @__PURE__ */ __name(() => set({
2763
+ bridgeLogs: []
2764
+ }), "clearBridgeLogs"),
2765
+ setWebMcpChannels: /* @__PURE__ */ __name((channels) => set({
2766
+ webmcpChannels: channels
2767
+ }), "setWebMcpChannels"),
2768
+ bridgeStart: /* @__PURE__ */ __name(() => {
2769
+ const service2 = getService();
2770
+ service2.startLocalBridgeDetached();
2771
+ set({
2772
+ startedByTui: true
2773
+ });
2774
+ appendSessionLog("[bridge] Started local bridge process (detached)");
2775
+ setSessionStatusLine("Bridge start triggered (detached)");
2776
+ }, "bridgeStart"),
2777
+ bridgeStop: /* @__PURE__ */ __name(async () => {
2778
+ const service2 = getService();
2779
+ const { localBridge } = get();
2780
+ await service2.stopLocalBridge(localBridge?.port);
2781
+ if (localBridge) {
2782
+ set({
2783
+ localBridge: {
2784
+ ...localBridge,
2785
+ running: false,
2786
+ uptimeSec: 0
2787
+ },
2788
+ webmcpChannels: []
2789
+ });
2790
+ }
2791
+ appendSessionLog("[bridge] Stop signal sent");
2792
+ setSessionStatusLine("Bridge stop signal sent");
2793
+ }, "bridgeStop")
2794
+ })));
2795
+
2796
+ // src/store/store-effects.ts
2797
+ function dispatchRefreshComplete(data) {
2798
+ useProjectDataStore.getState().setProjectData(data.specs, data.decisions, data.workflow, data.navPolicy);
2799
+ useExportStore.getState().setCapabilities(data.exportCapabilities);
2800
+ useBridgeStore.getState().setBridgeData(data.bridge, data.localBridge);
2801
+ }
2802
+ __name(dispatchRefreshComplete, "dispatchRefreshComplete");
2803
+ function dispatchClearProjectData() {
2804
+ useProjectDataStore.getState().clearProjectData();
2805
+ }
2806
+ __name(dispatchClearProjectData, "dispatchClearProjectData");
2807
+ function refreshOnAuthenticationError(message) {
2808
+ if (!isAuthenticationError(message)) return;
2809
+ const { appendLog, refresh } = useSessionStore.getState();
2810
+ useSessionStore.setState({
2811
+ statusLine: "Authentication required. Refreshing context..."
2812
+ });
2813
+ appendLog("[auth] action requires authentication, refreshing context");
2814
+ void refresh();
2815
+ }
2816
+ __name(refreshOnAuthenticationError, "refreshOnAuthenticationError");
2817
+ async function withSessionFeedback(opts) {
2818
+ const { appendLog, refresh } = useSessionStore.getState();
2819
+ useSessionStore.setState({
2820
+ statusLine: opts.statusLine
2821
+ });
2822
+ try {
2823
+ const result = await opts.action();
2824
+ appendLog(opts.successLog);
2825
+ if (opts.refresh !== false) {
2826
+ await refresh();
2827
+ }
2828
+ return result;
2829
+ } catch (error) {
2830
+ const message = error instanceof Error ? error.message : String(error);
2831
+ appendLog(`${opts.failureLog}: ${message}`);
2832
+ useSessionStore.setState({
2833
+ statusLine: opts.failureStatusLine
2834
+ });
2835
+ refreshOnAuthenticationError(message);
2836
+ throw error;
2837
+ }
2838
+ }
2839
+ __name(withSessionFeedback, "withSessionFeedback");
2840
+ function getBootContext() {
2841
+ return useSessionStore.getState().boot;
2842
+ }
2843
+ __name(getBootContext, "getBootContext");
2844
+ function setSessionStatusLine(line) {
2845
+ useSessionStore.setState({
2846
+ statusLine: line
2847
+ });
2848
+ }
2849
+ __name(setSessionStatusLine, "setSessionStatusLine");
2850
+ function appendSessionLog(entry) {
2851
+ useSessionStore.getState().appendLog(entry);
2852
+ }
2853
+ __name(appendSessionLog, "appendSessionLog");
2854
+
2855
+ // src/store/use-session-store.ts
2856
+ var onboardingInteractionResolver = null;
2857
+ function requestOnboardingInteraction(request) {
2858
+ return new Promise((resolve) => {
2859
+ onboardingInteractionResolver = resolve;
2860
+ useSessionStore.setState((prev) => ({
2861
+ onboarding: prev.onboarding ? {
2862
+ ...prev.onboarding,
2863
+ interaction: request
2864
+ } : prev.onboarding
2865
+ }));
2866
+ });
2867
+ }
2868
+ __name(requestOnboardingInteraction, "requestOnboardingInteraction");
2869
+ function refreshOnAuthenticationError2(message) {
2870
+ if (!isAuthenticationError(message)) return;
2871
+ const { appendLog, refresh } = useSessionStore.getState();
2872
+ useSessionStore.setState({
2873
+ statusLine: "Authentication required. Refreshing context..."
2874
+ });
2875
+ appendLog("[auth] action requires authentication, refreshing context");
2876
+ void refresh();
2877
+ }
2878
+ __name(refreshOnAuthenticationError2, "refreshOnAuthenticationError");
2879
+ async function buildOnboardingState(mode) {
2880
+ const service2 = getService();
2881
+ const boot = useSessionStore.getState().boot;
2882
+ const organizations = await service2.listOrganizations();
2883
+ if (organizations.length === 0) {
2884
+ return {
2885
+ mode,
2886
+ step: "create-organization",
2887
+ attachCurrentFolder: mode === "initial-setup",
2888
+ organizations: [],
2889
+ projects: [],
2890
+ availableAgents: [],
2891
+ canCreateProject: false,
2892
+ error: void 0
2893
+ };
2894
+ }
2895
+ const preferredOrganizationId = boot?.organizationId;
2896
+ const selectedOrganization = organizations.find((org) => org.id === preferredOrganizationId) ?? organizations[0];
2897
+ const projects = await service2.listProjects(selectedOrganization.id);
2898
+ return {
2899
+ mode,
2900
+ step: organizations.length === 1 ? "project" : "organization",
2901
+ attachCurrentFolder: mode === "initial-setup",
2902
+ organizations,
2903
+ projects,
2904
+ availableAgents: [],
2905
+ canCreateProject: service2.canCreateProject(selectedOrganization),
2906
+ selectedOrganizationId: selectedOrganization.id,
2907
+ interaction: void 0,
2908
+ error: void 0
2909
+ };
2910
+ }
2911
+ __name(buildOnboardingState, "buildOnboardingState");
2912
+ var useSessionStore = create6()(subscribeWithSelector6((set, get) => ({
2913
+ boot: null,
2914
+ auth: null,
2915
+ onboarding: null,
2916
+ loading: true,
2917
+ error: null,
2918
+ statusLine: "Bootstrapping...",
2919
+ logs: [],
2920
+ appendLog: /* @__PURE__ */ __name((entry) => {
2921
+ const screen = useTuiUiStore.getState().screen;
2922
+ const level = entry.startsWith("[error]") ? "error" : entry.startsWith("[warn]") ? "warn" : "info";
2923
+ appendGlobalStructuredLog({
2924
+ source: "tui.event-log",
2925
+ level,
2926
+ message: entry,
2927
+ details: {
2928
+ screen
2929
+ }
2930
+ });
2931
+ set((prev) => ({
2932
+ logs: [
2933
+ entry,
2934
+ ...prev.logs
2935
+ ].slice(0, 40)
2936
+ }));
2937
+ }, "appendLog"),
2938
+ setStatusLine: /* @__PURE__ */ __name((line) => set({
2939
+ statusLine: line
2940
+ }), "setStatusLine"),
2941
+ setLoading: /* @__PURE__ */ __name((loading) => set({
2942
+ loading
2943
+ }), "setLoading"),
2944
+ setError: /* @__PURE__ */ __name((error) => set({
2945
+ error
2946
+ }), "setError"),
2947
+ authenticate: /* @__PURE__ */ __name(async () => {
2948
+ const { appendLog } = get();
2949
+ const service2 = getService();
2950
+ set({
2951
+ loading: false,
2952
+ auth: {
2953
+ status: "authenticating"
2954
+ },
2955
+ statusLine: "Starting browser login flow..."
2956
+ });
2957
+ appendLog("[auth] starting login flow");
2958
+ const ok = await service2.authenticateViaCli((line) => {
2959
+ appendLog(`[auth] ${line}`);
2960
+ });
2961
+ if (!ok) {
2962
+ set({
2963
+ auth: {
2964
+ status: "auth_failed",
2965
+ error: "Authentication failed. Check logs and try again."
2966
+ },
2967
+ statusLine: "Authentication failed"
2968
+ });
2969
+ appendLog("[auth] login flow failed");
2970
+ return;
2971
+ }
2972
+ appendLog("[auth] login flow completed");
2973
+ await get().refresh();
2974
+ }, "authenticate"),
2975
+ refresh: /* @__PURE__ */ __name(async (projectId) => {
2976
+ const service2 = getService();
2977
+ set((prev) => ({
2978
+ loading: prev.boot ? false : true,
2979
+ statusLine: "Loading context..."
2980
+ }));
2981
+ try {
2982
+ const token = await service2.checkAuthentication();
2983
+ if (!token) {
2984
+ set({
2985
+ loading: false,
2986
+ error: null,
2987
+ auth: {
2988
+ status: "unauthenticated",
2989
+ error: "No valid credentials. Please run `spekn auth login` to authenticate."
2990
+ },
2991
+ statusLine: "Authentication required"
2992
+ });
2993
+ return;
2994
+ }
2995
+ set({
2996
+ auth: {
2997
+ status: "authenticated",
2998
+ userEmail: service2.extractUserEmail(token),
2999
+ tokenExpiresAt: service2.extractTokenExpiry(token)
3000
+ }
3001
+ });
3002
+ if (!projectId && !service2.hasLocalProjectContext()) {
3003
+ const onboarding = await buildOnboardingState("initial-setup");
3004
+ set({
3005
+ loading: false,
3006
+ error: null,
3007
+ onboarding,
3008
+ statusLine: "Project setup required"
3009
+ });
3010
+ return;
3011
+ }
3012
+ let bootstrapResult;
3013
+ try {
3014
+ bootstrapResult = await service2.bootstrap(projectId);
3015
+ } catch (bootstrapError) {
3016
+ if (bootstrapError instanceof Error && bootstrapError.message === "ONBOARDING_REQUIRED") {
3017
+ const onboarding = await buildOnboardingState("initial-setup");
3018
+ set({
3019
+ loading: false,
3020
+ error: null,
3021
+ onboarding,
3022
+ statusLine: "Project setup required"
3023
+ });
3024
+ return;
3025
+ }
3026
+ throw bootstrapError;
3027
+ }
3028
+ const { boot, client: client2 } = bootstrapResult;
3029
+ setClient(client2);
3030
+ const [specs, decisions, workflow, bridge, localBridge, exportCapabilities] = await Promise.all([
3031
+ service2.loadSpecs(client2, boot.projectId),
3032
+ service2.loadDecisions(client2, boot.projectId),
3033
+ service2.loadWorkflowSummary(client2, boot.projectId),
3034
+ service2.loadBridgeSummary(client2),
3035
+ service2.loadLocalBridgeSummary(),
3036
+ service2.discoverExportCapabilities(boot.projectId, boot.organizationId).catch(() => null)
3037
+ ]);
3038
+ const capabilityContext = {
3039
+ plan: boot.plan,
3040
+ role: boot.role,
3041
+ workflowPhase: workflow.currentPhase,
3042
+ permissions: boot.permissions
3043
+ };
3044
+ const navPolicy = resolveNavPolicy(capabilityContext);
3045
+ dispatchRefreshComplete({
3046
+ specs,
3047
+ decisions,
3048
+ workflow,
3049
+ navPolicy,
3050
+ exportCapabilities,
3051
+ bridge,
3052
+ localBridge
3053
+ });
3054
+ set({
3055
+ boot,
3056
+ loading: false,
3057
+ error: null,
3058
+ onboarding: null,
3059
+ statusLine: "Ready"
3060
+ });
3061
+ } catch (error) {
3062
+ const message = error instanceof Error ? error.message : String(error);
3063
+ let authStatus = null;
3064
+ if (message.includes("credentials") || message.includes("auth") || message.includes("token")) {
3065
+ authStatus = {
3066
+ status: "auth_failed",
3067
+ error: message
3068
+ };
3069
+ }
3070
+ set((prev) => ({
3071
+ loading: false,
3072
+ error: message,
3073
+ auth: authStatus || prev.auth,
3074
+ statusLine: "Error"
3075
+ }));
3076
+ get().appendLog(`[error] ${message}`);
3077
+ }
3078
+ }, "refresh"),
3079
+ switchContext: /* @__PURE__ */ __name(async () => {
3080
+ const { appendLog } = get();
3081
+ set({
3082
+ loading: true,
3083
+ error: null,
3084
+ statusLine: "Loading organizations..."
3085
+ });
3086
+ try {
3087
+ const onboarding = await buildOnboardingState("switch-context");
3088
+ set({
3089
+ loading: false,
3090
+ onboarding,
3091
+ statusLine: "Switch organization/project"
3092
+ });
3093
+ appendLog("[context] switch mode opened");
3094
+ } catch (error) {
3095
+ const message = error instanceof Error ? error.message : String(error);
3096
+ set({
3097
+ loading: false,
3098
+ statusLine: "Context switch failed",
3099
+ error: message
3100
+ });
3101
+ appendLog(`[error] context switch failed: ${message}`);
3102
+ refreshOnAuthenticationError2(message);
3103
+ }
3104
+ }, "switchContext"),
3105
+ detachRepoContext: /* @__PURE__ */ __name(async () => {
3106
+ const { boot, appendLog, refresh } = get();
3107
+ const service2 = getService();
3108
+ const result = await service2.detachContextViaCli(boot?.projectId, process.cwd());
3109
+ if (!result.success) {
3110
+ appendLog(`[error] Failed to detach context: ${result.output}`);
3111
+ set({
3112
+ statusLine: "Context detach failed"
3113
+ });
3114
+ return;
3115
+ }
3116
+ if (result.output) {
3117
+ appendLog(`[context] ${result.output}`);
3118
+ }
3119
+ appendLog("[context] Detached repository from local project context");
3120
+ set({
3121
+ boot: null,
3122
+ statusLine: "Repository detached. Select a project to attach."
3123
+ });
3124
+ dispatchClearProjectData();
3125
+ await refresh();
3126
+ }, "detachRepoContext"),
3127
+ syncRepository: /* @__PURE__ */ __name(async (options) => {
3128
+ const { boot, appendLog, refresh } = get();
3129
+ const service2 = getService();
3130
+ if (!boot) {
3131
+ appendLog("[error] repository sync unavailable: no active project context");
3132
+ set({
3133
+ statusLine: "Repository sync unavailable"
3134
+ });
3135
+ return false;
3136
+ }
3137
+ appendLog("[repo] running sync (metadata + drift analysis)");
3138
+ set({
3139
+ statusLine: "Syncing repository..."
3140
+ });
3141
+ const result = await service2.syncRepositoryViaCli(boot.organizationId, boot.projectId, boot.repoPath, {
3142
+ analyze: options?.analyze ?? true,
3143
+ importToProject: options?.importToProject,
3144
+ maxFiles: options?.maxFiles,
3145
+ analysisEngine: options?.analysisEngine,
3146
+ agent: options?.agent,
3147
+ requestInteraction: options?.requestInteraction,
3148
+ onProgress: /* @__PURE__ */ __name((line) => {
3149
+ appendLog(`[repo] ${line}`);
3150
+ options?.onProgress?.(line);
3151
+ }, "onProgress"),
3152
+ onActivity: /* @__PURE__ */ __name((event) => {
3153
+ options?.onActivity?.(event);
3154
+ }, "onActivity"),
3155
+ signal: options?.signal
3156
+ });
3157
+ if (!result.success) {
3158
+ appendLog(`[error] repository sync failed (exit ${result.exitCode})`);
3159
+ set({
3160
+ statusLine: "Repository sync failed"
3161
+ });
3162
+ refreshOnAuthenticationError2(result.output);
3163
+ return false;
3164
+ }
3165
+ appendLog("[repo] sync completed");
3166
+ set({
3167
+ statusLine: "Repository sync completed"
3168
+ });
3169
+ await refresh();
3170
+ return true;
3171
+ }, "syncRepository"),
3172
+ // Onboarding actions
3173
+ selectOnboardingOrganization: /* @__PURE__ */ __name(async (index) => {
3174
+ const { onboarding, appendLog } = get();
3175
+ const service2 = getService();
3176
+ if (!onboarding) return;
3177
+ if (index < 0 || index >= onboarding.organizations.length) {
3178
+ set((prev) => ({
3179
+ onboarding: prev.onboarding ? {
3180
+ ...prev.onboarding,
3181
+ error: `Invalid organization index: ${index + 1}`
3182
+ } : prev.onboarding
3183
+ }));
3184
+ return;
3185
+ }
3186
+ const selectedOrganization = onboarding.organizations[index];
3187
+ set((prev) => ({
3188
+ loading: true,
3189
+ statusLine: "Loading projects...",
3190
+ onboarding: prev.onboarding ? {
3191
+ ...prev.onboarding,
3192
+ selectedOrganizationId: selectedOrganization.id,
3193
+ error: void 0
3194
+ } : prev.onboarding
3195
+ }));
3196
+ try {
3197
+ const projects = await service2.listProjects(selectedOrganization.id);
3198
+ set((prev) => ({
3199
+ loading: false,
3200
+ statusLine: "Select a project",
3201
+ onboarding: prev.onboarding ? {
3202
+ ...prev.onboarding,
3203
+ step: "project",
3204
+ attachCurrentFolder: prev.onboarding.attachCurrentFolder,
3205
+ projects,
3206
+ availableAgents: prev.onboarding?.availableAgents ?? [],
3207
+ canCreateProject: service2.canCreateProject(selectedOrganization),
3208
+ selectedOrganizationId: selectedOrganization.id,
3209
+ interaction: void 0,
3210
+ error: projects.length === 0 ? "No projects found in selected organization." : void 0
3211
+ } : prev.onboarding
3212
+ }));
3213
+ } catch (error) {
3214
+ const message = error instanceof Error ? error.message : String(error);
3215
+ set((prev) => ({
3216
+ loading: false,
3217
+ statusLine: "Setup error",
3218
+ onboarding: prev.onboarding ? {
3219
+ ...prev.onboarding,
3220
+ error: message
3221
+ } : prev.onboarding
3222
+ }));
3223
+ }
3224
+ }, "selectOnboardingOrganization"),
3225
+ registerOnboardingProject: /* @__PURE__ */ __name(async (index) => {
3226
+ const { onboarding, appendLog, refresh } = get();
3227
+ const service2 = getService();
3228
+ if (!onboarding || !onboarding.selectedOrganizationId) return;
3229
+ if (index < 0 || index >= onboarding.projects.length) {
3230
+ set((prev) => ({
3231
+ onboarding: prev.onboarding ? {
3232
+ ...prev.onboarding,
3233
+ error: `Invalid project index: ${index + 1}`
3234
+ } : prev.onboarding
3235
+ }));
3236
+ return;
3237
+ }
3238
+ const selectedProject = onboarding.projects[index];
3239
+ if (onboarding.mode === "switch-context" || !onboarding.attachCurrentFolder) {
3240
+ set((prev) => ({
3241
+ loading: true,
3242
+ statusLine: "Switching context...",
3243
+ onboarding: prev.onboarding ? {
3244
+ ...prev.onboarding,
3245
+ error: void 0
3246
+ } : prev.onboarding
3247
+ }));
3248
+ try {
3249
+ service2.persistContext(onboarding.selectedOrganizationId, selectedProject.id);
3250
+ appendLog(`[context] switched to ${selectedProject.name}`);
3251
+ await refresh();
3252
+ } catch (error) {
3253
+ const message = error instanceof Error ? error.message : String(error);
3254
+ set((prev) => ({
3255
+ loading: false,
3256
+ statusLine: "Context switch failed",
3257
+ onboarding: prev.onboarding ? {
3258
+ ...prev.onboarding,
3259
+ step: "project",
3260
+ error: message
3261
+ } : prev.onboarding
3262
+ }));
3263
+ }
3264
+ return;
3265
+ }
3266
+ set((prev) => ({
3267
+ loading: true,
3268
+ statusLine: "Linking repository...",
3269
+ onboarding: prev.onboarding ? {
3270
+ ...prev.onboarding,
3271
+ error: void 0
3272
+ } : prev.onboarding
3273
+ }));
3274
+ try {
3275
+ persistProjectContext(process.cwd(), {
3276
+ projectId: selectedProject.id,
3277
+ organizationId: onboarding.selectedOrganizationId
3278
+ });
3279
+ appendLog(`[context] linked to ${selectedProject.name}`);
3280
+ await refresh();
3281
+ } catch (error) {
3282
+ const message = error instanceof Error ? error.message : String(error);
3283
+ set((prev) => ({
3284
+ loading: false,
3285
+ statusLine: "Setup error",
3286
+ onboarding: prev.onboarding ? {
3287
+ ...prev.onboarding,
3288
+ step: "project",
3289
+ error: message
3290
+ } : prev.onboarding
3291
+ }));
3292
+ }
3293
+ }, "registerOnboardingProject"),
3294
+ createOnboardingProject: /* @__PURE__ */ __name(async (name) => {
3295
+ const { onboarding, appendLog, refresh } = get();
3296
+ const service2 = getService();
3297
+ if (!onboarding || !onboarding.selectedOrganizationId) return;
3298
+ const trimmed = name.trim();
3299
+ if (!onboarding.canCreateProject) {
3300
+ set((prev) => ({
3301
+ onboarding: prev.onboarding ? {
3302
+ ...prev.onboarding,
3303
+ error: "You are not authorized to create projects in this organization."
3304
+ } : prev.onboarding
3305
+ }));
3306
+ return;
3307
+ }
3308
+ if (trimmed.length < 2) {
3309
+ set((prev) => ({
3310
+ onboarding: prev.onboarding ? {
3311
+ ...prev.onboarding,
3312
+ error: "Project name must be at least 2 characters."
3313
+ } : prev.onboarding
3314
+ }));
3315
+ return;
3316
+ }
3317
+ set((prev) => ({
3318
+ loading: true,
3319
+ statusLine: "Creating project...",
3320
+ onboarding: prev.onboarding ? {
3321
+ ...prev.onboarding,
3322
+ error: void 0
3323
+ } : prev.onboarding
3324
+ }));
3325
+ try {
3326
+ const created = await service2.createProject(onboarding.selectedOrganizationId, trimmed);
3327
+ appendLog(`[setup] Created project "${created.name}"`);
3328
+ if (onboarding.mode === "switch-context" || !onboarding.attachCurrentFolder) {
3329
+ service2.persistContext(onboarding.selectedOrganizationId, created.id);
3330
+ appendLog(`[context] switched to ${created.name}`);
3331
+ await refresh();
3332
+ return;
3333
+ }
3334
+ const registerStartedAt = Date.now();
3335
+ set((prev) => ({
3336
+ loading: true,
3337
+ statusLine: "Registering repository...",
3338
+ onboarding: prev.onboarding ? {
3339
+ ...prev.onboarding,
3340
+ step: "registering",
3341
+ availableAgents: prev.onboarding?.availableAgents ?? [],
3342
+ selectedProjectId: created.id,
3343
+ runLines: [],
3344
+ runStatus: "running",
3345
+ startedAt: registerStartedAt,
3346
+ interaction: void 0,
3347
+ error: void 0
3348
+ } : prev.onboarding
3349
+ }));
3350
+ useOnboardingWindow.getState().setStartedAt(registerStartedAt);
3351
+ await attachRepositoryFromOnboardingZustand({
3352
+ organizationId: onboarding.selectedOrganizationId,
3353
+ projectId: created.id
3354
+ });
3355
+ await refresh();
3356
+ } catch (error) {
3357
+ const message = error instanceof Error ? error.message : String(error);
3358
+ set((prev) => ({
3359
+ loading: false,
3360
+ statusLine: "Setup error",
3361
+ onboarding: prev.onboarding ? {
3362
+ ...prev.onboarding,
3363
+ error: message
3364
+ } : prev.onboarding
3365
+ }));
3366
+ }
3367
+ }, "createOnboardingProject"),
3368
+ resolveOnboardingInteraction: /* @__PURE__ */ __name((value) => {
3369
+ const resolver = onboardingInteractionResolver;
3370
+ onboardingInteractionResolver = null;
3371
+ set((prev) => ({
3372
+ onboarding: prev.onboarding ? {
3373
+ ...prev.onboarding,
3374
+ interaction: void 0
3375
+ } : prev.onboarding
3376
+ }));
3377
+ if (resolver) resolver(value);
3378
+ }, "resolveOnboardingInteraction"),
3379
+ setOnboardingAttachCurrentFolder: /* @__PURE__ */ __name((attach) => {
3380
+ set((prev) => ({
3381
+ onboarding: prev.onboarding ? prev.onboarding.attachCurrentFolder === attach ? prev.onboarding : {
3382
+ ...prev.onboarding,
3383
+ attachCurrentFolder: attach
3384
+ } : prev.onboarding
3385
+ }));
3386
+ }, "setOnboardingAttachCurrentFolder")
3387
+ })));
3388
+ async function attachRepositoryFromOnboardingZustand(input) {
3389
+ const service2 = getService();
3390
+ const { appendLog } = useSessionStore.getState();
3391
+ const result = await service2.attachOrSyncCurrentRepository(input.organizationId, input.projectId, (line) => {
3392
+ appendLog(`[setup] ${line}`);
3393
+ useSessionStore.setState((prev) => ({
3394
+ statusLine: line.slice(0, 100),
3395
+ onboarding: prev.onboarding ? {
3396
+ ...prev.onboarding,
3397
+ runLines: [
3398
+ ...prev.onboarding.runLines ?? [],
3399
+ line
3400
+ ].slice(-400)
3401
+ } : prev.onboarding
3402
+ }));
3403
+ }, void 0, requestOnboardingInteraction, (event) => {
3404
+ const { upsertToolCall, setActiveThought } = useOnboardingWindow.getState();
3405
+ if (event.event === "tool_call" || event.event === "tool_call_update") {
3406
+ upsertToolCall(event);
3407
+ } else if (event.event === "agent_thought") {
3408
+ setActiveThought(event.text);
3409
+ } else if (event.event === "agent_chunk" && event.text) {
3410
+ useSessionStore.setState((prev) => {
3411
+ if (!prev.onboarding) return prev;
3412
+ const lines = [
3413
+ ...prev.onboarding.runLines ?? []
3414
+ ];
3415
+ const parts = event.text.split("\n");
3416
+ if (lines.length === 0) {
3417
+ lines.push(parts[0]);
3418
+ } else {
3419
+ lines[lines.length - 1] += parts[0];
3420
+ }
3421
+ for (let i = 1; i < parts.length; i++) {
3422
+ lines.push(parts[i]);
3423
+ }
3424
+ return {
3425
+ onboarding: {
3426
+ ...prev.onboarding,
3427
+ runLines: lines.slice(-400)
3428
+ }
3429
+ };
3430
+ });
3431
+ }
3432
+ });
3433
+ useSessionStore.setState((prev) => ({
3434
+ onboarding: prev.onboarding ? {
3435
+ ...prev.onboarding,
3436
+ runStatus: result.warning ? "error" : "success"
3437
+ } : prev.onboarding
3438
+ }));
3439
+ logOnboardingAttachResult(appendLog, result);
3440
+ }
3441
+ __name(attachRepositoryFromOnboardingZustand, "attachRepositoryFromOnboardingZustand");
3442
+
3443
+ export {
3444
+ __name,
3445
+ appendGlobalStructuredLog,
3446
+ loadWebMcpChannels,
3447
+ initServiceBridge,
3448
+ getService,
3449
+ getClient,
3450
+ setInitialScreen,
3451
+ useTuiUiStore,
3452
+ resolveNavPolicy,
3453
+ useOnboardingWindow,
3454
+ buildOnboardingState,
3455
+ useSessionStore,
3456
+ useProjectDataStore,
3457
+ useExportStore,
3458
+ useBridgeStore
3459
+ };