@axiomatic-labs/claudeflow 2.13.17 → 2.13.18

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.
Files changed (2) hide show
  1. package/lib/install.js +65 -11
  2. package/package.json +1 -1
package/lib/install.js CHANGED
@@ -154,7 +154,7 @@ async function run() {
154
154
  copyDirSync(srcTemplates, dstTemplates);
155
155
  }
156
156
  materializeSharedAppendPrompt(cwd);
157
- materializeDefaultClaudeMd(cwd);
157
+ ensureDefaultClaudeMdRules(cwd);
158
158
  propagateTemplateRules(cwd, srcTemplates);
159
159
 
160
160
  // Copy template agents (only template-managed, preserve user agents)
@@ -991,17 +991,67 @@ function materializeSharedAppendPrompt(projectRoot) {
991
991
  fs.copyFileSync(templatePath, outputPath);
992
992
  }
993
993
 
994
- // Materialize the default CLAUDE.md only when the project has none.
995
- // CLAUDE.md is user-owned: customizations made by the user (or by other
996
- // frameworks) must survive `claudeflow update`. We therefore never
997
- // overwrite an existing file.
998
- function materializeDefaultClaudeMd(projectRoot) {
994
+ // Inject the canonical claudeflow rules block into the project's CLAUDE.md.
995
+ //
996
+ // Three cases:
997
+ // 1. CLAUDE.md absent create with the block plus user-content section
998
+ // 2. CLAUDE.md present, no sentinel, no opt-out → prepend the block,
999
+ // preserving every byte of the existing file
1000
+ // 3. CLAUDE.md present with sentinel → replace the block in place
1001
+ // (keeps future template revisions propagating idempotently)
1002
+ //
1003
+ // User opt-out: drop the literal string `claudeflow:default-rules:opt-out`
1004
+ // anywhere in CLAUDE.md (e.g. in a comment) to skip injection forever.
1005
+ //
1006
+ // The block is wrapped in HTML comments — markdown renders them invisibly
1007
+ // but they survive editor round-trips and remain machine-detectable.
1008
+ const CLAUDE_MD_INJECT_BEGIN = '<!-- claudeflow:default-rules:start (do not edit between markers) -->';
1009
+ const CLAUDE_MD_INJECT_END = '<!-- claudeflow:default-rules:end -->';
1010
+ const CLAUDE_MD_OPT_OUT = 'claudeflow:default-rules:opt-out';
1011
+
1012
+ function buildDefaultRulesBlock(projectRoot) {
999
1013
  const templatePath = path.join(projectRoot, DEFAULT_CLAUDE_MD_TEMPLATE);
1000
- if (!fs.existsSync(templatePath)) return false;
1014
+ if (!fs.existsSync(templatePath)) return null;
1015
+ let body = fs.readFileSync(templatePath, 'utf8');
1016
+ // Strip the leading `# CLAUDE.md` heading — when injected into an existing
1017
+ // file the heading would conflict with the user's own H1.
1018
+ body = body.replace(/^\s*#\s*CLAUDE\.md\s*\r?\n+/, '').trimEnd();
1019
+ return `${CLAUDE_MD_INJECT_BEGIN}\n${body}\n${CLAUDE_MD_INJECT_END}`;
1020
+ }
1021
+
1022
+ function ensureDefaultClaudeMdRules(projectRoot) {
1023
+ const block = buildDefaultRulesBlock(projectRoot);
1024
+ if (!block) return { action: 'no-template' };
1025
+
1001
1026
  const outputPath = path.join(projectRoot, DEFAULT_CLAUDE_MD_OUTPUT);
1002
- if (fs.existsSync(outputPath)) return false;
1003
- fs.copyFileSync(templatePath, outputPath);
1004
- return true;
1027
+
1028
+ if (!fs.existsSync(outputPath)) {
1029
+ fs.writeFileSync(outputPath, `${block}\n`);
1030
+ return { action: 'created' };
1031
+ }
1032
+
1033
+ const existing = fs.readFileSync(outputPath, 'utf8');
1034
+
1035
+ if (existing.includes(CLAUDE_MD_OPT_OUT)) {
1036
+ return { action: 'opt-out' };
1037
+ }
1038
+
1039
+ const beginIdx = existing.indexOf(CLAUDE_MD_INJECT_BEGIN);
1040
+ const endIdx = existing.indexOf(CLAUDE_MD_INJECT_END);
1041
+ if (beginIdx !== -1 && endIdx !== -1 && endIdx > beginIdx) {
1042
+ const before = existing.slice(0, beginIdx);
1043
+ const after = existing.slice(endIdx + CLAUDE_MD_INJECT_END.length);
1044
+ const next = `${before}${block}${after}`;
1045
+ if (next === existing) return { action: 'unchanged' };
1046
+ fs.writeFileSync(outputPath, next);
1047
+ return { action: 'updated' };
1048
+ }
1049
+
1050
+ // First injection into a pre-existing CLAUDE.md — prepend, keep the
1051
+ // user's content verbatim below.
1052
+ const separator = existing.startsWith('\n') ? '\n' : '\n\n';
1053
+ fs.writeFileSync(outputPath, `${block}${separator}${existing}`);
1054
+ return { action: 'injected' };
1005
1055
  }
1006
1056
 
1007
1057
  function isTemplateManagedAgent(agentName) {
@@ -1036,7 +1086,11 @@ function copyDirSync(src, dst, skipNames) {
1036
1086
  module.exports = Object.assign(run, {
1037
1087
  assertRequiredTemplateRules,
1038
1088
  materializeSharedAppendPrompt,
1039
- materializeDefaultClaudeMd,
1089
+ ensureDefaultClaudeMdRules,
1090
+ buildDefaultRulesBlock,
1091
+ CLAUDE_MD_INJECT_BEGIN,
1092
+ CLAUDE_MD_INJECT_END,
1093
+ CLAUDE_MD_OPT_OUT,
1040
1094
  isTemplateManagedAgent,
1041
1095
  listTemplateManagedAgents,
1042
1096
  mergeClaudeSettings,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiomatic-labs/claudeflow",
3
- "version": "2.13.17",
3
+ "version": "2.13.18",
4
4
  "description": "Claudeflow — AI-powered development toolkit for Claude Code. Skills, agents, hooks, and quality gates that ship production apps.",
5
5
  "bin": {
6
6
  "claudeflow": "./bin/cli.js"