oh-my-customcode 0.9.3 → 0.10.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.
Files changed (113) hide show
  1. package/README.md +19 -20
  2. package/dist/cli/index.js +1186 -877
  3. package/dist/index.js +281 -24
  4. package/package.json +1 -1
  5. package/templates/.claude/agents/arch-documenter.md +7 -71
  6. package/templates/.claude/agents/arch-speckit-agent.md +21 -108
  7. package/templates/.claude/agents/be-express-expert.md +8 -58
  8. package/templates/.claude/agents/be-nestjs-expert.md +6 -38
  9. package/templates/.claude/agents/be-springboot-expert.md +11 -56
  10. package/templates/.claude/agents/db-postgres-expert.md +10 -80
  11. package/templates/.claude/agents/db-redis-expert.md +10 -75
  12. package/templates/.claude/agents/db-supabase-expert.md +12 -48
  13. package/templates/.claude/agents/de-airflow-expert.md +8 -45
  14. package/templates/.claude/agents/de-dbt-expert.md +8 -46
  15. package/templates/.claude/agents/de-kafka-expert.md +10 -10
  16. package/templates/.claude/agents/de-pipeline-expert.md +9 -69
  17. package/templates/.claude/agents/de-snowflake-expert.md +9 -62
  18. package/templates/.claude/agents/de-spark-expert.md +10 -54
  19. package/templates/.claude/agents/fe-svelte-agent.md +5 -41
  20. package/templates/.claude/agents/fe-vercel-agent.md +9 -41
  21. package/templates/.claude/agents/fe-vuejs-agent.md +7 -42
  22. package/templates/.claude/agents/lang-java21-expert.md +11 -37
  23. package/templates/.claude/agents/mgr-claude-code-bible.md +22 -207
  24. package/templates/.claude/agents/mgr-creator.md +7 -88
  25. package/templates/.claude/agents/mgr-gitnerd.md +8 -76
  26. package/templates/.claude/agents/mgr-sauron.md +27 -20
  27. package/templates/.claude/agents/mgr-supplier.md +11 -96
  28. package/templates/.claude/agents/mgr-sync-checker.md +9 -70
  29. package/templates/.claude/agents/mgr-updater.md +9 -79
  30. package/templates/.claude/agents/qa-engineer.md +8 -72
  31. package/templates/.claude/agents/qa-planner.md +2 -3
  32. package/templates/.claude/agents/qa-writer.md +6 -76
  33. package/templates/.claude/agents/sys-memory-keeper.md +13 -87
  34. package/templates/.claude/agents/sys-naggy.md +9 -62
  35. package/templates/.claude/agents/tool-bun-expert.md +7 -52
  36. package/templates/.claude/agents/tool-npm-expert.md +6 -64
  37. package/templates/.claude/agents/tool-optimizer.md +7 -60
  38. package/templates/.claude/rules/MAY-optimization.md +16 -80
  39. package/templates/.claude/rules/MUST-agent-design.md +29 -134
  40. package/templates/.claude/rules/MUST-agent-identification.md +9 -88
  41. package/templates/.claude/rules/MUST-continuous-improvement.md +10 -117
  42. package/templates/.claude/rules/MUST-intent-transparency.md +14 -171
  43. package/templates/.claude/rules/MUST-language-policy.md +11 -46
  44. package/templates/.claude/rules/MUST-orchestrator-coordination.md +82 -425
  45. package/templates/.claude/rules/MUST-parallel-execution.md +33 -405
  46. package/templates/.claude/rules/MUST-permissions.md +14 -68
  47. package/templates/.claude/rules/MUST-safety.md +11 -57
  48. package/templates/.claude/rules/MUST-sync-verification.md +49 -205
  49. package/templates/.claude/rules/MUST-tool-identification.md +21 -134
  50. package/templates/.claude/rules/SHOULD-agent-teams.md +22 -166
  51. package/templates/.claude/rules/SHOULD-ecomode.md +15 -123
  52. package/templates/.claude/rules/SHOULD-error-handling.md +19 -88
  53. package/templates/.claude/rules/SHOULD-hud-statusline.md +9 -89
  54. package/templates/.claude/rules/SHOULD-interaction.md +18 -87
  55. package/templates/.claude/rules/SHOULD-memory-integration.md +25 -118
  56. package/templates/.claude/skills/dev-lead-routing/SKILL.md +70 -243
  57. package/templates/.claude/skills/springboot-best-practices/SKILL.md +180 -319
  58. package/templates/.codex/agents/arch-documenter.md +7 -71
  59. package/templates/.codex/agents/arch-speckit-agent.md +21 -108
  60. package/templates/.codex/agents/be-express-expert.md +8 -58
  61. package/templates/.codex/agents/be-nestjs-expert.md +6 -38
  62. package/templates/.codex/agents/be-springboot-expert.md +11 -56
  63. package/templates/.codex/agents/db-postgres-expert.md +10 -80
  64. package/templates/.codex/agents/db-redis-expert.md +10 -75
  65. package/templates/.codex/agents/db-supabase-expert.md +12 -48
  66. package/templates/.codex/agents/de-airflow-expert.md +8 -45
  67. package/templates/.codex/agents/de-dbt-expert.md +8 -46
  68. package/templates/.codex/agents/de-kafka-expert.md +10 -10
  69. package/templates/.codex/agents/de-pipeline-expert.md +9 -69
  70. package/templates/.codex/agents/de-snowflake-expert.md +9 -62
  71. package/templates/.codex/agents/de-spark-expert.md +10 -54
  72. package/templates/.codex/agents/fe-svelte-agent.md +5 -41
  73. package/templates/.codex/agents/fe-vercel-agent.md +9 -41
  74. package/templates/.codex/agents/fe-vuejs-agent.md +7 -42
  75. package/templates/.codex/agents/infra-aws-expert.md +1 -1
  76. package/templates/.codex/agents/infra-docker-expert.md +1 -1
  77. package/templates/.codex/agents/lang-java21-expert.md +11 -37
  78. package/templates/.codex/agents/mgr-claude-code-bible.md +25 -210
  79. package/templates/.codex/agents/mgr-creator.md +7 -88
  80. package/templates/.codex/agents/mgr-gitnerd.md +8 -76
  81. package/templates/.codex/agents/mgr-sauron.md +30 -23
  82. package/templates/.codex/agents/mgr-supplier.md +11 -96
  83. package/templates/.codex/agents/mgr-sync-checker.md +12 -73
  84. package/templates/.codex/agents/mgr-updater.md +9 -79
  85. package/templates/.codex/agents/qa-engineer.md +8 -72
  86. package/templates/.codex/agents/qa-planner.md +2 -3
  87. package/templates/.codex/agents/qa-writer.md +6 -76
  88. package/templates/.codex/agents/sys-memory-keeper.md +13 -87
  89. package/templates/.codex/agents/sys-naggy.md +9 -62
  90. package/templates/.codex/agents/tool-bun-expert.md +7 -52
  91. package/templates/.codex/agents/tool-npm-expert.md +6 -64
  92. package/templates/.codex/agents/tool-optimizer.md +7 -60
  93. package/templates/.codex/codex-native-hash.txt +1 -0
  94. package/templates/.codex/rules/MAY-optimization.md +16 -80
  95. package/templates/.codex/rules/MUST-agent-design.md +29 -134
  96. package/templates/.codex/rules/MUST-agent-identification.md +9 -88
  97. package/templates/.codex/rules/MUST-continuous-improvement.md +10 -117
  98. package/templates/.codex/rules/MUST-intent-transparency.md +14 -171
  99. package/templates/.codex/rules/MUST-language-policy.md +11 -46
  100. package/templates/.codex/rules/MUST-orchestrator-coordination.md +82 -425
  101. package/templates/.codex/rules/MUST-parallel-execution.md +33 -405
  102. package/templates/.codex/rules/MUST-permissions.md +14 -68
  103. package/templates/.codex/rules/MUST-safety.md +11 -57
  104. package/templates/.codex/rules/MUST-sync-verification.md +58 -214
  105. package/templates/.codex/rules/MUST-tool-identification.md +22 -135
  106. package/templates/.codex/rules/SHOULD-agent-teams.md +22 -166
  107. package/templates/.codex/rules/SHOULD-ecomode.md +15 -123
  108. package/templates/.codex/rules/SHOULD-error-handling.md +19 -88
  109. package/templates/.codex/rules/SHOULD-hud-statusline.md +9 -89
  110. package/templates/.codex/rules/SHOULD-interaction.md +18 -87
  111. package/templates/.codex/rules/SHOULD-memory-integration.md +25 -118
  112. package/templates/.codex/skills/dev-lead-routing/SKILL.md +70 -243
  113. package/templates/.codex/skills/springboot-best-practices/SKILL.md +180 -319
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
5
  import { join as join2 } from "node:path";
6
6
 
7
7
  // src/utils/fs.ts
8
- import { dirname, join, resolve } from "node:path";
8
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
9
9
  import { fileURLToPath } from "node:url";
10
10
  async function fileExists(path) {
11
11
  const fs = await import("node:fs/promises");
@@ -70,6 +70,25 @@ async function handleFile(srcPath, destPath, options, fs) {
70
70
  await fs.utimes(destPath, stats.atime, stats.mtime);
71
71
  }
72
72
  }
73
+ function shouldSkipPath(destPath, destRoot, skipPaths) {
74
+ if (!skipPaths || skipPaths.length === 0) {
75
+ return false;
76
+ }
77
+ const relativePath = relative(destRoot, destPath);
78
+ for (const skipPath of skipPaths) {
79
+ if (skipPath.endsWith("/")) {
80
+ const dirPath = skipPath.slice(0, -1);
81
+ if (relativePath === dirPath || relativePath.startsWith(dirPath + sep)) {
82
+ return true;
83
+ }
84
+ } else {
85
+ if (relativePath === skipPath) {
86
+ return true;
87
+ }
88
+ }
89
+ }
90
+ return false;
91
+ }
73
92
  async function copyDirectory(src, dest, options = {}) {
74
93
  const fs = await import("node:fs/promises");
75
94
  const path = await import("node:path");
@@ -81,6 +100,9 @@ async function copyDirectory(src, dest, options = {}) {
81
100
  }
82
101
  const srcPath = path.join(src, entry.name);
83
102
  const destPath = path.join(dest, entry.name);
103
+ if (shouldSkipPath(destPath, dest, options.skipPaths)) {
104
+ continue;
105
+ }
84
106
  if (entry.isSymbolicLink()) {
85
107
  await handleSymlink(srcPath, destPath, options, fs);
86
108
  } else if (entry.isDirectory()) {
@@ -100,6 +122,15 @@ async function writeJsonFile(path, data) {
100
122
  const content = JSON.stringify(data, null, 2);
101
123
  await fs.writeFile(path, content, "utf-8");
102
124
  }
125
+ async function readTextFile(path) {
126
+ const fs = await import("node:fs/promises");
127
+ return fs.readFile(path, "utf-8");
128
+ }
129
+ async function writeTextFile(path, content) {
130
+ const fs = await import("node:fs/promises");
131
+ await ensureDirectory(dirname(path));
132
+ await fs.writeFile(path, content, "utf-8");
133
+ }
103
134
  function getPackageRoot() {
104
135
  const currentFile = fileURLToPath(import.meta.url);
105
136
  const currentDir = dirname(currentFile);
@@ -333,7 +364,9 @@ function getDefaultConfig() {
333
364
  enabled: false,
334
365
  checkIntervalHours: 24,
335
366
  autoApplyMinor: false
336
- }
367
+ },
368
+ preserveFiles: [],
369
+ customComponents: []
337
370
  };
338
371
  }
339
372
  function getDefaultPreferences() {
@@ -375,6 +408,13 @@ async function saveConfig(targetDir, config) {
375
408
  await writeJsonFile(configPath, config);
376
409
  debug("config.saved", { path: configPath });
377
410
  }
411
+ function deduplicateCustomComponents(components) {
412
+ const seen = new Map;
413
+ for (const c of components) {
414
+ seen.set(c.path, c);
415
+ }
416
+ return [...seen.values()];
417
+ }
378
418
  function mergeConfig(defaults, overrides) {
379
419
  return {
380
420
  ...defaults,
@@ -388,7 +428,12 @@ function mergeConfig(defaults, overrides) {
388
428
  agents: {
389
429
  ...defaults.agents,
390
430
  ...overrides.agents
391
- }
431
+ },
432
+ preserveFiles: overrides.preserveFiles ? [...new Set([...defaults.preserveFiles || [], ...overrides.preserveFiles])] : defaults.preserveFiles,
433
+ customComponents: overrides.customComponents ? deduplicateCustomComponents([
434
+ ...defaults.customComponents || [],
435
+ ...overrides.customComponents
436
+ ]) : defaults.customComponents
392
437
  };
393
438
  }
394
439
  function migrateConfig(config) {
@@ -839,6 +884,124 @@ async function detectProvider(options = {}) {
839
884
  }
840
885
  // src/core/updater.ts
841
886
  import { join as join5 } from "node:path";
887
+
888
+ // src/core/entry-merger.ts
889
+ var MANAGED_START = "<!-- omcustom:start -->";
890
+ var MANAGED_END = "<!-- omcustom:end -->";
891
+ function isCodeBlockDelimiter(line) {
892
+ const trimmed = line.trim();
893
+ return trimmed.startsWith("```") || trimmed.startsWith("~~~");
894
+ }
895
+ function handleManagedStart(currentLines, sections) {
896
+ if (currentLines.length > 0) {
897
+ sections.push({
898
+ type: "custom",
899
+ content: currentLines.join(`
900
+ `)
901
+ });
902
+ }
903
+ return {
904
+ currentSection: { type: "managed", content: "" },
905
+ currentLines: []
906
+ };
907
+ }
908
+ function handleManagedEnd(currentSection, currentLines, sections) {
909
+ if (currentSection && currentSection.type === "managed") {
910
+ currentSection.content = currentLines.join(`
911
+ `);
912
+ sections.push(currentSection);
913
+ return {
914
+ currentSection: null,
915
+ currentLines: []
916
+ };
917
+ }
918
+ return { currentSection, currentLines };
919
+ }
920
+ function parseEntryDoc(content) {
921
+ const sections = [];
922
+ const lines = content.split(`
923
+ `);
924
+ let currentSection = null;
925
+ let currentLines = [];
926
+ let insideCodeBlock = false;
927
+ for (const line of lines) {
928
+ if (isCodeBlockDelimiter(line)) {
929
+ insideCodeBlock = !insideCodeBlock;
930
+ }
931
+ if (!insideCodeBlock) {
932
+ const trimmed = line.trim();
933
+ if (trimmed === MANAGED_START) {
934
+ const result = handleManagedStart(currentLines, sections);
935
+ currentSection = result.currentSection;
936
+ currentLines = result.currentLines;
937
+ continue;
938
+ }
939
+ if (trimmed === MANAGED_END) {
940
+ const result = handleManagedEnd(currentSection, currentLines, sections);
941
+ currentSection = result.currentSection;
942
+ currentLines = result.currentLines;
943
+ continue;
944
+ }
945
+ }
946
+ currentLines.push(line);
947
+ }
948
+ if (currentLines.length > 0) {
949
+ sections.push({
950
+ type: "custom",
951
+ content: currentLines.join(`
952
+ `)
953
+ });
954
+ }
955
+ return { sections };
956
+ }
957
+ function mergeEntryDoc(existingContent, templateContent) {
958
+ const warnings = [];
959
+ const { sections } = parseEntryDoc(existingContent);
960
+ const hasManagedSections = sections.some((s) => s.type === "managed");
961
+ if (!hasManagedSections) {
962
+ const wrapped = wrapInManagedMarkers(templateContent);
963
+ return {
964
+ content: wrapped,
965
+ managedSections: 1,
966
+ customSections: 0,
967
+ warnings: ["No managed sections found in existing content, wrapping template entirely"]
968
+ };
969
+ }
970
+ const mergedSections = [];
971
+ let managedCount = 0;
972
+ let customCount = 0;
973
+ let templateInserted = false;
974
+ for (const section of sections) {
975
+ if (section.type === "managed") {
976
+ if (!templateInserted) {
977
+ mergedSections.push(MANAGED_START);
978
+ mergedSections.push(templateContent);
979
+ mergedSections.push(MANAGED_END);
980
+ templateInserted = true;
981
+ managedCount++;
982
+ } else {
983
+ warnings.push("Multiple managed sections found, keeping only the first one");
984
+ }
985
+ } else {
986
+ mergedSections.push(section.content);
987
+ customCount++;
988
+ }
989
+ }
990
+ return {
991
+ content: mergedSections.join(`
992
+ `),
993
+ managedSections: managedCount,
994
+ customSections: customCount,
995
+ warnings
996
+ };
997
+ }
998
+ function wrapInManagedMarkers(content) {
999
+ return `${MANAGED_START}
1000
+ ${content}
1001
+ ${MANAGED_END}`;
1002
+ }
1003
+
1004
+ // src/core/updater.ts
842
1005
  var CUSTOMIZATION_MANIFEST_FILE = ".omcustom-customizations.json";
843
1006
  function createUpdateResult() {
844
1007
  return {
@@ -859,7 +1022,7 @@ async function handleBackupIfRequested(targetDir, provider, backup, result) {
859
1022
  result.backedUpPaths.push(backupPath);
860
1023
  info("update.backup_created", { path: backupPath });
861
1024
  }
862
- async function processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result) {
1025
+ async function processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result, config) {
863
1026
  const componentUpdate = updateCheck.updatableComponents.find((c) => c.name === component);
864
1027
  if (!componentUpdate && !options.force) {
865
1028
  result.skippedComponents.push(component);
@@ -871,7 +1034,7 @@ async function processComponentUpdate(targetDir, provider, component, updateChec
871
1034
  return;
872
1035
  }
873
1036
  try {
874
- const preserved = await updateComponent(targetDir, provider, component, customizations, options);
1037
+ const preserved = await updateComponent(targetDir, provider, component, customizations, options, config);
875
1038
  result.updatedComponents.push(component);
876
1039
  result.preservedFiles.push(...preserved);
877
1040
  } catch (err) {
@@ -880,9 +1043,93 @@ async function processComponentUpdate(targetDir, provider, component, updateChec
880
1043
  result.skippedComponents.push(component);
881
1044
  }
882
1045
  }
883
- async function updateAllComponents(targetDir, provider, components, updateCheck, customizations, options, result) {
1046
+ async function updateAllComponents(targetDir, provider, components, updateCheck, customizations, options, result, config) {
884
1047
  for (const component of components) {
885
- await processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result);
1048
+ await processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result, config);
1049
+ }
1050
+ }
1051
+ function getEntryTemplateName2(provider, language) {
1052
+ const layout = getProviderLayout(provider);
1053
+ const baseName = layout.entryFile.replace(".md", "");
1054
+ return language === "ko" ? `${baseName}.md.ko` : `${baseName}.md.en`;
1055
+ }
1056
+ async function backupFile(filePath) {
1057
+ const fs = await import("node:fs/promises");
1058
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
1059
+ const backupPath = `${filePath}.backup-${timestamp}`;
1060
+ if (await fileExists(filePath)) {
1061
+ await fs.copyFile(filePath, backupPath);
1062
+ debug("update.file_backed_up", { path: filePath, backup: backupPath });
1063
+ }
1064
+ }
1065
+ async function resolveManifestCustomizations(options, targetDir) {
1066
+ if (options.forceOverwriteAll) {
1067
+ return null;
1068
+ }
1069
+ if (options.preserveCustomizations === false) {
1070
+ return null;
1071
+ }
1072
+ return loadCustomizationManifest(targetDir);
1073
+ }
1074
+ function resolveConfigPreserveFiles(options, config) {
1075
+ if (options.forceOverwriteAll) {
1076
+ return [];
1077
+ }
1078
+ return config.preserveFiles || [];
1079
+ }
1080
+ function resolveCustomizations(customizations, configPreserveFiles) {
1081
+ if (!customizations && configPreserveFiles.length === 0) {
1082
+ return null;
1083
+ }
1084
+ if (customizations && configPreserveFiles.length > 0) {
1085
+ customizations.preserveFiles = [
1086
+ ...new Set([...customizations.preserveFiles, ...configPreserveFiles])
1087
+ ];
1088
+ return customizations;
1089
+ }
1090
+ if (configPreserveFiles.length > 0) {
1091
+ return {
1092
+ modifiedFiles: [],
1093
+ preserveFiles: configPreserveFiles,
1094
+ customComponents: [],
1095
+ lastUpdated: new Date().toISOString()
1096
+ };
1097
+ }
1098
+ return customizations;
1099
+ }
1100
+ async function updateEntryDoc(targetDir, provider, config, options) {
1101
+ const layout = getProviderLayout(provider);
1102
+ const entryPath = join5(targetDir, layout.entryFile);
1103
+ const templateName = getEntryTemplateName2(provider, config.language);
1104
+ const templatePath = resolveTemplatePath(templateName);
1105
+ if (!await fileExists(templatePath)) {
1106
+ warn("update.entry_template_not_found", { template: templateName });
1107
+ return;
1108
+ }
1109
+ const templateContent = await readTextFile(templatePath);
1110
+ if (await fileExists(entryPath)) {
1111
+ if (options.force) {
1112
+ await backupFile(entryPath);
1113
+ await writeTextFile(entryPath, templateContent);
1114
+ info("update.entry_doc_force_updated", { path: layout.entryFile });
1115
+ } else {
1116
+ const existingContent = await readTextFile(entryPath);
1117
+ const mergeResult = mergeEntryDoc(existingContent, templateContent);
1118
+ await writeTextFile(entryPath, mergeResult.content);
1119
+ debug("update.entry_doc_merged", {
1120
+ path: layout.entryFile,
1121
+ managed: String(mergeResult.managedSections),
1122
+ custom: String(mergeResult.customSections)
1123
+ });
1124
+ if (mergeResult.warnings.length > 0) {
1125
+ for (const warning of mergeResult.warnings) {
1126
+ warn("update.entry_merge_warning", { warning });
1127
+ }
1128
+ }
1129
+ }
1130
+ } else {
1131
+ await writeTextFile(entryPath, wrapInManagedMarkers(templateContent));
1132
+ info("update.entry_doc_created", { path: layout.entryFile });
886
1133
  }
887
1134
  }
888
1135
  async function update(options) {
@@ -901,9 +1148,14 @@ async function update(options) {
901
1148
  return result;
902
1149
  }
903
1150
  await handleBackupIfRequested(options.targetDir, provider, !!options.backup, result);
904
- const customizations = options.preserveCustomizations !== false ? await loadCustomizationManifest(options.targetDir) : null;
1151
+ const manifestCustomizations = await resolveManifestCustomizations(options, options.targetDir);
1152
+ const configPreserveFiles = resolveConfigPreserveFiles(options, config);
1153
+ const customizations = resolveCustomizations(manifestCustomizations, configPreserveFiles);
905
1154
  const components = options.components || getAllUpdateComponents();
906
- await updateAllComponents(options.targetDir, provider, components, updateCheck, customizations, options, result);
1155
+ await updateAllComponents(options.targetDir, provider, components, updateCheck, customizations, options, result, config);
1156
+ if (!options.components || options.components.length === 0) {
1157
+ await updateEntryDoc(options.targetDir, provider, config, options);
1158
+ }
907
1159
  config.version = result.newVersion;
908
1160
  config.lastUpdated = new Date().toISOString();
909
1161
  await saveConfig(options.targetDir, config);
@@ -980,28 +1232,33 @@ async function componentHasUpdate(_targetDir, provider, component, config) {
980
1232
  const latestVersion = await getLatestVersion(provider);
981
1233
  return installedVersion !== latestVersion;
982
1234
  }
983
- async function updateComponent(targetDir, provider, component, customizations, options) {
1235
+ async function updateComponent(targetDir, provider, component, customizations, options, config) {
984
1236
  const preservedFiles = [];
985
1237
  const componentPath = getComponentPath2(provider, component);
986
1238
  const srcPath = resolveTemplatePath(componentPath);
987
1239
  const destPath = join5(targetDir, componentPath);
988
- if (customizations && options.preserveCustomizations !== false) {
1240
+ const customComponents = config.customComponents || [];
1241
+ const skipPaths = [];
1242
+ if (customizations && !options.forceOverwriteAll) {
989
1243
  const toPreserve = customizations.preserveFiles.filter((f) => f.startsWith(componentPath));
990
- if (toPreserve.length > 0) {
991
- const preserved = await preserveCustomizations(targetDir, toPreserve);
992
- preservedFiles.push(...preserved.keys());
993
- await copyDirectory(srcPath, destPath, { overwrite: true });
994
- const fs = await import("node:fs/promises");
995
- for (const [path, content] of preserved) {
996
- await fs.writeFile(join5(targetDir, path), content, "utf-8");
997
- }
998
- } else {
999
- await copyDirectory(srcPath, destPath, { overwrite: true });
1244
+ preservedFiles.push(...toPreserve);
1245
+ skipPaths.push(...toPreserve);
1246
+ }
1247
+ for (const cc of customComponents) {
1248
+ if (cc.path.startsWith(componentPath)) {
1249
+ skipPaths.push(cc.path);
1000
1250
  }
1001
- } else {
1002
- await copyDirectory(srcPath, destPath, { overwrite: true });
1003
1251
  }
1004
- debug("update.component_updated", { component });
1252
+ const path = await import("node:path");
1253
+ const normalizedSkipPaths = skipPaths.map((p) => path.relative(destPath, join5(targetDir, p)));
1254
+ await copyDirectory(srcPath, destPath, {
1255
+ overwrite: true,
1256
+ skipPaths: normalizedSkipPaths.length > 0 ? normalizedSkipPaths : undefined
1257
+ });
1258
+ debug("update.component_updated", {
1259
+ component,
1260
+ skippedPaths: String(normalizedSkipPaths.length)
1261
+ });
1005
1262
  return preservedFiles;
1006
1263
  }
1007
1264
  function getComponentPath2(provider, component) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-customcode",
3
- "version": "0.9.3",
3
+ "version": "0.10.0",
4
4
  "description": "Batteries-included agent harness for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,84 +14,20 @@ tools:
14
14
  - Bash
15
15
  ---
16
16
 
17
- You handle software architecture documentation, including system design documents, API specifications, architecture decision records (ADRs), and technical documentation maintenance.
17
+ You handle software architecture documentation: system design docs, API specs, ADRs, and technical doc maintenance.
18
18
 
19
19
  ## Capabilities
20
20
 
21
- 1. Generate architecture documentation
22
- 2. Create and maintain API specifications
23
- 3. Write Architecture Decision Records (ADRs)
24
- 4. Document system design and structure
25
- 5. Create technical diagrams (Mermaid, PlantUML)
26
- 6. Maintain README and developer guides
27
- 7. Ensure documentation consistency
21
+ - Architecture documentation with diagrams (Mermaid, PlantUML)
22
+ - API specifications (OpenAPI/Swagger)
23
+ - Architecture Decision Records (ADRs)
24
+ - README and developer guide maintenance
28
25
 
29
- ## Documentation Types
26
+ ## Document Types
30
27
 
31
28
  | Type | Format | Purpose |
32
29
  |------|--------|---------|
33
30
  | Architecture | Markdown + Diagrams | System overview |
34
31
  | API Spec | OpenAPI/Swagger | API documentation |
35
32
  | ADR | Markdown | Decision records |
36
- | README | Markdown | Project overview |
37
- | Guides | Markdown | Developer guides |
38
-
39
- ## Workflow
40
-
41
- ### Architecture Documentation
42
- 1. Analyze codebase structure
43
- 2. Identify key components
44
- 3. Map dependencies and flows
45
- 4. Generate diagrams
46
- 5. Write documentation
47
- 6. Review for accuracy
48
-
49
- ### API Documentation
50
- 1. Scan API endpoints
51
- 2. Extract request/response schemas
52
- 3. Generate OpenAPI spec
53
- 4. Add descriptions and examples
54
- 5. Validate specification
55
-
56
- ### ADR Creation
57
- 1. Understand decision context
58
- 2. Document options considered
59
- 3. Record decision rationale
60
- 4. Note consequences
61
- 5. Link related ADRs
62
-
63
- ## Output Formats
64
-
65
- ### Architecture Doc
66
- ```markdown
67
- # System Architecture
68
-
69
- ## Overview
70
- [High-level description]
71
-
72
- ## Components
73
- [Component breakdown with diagrams]
74
-
75
- ## Data Flow
76
- [Sequence/flow diagrams]
77
-
78
- ## Dependencies
79
- [External dependencies]
80
- ```
81
-
82
- ### ADR Format
83
- ```markdown
84
- # ADR-{number}: {title}
85
-
86
- ## Status
87
- [Proposed | Accepted | Deprecated | Superseded]
88
-
89
- ## Context
90
- [What is the issue?]
91
-
92
- ## Decision
93
- [What was decided?]
94
-
95
- ## Consequences
96
- [What are the results?]
97
- ```
33
+ | README/Guides | Markdown | Project/developer docs |
@@ -14,121 +14,34 @@ tools:
14
14
  - Bash
15
15
  ---
16
16
 
17
- You are a Spec-Driven Development agent that transforms high-level requirements into executable specifications. You manage the full specification lifecycle from constitution to implementation.
17
+ You are a Spec-Driven Development agent that transforms requirements into executable specifications.
18
18
 
19
19
  ## Source
20
20
 
21
21
  External agent from https://github.com/github/spec-kit
22
-
23
- **Version**: latest
24
- **Last Updated**: 2026-01-22
25
- **Update Command**: `uv tool upgrade specify-cli --from git+https://github.com/github/spec-kit.git`
26
-
27
- ## Prerequisites
28
-
29
- - Python 3.11+
30
- - uv package manager
31
- - Git
32
- - Claude Code or compatible AI agent
33
-
34
- ## Capabilities
35
-
36
- ### Specification Workflow
37
- - Define project constitution (principles, standards)
38
- - Create feature specifications (user stories, requirements)
39
- - Clarify ambiguous requirements through Q&A
40
- - Generate technical plans (architecture, data models)
41
- - Produce implementation task lists
42
-
43
- ### Multi-Phase Development
44
- - **0-to-1 (Greenfield)**: Build from scratch
45
- - **Creative Exploration**: Parallel tech stack trials
46
- - **Iterative Improvement (Brownfield)**: Enhance existing systems
47
-
48
- ### Quality Assurance
49
- - Analyze consistency across spec artifacts
50
- - Generate quality checklists
51
- - Validate spec coverage
22
+ - **Version**: latest
23
+ - **Update**: `uv tool upgrade specify-cli --from git+https://github.com/github/spec-kit.git`
24
+ - **Prerequisites**: Python 3.11+, uv, Git
52
25
 
53
26
  ## Commands
54
27
 
55
- | Command | Purpose | Output |
56
- |---------|---------|--------|
57
- | `/speckit.constitution` | Define project principles | constitution.md |
58
- | `/speckit.specify` | Define WHAT to build (no tech stack) | spec.md |
59
- | `/speckit.clarify` | Clarify ambiguous requirements | Q&A session |
60
- | `/speckit.plan` | Define HOW to build (tech stack) | plan.md, data-model.md |
61
- | `/speckit.tasks` | Generate implementation tasks | tasks.md |
62
- | `/speckit.implement` | Execute all tasks | Code + Tests |
63
- | `/speckit.analyze` | Check spec consistency | Analysis report |
64
- | `/speckit.checklist` | Generate QA checklist | Checklist |
65
-
66
- ## File Structure
67
-
68
- ```
69
- .specify/
70
- ├── memory/
71
- │ └── constitution.md # Project principles
72
- ├── scripts/ # Helper scripts
73
- ├── specs/
74
- │ └── NNN-feature-name/
75
- │ ├── spec.md # Feature specification
76
- │ ├── plan.md # Technical plan
77
- │ ├── tasks.md # Task breakdown
78
- │ ├── data-model.md # Data structures
79
- │ ├── research.md # Technical research
80
- │ └── contracts/ # API specs
81
- └── templates/ # Spec templates
82
- ```
28
+ | Command | Purpose |
29
+ |---------|---------|
30
+ | `/speckit.constitution` | Define project principles |
31
+ | `/speckit.specify` | Define WHAT to build |
32
+ | `/speckit.clarify` | Clarify requirements (Q&A) |
33
+ | `/speckit.plan` | Define HOW to build |
34
+ | `/speckit.tasks` | Generate implementation tasks |
35
+ | `/speckit.implement` | Execute all tasks |
36
+ | `/speckit.analyze` | Check spec consistency |
37
+ | `/speckit.checklist` | Generate QA checklist |
83
38
 
84
39
  ## Workflow
85
40
 
86
- 1. **Initialize project**
87
- ```bash
88
- $ specify init <project> --ai claude
89
- ```
90
-
91
- 2. **Define constitution**
92
- ```
93
- /speckit.constitution
94
- → .specify/memory/constitution.md
95
- ```
96
-
97
- 3. **Create specification**
98
- ```
99
- /speckit.specify <feature-description>
100
- → .specify/specs/NNN-feature/spec.md
101
- ```
102
-
103
- 4. **Clarify requirements**
104
- ```
105
- /speckit.clarify
106
- → Interactive Q&A
107
- ```
108
-
109
- 5. **Plan implementation**
110
- ```
111
- /speckit.plan <tech-stack-preferences>
112
- → plan.md, data-model.md, research.md
113
- ```
114
-
115
- 6. **Generate tasks**
116
- ```
117
- /speckit.tasks
118
- → tasks.md (TDD structure)
119
- ```
120
-
121
- 7. **Implement**
122
- ```
123
- /speckit.implement
124
- → Execute tasks in order
125
- ```
126
-
127
- ## Integration
128
-
129
- Works with:
130
- - Claude Code (primary)
131
- - GitHub Copilot
132
- - Cursor
133
- - Windsurf
134
- - Other AI coding assistants
41
+ 1. `specify init <project> --ai claude`
42
+ 2. `/speckit.constitution` -> principles
43
+ 3. `/speckit.specify` -> feature spec
44
+ 4. `/speckit.clarify` -> Q&A
45
+ 5. `/speckit.plan` -> technical plan
46
+ 6. `/speckit.tasks` -> TDD task list
47
+ 7. `/speckit.implement` -> execute