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.
- package/.workflow/bridges/base-bridge.js +124 -4
- package/.workflow/bridges/claude-bridge.js +3 -5
- package/.workflow/bridges/codex-bridge.js +2 -2
- package/.workflow/bridges/cursor-bridge.js +2 -2
- package/.workflow/bridges/gemini-bridge.js +3 -3
- package/.workflow/bridges/kimi-bridge.js +4 -4
- package/.workflow/bridges/opencode-bridge.js +2 -2
- package/lib/installer.js +76 -36
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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(
|
|
419
|
-
return fs.readFileSync(
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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 =
|
|
85
|
-
if (
|
|
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 =
|
|
92
|
-
if (
|
|
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
|
-
//
|
|
62
|
-
const templatePath =
|
|
63
|
-
if (
|
|
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 =
|
|
92
|
-
if (!
|
|
93
|
-
templatePath =
|
|
91
|
+
let templatePath = this.getBestTemplatePath('kimi-agents-md.hbs');
|
|
92
|
+
if (!templatePath) {
|
|
93
|
+
templatePath = this.getBestTemplatePath('agents-md.hbs');
|
|
94
94
|
}
|
|
95
|
-
if (
|
|
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 =
|
|
88
|
-
if (
|
|
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
|
-
//
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
//
|
|
368
|
-
|
|
369
|
-
|
|
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
|
-
|
|
376
|
-
|
|
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
|
-
|
|
400
|
-
|
|
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
|
|