@relipa/ai-flow-kit 0.1.8 → 0.1.9
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 +21 -3
- package/bin/aiflow.js +3 -2
- package/custom/rules/ml-conventions.md +0 -1
- package/custom/rules/project-conventions.md +0 -1
- package/custom/skills/generate-spec/SKILL.md +7 -0
- package/custom/skills/read-study-requirement/SKILL.md +56 -9
- package/custom/skills/review-plan/SKILL.md +4 -26
- package/custom/templates/shared/coding-workflow.md +3 -1
- package/custom/templates/shared/create-spec-workflow.md +23 -7
- package/custom/templates/shared/gate-workflow.md +3 -4
- package/custom/templates/shared/ml-gate-workflow.md +1 -2
- package/custom/templates/tools/codex.md +75 -0
- package/docs/common/AIFLOW.md +3 -2
- package/docs/common/CHANGELOG.md +42 -0
- package/docs/common/INDEX.md +2 -1
- package/docs/common/QUICK_START.md +1 -0
- package/docs/common/ai-integration.md +69 -2
- package/docs/common/cli-reference.md +1 -1
- package/package.json +2 -2
- package/scripts/doctor.js +83 -0
- package/scripts/guide.js +1 -1
- package/scripts/init.js +331 -35
- package/scripts/prompt.js +3 -6
- package/scripts/remove.js +251 -224
- package/scripts/update.js +5 -1
- package/scripts/use.js +21 -2
- package/custom/rules/java/review-checklist.md +0 -36
- package/custom/rules/ml-review-checklist.md +0 -14
- package/custom/rules/review-checklist.md +0 -9
package/scripts/init.js
CHANGED
|
@@ -58,9 +58,58 @@ const AI_TOOL_FILES = {
|
|
|
58
58
|
'cursor': '.cursorrules',
|
|
59
59
|
'gemini': 'GEMINI.md',
|
|
60
60
|
'copilot': '.github/copilot-instructions.md',
|
|
61
|
+
'codex': 'AGENTS.md',
|
|
61
62
|
'generic': '.aiflow/docs/AI_INSTRUCTIONS.md',
|
|
62
63
|
};
|
|
63
64
|
|
|
65
|
+
// Instruction-file layout per tool.
|
|
66
|
+
// 'inline' — the full workflow set is embedded in the tool file (default).
|
|
67
|
+
// 'pointer' — the tool file stays small and links to .aiflow/instructions/ instead.
|
|
68
|
+
//
|
|
69
|
+
// Codex is 'pointer' because it caps the combined AGENTS.md chain at
|
|
70
|
+
// project_doc_max_bytes (32 KiB default) and silently drops whatever exceeds it.
|
|
71
|
+
// The assembled workflow set is ~115 KB, so inlining would truncate it mid-gate.
|
|
72
|
+
const AI_TOOL_LAYOUT = {
|
|
73
|
+
'codex': 'pointer',
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/** Return 'inline' or 'pointer' for a tool key. */
|
|
77
|
+
function resolveToolLayout(tool) {
|
|
78
|
+
return AI_TOOL_LAYOUT[tool] || 'inline';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Shared workflow templates copied to .aiflow/instructions/ for pointer-layout tools.
|
|
82
|
+
// Keys are the destination basenames; gate workflow resolves per framework.
|
|
83
|
+
const WORKFLOW_INSTRUCTION_FILES = [
|
|
84
|
+
{ dest: 'create-spec-workflow.md', src: 'create-spec-workflow.md' },
|
|
85
|
+
{ dest: 'create-testcase-workflow.md', src: 'create-testcase-workflow.md' },
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Write the full gate/spec/testcase workflows to .aiflow/instructions/ so
|
|
90
|
+
* pointer-layout tools (Codex) can read them on demand instead of having them
|
|
91
|
+
* inlined into an instruction file that would be truncated.
|
|
92
|
+
* Returns the list of project-relative paths written.
|
|
93
|
+
*/
|
|
94
|
+
async function writeWorkflowInstructions(projectDir, framework) {
|
|
95
|
+
const destDir = path.join(projectDir, '.aiflow', 'instructions');
|
|
96
|
+
await fs.ensureDir(destDir);
|
|
97
|
+
|
|
98
|
+
const files = [
|
|
99
|
+
{ dest: 'gate-workflow.md', src: resolveWorkflowFile(framework) },
|
|
100
|
+
...WORKFLOW_INSTRUCTION_FILES,
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
const written = [];
|
|
104
|
+
for (const f of files) {
|
|
105
|
+
const src = path.join(PKG_DIR, 'custom', 'templates', 'shared', f.src);
|
|
106
|
+
if (!(await fs.pathExists(src))) continue;
|
|
107
|
+
await fs.copy(src, path.join(destDir, f.dest), { overwrite: true });
|
|
108
|
+
written.push(`.aiflow/instructions/${f.dest}`);
|
|
109
|
+
}
|
|
110
|
+
return written;
|
|
111
|
+
}
|
|
112
|
+
|
|
64
113
|
/**
|
|
65
114
|
* Copy only docs/common/ to .aiflow/docs in the project.
|
|
66
115
|
* Internal aiflow docs (changelog, architecture…) are never copied out.
|
|
@@ -366,10 +415,212 @@ async function setupClaudeCommands(projectDir) {
|
|
|
366
415
|
}
|
|
367
416
|
}
|
|
368
417
|
|
|
418
|
+
// ── Codex (OpenAI) support ───────────────────────────────────────
|
|
419
|
+
// Codex reads AGENTS.md + .codex/ from the workspace root. The same files serve
|
|
420
|
+
// all three local surfaces — the Codex IDE extension (VS Code), Codex mode in
|
|
421
|
+
// the ChatGPT desktop app, and the `codex` CLI — because they share CODEX_HOME
|
|
422
|
+
// config, skills and MCP setup.
|
|
423
|
+
|
|
424
|
+
const CODEX_MANAGED_HEADER = '# ai-flow-kit managed — regenerate with `ak sync-skills`';
|
|
425
|
+
|
|
426
|
+
// Codex truncates the combined AGENTS.md chain at project_doc_max_bytes.
|
|
427
|
+
// The 32 KiB default is enough for one pointer block, but a multi-framework
|
|
428
|
+
// project appends one block per framework, so raise it.
|
|
429
|
+
const CODEX_PROJECT_DOC_MAX_BYTES = 131072;
|
|
430
|
+
|
|
431
|
+
/** Serialize a JS string as a TOML basic string. */
|
|
432
|
+
function tomlString(value) {
|
|
433
|
+
return JSON.stringify(String(value));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Render one [mcp_servers.<id>] table from an .mcp.json server entry.
|
|
438
|
+
* Codex uses the same command/args/env shape as .mcp.json, just in TOML.
|
|
439
|
+
*/
|
|
440
|
+
function renderCodexMcpServer(id, cfg) {
|
|
441
|
+
const lines = [`[mcp_servers.${id}]`];
|
|
442
|
+
if (cfg.command) lines.push(`command = ${tomlString(cfg.command)}`);
|
|
443
|
+
if (Array.isArray(cfg.args) && cfg.args.length) {
|
|
444
|
+
lines.push(`args = [${cfg.args.map(tomlString).join(', ')}]`);
|
|
445
|
+
}
|
|
446
|
+
const env = cfg.env && Object.keys(cfg.env).length ? cfg.env : null;
|
|
447
|
+
if (env) {
|
|
448
|
+
const pairs = Object.entries(env).map(([k, v]) => `${k} = ${tomlString(v)}`);
|
|
449
|
+
lines.push(`env = { ${pairs.join(', ')} }`);
|
|
450
|
+
}
|
|
451
|
+
return lines.join('\n');
|
|
452
|
+
}
|
|
369
453
|
|
|
370
|
-
|
|
454
|
+
/**
|
|
455
|
+
* Write .codex/config.toml — project-scoped Codex config mirroring .mcp.json.
|
|
456
|
+
*
|
|
457
|
+
* The file is fully kit-managed: it is only written when absent or when it still
|
|
458
|
+
* carries our header. A hand-edited config is left alone and the generated
|
|
459
|
+
* version is saved to .aiflow/reference/ for the developer to merge.
|
|
460
|
+
*/
|
|
461
|
+
async function setupCodexConfig(projectDir) {
|
|
462
|
+
const configPath = path.join(projectDir, '.codex', 'config.toml');
|
|
463
|
+
|
|
464
|
+
const mcpPath = path.join(projectDir, '.mcp.json');
|
|
465
|
+
let servers = {};
|
|
466
|
+
if (await fs.pathExists(mcpPath)) {
|
|
467
|
+
const mcp = await fs.readJson(mcpPath).catch(() => ({}));
|
|
468
|
+
servers = mcp.mcpServers || {};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const sections = [
|
|
472
|
+
CODEX_MANAGED_HEADER,
|
|
473
|
+
'# Project-scoped Codex config. Applies to the Codex IDE extension,',
|
|
474
|
+
'# Codex mode in the ChatGPT desktop app, and the `codex` CLI.',
|
|
475
|
+
'',
|
|
476
|
+
'# AGENTS.md is assembled per framework and can exceed the 32 KiB default.',
|
|
477
|
+
`project_doc_max_bytes = ${CODEX_PROJECT_DOC_MAX_BYTES}`,
|
|
478
|
+
];
|
|
479
|
+
|
|
480
|
+
const serverIds = Object.keys(servers);
|
|
481
|
+
for (const id of serverIds) {
|
|
482
|
+
sections.push('', renderCodexMcpServer(id, servers[id]));
|
|
483
|
+
}
|
|
484
|
+
const content = sections.join('\n') + '\n';
|
|
485
|
+
|
|
486
|
+
if (await fs.pathExists(configPath)) {
|
|
487
|
+
const existing = await fs.readFile(configPath, 'utf-8');
|
|
488
|
+
if (!existing.includes(CODEX_MANAGED_HEADER)) {
|
|
489
|
+
const refPath = path.join(projectDir, '.aiflow', 'reference', 'codex-config.toml');
|
|
490
|
+
await fs.ensureDir(path.dirname(refPath));
|
|
491
|
+
await fs.writeFile(refPath, content);
|
|
492
|
+
console.log(chalk.yellow(' ⚠ .codex/config.toml exists and is hand-managed — left untouched.'));
|
|
493
|
+
console.log(chalk.gray(' Generated version saved to .aiflow/reference/codex-config.toml — merge manually.'));
|
|
494
|
+
return { written: false, servers: serverIds };
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
await fs.ensureDir(path.dirname(configPath));
|
|
499
|
+
await fs.writeFile(configPath, content);
|
|
500
|
+
const mcpNote = serverIds.length ? ` (MCP: ${serverIds.join(', ')})` : '';
|
|
501
|
+
console.log(chalk.green(`✓ Codex config written → .codex/config.toml${mcpNote}`));
|
|
502
|
+
return { written: true, servers: serverIds };
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Mirror the kit's skills into .codex/skills/ and add the four entry-point
|
|
507
|
+
* skills that wrap the shared workflows.
|
|
508
|
+
*
|
|
509
|
+
* Codex discovers project skills as any directory under .codex/skills containing
|
|
510
|
+
* a SKILL.md with name/description frontmatter — the same layout .claude/skills
|
|
511
|
+
* already uses, so the canonical copy is reused verbatim.
|
|
512
|
+
*/
|
|
513
|
+
async function setupCodexSkills(projectDir) {
|
|
514
|
+
const claudeSkills = path.join(projectDir, '.claude', 'skills');
|
|
515
|
+
const codexSkills = path.join(projectDir, '.codex', 'skills');
|
|
516
|
+
|
|
517
|
+
if (!(await fs.pathExists(claudeSkills))) {
|
|
518
|
+
console.log(chalk.yellow(' ! .claude/skills not found — Codex skills not installed.'));
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
await fs.emptyDir(codexSkills);
|
|
523
|
+
await fs.copy(claudeSkills, codexSkills, { overwrite: true });
|
|
524
|
+
const count = (await fs.readdir(codexSkills)).length;
|
|
525
|
+
console.log(chalk.green(`✓ Mirrored ${count} skills → .codex/skills/`));
|
|
526
|
+
|
|
527
|
+
// Entry-point skills. Small workflows are inlined; the two large ones point at
|
|
528
|
+
// .aiflow/instructions/ so skill metadata scanning stays cheap.
|
|
529
|
+
const entryPoints = [
|
|
530
|
+
{
|
|
531
|
+
name: 'ak-coding',
|
|
532
|
+
description: 'Start or resume the DEV 5-Gate coding workflow for the ticket loaded by `ak use`. Trigger when the developer says start / Gate 1 / analyze ticket, or when a ticket was loaded while this session was already open. Do not trigger for BA spec or QA testcase work.',
|
|
533
|
+
src: 'coding-workflow.md',
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
name: 'ak-ask',
|
|
537
|
+
description: 'Answer a question about ai-flow-kit itself — install, roles, folder structure, CLI commands, memory draft/submit, troubleshooting. Trigger on an AKQ:/[AKQ]: prefix or a kit-usage question, even with no ticket loaded. Do not trigger for the developer\'s actual ticket work.',
|
|
538
|
+
src: 'ak-ask-workflow.md',
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
name: 'ak-create-spec',
|
|
542
|
+
description: 'BA 4-Gate Spec Creation Workflow — turn a raw requirement (Jira/Backlog/file) into a complete UC Spec: Gate 1 analysis & Q&A, Gate 2 Q&A loop until Confirmed, Gate 3 HTML/CSS prototype, Gate 4 full UC Spec. Trigger for BA spec work, not for coding or testcases.',
|
|
543
|
+
pointer: '.aiflow/instructions/create-spec-workflow.md',
|
|
544
|
+
subSkills: '.codex/skills/ba-skills/',
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
name: 'ak-create-testcase',
|
|
548
|
+
description: 'QA 4-Gate TestCase Creation Workflow — turn a requirement or spec (SRS/Backlog/Jira/Spec file) into an execution-ready test case set: Gate 1 requirement & risk analysis, Gate 2 scenarios, Gate 3 detailed cases, Gate 4 review & optimization. Trigger for QA testcase work, not for coding or BA specs.',
|
|
549
|
+
pointer: '.aiflow/instructions/create-testcase-workflow.md',
|
|
550
|
+
subSkills: '.codex/skills/test-skills/',
|
|
551
|
+
},
|
|
552
|
+
];
|
|
553
|
+
|
|
554
|
+
for (const ep of entryPoints) {
|
|
555
|
+
let body;
|
|
556
|
+
if (ep.pointer) {
|
|
557
|
+
body = [
|
|
558
|
+
`Read \`${ep.pointer}\` **in full**, then follow it exactly, gate by gate.`,
|
|
559
|
+
'',
|
|
560
|
+
'That file is the authoritative workflow — do not work from a summary of it and',
|
|
561
|
+
'do not skip, shorten, or merge gates. Sub-skills it references live in',
|
|
562
|
+
`\`${ep.subSkills}\`.`,
|
|
563
|
+
'',
|
|
564
|
+
'Before starting, read `.aiflow/context/current.json` for ticket context if it exists.',
|
|
565
|
+
].join('\n');
|
|
566
|
+
} else {
|
|
567
|
+
const src = path.join(PKG_DIR, 'custom', 'templates', 'shared', ep.src);
|
|
568
|
+
if (!(await fs.pathExists(src))) continue;
|
|
569
|
+
body = await fs.readFile(src, 'utf-8');
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const skillDir = path.join(codexSkills, ep.name);
|
|
573
|
+
await fs.ensureDir(skillDir);
|
|
574
|
+
const content = `---\nname: ${ep.name}\ndescription: ${ep.description}\n---\n\n${body}`;
|
|
575
|
+
await fs.writeFile(path.join(skillDir, 'SKILL.md'), content);
|
|
576
|
+
}
|
|
577
|
+
console.log(chalk.green(`✓ Installed ${entryPoints.length} Codex entry-point skills (${entryPoints.map(e => e.name).join(', ')})`));
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Full Codex setup: skill mirror + project config.
|
|
582
|
+
* Safe to call unconditionally — no-ops when 'codex' is not a selected AI tool.
|
|
583
|
+
*/
|
|
584
|
+
async function setupCodex(projectDir, selectedTools) {
|
|
585
|
+
if (!selectedTools || !selectedTools.includes('codex')) return;
|
|
586
|
+
await setupCodexSkills(projectDir);
|
|
587
|
+
await setupCodexConfig(projectDir);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Build the markdown skill registry table.
|
|
592
|
+
* Skills are always read from .claude/skills (the canonical copy). `skillsRoot`
|
|
593
|
+
* only rewrites the paths written into the table, so Codex can be pointed at its
|
|
594
|
+
* own .codex/skills mirror.
|
|
595
|
+
*/
|
|
596
|
+
/**
|
|
597
|
+
* Compact skill list for pointer-layout tools.
|
|
598
|
+
*
|
|
599
|
+
* Codex indexes .codex/skills/ itself — every SKILL.md's name and description are
|
|
600
|
+
* already in its skill index — so restating the full registry table in AGENTS.md
|
|
601
|
+
* would burn ~11 KB of the doc budget on every turn for nothing. Names only, so a
|
|
602
|
+
* workflow's "INVOKE: <skill>" step still resolves to a known path.
|
|
603
|
+
*/
|
|
604
|
+
async function generateSkillNameList(projectDir, skillsRoot = '.codex/skills') {
|
|
371
605
|
const skillsDir = path.join(projectDir, '.claude', 'skills');
|
|
372
|
-
|
|
606
|
+
if (!(await fs.pathExists(skillsDir))) return '';
|
|
607
|
+
|
|
608
|
+
const names = [];
|
|
609
|
+
for (const folder of await fs.readdir(skillsDir)) {
|
|
610
|
+
if (await fs.pathExists(path.join(skillsDir, folder, 'SKILL.md'))) names.push(folder);
|
|
611
|
+
}
|
|
612
|
+
if (!names.length) return '';
|
|
613
|
+
|
|
614
|
+
let out = `## Available skills\n\n`;
|
|
615
|
+
out += `Codex indexes these automatically from \`${skillsRoot}/\`. When a workflow step says `;
|
|
616
|
+
out += `**INVOKE:** \`<name>\`, read \`${skillsRoot}/<name>/SKILL.md\` in full and follow it:\n\n`;
|
|
617
|
+
out += names.sort().map(n => `\`${n}\``).join(' · ') + '\n';
|
|
618
|
+
return out;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
async function generateSkillRegistry(projectDir, skillsRoot = '.claude/skills') {
|
|
622
|
+
const skillsDir = path.join(projectDir, '.claude', 'skills');
|
|
623
|
+
|
|
373
624
|
if (!(await fs.pathExists(skillsDir))) return '';
|
|
374
625
|
|
|
375
626
|
let registry = `## AI Skill Registry (Superpowers)\n\n`;
|
|
@@ -393,8 +644,8 @@ async function generateSkillRegistry(projectDir) {
|
|
|
393
644
|
description = lines[0] ? lines[0].trim() : 'No description available';
|
|
394
645
|
}
|
|
395
646
|
|
|
396
|
-
// Path relative to project root
|
|
397
|
-
const relPath =
|
|
647
|
+
// Path relative to project root, re-rooted for the target tool
|
|
648
|
+
const relPath = `${skillsRoot}/${folder}/SKILL.md`;
|
|
398
649
|
registry += `| **${folder}** | ${description} | \`${relPath}\` |\n`;
|
|
399
650
|
}
|
|
400
651
|
}
|
|
@@ -432,11 +683,51 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
|
|
|
432
683
|
const workflowContent = [gateWorkflowContent, createSpecContent, createTestcaseContent].filter(Boolean).join('\n\n---\n\n');
|
|
433
684
|
|
|
434
685
|
const separator = `\n\n---\n\n`;
|
|
435
|
-
const skillRegistry
|
|
686
|
+
const skillRegistry = await generateSkillRegistry(projectDir);
|
|
687
|
+
const codexSkillList = await generateSkillNameList(projectDir, '.codex/skills');
|
|
436
688
|
|
|
437
689
|
const markerStart = '<!-- aiflow-kit-start -->';
|
|
438
690
|
const markerEnd = '<!-- aiflow-kit-end -->';
|
|
439
691
|
|
|
692
|
+
// Pointer-layout tools need the workflows on disk instead of inlined.
|
|
693
|
+
const needsPointer = selectedTools.some(t => resolveToolLayout(t) === 'pointer');
|
|
694
|
+
const instructionPaths = needsPointer
|
|
695
|
+
? await writeWorkflowInstructions(projectDir, framework)
|
|
696
|
+
: [];
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Assemble the marker block for one tool.
|
|
700
|
+
* 'inline' tools get the whole workflow set embedded; 'pointer' tools get a
|
|
701
|
+
* short "installed workflow files" list and read the workflows from disk.
|
|
702
|
+
*/
|
|
703
|
+
async function buildToolBlock(tool, blockMarkerStart, blockMarkerEnd) {
|
|
704
|
+
const pointer = resolveToolLayout(tool) === 'pointer';
|
|
705
|
+
let body = '';
|
|
706
|
+
|
|
707
|
+
const toolTemplatePath = path.join(PKG_DIR, 'custom', 'templates', 'tools', `${tool}.md`);
|
|
708
|
+
if (await fs.pathExists(toolTemplatePath)) {
|
|
709
|
+
body += await fs.readFile(toolTemplatePath, 'utf-8') + '\n\n';
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (frameworkContent) body += frameworkContent + '\n\n';
|
|
713
|
+
|
|
714
|
+
const registry = pointer ? codexSkillList : skillRegistry;
|
|
715
|
+
if (registry) body += registry + '\n\n';
|
|
716
|
+
|
|
717
|
+
if (pointer) {
|
|
718
|
+
if (instructionPaths.length) {
|
|
719
|
+
body += `## Installed workflow files\n\n`;
|
|
720
|
+
body += `Generated for framework \`${framework}\` by ai-flow-kit v${PKG_VERSION}. `;
|
|
721
|
+
body += `Read the relevant file in full before starting a gate:\n\n`;
|
|
722
|
+
body += instructionPaths.map(p => `- \`${p}\``).join('\n') + '\n\n';
|
|
723
|
+
}
|
|
724
|
+
} else if (workflowContent) {
|
|
725
|
+
body += workflowContent + '\n\n';
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
return `${blockMarkerStart}\n${body.trim()}\n${blockMarkerEnd}`;
|
|
729
|
+
}
|
|
730
|
+
|
|
440
731
|
const written = [];
|
|
441
732
|
const skipped = [];
|
|
442
733
|
|
|
@@ -472,15 +763,7 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
|
|
|
472
763
|
await fs.ensureDir(path.dirname(targetPath));
|
|
473
764
|
|
|
474
765
|
// Build content for this tool
|
|
475
|
-
|
|
476
|
-
const toolTemplatePath = path.join(PKG_DIR, 'custom', 'templates', 'tools', `${tool}.md`);
|
|
477
|
-
if (await fs.pathExists(toolTemplatePath)) {
|
|
478
|
-
finalContent += await fs.readFile(toolTemplatePath, 'utf-8') + '\n\n';
|
|
479
|
-
}
|
|
480
|
-
finalContent += frameworkContent + '\n\n';
|
|
481
|
-
if (skillRegistry) finalContent += skillRegistry + '\n\n';
|
|
482
|
-
if (workflowContent) finalContent += workflowContent + '\n\n';
|
|
483
|
-
finalContent = `${markerStart}\n${finalContent.trim()}\n${markerEnd}`;
|
|
766
|
+
const finalContent = await buildToolBlock(tool, markerStart, markerEnd);
|
|
484
767
|
|
|
485
768
|
// Backup existing file before overwriting
|
|
486
769
|
if (await fs.pathExists(targetPath)) {
|
|
@@ -513,27 +796,13 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
|
|
|
513
796
|
|
|
514
797
|
await fs.ensureDir(path.dirname(targetPath));
|
|
515
798
|
|
|
516
|
-
|
|
799
|
+
// Multi-framework projects get one self-contained, per-framework marker block
|
|
800
|
+
// per tool file so re-running init/update can find-and-replace each framework's
|
|
801
|
+
// block in place instead of blindly appending it again on every run.
|
|
802
|
+
const blockMarkerStart = multi ? `<!-- aiflow-kit-start:${framework} -->` : markerStart;
|
|
803
|
+
const blockMarkerEnd = multi ? `<!-- aiflow-kit-end:${framework} -->` : markerEnd;
|
|
517
804
|
|
|
518
|
-
const
|
|
519
|
-
if (await fs.pathExists(toolTemplatePath)) {
|
|
520
|
-
finalContent += await fs.readFile(toolTemplatePath, 'utf-8') + '\n\n';
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// Include framework conventions for single-framework projects
|
|
524
|
-
if (!multi && frameworkContent) {
|
|
525
|
-
finalContent += frameworkContent + '\n\n';
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (skillRegistry) {
|
|
529
|
-
finalContent += skillRegistry + '\n\n';
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
if (workflowContent) {
|
|
533
|
-
finalContent += workflowContent + '\n\n';
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
finalContent = `${markerStart}\n${finalContent.trim()}\n${markerEnd}`;
|
|
805
|
+
const finalContent = await buildToolBlock(tool, blockMarkerStart, blockMarkerEnd);
|
|
537
806
|
|
|
538
807
|
const fileExists = await fs.pathExists(targetPath);
|
|
539
808
|
|
|
@@ -593,7 +862,15 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
|
|
|
593
862
|
}
|
|
594
863
|
} else {
|
|
595
864
|
if (fileExists) {
|
|
596
|
-
await fs.
|
|
865
|
+
const existingContent = await fs.readFile(targetPath, 'utf-8');
|
|
866
|
+
const hasBlock = existingContent.includes(blockMarkerStart) && existingContent.includes(blockMarkerEnd);
|
|
867
|
+
if (hasBlock) {
|
|
868
|
+
const regex = new RegExp(`${blockMarkerStart}[\\s\\S]*?${blockMarkerEnd}`, 'g');
|
|
869
|
+
const updatedContent = existingContent.replace(regex, finalContent);
|
|
870
|
+
await fs.writeFile(targetPath, updatedContent);
|
|
871
|
+
} else {
|
|
872
|
+
await fs.appendFile(targetPath, separator + finalContent);
|
|
873
|
+
}
|
|
597
874
|
} else {
|
|
598
875
|
await fs.writeFile(targetPath, finalContent);
|
|
599
876
|
}
|
|
@@ -802,6 +1079,8 @@ async function ensureAiflowGitignored(projectDir) {
|
|
|
802
1079
|
'.mcp.json',
|
|
803
1080
|
'CLAUDE.md',
|
|
804
1081
|
'GEMINI.md',
|
|
1082
|
+
'AGENTS.md',
|
|
1083
|
+
'.codex/',
|
|
805
1084
|
'.cursorrules',
|
|
806
1085
|
'.github/copilot-instructions.md'
|
|
807
1086
|
];
|
|
@@ -1185,6 +1464,11 @@ async function init(options) {
|
|
|
1185
1464
|
// ── Optional: GitNexus code intelligence ─────────────────────
|
|
1186
1465
|
await maybeSetupGitNexus(projectDir, options.withGitnexus || false, options.wait || false);
|
|
1187
1466
|
|
|
1467
|
+
// ── Codex assets ─────────────────────────────────────────────
|
|
1468
|
+
// After adapters + GitNexus: .codex/config.toml mirrors .mcp.json, so it
|
|
1469
|
+
// has to be written once every MCP server is registered.
|
|
1470
|
+
await setupCodex(projectDir, selectedTools);
|
|
1471
|
+
|
|
1188
1472
|
// ── Ensure all aiflow files are gitignored ───────────────────
|
|
1189
1473
|
await ensureAiflowGitignored(projectDir);
|
|
1190
1474
|
|
|
@@ -1201,6 +1485,9 @@ async function init(options) {
|
|
|
1201
1485
|
|
|
1202
1486
|
console.log(` ${chalk.green('claude')} ${chalk.gray('← Claude Code CLI (recommended)')}`);
|
|
1203
1487
|
console.log(` ${chalk.green('Cursor / Gemini')} ${chalk.gray('← Instructions in .cursorrules / GEMINI.md')}`);
|
|
1488
|
+
if (selectedTools.includes('codex')) {
|
|
1489
|
+
console.log(` ${chalk.green('Codex')} ${chalk.gray('← AGENTS.md + .codex/ (VS Code extension, ChatGPT desktop app, codex CLI)')}`);
|
|
1490
|
+
}
|
|
1204
1491
|
console.log(chalk.blue('─'.repeat(62)));
|
|
1205
1492
|
console.log(chalk.gray(` Full guide: ${chalk.green('aiflow guide')} | Commands: ${chalk.green('aiflow guide --commands')}`));
|
|
1206
1493
|
console.log(chalk.blue('─'.repeat(62)) + '\n');
|
|
@@ -1211,6 +1498,15 @@ async function init(options) {
|
|
|
1211
1498
|
|
|
1212
1499
|
module.exports = init;
|
|
1213
1500
|
module.exports.AI_TOOL_FILES = AI_TOOL_FILES;
|
|
1501
|
+
module.exports.AI_TOOL_LAYOUT = AI_TOOL_LAYOUT;
|
|
1502
|
+
module.exports.resolveToolLayout = resolveToolLayout;
|
|
1503
|
+
module.exports.writeWorkflowInstructions = writeWorkflowInstructions;
|
|
1504
|
+
module.exports.setupCodex = setupCodex;
|
|
1505
|
+
module.exports.setupCodexSkills = setupCodexSkills;
|
|
1506
|
+
module.exports.setupCodexConfig = setupCodexConfig;
|
|
1507
|
+
module.exports.renderCodexMcpServer = renderCodexMcpServer;
|
|
1508
|
+
module.exports.generateSkillNameList = generateSkillNameList;
|
|
1509
|
+
module.exports.CODEX_PROJECT_DOC_MAX_BYTES = CODEX_PROJECT_DOC_MAX_BYTES;
|
|
1214
1510
|
module.exports.detectRtk = detectRtk;
|
|
1215
1511
|
module.exports.isRtkHookConfigured = isRtkHookConfigured;
|
|
1216
1512
|
module.exports.isGitNexusConfigured = isGitNexusConfigured;
|
package/scripts/prompt.js
CHANGED
|
@@ -51,8 +51,7 @@ Only runs after Gate 2 APPROVED.
|
|
|
51
51
|
**INVOKE:** \`review-plan\` skill
|
|
52
52
|
1. \`superpowers:verification-before-completion\` — all tests PASS (including old tests)
|
|
53
53
|
2. \`impact-analysis\` skill — fix doesn't cause new bugs elsewhere
|
|
54
|
-
3.
|
|
55
|
-
4. Create \`AK-Docs/04.Coding/04.Reviews/[functionId]/[ticketId].md\`
|
|
54
|
+
3. Create \`AK-Docs/04.Coding/04.Reviews/[functionId]/[ticketId].md\`
|
|
56
55
|
- Display: "GATE 4: type APPROVED or BUG: [description]"
|
|
57
56
|
- Coding bug → fix → repeat Gate 4
|
|
58
57
|
- Requirement bug → back to Gate 1
|
|
@@ -103,8 +102,7 @@ Only runs after Gate 2 APPROVED.
|
|
|
103
102
|
**INVOKE:** \`review-plan\` skill
|
|
104
103
|
1. \`superpowers:verification-before-completion\` — all tests PASS
|
|
105
104
|
2. \`impact-analysis\` skill — no regressions
|
|
106
|
-
3.
|
|
107
|
-
4. Create \`AK-Docs/04.Coding/04.Reviews/[functionId]/[ticketId].md\`
|
|
105
|
+
3. Create \`AK-Docs/04.Coding/04.Reviews/[functionId]/[ticketId].md\`
|
|
108
106
|
- Display: "GATE 4: type APPROVED or BUG: [description]"
|
|
109
107
|
- Coding bug → fix → repeat Gate 4
|
|
110
108
|
- Requirement bug → back to Gate 1
|
|
@@ -181,8 +179,7 @@ Only runs after Gate 2 APPROVED.
|
|
|
181
179
|
**INVOKE:** \`review-plan\` skill
|
|
182
180
|
1. \`superpowers:verification-before-completion\` — all tests PASS
|
|
183
181
|
2. \`impact-analysis\` skill — verify no regressions
|
|
184
|
-
3.
|
|
185
|
-
4. Create \`AK-Docs/04.Coding/04.Reviews/[functionId]/[ticketId].md\`
|
|
182
|
+
3. Create \`AK-Docs/04.Coding/04.Reviews/[functionId]/[ticketId].md\`
|
|
186
183
|
- Display: "GATE 4: type APPROVED or BUG: [description]"
|
|
187
184
|
|
|
188
185
|
### GATE 5 — Peer Review & Done
|