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.
- package/README.md +19 -20
- package/dist/cli/index.js +1186 -877
- package/dist/index.js +281 -24
- package/package.json +1 -1
- package/templates/.claude/agents/arch-documenter.md +7 -71
- package/templates/.claude/agents/arch-speckit-agent.md +21 -108
- package/templates/.claude/agents/be-express-expert.md +8 -58
- package/templates/.claude/agents/be-nestjs-expert.md +6 -38
- package/templates/.claude/agents/be-springboot-expert.md +11 -56
- package/templates/.claude/agents/db-postgres-expert.md +10 -80
- package/templates/.claude/agents/db-redis-expert.md +10 -75
- package/templates/.claude/agents/db-supabase-expert.md +12 -48
- package/templates/.claude/agents/de-airflow-expert.md +8 -45
- package/templates/.claude/agents/de-dbt-expert.md +8 -46
- package/templates/.claude/agents/de-kafka-expert.md +10 -10
- package/templates/.claude/agents/de-pipeline-expert.md +9 -69
- package/templates/.claude/agents/de-snowflake-expert.md +9 -62
- package/templates/.claude/agents/de-spark-expert.md +10 -54
- package/templates/.claude/agents/fe-svelte-agent.md +5 -41
- package/templates/.claude/agents/fe-vercel-agent.md +9 -41
- package/templates/.claude/agents/fe-vuejs-agent.md +7 -42
- package/templates/.claude/agents/lang-java21-expert.md +11 -37
- package/templates/.claude/agents/mgr-claude-code-bible.md +22 -207
- package/templates/.claude/agents/mgr-creator.md +7 -88
- package/templates/.claude/agents/mgr-gitnerd.md +8 -76
- package/templates/.claude/agents/mgr-sauron.md +27 -20
- package/templates/.claude/agents/mgr-supplier.md +11 -96
- package/templates/.claude/agents/mgr-sync-checker.md +9 -70
- package/templates/.claude/agents/mgr-updater.md +9 -79
- package/templates/.claude/agents/qa-engineer.md +8 -72
- package/templates/.claude/agents/qa-planner.md +2 -3
- package/templates/.claude/agents/qa-writer.md +6 -76
- package/templates/.claude/agents/sys-memory-keeper.md +13 -87
- package/templates/.claude/agents/sys-naggy.md +9 -62
- package/templates/.claude/agents/tool-bun-expert.md +7 -52
- package/templates/.claude/agents/tool-npm-expert.md +6 -64
- package/templates/.claude/agents/tool-optimizer.md +7 -60
- package/templates/.claude/rules/MAY-optimization.md +16 -80
- package/templates/.claude/rules/MUST-agent-design.md +29 -134
- package/templates/.claude/rules/MUST-agent-identification.md +9 -88
- package/templates/.claude/rules/MUST-continuous-improvement.md +10 -117
- package/templates/.claude/rules/MUST-intent-transparency.md +14 -171
- package/templates/.claude/rules/MUST-language-policy.md +11 -46
- package/templates/.claude/rules/MUST-orchestrator-coordination.md +82 -425
- package/templates/.claude/rules/MUST-parallel-execution.md +33 -405
- package/templates/.claude/rules/MUST-permissions.md +14 -68
- package/templates/.claude/rules/MUST-safety.md +11 -57
- package/templates/.claude/rules/MUST-sync-verification.md +49 -205
- package/templates/.claude/rules/MUST-tool-identification.md +21 -134
- package/templates/.claude/rules/SHOULD-agent-teams.md +22 -166
- package/templates/.claude/rules/SHOULD-ecomode.md +15 -123
- package/templates/.claude/rules/SHOULD-error-handling.md +19 -88
- package/templates/.claude/rules/SHOULD-hud-statusline.md +9 -89
- package/templates/.claude/rules/SHOULD-interaction.md +18 -87
- package/templates/.claude/rules/SHOULD-memory-integration.md +25 -118
- package/templates/.claude/skills/dev-lead-routing/SKILL.md +70 -243
- package/templates/.claude/skills/springboot-best-practices/SKILL.md +180 -319
- package/templates/.codex/agents/arch-documenter.md +7 -71
- package/templates/.codex/agents/arch-speckit-agent.md +21 -108
- package/templates/.codex/agents/be-express-expert.md +8 -58
- package/templates/.codex/agents/be-nestjs-expert.md +6 -38
- package/templates/.codex/agents/be-springboot-expert.md +11 -56
- package/templates/.codex/agents/db-postgres-expert.md +10 -80
- package/templates/.codex/agents/db-redis-expert.md +10 -75
- package/templates/.codex/agents/db-supabase-expert.md +12 -48
- package/templates/.codex/agents/de-airflow-expert.md +8 -45
- package/templates/.codex/agents/de-dbt-expert.md +8 -46
- package/templates/.codex/agents/de-kafka-expert.md +10 -10
- package/templates/.codex/agents/de-pipeline-expert.md +9 -69
- package/templates/.codex/agents/de-snowflake-expert.md +9 -62
- package/templates/.codex/agents/de-spark-expert.md +10 -54
- package/templates/.codex/agents/fe-svelte-agent.md +5 -41
- package/templates/.codex/agents/fe-vercel-agent.md +9 -41
- package/templates/.codex/agents/fe-vuejs-agent.md +7 -42
- package/templates/.codex/agents/infra-aws-expert.md +1 -1
- package/templates/.codex/agents/infra-docker-expert.md +1 -1
- package/templates/.codex/agents/lang-java21-expert.md +11 -37
- package/templates/.codex/agents/mgr-claude-code-bible.md +25 -210
- package/templates/.codex/agents/mgr-creator.md +7 -88
- package/templates/.codex/agents/mgr-gitnerd.md +8 -76
- package/templates/.codex/agents/mgr-sauron.md +30 -23
- package/templates/.codex/agents/mgr-supplier.md +11 -96
- package/templates/.codex/agents/mgr-sync-checker.md +12 -73
- package/templates/.codex/agents/mgr-updater.md +9 -79
- package/templates/.codex/agents/qa-engineer.md +8 -72
- package/templates/.codex/agents/qa-planner.md +2 -3
- package/templates/.codex/agents/qa-writer.md +6 -76
- package/templates/.codex/agents/sys-memory-keeper.md +13 -87
- package/templates/.codex/agents/sys-naggy.md +9 -62
- package/templates/.codex/agents/tool-bun-expert.md +7 -52
- package/templates/.codex/agents/tool-npm-expert.md +6 -64
- package/templates/.codex/agents/tool-optimizer.md +7 -60
- package/templates/.codex/codex-native-hash.txt +1 -0
- package/templates/.codex/rules/MAY-optimization.md +16 -80
- package/templates/.codex/rules/MUST-agent-design.md +29 -134
- package/templates/.codex/rules/MUST-agent-identification.md +9 -88
- package/templates/.codex/rules/MUST-continuous-improvement.md +10 -117
- package/templates/.codex/rules/MUST-intent-transparency.md +14 -171
- package/templates/.codex/rules/MUST-language-policy.md +11 -46
- package/templates/.codex/rules/MUST-orchestrator-coordination.md +82 -425
- package/templates/.codex/rules/MUST-parallel-execution.md +33 -405
- package/templates/.codex/rules/MUST-permissions.md +14 -68
- package/templates/.codex/rules/MUST-safety.md +11 -57
- package/templates/.codex/rules/MUST-sync-verification.md +58 -214
- package/templates/.codex/rules/MUST-tool-identification.md +22 -135
- package/templates/.codex/rules/SHOULD-agent-teams.md +22 -166
- package/templates/.codex/rules/SHOULD-ecomode.md +15 -123
- package/templates/.codex/rules/SHOULD-error-handling.md +19 -88
- package/templates/.codex/rules/SHOULD-hud-statusline.md +9 -89
- package/templates/.codex/rules/SHOULD-interaction.md +18 -87
- package/templates/.codex/rules/SHOULD-memory-integration.md +25 -118
- package/templates/.codex/skills/dev-lead-routing/SKILL.md +70 -243
- 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
|
|
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
|
-
|
|
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
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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
|
-
|
|
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
|
@@ -14,84 +14,20 @@ tools:
|
|
|
14
14
|
- Bash
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
-
You handle software architecture documentation
|
|
17
|
+
You handle software architecture documentation: system design docs, API specs, ADRs, and technical doc maintenance.
|
|
18
18
|
|
|
19
19
|
## Capabilities
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
##
|
|
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
|
|
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
|
|
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
|
-
**
|
|
24
|
-
**
|
|
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 |
|
|
56
|
-
|
|
57
|
-
| `/speckit.constitution` | Define project principles |
|
|
58
|
-
| `/speckit.specify` | Define WHAT to build
|
|
59
|
-
| `/speckit.clarify` | Clarify
|
|
60
|
-
| `/speckit.plan` | Define HOW to build
|
|
61
|
-
| `/speckit.tasks` | Generate implementation tasks |
|
|
62
|
-
| `/speckit.implement` | Execute all tasks |
|
|
63
|
-
| `/speckit.analyze` | Check spec consistency |
|
|
64
|
-
| `/speckit.checklist` | Generate QA 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.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|