@forloop-cc/forloop-cli 0.2.2 → 0.2.3

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 +782 -75
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -99,10 +99,7 @@ That's it. You're connected.
99
99
 
100
100
  Visit the ForLoop settings page to generate a token:
101
101
 
102
- | Environment | URL |
103
- |-------------|-----|
104
102
  | Production | https://forloop.cc/profile?tab=api-tokens |
105
- | Development | https://dev.forloop.cc/profile?tab=api-tokens |
106
103
 
107
104
  Tokens start with `floop_`. Create a token with at least these scopes:
108
105
  - `sprint:read`, `sprint:write`
@@ -132,69 +129,6 @@ forloop auth logout # Remove saved token
132
129
 
133
130
  ---
134
131
 
135
- ## Environments
136
-
137
- ForLoop has two environments — **Production** and **Development**. By default, the CLI connects to production. Use the development environment when you want to test features or work on staging data without affecting production.
138
-
139
- ### Production (default)
140
-
141
- No extra setup needed. The CLI connects to `https://api.forloop.cc` automatically.
142
-
143
- ```bash
144
- forloop sprint list
145
- ```
146
-
147
- ### Development
148
-
149
- The development environment connects to `https://api.dev.forloop.cc`. You need two things:
150
-
151
- 1. **A dev API token** — create one at https://dev.forloop.cc/profile?tab=api-tokens
152
- 2. **Environment variables** — the CLI requires explicit opt-in for security:
153
-
154
- ```bash
155
- export FORLOOP_ENV=development
156
- export FORLOOP_ALLOW_DEV=true
157
- forloop auth login --api-key floop_xxxxx
158
- ```
159
-
160
- > **Security note:** `FORLOOP_ALLOW_DEV=true` is required because dev API tokens have fewer restrictions. This flag prevents accidentally using a dev token against production data. Set it only when you intend to use the dev environment.
161
-
162
- ### One-Liner for Dev
163
-
164
- ```bash
165
- FORLOOP_ENV=development FORLOOP_ALLOW_DEV=true forloop sprint list
166
- ```
167
-
168
- ### Persistent Dev Setup
169
-
170
- To always use dev for a specific project, add an `opencode.json` in your project root:
171
-
172
- ```json
173
- {
174
- "forloop": {
175
- "apiUrl": "https://api.dev.forloop.cc",
176
- "allowDev": true
177
- }
178
- }
179
- ```
180
-
181
- Or set it globally in `~/.config/opencode/config.json`:
182
-
183
- ```json
184
- {
185
- "forloop": {
186
- "apiUrl": "https://api.dev.forloop.cc",
187
- "allowDev": true
188
- }
189
- }
190
- ```
191
-
192
- | Question | Answer |
193
- |----------|--------|
194
- | What happens if I don't set `FORLOOP_ALLOW_DEV=true`? | The CLI will ignore your dev URL and connect to production instead, with a warning message |
195
- | Can I switch between prod and dev per-command? | Yes, the `--api-url` flag or `FORLOOP_ENV` env var overrides the config |
196
- | Do I need separate tokens for prod and dev? | Yes — create one token at each environment's settings page |
197
-
198
132
  ---
199
133
 
200
134
  ## Commands
@@ -419,9 +353,7 @@ These flags work with every command:
419
353
  | Variable | Purpose |
420
354
  |----------|---------|
421
355
  | `FORLOOP_API_KEY` | API token (alternative to `forloop auth login`) |
422
- | `FORLOOP_API_URL` | Override the API base URL |
423
- | `FORLOOP_ENV` | Set to `production` or `development` |
424
- | `FORLOOP_ALLOW_DEV` | Set to `true` to enable dev environment URLs |
356
+ | `FORLOOP_API_URL` | Override the API base URL (e.g., `https://api.dev.forloop.cc`) |
425
357
  | `FORLOOP_SPRINT_ID` | Default sprint ID for commands that auto-detect it |
426
358
 
427
359
  ---
@@ -561,8 +493,7 @@ The CLI resolves configuration from these sources, in priority order:
561
493
  ```json
562
494
  {
563
495
  "forloop": {
564
- "apiUrl": "https://api.dev.forloop.cc",
565
- "allowDev": true
496
+ "apiUrl": "https://api.forloop.cc"
566
497
  }
567
498
  }
568
499
  ```
@@ -572,8 +503,7 @@ The CLI resolves configuration from these sources, in priority order:
572
503
  ```json
573
504
  {
574
505
  "forloop": {
575
- "apiUrl": "https://api.forloop.cc",
576
- "allowDev": false
506
+ "apiUrl": "https://api.forloop.cc"
577
507
  }
578
508
  }
579
509
  ```
@@ -600,23 +530,11 @@ export FORLOOP_API_KEY=floop_xxxxx
600
530
 
601
531
  Tokens must start with `floop_`. Double-check you copied the entire token from https://forloop.cc/profile?tab=api-tokens.
602
532
 
603
- ### "Dev API URL ignored"
604
-
605
- The CLI refuses dev URLs unless you explicitly opt in:
606
-
607
- ```bash
608
- export FORLOOP_ALLOW_DEV=true
609
- export FORLOOP_ENV=development
610
- ```
611
-
612
- This is a security measure — dev tokens should never accidentally hit production APIs.
613
-
614
533
  ### "Forbidden" or "Not Found" on Specific Resources
615
534
 
616
535
  Some resources (organizations, stories, files) return 403/404 when you don't have access or they don't exist in your current environment. Try:
617
- - Checking you're in the right environment (prod vs dev)
618
536
  - Verifying the resource ID exists with `forloop sprint get --id <id>`
619
- - Checking that your token has the required scopes
537
+ - Checking your token has the required scopes
620
538
 
621
539
  ### Verbose Debugging
622
540
 
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(),
@@ -211,7 +930,6 @@ function createAuthLogoutCommand() {
211
930
  }
212
931
 
213
932
  // src/commands/story.ts
214
- import { ForLoopAPIClient, getToken as getToken2, getConfig } from "@forloop-cc/forloop-opencode-plugin-planner";
215
933
  function createStoryCommands() {
216
934
  return {
217
935
  "story create": createStoryCreateCommand(),
@@ -222,7 +940,7 @@ function createStoryCommands() {
222
940
  }
223
941
  async function getClient() {
224
942
  const config = getConfig();
225
- const token = await getToken2();
943
+ const token = await getToken();
226
944
  if (!token) {
227
945
  console.error("Error: No API token configured. Run: forloop auth login --api-key <token>");
228
946
  process.exit(1);
@@ -412,7 +1130,6 @@ function createStoryDeleteCommand() {
412
1130
  }
413
1131
 
414
1132
  // 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
1133
  function createSprintCommands() {
417
1134
  return {
418
1135
  "sprint get": createSprintGetCommand(),
@@ -423,13 +1140,13 @@ function createSprintCommands() {
423
1140
  };
424
1141
  }
425
1142
  async function getClient2() {
426
- const config = getConfig2();
427
- const token = await getToken3();
1143
+ const config = getConfig();
1144
+ const token = await getToken();
428
1145
  if (!token) {
429
1146
  console.error("Error: No API token configured. Run: forloop auth login --api-key <token>");
430
1147
  process.exit(1);
431
1148
  }
432
- const client = new ForLoopAPIClient2({ token, baseUrl: config.apiUrl });
1149
+ const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl });
433
1150
  return { client };
434
1151
  }
435
1152
  function formatDate(dateString) {
@@ -450,7 +1167,7 @@ function createSprintGetCommand() {
450
1167
  const resolved = await getClient2();
451
1168
  if (!resolved) return;
452
1169
  const { client } = resolved;
453
- const resolution = await resolveSprintId2(flags.id);
1170
+ const resolution = await resolveSprintId(flags.id);
454
1171
  if (!resolution.sprintId) {
455
1172
  console.error("Error: No sprint ID resolved. Use --id, set FORLOOP_SPRINT_ID, or use a sprint-XXX branch.");
456
1173
  process.exit(1);
@@ -599,7 +1316,6 @@ function createSprintDeleteCommand() {
599
1316
  }
600
1317
 
601
1318
  // src/commands/org.ts
602
- import { ForLoopAPIClient as ForLoopAPIClient3, getToken as getToken4, getConfig as getConfig3 } from "@forloop-cc/forloop-opencode-plugin-planner";
603
1319
  function createOrgCommands() {
604
1320
  return {
605
1321
  "org list": createOrgListCommand(),
@@ -609,13 +1325,13 @@ function createOrgCommands() {
609
1325
  };
610
1326
  }
611
1327
  async function getClient3() {
612
- const config = getConfig3();
613
- const token = await getToken4();
1328
+ const config = getConfig();
1329
+ const token = await getToken();
614
1330
  if (!token) {
615
1331
  console.error("Error: No API token configured. Run: forloop auth login --api-key <token>");
616
1332
  process.exit(1);
617
1333
  }
618
- return { client: new ForLoopAPIClient3({ token, baseUrl: config.apiUrl }) };
1334
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl }) };
619
1335
  }
620
1336
  function createOrgListCommand() {
621
1337
  return defineCommand({
@@ -723,7 +1439,6 @@ function createOrgUpdateCommand() {
723
1439
  }
724
1440
 
725
1441
  // src/commands/template.ts
726
- import { ForLoopAPIClient as ForLoopAPIClient4, getToken as getToken5, getConfig as getConfig4 } from "@forloop-cc/forloop-opencode-plugin-planner";
727
1442
  function createTemplateCommands() {
728
1443
  return {
729
1444
  "template list": createTemplateListCommand()
@@ -736,13 +1451,13 @@ function createTemplateListCommand() {
736
1451
  usage: "forloop template list",
737
1452
  examples: ["forloop template list"],
738
1453
  async run(_config) {
739
- const config = getConfig4();
740
- const token = await getToken5();
1454
+ const config = getConfig();
1455
+ const token = await getToken();
741
1456
  if (!token) {
742
1457
  console.error("Error: No API token configured.");
743
1458
  process.exit(1);
744
1459
  }
745
- const client = new ForLoopAPIClient4({ token, baseUrl: config.apiUrl });
1460
+ const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl });
746
1461
  const templates = await client.listTemplates();
747
1462
  const active = templates.filter((t) => !t.deletedAt);
748
1463
  if (!active.length) {
@@ -764,7 +1479,6 @@ function createTemplateListCommand() {
764
1479
  }
765
1480
 
766
1481
  // 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
1482
  function createAgentCommands() {
769
1483
  return {
770
1484
  "agent history": createAgentHistoryCommand(),
@@ -773,13 +1487,13 @@ function createAgentCommands() {
773
1487
  };
774
1488
  }
775
1489
  async function getClient4() {
776
- const config = getConfig5();
777
- const token = await getToken6();
1490
+ const config = getConfig();
1491
+ const token = await getToken();
778
1492
  if (!token) {
779
1493
  console.error("Error: No API token configured.");
780
1494
  process.exit(1);
781
1495
  }
782
- return { client: new ForLoopAPIClient5({ token, baseUrl: config.apiUrl }) };
1496
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl }) };
783
1497
  }
784
1498
  function createAgentHistoryCommand() {
785
1499
  return defineCommand({
@@ -795,7 +1509,7 @@ function createAgentHistoryCommand() {
795
1509
  const resolved = await getClient4();
796
1510
  if (!resolved) return;
797
1511
  const { client } = resolved;
798
- const resolution = await resolveSprintId3(flags.sprint);
1512
+ const resolution = await resolveSprintId(flags.sprint);
799
1513
  const sprintId = resolution.sprintId || flags.sprint;
800
1514
  const history = await client.getConversationHistory({
801
1515
  sprintId,
@@ -866,7 +1580,7 @@ function createDeveloperStatusCommand() {
866
1580
  const resolved = await getClient4();
867
1581
  if (!resolved) return;
868
1582
  const { client } = resolved;
869
- const resolution = await resolveSprintId3(flags.sprint);
1583
+ const resolution = await resolveSprintId(flags.sprint);
870
1584
  if (!resolution.sprintId) {
871
1585
  console.error("Error: No sprint ID resolved.");
872
1586
  process.exit(1);
@@ -878,11 +1592,11 @@ function createDeveloperStatusCommand() {
878
1592
  console.log(`Sprint #${resolution.sprintId}: ${sprint.storiesDone || 0}/${sprint.storiesTotal || 0} stories done.`);
879
1593
  return;
880
1594
  }
881
- const exec = status.execution || {};
1595
+ const exec3 = status.execution || {};
882
1596
  const task = status.task || {};
883
- console.log(`Developer Task: ${exec.status}`);
1597
+ console.log(`Developer Task: ${exec3.status}`);
884
1598
  console.log(`Sprint: #${resolution.sprintId}`);
885
- if (exec.startedAt) console.log(`Started: ${new Date(exec.startedAt).toLocaleString()}`);
1599
+ if (exec3.startedAt) console.log(`Started: ${new Date(exec3.startedAt).toLocaleString()}`);
886
1600
  if (task.branchName) console.log(`Branch: ${task.branchName}`);
887
1601
  if (task.errorMessage) console.log(`Error: ${task.errorMessage}`);
888
1602
  console.log(`Progress: ${sprint.storiesDone || 0}/${sprint.storiesTotal || 0} done, ${sprint.storiesInProgress || 0} in progress.`);
@@ -891,7 +1605,6 @@ function createDeveloperStatusCommand() {
891
1605
  }
892
1606
 
893
1607
  // src/commands/user.ts
894
- import { ForLoopAPIClient as ForLoopAPIClient6, getToken as getToken7, getConfig as getConfig6 } from "@forloop-cc/forloop-opencode-plugin-planner";
895
1608
  function createUserCommands() {
896
1609
  return {
897
1610
  "user profile": createUserProfileCommand(),
@@ -900,13 +1613,13 @@ function createUserCommands() {
900
1613
  };
901
1614
  }
902
1615
  async function getClient5() {
903
- const config = getConfig6();
904
- const token = await getToken7();
1616
+ const config = getConfig();
1617
+ const token = await getToken();
905
1618
  if (!token) {
906
1619
  console.error("Error: No API token configured.");
907
1620
  process.exit(1);
908
1621
  }
909
- return { client: new ForLoopAPIClient6({ token, baseUrl: config.apiUrl }) };
1622
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl }) };
910
1623
  }
911
1624
  function createUserProfileCommand() {
912
1625
  return defineCommand({
@@ -966,9 +1679,8 @@ function createOrgQuotasCommand() {
966
1679
  }
967
1680
 
968
1681
  // 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";
1682
+ import * as fs4 from "fs";
1683
+ import * as path4 from "path";
972
1684
  function createFileCommands() {
973
1685
  return {
974
1686
  "file upload": createFileUploadCommand(),
@@ -978,13 +1690,13 @@ function createFileCommands() {
978
1690
  };
979
1691
  }
980
1692
  async function getClient6() {
981
- const config = getConfig7();
982
- const token = await getToken8();
1693
+ const config = getConfig();
1694
+ const token = await getToken();
983
1695
  if (!token) {
984
1696
  console.error("Error: No API token configured.");
985
1697
  process.exit(1);
986
1698
  }
987
- return { client: new ForLoopAPIClient7({ token, baseUrl: config.apiUrl }) };
1699
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl }) };
988
1700
  }
989
1701
  var mimeTypes = {
990
1702
  ".png": "image/png",
@@ -1023,13 +1735,13 @@ function createFileUploadCommand() {
1023
1735
  if (!resolved) return;
1024
1736
  const { client } = resolved;
1025
1737
  const filePath = flags.path;
1026
- if (!fs.existsSync(filePath)) {
1738
+ if (!fs4.existsSync(filePath)) {
1027
1739
  console.error(`Error: File not found: ${filePath}`);
1028
1740
  process.exit(1);
1029
1741
  }
1030
- const fileContent = fs.readFileSync(filePath);
1031
- const fileName = path.basename(filePath);
1032
- const ext = path.extname(fileName);
1742
+ const fileContent = fs4.readFileSync(filePath);
1743
+ const fileName = path4.basename(filePath);
1744
+ const ext = path4.extname(fileName);
1033
1745
  const contentType = mimeTypes[ext] || "application/octet-stream";
1034
1746
  const presign = await client.createPresignedUpload({
1035
1747
  sprintId: flags.sprint,
@@ -1132,7 +1844,6 @@ function createFileDownloadCommand() {
1132
1844
  }
1133
1845
 
1134
1846
  // src/commands/folder.ts
1135
- import { ForLoopAPIClient as ForLoopAPIClient8, getToken as getToken9, getConfig as getConfig8 } from "@forloop-cc/forloop-opencode-plugin-planner";
1136
1847
  function createFolderCommands() {
1137
1848
  return {
1138
1849
  "folder create": createFolderCreateCommand()
@@ -1151,13 +1862,13 @@ function createFolderCreateCommand() {
1151
1862
  ],
1152
1863
  examples: ['forloop folder create --title "Project Docs" --sprint 1 --permissions team'],
1153
1864
  async run(_config, flags) {
1154
- const config = getConfig8();
1155
- const token = await getToken9();
1865
+ const config = getConfig();
1866
+ const token = await getToken();
1156
1867
  if (!token) {
1157
1868
  console.error("Error: No API token configured.");
1158
1869
  process.exit(1);
1159
1870
  }
1160
- const client = new ForLoopAPIClient8({ token, baseUrl: config.apiUrl });
1871
+ const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl });
1161
1872
  const story = await client.createStory({
1162
1873
  title: flags.title,
1163
1874
  description: flags.description,
@@ -1172,9 +1883,8 @@ function createFolderCreateCommand() {
1172
1883
  }
1173
1884
 
1174
1885
  // 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";
1886
+ import * as fs5 from "fs";
1887
+ import * as path5 from "path";
1178
1888
  function createSyncCommands() {
1179
1889
  return {
1180
1890
  "sync aivy-folder": createSyncAivyFolderCommand(),
@@ -1184,13 +1894,13 @@ function createSyncCommands() {
1184
1894
  };
1185
1895
  }
1186
1896
  async function getClient7() {
1187
- const config = getConfig9();
1188
- const token = await getToken10();
1897
+ const config = getConfig();
1898
+ const token = await getToken();
1189
1899
  if (!token) {
1190
1900
  console.error("Error: No API token configured.");
1191
1901
  process.exit(1);
1192
1902
  }
1193
- return { client: new ForLoopAPIClient9({ token, baseUrl: config.apiUrl }) };
1903
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl }) };
1194
1904
  }
1195
1905
  function createSyncAivyFolderCommand() {
1196
1906
  return defineCommand({
@@ -1206,7 +1916,7 @@ function createSyncAivyFolderCommand() {
1206
1916
  const resolved = await getClient7();
1207
1917
  if (!resolved) return;
1208
1918
  const { client } = resolved;
1209
- const resolution = await resolveSprintId4(flags.sprint);
1919
+ const resolution = await resolveSprintId(flags.sprint);
1210
1920
  if (!resolution.sprintId) {
1211
1921
  console.error("Error: No sprint ID resolved.");
1212
1922
  process.exit(1);
@@ -1243,7 +1953,7 @@ function createAivyDocGetCommand() {
1243
1953
  const resolved = await getClient7();
1244
1954
  if (!resolved) return;
1245
1955
  const { client } = resolved;
1246
- const resolution = await resolveSprintId4(flags.sprint);
1956
+ const resolution = await resolveSprintId(flags.sprint);
1247
1957
  if (!resolution.sprintId) {
1248
1958
  console.error("Error: No sprint ID resolved.");
1249
1959
  process.exit(1);
@@ -1277,14 +1987,14 @@ function createS3ToLocalCommand() {
1277
1987
  const resolved = await getClient7();
1278
1988
  if (!resolved) return;
1279
1989
  const { client } = resolved;
1280
- const resolution = await resolveSprintId4(flags.sprint);
1990
+ const resolution = await resolveSprintId(flags.sprint);
1281
1991
  if (!resolution.sprintId) {
1282
1992
  console.error("Error: No sprint ID resolved.");
1283
1993
  process.exit(1);
1284
1994
  }
1285
1995
  const files = await client.getSprintFiles(resolution.sprintId);
1286
1996
  const localRoot = getForloopRoot(resolution.sprintId);
1287
- fs2.mkdirSync(localRoot, { recursive: true });
1997
+ fs5.mkdirSync(localRoot, { recursive: true });
1288
1998
  let downloaded = 0;
1289
1999
  let skipped = 0;
1290
2000
  for (const file of files) {
@@ -1299,9 +2009,9 @@ function createS3ToLocalCommand() {
1299
2009
  if (res.ok) {
1300
2010
  const buf = Buffer.from(await res.arrayBuffer());
1301
2011
  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);
2012
+ const dir = path5.join(localRoot, kind);
2013
+ fs5.mkdirSync(dir, { recursive: true });
2014
+ fs5.writeFileSync(path5.join(dir, name), buf);
1305
2015
  downloaded++;
1306
2016
  }
1307
2017
  }
@@ -1325,18 +2035,18 @@ function createLocalToS3Command() {
1325
2035
  const resolved = await getClient7();
1326
2036
  if (!resolved) return;
1327
2037
  const { client } = resolved;
1328
- const resolution = await resolveSprintId4(flags.sprint);
2038
+ const resolution = await resolveSprintId(flags.sprint);
1329
2039
  if (!resolution.sprintId) {
1330
2040
  console.error("Error: No sprint ID resolved.");
1331
2041
  process.exit(1);
1332
2042
  }
1333
2043
  const filePath = flags.path;
1334
- if (!fs2.existsSync(filePath)) {
2044
+ if (!fs5.existsSync(filePath)) {
1335
2045
  console.error(`Error: File not found: ${filePath}`);
1336
2046
  process.exit(1);
1337
2047
  }
1338
- const fileBuf = fs2.readFileSync(filePath);
1339
- const fileName = path2.basename(filePath);
2048
+ const fileBuf = fs5.readFileSync(filePath);
2049
+ const fileName = path5.basename(filePath);
1340
2050
  const contentType = "text/markdown";
1341
2051
  const folder = flags.folder || "project";
1342
2052
  const presign = await client.createPresignedUpload({
@@ -1367,12 +2077,12 @@ function createLocalToS3Command() {
1367
2077
  var CommandRegistry = class {
1368
2078
  root = { children: /* @__PURE__ */ new Map() };
1369
2079
  constructor(commands) {
1370
- for (const [path3, cmd] of Object.entries(commands)) {
1371
- this.register(path3, cmd);
2080
+ for (const [path6, cmd] of Object.entries(commands)) {
2081
+ this.register(path6, cmd);
1372
2082
  }
1373
2083
  }
1374
- register(path3, command) {
1375
- const parts = path3.split(" ");
2084
+ register(path6, command) {
2085
+ const parts = path6.split(" ");
1376
2086
  let node = this.root;
1377
2087
  for (const part of parts) {
1378
2088
  if (!node.children.has(part)) {
@@ -1652,10 +2362,7 @@ Tip: ${suggestion}
1652
2362
  }
1653
2363
 
1654
2364
  // 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";
2365
+ var CLI_VERSION = "0.2.3";
1659
2366
  process.on("SIGINT", () => {
1660
2367
  process.stderr.write("\nInterrupted. Exiting.\n");
1661
2368
  process.exit(130);
@@ -1682,7 +2389,7 @@ async function main() {
1682
2389
  if (commandPath.length === 0) {
1683
2390
  registry.printHelp([], process.stderr);
1684
2391
  try {
1685
- const token2 = await getToken11();
2392
+ const token2 = await getToken();
1686
2393
  if (token2) {
1687
2394
  const tokenType = detectTokenType(token2);
1688
2395
  const masked = token2.slice(0, 8) + "..." + token2.slice(-4);
@@ -1703,8 +2410,8 @@ async function main() {
1703
2410
  const { command, extra } = registry.resolve(commandPath);
1704
2411
  const flags = parseFlags(argv, [...GLOBAL_OPTIONS, ...command.options ?? []]);
1705
2412
  if (extra.length > 0) flags._positional = extra;
1706
- const pluginConfig = getConfig10();
1707
- const token = await getToken11();
2413
+ const pluginConfig = getConfig();
2414
+ const token = await getToken();
1708
2415
  const isNoAuth = NO_AUTH_COMMANDS.some(
1709
2416
  (cmd) => cmd.every((c, i) => commandPath[i] === c)
1710
2417
  );
@@ -1715,7 +2422,7 @@ async function main() {
1715
2422
  process.exit(3);
1716
2423
  }
1717
2424
  const cliConfig = {
1718
- region: pluginConfig.apiUrl.includes("dev.forloop.cc") ? "dev" : "global",
2425
+ region: "global",
1719
2426
  apiUrl: pluginConfig.apiUrl,
1720
2427
  serverLambdaUrl: "",
1721
2428
  environment: pluginConfig.environment,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forloop-cc/forloop-cli",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Command-line interface for the ForLoop AI development platform",
5
5
  "type": "module",
6
6
  "license": "MIT",