qfai 1.4.7 → 1.4.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 +13 -4
- package/dist/cli/index.cjs +386 -6
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.mjs +399 -19
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
- package/assets/init/root/features/spec-0001.feature +0 -20
package/README.md
CHANGED
|
@@ -207,7 +207,7 @@ flowchart LR
|
|
|
207
207
|
- Contracts SSOT: `.qfai/contracts/**`
|
|
208
208
|
- Report outputs (`.qfai/report/**`) are derived artifacts and not SSOT.
|
|
209
209
|
|
|
210
|
-
## Minimal tutorial (v1.4.
|
|
210
|
+
## Minimal tutorial (v1.4.9)
|
|
211
211
|
|
|
212
212
|
1. `npx qfai init`
|
|
213
213
|
2. Run `/qfai-discuss` to structure scope and open questions.
|
|
@@ -233,7 +233,9 @@ Release gate behavior:
|
|
|
233
233
|
|
|
234
234
|
## Continuous integration
|
|
235
235
|
|
|
236
|
-
QFAI v1.4.
|
|
236
|
+
QFAI v1.4.9 generates `.github/**` only for Copilot integration wrappers
|
|
237
|
+
(`.github/prompts`, `.github/agents`).
|
|
238
|
+
It does not generate GitHub Actions workflows.
|
|
237
239
|
Configure CI in your own platform and run:
|
|
238
240
|
|
|
239
241
|
```bash
|
|
@@ -348,10 +350,17 @@ Typical customizations.
|
|
|
348
350
|
└── qfai.config.yaml
|
|
349
351
|
```
|
|
350
352
|
|
|
353
|
+
Integration wrappers are also generated for immediate use:
|
|
354
|
+
|
|
355
|
+
- Claude Code: `.claude/commands/**`, `.claude/agents/**`
|
|
356
|
+
- GitHub Copilot: `.github/prompts/**`, `.github/agents/**`
|
|
357
|
+
- Codex: `.codex/skills/**`
|
|
358
|
+
|
|
351
359
|
## Agent integrations
|
|
352
360
|
|
|
353
|
-
`npx qfai init` installs
|
|
354
|
-
|
|
361
|
+
`npx qfai init` installs canonical skills under `.qfai/assistant/skills/**` (SSOT)
|
|
362
|
+
and generates thin wrapper assets for Copilot / Claude Code / Codex.
|
|
363
|
+
If wrapper assets drift from canonical skills, rerun `npx qfai init --force` to resync.
|
|
355
364
|
|
|
356
365
|
## Contributing (for QFAI maintainers)
|
|
357
366
|
|
package/dist/cli/index.cjs
CHANGED
|
@@ -1218,8 +1218,8 @@ var import_promises7 = require("fs/promises");
|
|
|
1218
1218
|
var import_node_path8 = __toESM(require("path"), 1);
|
|
1219
1219
|
var import_node_url2 = require("url");
|
|
1220
1220
|
async function resolveToolVersion() {
|
|
1221
|
-
if ("1.4.
|
|
1222
|
-
return "1.4.
|
|
1221
|
+
if ("1.4.9".length > 0) {
|
|
1222
|
+
return "1.4.9";
|
|
1223
1223
|
}
|
|
1224
1224
|
try {
|
|
1225
1225
|
const packagePath = resolvePackageJsonPath();
|
|
@@ -2400,11 +2400,12 @@ async function runInit(options) {
|
|
|
2400
2400
|
const assetsRoot = getInitAssetsDir();
|
|
2401
2401
|
const rootAssets = import_node_path14.default.join(assetsRoot, "root");
|
|
2402
2402
|
const qfaiAssets = import_node_path14.default.join(assetsRoot, ".qfai");
|
|
2403
|
+
const assistantAssets = import_node_path14.default.join(qfaiAssets, "assistant");
|
|
2403
2404
|
const destRoot = import_node_path14.default.resolve(options.dir);
|
|
2404
2405
|
const destQfai = import_node_path14.default.join(destRoot, ".qfai");
|
|
2405
2406
|
if (options.force) {
|
|
2406
2407
|
info(
|
|
2407
|
-
"NOTE: --force \u306F .qfai/assistant/skills/** \u3092\u4E0A\u66F8\u304D\u3057\u3001legacy 10_workflow.md \u3092\u524A\u9664\u3057\u307E\u3059\uFF08skills.local \u306F\u4FDD\u8B77\u3055\u308C\u3001specs/contracts \u7B49\u306F\u4E0A\u66F8\u304D\u3057\u307E\u305B\u3093\uFF09\u3002"
|
|
2408
|
+
"NOTE: --force \u306F .qfai/assistant/skills/** \u3068 wrapper assets\uFF08.claude/.github/.codex\uFF09\u3092\u4E0A\u66F8\u304D\u3057\u3001legacy 10_workflow.md \u3092\u524A\u9664\u3057\u307E\u3059\uFF08skills.local \u306F\u4FDD\u8B77\u3055\u308C\u3001specs/contracts \u7B49\u306F\u4E0A\u66F8\u304D\u3057\u307E\u305B\u3093\uFF09\u3002"
|
|
2408
2409
|
);
|
|
2409
2410
|
}
|
|
2410
2411
|
const rootResult = await copyTemplateTree(rootAssets, destRoot, {
|
|
@@ -2430,11 +2431,30 @@ async function runInit(options) {
|
|
|
2430
2431
|
protect: ["assistant/skills.local"]
|
|
2431
2432
|
}
|
|
2432
2433
|
);
|
|
2434
|
+
const wrappersResult = await syncIntegrationWrappers(
|
|
2435
|
+
assistantAssets,
|
|
2436
|
+
destRoot,
|
|
2437
|
+
{
|
|
2438
|
+
force: options.force,
|
|
2439
|
+
dryRun: options.dryRun
|
|
2440
|
+
}
|
|
2441
|
+
);
|
|
2433
2442
|
const removedLegacySkills = options.force ? await pruneLegacySkillFiles(destRoot, options.dryRun) : [];
|
|
2443
|
+
const removed = [...removedLegacySkills, ...wrappersResult.removed];
|
|
2434
2444
|
report(
|
|
2435
|
-
[
|
|
2436
|
-
|
|
2437
|
-
|
|
2445
|
+
[
|
|
2446
|
+
...rootResult.copied,
|
|
2447
|
+
...qfaiResult.copied,
|
|
2448
|
+
...skillsResult.copied,
|
|
2449
|
+
...wrappersResult.copied
|
|
2450
|
+
],
|
|
2451
|
+
[
|
|
2452
|
+
...rootResult.skipped,
|
|
2453
|
+
...qfaiResult.skipped,
|
|
2454
|
+
...skillsResult.skipped,
|
|
2455
|
+
...wrappersResult.skipped
|
|
2456
|
+
],
|
|
2457
|
+
removed,
|
|
2438
2458
|
options.dryRun,
|
|
2439
2459
|
"init",
|
|
2440
2460
|
destRoot
|
|
@@ -2505,6 +2525,366 @@ async function exists7(target) {
|
|
|
2505
2525
|
return false;
|
|
2506
2526
|
}
|
|
2507
2527
|
}
|
|
2528
|
+
async function syncIntegrationWrappers(assistantAssetsDir, destRoot, options) {
|
|
2529
|
+
const skills = await collectCanonicalSkillIds(assistantAssetsDir);
|
|
2530
|
+
const agents = await collectCanonicalAgentNames(assistantAssetsDir);
|
|
2531
|
+
const entries = buildWrapperEntries(skills, agents);
|
|
2532
|
+
const copied = [];
|
|
2533
|
+
const skipped = [];
|
|
2534
|
+
const removed = options.force ? await pruneStaleQfaiWrappers(destRoot, skills, options.dryRun) : [];
|
|
2535
|
+
for (const entry of entries) {
|
|
2536
|
+
const destination = import_node_path14.default.join(destRoot, ...entry.relativePath.split("/"));
|
|
2537
|
+
const alreadyExists = await exists7(destination);
|
|
2538
|
+
if (alreadyExists && !options.force) {
|
|
2539
|
+
skipped.push(destination);
|
|
2540
|
+
continue;
|
|
2541
|
+
}
|
|
2542
|
+
copied.push(destination);
|
|
2543
|
+
if (options.dryRun) {
|
|
2544
|
+
continue;
|
|
2545
|
+
}
|
|
2546
|
+
await (0, import_promises12.mkdir)(import_node_path14.default.dirname(destination), { recursive: true });
|
|
2547
|
+
await (0, import_promises12.writeFile)(destination, entry.body, "utf-8");
|
|
2548
|
+
}
|
|
2549
|
+
return { copied, skipped, removed };
|
|
2550
|
+
}
|
|
2551
|
+
async function collectCanonicalSkillIds(assistantAssetsDir) {
|
|
2552
|
+
const skillsDir = import_node_path14.default.join(assistantAssetsDir, "skills");
|
|
2553
|
+
if (!await exists7(skillsDir)) {
|
|
2554
|
+
return [];
|
|
2555
|
+
}
|
|
2556
|
+
const entries = await (0, import_promises12.readdir)(skillsDir, { withFileTypes: true });
|
|
2557
|
+
const skills = [];
|
|
2558
|
+
for (const entry of entries) {
|
|
2559
|
+
if (!entry.isDirectory()) {
|
|
2560
|
+
continue;
|
|
2561
|
+
}
|
|
2562
|
+
const skillDoc = import_node_path14.default.join(skillsDir, entry.name, "SKILL.md");
|
|
2563
|
+
if (await exists7(skillDoc)) {
|
|
2564
|
+
skills.push(entry.name);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
return skills.sort();
|
|
2568
|
+
}
|
|
2569
|
+
async function collectCanonicalAgentNames(assistantAssetsDir) {
|
|
2570
|
+
const agentsDir = import_node_path14.default.join(assistantAssetsDir, "agents");
|
|
2571
|
+
if (!await exists7(agentsDir)) {
|
|
2572
|
+
return [];
|
|
2573
|
+
}
|
|
2574
|
+
const entries = await (0, import_promises12.readdir)(agentsDir, { withFileTypes: true });
|
|
2575
|
+
const names = [];
|
|
2576
|
+
for (const entry of entries) {
|
|
2577
|
+
if (!entry.isFile()) {
|
|
2578
|
+
continue;
|
|
2579
|
+
}
|
|
2580
|
+
if (!entry.name.endsWith(".md") || entry.name === "README.md") {
|
|
2581
|
+
continue;
|
|
2582
|
+
}
|
|
2583
|
+
names.push(entry.name.slice(0, -".md".length));
|
|
2584
|
+
}
|
|
2585
|
+
return names.sort();
|
|
2586
|
+
}
|
|
2587
|
+
function buildWrapperEntries(skills, agents) {
|
|
2588
|
+
const entries = [
|
|
2589
|
+
{
|
|
2590
|
+
relativePath: ".codex/README.md",
|
|
2591
|
+
body: buildCodexReadme()
|
|
2592
|
+
},
|
|
2593
|
+
{
|
|
2594
|
+
relativePath: ".claude/agents/README.md",
|
|
2595
|
+
body: buildClaudeAgentsReadme()
|
|
2596
|
+
},
|
|
2597
|
+
{
|
|
2598
|
+
relativePath: ".github/agents/README.md",
|
|
2599
|
+
body: buildGithubAgentsReadme()
|
|
2600
|
+
},
|
|
2601
|
+
{
|
|
2602
|
+
relativePath: ".github/copilot-instructions.md",
|
|
2603
|
+
body: buildCopilotInstructions()
|
|
2604
|
+
}
|
|
2605
|
+
];
|
|
2606
|
+
for (const skillId of skills) {
|
|
2607
|
+
entries.push({
|
|
2608
|
+
relativePath: `.claude/commands/${skillId}.md`,
|
|
2609
|
+
body: buildClaudeCommandWrapper(skillId)
|
|
2610
|
+
});
|
|
2611
|
+
entries.push({
|
|
2612
|
+
relativePath: `.github/prompts/${skillId}.prompt.md`,
|
|
2613
|
+
body: buildGithubPromptWrapper(skillId)
|
|
2614
|
+
});
|
|
2615
|
+
entries.push({
|
|
2616
|
+
relativePath: `.codex/skills/${skillId}/SKILL.md`,
|
|
2617
|
+
body: buildCodexSkillWrapper(skillId)
|
|
2618
|
+
});
|
|
2619
|
+
}
|
|
2620
|
+
for (const agentName of agents) {
|
|
2621
|
+
entries.push({
|
|
2622
|
+
relativePath: `.claude/agents/${agentName}.md`,
|
|
2623
|
+
body: buildClaudeAgentWrapper(agentName)
|
|
2624
|
+
});
|
|
2625
|
+
entries.push({
|
|
2626
|
+
relativePath: `.github/agents/${agentName}.agent.md`,
|
|
2627
|
+
body: buildGithubAgentWrapper(agentName)
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2630
|
+
return entries;
|
|
2631
|
+
}
|
|
2632
|
+
async function pruneStaleQfaiWrappers(destRoot, canonicalSkills, dryRun) {
|
|
2633
|
+
const canonical = new Set(canonicalSkills);
|
|
2634
|
+
const removed = [];
|
|
2635
|
+
const claudeCommandsDir = import_node_path14.default.join(destRoot, ".claude", "commands");
|
|
2636
|
+
if (await exists7(claudeCommandsDir)) {
|
|
2637
|
+
const entries = await (0, import_promises12.readdir)(claudeCommandsDir, { withFileTypes: true });
|
|
2638
|
+
for (const entry of entries) {
|
|
2639
|
+
if (!entry.isFile()) {
|
|
2640
|
+
continue;
|
|
2641
|
+
}
|
|
2642
|
+
if (!entry.name.startsWith("qfai-") || !entry.name.endsWith(".md")) {
|
|
2643
|
+
continue;
|
|
2644
|
+
}
|
|
2645
|
+
const skillId = entry.name.slice(0, -".md".length);
|
|
2646
|
+
if (canonical.has(skillId)) {
|
|
2647
|
+
continue;
|
|
2648
|
+
}
|
|
2649
|
+
const target = import_node_path14.default.join(claudeCommandsDir, entry.name);
|
|
2650
|
+
removed.push(target);
|
|
2651
|
+
if (!dryRun) {
|
|
2652
|
+
await (0, import_promises12.rm)(target, { force: true });
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
const githubPromptsDir = import_node_path14.default.join(destRoot, ".github", "prompts");
|
|
2657
|
+
if (await exists7(githubPromptsDir)) {
|
|
2658
|
+
const entries = await (0, import_promises12.readdir)(githubPromptsDir, { withFileTypes: true });
|
|
2659
|
+
for (const entry of entries) {
|
|
2660
|
+
if (!entry.isFile()) {
|
|
2661
|
+
continue;
|
|
2662
|
+
}
|
|
2663
|
+
if (!entry.name.startsWith("qfai-") || !entry.name.endsWith(".prompt.md")) {
|
|
2664
|
+
continue;
|
|
2665
|
+
}
|
|
2666
|
+
const skillId = entry.name.slice(0, -".prompt.md".length);
|
|
2667
|
+
if (canonical.has(skillId)) {
|
|
2668
|
+
continue;
|
|
2669
|
+
}
|
|
2670
|
+
const target = import_node_path14.default.join(githubPromptsDir, entry.name);
|
|
2671
|
+
removed.push(target);
|
|
2672
|
+
if (!dryRun) {
|
|
2673
|
+
await (0, import_promises12.rm)(target, { force: true });
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
const codexSkillsDir = import_node_path14.default.join(destRoot, ".codex", "skills");
|
|
2678
|
+
if (await exists7(codexSkillsDir)) {
|
|
2679
|
+
const entries = await (0, import_promises12.readdir)(codexSkillsDir, { withFileTypes: true });
|
|
2680
|
+
for (const entry of entries) {
|
|
2681
|
+
if (!entry.isDirectory()) {
|
|
2682
|
+
continue;
|
|
2683
|
+
}
|
|
2684
|
+
if (!entry.name.startsWith("qfai-")) {
|
|
2685
|
+
continue;
|
|
2686
|
+
}
|
|
2687
|
+
if (canonical.has(entry.name)) {
|
|
2688
|
+
continue;
|
|
2689
|
+
}
|
|
2690
|
+
const target = import_node_path14.default.join(codexSkillsDir, entry.name);
|
|
2691
|
+
removed.push(target);
|
|
2692
|
+
if (!dryRun) {
|
|
2693
|
+
await (0, import_promises12.rm)(target, { recursive: true, force: true });
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
return removed;
|
|
2698
|
+
}
|
|
2699
|
+
function buildCodexReadme() {
|
|
2700
|
+
return [
|
|
2701
|
+
"# QFAI Codex skills",
|
|
2702
|
+
"",
|
|
2703
|
+
"This directory provides thin Codex skill wrappers for QFAI.",
|
|
2704
|
+
"",
|
|
2705
|
+
"## Canonical entrypoint",
|
|
2706
|
+
"",
|
|
2707
|
+
"Codex skill wrappers must point to QFAI's canonical skill documents under:",
|
|
2708
|
+
"",
|
|
2709
|
+
"- .qfai/assistant/skills/",
|
|
2710
|
+
"",
|
|
2711
|
+
"These canonical skill documents are the SSOT.",
|
|
2712
|
+
"Tool integrations must reference `.qfai/assistant/skills/`.",
|
|
2713
|
+
"",
|
|
2714
|
+
"## Usage",
|
|
2715
|
+
"",
|
|
2716
|
+
"In Codex CLI, select a skill by name (e.g., `qfai-configure`) and provide your request.",
|
|
2717
|
+
"All outputs must match the user's language.",
|
|
2718
|
+
""
|
|
2719
|
+
].join("\n");
|
|
2720
|
+
}
|
|
2721
|
+
function buildClaudeAgentsReadme() {
|
|
2722
|
+
return [
|
|
2723
|
+
"# QFAI Claude agents",
|
|
2724
|
+
"",
|
|
2725
|
+
"This directory provides thin Claude Code agent wrappers for QFAI.",
|
|
2726
|
+
"",
|
|
2727
|
+
"## Canonical entrypoint",
|
|
2728
|
+
"",
|
|
2729
|
+
"All wrappers must point to:",
|
|
2730
|
+
"",
|
|
2731
|
+
"- .qfai/assistant/agents/",
|
|
2732
|
+
"",
|
|
2733
|
+
"Use these wrappers as entrypoints and keep canonical behavior in `.qfai/assistant/agents/**`.",
|
|
2734
|
+
""
|
|
2735
|
+
].join("\n");
|
|
2736
|
+
}
|
|
2737
|
+
function buildGithubAgentsReadme() {
|
|
2738
|
+
return [
|
|
2739
|
+
"# QFAI GitHub agents",
|
|
2740
|
+
"",
|
|
2741
|
+
"This directory provides thin GitHub Copilot custom agent wrappers for QFAI.",
|
|
2742
|
+
"",
|
|
2743
|
+
"## Canonical entrypoint",
|
|
2744
|
+
"",
|
|
2745
|
+
"All wrappers must point to:",
|
|
2746
|
+
"",
|
|
2747
|
+
"- .qfai/assistant/agents/",
|
|
2748
|
+
"",
|
|
2749
|
+
"Use these wrappers as entrypoints and keep canonical behavior in `.qfai/assistant/agents/**`.",
|
|
2750
|
+
""
|
|
2751
|
+
].join("\n");
|
|
2752
|
+
}
|
|
2753
|
+
function buildCopilotInstructions() {
|
|
2754
|
+
return [
|
|
2755
|
+
"# QFAI repository instructions (Copilot)",
|
|
2756
|
+
"",
|
|
2757
|
+
"This repository uses QFAI (Quality-First AI) to improve the quality and consistency of AI-assisted development.",
|
|
2758
|
+
"",
|
|
2759
|
+
"## Golden rules",
|
|
2760
|
+
"",
|
|
2761
|
+
"- Always match the user\u2019s language in your outputs.",
|
|
2762
|
+
"- Treat `.qfai/` as the canonical source of truth for the QFAI workflow:",
|
|
2763
|
+
" - Skills (SSOT): `.qfai/assistant/skills/`",
|
|
2764
|
+
" - Instructions: `.qfai/assistant/instructions/`",
|
|
2765
|
+
" - Project steering: `.qfai/assistant/steering/`",
|
|
2766
|
+
"- When asked to perform QFAI workflow tasks, prefer using the QFAI prompt wrappers in `.github/prompts/`.",
|
|
2767
|
+
" - These wrappers forward to `.qfai/assistant/skills/<skill-name>/SKILL.md`.",
|
|
2768
|
+
"- Do not invent repository structure, tools, or frameworks. Inspect the repo first and align with what is already used.",
|
|
2769
|
+
"- Keep changes minimal and targeted. Update tests and docs when behavior changes.",
|
|
2770
|
+
""
|
|
2771
|
+
].join("\n");
|
|
2772
|
+
}
|
|
2773
|
+
function buildClaudeCommandWrapper(skillId) {
|
|
2774
|
+
return [
|
|
2775
|
+
"---",
|
|
2776
|
+
`description: "QFAI: ${skillId}"`,
|
|
2777
|
+
'argument-hint: "[optional notes]"',
|
|
2778
|
+
"---",
|
|
2779
|
+
"",
|
|
2780
|
+
"Follow the canonical QFAI skill document exactly:",
|
|
2781
|
+
`@.qfai/assistant/skills/${skillId}/SKILL.md`,
|
|
2782
|
+
"",
|
|
2783
|
+
"Follow the DoD/Checkpoints in the skill document.",
|
|
2784
|
+
"Use the repository as the source of truth.",
|
|
2785
|
+
"",
|
|
2786
|
+
"Additional user notes: $ARGUMENTS",
|
|
2787
|
+
"",
|
|
2788
|
+
"Critical: output must match the user's language.",
|
|
2789
|
+
""
|
|
2790
|
+
].join("\n");
|
|
2791
|
+
}
|
|
2792
|
+
function buildGithubPromptWrapper(skillId) {
|
|
2793
|
+
return [
|
|
2794
|
+
"---",
|
|
2795
|
+
'agent: "agent"',
|
|
2796
|
+
`description: "QFAI: ${skillId}"`,
|
|
2797
|
+
"---",
|
|
2798
|
+
"",
|
|
2799
|
+
"You are operating in a repository that uses QFAI.",
|
|
2800
|
+
"",
|
|
2801
|
+
"1. Open and follow the canonical QFAI skill:",
|
|
2802
|
+
"",
|
|
2803
|
+
`- .qfai/assistant/skills/${skillId}/SKILL.md`,
|
|
2804
|
+
"",
|
|
2805
|
+
"2. Use the repository as the source of truth (tools, frameworks, directory structure).",
|
|
2806
|
+
"3. Ask the user for missing inputs only when necessary.",
|
|
2807
|
+
"4. Do not modify files not required by the canonical skill.",
|
|
2808
|
+
"5. All outputs must match the user's language.",
|
|
2809
|
+
"",
|
|
2810
|
+
"User notes: ${input:notes:Optional}",
|
|
2811
|
+
""
|
|
2812
|
+
].join("\n");
|
|
2813
|
+
}
|
|
2814
|
+
function buildCodexSkillWrapper(skillId) {
|
|
2815
|
+
return [
|
|
2816
|
+
"---",
|
|
2817
|
+
`name: "${skillId}"`,
|
|
2818
|
+
`description: "QFAI: ${skillId} (Codex skill wrapper)"`,
|
|
2819
|
+
"---",
|
|
2820
|
+
"",
|
|
2821
|
+
`# ${skillId}`,
|
|
2822
|
+
"",
|
|
2823
|
+
"This skill is a thin wrapper that forwards to the canonical QFAI skill in this repository:",
|
|
2824
|
+
"",
|
|
2825
|
+
`- .qfai/assistant/skills/${skillId}/SKILL.md`,
|
|
2826
|
+
"",
|
|
2827
|
+
"How to invoke (Codex CLI):",
|
|
2828
|
+
"",
|
|
2829
|
+
`- Select the \`${skillId}\` skill, or reference it by name and provide your request.`,
|
|
2830
|
+
"",
|
|
2831
|
+
"Instructions:",
|
|
2832
|
+
"",
|
|
2833
|
+
"1. Read the skill document above and follow it precisely.",
|
|
2834
|
+
"2. Use the repository as the source of truth (tools, frameworks, directory structure).",
|
|
2835
|
+
"3. Ensure all outputs match the user's language.",
|
|
2836
|
+
""
|
|
2837
|
+
].join("\n");
|
|
2838
|
+
}
|
|
2839
|
+
function buildClaudeAgentWrapper(agentName) {
|
|
2840
|
+
const title = formatDisplayName(agentName);
|
|
2841
|
+
return [
|
|
2842
|
+
`# ${title} (Claude Code wrapper)`,
|
|
2843
|
+
"",
|
|
2844
|
+
"## Purpose",
|
|
2845
|
+
"",
|
|
2846
|
+
`This is a thin wrapper for Claude Code agents. The canonical role card lives in .qfai/assistant/agents/${agentName}.md.`,
|
|
2847
|
+
"",
|
|
2848
|
+
"## Rules",
|
|
2849
|
+
"",
|
|
2850
|
+
"- Always follow the .qfai role card and instructions first.",
|
|
2851
|
+
"- If this file conflicts with .qfai, the .qfai content wins.",
|
|
2852
|
+
"- Do not proceed without reading the role card.",
|
|
2853
|
+
"",
|
|
2854
|
+
"## Minimal steps",
|
|
2855
|
+
"",
|
|
2856
|
+
`1. Read .qfai/assistant/agents/${agentName}.md and follow its output format.`,
|
|
2857
|
+
"2. Use .qfai/assistant/steering/ and .qfai/assistant/instructions/ as required context.",
|
|
2858
|
+
"3. List unknowns as Open Questions; do not mix them with decisions.",
|
|
2859
|
+
""
|
|
2860
|
+
].join("\n");
|
|
2861
|
+
}
|
|
2862
|
+
function buildGithubAgentWrapper(agentName) {
|
|
2863
|
+
const title = formatDisplayName(agentName);
|
|
2864
|
+
return [
|
|
2865
|
+
`# ${title} (GitHub Copilot Custom wrapper)`,
|
|
2866
|
+
"",
|
|
2867
|
+
"## Purpose",
|
|
2868
|
+
"",
|
|
2869
|
+
`This is a thin wrapper for GitHub Copilot Custom agents. The canonical role card lives in .qfai/assistant/agents/${agentName}.md.`,
|
|
2870
|
+
"",
|
|
2871
|
+
"## Rules",
|
|
2872
|
+
"",
|
|
2873
|
+
"- Always follow the .qfai role card and instructions first.",
|
|
2874
|
+
"- If this file conflicts with .qfai, the .qfai content wins.",
|
|
2875
|
+
"- Do not proceed without reading the role card.",
|
|
2876
|
+
"",
|
|
2877
|
+
"## Minimal steps",
|
|
2878
|
+
"",
|
|
2879
|
+
`1. Read .qfai/assistant/agents/${agentName}.md and follow its output format.`,
|
|
2880
|
+
"2. Use .qfai/assistant/steering/ and .qfai/assistant/instructions/ as required context.",
|
|
2881
|
+
"3. List unknowns as Open Questions; do not mix them with decisions.",
|
|
2882
|
+
""
|
|
2883
|
+
].join("\n");
|
|
2884
|
+
}
|
|
2885
|
+
function formatDisplayName(raw) {
|
|
2886
|
+
return raw.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
2887
|
+
}
|
|
2508
2888
|
|
|
2509
2889
|
// src/cli/commands/report.ts
|
|
2510
2890
|
var import_promises22 = require("fs/promises");
|