@forloop-cc/forloop-cli 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +4 -86
  2. package/dist/forloop +888 -240
  3. package/package.json +1 -1
package/dist/forloop CHANGED
@@ -28,7 +28,7 @@ function buildSchema(options) {
28
28
  }
29
29
  function scanCommandPath(argv, globalOptions = []) {
30
30
  const globalSchema = buildSchema(globalOptions);
31
- const path3 = [];
31
+ const path6 = [];
32
32
  let i = 0;
33
33
  while (i < argv.length) {
34
34
  const arg = argv[i];
@@ -48,10 +48,10 @@ function scanCommandPath(argv, globalOptions = []) {
48
48
  i++;
49
49
  continue;
50
50
  }
51
- path3.push(arg);
51
+ path6.push(arg);
52
52
  i++;
53
53
  }
54
- return path3;
54
+ return path6;
55
55
  }
56
56
  function parseFlags(argv, options) {
57
57
  const schema = buildSchema(options);
@@ -142,8 +142,727 @@ var GLOBAL_OPTIONS = [
142
142
  { flag: "--version", description: "Print version" }
143
143
  ];
144
144
 
145
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/capabilities/token-storage.ts
146
+ import { promises as fs } from "fs";
147
+ import path from "path";
148
+ var getTokenPath = () => {
149
+ return path.join(
150
+ process.env.HOME || process.env.USERPROFILE || ".",
151
+ ".config",
152
+ "forloop",
153
+ "tokens.json"
154
+ );
155
+ };
156
+ async function loadTokens() {
157
+ const tokenPath = getTokenPath();
158
+ try {
159
+ const content = await fs.readFile(tokenPath, "utf-8");
160
+ return JSON.parse(content);
161
+ } catch (error) {
162
+ if (error.code === "ENOENT") {
163
+ return {
164
+ version: "1.0",
165
+ note: "ForLoop API tokens - do not share",
166
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
167
+ };
168
+ }
169
+ throw error;
170
+ }
171
+ }
172
+ async function saveTokens(tokens) {
173
+ const tokenPath = getTokenPath();
174
+ const configDir = path.dirname(tokenPath);
175
+ await fs.mkdir(configDir, { recursive: true });
176
+ await fs.writeFile(
177
+ tokenPath,
178
+ JSON.stringify(tokens, null, 2),
179
+ { mode: 384 }
180
+ );
181
+ }
182
+ async function getToken(profile) {
183
+ const tokens = await loadTokens();
184
+ if (profile && tokens.profiles?.[profile]) {
185
+ return tokens.profiles[profile];
186
+ }
187
+ if (tokens.default) return tokens.default;
188
+ if (tokens.profiles?.["default"]) return tokens.profiles["default"];
189
+ return null;
190
+ }
191
+ async function setToken(token, profile) {
192
+ const tokens = await loadTokens();
193
+ tokens.version = "1.0";
194
+ tokens.note = "ForLoop API tokens - do not share";
195
+ if (profile) {
196
+ if (!tokens.profiles) {
197
+ tokens.profiles = {};
198
+ }
199
+ tokens.profiles[profile] = token;
200
+ } else {
201
+ tokens.default = token;
202
+ }
203
+ tokens.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
204
+ await saveTokens(tokens);
205
+ }
206
+ async function clearToken(profile) {
207
+ const tokens = await loadTokens();
208
+ if (profile) {
209
+ if (tokens.profiles) {
210
+ delete tokens.profiles[profile];
211
+ }
212
+ } else {
213
+ delete tokens.default;
214
+ }
215
+ tokens.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
216
+ await saveTokens(tokens);
217
+ }
218
+
219
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/capabilities/config.ts
220
+ import fs2 from "fs";
221
+ import path2 from "path";
222
+ var PROD_API_URL = "https://api.forloop.cc";
223
+ var DEV_API_URL = "https://api.dev.forloop.cc";
224
+ var PROD_TOKENS_URL = "https://forloop.cc/profile?tab=api-tokens";
225
+ var DEV_TOKENS_URL = "https://dev.forloop.cc/profile?tab=api-tokens";
226
+ var readJsonFileIfExists = (filePath) => {
227
+ try {
228
+ if (!fs2.existsSync(filePath)) return null;
229
+ const raw = fs2.readFileSync(filePath, "utf-8");
230
+ return JSON.parse(raw);
231
+ } catch {
232
+ return null;
233
+ }
234
+ };
235
+ var getHomeDir = () => {
236
+ return process.env.HOME || process.env.USERPROFILE || "";
237
+ };
238
+ var getProjectConfig = () => {
239
+ return readJsonFileIfExists(path2.join(process.cwd(), "opencode.json"));
240
+ };
241
+ var getGlobalConfig = () => {
242
+ const home = getHomeDir();
243
+ if (!home) return null;
244
+ return readJsonFileIfExists(path2.join(home, ".config", "opencode", "config.json"));
245
+ };
246
+ var getNested = (obj, keys) => {
247
+ let cur = obj;
248
+ for (const key of keys) {
249
+ if (!cur || typeof cur !== "object") return void 0;
250
+ cur = cur[key];
251
+ }
252
+ return cur;
253
+ };
254
+ var isTruthy = (value) => {
255
+ if (typeof value === "boolean") return value;
256
+ if (typeof value === "string") return value.toLowerCase() === "true" || value === "1";
257
+ return false;
258
+ };
259
+ var normalizeUrl = (value) => {
260
+ if (typeof value !== "string") return null;
261
+ const trimmed = value.trim();
262
+ if (!trimmed) return null;
263
+ return trimmed;
264
+ };
265
+ var isDevUrl = (apiUrl) => {
266
+ return apiUrl.includes("api.dev.forloop.cc") || apiUrl.includes("dev.forloop.cc");
267
+ };
268
+ var resolveAllowDev = (projectConfig, globalConfig) => {
269
+ return isTruthy(process.env.FORLOOP_ALLOW_DEV) || isTruthy(getNested(projectConfig, ["forloop", "allowDev"])) || isTruthy(getNested(globalConfig, ["forloop", "allowDev"]));
270
+ };
271
+ var resolveApiUrl = (projectConfig, globalConfig, allowDev) => {
272
+ const envApiUrl = normalizeUrl(process.env.FORLOOP_API_URL || process.env.FORLOOP_API_BASE);
273
+ if (envApiUrl) {
274
+ if (isDevUrl(envApiUrl) && !allowDev) {
275
+ console.warn("[ForLoop Config] Dev API URL ignored (set FORLOOP_ALLOW_DEV=true to enable)");
276
+ return PROD_API_URL;
277
+ }
278
+ return envApiUrl;
279
+ }
280
+ const envName = normalizeUrl(process.env.FORLOOP_ENV);
281
+ if (envName) {
282
+ if (envName === "production") return PROD_API_URL;
283
+ if (envName === "development") {
284
+ if (!allowDev) {
285
+ console.warn("[ForLoop Config] FORLOOP_ENV=development ignored (set FORLOOP_ALLOW_DEV=true to enable)");
286
+ return PROD_API_URL;
287
+ }
288
+ return DEV_API_URL;
289
+ }
290
+ }
291
+ const projectApiUrl = normalizeUrl(getNested(projectConfig, ["forloop", "apiUrl"]));
292
+ if (projectApiUrl) {
293
+ if (isDevUrl(projectApiUrl) && !allowDev) {
294
+ console.warn("[ForLoop Config] Project dev API URL ignored (set forloop.allowDev=true or FORLOOP_ALLOW_DEV=true)");
295
+ return PROD_API_URL;
296
+ }
297
+ return projectApiUrl;
298
+ }
299
+ const globalApiUrl = normalizeUrl(getNested(globalConfig, ["forloop", "apiUrl"]));
300
+ if (globalApiUrl) {
301
+ if (isDevUrl(globalApiUrl) && !allowDev) {
302
+ console.warn("[ForLoop Config] Global dev API URL ignored (set forloop.allowDev=true or FORLOOP_ALLOW_DEV=true)");
303
+ return PROD_API_URL;
304
+ }
305
+ return globalApiUrl;
306
+ }
307
+ return PROD_API_URL;
308
+ };
309
+ var detectEnvironment = (apiUrl) => {
310
+ if (apiUrl.includes("dev.forloop.cc")) {
311
+ return "development";
312
+ }
313
+ if (apiUrl.includes("api.forloop.cc")) {
314
+ return "production";
315
+ }
316
+ return "custom";
317
+ };
318
+ var getConfig = () => {
319
+ const projectConfig = getProjectConfig();
320
+ const globalConfig = getGlobalConfig();
321
+ const allowDev = resolveAllowDev(projectConfig, globalConfig);
322
+ const apiUrl = resolveApiUrl(projectConfig, globalConfig, allowDev);
323
+ return {
324
+ apiUrl,
325
+ environment: detectEnvironment(apiUrl),
326
+ tokenStorage: "global",
327
+ debug: process.env.FORLOOP_DEBUG === "true",
328
+ allowDev
329
+ };
330
+ };
331
+ var getTokensUrl = () => {
332
+ const config = getConfig();
333
+ if (config.environment === "development") return DEV_TOKENS_URL;
334
+ return PROD_TOKENS_URL;
335
+ };
336
+ var isLambdaExecution = () => {
337
+ const forced = (process.env.FORLOOP_EXECUTION_CONTEXT || "").toLowerCase();
338
+ if (forced === "lambda" || forced === "aws_lambda" || forced === "aws-lambda") return true;
339
+ return !!(process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.AWS_EXECUTION_ENV || process.env.LAMBDA_TASK_ROOT);
340
+ };
341
+
342
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/capabilities/auth.ts
343
+ function detectTokenType(token) {
344
+ if (!token) return null;
345
+ if (token.startsWith("floop_")) {
346
+ return "api_key";
347
+ }
348
+ const parts = token.split(".");
349
+ if (parts.length === 3 && parts.every((p) => p.length > 0)) {
350
+ const base64urlRegex = /^[A-Za-z0-9_-]+$/;
351
+ if (parts.every((p) => base64urlRegex.test(p))) {
352
+ return "jwt";
353
+ }
354
+ }
355
+ return null;
356
+ }
357
+ async function validateToken(providedToken) {
358
+ const token = providedToken || await getToken();
359
+ if (!token) {
360
+ return {
361
+ valid: false,
362
+ error: "No API token configured. Please create a token at " + getTokensUrl()
363
+ };
364
+ }
365
+ const tokenType = detectTokenType(token);
366
+ if (!tokenType) {
367
+ return {
368
+ valid: false,
369
+ error: "Unrecognized token format. Use either a ForLoop API key (floop_*) or a valid JWT token."
370
+ };
371
+ }
372
+ if (tokenType === "jwt") {
373
+ const isLambda = isLambdaExecution();
374
+ if (!isLambda) {
375
+ return {
376
+ valid: false,
377
+ error: "JWT tokens are only supported in Lambda execution. Please use a ForLoop API key (floop_*) for local execution."
378
+ };
379
+ }
380
+ return {
381
+ valid: true,
382
+ token,
383
+ tokenType: "jwt"
384
+ };
385
+ }
386
+ return {
387
+ valid: true,
388
+ token,
389
+ tokenType: "api_key"
390
+ };
391
+ }
392
+
393
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/capabilities/api-client.ts
394
+ var APIError = class extends Error {
395
+ constructor(code, message, status) {
396
+ super(message);
397
+ this.code = code;
398
+ this.status = status;
399
+ this.name = "APIError";
400
+ }
401
+ };
402
+ var ForLoopAPIClient = class {
403
+ config;
404
+ defaultHeaders;
405
+ tokenType;
406
+ jwtExchangePromise;
407
+ constructor(config) {
408
+ this.config = {
409
+ timeout: 3e4,
410
+ retryCount: 3,
411
+ ...config
412
+ };
413
+ this.defaultHeaders = {
414
+ "Content-Type": "application/json"
415
+ };
416
+ this.tokenType = detectTokenType(config.token);
417
+ this.jwtExchangePromise = null;
418
+ }
419
+ setToken(token) {
420
+ this.config.token = token;
421
+ this.tokenType = detectTokenType(token);
422
+ }
423
+ async ensureOpencodeApiToken(endpoint) {
424
+ if (this.tokenType !== "jwt") return;
425
+ if (!endpoint.startsWith("/api/opencode/")) return;
426
+ if (endpoint.startsWith("/api/opencode/auth/tokens")) return;
427
+ if (!this.jwtExchangePromise) {
428
+ this.jwtExchangePromise = this.exchangeJwtForApiToken();
429
+ }
430
+ const token = await this.jwtExchangePromise;
431
+ this.setToken(token);
432
+ }
433
+ async exchangeJwtForApiToken() {
434
+ const url = `${this.config.baseUrl}/api/opencode/auth/tokens`;
435
+ const jwt = this.config.token;
436
+ const scopes = [
437
+ "sprint:read",
438
+ "sprint:write",
439
+ "story:read",
440
+ "story:write",
441
+ "story:delete",
442
+ "agent:read",
443
+ "agent:query",
444
+ "profile:read",
445
+ "organization:read",
446
+ "organization:write",
447
+ "organization:delete"
448
+ ];
449
+ const name = `lambda-planner-${Date.now()}`;
450
+ const response = await fetch(url, {
451
+ method: "POST",
452
+ headers: {
453
+ ...this.defaultHeaders,
454
+ Authorization: `Bearer ${jwt}`
455
+ },
456
+ body: JSON.stringify({
457
+ name,
458
+ expiresIn: "7d",
459
+ scopes
460
+ }),
461
+ signal: AbortSignal.timeout(this.config.timeout)
462
+ });
463
+ if (!response.ok) {
464
+ throw await this.parseError(response);
465
+ }
466
+ const payload = await response.json();
467
+ const token = typeof payload?.token === "string" ? payload.token.trim() : "";
468
+ if (!token) {
469
+ throw new APIError("invalid_token_exchange", "Token exchange returned empty token", 500);
470
+ }
471
+ await setToken(token);
472
+ return token;
473
+ }
474
+ /**
475
+ * Get auth headers based on token type
476
+ * - JWT tokens: Authorization: Bearer <token>
477
+ * - API keys: X-API-Token: <token>
478
+ */
479
+ getAuthHeaders() {
480
+ const headers = {};
481
+ if (this.tokenType === "jwt") {
482
+ headers["Authorization"] = `Bearer ${this.config.token}`;
483
+ } else {
484
+ headers["X-API-Token"] = this.config.token;
485
+ }
486
+ return headers;
487
+ }
488
+ async request(endpoint, options = {}) {
489
+ await this.ensureOpencodeApiToken(endpoint);
490
+ const url = `${this.config.baseUrl}${endpoint}`;
491
+ let lastError = null;
492
+ for (let attempt = 1; attempt <= this.config.retryCount; attempt++) {
493
+ try {
494
+ const response = await fetch(url, {
495
+ ...options,
496
+ headers: {
497
+ ...this.defaultHeaders,
498
+ ...this.getAuthHeaders(),
499
+ // Dynamic auth headers based on token type
500
+ ...options.headers || {}
501
+ },
502
+ signal: AbortSignal.timeout(this.config.timeout)
503
+ });
504
+ if (!response.ok) {
505
+ const error = await this.parseError(response);
506
+ throw error;
507
+ }
508
+ return response.json();
509
+ } catch (error) {
510
+ lastError = error;
511
+ if (error instanceof APIError && error.status >= 400 && error.status < 500) {
512
+ throw error;
513
+ }
514
+ if (attempt < this.config.retryCount) {
515
+ await this.sleep(Math.pow(2, attempt) * 100);
516
+ }
517
+ }
518
+ }
519
+ throw lastError || new Error("Request failed");
520
+ }
521
+ async parseError(response) {
522
+ try {
523
+ const body = await response.json();
524
+ return new APIError(body.error || "UnknownError", body.message || response.statusText, response.status);
525
+ } catch {
526
+ return new APIError("UnknownError", response.statusText, response.status);
527
+ }
528
+ }
529
+ sleep(ms) {
530
+ return new Promise((resolve) => setTimeout(resolve, ms));
531
+ }
532
+ async listSprints(params) {
533
+ const query = new URLSearchParams(params);
534
+ const response = await this.request(`/api/opencode/sprints?${query}`);
535
+ return Array.isArray(response) ? response : response.data || [];
536
+ }
537
+ async getSprint(id, options) {
538
+ const query = new URLSearchParams({
539
+ includeStories: options?.includeStories !== false ? "true" : "false",
540
+ includeFiles: options?.includeFiles !== false ? "true" : "false"
541
+ });
542
+ return this.request(`/api/opencode/sprints/${id}?${query}`);
543
+ }
544
+ async createSprint(data) {
545
+ return this.request("/api/opencode/sprints", {
546
+ method: "POST",
547
+ body: JSON.stringify(data)
548
+ });
549
+ }
550
+ async updateSprint(id, data) {
551
+ return this.request(`/api/opencode/sprints/${id}`, {
552
+ method: "PUT",
553
+ body: JSON.stringify(data)
554
+ });
555
+ }
556
+ async deleteSprint(id) {
557
+ await this.request(`/api/opencode/sprints/${id}`, {
558
+ method: "DELETE"
559
+ });
560
+ }
561
+ async createStory(data) {
562
+ return this.request("/api/opencode/stories", {
563
+ method: "POST",
564
+ body: JSON.stringify(data)
565
+ });
566
+ }
567
+ async getStory(id, options) {
568
+ const params = new URLSearchParams();
569
+ if (options?.includeComments) params.set("includeComments", "true");
570
+ const query = params.toString() ? `?${params.toString()}` : "";
571
+ return this.request(`/api/opencode/stories/${id}${query}`);
572
+ }
573
+ async updateStory(id, data) {
574
+ return this.request(`/api/opencode/stories/${id}`, {
575
+ method: "PUT",
576
+ body: JSON.stringify(data)
577
+ });
578
+ }
579
+ async deleteStory(id) {
580
+ await this.request(`/api/opencode/stories/${id}`, {
581
+ method: "DELETE"
582
+ });
583
+ }
584
+ async listTemplates() {
585
+ return this.request("/api/opencode/templates");
586
+ }
587
+ async getUserProfile() {
588
+ return this.request("/api/opencode/user/profile");
589
+ }
590
+ async getUserQuotas() {
591
+ return this.request("/api/opencode/user/quotas");
592
+ }
593
+ async getOrganizationQuotas(orgId) {
594
+ return this.request(`/api/opencode/organization/${orgId}/quotas`);
595
+ }
596
+ async listOrganizations() {
597
+ return this.request("/api/opencode/organizations");
598
+ }
599
+ async getOwnedOrganizations() {
600
+ return this.request("/api/opencode/organizations/owned");
601
+ }
602
+ async getOrganization(id) {
603
+ return this.request(`/api/opencode/organizations/${id}`);
604
+ }
605
+ async createOrganization(data) {
606
+ return this.request("/api/opencode/organizations", {
607
+ method: "POST",
608
+ body: JSON.stringify(data)
609
+ });
610
+ }
611
+ async updateOrganization(id, data) {
612
+ return this.request(`/api/opencode/organizations/${id}`, {
613
+ method: "PUT",
614
+ body: JSON.stringify(data)
615
+ });
616
+ }
617
+ async deleteOrganization(id) {
618
+ await this.request(`/api/opencode/organizations/${id}`, {
619
+ method: "DELETE"
620
+ });
621
+ }
622
+ async chatWithAI(request) {
623
+ return this.request("/api/opencode/agents/chat", {
624
+ method: "POST",
625
+ body: JSON.stringify(request)
626
+ });
627
+ }
628
+ async getDeveloperStatus(sprintId) {
629
+ return this.request(`/api/opencode/sprints/${sprintId}/developer-status`);
630
+ }
631
+ async getStorySuggestions(sprintId, storyId) {
632
+ return this.request("/api/opencode/agents/suggestions", {
633
+ method: "POST",
634
+ body: JSON.stringify({ sprintId, storyId })
635
+ });
636
+ }
637
+ async getAITasks(sprintId) {
638
+ const query = sprintId ? new URLSearchParams({ sprintId: String(sprintId) }) : new URLSearchParams();
639
+ return this.request(`/api/opencode/agents/tasks?${query}`);
640
+ }
641
+ async getSprintSummaries(sprintId) {
642
+ return this.request(`/api/opencode/agents/summary?sprintId=${sprintId}`);
643
+ }
644
+ async getAgentSuggestions(request) {
645
+ return this.request("/api/agents/suggestions", {
646
+ method: "POST",
647
+ body: JSON.stringify(request)
648
+ });
649
+ }
650
+ async getStoryBreakdown(storyId) {
651
+ return this.request(`/api/agents/stories/${storyId}/breakdown`);
652
+ }
653
+ async getStoryEstimate(storyId) {
654
+ return this.request(`/api/agents/stories/${storyId}/estimate`);
655
+ }
656
+ async getConversationHistory(params) {
657
+ const query = new URLSearchParams();
658
+ if (params?.sprintId) query.append("sprintId", String(params.sprintId));
659
+ if (params?.limit) query.append("limit", String(params.limit));
660
+ return this.request(`/api/opencode/messages?${query}`);
661
+ }
662
+ async clearConversationHistory(params) {
663
+ const query = new URLSearchParams();
664
+ if (params?.sprintId) query.append("sprintId", String(params.sprintId));
665
+ query.append("confirm", "true");
666
+ return this.request(`/api/opencode/messages?${query}`, {
667
+ method: "DELETE"
668
+ });
669
+ }
670
+ async createPresignedUpload(data) {
671
+ return this.request("/api/opencode/files/presign", {
672
+ method: "POST",
673
+ body: JSON.stringify(data)
674
+ });
675
+ }
676
+ async completeUpload(data) {
677
+ return this.request("/api/opencode/files/complete", {
678
+ method: "POST",
679
+ body: JSON.stringify(data)
680
+ });
681
+ }
682
+ async getSprintFiles(sprintId) {
683
+ return this.request(`/api/opencode/files/sprint/${sprintId}`);
684
+ }
685
+ async deleteFile(fileId) {
686
+ await this.request(`/api/opencode/files/${fileId}`, {
687
+ method: "DELETE"
688
+ });
689
+ }
690
+ async logFileRead(fileId) {
691
+ await this.request(`/api/opencode/files/${fileId}/read`, {
692
+ method: "POST"
693
+ });
694
+ }
695
+ async getFileDownloadUrl(fileId) {
696
+ return this.request(`/api/opencode/files/download/${fileId}`);
697
+ }
698
+ async getFileOpenUrl(fileId) {
699
+ return this.request(`/api/opencode/files/open-url/${fileId}`);
700
+ }
701
+ async updateFilePosition(fileId, data) {
702
+ return this.request(`/api/opencode/files/${fileId}/position`, {
703
+ method: "PUT",
704
+ body: JSON.stringify(data)
705
+ });
706
+ }
707
+ async listAiAgents() {
708
+ const response = await this.request("/api/opencode/ai/agents");
709
+ return response.agents || [];
710
+ }
711
+ async updateSprintAiAgents(sprintId, enabledAgentKeys) {
712
+ const response = await this.request(`/api/opencode/sprints/${sprintId}/ai-agents`, {
713
+ method: "PUT",
714
+ body: JSON.stringify({ enabledAgentKeys })
715
+ });
716
+ return response.sprintAiAgents || [];
717
+ }
718
+ async updateSchedule(storyId, data) {
719
+ return this.updateStory(storyId, {
720
+ title: data.title,
721
+ description: data.description,
722
+ metadata: data
723
+ });
724
+ }
725
+ async recordMessage(data) {
726
+ return this.request("/api/opencode/messages", {
727
+ method: "POST",
728
+ body: JSON.stringify(data)
729
+ });
730
+ }
731
+ async removeMessage(messageId, sprintId, sessionId) {
732
+ await this.request(`/api/opencode/messages/${encodeURIComponent(messageId)}`, {
733
+ method: "DELETE",
734
+ body: JSON.stringify({ sprintId, sessionId })
735
+ });
736
+ }
737
+ };
738
+
739
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/sprint-tools.ts
740
+ import { tool } from "@opencode-ai/plugin";
741
+
742
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/capabilities/context-resolver.ts
743
+ import { exec } from "child_process";
744
+ import { promisify } from "util";
745
+ import * as fs3 from "fs";
746
+ import * as path3 from "path";
747
+ var execAsync = promisify(exec);
748
+ async function getCurrentBranch(directory) {
749
+ try {
750
+ const result = await execAsync("git rev-parse --abbrev-ref HEAD", {
751
+ cwd: directory
752
+ });
753
+ return result.stdout.trim();
754
+ } catch (error) {
755
+ throw new Error("Git not available or not in a repository");
756
+ }
757
+ }
758
+ async function resolveSprintFromBranch(branch) {
759
+ const match = branch.match(/sprint-(\d+)/);
760
+ if (match) {
761
+ return parseInt(match[1], 10);
762
+ }
763
+ return null;
764
+ }
765
+ async function resolveSprintId(providedId, directory) {
766
+ if (providedId) {
767
+ return { sprintId: providedId, source: "provided" };
768
+ }
769
+ const envSprintId = process.env.FORLOOP_SPRINT_ID;
770
+ if (envSprintId) {
771
+ return { sprintId: parseInt(envSprintId, 10), source: "env" };
772
+ }
773
+ const manifestSprintId = getActiveSprintIdFromManifest(directory);
774
+ if (manifestSprintId) {
775
+ return { sprintId: manifestSprintId, source: "manifest" };
776
+ }
777
+ try {
778
+ const branch = await getCurrentBranch(directory);
779
+ const sprintId = await resolveSprintFromBranch(branch);
780
+ if (sprintId) {
781
+ return { sprintId, branch, source: "branch" };
782
+ }
783
+ } catch (error) {
784
+ }
785
+ return { source: "config" };
786
+ }
787
+ function getActiveSprintIdFromManifest(directory) {
788
+ const candidates = [];
789
+ if (directory) {
790
+ candidates.push(path3.join(directory, ".forloop", "manifest.json"));
791
+ }
792
+ const home = process.env.HOME;
793
+ if (home) {
794
+ candidates.push(path3.join(home, ".forloop", "manifest.json"));
795
+ }
796
+ for (const filePath of candidates) {
797
+ try {
798
+ if (!fs3.existsSync(filePath)) continue;
799
+ const raw = fs3.readFileSync(filePath, "utf8");
800
+ const parsed = JSON.parse(raw);
801
+ const id = parsed?.activeSprintId;
802
+ if (Number.isFinite(id)) return Number(id);
803
+ } catch {
804
+ }
805
+ }
806
+ return null;
807
+ }
808
+ var DEFAULT_HOME = "/tmp/home";
809
+ function getSprintDir(sprintId) {
810
+ return `sprint-${sprintId}`;
811
+ }
812
+ function getForloopRoot(sprintId) {
813
+ const home = process.env.HOME || DEFAULT_HOME;
814
+ const root = path3.join(home, ".forloop");
815
+ if (sprintId !== void 0) {
816
+ return path3.join(root, getSprintDir(sprintId));
817
+ }
818
+ return root;
819
+ }
820
+
821
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/story-tools.ts
822
+ import { tool as tool2 } from "@opencode-ai/plugin";
823
+
824
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/template-tools.ts
825
+ import { tool as tool3 } from "@opencode-ai/plugin";
826
+
827
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/user-tools.ts
828
+ import { tool as tool4 } from "@opencode-ai/plugin";
829
+
830
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/organization-tools.ts
831
+ import { tool as tool5 } from "@opencode-ai/plugin";
832
+
833
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/file-tools.ts
834
+ import { tool as tool6 } from "@opencode-ai/plugin";
835
+
836
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/schedule-tools.ts
837
+ import { tool as tool7 } from "@opencode-ai/plugin";
838
+
839
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/folder-tools.ts
840
+ import { tool as tool8 } from "@opencode-ai/plugin";
841
+
842
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/sync-tools.ts
843
+ import { tool as tool9 } from "@opencode-ai/plugin";
844
+
845
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/agent-tools.ts
846
+ import { tool as tool10 } from "@opencode-ai/plugin";
847
+
848
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/agent-suggestion-tools.ts
849
+ import { tool as tool11 } from "@opencode-ai/plugin";
850
+
851
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/agent-sprint-tools.ts
852
+ import { tool as tool12 } from "@opencode-ai/plugin";
853
+
854
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/ai-trigger-tools.ts
855
+ import { tool as tool13 } from "@opencode-ai/plugin";
856
+
857
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/tools/developer-status-tool.ts
858
+ import { tool as tool14 } from "@opencode-ai/plugin";
859
+
860
+ // node_modules/@forloop-cc/forloop-opencode-plugin-planner/plugins/hooks/shell-env.ts
861
+ import { exec as exec2 } from "child_process";
862
+ import { promisify as promisify2 } from "util";
863
+ var execAsync2 = promisify2(exec2);
864
+
145
865
  // src/commands/auth/index.ts
146
- import { getToken, setToken, clearToken, validateToken } from "@forloop-cc/forloop-opencode-plugin-planner";
147
866
  function createAuthCommands() {
148
867
  return {
149
868
  "auth login": createAuthLoginCommand(),
@@ -193,7 +912,6 @@ function createAuthStatusCommand() {
193
912
  } else {
194
913
  console.log("Not authenticated");
195
914
  console.log("Run: forloop auth login --api-key <token>");
196
- process.exit(1);
197
915
  }
198
916
  }
199
917
  });
@@ -210,8 +928,26 @@ function createAuthLogoutCommand() {
210
928
  });
211
929
  }
212
930
 
931
+ // src/output/formatter.ts
932
+ function print(config, data, label) {
933
+ if (config.output === "json") {
934
+ console.log(JSON.stringify(data, null, 2));
935
+ } else if (typeof data === "string") {
936
+ if (label && !config.quiet) console.log(label);
937
+ console.log(data);
938
+ } else {
939
+ if (label && !config.quiet) console.log(label);
940
+ console.log(JSON.stringify(data, null, 2));
941
+ }
942
+ }
943
+ function formatError(message, format) {
944
+ if (format === "json") {
945
+ return JSON.stringify({ error: { message } }, null, 2);
946
+ }
947
+ return `Error: ${message}`;
948
+ }
949
+
213
950
  // src/commands/story.ts
214
- import { ForLoopAPIClient, getToken as getToken2, getConfig } from "@forloop-cc/forloop-opencode-plugin-planner";
215
951
  function createStoryCommands() {
216
952
  return {
217
953
  "story create": createStoryCreateCommand(),
@@ -222,12 +958,12 @@ function createStoryCommands() {
222
958
  }
223
959
  async function getClient() {
224
960
  const config = getConfig();
225
- const token = await getToken2();
961
+ const token = await getToken();
226
962
  if (!token) {
227
963
  console.error("Error: No API token configured. Run: forloop auth login --api-key <token>");
228
964
  process.exit(1);
229
965
  }
230
- const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl });
966
+ const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 });
231
967
  return { client };
232
968
  }
233
969
  function createStoryCreateCommand() {
@@ -249,7 +985,7 @@ function createStoryCreateCommand() {
249
985
  'forloop story create --title "Meeting notes" --sprint 1 --type basic-note',
250
986
  'forloop story create --title "Design Docs" --sprint 1'
251
987
  ],
252
- async run(_config, flags) {
988
+ async run(config, flags) {
253
989
  const resolved = await getClient();
254
990
  if (!resolved) return;
255
991
  const { client } = resolved;
@@ -289,7 +1025,7 @@ function createStoryCreateCommand() {
289
1025
  points: flags.points
290
1026
  })
291
1027
  });
292
- console.log(`Story created: #${story.id} - ${story.title} (${story.type})`);
1028
+ print(config, story, `Story created: #${story.id} - ${story.title} (${story.type})`);
293
1029
  } else {
294
1030
  const story = await client.createStory({
295
1031
  title: flags.title,
@@ -299,7 +1035,7 @@ function createStoryCreateCommand() {
299
1035
  priority: flags.priority || "medium",
300
1036
  status: "todo"
301
1037
  });
302
- console.log(`Doc folder created: #${story.id} - ${story.title}`);
1038
+ print(config, story, `Doc folder created: #${story.id} - ${story.title}`);
303
1039
  }
304
1040
  }
305
1041
  });
@@ -314,7 +1050,7 @@ function createStoryGetCommand() {
314
1050
  { flag: "--no-comments", description: "Exclude comments" }
315
1051
  ],
316
1052
  examples: ["forloop story get --id 123"],
317
- async run(_config, flags) {
1053
+ async run(config, flags) {
318
1054
  const resolved = await getClient();
319
1055
  if (!resolved) return;
320
1056
  const { client } = resolved;
@@ -324,28 +1060,7 @@ function createStoryGetCommand() {
324
1060
  process.exit(1);
325
1061
  }
326
1062
  const story = await client.getStory(storyId, { includeComments: !flags.noComments });
327
- const lines = [
328
- `Story #${story.id}: ${story.title}`,
329
- `Sprint: #${story.sprint?.id || "N/A"} ${story.sprint?.title || ""}`,
330
- `Status: ${story.status}`,
331
- `Priority: ${story.priority}`,
332
- `Type: ${story.type}`
333
- ];
334
- if (story.points) lines.push(`Points: ${story.points}`);
335
- if (story.assignee) lines.push(`Assignee: ${story.assignee.name || story.assignee.email || "Unknown"}`);
336
- if (story.assigneeAgentKey) lines.push(`Agent: ${story.assigneeAgentKey}`);
337
- if (story.description) {
338
- lines.push("", "Description:", story.description);
339
- }
340
- if (!flags.noComments && story.comments?.length) {
341
- lines.push("", `--- Comments (${story.comments.length}) ---`);
342
- for (const comment of story.comments) {
343
- const author = comment.authorAgentKey || comment.user?.name || "Unknown";
344
- lines.push(` ${author}: ${comment.content || comment.body || ""}`);
345
- if (comment.artifacts?.length) lines.push(` Artifacts: ${comment.artifacts.join(", ")}`);
346
- }
347
- }
348
- console.log(lines.join("\n"));
1063
+ print(config, story);
349
1064
  }
350
1065
  });
351
1066
  }
@@ -363,7 +1078,7 @@ function createStoryUpdateCommand() {
363
1078
  { flag: "--points <n>", description: "Story points (0-10)", type: "number" }
364
1079
  ],
365
1080
  examples: ["forloop story update --id 123 --status in_progress"],
366
- async run(_config, flags) {
1081
+ async run(config, flags) {
367
1082
  const resolved = await getClient();
368
1083
  if (!resolved) return;
369
1084
  const { client } = resolved;
@@ -383,7 +1098,7 @@ function createStoryUpdateCommand() {
383
1098
  process.exit(1);
384
1099
  }
385
1100
  const story = await client.updateStory(storyId, data);
386
- console.log(`Story #${story.id} updated successfully.`);
1101
+ print(config, story, `Story #${story.id} updated successfully.`);
387
1102
  }
388
1103
  });
389
1104
  }
@@ -397,7 +1112,7 @@ function createStoryDeleteCommand() {
397
1112
  { flag: "--confirm", description: "Confirm deletion" }
398
1113
  ],
399
1114
  examples: ["forloop story delete --id 123 --confirm"],
400
- async run(_config, flags) {
1115
+ async run(config, flags) {
401
1116
  if (!flags.confirm) {
402
1117
  console.error("Error: --confirm is required to permanently delete a story.");
403
1118
  process.exit(1);
@@ -406,13 +1121,12 @@ function createStoryDeleteCommand() {
406
1121
  if (!resolved) return;
407
1122
  const { client } = resolved;
408
1123
  await client.deleteStory(flags.id);
409
- console.log(`Story #${flags.id} deleted successfully.`);
1124
+ print(config, { deleted: flags.id }, `Story #${flags.id} deleted successfully.`);
410
1125
  }
411
1126
  });
412
1127
  }
413
1128
 
414
1129
  // src/commands/sprint.ts
415
- import { ForLoopAPIClient as ForLoopAPIClient2, getToken as getToken3, resolveSprintId as resolveSprintId2, getConfig as getConfig2 } from "@forloop-cc/forloop-opencode-plugin-planner";
416
1130
  function createSprintCommands() {
417
1131
  return {
418
1132
  "sprint get": createSprintGetCommand(),
@@ -423,18 +1137,15 @@ function createSprintCommands() {
423
1137
  };
424
1138
  }
425
1139
  async function getClient2() {
426
- const config = getConfig2();
427
- const token = await getToken3();
1140
+ const config = getConfig();
1141
+ const token = await getToken();
428
1142
  if (!token) {
429
1143
  console.error("Error: No API token configured. Run: forloop auth login --api-key <token>");
430
1144
  process.exit(1);
431
1145
  }
432
- const client = new ForLoopAPIClient2({ token, baseUrl: config.apiUrl });
1146
+ const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 });
433
1147
  return { client };
434
1148
  }
435
- function formatDate(dateString) {
436
- return new Date(dateString).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" });
437
- }
438
1149
  function createSprintGetCommand() {
439
1150
  return defineCommand({
440
1151
  name: "sprint get",
@@ -446,11 +1157,11 @@ function createSprintGetCommand() {
446
1157
  { flag: "--no-files", description: "Exclude files from response" }
447
1158
  ],
448
1159
  examples: ["forloop sprint get --id 456", "forloop sprint get --id 456 --no-files"],
449
- async run(_config, flags) {
1160
+ async run(config, flags) {
450
1161
  const resolved = await getClient2();
451
1162
  if (!resolved) return;
452
1163
  const { client } = resolved;
453
- const resolution = await resolveSprintId2(flags.id);
1164
+ const resolution = await resolveSprintId(flags.id);
454
1165
  if (!resolution.sprintId) {
455
1166
  console.error("Error: No sprint ID resolved. Use --id, set FORLOOP_SPRINT_ID, or use a sprint-XXX branch.");
456
1167
  process.exit(1);
@@ -459,20 +1170,7 @@ function createSprintGetCommand() {
459
1170
  includeStories: !flags.noStories,
460
1171
  includeFiles: !flags.noFiles
461
1172
  });
462
- const lines = [
463
- `Sprint #${sprint.id}: ${sprint.title}`,
464
- `Status: ${sprint.status}`,
465
- `Period: ${formatDate(sprint.startDate)} - ${formatDate(sprint.endDate)}`,
466
- `Stories: ${sprint.stories?.length || 0}`
467
- ];
468
- if (sprint.stories?.length) {
469
- lines.push("", "Stories:", "");
470
- for (const story of sprint.stories) {
471
- const icons = { todo: "[ ]", in_progress: "[>]", done: "[x]", blocked: "[!]" };
472
- lines.push(` ${icons[story.status] || "[?]"} #${story.id} ${story.title} (${story.status})`);
473
- }
474
- }
475
- console.log(lines.join("\n"));
1173
+ print(config, sprint);
476
1174
  }
477
1175
  });
478
1176
  }
@@ -486,23 +1184,15 @@ function createSprintListCommand() {
486
1184
  { flag: "--include-system-org", description: "Include system organization sprints" }
487
1185
  ],
488
1186
  examples: ["forloop sprint list", "forloop sprint list --org-id 1"],
489
- async run(_config, flags) {
1187
+ async run(config, flags) {
490
1188
  const resolved = await getClient2();
491
1189
  if (!resolved) return;
492
1190
  const { client } = resolved;
493
1191
  const params = {};
494
1192
  if (flags.orgId) params.organizationId = String(flags.orgId);
1193
+ if (flags.includeSystemOrg) params.includeSystemOrg = "true";
495
1194
  const sprints = await client.listSprints(params);
496
- if (!sprints.length) {
497
- console.log("No sprints found.");
498
- return;
499
- }
500
- const lines = ["Sprints:", ""];
501
- for (const sprint of sprints) {
502
- const orgInfo = sprint.organizationName ? ` (${sprint.organizationName})` : "";
503
- lines.push(` #${sprint.id} ${sprint.title}${orgInfo} - ${sprint.status}`);
504
- }
505
- console.log(lines.join("\n"));
1195
+ print(config, sprints);
506
1196
  }
507
1197
  });
508
1198
  }
@@ -520,7 +1210,7 @@ function createSprintCreateCommand() {
520
1210
  { flag: "--org-id <id>", description: "Organization ID", type: "number" }
521
1211
  ],
522
1212
  examples: ['forloop sprint create --title "Sprint 15" --start-date 2026-06-09 --end-date 2026-06-23'],
523
- async run(_config, flags) {
1213
+ async run(config, flags) {
524
1214
  const resolved = await getClient2();
525
1215
  if (!resolved) return;
526
1216
  const { client } = resolved;
@@ -536,8 +1226,7 @@ function createSprintCreateCommand() {
536
1226
  isPrivate: flags.private === true,
537
1227
  organizationId: flags.orgId
538
1228
  });
539
- console.log(`Sprint created: #${sprint.id} - ${sprint.title}`);
540
- console.log(`Period: ${formatDate(sprint.startDate)} - ${formatDate(sprint.endDate)}`);
1229
+ print(config, sprint, `Sprint created: #${sprint.id} - ${sprint.title}`);
541
1230
  }
542
1231
  });
543
1232
  }
@@ -555,7 +1244,7 @@ function createSprintUpdateCommand() {
555
1244
  { flag: "--private", description: "Set visibility to private" }
556
1245
  ],
557
1246
  examples: ['forloop sprint update --id 456 --title "Updated Sprint"'],
558
- async run(_config, flags) {
1247
+ async run(config, flags) {
559
1248
  const resolved = await getClient2();
560
1249
  if (!resolved) return;
561
1250
  const { client } = resolved;
@@ -570,7 +1259,7 @@ function createSprintUpdateCommand() {
570
1259
  process.exit(1);
571
1260
  }
572
1261
  const sprint = await client.updateSprint(flags.id, updateData);
573
- console.log(`Sprint #${sprint.id} updated successfully.`);
1262
+ print(config, sprint, `Sprint #${sprint.id} updated successfully.`);
574
1263
  }
575
1264
  });
576
1265
  }
@@ -584,7 +1273,7 @@ function createSprintDeleteCommand() {
584
1273
  { flag: "--confirm", description: "Confirm deletion" }
585
1274
  ],
586
1275
  examples: ["forloop sprint delete --id 456 --confirm"],
587
- async run(_config, flags) {
1276
+ async run(config, flags) {
588
1277
  if (!flags.confirm) {
589
1278
  console.error("Error: --confirm is required to permanently delete a sprint.");
590
1279
  process.exit(1);
@@ -592,14 +1281,22 @@ function createSprintDeleteCommand() {
592
1281
  const resolved = await getClient2();
593
1282
  if (!resolved) return;
594
1283
  const { client } = resolved;
595
- await client.deleteSprint(flags.id);
596
- console.log(`Sprint #${flags.id} deleted successfully.`);
1284
+ const result = await client.deleteSprint(flags.id);
1285
+ print(config, result, `Sprint #${flags.id} deleted successfully.`);
597
1286
  }
598
1287
  });
599
1288
  }
600
1289
 
601
1290
  // src/commands/org.ts
602
- import { ForLoopAPIClient as ForLoopAPIClient3, getToken as getToken4, getConfig as getConfig3 } from "@forloop-cc/forloop-opencode-plugin-planner";
1291
+ async function getClient3() {
1292
+ const config = getConfig();
1293
+ const token = await getToken();
1294
+ if (!token) {
1295
+ console.error("Error: No API token configured. Run: forloop auth login --api-key <token>");
1296
+ process.exit(1);
1297
+ }
1298
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 }) };
1299
+ }
603
1300
  function createOrgCommands() {
604
1301
  return {
605
1302
  "org list": createOrgListCommand(),
@@ -608,15 +1305,6 @@ function createOrgCommands() {
608
1305
  "org update": createOrgUpdateCommand()
609
1306
  };
610
1307
  }
611
- async function getClient3() {
612
- const config = getConfig3();
613
- const token = await getToken4();
614
- if (!token) {
615
- console.error("Error: No API token configured. Run: forloop auth login --api-key <token>");
616
- process.exit(1);
617
- }
618
- return { client: new ForLoopAPIClient3({ token, baseUrl: config.apiUrl }) };
619
- }
620
1308
  function createOrgListCommand() {
621
1309
  return defineCommand({
622
1310
  name: "org list",
@@ -625,12 +1313,16 @@ function createOrgListCommand() {
625
1313
  options: [
626
1314
  { flag: "--owned-only", description: "List only organizations you own" }
627
1315
  ],
628
- examples: ["forloop org list", "forloop org list --owned-only"],
629
- async run(_config, flags) {
1316
+ examples: ["forloop org list", "forloop org list --owned-only", "forloop org list --output json"],
1317
+ async run(config, flags) {
630
1318
  const resolved = await getClient3();
631
1319
  if (!resolved) return;
632
1320
  const { client } = resolved;
633
1321
  const orgs = flags.ownedOnly ? await client.getOwnedOrganizations() : await client.listOrganizations();
1322
+ if (config.output === "json") {
1323
+ print(config, orgs);
1324
+ return;
1325
+ }
634
1326
  if (!orgs.length) {
635
1327
  console.log("No organizations found.");
636
1328
  return;
@@ -652,19 +1344,16 @@ function createOrgGetCommand() {
652
1344
  options: [
653
1345
  { flag: "--id <number>", description: "Organization ID", required: true, type: "number" }
654
1346
  ],
655
- examples: ["forloop org get --id 1"],
656
- async run(_config, flags) {
1347
+ examples: ["forloop org get --id 1", "forloop org get --id 1 --output json"],
1348
+ async run(config, flags) {
657
1349
  if (!flags.id) {
658
1350
  console.error("Error: --id is required");
659
1351
  process.exit(1);
660
1352
  }
661
1353
  const resolved = await getClient3();
662
1354
  if (!resolved) return;
663
- const { client } = resolved;
664
- const org = await client.getOrganization(flags.id);
665
- console.log(`Organization #${org.id}: ${org.name}`);
666
- console.log(`Type: ${org.type || "user"}`);
667
- if (org.description) console.log(`Description: ${org.description}`);
1355
+ const org = await resolved.client.getOrganization(flags.id);
1356
+ print(config, org);
668
1357
  }
669
1358
  });
670
1359
  }
@@ -678,19 +1367,18 @@ function createOrgCreateCommand() {
678
1367
  { flag: "--description <text>", description: "Organization description" }
679
1368
  ],
680
1369
  examples: ['forloop org create --name "My Team" --description "Engineering team"'],
681
- async run(_config, flags) {
1370
+ async run(config, flags) {
682
1371
  if (!flags.name) {
683
1372
  console.error("Error: --name is required");
684
1373
  process.exit(1);
685
1374
  }
686
1375
  const resolved = await getClient3();
687
1376
  if (!resolved) return;
688
- const { client } = resolved;
689
- const org = await client.createOrganization({
1377
+ const org = await resolved.client.createOrganization({
690
1378
  name: flags.name,
691
1379
  description: flags.description
692
1380
  });
693
- console.log(`Organization created: #${org.id} - ${org.name}`);
1381
+ print(config, org, `Organization created: #${org.id} - ${org.name}`);
694
1382
  }
695
1383
  });
696
1384
  }
@@ -705,25 +1393,23 @@ function createOrgUpdateCommand() {
705
1393
  { flag: "--description <text>", description: "New description" }
706
1394
  ],
707
1395
  examples: ['forloop org update --id 1 --name "New Name"'],
708
- async run(_config, flags) {
1396
+ async run(config, flags) {
709
1397
  if (!flags.id) {
710
1398
  console.error("Error: --id is required");
711
1399
  process.exit(1);
712
1400
  }
713
1401
  const resolved = await getClient3();
714
1402
  if (!resolved) return;
715
- const { client } = resolved;
716
- const org = await client.updateOrganization(flags.id, {
1403
+ const org = await resolved.client.updateOrganization(flags.id, {
717
1404
  name: flags.name,
718
1405
  description: flags.description
719
1406
  });
720
- console.log(`Organization #${org.id} updated successfully.`);
1407
+ print(config, org, `Organization #${org.id} updated successfully.`);
721
1408
  }
722
1409
  });
723
1410
  }
724
1411
 
725
1412
  // src/commands/template.ts
726
- import { ForLoopAPIClient as ForLoopAPIClient4, getToken as getToken5, getConfig as getConfig4 } from "@forloop-cc/forloop-opencode-plugin-planner";
727
1413
  function createTemplateCommands() {
728
1414
  return {
729
1415
  "template list": createTemplateListCommand()
@@ -732,30 +1418,41 @@ function createTemplateCommands() {
732
1418
  function createTemplateListCommand() {
733
1419
  return defineCommand({
734
1420
  name: "template list",
735
- description: "List available story templates",
1421
+ description: "List available story templates (basic-task, basic-note, schedule-meeting, doc-folder)",
736
1422
  usage: "forloop template list",
737
- examples: ["forloop template list"],
738
- async run(_config) {
739
- const config = getConfig4();
740
- const token = await getToken5();
1423
+ examples: ["forloop template list", "forloop template list --output json"],
1424
+ async run(config) {
1425
+ const apiConfig = getConfig();
1426
+ const token = await getToken();
741
1427
  if (!token) {
742
1428
  console.error("Error: No API token configured.");
743
1429
  process.exit(1);
744
1430
  }
745
- const client = new ForLoopAPIClient4({ token, baseUrl: config.apiUrl });
1431
+ const client = new ForLoopAPIClient({ token, baseUrl: apiConfig.apiUrl, timeout: 3e4 });
746
1432
  const templates = await client.listTemplates();
747
1433
  const active = templates.filter((t) => !t.deletedAt);
1434
+ if (config.output === "json") {
1435
+ print(config, active);
1436
+ return;
1437
+ }
748
1438
  if (!active.length) {
749
1439
  console.log("No templates found.");
750
1440
  return;
751
1441
  }
1442
+ const builtIn = [
1443
+ { name: "Basic Task", slug: "basic-task", description: "Standard task with title, description, priority, and story points", fields: [] },
1444
+ { name: "Basic Note", slug: "basic-note", description: "Simple note with title and description", fields: [] }
1445
+ ];
1446
+ const allTemplates = [...builtIn, ...active];
752
1447
  const lines = ["Available Templates:", ""];
753
- for (const t of active) {
754
- const fields = JSON.parse(t.fields);
755
- const fieldNames = fields.map((f) => f.label).join(", ");
756
- lines.push(` ${t.name} (slug: ${t.slug})`);
1448
+ for (const t of allTemplates) {
1449
+ const fields = t.fields && typeof t.fields === "string" ? JSON.parse(t.fields) : Array.isArray(t.fields) ? t.fields : [];
1450
+ const fieldNames = Array.isArray(fields) ? fields.map((f) => f.label || f).join(", ") : "";
1451
+ const isBuiltIn = builtIn.some((b) => b.slug === t.slug);
1452
+ const badge = isBuiltIn ? " (built-in)" : "";
1453
+ lines.push(` ${t.name} (slug: ${t.slug})${badge}`);
757
1454
  lines.push(` ${t.description}`);
758
- lines.push(` Fields: ${fieldNames}`);
1455
+ if (fieldNames) lines.push(` Fields: ${fieldNames}`);
759
1456
  lines.push("");
760
1457
  }
761
1458
  console.log(lines.join("\n"));
@@ -764,7 +1461,6 @@ function createTemplateListCommand() {
764
1461
  }
765
1462
 
766
1463
  // src/commands/agent.ts
767
- import { ForLoopAPIClient as ForLoopAPIClient5, getToken as getToken6, resolveSprintId as resolveSprintId3, getConfig as getConfig5 } from "@forloop-cc/forloop-opencode-plugin-planner";
768
1464
  function createAgentCommands() {
769
1465
  return {
770
1466
  "agent history": createAgentHistoryCommand(),
@@ -773,13 +1469,13 @@ function createAgentCommands() {
773
1469
  };
774
1470
  }
775
1471
  async function getClient4() {
776
- const config = getConfig5();
777
- const token = await getToken6();
1472
+ const config = getConfig();
1473
+ const token = await getToken();
778
1474
  if (!token) {
779
1475
  console.error("Error: No API token configured.");
780
1476
  process.exit(1);
781
1477
  }
782
- return { client: new ForLoopAPIClient5({ token, baseUrl: config.apiUrl }) };
1478
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 }) };
783
1479
  }
784
1480
  function createAgentHistoryCommand() {
785
1481
  return defineCommand({
@@ -791,31 +1487,17 @@ function createAgentHistoryCommand() {
791
1487
  { flag: "--limit <n>", description: "Number of messages (max 200, default 50)", type: "number" }
792
1488
  ],
793
1489
  examples: ["forloop agent history --sprint 1 --limit 20"],
794
- async run(_config, flags) {
1490
+ async run(config, flags) {
795
1491
  const resolved = await getClient4();
796
1492
  if (!resolved) return;
797
1493
  const { client } = resolved;
798
- const resolution = await resolveSprintId3(flags.sprint);
1494
+ const resolution = await resolveSprintId(flags.sprint);
799
1495
  const sprintId = resolution.sprintId || flags.sprint;
800
1496
  const history = await client.getConversationHistory({
801
1497
  sprintId,
802
1498
  limit: flags.limit || 50
803
1499
  });
804
- const messages = history.messages || [];
805
- if (!messages.length) {
806
- console.log("No conversation history found.");
807
- return;
808
- }
809
- console.log(`Conversation History (${history.total || messages.length} total):`);
810
- for (const msg of messages) {
811
- const preview = (msg.userMessage || "").substring(0, 80);
812
- console.log(` ${preview}${preview.length >= 80 ? "..." : ""}`);
813
- if (msg.agentResponse) {
814
- const resp = msg.agentResponse.substring(0, 100);
815
- console.log(` -> ${resp}${msg.agentResponse.length > 100 ? "..." : ""}`);
816
- }
817
- console.log("");
818
- }
1500
+ print(config, history);
819
1501
  }
820
1502
  });
821
1503
  }
@@ -866,7 +1548,7 @@ function createDeveloperStatusCommand() {
866
1548
  const resolved = await getClient4();
867
1549
  if (!resolved) return;
868
1550
  const { client } = resolved;
869
- const resolution = await resolveSprintId3(flags.sprint);
1551
+ const resolution = await resolveSprintId(flags.sprint);
870
1552
  if (!resolution.sprintId) {
871
1553
  console.error("Error: No sprint ID resolved.");
872
1554
  process.exit(1);
@@ -878,11 +1560,11 @@ function createDeveloperStatusCommand() {
878
1560
  console.log(`Sprint #${resolution.sprintId}: ${sprint.storiesDone || 0}/${sprint.storiesTotal || 0} stories done.`);
879
1561
  return;
880
1562
  }
881
- const exec = status.execution || {};
1563
+ const exec3 = status.execution || {};
882
1564
  const task = status.task || {};
883
- console.log(`Developer Task: ${exec.status}`);
1565
+ console.log(`Developer Task: ${exec3.status}`);
884
1566
  console.log(`Sprint: #${resolution.sprintId}`);
885
- if (exec.startedAt) console.log(`Started: ${new Date(exec.startedAt).toLocaleString()}`);
1567
+ if (exec3.startedAt) console.log(`Started: ${new Date(exec3.startedAt).toLocaleString()}`);
886
1568
  if (task.branchName) console.log(`Branch: ${task.branchName}`);
887
1569
  if (task.errorMessage) console.log(`Error: ${task.errorMessage}`);
888
1570
  console.log(`Progress: ${sprint.storiesDone || 0}/${sprint.storiesTotal || 0} done, ${sprint.storiesInProgress || 0} in progress.`);
@@ -891,7 +1573,15 @@ function createDeveloperStatusCommand() {
891
1573
  }
892
1574
 
893
1575
  // src/commands/user.ts
894
- import { ForLoopAPIClient as ForLoopAPIClient6, getToken as getToken7, getConfig as getConfig6 } from "@forloop-cc/forloop-opencode-plugin-planner";
1576
+ async function getClient5() {
1577
+ const config = getConfig();
1578
+ const token = await getToken();
1579
+ if (!token) {
1580
+ console.error("Error: No API token configured.");
1581
+ process.exit(1);
1582
+ }
1583
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 }) };
1584
+ }
895
1585
  function createUserCommands() {
896
1586
  return {
897
1587
  "user profile": createUserProfileCommand(),
@@ -899,29 +1589,17 @@ function createUserCommands() {
899
1589
  "org quotas": createOrgQuotasCommand()
900
1590
  };
901
1591
  }
902
- async function getClient5() {
903
- const config = getConfig6();
904
- const token = await getToken7();
905
- if (!token) {
906
- console.error("Error: No API token configured.");
907
- process.exit(1);
908
- }
909
- return { client: new ForLoopAPIClient6({ token, baseUrl: config.apiUrl }) };
910
- }
911
1592
  function createUserProfileCommand() {
912
1593
  return defineCommand({
913
1594
  name: "user profile",
914
1595
  description: "Get current user profile",
915
1596
  usage: "forloop user profile",
916
- examples: ["forloop user profile"],
917
- async run(_config) {
1597
+ examples: ["forloop user profile", "forloop user profile --output json"],
1598
+ async run(config) {
918
1599
  const resolved = await getClient5();
919
1600
  if (!resolved) return;
920
- const { client } = resolved;
921
- const profile = await client.getUserProfile();
922
- console.log(`User: ${profile.name || profile.email || "Unknown"}`);
923
- console.log(`Email: ${profile.email}`);
924
- if (profile.tier) console.log(`Tier: ${profile.tier}`);
1601
+ const profile = await resolved.client.getUserProfile();
1602
+ print(config, profile, "User Profile:");
925
1603
  }
926
1604
  });
927
1605
  }
@@ -930,14 +1608,12 @@ function createUserQuotasCommand() {
930
1608
  name: "user quotas",
931
1609
  description: "Check user quota limits",
932
1610
  usage: "forloop user quotas",
933
- examples: ["forloop user quotas"],
934
- async run(_config) {
1611
+ examples: ["forloop user quotas", "forloop user quotas --output json"],
1612
+ async run(config) {
935
1613
  const resolved = await getClient5();
936
1614
  if (!resolved) return;
937
- const { client } = resolved;
938
- const quotas = await client.getUserQuotas();
939
- console.log("User Quotas:");
940
- console.log(JSON.stringify(quotas, null, 2));
1615
+ const quotas = await resolved.client.getUserQuotas();
1616
+ print(config, quotas, "User Quotas:");
941
1617
  }
942
1618
  });
943
1619
  }
@@ -949,26 +1625,23 @@ function createOrgQuotasCommand() {
949
1625
  options: [
950
1626
  { flag: "--org-id <id>", description: "Organization ID", required: true, type: "number" }
951
1627
  ],
952
- examples: ["forloop org quotas --org-id 1"],
953
- async run(_config, flags) {
1628
+ examples: ["forloop org quotas --org-id 1", "forloop org quotas --org-id 1 --output json"],
1629
+ async run(config, flags) {
954
1630
  if (!flags.orgId) {
955
1631
  console.error("Error: --org-id is required");
956
1632
  process.exit(1);
957
1633
  }
958
1634
  const resolved = await getClient5();
959
1635
  if (!resolved) return;
960
- const { client } = resolved;
961
- const quotas = await client.getOrganizationQuotas(flags.orgId);
962
- console.log("Organization Quotas:");
963
- console.log(JSON.stringify(quotas, null, 2));
1636
+ const quotas = await resolved.client.getOrganizationQuotas(flags.orgId);
1637
+ print(config, quotas, "Organization Quotas:");
964
1638
  }
965
1639
  });
966
1640
  }
967
1641
 
968
1642
  // src/commands/file.ts
969
- import { ForLoopAPIClient as ForLoopAPIClient7, getToken as getToken8, getConfig as getConfig7 } from "@forloop-cc/forloop-opencode-plugin-planner";
970
- import * as fs from "fs";
971
- import * as path from "path";
1643
+ import * as fs4 from "fs";
1644
+ import * as path4 from "path";
972
1645
  function createFileCommands() {
973
1646
  return {
974
1647
  "file upload": createFileUploadCommand(),
@@ -978,13 +1651,13 @@ function createFileCommands() {
978
1651
  };
979
1652
  }
980
1653
  async function getClient6() {
981
- const config = getConfig7();
982
- const token = await getToken8();
1654
+ const config = getConfig();
1655
+ const token = await getToken();
983
1656
  if (!token) {
984
1657
  console.error("Error: No API token configured.");
985
1658
  process.exit(1);
986
1659
  }
987
- return { client: new ForLoopAPIClient7({ token, baseUrl: config.apiUrl }) };
1660
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 }) };
988
1661
  }
989
1662
  var mimeTypes = {
990
1663
  ".png": "image/png",
@@ -1018,18 +1691,18 @@ function createFileUploadCommand() {
1018
1691
  { flag: "--folder <text>", description: "S3 folder within sprint" }
1019
1692
  ],
1020
1693
  examples: ["forloop file upload --path ./report.pdf --sprint 1 --folder docs"],
1021
- async run(_config, flags) {
1694
+ async run(config, flags) {
1022
1695
  const resolved = await getClient6();
1023
1696
  if (!resolved) return;
1024
1697
  const { client } = resolved;
1025
1698
  const filePath = flags.path;
1026
- if (!fs.existsSync(filePath)) {
1699
+ if (!fs4.existsSync(filePath)) {
1027
1700
  console.error(`Error: File not found: ${filePath}`);
1028
1701
  process.exit(1);
1029
1702
  }
1030
- const fileContent = fs.readFileSync(filePath);
1031
- const fileName = path.basename(filePath);
1032
- const ext = path.extname(fileName);
1703
+ const fileContent = fs4.readFileSync(filePath);
1704
+ const fileName = path4.basename(filePath);
1705
+ const ext = path4.extname(fileName);
1033
1706
  const contentType = mimeTypes[ext] || "application/octet-stream";
1034
1707
  const presign = await client.createPresignedUpload({
1035
1708
  sprintId: flags.sprint,
@@ -1054,8 +1727,7 @@ function createFileUploadCommand() {
1054
1727
  size: fileContent.length,
1055
1728
  folder: flags.folder
1056
1729
  });
1057
- console.log(`File uploaded: ${fileName} (${formatSize(fileContent.length)})`);
1058
- console.log(`URL: ${complete.url}`);
1730
+ print(config, { fileName, size: fileContent.length, url: complete.url, fileId: complete.id }, `File uploaded: ${fileName} (${formatSize(fileContent.length)})`);
1059
1731
  }
1060
1732
  });
1061
1733
  }
@@ -1068,23 +1740,12 @@ function createFileListCommand() {
1068
1740
  { flag: "--sprint <id>", description: "Sprint ID", required: true, type: "number" }
1069
1741
  ],
1070
1742
  examples: ["forloop file list --sprint 1"],
1071
- async run(_config, flags) {
1743
+ async run(config, flags) {
1072
1744
  const resolved = await getClient6();
1073
1745
  if (!resolved) return;
1074
1746
  const { client } = resolved;
1075
1747
  const files = await client.getSprintFiles(flags.sprint);
1076
- if (!files.length) {
1077
- console.log("No files found.");
1078
- return;
1079
- }
1080
- const lines = [`Files in Sprint #${flags.sprint}:`, ""];
1081
- for (const file of files) {
1082
- lines.push(` ${file.originalName || file.filename}`);
1083
- lines.push(` Size: ${formatSize(file.size)} | Type: ${file.fileType}`);
1084
- lines.push(` URL: ${file.url}`);
1085
- lines.push("");
1086
- }
1087
- console.log(lines.join("\n"));
1748
+ print(config, files, `Files in Sprint #${flags.sprint}:`);
1088
1749
  }
1089
1750
  });
1090
1751
  }
@@ -1132,7 +1793,6 @@ function createFileDownloadCommand() {
1132
1793
  }
1133
1794
 
1134
1795
  // src/commands/folder.ts
1135
- import { ForLoopAPIClient as ForLoopAPIClient8, getToken as getToken9, getConfig as getConfig8 } from "@forloop-cc/forloop-opencode-plugin-planner";
1136
1796
  function createFolderCommands() {
1137
1797
  return {
1138
1798
  "folder create": createFolderCreateCommand()
@@ -1151,13 +1811,13 @@ function createFolderCreateCommand() {
1151
1811
  ],
1152
1812
  examples: ['forloop folder create --title "Project Docs" --sprint 1 --permissions team'],
1153
1813
  async run(_config, flags) {
1154
- const config = getConfig8();
1155
- const token = await getToken9();
1814
+ const config = getConfig();
1815
+ const token = await getToken();
1156
1816
  if (!token) {
1157
1817
  console.error("Error: No API token configured.");
1158
1818
  process.exit(1);
1159
1819
  }
1160
- const client = new ForLoopAPIClient8({ token, baseUrl: config.apiUrl });
1820
+ const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 });
1161
1821
  const story = await client.createStory({
1162
1822
  title: flags.title,
1163
1823
  description: flags.description,
@@ -1172,9 +1832,8 @@ function createFolderCreateCommand() {
1172
1832
  }
1173
1833
 
1174
1834
  // src/commands/sync.ts
1175
- import { ForLoopAPIClient as ForLoopAPIClient9, getToken as getToken10, resolveSprintId as resolveSprintId4, getConfig as getConfig9, getForloopRoot } from "@forloop-cc/forloop-opencode-plugin-planner";
1176
- import * as fs2 from "fs";
1177
- import * as path2 from "path";
1835
+ import * as fs5 from "fs";
1836
+ import * as path5 from "path";
1178
1837
  function createSyncCommands() {
1179
1838
  return {
1180
1839
  "sync aivy-folder": createSyncAivyFolderCommand(),
@@ -1184,13 +1843,13 @@ function createSyncCommands() {
1184
1843
  };
1185
1844
  }
1186
1845
  async function getClient7() {
1187
- const config = getConfig9();
1188
- const token = await getToken10();
1846
+ const config = getConfig();
1847
+ const token = await getToken();
1189
1848
  if (!token) {
1190
1849
  console.error("Error: No API token configured.");
1191
1850
  process.exit(1);
1192
1851
  }
1193
- return { client: new ForLoopAPIClient9({ token, baseUrl: config.apiUrl }) };
1852
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 }) };
1194
1853
  }
1195
1854
  function createSyncAivyFolderCommand() {
1196
1855
  return defineCommand({
@@ -1206,7 +1865,7 @@ function createSyncAivyFolderCommand() {
1206
1865
  const resolved = await getClient7();
1207
1866
  if (!resolved) return;
1208
1867
  const { client } = resolved;
1209
- const resolution = await resolveSprintId4(flags.sprint);
1868
+ const resolution = await resolveSprintId(flags.sprint);
1210
1869
  if (!resolution.sprintId) {
1211
1870
  console.error("Error: No sprint ID resolved.");
1212
1871
  process.exit(1);
@@ -1243,7 +1902,7 @@ function createAivyDocGetCommand() {
1243
1902
  const resolved = await getClient7();
1244
1903
  if (!resolved) return;
1245
1904
  const { client } = resolved;
1246
- const resolution = await resolveSprintId4(flags.sprint);
1905
+ const resolution = await resolveSprintId(flags.sprint);
1247
1906
  if (!resolution.sprintId) {
1248
1907
  console.error("Error: No sprint ID resolved.");
1249
1908
  process.exit(1);
@@ -1277,14 +1936,14 @@ function createS3ToLocalCommand() {
1277
1936
  const resolved = await getClient7();
1278
1937
  if (!resolved) return;
1279
1938
  const { client } = resolved;
1280
- const resolution = await resolveSprintId4(flags.sprint);
1939
+ const resolution = await resolveSprintId(flags.sprint);
1281
1940
  if (!resolution.sprintId) {
1282
1941
  console.error("Error: No sprint ID resolved.");
1283
1942
  process.exit(1);
1284
1943
  }
1285
1944
  const files = await client.getSprintFiles(resolution.sprintId);
1286
1945
  const localRoot = getForloopRoot(resolution.sprintId);
1287
- fs2.mkdirSync(localRoot, { recursive: true });
1946
+ fs5.mkdirSync(localRoot, { recursive: true });
1288
1947
  let downloaded = 0;
1289
1948
  let skipped = 0;
1290
1949
  for (const file of files) {
@@ -1299,9 +1958,9 @@ function createS3ToLocalCommand() {
1299
1958
  if (res.ok) {
1300
1959
  const buf = Buffer.from(await res.arrayBuffer());
1301
1960
  const kind = name.startsWith("knowledge-") ? "knowledge" : name.startsWith("plan-") ? "plan" : name.startsWith("task-") ? "task" : "other";
1302
- const dir = path2.join(localRoot, kind);
1303
- fs2.mkdirSync(dir, { recursive: true });
1304
- fs2.writeFileSync(path2.join(dir, name), buf);
1961
+ const dir = path5.join(localRoot, kind);
1962
+ fs5.mkdirSync(dir, { recursive: true });
1963
+ fs5.writeFileSync(path5.join(dir, name), buf);
1305
1964
  downloaded++;
1306
1965
  }
1307
1966
  }
@@ -1325,18 +1984,18 @@ function createLocalToS3Command() {
1325
1984
  const resolved = await getClient7();
1326
1985
  if (!resolved) return;
1327
1986
  const { client } = resolved;
1328
- const resolution = await resolveSprintId4(flags.sprint);
1987
+ const resolution = await resolveSprintId(flags.sprint);
1329
1988
  if (!resolution.sprintId) {
1330
1989
  console.error("Error: No sprint ID resolved.");
1331
1990
  process.exit(1);
1332
1991
  }
1333
1992
  const filePath = flags.path;
1334
- if (!fs2.existsSync(filePath)) {
1993
+ if (!fs5.existsSync(filePath)) {
1335
1994
  console.error(`Error: File not found: ${filePath}`);
1336
1995
  process.exit(1);
1337
1996
  }
1338
- const fileBuf = fs2.readFileSync(filePath);
1339
- const fileName = path2.basename(filePath);
1997
+ const fileBuf = fs5.readFileSync(filePath);
1998
+ const fileName = path5.basename(filePath);
1340
1999
  const contentType = "text/markdown";
1341
2000
  const folder = flags.folder || "project";
1342
2001
  const presign = await client.createPresignedUpload({
@@ -1367,12 +2026,12 @@ function createLocalToS3Command() {
1367
2026
  var CommandRegistry = class {
1368
2027
  root = { children: /* @__PURE__ */ new Map() };
1369
2028
  constructor(commands) {
1370
- for (const [path3, cmd] of Object.entries(commands)) {
1371
- this.register(path3, cmd);
2029
+ for (const [path6, cmd] of Object.entries(commands)) {
2030
+ this.register(path6, cmd);
1372
2031
  }
1373
2032
  }
1374
- register(path3, command) {
1375
- const parts = path3.split(" ");
2033
+ register(path6, command) {
2034
+ const parts = path6.split(" ");
1376
2035
  let node = this.root;
1377
2036
  for (const part of parts) {
1378
2037
  if (!node.children.has(part)) {
@@ -1622,14 +2281,6 @@ var registry = new CommandRegistry({
1622
2281
  "agent sprint": agentDeveloperSprintCmd
1623
2282
  });
1624
2283
 
1625
- // src/output/formatter.ts
1626
- function formatError(message, format) {
1627
- if (format === "json") {
1628
- return JSON.stringify({ error: { message } }, null, 2);
1629
- }
1630
- return `\u274C ${message}`;
1631
- }
1632
-
1633
2284
  // src/errors/handler.ts
1634
2285
  function handleError(error) {
1635
2286
  const isJson = process.argv.includes("--output=json");
@@ -1652,10 +2303,7 @@ Tip: ${suggestion}
1652
2303
  }
1653
2304
 
1654
2305
  // src/main.ts
1655
- import { getConfig as getConfig10 } from "@forloop-cc/forloop-opencode-plugin-planner";
1656
- import { getToken as getToken11 } from "@forloop-cc/forloop-opencode-plugin-planner";
1657
- import { detectTokenType } from "@forloop-cc/forloop-opencode-plugin-planner";
1658
- var CLI_VERSION = "0.2.2";
2306
+ var CLI_VERSION = "0.2.4";
1659
2307
  process.on("SIGINT", () => {
1660
2308
  process.stderr.write("\nInterrupted. Exiting.\n");
1661
2309
  process.exit(130);
@@ -1682,7 +2330,7 @@ async function main() {
1682
2330
  if (commandPath.length === 0) {
1683
2331
  registry.printHelp([], process.stderr);
1684
2332
  try {
1685
- const token2 = await getToken11();
2333
+ const token2 = await getToken();
1686
2334
  if (token2) {
1687
2335
  const tokenType = detectTokenType(token2);
1688
2336
  const masked = token2.slice(0, 8) + "..." + token2.slice(-4);
@@ -1703,8 +2351,8 @@ async function main() {
1703
2351
  const { command, extra } = registry.resolve(commandPath);
1704
2352
  const flags = parseFlags(argv, [...GLOBAL_OPTIONS, ...command.options ?? []]);
1705
2353
  if (extra.length > 0) flags._positional = extra;
1706
- const pluginConfig = getConfig10();
1707
- const token = await getToken11();
2354
+ const pluginConfig = getConfig();
2355
+ const token = await getToken();
1708
2356
  const isNoAuth = NO_AUTH_COMMANDS.some(
1709
2357
  (cmd) => cmd.every((c, i) => commandPath[i] === c)
1710
2358
  );
@@ -1715,7 +2363,7 @@ async function main() {
1715
2363
  process.exit(3);
1716
2364
  }
1717
2365
  const cliConfig = {
1718
- region: pluginConfig.apiUrl.includes("dev.forloop.cc") ? "dev" : "global",
2366
+ region: "global",
1719
2367
  apiUrl: pluginConfig.apiUrl,
1720
2368
  serverLambdaUrl: "",
1721
2369
  environment: pluginConfig.environment,