@devobsessed/code-captain 0.4.0 → 0.4.1

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 (2) hide show
  1. package/bin/install.js +62 -11
  2. package/package.json +1 -1
package/bin/install.js CHANGED
@@ -444,16 +444,44 @@ class CodeCaptainInstaller {
444
444
  return recommendations;
445
445
  }
446
446
 
447
- // Detect .sln files in the current directory
447
+ // Detect .sln / .slnx files in the current directory
448
448
  async detectSlnFiles() {
449
449
  try {
450
450
  const entries = await fs.readdir(".");
451
- return entries.filter((entry) => entry.endsWith(".sln"));
451
+ return entries.filter(
452
+ (entry) => entry.endsWith(".sln") || entry.endsWith(".slnx")
453
+ );
452
454
  } catch {
453
455
  return [];
454
456
  }
455
457
  }
456
458
 
459
+ // Add code-captain.csproj to an XML-format .slnx file
460
+ async addProjectToSlnx(slnxPath, csprojFileName) {
461
+ const content = await fs.readFile(slnxPath, "utf8");
462
+ const idMarker = `Path="${csprojFileName}"`;
463
+
464
+ if (content.includes(idMarker)) return "up-to-date";
465
+
466
+ // Detect line ending style
467
+ const nl = content.includes("\r\n") ? "\r\n" : "\n";
468
+
469
+ const updated = content.replace(
470
+ /<\/Solution>/,
471
+ ` <Project Path="${csprojFileName}" />${nl}</Solution>`
472
+ );
473
+
474
+ if (updated === content) {
475
+ throw new Error(
476
+ `Could not find </Solution> in ${slnxPath}. Please add code-captain.csproj to your solution manually in Visual Studio.`
477
+ );
478
+ }
479
+
480
+ await fs.copy(slnxPath, `${slnxPath}.backup`);
481
+ await fs.writeFile(slnxPath, updated);
482
+ return "updated";
483
+ }
484
+
457
485
  // Generate a random GUID (uppercase)
458
486
  generateGuid() {
459
487
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
@@ -472,15 +500,33 @@ class CodeCaptainInstaller {
472
500
 
473
501
  if (content.includes(idMarker)) return "up-to-date";
474
502
 
503
+ // Detect line ending style used by the .sln file
504
+ const nl = content.includes("\r\n") ? "\r\n" : "\n";
505
+
475
506
  const projectGuid = this.generateGuid();
476
507
  // SDK-style C# project type GUID
477
508
  const typeGuid = "{9A19103F-16F7-4668-BE54-9A1E7A4F7556}";
478
509
  const projectBlock =
479
- `Project("${typeGuid}") = "${projectName}", "${csprojFileName}", "{${projectGuid}}"\n` +
480
- `EndProject\n`;
510
+ `Project("${typeGuid}") = "${projectName}", "${csprojFileName}", "{${projectGuid}}"${nl}` +
511
+ `EndProject${nl}`;
512
+
513
+ // Find the last EndProject and insert after it (more reliable than searching for Global)
514
+ const lastEndProjectIdx = content.lastIndexOf(`EndProject${nl}`);
515
+ let updated;
516
+ if (lastEndProjectIdx !== -1) {
517
+ const insertAt = lastEndProjectIdx + `EndProject${nl}`.length;
518
+ updated = content.slice(0, insertAt) + projectBlock + content.slice(insertAt);
519
+ } else {
520
+ // Fallback: insert before Global section
521
+ updated = content.replace(/^(Global\r?\n)/m, `${projectBlock}$1`);
522
+ }
481
523
 
482
- // Insert before the Global section
483
- let updated = content.replace(/^(Global\r?\n)/m, `${projectBlock}$1`);
524
+ if (updated === content) {
525
+ // Regex fallback also failed — .sln format not recognized
526
+ throw new Error(
527
+ `Could not find insertion point in ${slnPath}. Please add code-captain.csproj to your solution manually in Visual Studio.`
528
+ );
529
+ }
484
530
 
485
531
  // Parse solution configs and add ActiveCfg entries (but not Build.0, so it's excluded from builds)
486
532
  const configSectionMatch = content.match(
@@ -496,11 +542,11 @@ class CodeCaptainInstaller {
496
542
  if (configNames.length > 0) {
497
543
  const activeCfgEntries = configNames
498
544
  .map((c) => `\t\t{${projectGuid}}.${c}.ActiveCfg = ${c}`)
499
- .join("\n");
545
+ .join(nl);
500
546
 
501
547
  updated = updated.replace(
502
548
  /(GlobalSection\(ProjectConfigurationPlatforms\)[^\n]*\n)([\s\S]*?)(EndGlobalSection)/,
503
- `$1$2${activeCfgEntries}\n\t$3`
549
+ `$1$2${activeCfgEntries}${nl}\t$3`
504
550
  );
505
551
  }
506
552
  }
@@ -591,11 +637,16 @@ class CodeCaptainInstaller {
591
637
  }
592
638
  }
593
639
 
594
- // Register in .sln
640
+ // Register in .sln / .slnx
595
641
  const slnFiles = await this.detectSlnFiles();
596
642
  let slnAction = "no-sln";
597
643
  if (slnFiles.length > 0) {
598
- slnAction = await this.addProjectToSln(slnFiles[0], targetPath);
644
+ const slnFile = slnFiles[0];
645
+ if (slnFile.endsWith(".slnx")) {
646
+ slnAction = await this.addProjectToSlnx(slnFile, targetPath);
647
+ } else {
648
+ slnAction = await this.addProjectToSln(slnFile, targetPath);
649
+ }
599
650
  }
600
651
 
601
652
  // Migrate: remove Code Captain section from Directory.Build.props if present
@@ -1466,7 +1517,7 @@ class CodeCaptainInstaller {
1466
1517
  const slnMessages = {
1467
1518
  updated: "registered in solution (.sln backup saved as .backup)",
1468
1519
  "up-to-date": "already registered in solution",
1469
- "no-sln": "no .sln file found — add code-captain.csproj to your solution manually",
1520
+ "no-sln": "no .sln/.slnx file found — add code-captain.csproj to your solution manually in Visual Studio",
1470
1521
  };
1471
1522
  let message = csprojMessages[csprojAction] || csprojAction;
1472
1523
  if (slnMessages[slnAction]) message += ` — ${slnMessages[slnAction]}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devobsessed/code-captain",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Unified AI Development Agent System with intelligent change detection",
5
5
  "type": "module",
6
6
  "bin": {