@qlucent/fishi-core 0.1.0 → 0.2.0
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/dist/index.d.ts +59 -1
- package/dist/index.js +296 -48
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -441,10 +441,17 @@ declare function getAgentFactoryTemplate(): string;
|
|
|
441
441
|
*/
|
|
442
442
|
declare function getCoordinatorFactoryTemplate(): string;
|
|
443
443
|
|
|
444
|
+
type ConflictResolution = 'skip' | 'merge' | 'replace';
|
|
445
|
+
interface FileResolutionMap {
|
|
446
|
+
categories: Record<string, ConflictResolution>;
|
|
447
|
+
files: Record<string, ConflictResolution>;
|
|
448
|
+
}
|
|
444
449
|
interface ScaffoldOptions extends InitOptions {
|
|
445
450
|
projectName: string;
|
|
446
451
|
projectType: ProjectType;
|
|
447
452
|
brownfieldAnalysis?: BrownfieldAnalysisData;
|
|
453
|
+
resolutions?: FileResolutionMap;
|
|
454
|
+
docsReadmeExists?: boolean;
|
|
448
455
|
}
|
|
449
456
|
interface ScaffoldResult {
|
|
450
457
|
agentCount: number;
|
|
@@ -455,4 +462,55 @@ interface ScaffoldResult {
|
|
|
455
462
|
}
|
|
456
463
|
declare function generateScaffold(targetDir: string, options: ScaffoldOptions): Promise<ScaffoldResult>;
|
|
457
464
|
|
|
458
|
-
|
|
465
|
+
interface FileConflict {
|
|
466
|
+
path: string;
|
|
467
|
+
size: number;
|
|
468
|
+
}
|
|
469
|
+
interface ConflictCategory {
|
|
470
|
+
name: string;
|
|
471
|
+
label: string;
|
|
472
|
+
conflicts: FileConflict[];
|
|
473
|
+
}
|
|
474
|
+
interface ConflictMap {
|
|
475
|
+
categories: ConflictCategory[];
|
|
476
|
+
hasConflicts: boolean;
|
|
477
|
+
totalConflicts: number;
|
|
478
|
+
docsReadmeExists: boolean;
|
|
479
|
+
}
|
|
480
|
+
declare function detectConflicts(targetDir: string): ConflictMap;
|
|
481
|
+
|
|
482
|
+
interface BackupManifest {
|
|
483
|
+
timestamp: string;
|
|
484
|
+
fishi_version: string;
|
|
485
|
+
files: {
|
|
486
|
+
path: string;
|
|
487
|
+
size: number;
|
|
488
|
+
}[];
|
|
489
|
+
}
|
|
490
|
+
declare function createBackup(targetDir: string, conflictingFiles: string[]): Promise<string>;
|
|
491
|
+
|
|
492
|
+
declare function mergeClaudeMd(existing: string, fishiContent: string): string;
|
|
493
|
+
interface HookEntry {
|
|
494
|
+
matcher: string;
|
|
495
|
+
command: string;
|
|
496
|
+
[key: string]: unknown;
|
|
497
|
+
}
|
|
498
|
+
interface SettingsJson {
|
|
499
|
+
hooks?: Record<string, HookEntry[]>;
|
|
500
|
+
permissions?: {
|
|
501
|
+
allow?: string[];
|
|
502
|
+
deny?: string[];
|
|
503
|
+
};
|
|
504
|
+
env?: Record<string, string>;
|
|
505
|
+
enabledPlugins?: string[];
|
|
506
|
+
[key: string]: unknown;
|
|
507
|
+
}
|
|
508
|
+
declare function mergeSettingsJson(existing: SettingsJson, fishi: SettingsJson): SettingsJson;
|
|
509
|
+
interface McpJson {
|
|
510
|
+
mcpServers: Record<string, Record<string, unknown>>;
|
|
511
|
+
[key: string]: unknown;
|
|
512
|
+
}
|
|
513
|
+
declare function mergeMcpJson(existing: McpJson, fishi: McpJson): McpJson;
|
|
514
|
+
declare function mergeGitignore(existing: string, fishiAdditions: string): string;
|
|
515
|
+
|
|
516
|
+
export { type AgentDefinition, type AgentRole, type AgentTemplate, type BackupManifest, type BrownfieldAnalysisData, type ClaudeMdOptions, type CommandTemplate, type ConflictCategory, type ConflictMap, type ConflictResolution, type CostMode, type DetectionCheck, type DetectionResult, type DynamicAgentConfig, type ExecutionConfig, type FileConflict, type FileResolutionMap, type FishiConfig, type FishiYamlOptions, type GateConfig, type GateStatus, type GitConfig, type HookTemplate, type InitOptions, type McpConfig, type McpServerConfig, type ModelRoutingConfig, type ModelTier, type ProjectConfig, type ProjectType, type ProjectYamlOptions, type ScaffoldOptions, type ScaffoldResult, type SkillTemplate, type StateConfig, type TaskStatus, type TaskboardConfig, type TemplateContext, architectAgentTemplate, backendAgentTemplate, createBackup, detectConflicts, devLeadTemplate, devopsAgentTemplate, docsAgentTemplate, frontendAgentTemplate, fullstackAgentTemplate, generateScaffold, getAdaptiveTaskGraphSkill, getAgentCompleteHook, getAgentFactoryTemplate, getAgentRegistryTemplate, getApiDesignSkill, getAutoCheckpointHook, getBoardCommand, getBrainstormingSkill, getBrownfieldAnalysisSkill, getBrownfieldDiscoverySkill, getClaudeMdTemplate, getCodeGenSkill, getCoordinatorFactoryTemplate, getDebuggingSkill, getDeploymentSkill, getDocCheckerScript, getDocumentationSkill, getFishiYamlTemplate, getGateCommand, getGateManagerScript, getGitignoreAdditions, getInitCommand, getLearningsManagerScript, getMasterOrchestratorTemplate, getMcpJsonTemplate, getMemoryManagerScript, getModelRoutingReference, getPhaseRunnerScript, getPostEditHook, getPrdCommand, getPrdSkill, getProjectYamlTemplate, getResetCommand, getResumeCommand, getSafetyCheckHook, getSessionStartHook, getSettingsJsonTemplate, getSprintCommand, getStatusCommand, getTaskboardOpsSkill, getTaskboardUpdateHook, getTestingSkill, getTodoManagerScript, getValidateScaffoldScript, getWorktreeManagerScript, getWorktreeSetupHook, marketingAgentTemplate, mergeClaudeMd, mergeGitignore, mergeMcpJson, mergeSettingsJson, opsLeadTemplate, planningAgentTemplate, planningLeadTemplate, qualityLeadTemplate, researchAgentTemplate, securityAgentTemplate, testingAgentTemplate, uiuxAgentTemplate, writingAgentTemplate };
|
package/dist/index.js
CHANGED
|
@@ -10375,12 +10375,96 @@ RECOMMENDATION: suggested next step
|
|
|
10375
10375
|
}
|
|
10376
10376
|
|
|
10377
10377
|
// src/generators/scaffold.ts
|
|
10378
|
-
import { mkdir, writeFile, readFile
|
|
10378
|
+
import { mkdir, writeFile, readFile } from "fs/promises";
|
|
10379
|
+
import { readFile as fsReadFile } from "fs/promises";
|
|
10379
10380
|
import { existsSync } from "fs";
|
|
10380
10381
|
import { join, dirname } from "path";
|
|
10382
|
+
|
|
10383
|
+
// src/generators/merge-strategies.ts
|
|
10384
|
+
var FISHI_START = "<!-- FISHI:START \u2014 Auto-generated by FISHI. Do not edit between markers. -->";
|
|
10385
|
+
var FISHI_END = "<!-- FISHI:END -->";
|
|
10386
|
+
function mergeClaudeMd(existing, fishiContent) {
|
|
10387
|
+
const section = `
|
|
10388
|
+
---
|
|
10389
|
+
### FISHI Framework
|
|
10390
|
+
${FISHI_START}
|
|
10391
|
+
${fishiContent}
|
|
10392
|
+
${FISHI_END}`;
|
|
10393
|
+
const startIdx = existing.indexOf(FISHI_START);
|
|
10394
|
+
const endIdx = existing.indexOf(FISHI_END);
|
|
10395
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
10396
|
+
let sectionStart = existing.lastIndexOf("\n---\n### FISHI Framework", startIdx);
|
|
10397
|
+
if (sectionStart === -1) sectionStart = startIdx;
|
|
10398
|
+
const sectionEnd = endIdx + FISHI_END.length;
|
|
10399
|
+
return existing.slice(0, sectionStart) + section + existing.slice(sectionEnd);
|
|
10400
|
+
}
|
|
10401
|
+
return existing + section;
|
|
10402
|
+
}
|
|
10403
|
+
function mergeSettingsJson(existing, fishi) {
|
|
10404
|
+
const result = { ...existing };
|
|
10405
|
+
const mergedHooks = { ...existing.hooks };
|
|
10406
|
+
if (fishi.hooks) {
|
|
10407
|
+
for (const [event, entries] of Object.entries(fishi.hooks)) {
|
|
10408
|
+
if (!mergedHooks[event]) {
|
|
10409
|
+
mergedHooks[event] = [...entries];
|
|
10410
|
+
} else {
|
|
10411
|
+
for (const entry of entries) {
|
|
10412
|
+
const isDuplicate = mergedHooks[event].some(
|
|
10413
|
+
(e) => e.matcher === entry.matcher && e.command === entry.command
|
|
10414
|
+
);
|
|
10415
|
+
if (!isDuplicate) {
|
|
10416
|
+
mergedHooks[event] = [...mergedHooks[event], entry];
|
|
10417
|
+
}
|
|
10418
|
+
}
|
|
10419
|
+
}
|
|
10420
|
+
}
|
|
10421
|
+
}
|
|
10422
|
+
result.hooks = mergedHooks;
|
|
10423
|
+
const existingAllow = existing.permissions?.allow || [];
|
|
10424
|
+
const fishiAllow = fishi.permissions?.allow || [];
|
|
10425
|
+
const existingDeny = existing.permissions?.deny || [];
|
|
10426
|
+
const fishiDeny = fishi.permissions?.deny || [];
|
|
10427
|
+
result.permissions = {
|
|
10428
|
+
allow: [.../* @__PURE__ */ new Set([...existingAllow, ...fishiAllow])],
|
|
10429
|
+
deny: [.../* @__PURE__ */ new Set([...existingDeny, ...fishiDeny])]
|
|
10430
|
+
};
|
|
10431
|
+
if (fishi.env) {
|
|
10432
|
+
result.env = { ...fishi.env, ...existing.env };
|
|
10433
|
+
}
|
|
10434
|
+
if (fishi.enabledPlugins) {
|
|
10435
|
+
result.enabledPlugins = [
|
|
10436
|
+
.../* @__PURE__ */ new Set([...existing.enabledPlugins || [], ...fishi.enabledPlugins])
|
|
10437
|
+
];
|
|
10438
|
+
}
|
|
10439
|
+
return result;
|
|
10440
|
+
}
|
|
10441
|
+
function mergeMcpJson(existing, fishi) {
|
|
10442
|
+
const merged = { ...existing, mcpServers: { ...existing.mcpServers } };
|
|
10443
|
+
for (const [name, config] of Object.entries(fishi.mcpServers)) {
|
|
10444
|
+
if (!(name in merged.mcpServers)) {
|
|
10445
|
+
merged.mcpServers[name] = config;
|
|
10446
|
+
}
|
|
10447
|
+
}
|
|
10448
|
+
return merged;
|
|
10449
|
+
}
|
|
10450
|
+
function mergeGitignore(existing, fishiAdditions) {
|
|
10451
|
+
if (existing.includes(".trees/")) {
|
|
10452
|
+
return existing;
|
|
10453
|
+
}
|
|
10454
|
+
return existing + fishiAdditions;
|
|
10455
|
+
}
|
|
10456
|
+
|
|
10457
|
+
// src/generators/scaffold.ts
|
|
10381
10458
|
async function generateScaffold(targetDir, options) {
|
|
10382
10459
|
let filesCreated = 0;
|
|
10383
|
-
|
|
10460
|
+
const resolutions = options.resolutions;
|
|
10461
|
+
async function write(relativePath, content, category) {
|
|
10462
|
+
if (resolutions) {
|
|
10463
|
+
const fileRes = resolutions.files[relativePath];
|
|
10464
|
+
const catRes = category ? resolutions.categories[category] : void 0;
|
|
10465
|
+
const resolution = fileRes || catRes;
|
|
10466
|
+
if (resolution === "skip") return;
|
|
10467
|
+
}
|
|
10384
10468
|
const fullPath = join(targetDir, relativePath);
|
|
10385
10469
|
await mkdir(dirname(fullPath), { recursive: true });
|
|
10386
10470
|
await writeFile(fullPath, content, "utf-8");
|
|
@@ -10431,39 +10515,39 @@ async function generateScaffold(targetDir, options) {
|
|
|
10431
10515
|
for (const dir of dirs) {
|
|
10432
10516
|
await mkdir(join(targetDir, dir), { recursive: true });
|
|
10433
10517
|
}
|
|
10434
|
-
await write(".claude/agents/master-orchestrator.md", getMasterOrchestratorTemplate());
|
|
10435
|
-
await write(".claude/agents/coordinators/planning-lead.md", planningLeadTemplate(ctx));
|
|
10436
|
-
await write(".claude/agents/coordinators/dev-lead.md", devLeadTemplate(ctx));
|
|
10437
|
-
await write(".claude/agents/coordinators/quality-lead.md", qualityLeadTemplate(ctx));
|
|
10438
|
-
await write(".claude/agents/coordinators/ops-lead.md", opsLeadTemplate(ctx));
|
|
10439
|
-
await write(".claude/agents/research-agent.md", researchAgentTemplate(ctx));
|
|
10440
|
-
await write(".claude/agents/planning-agent.md", planningAgentTemplate(ctx));
|
|
10441
|
-
await write(".claude/agents/architect-agent.md", architectAgentTemplate(ctx));
|
|
10442
|
-
await write(".claude/agents/backend-agent.md", backendAgentTemplate(ctx));
|
|
10443
|
-
await write(".claude/agents/frontend-agent.md", frontendAgentTemplate(ctx));
|
|
10444
|
-
await write(".claude/agents/uiux-agent.md", uiuxAgentTemplate(ctx));
|
|
10445
|
-
await write(".claude/agents/fullstack-agent.md", fullstackAgentTemplate(ctx));
|
|
10446
|
-
await write(".claude/agents/devops-agent.md", devopsAgentTemplate(ctx));
|
|
10447
|
-
await write(".claude/agents/testing-agent.md", testingAgentTemplate(ctx));
|
|
10448
|
-
await write(".claude/agents/security-agent.md", securityAgentTemplate(ctx));
|
|
10449
|
-
await write(".claude/agents/docs-agent.md", docsAgentTemplate(ctx));
|
|
10450
|
-
await write(".claude/agents/writing-agent.md", writingAgentTemplate(ctx));
|
|
10451
|
-
await write(".claude/agents/marketing-agent.md", marketingAgentTemplate(ctx));
|
|
10518
|
+
await write(".claude/agents/master-orchestrator.md", getMasterOrchestratorTemplate(), "agents");
|
|
10519
|
+
await write(".claude/agents/coordinators/planning-lead.md", planningLeadTemplate(ctx), "agents");
|
|
10520
|
+
await write(".claude/agents/coordinators/dev-lead.md", devLeadTemplate(ctx), "agents");
|
|
10521
|
+
await write(".claude/agents/coordinators/quality-lead.md", qualityLeadTemplate(ctx), "agents");
|
|
10522
|
+
await write(".claude/agents/coordinators/ops-lead.md", opsLeadTemplate(ctx), "agents");
|
|
10523
|
+
await write(".claude/agents/research-agent.md", researchAgentTemplate(ctx), "agents");
|
|
10524
|
+
await write(".claude/agents/planning-agent.md", planningAgentTemplate(ctx), "agents");
|
|
10525
|
+
await write(".claude/agents/architect-agent.md", architectAgentTemplate(ctx), "agents");
|
|
10526
|
+
await write(".claude/agents/backend-agent.md", backendAgentTemplate(ctx), "agents");
|
|
10527
|
+
await write(".claude/agents/frontend-agent.md", frontendAgentTemplate(ctx), "agents");
|
|
10528
|
+
await write(".claude/agents/uiux-agent.md", uiuxAgentTemplate(ctx), "agents");
|
|
10529
|
+
await write(".claude/agents/fullstack-agent.md", fullstackAgentTemplate(ctx), "agents");
|
|
10530
|
+
await write(".claude/agents/devops-agent.md", devopsAgentTemplate(ctx), "agents");
|
|
10531
|
+
await write(".claude/agents/testing-agent.md", testingAgentTemplate(ctx), "agents");
|
|
10532
|
+
await write(".claude/agents/security-agent.md", securityAgentTemplate(ctx), "agents");
|
|
10533
|
+
await write(".claude/agents/docs-agent.md", docsAgentTemplate(ctx), "agents");
|
|
10534
|
+
await write(".claude/agents/writing-agent.md", writingAgentTemplate(ctx), "agents");
|
|
10535
|
+
await write(".claude/agents/marketing-agent.md", marketingAgentTemplate(ctx), "agents");
|
|
10452
10536
|
const agentCount = 18;
|
|
10453
10537
|
await write(".fishi/agent-factory/agent-template.md", getAgentFactoryTemplate());
|
|
10454
10538
|
await write(".fishi/agent-factory/coordinator-template.md", getCoordinatorFactoryTemplate());
|
|
10455
|
-
await write(".claude/skills/brainstorming/SKILL.md", getBrainstormingSkill());
|
|
10456
|
-
await write(".claude/skills/brownfield-analysis/SKILL.md", getBrownfieldAnalysisSkill());
|
|
10457
|
-
await write(".claude/skills/taskboard-ops/SKILL.md", getTaskboardOpsSkill());
|
|
10458
|
-
await write(".claude/skills/code-gen/SKILL.md", getCodeGenSkill());
|
|
10459
|
-
await write(".claude/skills/debugging/SKILL.md", getDebuggingSkill());
|
|
10460
|
-
await write(".claude/skills/api-design/SKILL.md", getApiDesignSkill());
|
|
10461
|
-
await write(".claude/skills/testing/SKILL.md", getTestingSkill());
|
|
10462
|
-
await write(".claude/skills/deployment/SKILL.md", getDeploymentSkill());
|
|
10463
|
-
await write(".claude/skills/prd/SKILL.md", getPrdSkill());
|
|
10464
|
-
await write(".claude/skills/brownfield-discovery/SKILL.md", getBrownfieldDiscoverySkill());
|
|
10465
|
-
await write(".claude/skills/adaptive-taskgraph/SKILL.md", getAdaptiveTaskGraphSkill());
|
|
10466
|
-
await write(".claude/skills/documentation/SKILL.md", getDocumentationSkill());
|
|
10539
|
+
await write(".claude/skills/brainstorming/SKILL.md", getBrainstormingSkill(), "skills");
|
|
10540
|
+
await write(".claude/skills/brownfield-analysis/SKILL.md", getBrownfieldAnalysisSkill(), "skills");
|
|
10541
|
+
await write(".claude/skills/taskboard-ops/SKILL.md", getTaskboardOpsSkill(), "skills");
|
|
10542
|
+
await write(".claude/skills/code-gen/SKILL.md", getCodeGenSkill(), "skills");
|
|
10543
|
+
await write(".claude/skills/debugging/SKILL.md", getDebuggingSkill(), "skills");
|
|
10544
|
+
await write(".claude/skills/api-design/SKILL.md", getApiDesignSkill(), "skills");
|
|
10545
|
+
await write(".claude/skills/testing/SKILL.md", getTestingSkill(), "skills");
|
|
10546
|
+
await write(".claude/skills/deployment/SKILL.md", getDeploymentSkill(), "skills");
|
|
10547
|
+
await write(".claude/skills/prd/SKILL.md", getPrdSkill(), "skills");
|
|
10548
|
+
await write(".claude/skills/brownfield-discovery/SKILL.md", getBrownfieldDiscoverySkill(), "skills");
|
|
10549
|
+
await write(".claude/skills/adaptive-taskgraph/SKILL.md", getAdaptiveTaskGraphSkill(), "skills");
|
|
10550
|
+
await write(".claude/skills/documentation/SKILL.md", getDocumentationSkill(), "skills");
|
|
10467
10551
|
const skillCount = 12;
|
|
10468
10552
|
await write(".fishi/scripts/session-start.mjs", getSessionStartHook());
|
|
10469
10553
|
await write(".fishi/scripts/auto-checkpoint.mjs", getAutoCheckpointHook());
|
|
@@ -10496,14 +10580,14 @@ async function generateScaffold(targetDir, options) {
|
|
|
10496
10580
|
await write(".fishi/todos/agents/frontend-agent.md", todoTemplate("frontend-agent"));
|
|
10497
10581
|
await write(".fishi/todos/agents/testing-agent.md", todoTemplate("testing-agent"));
|
|
10498
10582
|
await write(".fishi/todos/agents/devops-agent.md", todoTemplate("devops-agent"));
|
|
10499
|
-
await write(".claude/commands/fishi-init.md", getInitCommand());
|
|
10500
|
-
await write(".claude/commands/fishi-status.md", getStatusCommand());
|
|
10501
|
-
await write(".claude/commands/fishi-resume.md", getResumeCommand());
|
|
10502
|
-
await write(".claude/commands/fishi-gate.md", getGateCommand());
|
|
10503
|
-
await write(".claude/commands/fishi-board.md", getBoardCommand());
|
|
10504
|
-
await write(".claude/commands/fishi-sprint.md", getSprintCommand());
|
|
10505
|
-
await write(".claude/commands/fishi-reset.md", getResetCommand());
|
|
10506
|
-
await write(".claude/commands/fishi-prd.md", getPrdCommand());
|
|
10583
|
+
await write(".claude/commands/fishi-init.md", getInitCommand(), "commands");
|
|
10584
|
+
await write(".claude/commands/fishi-status.md", getStatusCommand(), "commands");
|
|
10585
|
+
await write(".claude/commands/fishi-resume.md", getResumeCommand(), "commands");
|
|
10586
|
+
await write(".claude/commands/fishi-gate.md", getGateCommand(), "commands");
|
|
10587
|
+
await write(".claude/commands/fishi-board.md", getBoardCommand(), "commands");
|
|
10588
|
+
await write(".claude/commands/fishi-sprint.md", getSprintCommand(), "commands");
|
|
10589
|
+
await write(".claude/commands/fishi-reset.md", getResetCommand(), "commands");
|
|
10590
|
+
await write(".claude/commands/fishi-prd.md", getPrdCommand(), "commands");
|
|
10507
10591
|
const commandCount = 8;
|
|
10508
10592
|
await write(".fishi/fishi.yaml", getFishiYamlTemplate({
|
|
10509
10593
|
projectName: options.projectName,
|
|
@@ -10513,16 +10597,37 @@ async function generateScaffold(targetDir, options) {
|
|
|
10513
10597
|
language: options.language,
|
|
10514
10598
|
framework: options.framework
|
|
10515
10599
|
}));
|
|
10516
|
-
|
|
10517
|
-
|
|
10600
|
+
const settingsContent = getSettingsJsonTemplate();
|
|
10601
|
+
if (resolutions?.categories["settings-json"] === "merge") {
|
|
10602
|
+
const existingRaw = await fsReadFile(join(targetDir, ".claude", "settings.json"), "utf-8");
|
|
10603
|
+
const merged = mergeSettingsJson(JSON.parse(existingRaw), JSON.parse(settingsContent));
|
|
10604
|
+
await write(".claude/settings.json", JSON.stringify(merged, null, 2) + "\n", "settings-json");
|
|
10605
|
+
} else {
|
|
10606
|
+
await write(".claude/settings.json", settingsContent, "settings-json");
|
|
10607
|
+
}
|
|
10608
|
+
const claudeMdContent = getClaudeMdTemplate({
|
|
10518
10609
|
projectName: options.projectName,
|
|
10519
10610
|
projectDescription: ctx.projectDescription,
|
|
10520
10611
|
projectType: options.projectType,
|
|
10521
10612
|
language: options.language,
|
|
10522
10613
|
framework: options.framework,
|
|
10523
10614
|
brownfieldAnalysis: options.brownfieldAnalysis
|
|
10524
|
-
})
|
|
10525
|
-
|
|
10615
|
+
});
|
|
10616
|
+
if (resolutions?.categories["claude-md"] === "merge") {
|
|
10617
|
+
const existingMd = await fsReadFile(join(targetDir, ".claude", "CLAUDE.md"), "utf-8");
|
|
10618
|
+
const merged = mergeClaudeMd(existingMd, claudeMdContent);
|
|
10619
|
+
await write(".claude/CLAUDE.md", merged, "claude-md");
|
|
10620
|
+
} else {
|
|
10621
|
+
await write(".claude/CLAUDE.md", claudeMdContent, "claude-md");
|
|
10622
|
+
}
|
|
10623
|
+
const mcpContent = getMcpJsonTemplate();
|
|
10624
|
+
if (resolutions?.categories["mcp-json"] === "merge") {
|
|
10625
|
+
const existingRaw = await fsReadFile(join(targetDir, ".mcp.json"), "utf-8");
|
|
10626
|
+
const merged = mergeMcpJson(JSON.parse(existingRaw), JSON.parse(mcpContent));
|
|
10627
|
+
await write(".mcp.json", JSON.stringify(merged, null, 2) + "\n", "mcp-json");
|
|
10628
|
+
} else {
|
|
10629
|
+
await write(".mcp.json", mcpContent, "mcp-json");
|
|
10630
|
+
}
|
|
10526
10631
|
await write(".fishi/state/project.yaml", getProjectYamlTemplate({
|
|
10527
10632
|
projectName: options.projectName,
|
|
10528
10633
|
projectDescription: ctx.projectDescription,
|
|
@@ -10537,18 +10642,24 @@ async function generateScaffold(targetDir, options) {
|
|
|
10537
10642
|
await write(".fishi/memory/decisions.md", "# Architecture Decision Log\n\n_No decisions recorded yet._\n");
|
|
10538
10643
|
await write(".fishi/memory/agents/master-orchestrator.md", getMasterOrchestratorMemory(ctx));
|
|
10539
10644
|
await write(".fishi/learnings/shared.md", "# Learnings \u2014 shared\n\n## Mistakes & Fixes\n\n## Best Practices\n");
|
|
10540
|
-
|
|
10645
|
+
if (!options.docsReadmeExists) {
|
|
10646
|
+
await write("docs/README.md", `# ${options.projectName}
|
|
10541
10647
|
|
|
10542
10648
|
Documentation will be generated as the project progresses.
|
|
10543
10649
|
`);
|
|
10650
|
+
}
|
|
10544
10651
|
await write(".fishi/taskboard/board.md", getEmptyBoard());
|
|
10545
10652
|
await write(".fishi/taskboard/backlog.md", getEmptyBacklog());
|
|
10546
10653
|
const gitignorePath = join(targetDir, ".gitignore");
|
|
10547
10654
|
const additions = getGitignoreAdditions();
|
|
10548
10655
|
if (existsSync(gitignorePath)) {
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
await
|
|
10656
|
+
if (resolutions?.categories["gitignore"] === "skip") {
|
|
10657
|
+
} else {
|
|
10658
|
+
const existing = await readFile(gitignorePath, "utf-8");
|
|
10659
|
+
const merged = mergeGitignore(existing, "\n" + additions);
|
|
10660
|
+
if (merged !== existing) {
|
|
10661
|
+
await writeFile(join(targetDir, ".gitignore"), merged, "utf-8");
|
|
10662
|
+
}
|
|
10552
10663
|
}
|
|
10553
10664
|
} else {
|
|
10554
10665
|
await writeFile(gitignorePath, additions, "utf-8");
|
|
@@ -10648,9 +10759,142 @@ Prioritized list of all upcoming work items.
|
|
|
10648
10759
|
_Backlog is empty. Use /fishi-init to start the planning process._
|
|
10649
10760
|
`;
|
|
10650
10761
|
}
|
|
10762
|
+
|
|
10763
|
+
// src/generators/conflict-detector.ts
|
|
10764
|
+
import { existsSync as existsSync2, statSync } from "fs";
|
|
10765
|
+
import { join as join2 } from "path";
|
|
10766
|
+
var FISHI_FILES = {
|
|
10767
|
+
"claude-md": {
|
|
10768
|
+
label: ".claude/CLAUDE.md",
|
|
10769
|
+
files: [".claude/CLAUDE.md"]
|
|
10770
|
+
},
|
|
10771
|
+
"settings-json": {
|
|
10772
|
+
label: ".claude/settings.json",
|
|
10773
|
+
files: [".claude/settings.json"]
|
|
10774
|
+
},
|
|
10775
|
+
"mcp-json": {
|
|
10776
|
+
label: ".mcp.json",
|
|
10777
|
+
files: [".mcp.json"]
|
|
10778
|
+
},
|
|
10779
|
+
"agents": {
|
|
10780
|
+
label: "Agents",
|
|
10781
|
+
files: [
|
|
10782
|
+
".claude/agents/master-orchestrator.md",
|
|
10783
|
+
".claude/agents/coordinators/planning-lead.md",
|
|
10784
|
+
".claude/agents/coordinators/dev-lead.md",
|
|
10785
|
+
".claude/agents/coordinators/quality-lead.md",
|
|
10786
|
+
".claude/agents/coordinators/ops-lead.md",
|
|
10787
|
+
".claude/agents/research-agent.md",
|
|
10788
|
+
".claude/agents/planning-agent.md",
|
|
10789
|
+
".claude/agents/architect-agent.md",
|
|
10790
|
+
".claude/agents/backend-agent.md",
|
|
10791
|
+
".claude/agents/frontend-agent.md",
|
|
10792
|
+
".claude/agents/uiux-agent.md",
|
|
10793
|
+
".claude/agents/fullstack-agent.md",
|
|
10794
|
+
".claude/agents/devops-agent.md",
|
|
10795
|
+
".claude/agents/testing-agent.md",
|
|
10796
|
+
".claude/agents/security-agent.md",
|
|
10797
|
+
".claude/agents/docs-agent.md",
|
|
10798
|
+
".claude/agents/writing-agent.md",
|
|
10799
|
+
".claude/agents/marketing-agent.md"
|
|
10800
|
+
]
|
|
10801
|
+
},
|
|
10802
|
+
"skills": {
|
|
10803
|
+
label: "Skills",
|
|
10804
|
+
files: [
|
|
10805
|
+
".claude/skills/brainstorming/SKILL.md",
|
|
10806
|
+
".claude/skills/brownfield-analysis/SKILL.md",
|
|
10807
|
+
".claude/skills/taskboard-ops/SKILL.md",
|
|
10808
|
+
".claude/skills/code-gen/SKILL.md",
|
|
10809
|
+
".claude/skills/debugging/SKILL.md",
|
|
10810
|
+
".claude/skills/api-design/SKILL.md",
|
|
10811
|
+
".claude/skills/testing/SKILL.md",
|
|
10812
|
+
".claude/skills/deployment/SKILL.md",
|
|
10813
|
+
".claude/skills/prd/SKILL.md",
|
|
10814
|
+
".claude/skills/brownfield-discovery/SKILL.md",
|
|
10815
|
+
".claude/skills/adaptive-taskgraph/SKILL.md",
|
|
10816
|
+
".claude/skills/documentation/SKILL.md"
|
|
10817
|
+
]
|
|
10818
|
+
},
|
|
10819
|
+
"commands": {
|
|
10820
|
+
label: "Commands",
|
|
10821
|
+
files: [
|
|
10822
|
+
".claude/commands/fishi-init.md",
|
|
10823
|
+
".claude/commands/fishi-status.md",
|
|
10824
|
+
".claude/commands/fishi-resume.md",
|
|
10825
|
+
".claude/commands/fishi-gate.md",
|
|
10826
|
+
".claude/commands/fishi-board.md",
|
|
10827
|
+
".claude/commands/fishi-sprint.md",
|
|
10828
|
+
".claude/commands/fishi-reset.md",
|
|
10829
|
+
".claude/commands/fishi-prd.md"
|
|
10830
|
+
]
|
|
10831
|
+
},
|
|
10832
|
+
"gitignore": {
|
|
10833
|
+
label: ".gitignore",
|
|
10834
|
+
files: [".gitignore"]
|
|
10835
|
+
}
|
|
10836
|
+
};
|
|
10837
|
+
function detectConflicts(targetDir) {
|
|
10838
|
+
const categories = [];
|
|
10839
|
+
let totalConflicts = 0;
|
|
10840
|
+
for (const [name, def] of Object.entries(FISHI_FILES)) {
|
|
10841
|
+
const conflicts = [];
|
|
10842
|
+
for (const relPath of def.files) {
|
|
10843
|
+
const fullPath = join2(targetDir, relPath);
|
|
10844
|
+
if (existsSync2(fullPath)) {
|
|
10845
|
+
const stat = statSync(fullPath);
|
|
10846
|
+
conflicts.push({ path: relPath, size: stat.size });
|
|
10847
|
+
}
|
|
10848
|
+
}
|
|
10849
|
+
categories.push({ name, label: def.label, conflicts });
|
|
10850
|
+
totalConflicts += conflicts.length;
|
|
10851
|
+
}
|
|
10852
|
+
const docsReadmeExists = existsSync2(join2(targetDir, "docs", "README.md"));
|
|
10853
|
+
return {
|
|
10854
|
+
categories,
|
|
10855
|
+
hasConflicts: totalConflicts > 0,
|
|
10856
|
+
totalConflicts,
|
|
10857
|
+
docsReadmeExists
|
|
10858
|
+
};
|
|
10859
|
+
}
|
|
10860
|
+
|
|
10861
|
+
// src/generators/backup-manager.ts
|
|
10862
|
+
import { mkdir as mkdir2, copyFile, writeFile as writeFile2, rename } from "fs/promises";
|
|
10863
|
+
import { existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
10864
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
10865
|
+
async function createBackup(targetDir, conflictingFiles) {
|
|
10866
|
+
const now = /* @__PURE__ */ new Date();
|
|
10867
|
+
const timestamp = now.toISOString().replace(/:/g, "-").replace(/\.\d+Z$/, "");
|
|
10868
|
+
const backupDir = join3(targetDir, ".fishi", "backup", timestamp);
|
|
10869
|
+
await mkdir2(backupDir, { recursive: true });
|
|
10870
|
+
const manifestFiles = [];
|
|
10871
|
+
for (const relPath of conflictingFiles) {
|
|
10872
|
+
const srcPath = join3(targetDir, relPath);
|
|
10873
|
+
const destPath = join3(backupDir, relPath);
|
|
10874
|
+
if (existsSync3(srcPath)) {
|
|
10875
|
+
await mkdir2(dirname2(destPath), { recursive: true });
|
|
10876
|
+
await copyFile(srcPath, destPath);
|
|
10877
|
+
const stat = statSync2(srcPath);
|
|
10878
|
+
manifestFiles.push({ path: relPath, size: stat.size });
|
|
10879
|
+
}
|
|
10880
|
+
}
|
|
10881
|
+
const fishiVersion = "0.2.0";
|
|
10882
|
+
const manifest = {
|
|
10883
|
+
timestamp: now.toISOString(),
|
|
10884
|
+
fishi_version: fishiVersion,
|
|
10885
|
+
files: manifestFiles
|
|
10886
|
+
};
|
|
10887
|
+
const manifestPath = join3(backupDir, "manifest.json");
|
|
10888
|
+
const tmpPath = manifestPath + ".tmp";
|
|
10889
|
+
await writeFile2(tmpPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
|
|
10890
|
+
await rename(tmpPath, manifestPath);
|
|
10891
|
+
return backupDir;
|
|
10892
|
+
}
|
|
10651
10893
|
export {
|
|
10652
10894
|
architectAgentTemplate,
|
|
10653
10895
|
backendAgentTemplate,
|
|
10896
|
+
createBackup,
|
|
10897
|
+
detectConflicts,
|
|
10654
10898
|
devLeadTemplate,
|
|
10655
10899
|
devopsAgentTemplate,
|
|
10656
10900
|
docsAgentTemplate,
|
|
@@ -10704,6 +10948,10 @@ export {
|
|
|
10704
10948
|
getWorktreeManagerScript,
|
|
10705
10949
|
getWorktreeSetupHook,
|
|
10706
10950
|
marketingAgentTemplate,
|
|
10951
|
+
mergeClaudeMd,
|
|
10952
|
+
mergeGitignore,
|
|
10953
|
+
mergeMcpJson,
|
|
10954
|
+
mergeSettingsJson,
|
|
10707
10955
|
opsLeadTemplate,
|
|
10708
10956
|
planningAgentTemplate,
|
|
10709
10957
|
planningLeadTemplate,
|