@iloom/cli 0.3.2 → 0.3.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.
- package/README.md +3 -3
- package/dist/{BranchNamingService-JYO746H7.js → BranchNamingService-A77VI6AI.js} +2 -2
- package/dist/ClaudeContextManager-BN7RE5ZQ.js +15 -0
- package/dist/ClaudeService-DLYLJUPA.js +14 -0
- package/dist/{GitHubService-UAMH7DMF.js → GitHubService-FZHHBOFG.js} +3 -3
- package/dist/{LoomLauncher-FVZECY3C.js → LoomLauncher-ZV3ZZIBA.js} +23 -20
- package/dist/LoomLauncher-ZV3ZZIBA.js.map +1 -0
- package/dist/{PromptTemplateManager-A52RUAMS.js → PromptTemplateManager-6HH3PVXV.js} +2 -2
- package/dist/README.md +3 -3
- package/dist/{SettingsManager-MTVX57WR.js → SettingsManager-I2LRCW2A.js} +2 -2
- package/dist/{SettingsMigrationManager-AGIIIPDQ.js → SettingsMigrationManager-TJ7UWZG5.js} +3 -3
- package/dist/agents/iloom-issue-complexity-evaluator.md +18 -3
- package/dist/agents/iloom-issue-enhancer.md +1 -1
- package/dist/{chunk-OAP6SASD.js → chunk-2CXREBLZ.js} +2 -2
- package/dist/{chunk-KG343GSG.js → chunk-2IJEMXOB.js} +359 -99
- package/dist/chunk-2IJEMXOB.js.map +1 -0
- package/dist/{chunk-FOV7RRQ2.js → chunk-2MAIX45J.js} +4 -4
- package/dist/{chunk-5NTD4MCZ.js → chunk-5Q3NDNNV.js} +29 -5
- package/dist/chunk-5Q3NDNNV.js.map +1 -0
- package/dist/{chunk-GVICXJHW.js → chunk-5VK4NRSF.js} +2 -2
- package/dist/{chunk-C7ASXK6J.js → chunk-AKUJXDNW.js} +2 -2
- package/dist/{chunk-UCZ24SUE.js → chunk-CDZERT7Z.js} +21 -9
- package/dist/chunk-CDZERT7Z.js.map +1 -0
- package/dist/{chunk-JKXJ7BGL.js → chunk-CE26YH2U.js} +42 -3
- package/dist/chunk-CE26YH2U.js.map +1 -0
- package/dist/{chunk-ZZZWQGTS.js → chunk-CFFQ2Z7A.js} +74 -75
- package/dist/chunk-CFFQ2Z7A.js.map +1 -0
- package/dist/{chunk-FXMLNKLT.js → chunk-DLHA5VQ3.js} +174 -5
- package/dist/chunk-DLHA5VQ3.js.map +1 -0
- package/dist/{chunk-GOG3ZB7O.js → chunk-IFB4Z76W.js} +23 -4
- package/dist/chunk-IFB4Z76W.js.map +1 -0
- package/dist/{chunk-F4ENT6AC.js → chunk-M7JJCX53.js} +2 -2
- package/dist/{chunk-KLBYVHPK.js → chunk-OSCLCMDG.js} +2 -2
- package/dist/{chunk-ZLIHIUDQ.js → chunk-OYF4VIFI.js} +2 -2
- package/dist/{chunk-A2P7NZTB.js → chunk-PGPI5LR4.js} +4 -4
- package/dist/{chunk-WEN5C5DM.js → chunk-RIEO2WML.js} +4 -1
- package/dist/chunk-RIEO2WML.js.map +1 -0
- package/dist/{chunk-UERERX6M.js → chunk-SUOXY5WJ.js} +2 -2
- package/dist/{init-7IIM35LQ.js → chunk-UAN4A3YU.js} +343 -46
- package/dist/chunk-UAN4A3YU.js.map +1 -0
- package/dist/{claude-KIZYXTSG.js → claude-W52VKI6L.js} +2 -2
- package/dist/{cleanup-6WYUD5SN.js → cleanup-H4VXU3C3.js} +12 -12
- package/dist/cli.js +188 -91
- package/dist/cli.js.map +1 -1
- package/dist/{color-ZVALX37U.js → color-F7RU6B6Z.js} +10 -4
- package/dist/{contribute-7YJHZTO7.js → contribute-Y7IQV5QY.js} +4 -4
- package/dist/{feedback-752MGDPG.js → feedback-XTUCKJNT.js} +11 -9
- package/dist/{feedback-752MGDPG.js.map → feedback-XTUCKJNT.js.map} +1 -1
- package/dist/{git-TAFVDB3J.js → git-IYA53VIC.js} +7 -3
- package/dist/{ignite-OAMDUN27.js → ignite-T74RYXCA.js} +19 -71
- package/dist/ignite-T74RYXCA.js.map +1 -0
- package/dist/index.d.ts +52 -8
- package/dist/index.js +132 -84
- package/dist/index.js.map +1 -1
- package/dist/init-4FHTAM3F.js +19 -0
- package/dist/{neon-helpers-5HBYO2VP.js → neon-helpers-77PBPGJ5.js} +3 -3
- package/dist/{open-FCHKQ77R.js → open-UMXANW5S.js} +4 -4
- package/dist/{prompt-7INJ7YRU.js → prompt-QALMYTVC.js} +4 -2
- package/dist/prompt-QALMYTVC.js.map +1 -0
- package/dist/prompts/init-prompt.txt +82 -8
- package/dist/prompts/issue-prompt.txt +18 -11
- package/dist/prompts/regular-prompt.txt +373 -14
- package/dist/{rebase-Q7WXK566.js → rebase-VJ2VKR6R.js} +11 -11
- package/dist/rebase-VJ2VKR6R.js.map +1 -0
- package/dist/{run-SJPM6YRI.js → run-MJYY4PUT.js} +4 -4
- package/dist/schema/settings.schema.json +21 -3
- package/dist/{test-git-DEUE656D.js → test-git-IT5EWQ5C.js} +3 -3
- package/dist/{test-prefix-Y6Z6ZHSF.js → test-prefix-NPWDPUUH.js} +3 -3
- package/package.json +2 -1
- package/dist/ClaudeContextManager-5WPRJIIW.js +0 -15
- package/dist/ClaudeService-Z4KA7QOW.js +0 -14
- package/dist/LoomLauncher-FVZECY3C.js.map +0 -1
- package/dist/chunk-5NTD4MCZ.js.map +0 -1
- package/dist/chunk-FXMLNKLT.js.map +0 -1
- package/dist/chunk-GOG3ZB7O.js.map +0 -1
- package/dist/chunk-JKXJ7BGL.js.map +0 -1
- package/dist/chunk-KG343GSG.js.map +0 -1
- package/dist/chunk-PRE7KTM4.js +0 -302
- package/dist/chunk-PRE7KTM4.js.map +0 -1
- package/dist/chunk-UCZ24SUE.js.map +0 -1
- package/dist/chunk-WEN5C5DM.js.map +0 -1
- package/dist/chunk-ZZZWQGTS.js.map +0 -1
- package/dist/ignite-OAMDUN27.js.map +0 -1
- package/dist/init-7IIM35LQ.js.map +0 -1
- package/dist/rebase-Q7WXK566.js.map +0 -1
- /package/dist/{BranchNamingService-JYO746H7.js.map → BranchNamingService-A77VI6AI.js.map} +0 -0
- /package/dist/{ClaudeContextManager-5WPRJIIW.js.map → ClaudeContextManager-BN7RE5ZQ.js.map} +0 -0
- /package/dist/{ClaudeService-Z4KA7QOW.js.map → ClaudeService-DLYLJUPA.js.map} +0 -0
- /package/dist/{GitHubService-UAMH7DMF.js.map → GitHubService-FZHHBOFG.js.map} +0 -0
- /package/dist/{PromptTemplateManager-A52RUAMS.js.map → PromptTemplateManager-6HH3PVXV.js.map} +0 -0
- /package/dist/{SettingsManager-MTVX57WR.js.map → SettingsManager-I2LRCW2A.js.map} +0 -0
- /package/dist/{SettingsMigrationManager-AGIIIPDQ.js.map → SettingsMigrationManager-TJ7UWZG5.js.map} +0 -0
- /package/dist/{chunk-OAP6SASD.js.map → chunk-2CXREBLZ.js.map} +0 -0
- /package/dist/{chunk-FOV7RRQ2.js.map → chunk-2MAIX45J.js.map} +0 -0
- /package/dist/{chunk-GVICXJHW.js.map → chunk-5VK4NRSF.js.map} +0 -0
- /package/dist/{chunk-C7ASXK6J.js.map → chunk-AKUJXDNW.js.map} +0 -0
- /package/dist/{chunk-F4ENT6AC.js.map → chunk-M7JJCX53.js.map} +0 -0
- /package/dist/{chunk-KLBYVHPK.js.map → chunk-OSCLCMDG.js.map} +0 -0
- /package/dist/{chunk-ZLIHIUDQ.js.map → chunk-OYF4VIFI.js.map} +0 -0
- /package/dist/{chunk-A2P7NZTB.js.map → chunk-PGPI5LR4.js.map} +0 -0
- /package/dist/{chunk-UERERX6M.js.map → chunk-SUOXY5WJ.js.map} +0 -0
- /package/dist/{claude-KIZYXTSG.js.map → claude-W52VKI6L.js.map} +0 -0
- /package/dist/{cleanup-6WYUD5SN.js.map → cleanup-H4VXU3C3.js.map} +0 -0
- /package/dist/{color-ZVALX37U.js.map → color-F7RU6B6Z.js.map} +0 -0
- /package/dist/{contribute-7YJHZTO7.js.map → contribute-Y7IQV5QY.js.map} +0 -0
- /package/dist/{git-TAFVDB3J.js.map → git-IYA53VIC.js.map} +0 -0
- /package/dist/{neon-helpers-5HBYO2VP.js.map → init-4FHTAM3F.js.map} +0 -0
- /package/dist/{prompt-7INJ7YRU.js.map → neon-helpers-77PBPGJ5.js.map} +0 -0
- /package/dist/{open-FCHKQ77R.js.map → open-UMXANW5S.js.map} +0 -0
- /package/dist/{run-SJPM6YRI.js.map → run-MJYY4PUT.js.map} +0 -0
- /package/dist/{test-git-DEUE656D.js.map → test-git-IT5EWQ5C.js.map} +0 -0
- /package/dist/{test-prefix-Y6Z6ZHSF.js.map → test-prefix-NPWDPUUH.js.map} +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
GitHubService
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-M7JJCX53.js";
|
|
5
5
|
import {
|
|
6
6
|
getRepoInfo
|
|
7
7
|
} from "./chunk-3RUPPQRG.js";
|
|
8
8
|
import {
|
|
9
9
|
promptConfirmation
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-CE26YH2U.js";
|
|
11
11
|
import {
|
|
12
12
|
logger
|
|
13
13
|
} from "./chunk-GEHQXLEI.js";
|
|
@@ -376,8 +376,176 @@ var IssueTrackerFactory = class {
|
|
|
376
376
|
}
|
|
377
377
|
};
|
|
378
378
|
|
|
379
|
-
// src/utils/
|
|
379
|
+
// src/utils/FirstRunManager.ts
|
|
380
|
+
import os from "os";
|
|
380
381
|
import path from "path";
|
|
382
|
+
import fs from "fs-extra";
|
|
383
|
+
var FirstRunManager = class {
|
|
384
|
+
constructor(feature = "spin") {
|
|
385
|
+
this.configDir = path.join(os.homedir(), ".config", "iloom-ai");
|
|
386
|
+
this.markerFilePath = path.join(this.configDir, `${feature}-first-run`);
|
|
387
|
+
logger.debug("FirstRunManager initialized", {
|
|
388
|
+
feature,
|
|
389
|
+
markerFilePath: this.markerFilePath
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Get the directory for project marker files
|
|
394
|
+
*/
|
|
395
|
+
getProjectsDir() {
|
|
396
|
+
return path.join(this.configDir, "projects");
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Convert a project path to a readable filename
|
|
400
|
+
* /Users/adam/Projects/my-app -> Users__adam__Projects__my-app
|
|
401
|
+
*/
|
|
402
|
+
projectPathToFileName(projectPath) {
|
|
403
|
+
const normalized = path.normalize(projectPath);
|
|
404
|
+
return normalized.replace(/^[/\\]+/, "").replace(/[/\\]+/g, "__");
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Get full path to a project's marker file
|
|
408
|
+
*/
|
|
409
|
+
getProjectMarkerPath(projectPath) {
|
|
410
|
+
return path.join(this.getProjectsDir(), this.projectPathToFileName(projectPath));
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Extract project name from path
|
|
414
|
+
*/
|
|
415
|
+
getProjectName(projectPath) {
|
|
416
|
+
return path.basename(projectPath);
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Check if a project has been configured
|
|
420
|
+
* Returns true if project marker file exists
|
|
421
|
+
*/
|
|
422
|
+
async isProjectConfigured(projectPath) {
|
|
423
|
+
const resolvedPath = projectPath ?? process.cwd();
|
|
424
|
+
const markerPath = this.getProjectMarkerPath(resolvedPath);
|
|
425
|
+
logger.debug("isProjectConfigured: Checking for marker file", { markerPath });
|
|
426
|
+
try {
|
|
427
|
+
const exists = await fs.pathExists(markerPath);
|
|
428
|
+
logger.debug(`isProjectConfigured: Marker file exists=${exists}`);
|
|
429
|
+
return exists;
|
|
430
|
+
} catch (error) {
|
|
431
|
+
logger.debug(`isProjectConfigured: Error checking marker file, treating as not configured: ${error}`);
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Check if a project has local .iloom settings files
|
|
437
|
+
* Returns true if .iloom/settings.json or .iloom/settings.local.json exists with content
|
|
438
|
+
*/
|
|
439
|
+
async hasLocalSettings(projectPath) {
|
|
440
|
+
const iloomDir = path.join(projectPath, ".iloom");
|
|
441
|
+
const settingsPath = path.join(iloomDir, "settings.json");
|
|
442
|
+
const settingsLocalPath = path.join(iloomDir, "settings.local.json");
|
|
443
|
+
const hasSettings = await this.hasNonEmptySettingsFile(settingsPath);
|
|
444
|
+
const hasLocalSettings = await this.hasNonEmptySettingsFile(settingsLocalPath);
|
|
445
|
+
return hasSettings || hasLocalSettings;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Check if a settings file exists and has non-empty content
|
|
449
|
+
*/
|
|
450
|
+
async hasNonEmptySettingsFile(filePath) {
|
|
451
|
+
try {
|
|
452
|
+
const exists = await fs.pathExists(filePath);
|
|
453
|
+
if (!exists) return false;
|
|
454
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
455
|
+
const parsed = JSON.parse(content);
|
|
456
|
+
return Object.keys(parsed).length > 0;
|
|
457
|
+
} catch {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Fixup legacy projects that have local config but lack the global marker file
|
|
463
|
+
* This handles projects configured before global project tracking was implemented
|
|
464
|
+
*
|
|
465
|
+
* Returns object with:
|
|
466
|
+
* - isConfigured: true if project is configured (either already had marker or fixup created one)
|
|
467
|
+
* - wasFixedUp: true if fixup was performed (marker was created by this call)
|
|
468
|
+
*
|
|
469
|
+
* This combined return avoids duplicate isProjectConfigured() calls in needsFirstRunSetup()
|
|
470
|
+
*/
|
|
471
|
+
async fixupLegacyProject(projectPath) {
|
|
472
|
+
const resolvedPath = projectPath ?? process.cwd();
|
|
473
|
+
logger.debug("fixupLegacyProject: Checking for legacy project", { projectPath: resolvedPath });
|
|
474
|
+
const hasMarker = await this.isProjectConfigured(resolvedPath);
|
|
475
|
+
if (hasMarker) {
|
|
476
|
+
logger.debug("fixupLegacyProject: Project already has global marker");
|
|
477
|
+
return { isConfigured: true, wasFixedUp: false };
|
|
478
|
+
}
|
|
479
|
+
const hasLocal = await this.hasLocalSettings(resolvedPath);
|
|
480
|
+
if (!hasLocal) {
|
|
481
|
+
logger.debug("fixupLegacyProject: No local settings found, not a legacy project");
|
|
482
|
+
return { isConfigured: false, wasFixedUp: false };
|
|
483
|
+
}
|
|
484
|
+
logger.debug("fixupLegacyProject: Legacy project detected, creating global marker");
|
|
485
|
+
await this.markProjectAsConfigured(resolvedPath);
|
|
486
|
+
return { isConfigured: true, wasFixedUp: true };
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Mark a project as configured
|
|
490
|
+
* Creates a marker file with project metadata
|
|
491
|
+
*/
|
|
492
|
+
async markProjectAsConfigured(projectPath) {
|
|
493
|
+
const resolvedPath = projectPath ?? process.cwd();
|
|
494
|
+
const markerPath = this.getProjectMarkerPath(resolvedPath);
|
|
495
|
+
logger.debug("markProjectAsConfigured: Creating marker file", { markerPath });
|
|
496
|
+
try {
|
|
497
|
+
await fs.ensureDir(this.getProjectsDir());
|
|
498
|
+
const markerContent = {
|
|
499
|
+
configuredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
500
|
+
projectPath: resolvedPath,
|
|
501
|
+
projectName: this.getProjectName(resolvedPath)
|
|
502
|
+
};
|
|
503
|
+
await fs.writeFile(markerPath, JSON.stringify(markerContent, null, 2), "utf8");
|
|
504
|
+
logger.debug("markProjectAsConfigured: Marker file created successfully");
|
|
505
|
+
} catch (error) {
|
|
506
|
+
logger.debug(`markProjectAsConfigured: Failed to create marker file: ${error}`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Check if this is the first run of the feature
|
|
511
|
+
* Returns true if marker file doesn't exist
|
|
512
|
+
* Handles errors gracefully by returning true (treat as first-run on error)
|
|
513
|
+
*/
|
|
514
|
+
async isFirstRun() {
|
|
515
|
+
logger.debug("isFirstRun: Checking for marker file", { markerFilePath: this.markerFilePath });
|
|
516
|
+
try {
|
|
517
|
+
const exists = await fs.pathExists(this.markerFilePath);
|
|
518
|
+
logger.debug(`isFirstRun: Marker file exists=${exists}`);
|
|
519
|
+
return !exists;
|
|
520
|
+
} catch (error) {
|
|
521
|
+
logger.debug(`isFirstRun: Error checking marker file, treating as first-run: ${error}`);
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Mark the feature as having been run
|
|
527
|
+
* Creates the marker file in config directory
|
|
528
|
+
* Handles errors gracefully without throwing
|
|
529
|
+
*/
|
|
530
|
+
async markAsRun() {
|
|
531
|
+
logger.debug("markAsRun: Attempting to create marker file", { markerFilePath: this.markerFilePath });
|
|
532
|
+
try {
|
|
533
|
+
const configDir = path.dirname(this.markerFilePath);
|
|
534
|
+
logger.debug(`markAsRun: Ensuring config directory exists: ${configDir}`);
|
|
535
|
+
await fs.ensureDir(configDir);
|
|
536
|
+
const markerContent = {
|
|
537
|
+
firstRun: (/* @__PURE__ */ new Date()).toISOString()
|
|
538
|
+
};
|
|
539
|
+
await fs.writeFile(this.markerFilePath, JSON.stringify(markerContent, null, 2), "utf8");
|
|
540
|
+
logger.debug("markAsRun: Marker file created successfully");
|
|
541
|
+
} catch (error) {
|
|
542
|
+
logger.debug(`markAsRun: Failed to create marker file: ${error}`);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
// src/utils/mcp.ts
|
|
548
|
+
import path2 from "path";
|
|
381
549
|
async function generateIssueManagementMcpConfig(contextType, repo, provider = "github", settings) {
|
|
382
550
|
var _a, _b;
|
|
383
551
|
let envVars = {
|
|
@@ -429,7 +597,7 @@ async function generateIssueManagementMcpConfig(contextType, repo, provider = "g
|
|
|
429
597
|
issue_management: {
|
|
430
598
|
transport: "stdio",
|
|
431
599
|
command: "node",
|
|
432
|
-
args: [
|
|
600
|
+
args: [path2.join(path2.dirname(new globalThis.URL(import.meta.url).pathname), "../dist/mcp/issue-management-server.js")],
|
|
433
601
|
env: envVars
|
|
434
602
|
}
|
|
435
603
|
}
|
|
@@ -439,6 +607,7 @@ async function generateIssueManagementMcpConfig(contextType, repo, provider = "g
|
|
|
439
607
|
|
|
440
608
|
export {
|
|
441
609
|
IssueTrackerFactory,
|
|
610
|
+
FirstRunManager,
|
|
442
611
|
generateIssueManagementMcpConfig
|
|
443
612
|
};
|
|
444
|
-
//# sourceMappingURL=chunk-
|
|
613
|
+
//# sourceMappingURL=chunk-DLHA5VQ3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types/linear.ts","../src/utils/linear.ts","../src/lib/LinearService.ts","../src/lib/IssueTrackerFactory.ts","../src/utils/FirstRunManager.ts","../src/utils/mcp.ts"],"sourcesContent":["/**\n * Linear API response types (from @linear/sdk)\n */\n\n/**\n * Linear issue response from SDK\n */\nexport interface LinearIssue {\n /** Linear internal UUID */\n id: string\n /** Issue identifier in TEAM-NUMBER format (e.g., ENG-123) */\n identifier: string\n /** Issue title */\n title: string\n /** Issue description (markdown) */\n description?: string\n /** Current workflow state name */\n state?: string\n /** Linear web URL */\n url: string\n /** Creation timestamp (ISO string) */\n createdAt: string\n /** Last update timestamp (ISO string) */\n updatedAt: string\n}\n\n/**\n * Linear comment response from SDK\n */\nexport interface LinearComment {\n /** Comment UUID */\n id: string\n /** Comment body (markdown) */\n body: string\n /** Creation timestamp (ISO string) */\n createdAt: string\n /** Last update timestamp (ISO string) */\n updatedAt: string\n /** Comment URL */\n url: string\n}\n\n/**\n * Linear error codes\n */\nexport type LinearErrorCode =\n | 'NOT_FOUND'\n | 'UNAUTHORIZED'\n | 'INVALID_STATE'\n | 'RATE_LIMITED'\n | 'CLI_NOT_FOUND'\n | 'CLI_ERROR'\n\n/**\n * Linear error details\n */\nexport interface LinearError {\n code: LinearErrorCode\n message: string\n details?: unknown\n}\n\n/**\n * Custom error class for Linear operations\n */\nexport class LinearServiceError extends Error {\n constructor(\n public code: LinearErrorCode,\n message: string,\n public details?: unknown,\n ) {\n super(message)\n this.name = 'LinearServiceError'\n // Maintain proper stack trace for where error was thrown (V8 only)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, LinearServiceError)\n }\n }\n}\n","/**\n * Linear SDK utilities\n * Wrapper functions for the @linear/sdk\n */\n\nimport { LinearClient } from '@linear/sdk'\nimport type { LinearIssue, LinearComment } from '../types/linear.js'\nimport { LinearServiceError } from '../types/linear.js'\nimport { logger } from './logger.js'\n\n/**\n * Slugify a title for use in Linear URLs\n * Converts to lowercase, replaces non-alphanumeric with hyphens, truncates to reasonable length\n * @param title - Issue title\n * @param maxLength - Maximum slug length (default: 50)\n * @returns Slugified title\n */\nexport function slugifyTitle(title: string, maxLength: number = 50): string {\n // Convert to lowercase, replace non-alphanumeric chars with hyphens\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '') // trim leading/trailing hyphens\n\n // If already short enough, return as-is\n if (slug.length <= maxLength) {\n return slug\n }\n\n // Split by hyphens and rebuild until we hit the limit\n const parts = slug.split('-')\n let result = ''\n for (const part of parts) {\n const candidate = result ? `${result}-${part}` : part\n if (candidate.length > maxLength) {\n break\n }\n result = candidate\n }\n\n return result || slug.slice(0, maxLength) // fallback if first part is too long\n}\n\n/**\n * Build a Linear issue URL with optional title slug\n * @param identifier - Issue identifier (e.g., \"ENG-123\")\n * @param title - Optional issue title for slug\n * @returns Linear URL\n */\nexport function buildLinearIssueUrl(identifier: string, title?: string): string {\n const base = `https://linear.app/issue/${identifier}`\n if (title) {\n const slug = slugifyTitle(title)\n return slug ? `${base}/${slug}` : base\n }\n return base\n}\n\n/**\n * Get Linear API token from environment\n * @returns API token\n * @throws LinearServiceError if token not found\n */\nfunction getLinearApiToken(): string {\n const token = process.env.LINEAR_API_TOKEN\n if (!token) {\n throw new LinearServiceError(\n 'UNAUTHORIZED',\n 'LINEAR_API_TOKEN not set. Configure in settings.local.json or set environment variable.',\n )\n }\n return token\n}\n\n/**\n * Create a Linear SDK client instance\n * @returns Configured LinearClient\n */\nfunction createLinearClient(): LinearClient {\n return new LinearClient({ apiKey: getLinearApiToken() })\n}\n\n/**\n * Handle SDK errors and convert to LinearServiceError\n * @param error - Error from SDK\n * @param context - Context string for debugging\n * @throws LinearServiceError\n */\nfunction handleLinearError(error: unknown, context: string): never {\n logger.debug(`${context}: Handling error`, { error })\n\n // SDK errors typically have a message property\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n // Map common error patterns\n if (errorMessage.includes('not found') || errorMessage.includes('Not found')) {\n throw new LinearServiceError('NOT_FOUND', 'Linear issue or resource not found', { error })\n }\n\n if (\n errorMessage.includes('unauthorized') ||\n errorMessage.includes('Unauthorized') ||\n errorMessage.includes('Invalid API key')\n ) {\n throw new LinearServiceError(\n 'UNAUTHORIZED',\n 'Linear authentication failed. Check LINEAR_API_TOKEN.',\n { error },\n )\n }\n\n if (errorMessage.includes('rate limit')) {\n throw new LinearServiceError('RATE_LIMITED', 'Linear API rate limit exceeded', { error })\n }\n\n // Generic SDK error\n throw new LinearServiceError('CLI_ERROR', `Linear SDK error: ${errorMessage}`, { error })\n}\n\n/**\n * Fetch a Linear issue by identifier\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @returns Linear issue details\n * @throws LinearServiceError if issue not found or SDK error\n */\nexport async function fetchLinearIssue(identifier: string): Promise<LinearIssue> {\n try {\n logger.debug(`Fetching Linear issue: ${identifier}`)\n const client = createLinearClient()\n const issue = await client.issue(identifier)\n\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Convert SDK issue to our LinearIssue type\n const result: LinearIssue = {\n id: issue.id,\n identifier: issue.identifier,\n title: issue.title,\n url: issue.url,\n createdAt: issue.createdAt.toISOString(),\n updatedAt: issue.updatedAt.toISOString(),\n }\n\n // Add optional fields if present\n if (issue.description) {\n result.description = issue.description\n }\n\n if (issue.state) {\n const state = await issue.state\n if (state?.name) {\n result.state = state.name\n }\n }\n\n return result\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'fetchLinearIssue')\n }\n}\n\n/**\n * Create a new Linear issue\n * @param title - Issue title\n * @param body - Issue description (markdown)\n * @param teamKey - Team key (e.g., \"ENG\", \"PLAT\")\n * @param labels - Optional label names to apply\n * @returns Created issue identifier and URL\n * @throws LinearServiceError on creation failure\n */\nexport async function createLinearIssue(\n title: string,\n body: string,\n teamKey: string,\n _labels?: string[],\n): Promise<{ identifier: string; url: string }> {\n try {\n logger.debug(`Creating Linear issue in team ${teamKey}: ${title}`)\n const client = createLinearClient()\n\n // Get team by key\n const teams = await client.teams()\n const team = teams.nodes.find((t) => t.key === teamKey)\n\n if (!team) {\n throw new LinearServiceError('NOT_FOUND', `Linear team ${teamKey} not found`)\n }\n\n // Create issue\n const issueInput: { teamId: string; title: string; description?: string } = {\n teamId: team.id,\n title,\n }\n\n if (body) {\n issueInput.description = body\n }\n\n const payload = await client.createIssue(issueInput)\n\n const issue = await payload.issue\n\n if (!issue) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to create Linear issue')\n }\n\n // Construct URL\n const url = issue.url ?? buildLinearIssueUrl(issue.identifier, title)\n\n return {\n identifier: issue.identifier,\n url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'createLinearIssue')\n }\n}\n\n/**\n * Create a comment on a Linear issue\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @param body - Comment body (markdown)\n * @returns Created comment details\n * @throws LinearServiceError on creation failure\n */\nexport async function createLinearComment(\n identifier: string,\n body: string,\n): Promise<LinearComment> {\n try {\n logger.debug(`Creating comment on Linear issue ${identifier}`)\n const client = createLinearClient()\n\n // Get issue by identifier to get its ID\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Create comment using issue ID\n const payload = await client.createComment({\n issueId: issue.id,\n body,\n })\n\n const comment = await payload.comment\n\n if (!comment) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to create Linear comment')\n }\n\n return {\n id: comment.id,\n body: comment.body,\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n url: comment.url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'createLinearComment')\n }\n}\n\n/**\n * Update a Linear issue's workflow state\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @param stateName - Target state name (e.g., \"In Progress\", \"Done\")\n * @throws LinearServiceError on update failure\n */\nexport async function updateLinearIssueState(\n identifier: string,\n stateName: string,\n): Promise<void> {\n try {\n logger.debug(`Updating Linear issue ${identifier} state to: ${stateName}`)\n const client = createLinearClient()\n\n // Get issue by identifier\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Get team to find state\n const team = await issue.team\n if (!team) {\n throw new LinearServiceError('CLI_ERROR', 'Issue has no team')\n }\n\n // Find state by name\n const states = await team.states()\n const state = states.nodes.find((s) => s.name === stateName)\n\n if (!state) {\n throw new LinearServiceError(\n 'NOT_FOUND',\n `State \"${stateName}\" not found in team ${team.key}`,\n )\n }\n\n // Update issue state\n await client.updateIssue(issue.id, {\n stateId: state.id,\n })\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'updateLinearIssueState')\n }\n}\n\n/**\n * Get a specific comment by ID\n * @param commentId - Linear comment UUID\n * @returns Comment details\n * @throws LinearServiceError if comment not found\n */\nexport async function getLinearComment(commentId: string): Promise<LinearComment> {\n try {\n logger.debug(`Fetching Linear comment: ${commentId}`)\n const client = createLinearClient()\n const comment = await client.comment({ id: commentId })\n\n if (!comment) {\n throw new LinearServiceError('NOT_FOUND', `Linear comment ${commentId} not found`)\n }\n\n return {\n id: comment.id,\n body: comment.body,\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n url: comment.url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'getLinearComment')\n }\n}\n\n/**\n * Update an existing comment\n * @param commentId - Linear comment UUID\n * @param body - New comment body (markdown)\n * @returns Updated comment details\n * @throws LinearServiceError on update failure\n */\nexport async function updateLinearComment(\n commentId: string,\n body: string,\n): Promise<LinearComment> {\n try {\n logger.debug(`Updating Linear comment: ${commentId}`)\n const client = createLinearClient()\n\n const payload = await client.updateComment(commentId, { body })\n const comment = await payload.comment\n\n if (!comment) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to update Linear comment')\n }\n\n return {\n id: comment.id,\n body: comment.body,\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n url: comment.url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'updateLinearComment')\n }\n}\n\n/**\n * Fetch all comments for a Linear issue\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @returns Array of comments\n * @throws LinearServiceError on fetch failure\n */\nexport async function fetchLinearIssueComments(identifier: string): Promise<LinearComment[]> {\n try {\n logger.debug(`Fetching comments for Linear issue: ${identifier}`)\n const client = createLinearClient()\n\n // Get issue by identifier\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Fetch comments\n const comments = await issue.comments({ first: 100 })\n\n return comments.nodes.map((comment) => ({\n id: comment.id,\n body: comment.body,\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n url: comment.url,\n }))\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'fetchLinearIssueComments')\n }\n}\n","/**\n * LinearService - IssueTracker implementation for Linear\n * Implements issue tracking operations using the @linear/sdk\n */\n\nimport type { Issue, PullRequest, IssueTrackerInputDetection } from '../types/index.js'\nimport type { LinearIssue } from '../types/linear.js'\nimport { LinearServiceError } from '../types/linear.js'\nimport {\n fetchLinearIssue,\n createLinearIssue,\n updateLinearIssueState,\n} from '../utils/linear.js'\nimport { promptConfirmation } from '../utils/prompt.js'\nimport type { IssueTracker } from './IssueTracker.js'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Linear service configuration options\n */\nexport interface LinearServiceConfig {\n /** Linear team key (e.g., \"ENG\", \"PLAT\") */\n teamId?: string\n /** Branch naming template (e.g., \"feat/{{key}}__{{title}}\") */\n branchFormat?: string\n}\n\n/**\n * Linear implementation of IssueTracker interface\n */\nexport class LinearService implements IssueTracker {\n // IssueTracker interface implementation\n readonly providerName = 'linear'\n readonly supportsPullRequests = false // Linear doesn't have pull requests\n\n private config: LinearServiceConfig\n private prompter: (message: string) => Promise<boolean>\n\n constructor(\n config?: LinearServiceConfig,\n options?: { prompter?: (message: string) => Promise<boolean> },\n ) {\n this.config = config ?? {}\n this.prompter = options?.prompter ?? promptConfirmation\n }\n\n /**\n * Detect if input matches Linear identifier format (TEAM-NUMBER)\n * @param input - User input string\n * @param _repo - Repository (unused for Linear)\n * @returns Detection result with type and identifier\n */\n public async detectInputType(\n input: string,\n _repo?: string,\n ): Promise<IssueTrackerInputDetection> {\n logger.debug(`LinearService.detectInputType called with input: \"${input}\"`)\n\n // Pattern: TEAM-NUMBER (e.g., ENG-123, PLAT-456)\n // Requires at least 2 letters before dash to avoid conflict with PR-123 format\n const linearPattern = /^([A-Z]{2,}-\\d+)$/i\n const match = input.match(linearPattern)\n\n if (!match?.[1]) {\n logger.debug(`LinearService: Input \"${input}\" does not match Linear pattern`)\n return { type: 'unknown', identifier: null, rawInput: input }\n }\n\n const identifier = match[1].toUpperCase()\n logger.debug(`LinearService: Matched Linear identifier: ${identifier}`)\n\n // Validate the issue exists in Linear\n logger.debug(`LinearService: Checking if ${identifier} is a valid Linear issue via SDK`)\n const issue = await this.isValidIssue(identifier)\n\n if (issue) {\n logger.debug(`LinearService: Issue ${identifier} found: \"${issue.title}\"`)\n return { type: 'issue', identifier, rawInput: input }\n }\n\n // Not found\n logger.debug(`LinearService: Issue ${identifier} NOT found by SDK`)\n return { type: 'unknown', identifier: null, rawInput: input }\n }\n\n /**\n * Fetch a Linear issue by identifier\n * @param identifier - Linear issue identifier (string or number)\n * @param _repo - Repository (unused for Linear)\n * @returns Generic Issue type\n * @throws LinearServiceError if issue not found\n */\n public async fetchIssue(identifier: string | number, _repo?: string): Promise<Issue> {\n const linearIssue = await fetchLinearIssue(String(identifier))\n return this.mapLinearIssueToIssue(linearIssue)\n }\n\n /**\n * Check if an issue identifier is valid (silent validation)\n * @param identifier - Linear issue identifier\n * @param _repo - Repository (unused for Linear)\n * @returns Issue if valid, false if not found\n */\n public async isValidIssue(identifier: string | number, _repo?: string): Promise<Issue | false> {\n try {\n return await this.fetchIssue(identifier)\n } catch (error) {\n // Return false for NOT_FOUND errors (expected during detection)\n if (error instanceof LinearServiceError && error.code === 'NOT_FOUND') {\n return false\n }\n // Re-throw unexpected errors\n throw error\n }\n }\n\n /**\n * Validate issue state and prompt user if closed\n * @param issue - Issue to validate\n * @throws LinearServiceError if user cancels due to closed issue\n */\n public async validateIssueState(issue: Issue): Promise<void> {\n if (issue.state === 'closed') {\n const shouldContinue = await this.prompter(\n `Issue ${issue.number} is closed. Continue anyway?`,\n )\n\n if (!shouldContinue) {\n throw new LinearServiceError('INVALID_STATE', 'User cancelled due to closed issue')\n }\n }\n }\n\n /**\n * Create a new Linear issue\n * @param title - Issue title\n * @param body - Issue description (markdown)\n * @param _repository - Repository (unused for Linear)\n * @param labels - Optional label names\n * @returns Created issue identifier and URL\n * @throws LinearServiceError if teamId not configured or creation fails\n */\n public async createIssue(\n title: string,\n body: string,\n _repository?: string,\n labels?: string[],\n ): Promise<{ number: string | number; url: string }> {\n // Require teamId configuration\n if (!this.config.teamId) {\n throw new LinearServiceError(\n 'INVALID_STATE',\n 'Linear teamId not configured. Run `il init` to configure Linear settings.',\n )\n }\n\n logger.info(`Creating Linear issue in team ${this.config.teamId}: ${title}`)\n\n const result = await createLinearIssue(title, body, this.config.teamId, labels)\n\n return {\n number: result.identifier,\n url: result.url,\n }\n }\n\n /**\n * Get the web URL for a Linear issue\n * @param identifier - Linear issue identifier\n * @param _repo - Repository (unused for Linear)\n * @returns Issue URL\n */\n public async getIssueUrl(identifier: string | number, _repo?: string): Promise<string> {\n const issue = await this.fetchIssue(identifier)\n return issue.url\n }\n\n /**\n * Move a Linear issue to \"In Progress\" state\n * @param identifier - Linear issue identifier\n * @throws LinearServiceError if state update fails\n */\n public async moveIssueToInProgress(identifier: string | number): Promise<void> {\n logger.info(`Moving Linear issue ${identifier} to In Progress`)\n await updateLinearIssueState(String(identifier), 'In Progress')\n }\n\n /**\n * Extract issue context for AI prompts\n * @param entity - Issue (Linear doesn't have PRs)\n * @returns Formatted context string\n */\n public extractContext(entity: Issue | PullRequest): string {\n // Linear doesn't have PRs, always an issue\n const issue = entity as Issue\n return `Linear Issue ${issue.number}: ${issue.title}\\nState: ${issue.state}\\n\\n${issue.body}`\n }\n\n /**\n * Map Linear API issue to generic Issue type\n * @param linear - Linear issue from SDK\n * @returns Generic Issue type\n */\n private mapLinearIssueToIssue(linear: LinearIssue): Issue {\n return {\n number: linear.identifier, // Keep as string (e.g., \"ENG-123\")\n title: linear.title,\n body: linear.description ?? '',\n state: linear.state ? (linear.state.toLowerCase().includes('done') || linear.state.toLowerCase().includes('completed') || linear.state.toLowerCase().includes('canceled') ? 'closed' : 'open') : 'open',\n labels: [],\n assignees: [],\n url: linear.url,\n }\n }\n}\n","// IssueTrackerFactory - creates appropriate IssueTracker based on settings\n// Follows pattern from database provider instantiation\n\nimport type { IssueTracker } from './IssueTracker.js'\nimport { GitHubService } from './GitHubService.js'\nimport { LinearService, type LinearServiceConfig } from './LinearService.js'\nimport type { IloomSettings } from './SettingsManager.js'\nimport { logger } from '../utils/logger.js'\n\nexport type IssueTrackerProviderType = 'github' | 'linear'\n\n/**\n * Factory for creating IssueTracker instances based on settings\n * Provides a single point of provider instantiation\n *\n * Usage:\n * const tracker = IssueTrackerFactory.create(settings, { useClaude: true })\n * const issue = await tracker.fetchIssue(123)\n */\nexport class IssueTrackerFactory {\n\t/**\n\t * Create an IssueTracker instance based on settings configuration\n\t * Defaults to GitHub if no provider specified\n\t *\n\t * @param settings - iloom settings containing issueManagement.provider\n\t * @returns IssueTracker instance configured for the specified provider\n\t * @throws Error if provider type is not supported\n\t */\n\tstatic create(settings: IloomSettings): IssueTracker {\n\t\tconst provider = settings.issueManagement?.provider ?? 'github'\n\n\t\tlogger.debug(`IssueTrackerFactory: Creating tracker for provider \"${provider}\"`)\n\t\tlogger.debug(`IssueTrackerFactory: issueManagement settings:`, JSON.stringify(settings.issueManagement, null, 2))\n\n\t\tswitch (provider) {\n\t\t\tcase 'github':\n\t\t\t\tlogger.debug('IssueTrackerFactory: Creating GitHubService')\n\t\t\t\treturn new GitHubService()\n\t\t\tcase 'linear': {\n\t\t\t\tconst linearSettings = settings.issueManagement?.linear\n\t\t\t\tconst linearConfig: LinearServiceConfig = {}\n\n\t\t\t\tif (linearSettings?.teamId) {\n\t\t\t\t\tlinearConfig.teamId = linearSettings.teamId\n\t\t\t\t}\n\t\t\t\tif (linearSettings?.branchFormat) {\n\t\t\t\t\tlinearConfig.branchFormat = linearSettings.branchFormat\n\t\t\t\t}\n\n\t\t\t\tlogger.debug(`IssueTrackerFactory: Creating LinearService with config:`, JSON.stringify(linearConfig, null, 2))\n\t\t\t\treturn new LinearService(linearConfig)\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unsupported issue tracker provider: ${provider}`)\n\t\t}\n\t}\n\n\t/**\n\t * Get the configured provider name from settings\n\t * Defaults to 'github' if not configured\n\t *\n\t * @param settings - iloom settings\n\t * @returns Provider type string\n\t */\n\tstatic getProviderName(settings: IloomSettings): IssueTrackerProviderType {\n\t\treturn (settings.issueManagement?.provider ?? 'github') as IssueTrackerProviderType\n\t}\n}\n","import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport { logger } from './logger.js'\n\n/**\n * FirstRunManager: Detect and track first-time spin usage via marker file\n *\n * Follows the same pattern as UpdateNotifier for file-based state tracking\n * in ~/.config/iloom-ai/ directory.\n *\n * Also supports project-level tracking for config wizard completion using\n * individual marker files per project in ~/.config/iloom-ai/projects/\n */\nexport class FirstRunManager {\n\tprivate markerFilePath: string\n\tprivate configDir: string\n\n\tconstructor(feature: string = 'spin') {\n\t\tthis.configDir = path.join(os.homedir(), '.config', 'iloom-ai')\n\t\tthis.markerFilePath = path.join(this.configDir, `${feature}-first-run`)\n\t\tlogger.debug('FirstRunManager initialized', {\n\t\t\tfeature,\n\t\t\tmarkerFilePath: this.markerFilePath\n\t\t})\n\t}\n\n\t/**\n\t * Get the directory for project marker files\n\t */\n\tprivate getProjectsDir(): string {\n\t\treturn path.join(this.configDir, 'projects')\n\t}\n\n\t/**\n\t * Convert a project path to a readable filename\n\t * /Users/adam/Projects/my-app -> Users__adam__Projects__my-app\n\t */\n\tprivate projectPathToFileName(projectPath: string): string {\n\t\tconst normalized = path.normalize(projectPath)\n\t\treturn normalized.replace(/^[/\\\\]+/, '').replace(/[/\\\\]+/g, '__')\n\t}\n\n\t/**\n\t * Get full path to a project's marker file\n\t */\n\tprivate getProjectMarkerPath(projectPath: string): string {\n\t\treturn path.join(this.getProjectsDir(), this.projectPathToFileName(projectPath))\n\t}\n\n\t/**\n\t * Extract project name from path\n\t */\n\tprivate getProjectName(projectPath: string): string {\n\t\treturn path.basename(projectPath)\n\t}\n\n\t/**\n\t * Check if a project has been configured\n\t * Returns true if project marker file exists\n\t */\n\tasync isProjectConfigured(projectPath?: string): Promise<boolean> {\n\t\tconst resolvedPath = projectPath ?? process.cwd()\n\t\tconst markerPath = this.getProjectMarkerPath(resolvedPath)\n\t\tlogger.debug('isProjectConfigured: Checking for marker file', { markerPath })\n\t\ttry {\n\t\t\tconst exists = await fs.pathExists(markerPath)\n\t\t\tlogger.debug(`isProjectConfigured: Marker file exists=${exists}`)\n\t\t\treturn exists\n\t\t} catch (error) {\n\t\t\t// On error, treat as not configured to allow wizard to run\n\t\t\tlogger.debug(`isProjectConfigured: Error checking marker file, treating as not configured: ${error}`)\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Check if a project has local .iloom settings files\n\t * Returns true if .iloom/settings.json or .iloom/settings.local.json exists with content\n\t */\n\tprivate async hasLocalSettings(projectPath: string): Promise<boolean> {\n\t\tconst iloomDir = path.join(projectPath, '.iloom')\n\t\tconst settingsPath = path.join(iloomDir, 'settings.json')\n\t\tconst settingsLocalPath = path.join(iloomDir, 'settings.local.json')\n\n\t\tconst hasSettings = await this.hasNonEmptySettingsFile(settingsPath)\n\t\tconst hasLocalSettings = await this.hasNonEmptySettingsFile(settingsLocalPath)\n\n\t\treturn hasSettings || hasLocalSettings\n\t}\n\n\t/**\n\t * Check if a settings file exists and has non-empty content\n\t */\n\tprivate async hasNonEmptySettingsFile(filePath: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst exists = await fs.pathExists(filePath)\n\t\t\tif (!exists) return false\n\t\t\tconst content = await fs.readFile(filePath, 'utf8')\n\t\t\tconst parsed = JSON.parse(content)\n\t\t\treturn Object.keys(parsed).length > 0\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Fixup legacy projects that have local config but lack the global marker file\n\t * This handles projects configured before global project tracking was implemented\n\t *\n\t * Returns object with:\n\t * - isConfigured: true if project is configured (either already had marker or fixup created one)\n\t * - wasFixedUp: true if fixup was performed (marker was created by this call)\n\t *\n\t * This combined return avoids duplicate isProjectConfigured() calls in needsFirstRunSetup()\n\t */\n\tasync fixupLegacyProject(projectPath?: string): Promise<{ isConfigured: boolean; wasFixedUp: boolean }> {\n\t\tconst resolvedPath = projectPath ?? process.cwd()\n\t\tlogger.debug('fixupLegacyProject: Checking for legacy project', { projectPath: resolvedPath })\n\n\t\t// Check if already has global marker - no fixup needed, but project IS configured\n\t\tconst hasMarker = await this.isProjectConfigured(resolvedPath)\n\t\tif (hasMarker) {\n\t\t\tlogger.debug('fixupLegacyProject: Project already has global marker')\n\t\t\treturn { isConfigured: true, wasFixedUp: false }\n\t\t}\n\n\t\t// Check if has local settings files - this indicates a legacy configured project\n\t\tconst hasLocal = await this.hasLocalSettings(resolvedPath)\n\t\tif (!hasLocal) {\n\t\t\tlogger.debug('fixupLegacyProject: No local settings found, not a legacy project')\n\t\t\treturn { isConfigured: false, wasFixedUp: false }\n\t\t}\n\n\t\t// Legacy project found - create the missing global marker\n\t\tlogger.debug('fixupLegacyProject: Legacy project detected, creating global marker')\n\t\tawait this.markProjectAsConfigured(resolvedPath)\n\t\treturn { isConfigured: true, wasFixedUp: true }\n\t}\n\n\t/**\n\t * Mark a project as configured\n\t * Creates a marker file with project metadata\n\t */\n\tasync markProjectAsConfigured(projectPath?: string): Promise<void> {\n\t\tconst resolvedPath = projectPath ?? process.cwd()\n\t\tconst markerPath = this.getProjectMarkerPath(resolvedPath)\n\t\tlogger.debug('markProjectAsConfigured: Creating marker file', { markerPath })\n\t\ttry {\n\t\t\tawait fs.ensureDir(this.getProjectsDir())\n\t\t\tconst markerContent = {\n\t\t\t\tconfiguredAt: new Date().toISOString(),\n\t\t\t\tprojectPath: resolvedPath,\n\t\t\t\tprojectName: this.getProjectName(resolvedPath)\n\t\t\t}\n\t\t\tawait fs.writeFile(markerPath, JSON.stringify(markerContent, null, 2), 'utf8')\n\t\t\tlogger.debug('markProjectAsConfigured: Marker file created successfully')\n\t\t} catch (error) {\n\t\t\t// Don't throw on errors - just log debug message\n\t\t\tlogger.debug(`markProjectAsConfigured: Failed to create marker file: ${error}`)\n\t\t}\n\t}\n\n\t/**\n\t * Check if this is the first run of the feature\n\t * Returns true if marker file doesn't exist\n\t * Handles errors gracefully by returning true (treat as first-run on error)\n\t */\n\tasync isFirstRun(): Promise<boolean> {\n\t\tlogger.debug('isFirstRun: Checking for marker file', { markerFilePath: this.markerFilePath })\n\t\ttry {\n\t\t\tconst exists = await fs.pathExists(this.markerFilePath)\n\t\t\tlogger.debug(`isFirstRun: Marker file exists=${exists}`)\n\t\t\treturn !exists\n\t\t} catch (error) {\n\t\t\t// On error, gracefully degrade by treating as first-run\n\t\t\tlogger.debug(`isFirstRun: Error checking marker file, treating as first-run: ${error}`)\n\t\t\treturn true\n\t\t}\n\t}\n\n\t/**\n\t * Mark the feature as having been run\n\t * Creates the marker file in config directory\n\t * Handles errors gracefully without throwing\n\t */\n\tasync markAsRun(): Promise<void> {\n\t\tlogger.debug('markAsRun: Attempting to create marker file', { markerFilePath: this.markerFilePath })\n\t\ttry {\n\t\t\t// Ensure directory exists\n\t\t\tconst configDir = path.dirname(this.markerFilePath)\n\t\t\tlogger.debug(`markAsRun: Ensuring config directory exists: ${configDir}`)\n\t\t\tawait fs.ensureDir(configDir)\n\n\t\t\t// Write marker file with timestamp for debugging\n\t\t\tconst markerContent = {\n\t\t\t\tfirstRun: new Date().toISOString(),\n\t\t\t}\n\t\t\tawait fs.writeFile(this.markerFilePath, JSON.stringify(markerContent, null, 2), 'utf8')\n\t\t\tlogger.debug('markAsRun: Marker file created successfully')\n\t\t} catch (error) {\n\t\t\t// Don't throw on errors - just log debug message\n\t\t\t// Failing to write marker shouldn't break the workflow\n\t\t\tlogger.debug(`markAsRun: Failed to create marker file: ${error}`)\n\t\t}\n\t}\n}\n","import path from 'path'\nimport { getRepoInfo } from './github.js'\nimport { logger } from './logger.js'\nimport type { IloomSettings } from '../lib/SettingsManager.js'\n\n/**\n * Generate MCP configuration for issue management\n * Uses a single server that can handle both issues and pull requests\n * Returns array of MCP server config objects\n * @param contextType - Optional context type (issue or pr)\n * @param repo - Optional repo in \"owner/repo\" format. If not provided, will auto-detect from git.\n * @param provider - Issue management provider (default: 'github')\n * @param settings - Optional settings to extract Linear API token from\n */\nexport async function generateIssueManagementMcpConfig(\n\tcontextType?: 'issue' | 'pr',\n\trepo?: string,\n\tprovider: 'github' | 'linear' = 'github',\n\tsettings?: IloomSettings\n): Promise<Record<string, unknown>[]> {\n\t// Build provider-specific environment variables\n\tlet envVars: Record<string, string> = {\n\t\tISSUE_PROVIDER: provider,\n\t}\n\n\tif (provider === 'github') {\n\t\t// Get repository information for GitHub - either from provided repo string or auto-detect\n\t\tlet owner: string\n\t\tlet name: string\n\n\t\tif (repo) {\n\t\t\tconst parts = repo.split('/')\n\t\t\tif (parts.length !== 2 || !parts[0] || !parts[1]) {\n\t\t\t\tthrow new Error(`Invalid repo format: ${repo}. Expected \"owner/repo\"`)\n\t\t\t}\n\t\t\towner = parts[0]\n\t\t\tname = parts[1]\n\t\t} else {\n\t\t\tconst repoInfo = await getRepoInfo()\n\t\t\towner = repoInfo.owner\n\t\t\tname = repoInfo.name\n\t\t}\n\n\t\t// Map logical types to GitHub's webhook event names (handle GitHub's naming quirk here)\n\t\tconst githubEventName = contextType === 'issue' ? 'issues' : contextType === 'pr' ? 'pull_request' : undefined\n\n\t\tenvVars = {\n\t\t\t...envVars,\n\t\t\tREPO_OWNER: owner,\n\t\t\tREPO_NAME: name,\n\t\t\tGITHUB_API_URL: 'https://api.github.com/',\n\t\t\t...(githubEventName && { GITHUB_EVENT_NAME: githubEventName }),\n\t\t}\n\n\t\tlogger.debug('Generated MCP config for GitHub issue management', {\n\t\t\tprovider,\n\t\t\trepoOwner: owner,\n\t\t\trepoName: name,\n\t\t\tcontextType: contextType ?? 'auto-detect',\n\t\t\tgithubEventName: githubEventName ?? 'auto-detect'\n\t\t})\n\t} else {\n\t\t// Linear needs API token passed through\n\t\tconst apiToken = settings?.issueManagement?.linear?.apiToken ?? process.env.LINEAR_API_TOKEN\n\n\t\tif (apiToken) {\n\t\t\tenvVars.LINEAR_API_TOKEN = apiToken\n\t\t}\n\n\t\tlogger.debug('Generated MCP config for Linear issue management', {\n\t\t\tprovider,\n\t\t\thasApiToken: !!apiToken,\n\t\t\tcontextType: contextType ?? 'auto-detect',\n\t\t})\n\t}\n\n\t// Generate single MCP server config\n\tconst mcpServerConfig = {\n\t\tmcpServers: {\n\t\t\tissue_management: {\n\t\t\t\ttransport: 'stdio',\n\t\t\t\tcommand: 'node',\n\t\t\t\targs: [path.join(path.dirname(new globalThis.URL(import.meta.url).pathname), '../dist/mcp/issue-management-server.js')],\n\t\t\t\tenv: envVars,\n\t\t\t},\n\t\t},\n\t}\n\n\treturn [mcpServerConfig]\n}"],"mappings":"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","names":["path","path"]}
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
} from "./chunk-YETJNRQM.js";
|
|
5
5
|
import {
|
|
6
6
|
waitForKeypress
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-CE26YH2U.js";
|
|
8
8
|
import {
|
|
9
9
|
launchClaude
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-OYF4VIFI.js";
|
|
11
11
|
import {
|
|
12
12
|
logger
|
|
13
13
|
} from "./chunk-GEHQXLEI.js";
|
|
@@ -120,7 +120,26 @@ Press any key to open issue for editing...`;
|
|
|
120
120
|
}
|
|
121
121
|
};
|
|
122
122
|
|
|
123
|
+
// src/utils/text.ts
|
|
124
|
+
function capitalizeFirstLetter(str) {
|
|
125
|
+
if (!str || str.length === 0) {
|
|
126
|
+
return str;
|
|
127
|
+
}
|
|
128
|
+
if (str.startsWith(" ")) {
|
|
129
|
+
return str.slice(1);
|
|
130
|
+
}
|
|
131
|
+
const firstChar = str.charAt(0);
|
|
132
|
+
const upperChar = firstChar.toUpperCase();
|
|
133
|
+
if (upperChar !== firstChar.toLowerCase() || new RegExp("\\p{L}", "u").test(firstChar)) {
|
|
134
|
+
if (upperChar !== firstChar) {
|
|
135
|
+
return upperChar + str.slice(1);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return str;
|
|
139
|
+
}
|
|
140
|
+
|
|
123
141
|
export {
|
|
124
|
-
IssueEnhancementService
|
|
142
|
+
IssueEnhancementService,
|
|
143
|
+
capitalizeFirstLetter
|
|
125
144
|
};
|
|
126
|
-
//# sourceMappingURL=chunk-
|
|
145
|
+
//# sourceMappingURL=chunk-IFB4Z76W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/IssueEnhancementService.ts","../src/utils/text.ts"],"sourcesContent":["import type { IssueTracker } from './IssueTracker.js'\nimport type { AgentManager } from './AgentManager.js'\nimport type { SettingsManager } from './SettingsManager.js'\nimport { launchClaude } from '../utils/claude.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { waitForKeypress } from '../utils/prompt.js'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Service for enhancing and creating issues with AI assistance.\n * Extracts reusable issue enhancement logic from StartCommand.\n */\nexport class IssueEnhancementService {\n\tconstructor(\n\t\tprivate issueTrackerService: IssueTracker,\n\t\tprivate agentManager: AgentManager,\n\t\tprivate settingsManager: SettingsManager\n\t) {}\n\n\t/**\n\t * Expose issue tracker for provider checks\n\t */\n\tpublic get issueTracker(): IssueTracker {\n\t\treturn this.issueTrackerService\n\t}\n\n\t/**\n\t * Validates that a description meets minimum requirements.\n\t * Requirements: >30 characters AND >2 spaces\n\t */\n\tpublic validateDescription(description: string): boolean {\n\t\tconst trimmedDescription = description.trim()\n\t\tconst spaceCount = (trimmedDescription.match(/ /g) ?? []).length\n\n\t\treturn trimmedDescription.length > 30 && spaceCount > 2\n\t}\n\n\t/**\n\t * Enhances a description using Claude Code in headless mode.\n\t * Falls back to original description if enhancement fails.\n\t */\n\tpublic async enhanceDescription(description: string): Promise<string> {\n\t\ttry {\n\t\t\tlogger.info('Enhancing description with Claude Code. This may take a moment...')\n\n\t\t\t// Load agent configurations\n\t\t\tconst settings = await this.settingsManager.loadSettings()\n\t\t\tconst loadedAgents = await this.agentManager.loadAgents(settings)\n\t\t\tconst agents = this.agentManager.formatForCli(loadedAgents)\n\n\t\t\t// Call Claude in headless mode with issue enhancer agent\n\t\t\tconst prompt = `@agent-iloom-issue-enhancer\n\nTASK: Enhance the following issue description for GitHub.\n\nINPUT:\n${description}\n\nOUTPUT REQUIREMENTS:\n- Return ONLY the enhanced description markdown text\n- NO meta-commentary (no \"Here is...\", \"The enhanced...\", \"I have...\", etc)\n- NO code block markers (\\`\\`\\`)\n- NO conversational framing or acknowledgments\n- NO explanations of your work\n- Start your response immediately with the enhanced content\n\nYour response should be the raw markdown that will become the GitHub issue body.`\n\n\t\t\tconst enhanced = await launchClaude(prompt, {\n\t\t\t\theadless: true,\n\t\t\t\tmodel: 'sonnet',\n\t\t\t\tagents,\n\t\t\t})\n\n\t\t\tif (enhanced && typeof enhanced === 'string') {\n\t\t\t\tlogger.success('Description enhanced successfully')\n\t\t\t\treturn enhanced\n\t\t\t}\n\n\t\t\t// Fallback to original description\n\t\t\tlogger.warn('Claude enhancement returned empty result, using original description')\n\t\t\treturn description\n\t\t} catch (error) {\n\t\t\tlogger.warn(`Failed to enhance description: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\treturn description\n\t\t}\n\t}\n\n\t/**\n\t * Creates a GitHub issue with title and enhanced body.\n\t * @param originalDescription - Used as the issue title\n\t * @param enhancedDescription - Used as the issue body\n\t * @param repository - Optional repository override (format: \"owner/repo\")\n\t * @param labels - Optional array of label names to add to the issue\n\t * @returns Issue number and URL\n\t */\n\tpublic async createEnhancedIssue(\n\t\toriginalDescription: string,\n\t\tenhancedDescription: string,\n\t\trepository?: string,\n\t\tlabels?: string[]\n\t): Promise<{ number: string | number; url: string }> {\n\t\tlogger.info('Creating GitHub issue from description...')\n\n\t\tconst result = await this.issueTrackerService.createIssue(\n\t\t\toriginalDescription, // Use original description as title\n\t\t\tenhancedDescription, // Use enhanced description as body\n\t\t\trepository,\n\t\t\tlabels\n\t\t)\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Waits for user keypress and opens issue in browser for review.\n\t * @param issueNumber - Issue number to open for review\n\t * @param confirm - If true, wait for additional keypress after opening browser before returning\n\t * @param repository - Optional repository to fetch issue from (format: \"owner/repo\")\n\t */\n\tpublic async waitForReviewAndOpen(issueNumber: string | number, confirm = false, repository?: string): Promise<void> {\n\t\t// Check if running in CI environment\n\t\tconst isCI = process.env.CI === 'true'\n\n\t\tif (isCI) {\n\t\t\t// In CI: Skip all interactive operations\n\t\t\tlogger.info(`Running in CI environment - skipping interactive prompts for issue #${issueNumber}`)\n\t\t\treturn\n\t\t}\n\n\t\t// Get issue URL\n\t\tconst issueUrl = await this.issueTrackerService.getIssueUrl(issueNumber, repository)\n\n\t\t// Display message and wait for first keypress\n\t\tconst message = `Created issue #${issueNumber}.\nReview and edit the issue in your browser if needed.\nPress any key to open issue for editing...`\n\t\tawait waitForKeypress(message)\n\n\t\t// Open issue in browser\n\t\tawait openBrowser(issueUrl)\n\n\t\t// If confirmation required, wait for second keypress\n\t\tif (confirm) {\n\t\t\tawait waitForKeypress('Press any key to continue with loom creation...')\n\t\t}\n\t}\n}\n","/**\n * Capitalizes the first letter of a string.\n *\n * Override behavior: If the string starts with a space, it signals the user\n * wants to opt-out of auto-capitalization. In this case, the leading space\n * is stripped and the first letter is NOT capitalized.\n *\n * @param str - The string to process\n * @returns The processed string with first letter capitalized (or original if override)\n */\nexport function capitalizeFirstLetter(str: string): string {\n\t// Handle empty or whitespace-only strings\n\tif (!str || str.length === 0) {\n\t\treturn str\n\t}\n\n\t// Check for space-prefix override: strip leading space and return as-is\n\tif (str.startsWith(' ')) {\n\t\treturn str.slice(1)\n\t}\n\n\t// Find the first character that could be capitalized (a letter)\n\tconst firstChar = str.charAt(0)\n\n\t// If first character is a letter (including unicode), capitalize it\n\t// Check if toUpperCase() produces a different result (indicates it's a letter with case)\n\tconst upperChar = firstChar.toUpperCase()\n\tif (upperChar !== firstChar.toLowerCase() || /\\p{L}/u.test(firstChar)) {\n\t\t// Only capitalize if it actually changes (avoids issues with non-cased scripts)\n\t\tif (upperChar !== firstChar) {\n\t\t\treturn upperChar + str.slice(1)\n\t\t}\n\t}\n\n\t// Non-letter first character or no case transformation available: return unchanged\n\treturn str\n}\n"],"mappings":";;;;;;;;;;;;;;;AAYO,IAAM,0BAAN,MAA8B;AAAA,EACpC,YACS,qBACA,cACA,iBACP;AAHO;AACA;AACA;AAAA,EACN;AAAA;AAAA;AAAA;AAAA,EAKH,IAAW,eAA6B;AACvC,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,oBAAoB,aAA8B;AACxD,UAAM,qBAAqB,YAAY,KAAK;AAC5C,UAAM,cAAc,mBAAmB,MAAM,IAAI,KAAK,CAAC,GAAG;AAE1D,WAAO,mBAAmB,SAAS,MAAM,aAAa;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,mBAAmB,aAAsC;AACrE,QAAI;AACH,aAAO,KAAK,mEAAmE;AAG/E,YAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa;AACzD,YAAM,eAAe,MAAM,KAAK,aAAa,WAAW,QAAQ;AAChE,YAAM,SAAS,KAAK,aAAa,aAAa,YAAY;AAG1D,YAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYV,YAAM,WAAW,MAAM,aAAa,QAAQ;AAAA,QAC3C,UAAU;AAAA,QACV,OAAO;AAAA,QACP;AAAA,MACD,CAAC;AAED,UAAI,YAAY,OAAO,aAAa,UAAU;AAC7C,eAAO,QAAQ,mCAAmC;AAClD,eAAO;AAAA,MACR;AAGA,aAAO,KAAK,sEAAsE;AAClF,aAAO;AAAA,IACR,SAAS,OAAO;AACf,aAAO,KAAK,kCAAkC,iBAAiB,QAAQ,MAAM,UAAU,eAAe,EAAE;AACxG,aAAO;AAAA,IACR;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,oBACZ,qBACA,qBACA,YACA,QACoD;AACpD,WAAO,KAAK,2CAA2C;AAEvD,UAAM,SAAS,MAAM,KAAK,oBAAoB;AAAA,MAC7C;AAAA;AAAA,MACA;AAAA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,qBAAqB,aAA8B,UAAU,OAAO,YAAoC;AAEpH,UAAM,OAAO,QAAQ,IAAI,OAAO;AAEhC,QAAI,MAAM;AAET,aAAO,KAAK,uEAAuE,WAAW,EAAE;AAChG;AAAA,IACD;AAGA,UAAM,WAAW,MAAM,KAAK,oBAAoB,YAAY,aAAa,UAAU;AAGnF,UAAM,UAAU,kBAAkB,WAAW;AAAA;AAAA;AAG7C,UAAM,gBAAgB,OAAO;AAG7B,UAAM,YAAY,QAAQ;AAG1B,QAAI,SAAS;AACZ,YAAM,gBAAgB,iDAAiD;AAAA,IACxE;AAAA,EACD;AACD;;;ACzIO,SAAS,sBAAsB,KAAqB;AAE1D,MAAI,CAAC,OAAO,IAAI,WAAW,GAAG;AAC7B,WAAO;AAAA,EACR;AAGA,MAAI,IAAI,WAAW,GAAG,GAAG;AACxB,WAAO,IAAI,MAAM,CAAC;AAAA,EACnB;AAGA,QAAM,YAAY,IAAI,OAAO,CAAC;AAI9B,QAAM,YAAY,UAAU,YAAY;AACxC,MAAI,cAAc,UAAU,YAAY,KAAK,WAAC,UAAM,GAAC,EAAC,KAAK,SAAS,GAAG;AAEtE,QAAI,cAAc,WAAW;AAC5B,aAAO,YAAY,IAAI,MAAM,CAAC;AAAA,IAC/B;AAAA,EACD;AAGA,SAAO;AACR;","names":[]}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "./chunk-3RUPPQRG.js";
|
|
13
13
|
import {
|
|
14
14
|
promptConfirmation
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-CE26YH2U.js";
|
|
16
16
|
import {
|
|
17
17
|
logger
|
|
18
18
|
} from "./chunk-GEHQXLEI.js";
|
|
@@ -300,4 +300,4 @@ State: ${entity.state}`;
|
|
|
300
300
|
export {
|
|
301
301
|
GitHubService
|
|
302
302
|
};
|
|
303
|
-
//# sourceMappingURL=chunk-
|
|
303
|
+
//# sourceMappingURL=chunk-M7JJCX53.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
promptConfirmation
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-CE26YH2U.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
7
|
} from "./chunk-GEHQXLEI.js";
|
|
@@ -144,4 +144,4 @@ var SettingsMigrationManager = class {
|
|
|
144
144
|
export {
|
|
145
145
|
SettingsMigrationManager
|
|
146
146
|
};
|
|
147
|
-
//# sourceMappingURL=chunk-
|
|
147
|
+
//# sourceMappingURL=chunk-OSCLCMDG.js.map
|
|
@@ -177,7 +177,7 @@ async function launchClaudeInNewTerminalWindow(_prompt, options) {
|
|
|
177
177
|
let backgroundColor;
|
|
178
178
|
if (branchName) {
|
|
179
179
|
try {
|
|
180
|
-
const { generateColorFromBranchName } = await import("./color-
|
|
180
|
+
const { generateColorFromBranchName } = await import("./color-F7RU6B6Z.js");
|
|
181
181
|
const colorData = generateColorFromBranchName(branchName);
|
|
182
182
|
backgroundColor = colorData.rgb;
|
|
183
183
|
} catch (error) {
|
|
@@ -249,4 +249,4 @@ export {
|
|
|
249
249
|
launchClaudeInNewTerminalWindow,
|
|
250
250
|
generateBranchName
|
|
251
251
|
};
|
|
252
|
-
//# sourceMappingURL=chunk-
|
|
252
|
+
//# sourceMappingURL=chunk-OYF4VIFI.js.map
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
PromptTemplateManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RIEO2WML.js";
|
|
5
5
|
import {
|
|
6
6
|
SettingsManager
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-CDZERT7Z.js";
|
|
8
8
|
import {
|
|
9
9
|
detectClaudeCli,
|
|
10
10
|
launchClaude,
|
|
11
11
|
launchClaudeInNewTerminalWindow
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OYF4VIFI.js";
|
|
13
13
|
import {
|
|
14
14
|
logger
|
|
15
15
|
} from "./chunk-GEHQXLEI.js";
|
|
@@ -136,4 +136,4 @@ var ClaudeService = class {
|
|
|
136
136
|
export {
|
|
137
137
|
ClaudeService
|
|
138
138
|
};
|
|
139
|
-
//# sourceMappingURL=chunk-
|
|
139
|
+
//# sourceMappingURL=chunk-PGPI5LR4.js.map
|
|
@@ -122,6 +122,9 @@ var PromptTemplateManager = class {
|
|
|
122
122
|
if (variables.SETTINGS_SCHEMA_CONTENT !== void 0) {
|
|
123
123
|
result = result.replace(/SETTINGS_SCHEMA_CONTENT/g, variables.SETTINGS_SCHEMA_CONTENT);
|
|
124
124
|
}
|
|
125
|
+
if (variables.VSCODE_SETTINGS_GITIGNORED !== void 0) {
|
|
126
|
+
result = result.replace(/VSCODE_SETTINGS_GITIGNORED/g, variables.VSCODE_SETTINGS_GITIGNORED);
|
|
127
|
+
}
|
|
125
128
|
return result;
|
|
126
129
|
}
|
|
127
130
|
/**
|
|
@@ -194,4 +197,4 @@ var PromptTemplateManager = class {
|
|
|
194
197
|
export {
|
|
195
198
|
PromptTemplateManager
|
|
196
199
|
};
|
|
197
|
-
//# sourceMappingURL=chunk-
|
|
200
|
+
//# sourceMappingURL=chunk-RIEO2WML.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/PromptTemplateManager.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport { accessSync } from 'fs'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport { logger } from '../utils/logger.js'\n\nexport interface TemplateVariables {\n\tISSUE_NUMBER?: string | number\n\tPR_NUMBER?: number\n\tISSUE_TITLE?: string\n\tPR_TITLE?: string\n\tWORKSPACE_PATH?: string\n\tPORT?: number\n\tONE_SHOT_MODE?: boolean\n\tSETTINGS_SCHEMA?: string\n\tSETTINGS_GLOBAL_JSON?: string\n\tSETTINGS_JSON?: string\n\tSETTINGS_LOCAL_JSON?: string\n\tSHELL_TYPE?: string\n\tSHELL_CONFIG_PATH?: string\n\tSHELL_CONFIG_CONTENT?: string\n\tREMOTES_INFO?: string\n\tMULTIPLE_REMOTES?: string\n\tSINGLE_REMOTE?: string\n\tSINGLE_REMOTE_NAME?: string\n\tSINGLE_REMOTE_URL?: string\n\tNO_REMOTES?: string\n\tREADME_CONTENT?: string\n\tSETTINGS_SCHEMA_CONTENT?: string\n\tFIRST_TIME_USER?: boolean\n\tVSCODE_SETTINGS_GITIGNORED?: string\n}\n\nexport class PromptTemplateManager {\n\tprivate templateDir: string\n\n\tconstructor(templateDir?: string) {\n\t\tif (templateDir) {\n\t\t\tthis.templateDir = templateDir\n\t\t} else {\n\t\t\t// Find templates relative to the package installation\n\t\t\t// When running from dist/, templates are copied to dist/prompts/\n\t\t\tconst currentFileUrl = import.meta.url\n\t\t\tconst currentFilePath = fileURLToPath(currentFileUrl)\n\t\t\tconst distDir = path.dirname(currentFilePath) // dist directory (may be chunked file location)\n\n\t\t\t// Walk up to find the dist directory (in case of chunked files)\n\t\t\tlet templateDir = path.join(distDir, 'prompts')\n\t\t\tlet currentDir = distDir\n\n\t\t\t// Try to find the prompts directory by walking up\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst candidatePath = path.join(currentDir, 'prompts')\n\t\t\t\ttry {\n\t\t\t\t\t// Check if this directory exists (sync check for constructor)\n\t\t\t\t\taccessSync(candidatePath)\n\t\t\t\t\ttemplateDir = candidatePath\n\t\t\t\t\tbreak\n\t\t\t\t} catch {\n\t\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.templateDir = templateDir\n\t\t\tlogger.debug('PromptTemplateManager initialized', {\n\t\t\t\tcurrentFilePath,\n\t\t\t\tdistDir,\n\t\t\t\ttemplateDir: this.templateDir\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Load a template file by name\n\t */\n\tasync loadTemplate(templateName: 'issue' | 'pr' | 'regular' | 'init'): Promise<string> {\n\t\tconst templatePath = path.join(this.templateDir, `${templateName}-prompt.txt`)\n\n\t\tlogger.debug('Loading template', {\n\t\t\ttemplateName,\n\t\t\ttemplateDir: this.templateDir,\n\t\t\ttemplatePath\n\t\t})\n\n\t\ttry {\n\t\t\treturn await readFile(templatePath, 'utf-8')\n\t\t} catch (error) {\n\t\t\tlogger.error('Failed to load template', { templateName, templatePath, error })\n\t\t\tthrow new Error(`Template not found: ${templatePath}`)\n\t\t}\n\t}\n\n\t/**\n\t * Substitute variables in a template string\n\t */\n\tsubstituteVariables(template: string, variables: TemplateVariables): string {\n\t\tlet result = template\n\n\t\t// Process conditional sections first\n\t\tresult = this.processConditionalSections(result, variables)\n\n\t\t// Replace each variable if it exists\n\t\tif (variables.ISSUE_NUMBER !== undefined) {\n\t\t\tresult = result.replace(/ISSUE_NUMBER/g, String(variables.ISSUE_NUMBER))\n\t\t}\n\n\t\tif (variables.PR_NUMBER !== undefined) {\n\t\t\tresult = result.replace(/PR_NUMBER/g, String(variables.PR_NUMBER))\n\t\t}\n\n\t\tif (variables.ISSUE_TITLE !== undefined) {\n\t\t\tresult = result.replace(/ISSUE_TITLE/g, variables.ISSUE_TITLE)\n\t\t}\n\n\t\tif (variables.PR_TITLE !== undefined) {\n\t\t\tresult = result.replace(/PR_TITLE/g, variables.PR_TITLE)\n\t\t}\n\n\t\tif (variables.WORKSPACE_PATH !== undefined) {\n\t\t\tresult = result.replace(/WORKSPACE_PATH/g, variables.WORKSPACE_PATH)\n\t\t}\n\n\t\tif (variables.PORT !== undefined) {\n\t\t\tresult = result.replace(/PORT/g, String(variables.PORT))\n\t\t}\n\n\t\tif (variables.SETTINGS_SCHEMA !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_SCHEMA/g, variables.SETTINGS_SCHEMA)\n\t\t}\n\n\t\tif (variables.SETTINGS_GLOBAL_JSON !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_GLOBAL_JSON/g, variables.SETTINGS_GLOBAL_JSON)\n\t\t}\n\n\t\tif (variables.SETTINGS_JSON !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_JSON/g, variables.SETTINGS_JSON)\n\t\t}\n\n\t\tif (variables.SETTINGS_LOCAL_JSON !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_LOCAL_JSON/g, variables.SETTINGS_LOCAL_JSON)\n\t\t}\n\n\t\tif (variables.SHELL_TYPE !== undefined) {\n\t\t\tresult = result.replace(/SHELL_TYPE/g, variables.SHELL_TYPE)\n\t\t}\n\n\t\tif (variables.SHELL_CONFIG_PATH !== undefined) {\n\t\t\tresult = result.replace(/SHELL_CONFIG_PATH/g, variables.SHELL_CONFIG_PATH)\n\t\t}\n\n\t\tif (variables.SHELL_CONFIG_CONTENT !== undefined) {\n\t\t\tresult = result.replace(/SHELL_CONFIG_CONTENT/g, variables.SHELL_CONFIG_CONTENT)\n\t\t}\n\n\t\tif (variables.REMOTES_INFO !== undefined) {\n\t\t\tresult = result.replace(/REMOTES_INFO/g, variables.REMOTES_INFO)\n\t\t}\n\n\t\tif (variables.MULTIPLE_REMOTES !== undefined) {\n\t\t\tresult = result.replace(/MULTIPLE_REMOTES/g, variables.MULTIPLE_REMOTES)\n\t\t}\n\n\t\tif (variables.SINGLE_REMOTE !== undefined) {\n\t\t\tresult = result.replace(/SINGLE_REMOTE/g, variables.SINGLE_REMOTE)\n\t\t}\n\n\t\tif (variables.SINGLE_REMOTE_NAME !== undefined) {\n\t\t\tresult = result.replace(/SINGLE_REMOTE_NAME/g, variables.SINGLE_REMOTE_NAME)\n\t\t}\n\n\t\tif (variables.SINGLE_REMOTE_URL !== undefined) {\n\t\t\tresult = result.replace(/SINGLE_REMOTE_URL/g, variables.SINGLE_REMOTE_URL)\n\t\t}\n\n\t\tif (variables.NO_REMOTES !== undefined) {\n\t\t\tresult = result.replace(/NO_REMOTES/g, variables.NO_REMOTES)\n\t\t}\n\n\t\tif (variables.README_CONTENT !== undefined) {\n\t\t\tresult = result.replace(/README_CONTENT/g, variables.README_CONTENT)\n\t\t}\n\n\t\tif (variables.SETTINGS_SCHEMA_CONTENT !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_SCHEMA_CONTENT/g, variables.SETTINGS_SCHEMA_CONTENT)\n\t\t}\n\n\t\tif (variables.VSCODE_SETTINGS_GITIGNORED !== undefined) {\n\t\t\tresult = result.replace(/VSCODE_SETTINGS_GITIGNORED/g, variables.VSCODE_SETTINGS_GITIGNORED)\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Process conditional sections in template\n\t * Format: {{#IF ONE_SHOT_MODE}}content{{/IF ONE_SHOT_MODE}}\n\t *\n\t * Note: /s flag allows . to match newlines\n\t */\n\tprivate processConditionalSections(template: string, variables: TemplateVariables): string {\n\t\tlet result = template\n\n\t\t// Process ONE_SHOT_MODE conditionals\n\t\tconst oneShotRegex = /\\{\\{#IF ONE_SHOT_MODE\\}\\}(.*?)\\{\\{\\/IF ONE_SHOT_MODE\\}\\}/gs\n\n\t\tif (variables.ONE_SHOT_MODE === true) {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(oneShotRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(oneShotRegex, '')\n\t\t}\n\n\t\t// Process SETTINGS_JSON conditionals\n\t\tconst settingsJsonRegex = /\\{\\{#IF SETTINGS_JSON\\}\\}(.*?)\\{\\{\\/IF SETTINGS_JSON\\}\\}/gs\n\n\t\tif (variables.SETTINGS_JSON !== undefined && variables.SETTINGS_JSON !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(settingsJsonRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(settingsJsonRegex, '')\n\t\t}\n\n\t\t// Process SETTINGS_GLOBAL_JSON conditionals\n\t\tconst settingsGlobalJsonRegex = /\\{\\{#IF SETTINGS_GLOBAL_JSON\\}\\}(.*?)\\{\\{\\/IF SETTINGS_GLOBAL_JSON\\}\\}/gs\n\n\t\tif (variables.SETTINGS_GLOBAL_JSON !== undefined && variables.SETTINGS_GLOBAL_JSON !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(settingsGlobalJsonRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(settingsGlobalJsonRegex, '')\n\t\t}\n\n\t\t// Process SETTINGS_LOCAL_JSON conditionals\n\t\tconst settingsLocalJsonRegex = /\\{\\{#IF SETTINGS_LOCAL_JSON\\}\\}(.*?)\\{\\{\\/IF SETTINGS_LOCAL_JSON\\}\\}/gs\n\n\t\tif (variables.SETTINGS_LOCAL_JSON !== undefined && variables.SETTINGS_LOCAL_JSON !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(settingsLocalJsonRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(settingsLocalJsonRegex, '')\n\t\t}\n\n\t\t// Process MULTIPLE_REMOTES conditionals\n\t\tconst multipleRemotesRegex = /\\{\\{#IF MULTIPLE_REMOTES\\}\\}(.*?)\\{\\{\\/IF MULTIPLE_REMOTES\\}\\}/gs\n\n\t\tif (variables.MULTIPLE_REMOTES !== undefined && variables.MULTIPLE_REMOTES !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(multipleRemotesRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(multipleRemotesRegex, '')\n\t\t}\n\n\t\t// Process SINGLE_REMOTE conditionals\n\t\tconst singleRemoteRegex = /\\{\\{#IF SINGLE_REMOTE\\}\\}(.*?)\\{\\{\\/IF SINGLE_REMOTE\\}\\}/gs\n\n\t\tif (variables.SINGLE_REMOTE !== undefined && variables.SINGLE_REMOTE !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(singleRemoteRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(singleRemoteRegex, '')\n\t\t}\n\n\t\t// Process NO_REMOTES conditionals\n\t\tconst noRemotesRegex = /\\{\\{#IF NO_REMOTES\\}\\}(.*?)\\{\\{\\/IF NO_REMOTES\\}\\}/gs\n\n\t\tif (variables.NO_REMOTES !== undefined && variables.NO_REMOTES !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(noRemotesRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(noRemotesRegex, '')\n\t\t}\n\n\t\t// Process FIRST_TIME_USER conditionals\n\t\tconst firstTimeUserRegex = /\\{\\{#IF FIRST_TIME_USER\\}\\}(.*?)\\{\\{\\/IF FIRST_TIME_USER\\}\\}/gs\n\n\t\tif (variables.FIRST_TIME_USER === true) {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(firstTimeUserRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(firstTimeUserRegex, '')\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Get a fully processed prompt for a workflow type\n\t */\n\tasync getPrompt(\n\t\ttype: 'issue' | 'pr' | 'regular' | 'init',\n\t\tvariables: TemplateVariables\n\t): Promise<string> {\n\t\tconst template = await this.loadTemplate(type)\n\t\treturn this.substituteVariables(template, variables)\n\t}\n}\n"],"mappings":"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","names":["templateDir"]}
|
|
@@ -15,7 +15,7 @@ var ClaudeBranchNameStrategy = class {
|
|
|
15
15
|
this.claudeModel = claudeModel;
|
|
16
16
|
}
|
|
17
17
|
async generate(issueNumber, title) {
|
|
18
|
-
const { generateBranchName } = await import("./claude-
|
|
18
|
+
const { generateBranchName } = await import("./claude-W52VKI6L.js");
|
|
19
19
|
return generateBranchName(title, issueNumber, this.claudeModel);
|
|
20
20
|
}
|
|
21
21
|
};
|
|
@@ -52,4 +52,4 @@ export {
|
|
|
52
52
|
ClaudeBranchNameStrategy,
|
|
53
53
|
DefaultBranchNamingService
|
|
54
54
|
};
|
|
55
|
-
//# sourceMappingURL=chunk-
|
|
55
|
+
//# sourceMappingURL=chunk-SUOXY5WJ.js.map
|