agentic-forge 0.0.0 → 0.7.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 (209) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +31 -21
  3. package/dist/checkpoints/manager.d.ts +5 -0
  4. package/dist/checkpoints/manager.js +87 -0
  5. package/dist/checkpoints/manager.js.map +1 -0
  6. package/{src → dist}/claude/.claude/skills/analyze/SKILL.md +1 -1
  7. package/{src → dist}/claude/.claude/skills/create-checkpoint/SKILL.md +1 -1
  8. package/{src → dist}/claude/.claude/skills/create-log/SKILL.md +1 -1
  9. package/{src → dist}/claude/.claude/skills/fix-analyze/SKILL.md +1 -1
  10. package/{src → dist}/claude/.claude/skills/git-branch/SKILL.md +1 -1
  11. package/{src → dist}/claude/.claude/skills/git-commit/SKILL.md +1 -1
  12. package/{src → dist}/claude/.claude/skills/git-pr/SKILL.md +1 -1
  13. package/{src → dist}/claude/.claude/skills/sdlc-plan/SKILL.md +1 -1
  14. package/{src → dist}/claude/.claude/skills/sdlc-review/SKILL.md +1 -1
  15. package/{src → dist}/claude/.claude/skills/workflow-builder/SKILL.md +1 -1
  16. package/dist/cli.d.ts +3 -0
  17. package/dist/cli.js +155 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/commands/config-cmd.d.ts +2 -0
  20. package/dist/commands/config-cmd.js +30 -0
  21. package/dist/commands/config-cmd.js.map +1 -0
  22. package/{src/commands/index.ts → dist/commands/index.d.ts} +1 -10
  23. package/dist/commands/index.js +13 -0
  24. package/dist/commands/index.js.map +1 -0
  25. package/dist/commands/init.d.ts +6 -0
  26. package/dist/commands/init.js +83 -0
  27. package/dist/commands/init.js.map +1 -0
  28. package/dist/commands/release-notes.d.ts +5 -0
  29. package/dist/commands/release-notes.js +68 -0
  30. package/dist/commands/release-notes.js.map +1 -0
  31. package/dist/commands/resume.d.ts +5 -0
  32. package/dist/commands/resume.js +79 -0
  33. package/dist/commands/resume.js.map +1 -0
  34. package/dist/commands/run.d.ts +14 -0
  35. package/dist/commands/run.js +193 -0
  36. package/dist/commands/run.js.map +1 -0
  37. package/dist/commands/shortcuts.d.ts +2 -0
  38. package/dist/commands/shortcuts.js +11 -0
  39. package/dist/commands/shortcuts.js.map +1 -0
  40. package/dist/commands/skills-dir.d.ts +2 -0
  41. package/dist/commands/skills-dir.js +9 -0
  42. package/dist/commands/skills-dir.js.map +1 -0
  43. package/dist/commands/status.d.ts +4 -0
  44. package/dist/commands/status.js +99 -0
  45. package/dist/commands/status.js.map +1 -0
  46. package/dist/commands/update.d.ts +4 -0
  47. package/dist/commands/update.js +65 -0
  48. package/dist/commands/update.js.map +1 -0
  49. package/dist/commands/version.d.ts +3 -0
  50. package/dist/commands/version.js +26 -0
  51. package/dist/commands/version.js.map +1 -0
  52. package/dist/commands/workflows.d.ts +4 -0
  53. package/dist/commands/workflows.js +109 -0
  54. package/dist/commands/workflows.js.map +1 -0
  55. package/dist/config.d.ts +8 -0
  56. package/dist/config.js +110 -0
  57. package/dist/config.js.map +1 -0
  58. package/dist/console.d.ts +103 -0
  59. package/dist/console.js +670 -0
  60. package/dist/console.js.map +1 -0
  61. package/dist/executor.d.ts +27 -0
  62. package/dist/executor.js +236 -0
  63. package/dist/executor.js.map +1 -0
  64. package/dist/git/worktree.d.ts +23 -0
  65. package/dist/git/worktree.js +170 -0
  66. package/dist/git/worktree.js.map +1 -0
  67. package/dist/logging/logger.d.ts +27 -0
  68. package/dist/logging/logger.js +69 -0
  69. package/dist/logging/logger.js.map +1 -0
  70. package/dist/orchestrator.d.ts +44 -0
  71. package/dist/orchestrator.js +587 -0
  72. package/dist/orchestrator.js.map +1 -0
  73. package/dist/parser.d.ts +17 -0
  74. package/dist/parser.js +184 -0
  75. package/dist/parser.js.map +1 -0
  76. package/dist/progress.d.ts +29 -0
  77. package/dist/progress.js +275 -0
  78. package/dist/progress.js.map +1 -0
  79. package/dist/ralph-loop.d.ts +26 -0
  80. package/dist/ralph-loop.js +194 -0
  81. package/dist/ralph-loop.js.map +1 -0
  82. package/dist/renderer.d.ts +15 -0
  83. package/dist/renderer.js +123 -0
  84. package/dist/renderer.js.map +1 -0
  85. package/dist/runner.d.ts +84 -0
  86. package/dist/runner.js +529 -0
  87. package/dist/runner.js.map +1 -0
  88. package/dist/signal-manager.d.ts +16 -0
  89. package/dist/signal-manager.js +50 -0
  90. package/dist/signal-manager.js.map +1 -0
  91. package/dist/steps/base.d.ts +28 -0
  92. package/dist/steps/base.js +23 -0
  93. package/dist/steps/base.js.map +1 -0
  94. package/dist/steps/conditional-step.d.ts +12 -0
  95. package/dist/steps/conditional-step.js +106 -0
  96. package/dist/steps/conditional-step.js.map +1 -0
  97. package/{src/steps/index.ts → dist/steps/index.d.ts} +1 -9
  98. package/dist/steps/index.js +8 -0
  99. package/dist/steps/index.js.map +1 -0
  100. package/dist/steps/parallel-step.d.ts +11 -0
  101. package/dist/steps/parallel-step.js +166 -0
  102. package/dist/steps/parallel-step.js.map +1 -0
  103. package/dist/steps/prompt-step.d.ts +8 -0
  104. package/dist/steps/prompt-step.js +94 -0
  105. package/dist/steps/prompt-step.js.map +1 -0
  106. package/dist/steps/ralph-loop-step.d.ts +8 -0
  107. package/dist/steps/ralph-loop-step.js +132 -0
  108. package/dist/steps/ralph-loop-step.js.map +1 -0
  109. package/dist/steps/serial-step.d.ts +10 -0
  110. package/dist/steps/serial-step.js +57 -0
  111. package/dist/steps/serial-step.js.map +1 -0
  112. package/dist/types.d.ts +118 -0
  113. package/dist/types.js +10 -0
  114. package/dist/types.js.map +1 -0
  115. package/package.json +56 -2
  116. package/.gitattributes +0 -24
  117. package/.github/workflows/ci.yml +0 -70
  118. package/.markdownlint-cli2.jsonc +0 -16
  119. package/.prettierignore +0 -3
  120. package/.prettierrc +0 -6
  121. package/.vscode/agentic-forge.code-workspace +0 -26
  122. package/CHANGELOG.md +0 -100
  123. package/CLAUDE.md +0 -158
  124. package/CONTRIBUTING.md +0 -152
  125. package/biome.json +0 -21
  126. package/scripts/copy-assets.js +0 -21
  127. package/src/checkpoints/manager.ts +0 -119
  128. package/src/cli.ts +0 -182
  129. package/src/commands/config-cmd.ts +0 -28
  130. package/src/commands/init.ts +0 -96
  131. package/src/commands/release-notes.ts +0 -85
  132. package/src/commands/resume.ts +0 -103
  133. package/src/commands/run.ts +0 -234
  134. package/src/commands/shortcuts.ts +0 -11
  135. package/src/commands/skills-dir.ts +0 -11
  136. package/src/commands/status.ts +0 -112
  137. package/src/commands/update.ts +0 -64
  138. package/src/commands/version.ts +0 -27
  139. package/src/commands/workflows.ts +0 -129
  140. package/src/config.ts +0 -129
  141. package/src/console.ts +0 -790
  142. package/src/executor.ts +0 -354
  143. package/src/git/worktree.ts +0 -236
  144. package/src/logging/logger.ts +0 -95
  145. package/src/orchestrator.ts +0 -815
  146. package/src/parser.ts +0 -225
  147. package/src/progress.ts +0 -306
  148. package/src/ralph-loop.ts +0 -260
  149. package/src/renderer.ts +0 -164
  150. package/src/runner.ts +0 -634
  151. package/src/signal-manager.ts +0 -55
  152. package/src/steps/base.ts +0 -71
  153. package/src/steps/conditional-step.ts +0 -144
  154. package/src/steps/parallel-step.ts +0 -213
  155. package/src/steps/prompt-step.ts +0 -121
  156. package/src/steps/ralph-loop-step.ts +0 -186
  157. package/src/steps/serial-step.ts +0 -84
  158. package/src/types.ts +0 -141
  159. package/tests/config.test.ts +0 -219
  160. package/tests/console.test.ts +0 -506
  161. package/tests/executor.test.ts +0 -339
  162. package/tests/init.test.ts +0 -86
  163. package/tests/logger.test.ts +0 -110
  164. package/tests/parser.test.ts +0 -290
  165. package/tests/progress.test.ts +0 -345
  166. package/tests/ralph-loop.test.ts +0 -418
  167. package/tests/renderer.test.ts +0 -350
  168. package/tests/runner.test.ts +0 -497
  169. package/tests/setup.test.ts +0 -7
  170. package/tests/signal-manager.test.ts +0 -26
  171. package/tests/steps.test.ts +0 -412
  172. package/tests/worktree.test.ts +0 -411
  173. package/tsconfig.json +0 -18
  174. package/vitest.config.ts +0 -8
  175. /package/{src → dist}/agents/explorer.md +0 -0
  176. /package/{src → dist}/agents/reviewer.md +0 -0
  177. /package/{src → dist}/claude/.claude/skills/analyze/references/bug.md +0 -0
  178. /package/{src → dist}/claude/.claude/skills/analyze/references/debt.md +0 -0
  179. /package/{src → dist}/claude/.claude/skills/analyze/references/doc.md +0 -0
  180. /package/{src → dist}/claude/.claude/skills/analyze/references/security.md +0 -0
  181. /package/{src → dist}/claude/.claude/skills/analyze/references/style.md +0 -0
  182. /package/{src → dist}/claude/.claude/skills/orchestrate/SKILL.md +0 -0
  183. /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/bug.md +0 -0
  184. /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/chore.md +0 -0
  185. /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/feature.md +0 -0
  186. /package/{src → dist}/claude/.claude/skills/workflow-builder/references/REFERENCE.md +0 -0
  187. /package/{src → dist}/claude/.claude/skills/workflow-builder/references/workflow-example.yaml +0 -0
  188. /package/{src → dist}/prompts/agentic-system.md +0 -0
  189. /package/{src → dist}/templates/analysis/bug.md.j2 +0 -0
  190. /package/{src → dist}/templates/analysis/debt.md.j2 +0 -0
  191. /package/{src → dist}/templates/analysis/doc.md.j2 +0 -0
  192. /package/{src → dist}/templates/analysis/security.md.j2 +0 -0
  193. /package/{src → dist}/templates/analysis/style.md.j2 +0 -0
  194. /package/{src → dist}/templates/analysis-summary.md.j2 +0 -0
  195. /package/{src → dist}/templates/checkpoint.md.j2 +0 -0
  196. /package/{src → dist}/templates/implementation-report.md.j2 +0 -0
  197. /package/{src → dist}/templates/memory.md.j2 +0 -0
  198. /package/{src → dist}/templates/plan-bug.md.j2 +0 -0
  199. /package/{src → dist}/templates/plan-chore.md.j2 +0 -0
  200. /package/{src → dist}/templates/plan-feature.md.j2 +0 -0
  201. /package/{src → dist}/templates/progress.json.j2 +0 -0
  202. /package/{src → dist}/templates/ralph-report.md.j2 +0 -0
  203. /package/{src → dist}/workflows/analyze-codebase-merge.yaml +0 -0
  204. /package/{src → dist}/workflows/analyze-codebase.yaml +0 -0
  205. /package/{src → dist}/workflows/analyze-single.yaml +0 -0
  206. /package/{src → dist}/workflows/demo.yaml +0 -0
  207. /package/{src → dist}/workflows/one-shot.yaml +0 -0
  208. /package/{src → dist}/workflows/plan-build-review.yaml +0 -0
  209. /package/{src → dist}/workflows/ralph-loop.yaml +0 -0
@@ -1,64 +0,0 @@
1
- /** Update command handler. */
2
-
3
- import { execFileSync } from "node:child_process";
4
-
5
- import { getVersion } from "./version.js";
6
-
7
- function commandExists(cmd: string): boolean {
8
- try {
9
- execFileSync(process.platform === "win32" ? "where" : "which", [cmd], {
10
- stdio: "ignore",
11
- });
12
- return true;
13
- } catch {
14
- return false;
15
- }
16
- }
17
-
18
- export function cmdUpdate(options: { check?: boolean }): void {
19
- const currentVersion = getVersion();
20
- if (currentVersion === "unknown") {
21
- process.stderr.write("Error: agentic-forge is not installed\n");
22
- process.exit(1);
23
- }
24
-
25
- if (!commandExists("npm")) {
26
- process.stderr.write("Error: npm is required for updating agentic-forge\n");
27
- process.exit(1);
28
- }
29
-
30
- if (options.check) {
31
- process.stdout.write(`Current version: ${currentVersion}\n`);
32
- try {
33
- const result = execFileSync("npm", ["view", "agentic-forge", "version"], {
34
- encoding: "utf-8",
35
- stdio: ["pipe", "pipe", "pipe"],
36
- }).trim();
37
- if (result && result !== currentVersion) {
38
- process.stdout.write(`Latest version: ${result}\n`);
39
- } else {
40
- process.stdout.write("Already at the latest version.\n");
41
- }
42
- } catch {
43
- process.stdout.write("Could not check for updates.\n");
44
- }
45
- } else {
46
- process.stdout.write(`Current version: ${currentVersion}\n`);
47
- process.stdout.write("Upgrading...\n");
48
- try {
49
- execFileSync("npm", ["install", "-g", "agentic-forge"], {
50
- encoding: "utf-8",
51
- stdio: ["pipe", "pipe", "pipe"],
52
- });
53
- const newVersion = getVersion();
54
- if (newVersion !== currentVersion) {
55
- process.stdout.write(`Successfully updated to version ${newVersion}\n`);
56
- } else {
57
- process.stdout.write("Already at the latest version.\n");
58
- }
59
- } catch {
60
- process.stderr.write("Update failed.\n");
61
- process.exit(1);
62
- }
63
- }
64
- }
@@ -1,27 +0,0 @@
1
- /** Version command handler. */
2
-
3
- import { readFileSync } from "node:fs";
4
- import path from "node:path";
5
- import { fileURLToPath } from "node:url";
6
-
7
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
-
9
- export function cmdVersion(): void {
10
- try {
11
- const packageJsonPath = path.join(__dirname, "..", "..", "package.json");
12
- const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as { version: string };
13
- process.stdout.write(`agentic-forge ${pkg.version}\n`);
14
- } catch {
15
- process.stdout.write("agentic-forge version unknown (package not installed)\n");
16
- }
17
- }
18
-
19
- export function getVersion(): string {
20
- try {
21
- const packageJsonPath = path.join(__dirname, "..", "..", "package.json");
22
- const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as { version: string };
23
- return pkg.version;
24
- } catch {
25
- return "unknown";
26
- }
27
- }
@@ -1,129 +0,0 @@
1
- /** Workflows command handler. */
2
-
3
- import { existsSync, readFileSync, readdirSync } from "node:fs";
4
- import path from "node:path";
5
- import yaml from "js-yaml";
6
-
7
- import { getBundledWorkflowsDir, getProjectWorkflowsDir, getUserWorkflowsDir } from "./run.js";
8
-
9
- interface WorkflowMetadata {
10
- name: string;
11
- description: string;
12
- variables: { name: string; required?: boolean }[];
13
- }
14
-
15
- function getWorkflowMetadata(filePath: string): WorkflowMetadata {
16
- try {
17
- const content = yaml.load(readFileSync(filePath, "utf-8")) as Record<string, unknown> | null;
18
- if (!content || typeof content !== "object") {
19
- return { name: path.basename(filePath, ".yaml"), description: "", variables: [] };
20
- }
21
- return {
22
- name: (content.name as string) ?? path.basename(filePath, ".yaml"),
23
- description: (content.description as string) ?? "",
24
- variables: (content.variables as WorkflowMetadata["variables"]) ?? [],
25
- };
26
- } catch {
27
- return { name: path.basename(filePath, ".yaml"), description: "", variables: [] };
28
- }
29
- }
30
-
31
- function listAllWorkflows(): [string, string, string, WorkflowMetadata][] {
32
- const workflows: [string, string, string, WorkflowMetadata][] = [];
33
-
34
- const searchLocations: [string, string][] = [
35
- [getProjectWorkflowsDir(), "project"],
36
- [getUserWorkflowsDir(), "user"],
37
- [getBundledWorkflowsDir(), "bundled"],
38
- ];
39
-
40
- for (const [directory, locationType] of searchLocations) {
41
- if (existsSync(directory)) {
42
- const files = readdirSync(directory)
43
- .filter((f) => f.endsWith(".yaml"))
44
- .sort();
45
- for (const file of files) {
46
- const fullPath = path.join(directory, file);
47
- const metadata = getWorkflowMetadata(fullPath);
48
- workflows.push([path.basename(file, ".yaml"), fullPath, locationType, metadata]);
49
- }
50
- }
51
- }
52
-
53
- return workflows;
54
- }
55
-
56
- export function cmdWorkflows(options: { verbose?: boolean }): void {
57
- const workflows = listAllWorkflows();
58
-
59
- if (workflows.length === 0) {
60
- process.stdout.write("No workflows found.\n");
61
- process.stdout.write("\nSearched locations:\n");
62
- process.stdout.write(` - Project: ${getProjectWorkflowsDir()}\n`);
63
- process.stdout.write(` - User: ${getUserWorkflowsDir()}\n`);
64
- process.stdout.write(` - Bundled: ${getBundledWorkflowsDir()}\n`);
65
- process.stdout.write("\nUse 'agentic-forge init' to copy bundled workflows locally.\n");
66
- return;
67
- }
68
-
69
- const verbose = options.verbose ?? false;
70
-
71
- // Group by location
72
- const byLocation: Record<string, [string, string, WorkflowMetadata][]> = {};
73
- for (const [name, wfPath, location, metadata] of workflows) {
74
- if (!byLocation[location]) {
75
- byLocation[location] = [];
76
- }
77
- byLocation[location].push([name, wfPath, metadata]);
78
- }
79
-
80
- process.stdout.write("Available Workflows\n");
81
- process.stdout.write(`${"=".repeat(50)}\n\n`);
82
-
83
- const locationOrder = ["project", "user", "bundled"];
84
- const locationLabels: Record<string, string> = {
85
- project: "Project",
86
- user: "User",
87
- bundled: "Bundled",
88
- };
89
-
90
- for (const location of locationOrder) {
91
- if (!byLocation[location]) {
92
- continue;
93
- }
94
-
95
- process.stdout.write(`${locationLabels[location]}:\n`);
96
- for (const [name, , metadata] of byLocation[location]) {
97
- let desc = metadata.description;
98
- if (desc) {
99
- if (desc.length > 60 && !verbose) {
100
- desc = `${desc.slice(0, 57)}...`;
101
- }
102
- process.stdout.write(` ${name.padEnd(25)} ${desc}\n`);
103
- } else {
104
- process.stdout.write(` ${name}\n`);
105
- }
106
-
107
- if (verbose && metadata.variables.length > 0) {
108
- const required = metadata.variables.filter((v) => v.required).map((v) => v.name);
109
- const optional = metadata.variables.filter((v) => !v.required).map((v) => v.name);
110
- if (required.length > 0) {
111
- process.stdout.write(` Required: ${required.join(", ")}\n`);
112
- }
113
- if (optional.length > 0) {
114
- process.stdout.write(` Optional: ${optional.join(", ")}\n`);
115
- }
116
- }
117
- }
118
- process.stdout.write("\n");
119
- }
120
-
121
- process.stdout.write(`Total: ${workflows.length} workflow(s)\n\n`);
122
- process.stdout.write("Usage:\n");
123
- process.stdout.write(" agentic-forge run <workflow-name>\n");
124
- process.stdout.write(" agentic-forge run <workflow-name> --var key=value\n\n");
125
- process.stdout.write("Examples:\n");
126
- process.stdout.write(' agentic-forge run one-shot --var task="Add login button"\n');
127
- process.stdout.write(" agentic-forge run analyze-single --var analysis_type=bug\n");
128
- process.stdout.write(' agentic-forge run plan-build-review --var task="Refactor auth"\n');
129
- }
package/src/config.ts DELETED
@@ -1,129 +0,0 @@
1
- /** Configuration management for agentic-forge. */
2
-
3
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
- import path from "node:path";
5
-
6
- const DEFAULT_CONFIG: Record<string, unknown> = {
7
- outputDirectory: "agentic",
8
- logging: {
9
- enabled: true,
10
- level: "Error",
11
- },
12
- git: {
13
- mainBranch: "main",
14
- autoCommit: true,
15
- autoPr: true,
16
- },
17
- defaults: {
18
- model: "sonnet",
19
- maxRetry: 3,
20
- timeoutMinutes: 60,
21
- trackProgress: true,
22
- terminalOutput: "base",
23
- },
24
- execution: {
25
- maxWorkers: 4,
26
- pollingIntervalSeconds: 5,
27
- },
28
- };
29
-
30
- export function getConfigPath(repoRoot?: string): string {
31
- const root = repoRoot ?? process.cwd();
32
- return path.join(root, "agentic", "config.json");
33
- }
34
-
35
- export function getDefaultConfig(): Record<string, unknown> {
36
- return deepCopy(DEFAULT_CONFIG);
37
- }
38
-
39
- export function loadConfig(repoRoot?: string): Record<string, unknown> {
40
- const configPath = getConfigPath(repoRoot);
41
-
42
- if (existsSync(configPath)) {
43
- const content = readFileSync(configPath, "utf-8");
44
- const userConfig = JSON.parse(content) as Record<string, unknown>;
45
- return deepMerge(getDefaultConfig(), userConfig);
46
- }
47
-
48
- return getDefaultConfig();
49
- }
50
-
51
- export function saveConfig(config: Record<string, unknown>, repoRoot?: string): void {
52
- const configPath = getConfigPath(repoRoot);
53
- const dir = path.dirname(configPath);
54
- mkdirSync(dir, { recursive: true });
55
- writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
56
- }
57
-
58
- export function getConfigValue(key: string, repoRoot?: string): unknown {
59
- const config = loadConfig(repoRoot);
60
- const parts = key.split(".");
61
- let value: unknown = config;
62
- for (const part of parts) {
63
- if (
64
- value !== null &&
65
- typeof value === "object" &&
66
- !Array.isArray(value) &&
67
- part in (value as Record<string, unknown>)
68
- ) {
69
- value = (value as Record<string, unknown>)[part];
70
- } else {
71
- return null;
72
- }
73
- }
74
- return value;
75
- }
76
-
77
- export function setConfigValue(key: string, value: string, repoRoot?: string): void {
78
- const config = loadConfig(repoRoot);
79
- const parts = key.split(".");
80
- let target = config;
81
- for (const part of parts.slice(0, -1)) {
82
- if (!(part in target)) {
83
- target[part] = {};
84
- }
85
- target = target[part] as Record<string, unknown>;
86
- }
87
-
88
- let parsedValue: unknown = value;
89
- if (value.toLowerCase() === "true") {
90
- parsedValue = true;
91
- } else if (value.toLowerCase() === "false") {
92
- parsedValue = false;
93
- } else if (/^\d+$/.test(value)) {
94
- parsedValue = Number.parseInt(value, 10);
95
- }
96
-
97
- target[parts[parts.length - 1]] = parsedValue;
98
- saveConfig(config, repoRoot);
99
- }
100
-
101
- export function deepMerge(
102
- base: Record<string, unknown>,
103
- override: Record<string, unknown>,
104
- ): Record<string, unknown> {
105
- const result = { ...base };
106
- for (const key of Object.keys(override)) {
107
- if (
108
- key in result &&
109
- typeof result[key] === "object" &&
110
- result[key] !== null &&
111
- !Array.isArray(result[key]) &&
112
- typeof override[key] === "object" &&
113
- override[key] !== null &&
114
- !Array.isArray(override[key])
115
- ) {
116
- result[key] = deepMerge(
117
- result[key] as Record<string, unknown>,
118
- override[key] as Record<string, unknown>,
119
- );
120
- } else {
121
- result[key] = override[key];
122
- }
123
- }
124
- return result;
125
- }
126
-
127
- function deepCopy(obj: Record<string, unknown>): Record<string, unknown> {
128
- return JSON.parse(JSON.stringify(obj)) as Record<string, unknown>;
129
- }