@contextforge/core 0.1.5 → 0.1.7

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 CHANGED
@@ -365,9 +365,9 @@ declare function installPack(projectRoot: string, registryUrl: string, packName:
365
365
 
366
366
  declare function compileOutputs(config: ContextForgeConfig, packs: InstalledPack[], analysis: ProjectAnalysis): GeneratedFile[];
367
367
 
368
- declare function compileAgentsMd(root: string, packs: InstalledPack[]): Promise<string>;
368
+ declare function compileAgentsMd(): Promise<string>;
369
369
 
370
- declare function compileClaudeMd(root: string, packs: InstalledPack[]): Promise<string>;
370
+ declare function compileClaudeMd(): Promise<string>;
371
371
 
372
372
  declare function generateToolOutputs(root: string, packs: InstalledPack[], config: ContextForgeConfig, previousFiles?: string[]): Promise<string[]>;
373
373
 
package/dist/index.js CHANGED
@@ -657,6 +657,44 @@ async function installPack(projectRoot, registryUrl, packName, options = {}) {
657
657
  };
658
658
  }
659
659
 
660
+ // src/fs/updateGeneratedBlock.ts
661
+ var GENERATED_BLOCK_START = "<!-- contextforge:start -->";
662
+ var GENERATED_BLOCK_END = "<!-- contextforge:end -->";
663
+ var blockPattern = new RegExp(
664
+ `${escapeRegExp(GENERATED_BLOCK_START)}[\\s\\S]*?${escapeRegExp(GENERATED_BLOCK_END)}`,
665
+ "m"
666
+ );
667
+ function escapeRegExp(value) {
668
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
669
+ }
670
+ function getGeneratedBlock(content) {
671
+ return content.match(blockPattern)?.[0] ?? null;
672
+ }
673
+ function removeGeneratedBlock(content) {
674
+ return content.replace(blockPattern, "").replace(/\s+$/u, "");
675
+ }
676
+ function wrapGeneratedBlock(content) {
677
+ return `${GENERATED_BLOCK_START}
678
+ ${content.trim()}
679
+ ${GENERATED_BLOCK_END}`;
680
+ }
681
+ function updateGeneratedBlock(existingContent, generatedContent) {
682
+ const existing = existingContent?.replace(/\s+$/u, "") ?? "";
683
+ const generatedBlock = getGeneratedBlock(generatedContent) ?? wrapGeneratedBlock(generatedContent);
684
+ if (!existing) {
685
+ return `${generatedContent.includes(GENERATED_BLOCK_START) ? generatedContent.trim() : generatedBlock}
686
+ `;
687
+ }
688
+ if (blockPattern.test(existing)) {
689
+ return `${existing.replace(blockPattern, generatedBlock)}
690
+ `;
691
+ }
692
+ return `${existing}
693
+
694
+ ${generatedBlock}
695
+ `;
696
+ }
697
+
660
698
  // src/compiler/compileOutputs.ts
661
699
  function normalizeOutputPath(outputPath) {
662
700
  return outputPath.split(/[\\/]/u).join("/");
@@ -664,11 +702,11 @@ function normalizeOutputPath(outputPath) {
664
702
  function defaultOutput(packName, type) {
665
703
  const defaults = {
666
704
  rules: null,
667
- agents: `.contextforge/instructions/agents/${packName}.md`,
668
- claude: `.contextforge/instructions/claude/${packName}.md`,
669
- skill: `.agents/skills/${packName}/SKILL.md`,
670
- cursor: `.cursor/rules/${packName}.mdc`,
671
- copilot: `.github/instructions/${packName}.instructions.md`
705
+ agents: `.contextforge/agents/codex/${packName}.md`,
706
+ claude: `.contextforge/agents/claude/${packName}.md`,
707
+ skill: `.contextforge/skills/${packName}/SKILL.md`,
708
+ cursor: `.contextforge/agents/cursor/${packName}.md`,
709
+ copilot: `.contextforge/agents/copilot/${packName}.md`
672
710
  };
673
711
  return defaults[type];
674
712
  }
@@ -680,7 +718,7 @@ function shouldGenerateFile(type, tools) {
680
718
  return tools.includes("claude");
681
719
  }
682
720
  if (type === "skill") {
683
- return tools.includes("codex") || tools.includes("claude");
721
+ return tools.length > 0;
684
722
  }
685
723
  if (type === "cursor") {
686
724
  return tools.includes("cursor");
@@ -691,67 +729,37 @@ function shouldGenerateFile(type, tools) {
691
729
  return false;
692
730
  }
693
731
  function compileRootAgents(packs, analysis) {
694
- const summaries = packs.map((pack) => pack.files.agents?.trim()).filter((summary) => Boolean(summary));
695
- const gitSafetyWarning = packs.some((pack) => pack.manifest.name === "git-workflow") ? [
696
- "## Git Safety",
697
- "",
698
- "Do not commit, push, merge, rebase, reset, delete branches, or rewrite history unless explicitly requested by the user.",
699
- ""
700
- ] : [];
701
732
  return [
702
733
  "# Project Agent Instructions",
703
734
  "",
704
- "Generated by ContextForge from installed instruction packs.",
705
- "",
706
- "## Core Behavior",
707
- "",
708
- summaries.length > 0 ? summaries.join("\n\n") : "No pack-specific agent summaries are installed yet.",
709
- "",
710
- ...gitSafetyWarning,
711
- "## Installed Packs",
712
- "",
713
- ...packs.length > 0 ? packs.map((pack) => `- ${pack.manifest.name}: ${pack.manifest.title}`) : ["- No packs installed"],
714
- "",
715
- "## Detected Project",
735
+ GENERATED_BLOCK_START,
736
+ "ContextForge is installed for this repo.",
716
737
  "",
717
- `- Framework: ${analysis.framework}`,
718
- `- Language: ${analysis.language}`,
719
- `- Package manager: ${analysis.packageManager}`,
738
+ "Before working, read the relevant instruction files in:",
720
739
  "",
721
- "## Notes",
740
+ "- `.contextforge/agents/codex/`",
741
+ "- `.contextforge/skills/`",
722
742
  "",
723
- "Detailed Codex skills live in `.agents/skills`.",
724
- "Pack sources live in `.contextforge/packs`.",
725
- "Pack-specific agent summaries live in `.contextforge/instructions/agents`."
743
+ "Follow the installed packs listed in `.contextforge/config.json`.",
744
+ "Do not copy these instructions into this file.",
745
+ GENERATED_BLOCK_END
726
746
  ].join("\n");
727
747
  }
728
748
  function compileRootClaude(packs) {
729
- const summaries = packs.map((pack) => pack.files.claude?.trim()).filter((summary) => Boolean(summary));
730
- const gitSafetyWarning = packs.some((pack) => pack.manifest.name === "git-workflow") ? [
731
- "## Git Safety",
732
- "",
733
- "Do not commit, push, merge, rebase, reset, delete branches, or rewrite history unless explicitly requested by the user.",
734
- ""
735
- ] : [];
736
749
  return [
737
750
  "# Claude Code Instructions",
738
751
  "",
739
- "Generated by ContextForge from installed instruction packs.",
740
- "",
741
- "## Core Behavior",
742
- "",
743
- summaries.length > 0 ? summaries.join("\n\n") : "No pack-specific Claude summaries are installed yet.",
744
- "",
745
- ...gitSafetyWarning,
746
- "## Installed Packs",
752
+ GENERATED_BLOCK_START,
753
+ "ContextForge is installed for this repo.",
747
754
  "",
748
- ...packs.length > 0 ? packs.map((pack) => `- ${pack.manifest.name}: ${pack.manifest.title}`) : ["- No packs installed"],
755
+ "Before working, read the relevant instruction files in:",
749
756
  "",
750
- "## Notes",
757
+ "- `.contextforge/agents/claude/`",
758
+ "- `.contextforge/skills/`",
751
759
  "",
752
- "Keep this file concise.",
753
- "Detailed pack sources live in `.contextforge/packs`.",
754
- "Pack-specific Claude summaries live in `.contextforge/instructions/claude`."
760
+ "Follow the installed packs listed in `.contextforge/config.json`.",
761
+ "Do not copy these instructions into this file.",
762
+ GENERATED_BLOCK_END
755
763
  ].join("\n");
756
764
  }
757
765
  function compileOutputs(config, packs, analysis) {
@@ -762,13 +770,13 @@ function compileOutputs(config, packs, analysis) {
762
770
  continue;
763
771
  }
764
772
  const content = pack.files[file.type];
765
- const outputPath = file.output ?? defaultOutput(pack.manifest.name, file.type);
773
+ const outputPath = defaultOutput(pack.manifest.name, file.type);
766
774
  if (!content || !outputPath) {
767
775
  continue;
768
776
  }
769
777
  outputs.push({
770
778
  path: normalizeOutputPath(outputPath),
771
- content
779
+ content: withContextForgePreamble(pack, file.type, content)
772
780
  });
773
781
  }
774
782
  }
@@ -780,176 +788,94 @@ function compileOutputs(config, packs, analysis) {
780
788
  }
781
789
  return outputs;
782
790
  }
783
-
784
- // src/compiler/compileAgentsMd.ts
785
- import path14 from "path";
786
- import fs13 from "fs-extra";
787
-
788
- // src/fs/updateGeneratedBlock.ts
789
- var GENERATED_BLOCK_START = "<!-- contextforge:start -->";
790
- var GENERATED_BLOCK_END = "<!-- contextforge:end -->";
791
- var blockPattern = new RegExp(
792
- `${escapeRegExp(GENERATED_BLOCK_START)}[\\s\\S]*?${escapeRegExp(GENERATED_BLOCK_END)}`,
793
- "m"
794
- );
795
- function escapeRegExp(value) {
796
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
797
- }
798
- function getGeneratedBlock(content) {
799
- return content.match(blockPattern)?.[0] ?? null;
800
- }
801
- function removeGeneratedBlock(content) {
802
- return content.replace(blockPattern, "").replace(/\s+$/u, "");
803
- }
804
- function wrapGeneratedBlock(content) {
805
- return `${GENERATED_BLOCK_START}
806
- ${content.trim()}
807
- ${GENERATED_BLOCK_END}`;
808
- }
809
- function updateGeneratedBlock(existingContent, generatedContent) {
810
- const existing = existingContent?.replace(/\s+$/u, "") ?? "";
811
- const generatedBlock = getGeneratedBlock(generatedContent) ?? wrapGeneratedBlock(generatedContent);
812
- if (!existing) {
813
- return `${generatedContent.includes(GENERATED_BLOCK_START) ? generatedContent.trim() : generatedBlock}
814
- `;
791
+ function withContextForgePreamble(pack, type, content) {
792
+ if (pack.manifest.name !== "git-workflow" || !["agents", "claude", "cursor", "copilot"].includes(type)) {
793
+ return content;
815
794
  }
816
- if (blockPattern.test(existing)) {
817
- return `${existing.replace(blockPattern, generatedBlock)}
818
- `;
795
+ const warning = "Do not commit, push, merge, rebase, reset, delete branches, or rewrite history unless explicitly requested by the user.";
796
+ if (content.includes(warning)) {
797
+ return content;
819
798
  }
820
- return `${existing}
821
-
822
- ${generatedBlock}
823
- `;
799
+ return ["# ContextForge Git Safety", "", warning, "", content.trim()].join("\n");
824
800
  }
825
801
 
826
802
  // src/compiler/compileAgentsMd.ts
827
- var AGENTS_INSTRUCTIONS_DIR = ".contextforge/instructions/agents";
828
- async function readInstructionSummaries(root) {
829
- const directory = path14.join(root, AGENTS_INSTRUCTIONS_DIR);
830
- if (!await fs13.pathExists(directory)) {
831
- return [];
832
- }
833
- const files = (await fs13.readdir(directory)).filter((file) => file.endsWith(".md")).sort((a, b) => a.localeCompare(b));
834
- return Promise.all(files.map((file) => fs13.readFile(path14.join(directory, file), "utf8")));
835
- }
836
- async function compileAgentsMd(root, packs) {
837
- const summaries = await readInstructionSummaries(root);
838
- const gitSafetyWarning = packs.some((pack) => pack.manifest.name === "git-workflow") ? [
839
- "## Git Safety",
840
- "",
841
- "Do not commit, push, merge, rebase, reset, delete branches, or rewrite history unless explicitly requested by the user.",
842
- ""
843
- ] : [];
803
+ async function compileAgentsMd() {
844
804
  return [
845
805
  "# Project Agent Instructions",
846
806
  "",
847
807
  GENERATED_BLOCK_START,
848
- "Generated by ContextForge from installed instruction packs.",
849
- "",
850
- "## Core Behavior",
851
- "",
852
- summaries.length > 0 ? summaries.map((summary) => summary.trim()).join("\n\n") : "No pack-specific agent summaries are installed yet.",
808
+ "ContextForge is installed for this repo.",
853
809
  "",
854
- ...gitSafetyWarning,
855
- "## Installed Packs",
810
+ "Before working, read the relevant instruction files in:",
856
811
  "",
857
- ...packs.length > 0 ? packs.map((pack) => `- ${pack.manifest.name}: ${pack.manifest.title}`) : ["- No packs installed"],
858
- "",
859
- "## Notes",
860
- "",
861
- "Detailed Codex skills live in `.agents/skills`.",
862
- "Pack sources live in `.contextforge/packs`.",
863
- "Pack-specific agent summaries live in `.contextforge/instructions/agents`.",
812
+ "- `.contextforge/agents/codex/`",
813
+ "- `.contextforge/skills/`",
864
814
  "",
815
+ "Follow the installed packs listed in `.contextforge/config.json`.",
816
+ "Do not copy these instructions into this file.",
865
817
  GENERATED_BLOCK_END
866
818
  ].join("\n");
867
819
  }
868
820
 
869
821
  // src/compiler/compileClaudeMd.ts
870
- import path15 from "path";
871
- import fs14 from "fs-extra";
872
- var CLAUDE_INSTRUCTIONS_DIR = ".contextforge/instructions/claude";
873
- async function readInstructionSummaries2(root) {
874
- const directory = path15.join(root, CLAUDE_INSTRUCTIONS_DIR);
875
- if (!await fs14.pathExists(directory)) {
876
- return [];
877
- }
878
- const files = (await fs14.readdir(directory)).filter((file) => file.endsWith(".md")).sort((a, b) => a.localeCompare(b));
879
- return Promise.all(files.map((file) => fs14.readFile(path15.join(directory, file), "utf8")));
880
- }
881
- async function compileClaudeMd(root, packs) {
882
- const summaries = await readInstructionSummaries2(root);
883
- const gitSafetyWarning = packs.some((pack) => pack.manifest.name === "git-workflow") ? [
884
- "## Git Safety",
885
- "",
886
- "Do not commit, push, merge, rebase, reset, delete branches, or rewrite history unless explicitly requested by the user.",
887
- ""
888
- ] : [];
822
+ async function compileClaudeMd() {
889
823
  return [
890
824
  "# Claude Code Instructions",
891
825
  "",
892
826
  GENERATED_BLOCK_START,
893
- "Generated by ContextForge from installed instruction packs.",
827
+ "ContextForge is installed for this repo.",
894
828
  "",
895
- "## Core Behavior",
829
+ "Before working, read the relevant instruction files in:",
896
830
  "",
897
- summaries.length > 0 ? summaries.map((summary) => summary.trim()).join("\n\n") : "No pack-specific Claude summaries are installed yet.",
898
- "",
899
- ...gitSafetyWarning,
900
- "## Installed Packs",
901
- "",
902
- ...packs.length > 0 ? packs.map((pack) => `- ${pack.manifest.name}: ${pack.manifest.title}`) : ["- No packs installed"],
903
- "",
904
- "## Notes",
905
- "",
906
- "Keep this file concise.",
907
- "Detailed pack sources live in `.contextforge/packs`.",
908
- "Pack-specific Claude summaries live in `.contextforge/instructions/claude`.",
831
+ "- `.contextforge/agents/claude/`",
832
+ "- `.contextforge/skills/`",
909
833
  "",
834
+ "Follow the installed packs listed in `.contextforge/config.json`.",
835
+ "Do not copy these instructions into this file.",
910
836
  GENERATED_BLOCK_END
911
837
  ].join("\n");
912
838
  }
913
839
 
914
840
  // src/compiler/generateToolOutputs.ts
915
- import path18 from "path";
916
- import fs17 from "fs-extra";
917
-
918
- // src/fs/safeWriteFile.ts
919
841
  import path16 from "path";
920
842
  import fs15 from "fs-extra";
843
+
844
+ // src/fs/safeWriteFile.ts
845
+ import path14 from "path";
846
+ import fs13 from "fs-extra";
921
847
  async function safeWriteFile(filePath, generatedContent) {
922
- const existingContent = await fs15.pathExists(filePath) ? await fs15.readFile(filePath, "utf8") : null;
848
+ const existingContent = await fs13.pathExists(filePath) ? await fs13.readFile(filePath, "utf8") : null;
923
849
  const nextContent = updateGeneratedBlock(existingContent, generatedContent);
924
- await fs15.ensureDir(path16.dirname(filePath));
925
- await fs15.writeFile(filePath, nextContent);
850
+ await fs13.ensureDir(path14.dirname(filePath));
851
+ await fs13.writeFile(filePath, nextContent);
926
852
  }
927
853
 
928
854
  // src/compiler/cleanupGeneratedFiles.ts
929
- import path17 from "path";
930
- import fs16 from "fs-extra";
855
+ import path15 from "path";
856
+ import fs14 from "fs-extra";
931
857
  function isGeneratedOnlyPath(relativePath) {
932
- return relativePath.startsWith(".agents/skills/") || relativePath.startsWith(".cursor/rules/");
858
+ return relativePath.startsWith(".agents/skills/") || relativePath.startsWith(".cursor/rules/") || relativePath.startsWith(".github/instructions/") || relativePath.startsWith(".contextforge/instructions/") || relativePath.startsWith(".contextforge/agents/") || relativePath.startsWith(".contextforge/skills/");
933
859
  }
934
860
  async function cleanupFile(root, relativePath) {
935
- const filePath = path17.join(root, relativePath);
936
- if (!await fs16.pathExists(filePath)) {
861
+ const filePath = path15.join(root, relativePath);
862
+ if (!await fs14.pathExists(filePath)) {
937
863
  return;
938
864
  }
939
865
  if (isGeneratedOnlyPath(relativePath)) {
940
- await fs16.remove(filePath);
866
+ await fs14.remove(filePath);
941
867
  return;
942
868
  }
943
- const content = await fs16.readFile(filePath, "utf8");
869
+ const content = await fs14.readFile(filePath, "utf8");
944
870
  if (!getGeneratedBlock(content)) {
945
871
  return;
946
872
  }
947
873
  const nextContent = removeGeneratedBlock(content);
948
874
  if (nextContent.trim().length === 0) {
949
- await fs16.remove(filePath);
875
+ await fs14.remove(filePath);
950
876
  return;
951
877
  }
952
- await fs16.writeFile(filePath, `${nextContent}
878
+ await fs14.writeFile(filePath, `${nextContent}
953
879
  `);
954
880
  }
955
881
  async function cleanupStaleGeneratedFiles(root, previousFiles, currentFiles) {
@@ -962,10 +888,8 @@ async function cleanupStaleGeneratedFiles(root, previousFiles, currentFiles) {
962
888
 
963
889
  // src/compiler/generateToolOutputs.ts
964
890
  var GENERATED_ONLY_PREFIXES = [
965
- ".agents/skills/",
966
- ".cursor/rules/",
967
- ".github/instructions/",
968
- ".contextforge/instructions/"
891
+ ".contextforge/agents/",
892
+ ".contextforge/skills/"
969
893
  ];
970
894
  function normalizeOutputPath2(outputPath) {
971
895
  return outputPath.split(/[\\/]/u).join("/");
@@ -973,11 +897,11 @@ function normalizeOutputPath2(outputPath) {
973
897
  function defaultOutput2(packName, type) {
974
898
  const defaults = {
975
899
  rules: null,
976
- agents: `.contextforge/instructions/agents/${packName}.md`,
977
- claude: `.contextforge/instructions/claude/${packName}.md`,
978
- skill: `.agents/skills/${packName}/SKILL.md`,
979
- cursor: `.cursor/rules/${packName}.mdc`,
980
- copilot: `.github/instructions/${packName}.instructions.md`
900
+ agents: `.contextforge/agents/codex/${packName}.md`,
901
+ claude: `.contextforge/agents/claude/${packName}.md`,
902
+ skill: `.contextforge/skills/${packName}/SKILL.md`,
903
+ cursor: `.contextforge/agents/cursor/${packName}.md`,
904
+ copilot: `.contextforge/agents/copilot/${packName}.md`
981
905
  };
982
906
  return defaults[type];
983
907
  }
@@ -989,7 +913,7 @@ function shouldGenerateFile2(type, tools) {
989
913
  return tools.includes("claude");
990
914
  }
991
915
  if (type === "skill") {
992
- return tools.includes("codex") || tools.includes("claude");
916
+ return tools.length > 0;
993
917
  }
994
918
  if (type === "cursor") {
995
919
  return tools.includes("cursor");
@@ -1001,19 +925,29 @@ function shouldGenerateFile2(type, tools) {
1001
925
  }
1002
926
  function generatedFileFromPack(pack, file) {
1003
927
  const content = pack.files[file.type];
1004
- const outputPath = file.output ?? defaultOutput2(pack.manifest.name, file.type);
928
+ const outputPath = defaultOutput2(pack.manifest.name, file.type);
1005
929
  if (!content || !outputPath) {
1006
930
  return null;
1007
931
  }
1008
932
  return {
1009
933
  path: normalizeOutputPath2(outputPath),
1010
- content
934
+ content: withContextForgePreamble2(pack, file.type, content)
1011
935
  };
1012
936
  }
937
+ function withContextForgePreamble2(pack, type, content) {
938
+ if (pack.manifest.name !== "git-workflow" || !["agents", "claude", "cursor", "copilot"].includes(type)) {
939
+ return content;
940
+ }
941
+ const warning = "Do not commit, push, merge, rebase, reset, delete branches, or rewrite history unless explicitly requested by the user.";
942
+ if (content.includes(warning)) {
943
+ return content;
944
+ }
945
+ return ["# ContextForge Git Safety", "", warning, "", content.trim()].join("\n");
946
+ }
1013
947
  async function writeGeneratedOnlyFile(root, output) {
1014
- const filePath = path18.join(root, output.path);
1015
- await fs17.ensureDir(path18.dirname(filePath));
1016
- await fs17.writeFile(filePath, output.content.endsWith("\n") ? output.content : `${output.content}
948
+ const filePath = path16.join(root, output.path);
949
+ await fs15.ensureDir(path16.dirname(filePath));
950
+ await fs15.writeFile(filePath, output.content.endsWith("\n") ? output.content : `${output.content}
1017
951
  `);
1018
952
  }
1019
953
  function packOutputs(pack, tools) {
@@ -1026,18 +960,18 @@ async function generateToolOutputs(root, packs, config, previousFiles = []) {
1026
960
  if (isGeneratedOnly) {
1027
961
  await writeGeneratedOnlyFile(root, output);
1028
962
  } else {
1029
- await safeWriteFile(path18.join(root, output.path), output.content);
963
+ await safeWriteFile(path16.join(root, output.path), output.content);
1030
964
  }
1031
965
  }
1032
966
  const rootOutputs = [];
1033
967
  if (config.tools.includes("codex")) {
1034
- rootOutputs.push({ path: "AGENTS.md", content: await compileAgentsMd(root, packs) });
968
+ rootOutputs.push({ path: "AGENTS.md", content: await compileAgentsMd() });
1035
969
  }
1036
970
  if (config.tools.includes("claude")) {
1037
- rootOutputs.push({ path: "CLAUDE.md", content: await compileClaudeMd(root, packs) });
971
+ rootOutputs.push({ path: "CLAUDE.md", content: await compileClaudeMd() });
1038
972
  }
1039
973
  for (const output of rootOutputs) {
1040
- await safeWriteFile(path18.join(root, output.path), output.content);
974
+ await safeWriteFile(path16.join(root, output.path), output.content);
1041
975
  }
1042
976
  const currentFiles = [...packGeneratedOutputs, ...rootOutputs].map((output) => output.path);
1043
977
  await cleanupStaleGeneratedFiles(root, previousFiles, currentFiles);
@@ -1045,19 +979,19 @@ async function generateToolOutputs(root, packs, config, previousFiles = []) {
1045
979
  }
1046
980
 
1047
981
  // src/compiler/writeGeneratedFiles.ts
1048
- import path19 from "path";
982
+ import path17 from "path";
1049
983
  async function writeGeneratedFiles(root, outputs, previousFiles = []) {
1050
984
  const currentFiles = outputs.map((output) => output.path);
1051
985
  for (const output of outputs) {
1052
- await safeWriteFile(path19.join(root, output.path), output.content);
986
+ await safeWriteFile(path17.join(root, output.path), output.content);
1053
987
  }
1054
988
  await cleanupStaleGeneratedFiles(root, previousFiles, currentFiles);
1055
989
  return currentFiles;
1056
990
  }
1057
991
 
1058
992
  // src/sync.ts
1059
- import path20 from "path";
1060
- import fs18 from "fs-extra";
993
+ import path18 from "path";
994
+ import fs16 from "fs-extra";
1061
995
  async function updateContextForgeConfig(projectRoot, packName, registryUrl, generatedFiles) {
1062
996
  const existing = await loadOptionalConfig(projectRoot);
1063
997
  const config = addPackToConfig(
@@ -1092,7 +1026,7 @@ async function loadInstalledPackFromRegistry(projectRoot, registryUrl, summary,
1092
1026
  return downloadPackToContextForge(projectRoot, manifest.name, manifest, packUrl, timeoutMs);
1093
1027
  }
1094
1028
  async function syncInstalledPacks(projectRoot, providedConfig) {
1095
- const root = path20.resolve(projectRoot);
1029
+ const root = path18.resolve(projectRoot);
1096
1030
  const analysis = await detectProject(root);
1097
1031
  const previousConfig = await loadOptionalConfig(root);
1098
1032
  const config = providedConfig ?? await loadConfig(root);
@@ -1144,7 +1078,7 @@ async function syncProject(root, providedConfig) {
1144
1078
  return syncInstalledPacks(root, providedConfig);
1145
1079
  }
1146
1080
  async function installPackAndSync(projectRoot, registryUrl, packName, options = {}) {
1147
- const root = path20.resolve(projectRoot);
1081
+ const root = path18.resolve(projectRoot);
1148
1082
  const config = await loadOptionalConfig(root);
1149
1083
  const result = await installPack(root, registryUrl, packName, options);
1150
1084
  if (result.alreadyInstalled && !options.force && config?.installedPacks.includes(packName)) {
@@ -1180,21 +1114,21 @@ async function readInstalledPacks(projectRoot) {
1180
1114
  return loadProjectPacks(projectRoot);
1181
1115
  }
1182
1116
  async function pathExists(root, relativePath) {
1183
- return fs18.pathExists(path20.join(root, relativePath));
1117
+ return fs16.pathExists(path18.join(root, relativePath));
1184
1118
  }
1185
1119
 
1186
1120
  // src/doctor/doctorProject.ts
1187
- import path21 from "path";
1188
- import fs19 from "fs-extra";
1121
+ import path19 from "path";
1122
+ import fs17 from "fs-extra";
1189
1123
  async function fileExists(root, relativePath) {
1190
- return fs19.pathExists(path21.join(root, relativePath));
1124
+ return fs17.pathExists(path19.join(root, relativePath));
1191
1125
  }
1192
1126
  async function readIfExists(root, relativePath) {
1193
- const filePath = path21.join(root, relativePath);
1194
- if (!await fs19.pathExists(filePath)) {
1127
+ const filePath = path19.join(root, relativePath);
1128
+ if (!await fs17.pathExists(filePath)) {
1195
1129
  return null;
1196
1130
  }
1197
- return fs19.readFile(filePath, "utf8");
1131
+ return fs17.readFile(filePath, "utf8");
1198
1132
  }
1199
1133
  function hasContextForgeBlock(content) {
1200
1134
  return Boolean(content && getGeneratedBlock(content));
@@ -1204,27 +1138,27 @@ function tooLarge(content) {
1204
1138
  }
1205
1139
  function expectedGeneratedOutputs(packName, tools) {
1206
1140
  const outputs = [];
1207
- if (tools.includes("codex") || tools.includes("claude")) {
1208
- outputs.push(`.agents/skills/${packName}/SKILL.md`);
1209
- }
1210
- if (tools.includes("cursor")) {
1211
- outputs.push(`.cursor/rules/${packName}.mdc`);
1212
- }
1213
- if (tools.includes("copilot")) {
1214
- outputs.push(`.github/instructions/${packName}.instructions.md`);
1141
+ if (tools.length > 0) {
1142
+ outputs.push(`.contextforge/skills/${packName}/SKILL.md`);
1215
1143
  }
1216
1144
  if (tools.includes("codex")) {
1217
- outputs.push(`.contextforge/instructions/agents/${packName}.md`);
1145
+ outputs.push(`.contextforge/agents/codex/${packName}.md`);
1218
1146
  }
1219
1147
  if (tools.includes("claude")) {
1220
- outputs.push(`.contextforge/instructions/claude/${packName}.md`);
1148
+ outputs.push(`.contextforge/agents/claude/${packName}.md`);
1149
+ }
1150
+ if (tools.includes("cursor")) {
1151
+ outputs.push(`.contextforge/agents/cursor/${packName}.md`);
1152
+ }
1153
+ if (tools.includes("copilot")) {
1154
+ outputs.push(`.contextforge/agents/copilot/${packName}.md`);
1221
1155
  }
1222
1156
  return outputs;
1223
1157
  }
1224
1158
  async function doctorProject(root, options = {}) {
1225
1159
  const checks = [];
1226
1160
  const issues = [];
1227
- const resolvedRoot = path21.resolve(root);
1161
+ const resolvedRoot = path19.resolve(root);
1228
1162
  if (!await fileExists(resolvedRoot, CONFIG_PATH)) {
1229
1163
  return {
1230
1164
  checks,
@@ -1324,7 +1258,18 @@ async function doctorProject(root, options = {}) {
1324
1258
  }
1325
1259
  const gitWorkflow = cachedPacks.find((pack) => pack.manifest.name === "git-workflow");
1326
1260
  if (gitWorkflow) {
1327
- const gitSummary = [gitWorkflow.files.agents, gitWorkflow.files.claude, agentsMd, claudeMd].filter((content) => Boolean(content)).join("\n").toLowerCase();
1261
+ const generatedGitFiles = await Promise.all(
1262
+ ["codex", "claude", "cursor", "copilot"].map(
1263
+ (tool) => readIfExists(resolvedRoot, `.contextforge/agents/${tool}/git-workflow.md`)
1264
+ )
1265
+ );
1266
+ const gitSummary = [
1267
+ gitWorkflow.files.agents,
1268
+ gitWorkflow.files.claude,
1269
+ ...generatedGitFiles,
1270
+ agentsMd,
1271
+ claudeMd
1272
+ ].filter((content) => Boolean(content)).join("\n").toLowerCase();
1328
1273
  if (!gitSummary.includes("do not commit") || !gitSummary.includes("push") || !gitSummary.includes("explicitly")) {
1329
1274
  issues.push({
1330
1275
  level: "warning",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contextforge/core",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",