wogiflow 1.1.2 → 1.1.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.
@@ -400,13 +400,112 @@ class BaseBridge {
400
400
 
401
401
  // ==================== Template Utility Methods ====================
402
402
 
403
+ /**
404
+ * Get the package root directory (where wogiflow is installed)
405
+ * @returns {string|null} Package root path or null if not found
406
+ */
407
+ getPackageRoot() {
408
+ // Try to find the package by looking up from scripts directory
409
+ const possibleRoots = [
410
+ path.resolve(__dirname, '..', '..'), // From .workflow/bridges/ -> package root
411
+ path.resolve(__dirname, '..', '..', '..', 'node_modules', 'wogiflow'), // From project
412
+ ];
413
+
414
+ for (const root of possibleRoots) {
415
+ const pkgPath = path.join(root, 'package.json');
416
+ if (fs.existsSync(pkgPath)) {
417
+ try {
418
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
419
+ if (pkg.name === 'wogiflow') {
420
+ return root;
421
+ }
422
+ } catch {
423
+ // Continue to next option
424
+ }
425
+ }
426
+ }
427
+ return null;
428
+ }
429
+
430
+ /**
431
+ * Check if a template file is outdated/stub
432
+ * @param {string} templatePath - Path to template file
433
+ * @returns {boolean} True if template appears to be outdated
434
+ */
435
+ isTemplateOutdated(templatePath) {
436
+ if (!fs.existsSync(templatePath)) {
437
+ return true;
438
+ }
439
+
440
+ try {
441
+ const content = fs.readFileSync(templatePath, 'utf-8');
442
+
443
+ // Check for stub/placeholder markers
444
+ const stubMarkers = [
445
+ 'Full implementation pending',
446
+ 'stub template',
447
+ 'not yet implemented',
448
+ 'TODO: implement',
449
+ 'placeholder template'
450
+ ];
451
+
452
+ for (const marker of stubMarkers) {
453
+ if (content.toLowerCase().includes(marker.toLowerCase())) {
454
+ return true;
455
+ }
456
+ }
457
+
458
+ // Check if template is too short (likely incomplete)
459
+ if (content.length < 500) {
460
+ return true;
461
+ }
462
+
463
+ return false;
464
+ } catch {
465
+ return true;
466
+ }
467
+ }
468
+
469
+ /**
470
+ * Get template path, preferring package template if project template is outdated
471
+ * @param {string} templateName - Template filename (e.g., 'gemini-md.hbs')
472
+ * @returns {string|null} Path to best available template
473
+ */
474
+ getBestTemplatePath(templateName) {
475
+ const projectTemplate = path.join(this.projectDir, this.workflowDir, 'templates', templateName);
476
+
477
+ // If project template exists and is not outdated, use it
478
+ if (!this.isTemplateOutdated(projectTemplate)) {
479
+ return projectTemplate;
480
+ }
481
+
482
+ // Try to use package template instead
483
+ const packageRoot = this.getPackageRoot();
484
+ if (packageRoot) {
485
+ const packageTemplate = path.join(packageRoot, '.workflow', 'templates', templateName);
486
+ if (fs.existsSync(packageTemplate)) {
487
+ this.log(`Using package template for ${templateName} (project template outdated)`);
488
+ return packageTemplate;
489
+ }
490
+ }
491
+
492
+ // Fall back to project template even if outdated
493
+ if (fs.existsSync(projectTemplate)) {
494
+ return projectTemplate;
495
+ }
496
+
497
+ return null;
498
+ }
499
+
403
500
  /**
404
501
  * Load a partial template from .workflow/templates/partials/
502
+ * Checks both project and package directories
405
503
  * @param {string} partialName - Name of the partial (without .hbs extension)
406
504
  * @returns {string} Partial content or empty string if not found
407
505
  */
408
506
  loadPartial(partialName) {
409
- const partialPath = path.join(
507
+ // Try project partials first
508
+ const projectPartialPath = path.join(
410
509
  this.projectDir,
411
510
  this.workflowDir,
412
511
  'templates',
@@ -415,12 +514,33 @@ class BaseBridge {
415
514
  );
416
515
 
417
516
  try {
418
- if (fs.existsSync(partialPath)) {
419
- return fs.readFileSync(partialPath, 'utf-8');
517
+ if (fs.existsSync(projectPartialPath)) {
518
+ return fs.readFileSync(projectPartialPath, 'utf-8');
420
519
  }
421
520
  } catch (err) {
422
- this.log(`Warning: Could not load partial ${partialName}: ${err.message}`);
521
+ this.log(`Warning: Could not load project partial ${partialName}: ${err.message}`);
522
+ }
523
+
524
+ // Try package partials as fallback
525
+ const packageRoot = this.getPackageRoot();
526
+ if (packageRoot) {
527
+ const packagePartialPath = path.join(
528
+ packageRoot,
529
+ '.workflow',
530
+ 'templates',
531
+ 'partials',
532
+ `${partialName}.hbs`
533
+ );
534
+
535
+ try {
536
+ if (fs.existsSync(packagePartialPath)) {
537
+ return fs.readFileSync(packagePartialPath, 'utf-8');
538
+ }
539
+ } catch (err) {
540
+ this.log(`Warning: Could not load package partial ${partialName}: ${err.message}`);
541
+ }
423
542
  }
543
+
424
544
  return '';
425
545
  }
426
546
 
@@ -45,11 +45,9 @@ class ClaudeBridge extends BaseBridge {
45
45
  * @returns {string} Generated CLAUDE.md content
46
46
  */
47
47
  generateRulesContent(config) {
48
- const projectName = config.projectName || 'Project';
49
-
50
- // Check if custom template exists
51
- const templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'claude-md.hbs');
52
- if (fs.existsSync(templatePath)) {
48
+ // Use getBestTemplatePath to find the best template (prefers package over outdated project)
49
+ const templatePath = this.getBestTemplatePath('claude-md.hbs');
50
+ if (templatePath) {
53
51
  return this.generateFromTemplate(templatePath, config);
54
52
  }
55
53
 
@@ -81,8 +81,8 @@ class CodexBridge extends BaseBridge {
81
81
  this.registerPartials();
82
82
 
83
83
  try {
84
- const templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'agents-md.hbs');
85
- if (fs.existsSync(templatePath)) {
84
+ const templatePath = this.getBestTemplatePath('agents-md.hbs');
85
+ if (templatePath) {
86
86
  const templateSource = fs.readFileSync(templatePath, 'utf-8');
87
87
  const template = Handlebars.compile(templateSource);
88
88
  return template(context);
@@ -88,8 +88,8 @@ class CursorBridge extends BaseBridge {
88
88
  // Register partials before compiling
89
89
  this.registerPartials();
90
90
 
91
- const templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'cursor-rules.mdc.hbs');
92
- if (fs.existsSync(templatePath)) {
91
+ const templatePath = this.getBestTemplatePath('cursor-rules.mdc.hbs');
92
+ if (templatePath) {
93
93
  try {
94
94
  const templateSource = fs.readFileSync(templatePath, 'utf-8');
95
95
  const template = Handlebars.compile(templateSource);
@@ -58,9 +58,9 @@ class GeminiBridge extends BaseBridge {
58
58
  * @returns {string} Generated GEMINI.md content
59
59
  */
60
60
  generateRulesContent(config) {
61
- // Check if custom template exists
62
- const templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'gemini-md.hbs');
63
- if (fs.existsSync(templatePath)) {
61
+ // Use getBestTemplatePath to find the best template (prefers package over outdated project)
62
+ const templatePath = this.getBestTemplatePath('gemini-md.hbs');
63
+ if (templatePath) {
64
64
  return this.generateFromTemplate(templatePath, config);
65
65
  }
66
66
 
@@ -88,11 +88,11 @@ class KimiBridge extends BaseBridge {
88
88
 
89
89
  try {
90
90
  // Try kimi-specific template first, fall back to generic agents-md.hbs
91
- let templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'kimi-agents-md.hbs');
92
- if (!fs.existsSync(templatePath)) {
93
- templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'agents-md.hbs');
91
+ let templatePath = this.getBestTemplatePath('kimi-agents-md.hbs');
92
+ if (!templatePath) {
93
+ templatePath = this.getBestTemplatePath('agents-md.hbs');
94
94
  }
95
- if (fs.existsSync(templatePath)) {
95
+ if (templatePath) {
96
96
  let templateSource;
97
97
  try {
98
98
  templateSource = fs.readFileSync(templatePath, 'utf-8');
@@ -84,8 +84,8 @@ class OpenCodeBridge extends BaseBridge {
84
84
  this.registerPartials();
85
85
 
86
86
  try {
87
- const templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'opencode-agents-md.hbs');
88
- if (fs.existsSync(templatePath)) {
87
+ const templatePath = this.getBestTemplatePath('opencode-agents-md.hbs');
88
+ if (templatePath) {
89
89
  const templateSource = fs.readFileSync(templatePath, 'utf-8');
90
90
  const template = Handlebars.compile(templateSource);
91
91
  return template(context);
package/lib/installer.js CHANGED
@@ -322,6 +322,31 @@ function createWorkflowStructure(projectRoot, config) {
322
322
  console.log(' Created .workflow/ directory structure');
323
323
  }
324
324
 
325
+ /**
326
+ * CLI-specific resource mappings
327
+ * Maps CLI keys to their package source directories and output file names
328
+ */
329
+ const CLI_RESOURCES = {
330
+ claude: {
331
+ packageDir: '.claude',
332
+ rulesFile: 'CLAUDE.md',
333
+ templateName: 'claude-md.hbs',
334
+ subdirs: ['commands', 'docs', 'rules', 'skills']
335
+ },
336
+ gemini: {
337
+ packageDir: '.claude', // Share Claude's resources as base, bridge customizes output
338
+ rulesFile: 'GEMINI.md',
339
+ templateName: 'gemini-md.hbs',
340
+ subdirs: ['commands', 'docs', 'rules', 'skills']
341
+ },
342
+ opencode: {
343
+ packageDir: '.claude',
344
+ rulesFile: '.opencode/agents.md',
345
+ templateName: 'opencode-agents-md.hbs',
346
+ subdirs: ['docs', 'skills']
347
+ }
348
+ };
349
+
325
350
  /**
326
351
  * Create CLI-specific configuration
327
352
  * @param {string} projectRoot - Project root directory
@@ -338,45 +363,48 @@ function createCLIConfig(projectRoot, cliKey, config) {
338
363
  const cliDir = path.join(projectRoot, cli.dir);
339
364
  fs.mkdirSync(cliDir, { recursive: true });
340
365
 
341
- // Create CLI-specific config file and copy resources
342
- if (cliKey === 'claude') {
343
- // Copy commands from package
344
- const packageCommands = path.join(PACKAGE_ROOT, '.claude', 'commands');
345
- const projectCommands = path.join(cliDir, 'commands');
346
- if (fs.existsSync(packageCommands)) {
347
- copyDir(packageCommands, projectCommands);
348
- console.log(' Copied .claude/commands/ (slash commands)');
349
- }
366
+ // Get CLI-specific resource configuration
367
+ const resources = CLI_RESOURCES[cliKey];
368
+ if (!resources) {
369
+ console.log(` ${cli.name} will be configured via bridge sync`);
370
+ console.log(` Configured ${cli.name}`);
371
+ return;
372
+ }
350
373
 
351
- // Copy docs from package
352
- const packageDocs = path.join(PACKAGE_ROOT, '.claude', 'docs');
353
- const projectDocs = path.join(cliDir, 'docs');
354
- if (fs.existsSync(packageDocs)) {
355
- copyDir(packageDocs, projectDocs);
356
- console.log(' Copied .claude/docs/ (documentation)');
374
+ // Copy common subdirectories (commands, docs, rules, skills)
375
+ const packageCliDir = path.join(PACKAGE_ROOT, resources.packageDir);
376
+ for (const subdir of resources.subdirs) {
377
+ const packageSubdir = path.join(packageCliDir, subdir);
378
+ const projectSubdir = path.join(cliDir, subdir);
379
+ if (fs.existsSync(packageSubdir)) {
380
+ copyDir(packageSubdir, projectSubdir);
381
+ console.log(` Copied ${cli.dir}/${subdir}/`);
357
382
  }
383
+ }
358
384
 
359
- // Copy rules from package
360
- const packageRules = path.join(PACKAGE_ROOT, '.claude', 'rules');
361
- const projectRules = path.join(cliDir, 'rules');
362
- if (fs.existsSync(packageRules)) {
363
- copyDir(packageRules, projectRules);
364
- console.log(' Copied .claude/rules/ (coding rules)');
385
+ // Generate the rules/instructions file (CLAUDE.md, GEMINI.md, etc.)
386
+ // First try to use the bridge for proper template rendering
387
+ try {
388
+ const bridgesPath = path.join(projectRoot, '.workflow', 'bridges');
389
+ if (fs.existsSync(bridgesPath)) {
390
+ const bridges = require(bridgesPath);
391
+ const bridge = bridges.getBridge({ projectDir: projectRoot, cliType: cliKey === 'claude' ? 'claude-code' : cliKey === 'gemini' ? 'gemini-cli' : cliKey, verbose: false });
392
+ if (bridge) {
393
+ bridge.generateRulesFile();
394
+ console.log(` Created ${resources.rulesFile} (via bridge)`);
395
+ console.log(` Configured ${cli.name}`);
396
+ return;
397
+ }
365
398
  }
366
-
367
- // Copy skills from package (base skills only)
368
- const packageSkills = path.join(PACKAGE_ROOT, '.claude', 'skills');
369
- const projectSkills = path.join(cliDir, 'skills');
370
- if (fs.existsSync(packageSkills)) {
371
- copyDir(packageSkills, projectSkills);
372
- console.log(' Copied .claude/skills/ (base skills)');
399
+ } catch (err) {
400
+ // Bridge not available yet - fall through to simple generation
401
+ if (process.env.DEBUG) {
402
+ console.log(` Bridge not available: ${err.message}`);
373
403
  }
404
+ }
374
405
 
375
- // Generate CLAUDE.md from template
376
- const claudeMdTemplate = path.join(PACKAGE_ROOT, '.workflow', 'templates', 'claude-md.hbs');
377
- if (fs.existsSync(claudeMdTemplate)) {
378
- // For now, create a simple CLAUDE.md - the bridge will regenerate with full template
379
- const claudeMd = `# Project Instructions
406
+ // Fallback: Create a simple rules file - the bridge will regenerate with full template
407
+ const simpleContent = `# Project Instructions
380
408
 
381
409
  You are an AI development assistant using the WogiFlow methodology v1.0.
382
410
 
@@ -394,13 +422,25 @@ cat .workflow/state/ready.json # Check tasks
394
422
  - \`/wogi-status\` - Project overview
395
423
  - \`/wogi-health\` - Check workflow health
396
424
 
425
+ Run \`flow bridge sync\` to regenerate this file with full template.
426
+
397
427
  Generated by Wogi Flow v${config.version}
398
428
  `;
399
- fs.writeFileSync(path.join(projectRoot, 'CLAUDE.md'), claudeMd);
400
- console.log(' Created CLAUDE.md');
401
- }
429
+
430
+ // Determine output path (handle nested paths like .opencode/agents.md)
431
+ const rulesFilePath = resources.rulesFile.includes('/')
432
+ ? path.join(projectRoot, resources.rulesFile)
433
+ : path.join(projectRoot, resources.rulesFile);
434
+
435
+ // Ensure parent directory exists for nested paths
436
+ const rulesFileDir = path.dirname(rulesFilePath);
437
+ if (!fs.existsSync(rulesFileDir)) {
438
+ fs.mkdirSync(rulesFileDir, { recursive: true });
402
439
  }
403
440
 
441
+ fs.writeFileSync(rulesFilePath, simpleContent);
442
+ console.log(` Created ${resources.rulesFile}`);
443
+
404
444
  console.log(` Configured ${cli.name}`);
405
445
  }
406
446
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {