@relipa/ai-flow-kit 0.2.0 → 0.2.2-beta.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/README.md +4 -4
- package/custom/rules/java/spring-boot-rules.md +209 -0
- package/custom/rules/javascript/nestjs-examples.md +41 -0
- package/custom/rules/javascript/nestjs-rules.md +42 -0
- package/custom/rules/javascript/nodejs-express-examples.md +35 -0
- package/custom/rules/javascript/nodejs-express-rules.md +49 -0
- package/custom/rules/javascript/reactjs-examples.md +380 -0
- package/custom/rules/javascript/reactjs-rules.md +173 -0
- package/custom/rules/php/php-examples.md +161 -0
- package/custom/rules/php/php-rules.md +127 -0
- package/custom/rules/python/python-django-examples.md +34 -0
- package/custom/rules/python/python-django-rules.md +48 -0
- package/custom/rules/python/python-examples.md +32 -0
- package/custom/rules/python/python-fastapi-examples.md +30 -0
- package/custom/rules/python/python-fastapi-rules.md +35 -0
- package/custom/rules/python/python-ml-examples.md +187 -0
- package/custom/rules/python/python-ml-rules.md +121 -0
- package/custom/rules/python/python-rules.md +58 -0
- package/custom/skills/ba-skills/skill-ba-qna-template-v1.md +4 -4
- package/custom/skills/ba-skills/skill-ba-qna-v1.md +6 -0
- package/custom/skills/create-system-requirement/SKILL.md +99 -25
- package/custom/skills/create-system-requirement/system-requirement-template-v1.md +282 -0
- package/custom/skills/execute-flow/SKILL.md +142 -36
- package/custom/skills/execute-flow/templates/evidence-helper.ts +145 -0
- package/custom/skills/execute-flow/templates/playwright.config.ts +28 -8
- package/custom/skills/impact-analysis/SKILL.md +106 -106
- package/custom/skills/read-study-requirement/SKILL.md +1 -2
- package/custom/skills/report-customer/SKILL.md +99 -99
- package/custom/skills/script-sync/SKILL.md +54 -16
- package/custom/templates/nestjs.md +5 -72
- package/custom/templates/nodejs-express.md +5 -73
- package/custom/templates/php-plain.md +5 -261
- package/custom/templates/php.md +5 -261
- package/custom/templates/python-django.md +5 -71
- package/custom/templates/python-fastapi.md +5 -54
- package/custom/templates/python-ml.md +1 -269
- package/custom/templates/python.md +5 -79
- package/custom/templates/reactjs.md +5 -492
- package/custom/templates/shared/gate-workflow.md +18 -11
- package/custom/templates/shared/ml-gate-workflow.md +1 -0
- package/custom/templates/spring-boot.md +5 -224
- package/docs/common/CHANGELOG.md +24 -6
- package/docs/common/INDEX.md +1 -0
- package/docs/common/QUICK_START.md +1 -1
- package/docs/common/System-Requirement-Read-Guide.md +178 -0
- package/docs/common/Testing-Structure.md +31 -25
- package/docs/common/cli-reference.md +12 -10
- package/package.json +1 -1
- package/scripts/init.js +143 -40
- package/scripts/prompt.js +3 -3
- package/scripts/scaffold-playwright.js +2 -0
package/scripts/init.js
CHANGED
|
@@ -25,14 +25,19 @@ function commandExists(cmd) {
|
|
|
25
25
|
|
|
26
26
|
// Map framework → language family for picking the right rules
|
|
27
27
|
const FRAMEWORK_LANGUAGE = {
|
|
28
|
-
'laravel':
|
|
29
|
-
'php':
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
28
|
+
'laravel': 'php',
|
|
29
|
+
'php': 'php',
|
|
30
|
+
'php-plain': 'php',
|
|
31
|
+
'spring-boot': 'java',
|
|
32
|
+
'reactjs': 'javascript',
|
|
33
|
+
'nextjs': 'javascript',
|
|
34
|
+
'vue-nuxt': 'javascript',
|
|
35
|
+
'nestjs': 'javascript',
|
|
36
|
+
'nodejs-express': 'javascript',
|
|
37
|
+
'python-ml': 'python',
|
|
38
|
+
'python': 'python',
|
|
39
|
+
'python-django': 'python',
|
|
40
|
+
'python-fastapi': 'python',
|
|
36
41
|
};
|
|
37
42
|
|
|
38
43
|
// Frameworks that use the ML-specific gate workflow instead of the default one
|
|
@@ -69,8 +74,16 @@ const AI_TOOL_FILES = {
|
|
|
69
74
|
// Codex is 'pointer' because it caps the combined AGENTS.md chain at
|
|
70
75
|
// project_doc_max_bytes (32 KiB default) and silently drops whatever exceeds it.
|
|
71
76
|
// The assembled workflow set is ~115 KB, so inlining would truncate it mid-gate.
|
|
77
|
+
//
|
|
78
|
+
// Claude is 'pointer' for a softer reason: no hard byte cap, but CLAUDE.md is
|
|
79
|
+
// loaded in full at the start of every session regardless of which gate (or none)
|
|
80
|
+
// is actually active, so inlining ~115 KB of DEV/TESTER/EXECUTE/gen-doc/ingest-data
|
|
81
|
+
// + BA + QA workflow text spends context on gates most sessions never touch.
|
|
82
|
+
// Claude Code already reads files on demand via Read/Skill, so a short pointer
|
|
83
|
+
// works the same way a Skill reference does.
|
|
72
84
|
const AI_TOOL_LAYOUT = {
|
|
73
85
|
'codex': 'pointer',
|
|
86
|
+
'claude': 'pointer',
|
|
74
87
|
};
|
|
75
88
|
|
|
76
89
|
/** Return 'inline' or 'pointer' for a tool key. */
|
|
@@ -78,6 +91,39 @@ function resolveToolLayout(tool) {
|
|
|
78
91
|
return AI_TOOL_LAYOUT[tool] || 'inline';
|
|
79
92
|
}
|
|
80
93
|
|
|
94
|
+
// Skill-list rendering per tool.
|
|
95
|
+
// 'full' — verbose markdown table with description per skill (default, for
|
|
96
|
+
// tools with no native skill index — cursor/gemini/copilot/generic).
|
|
97
|
+
// 'compact' — bare skill-name list.
|
|
98
|
+
//
|
|
99
|
+
// Both Codex (.codex/skills/) and Claude Code (.claude/skills/) auto-discover
|
|
100
|
+
// name + description from each SKILL.md's frontmatter on their own, so restating
|
|
101
|
+
// the full table in the generated instruction file just burns context on every
|
|
102
|
+
// turn for information the tool already surfaces natively. The compact list still
|
|
103
|
+
// tells the AI which skill names exist and where to `Read` the full SKILL.md when
|
|
104
|
+
// a workflow step names one — it just stops duplicating the description.
|
|
105
|
+
const AI_TOOL_SKILL_LIST_STYLE = {
|
|
106
|
+
'codex': 'compact',
|
|
107
|
+
'claude': 'compact',
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/** Return 'full' or 'compact' skill-list style for a tool key. */
|
|
111
|
+
function resolveSkillListStyle(tool) {
|
|
112
|
+
return AI_TOOL_SKILL_LIST_STYLE[tool] || 'full';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Where each tool actually reads its skills from, for re-rooting paths in the
|
|
116
|
+
// generated registry/name-list. Only Codex mirrors skills into its own directory
|
|
117
|
+
// (.codex/skills/); every other tool reads the canonical .claude/skills/ directly.
|
|
118
|
+
const AI_TOOL_SKILLS_ROOT = {
|
|
119
|
+
'codex': '.codex/skills',
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/** Return the skills-root path to embed in a tool's generated instruction file. */
|
|
123
|
+
function resolveSkillsRoot(tool) {
|
|
124
|
+
return AI_TOOL_SKILLS_ROOT[tool] || '.claude/skills';
|
|
125
|
+
}
|
|
126
|
+
|
|
81
127
|
// Shared workflow templates copied to .aiflow/instructions/ for pointer-layout tools.
|
|
82
128
|
// Keys are the destination basenames; gate workflow resolves per framework.
|
|
83
129
|
const WORKFLOW_INSTRUCTION_FILES = [
|
|
@@ -612,7 +658,8 @@ async function generateSkillNameList(projectDir, skillsRoot = '.codex/skills') {
|
|
|
612
658
|
if (!names.length) return '';
|
|
613
659
|
|
|
614
660
|
let out = `## Available skills\n\n`;
|
|
615
|
-
out += `
|
|
661
|
+
out += `These skills are indexed automatically from \`${skillsRoot}/\` (name + description come from `;
|
|
662
|
+
out += `each SKILL.md's frontmatter). When a workflow step says `;
|
|
616
663
|
out += `**INVOKE:** \`<name>\`, read \`${skillsRoot}/<name>/SKILL.md\` in full and follow it:\n\n`;
|
|
617
664
|
out += names.sort().map(n => `\`${n}\``).join(' · ') + '\n';
|
|
618
665
|
return out;
|
|
@@ -653,6 +700,29 @@ async function generateSkillRegistry(projectDir, skillsRoot = '.claude/skills')
|
|
|
653
700
|
return registry;
|
|
654
701
|
}
|
|
655
702
|
|
|
703
|
+
/**
|
|
704
|
+
* Write `finalContent` into `targetPath` under its own marker pair: replace the
|
|
705
|
+
* existing block in place if the markers are already present, append (separated
|
|
706
|
+
* by `sep`) if the file exists without them, or create the file fresh otherwise.
|
|
707
|
+
* Used for multi-framework projects, where several independent markers
|
|
708
|
+
* (one per framework, plus the shared tool/registry block) share one file.
|
|
709
|
+
*/
|
|
710
|
+
async function upsertMarkerBlock(targetPath, markerStart, markerEnd, finalContent, sep) {
|
|
711
|
+
const exists = await fs.pathExists(targetPath);
|
|
712
|
+
if (!exists) {
|
|
713
|
+
await fs.writeFile(targetPath, finalContent);
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
const existingContent = await fs.readFile(targetPath, 'utf-8');
|
|
717
|
+
const hasBlock = existingContent.includes(markerStart) && existingContent.includes(markerEnd);
|
|
718
|
+
if (hasBlock) {
|
|
719
|
+
const regex = new RegExp(`${markerStart}[\\s\\S]*?${markerEnd}`, 'g');
|
|
720
|
+
await fs.writeFile(targetPath, existingContent.replace(regex, finalContent));
|
|
721
|
+
} else {
|
|
722
|
+
await fs.appendFile(targetPath, sep + finalContent);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
656
726
|
async function setupFramework(projectDir, framework, multi = false, selectedTools = Object.keys(AI_TOOL_FILES), options = {}) {
|
|
657
727
|
if (!framework) return;
|
|
658
728
|
const frameworkTemplatePath = path.join(PKG_DIR, 'custom', 'templates', `${framework}.md`);
|
|
@@ -683,12 +753,19 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
|
|
|
683
753
|
const workflowContent = [gateWorkflowContent, createSpecContent, createTestcaseContent].filter(Boolean).join('\n\n---\n\n');
|
|
684
754
|
|
|
685
755
|
const separator = `\n\n---\n\n`;
|
|
686
|
-
const skillRegistry = await generateSkillRegistry(projectDir);
|
|
687
|
-
const codexSkillList = await generateSkillNameList(projectDir, '.codex/skills');
|
|
688
756
|
|
|
689
757
|
const markerStart = '<!-- aiflow-kit-start -->';
|
|
690
758
|
const markerEnd = '<!-- aiflow-kit-end -->';
|
|
691
759
|
|
|
760
|
+
// Multi-framework projects get exactly one shared block per tool (tool header +
|
|
761
|
+
// skill registry) instead of one copy per framework block — neither depends on
|
|
762
|
+
// which framework triggered this call, so repeating them per framework was pure
|
|
763
|
+
// duplication (e.g. spring-boot + php in the same project doubled the ~44-skill
|
|
764
|
+
// registry). Re-running for a second framework upserts this block in place via
|
|
765
|
+
// its fixed markers rather than appending another copy.
|
|
766
|
+
const sharedMarkerStart = '<!-- aiflow-kit-start:shared -->';
|
|
767
|
+
const sharedMarkerEnd = '<!-- aiflow-kit-end:shared -->';
|
|
768
|
+
|
|
692
769
|
// Pointer-layout tools need the workflows on disk instead of inlined.
|
|
693
770
|
const needsPointer = selectedTools.some(t => resolveToolLayout(t) === 'pointer');
|
|
694
771
|
const instructionPaths = needsPointer
|
|
@@ -696,12 +773,13 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
|
|
|
696
773
|
: [];
|
|
697
774
|
|
|
698
775
|
/**
|
|
699
|
-
*
|
|
700
|
-
*
|
|
701
|
-
*
|
|
776
|
+
* Tool header + skill registry + (for pointer tools) the "installed workflow
|
|
777
|
+
* files" pointer note — none of this depends on which framework is active:
|
|
778
|
+
* the 3 instruction-file paths are always the same 3 filenames regardless of
|
|
779
|
+
* framework (only their on-disk *content* varies), so this used to be
|
|
780
|
+
* duplicated per framework block for no reason.
|
|
702
781
|
*/
|
|
703
|
-
async function
|
|
704
|
-
const pointer = resolveToolLayout(tool) === 'pointer';
|
|
782
|
+
async function buildSharedBlock(tool) {
|
|
705
783
|
let body = '';
|
|
706
784
|
|
|
707
785
|
const toolTemplatePath = path.join(PKG_DIR, 'custom', 'templates', 'tools', `${tool}.md`);
|
|
@@ -709,19 +787,46 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
|
|
|
709
787
|
body += await fs.readFile(toolTemplatePath, 'utf-8') + '\n\n';
|
|
710
788
|
}
|
|
711
789
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
790
|
+
const skillsRoot = resolveSkillsRoot(tool);
|
|
791
|
+
const registry = resolveSkillListStyle(tool) === 'compact'
|
|
792
|
+
? await generateSkillNameList(projectDir, skillsRoot)
|
|
793
|
+
: await generateSkillRegistry(projectDir, skillsRoot);
|
|
715
794
|
if (registry) body += registry + '\n\n';
|
|
716
795
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
796
|
+
const pointer = resolveToolLayout(tool) === 'pointer';
|
|
797
|
+
if (pointer && instructionPaths.length) {
|
|
798
|
+
body += `## Installed workflow files\n\n`;
|
|
799
|
+
body += `Generated by ai-flow-kit v${PKG_VERSION}. `;
|
|
800
|
+
body += `These are the full DEV/TESTER/EXECUTE (+ gen-doc/ingest-data), BA spec, and QA `;
|
|
801
|
+
body += `testcase gate workflows — too large to inline here. Read the relevant file `;
|
|
802
|
+
body += `**in full** before starting a gate; do not work from a summary and do not skip, `;
|
|
803
|
+
body += `shorten, or merge gates:\n\n`;
|
|
804
|
+
body += instructionPaths.map(p => `- \`${p}\``).join('\n');
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
return body.trim();
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Assemble the marker block for one tool.
|
|
812
|
+
* 'inline' tools get the framework's gate workflow embedded inline; 'pointer'
|
|
813
|
+
* tools already got their "read from disk" note via buildSharedBlock() above.
|
|
814
|
+
* Pass includeShared: false for multi-framework projects — the tool header,
|
|
815
|
+
* skill registry, and workflow-files note are written once via
|
|
816
|
+
* buildSharedBlock() instead.
|
|
817
|
+
*/
|
|
818
|
+
async function buildToolBlock(tool, blockMarkerStart, blockMarkerEnd, { includeShared = true } = {}) {
|
|
819
|
+
const pointer = resolveToolLayout(tool) === 'pointer';
|
|
820
|
+
let body = '';
|
|
821
|
+
|
|
822
|
+
if (includeShared) {
|
|
823
|
+
const shared = await buildSharedBlock(tool);
|
|
824
|
+
if (shared) body += shared + '\n\n';
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
if (frameworkContent) body += frameworkContent + '\n\n';
|
|
828
|
+
|
|
829
|
+
if (!pointer && workflowContent) {
|
|
725
830
|
body += workflowContent + '\n\n';
|
|
726
831
|
}
|
|
727
832
|
|
|
@@ -802,7 +907,7 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
|
|
|
802
907
|
const blockMarkerStart = multi ? `<!-- aiflow-kit-start:${framework} -->` : markerStart;
|
|
803
908
|
const blockMarkerEnd = multi ? `<!-- aiflow-kit-end:${framework} -->` : markerEnd;
|
|
804
909
|
|
|
805
|
-
const finalContent = await buildToolBlock(tool, blockMarkerStart, blockMarkerEnd);
|
|
910
|
+
const finalContent = await buildToolBlock(tool, blockMarkerStart, blockMarkerEnd, { includeShared: !multi });
|
|
806
911
|
|
|
807
912
|
const fileExists = await fs.pathExists(targetPath);
|
|
808
913
|
|
|
@@ -861,19 +966,15 @@ async function setupFramework(projectDir, framework, multi = false, selectedTool
|
|
|
861
966
|
}
|
|
862
967
|
}
|
|
863
968
|
} else {
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
} else {
|
|
872
|
-
await fs.appendFile(targetPath, separator + finalContent);
|
|
873
|
-
}
|
|
874
|
-
} else {
|
|
875
|
-
await fs.writeFile(targetPath, finalContent);
|
|
969
|
+
// Upsert the shared block (tool header + skill registry) once per tool —
|
|
970
|
+
// fixed markers mean a second framework's call replaces it in place
|
|
971
|
+
// instead of appending another copy.
|
|
972
|
+
const sharedBody = await buildSharedBlock(tool);
|
|
973
|
+
if (sharedBody) {
|
|
974
|
+
const sharedFinal = `${sharedMarkerStart}\n${sharedBody}\n${sharedMarkerEnd}`;
|
|
975
|
+
await upsertMarkerBlock(targetPath, sharedMarkerStart, sharedMarkerEnd, sharedFinal, separator);
|
|
876
976
|
}
|
|
977
|
+
await upsertMarkerBlock(targetPath, blockMarkerStart, blockMarkerEnd, finalContent, separator);
|
|
877
978
|
written.push(AI_TOOL_FILES[tool]);
|
|
878
979
|
}
|
|
879
980
|
}
|
|
@@ -1523,6 +1624,8 @@ module.exports = init;
|
|
|
1523
1624
|
module.exports.AI_TOOL_FILES = AI_TOOL_FILES;
|
|
1524
1625
|
module.exports.AI_TOOL_LAYOUT = AI_TOOL_LAYOUT;
|
|
1525
1626
|
module.exports.resolveToolLayout = resolveToolLayout;
|
|
1627
|
+
module.exports.resolveSkillListStyle = resolveSkillListStyle;
|
|
1628
|
+
module.exports.resolveSkillsRoot = resolveSkillsRoot;
|
|
1526
1629
|
module.exports.writeWorkflowInstructions = writeWorkflowInstructions;
|
|
1527
1630
|
module.exports.setupCodex = setupCodex;
|
|
1528
1631
|
module.exports.setupCodexSkills = setupCodexSkills;
|
package/scripts/prompt.js
CHANGED
|
@@ -386,16 +386,16 @@ Follow gates IN ORDER. Do NOT skip or merge gates.
|
|
|
386
386
|
|
|
387
387
|
### GATE 1 — Pre-flight & Work Plan (auto-start)
|
|
388
388
|
**INVOKE:** \`execute-flow\` skill
|
|
389
|
-
Check: MCP Playwright configured, TC file is parseable, \`
|
|
389
|
+
Check: MCP Playwright configured, TC file is parseable, \`AK-Docs/03.Testing/\` subfolders exist (\`04.Evidence/\`, \`05.Scripts/\`, \`02.Reports/\`, \`06.Bugs/\`), \`BASE_URL\` set, \`AK-Docs/03.Testing/05.Scripts/{repo}/playwright.config.ts\` exists (auto-scaffold if missing), \`.gitignore\` covers \`03.Testing/04.Evidence/\` and \`05.Scripts/**/node_modules\`. Display the work plan (TC file, repo, screen, BASE_URL) → wait for **APPROVED**.
|
|
390
390
|
|
|
391
391
|
### GATE 2 — Script Sync
|
|
392
392
|
Only runs after Gate 1 APPROVED.
|
|
393
393
|
**INVOKE:** \`script-sync\` skill
|
|
394
|
-
Hash-based TC↔script sync: new TC → generate Playwright test via MCP snapshot/locator (never fabricate selectors); changed TC → update; unchanged → skip; manual/blocked → mark Untest/Pending. Display new/updated/skipped/manual counts → wait for **APPROVED**.
|
|
394
|
+
Hash-based TC↔script sync: new TC → generate Playwright test via MCP snapshot/locator (never fabricate selectors, never use a locator matching more than one element); changed TC → update; unchanged → skip; manual/blocked → mark Untest/Pending. Every step calls \`captureStepEvidence()\` from \`Shared/evidence-helper.ts\`. Display new/updated/skipped/manual counts → wait for **APPROVED**.
|
|
395
395
|
|
|
396
396
|
### GATE 3 — Execute & Evidence
|
|
397
397
|
Only runs after Gate 2 APPROVED.
|
|
398
|
-
Run \`
|
|
398
|
+
Run from \`AK-Docs/03.Testing/05.Scripts/\` with \`EVIDENCE_DIR\` pointed at \`AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}/\` (gitignored), organize evidence (screenshots/trace) there and text output (\`result.md\`, \`testreport.md\`) into \`AK-Docs/03.Testing/02.Reports/{repo}/{featureDir}/run-{N}/\`, auto-draft bug reports into \`AK-Docs/03.Testing/06.Bugs/{repo}/{featureDir}/run-{N}/\`, update R1/R2 columns in the TC file. Cross-check assertions against the TC's actual Expected Result before recording PASS/FAIL — don't trust Playwright's exit code alone. Supports \`RETEST: [TC_ID]\` and \`PR: [url]\`. Display pass/fail/untest/pending counts → wait for **APPROVED** (all Critical/High bugs resolved).
|
|
399
399
|
|
|
400
400
|
### GATE 4 — Report & Bug Logging (wait for APPROVED)
|
|
401
401
|
Only runs after Gate 3 APPROVED.
|