@agiflowai/aicode-utils 1.0.17 → 1.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -783,6 +783,27 @@ var ScaffoldProcessingService = class {
783
783
  }
784
784
  };
785
785
 
786
+ //#endregion
787
+ //#region src/utils/fallbacks.ts
788
+ function resolveFallbackConfig(config) {
789
+ if (!config) return {};
790
+ const tool = ("fallbackTool" in config ? config.fallbackTool : void 0) ?? ("fallback-tool" in config ? config["fallback-tool"] : void 0);
791
+ const fallbackConfig = ("fallbackToolConfig" in config ? config.fallbackToolConfig : void 0) ?? ("fallback-tool-config" in config ? config["fallback-tool-config"] : void 0);
792
+ if (tool) return {
793
+ tool,
794
+ config: fallbackConfig
795
+ };
796
+ const firstValidFallback = config.fallbacks?.find((entry) => isFallbackConfigEntry(entry));
797
+ if (!firstValidFallback) return {};
798
+ return {
799
+ tool: firstValidFallback.tool,
800
+ config: firstValidFallback.config
801
+ };
802
+ }
803
+ function isFallbackConfigEntry(entry) {
804
+ return Boolean(entry?.tool && typeof entry.tool === "string");
805
+ }
806
+
786
807
  //#endregion
787
808
  //#region src/utils/generateStableId.ts
788
809
  /**
@@ -1307,6 +1328,7 @@ exports.readJson = readJson;
1307
1328
  exports.readJsonSync = readJsonSync;
1308
1329
  exports.readdir = readdir;
1309
1330
  exports.remove = remove;
1331
+ exports.resolveFallbackConfig = resolveFallbackConfig;
1310
1332
  exports.sections = sections;
1311
1333
  exports.stat = stat;
1312
1334
  exports.statSync = node_fs.statSync;
package/dist/index.d.cts CHANGED
@@ -46,6 +46,15 @@ interface NxProjectJson {
46
46
  //#endregion
47
47
  //#region src/types/index.d.ts
48
48
 
49
+ /**
50
+ * Ordered fallback LLM entry used in toolkit settings.
51
+ */
52
+ interface FallbackConfigEntry {
53
+ /** Fallback LLM tool identifier. */
54
+ tool: string;
55
+ /** Config object forwarded when this fallback tool is selected. */
56
+ config?: Record<string, unknown>;
57
+ }
49
58
  /**
50
59
  * Configuration for the scaffold-mcp mcp-serve command.
51
60
  * Keys map 1-to-1 with CLI flags (camelCase).
@@ -65,6 +74,8 @@ interface McpServeConfig {
65
74
  fallbackTool?: string;
66
75
  /** Config passed to the fallback LLM tool. */
67
76
  fallbackToolConfig?: Record<string, unknown>;
77
+ /** Ordered fallback LLM chain consulted when fallbackTool is not set. */
78
+ fallbacks?: FallbackConfigEntry[];
68
79
  /** Extra CLI args merged into the mcp-serve command (key → --key value). */
69
80
  args?: Record<string, string | boolean | number>;
70
81
  }
@@ -81,6 +92,10 @@ interface HookMethodConfig {
81
92
  'fallback-tool'?: string;
82
93
  /** Config object forwarded to the fallback LLM tool. */
83
94
  'fallback-tool-config'?: Record<string, unknown>;
95
+ /** Ordered fallback LLM chain consulted when fallback-tool is not set. */
96
+ fallbacks?: FallbackConfigEntry[];
97
+ /** Optional Claude Code tool matcher written to .claude/settings.json. */
98
+ matcher?: string;
84
99
  /** Extra CLI args appended to the generated hook command (key → --key value). */
85
100
  args?: Record<string, string | boolean | number>;
86
101
  }
@@ -134,6 +149,8 @@ interface ArchitectMcpServeConfig {
134
149
  fallbackTool?: string;
135
150
  /** Config passed to the fallback LLM tool. */
136
151
  fallbackToolConfig?: Record<string, unknown>;
152
+ /** Ordered fallback LLM chain consulted when fallbackTool is not set. */
153
+ fallbacks?: FallbackConfigEntry[];
137
154
  /** LLM tool used specifically for get-file-design-pattern analysis. */
138
155
  designPatternTool?: string;
139
156
  /** Config passed to the design-pattern LLM tool. */
@@ -154,6 +171,14 @@ interface ArchitectHookMethodConfig {
154
171
  'llm-tool'?: string;
155
172
  /** Config object forwarded to the LLM tool. */
156
173
  'tool-config'?: Record<string, unknown>;
174
+ /** Fallback LLM tool used when llm-tool is not set. */
175
+ 'fallback-tool'?: string;
176
+ /** Config object forwarded to the fallback LLM tool. */
177
+ 'fallback-tool-config'?: Record<string, unknown>;
178
+ /** Ordered fallback LLM chain consulted when fallback-tool is not set. */
179
+ fallbacks?: FallbackConfigEntry[];
180
+ /** Optional Claude Code tool matcher written to .claude/settings.json. */
181
+ matcher?: string;
157
182
  /** Extra CLI args appended to the generated hook command (key → --key value). */
158
183
  args?: Record<string, string | boolean | number>;
159
184
  }
@@ -653,6 +678,24 @@ declare const readdir: typeof fs.readdir;
653
678
  declare const mkdir: typeof fs.mkdir;
654
679
  declare const stat: typeof fs.stat;
655
680
  //#endregion
681
+ //#region src/utils/fallbacks.d.ts
682
+ interface SingleFallbackShape {
683
+ fallbackTool?: string;
684
+ fallbackToolConfig?: Record<string, unknown>;
685
+ }
686
+ interface HookFallbackShape {
687
+ 'fallback-tool'?: string;
688
+ 'fallback-tool-config'?: Record<string, unknown>;
689
+ }
690
+ interface FallbackListShape {
691
+ fallbacks?: FallbackConfigEntry[];
692
+ }
693
+ interface ResolvedFallbackConfig {
694
+ tool?: string;
695
+ config?: Record<string, unknown>;
696
+ }
697
+ declare function resolveFallbackConfig(config?: (SingleFallbackShape | HookFallbackShape) & FallbackListShape): ResolvedFallbackConfig;
698
+ //#endregion
656
699
  //#region src/utils/generateStableId.d.ts
657
700
  /**
658
701
  * Generate a stable, random ID string
@@ -956,4 +999,4 @@ interface ProjectTypeDetectionResult {
956
999
  */
957
1000
  declare function detectProjectType(workspaceRoot: string): Promise<ProjectTypeDetectionResult>;
958
1001
  //#endregion
959
- export { ArchitectHookAgentConfig, ArchitectHookConfig, ArchitectHookMethodConfig, ArchitectMcpConfig, ArchitectMcpServeConfig, ConfigSource, FileStat, GeneratorContext, GeneratorFunction, GitHubDirectoryEntry, HookAgentConfig, HookConfig, HookMethodConfig, IFileSystemService, IVariableReplacementService, McpServeConfig, type NxProjectJson, ParsedGitHubUrl, ParsedInclude, ProjectConfig, ProjectConfigResolver, type ProjectConfigResult, ProjectFinderService, ProjectType, ScaffoldMcpConfig, ScaffoldProcessingService, ScaffoldResult, TemplatesManagerService, ToolkitConfig, accessSync, cloneRepository, cloneSubdirectory, copy, detectProjectType, ensureDir, fetchGitHubDirectoryContents, findWorkspaceRoot, generateStableId, gitInit, icons, log, logger, messages, mkdir, mkdirSync, move, parseGitHubUrl, pathExists, pathExistsSync, print, readFile, readFileSync, readJson, readJsonSync, readdir, remove, sections, stat, statSync, writeFile, writeFileSync };
1002
+ export { ArchitectHookAgentConfig, ArchitectHookConfig, ArchitectHookMethodConfig, ArchitectMcpConfig, ArchitectMcpServeConfig, ConfigSource, FallbackConfigEntry, FileStat, GeneratorContext, GeneratorFunction, GitHubDirectoryEntry, HookAgentConfig, HookConfig, HookMethodConfig, IFileSystemService, IVariableReplacementService, McpServeConfig, type NxProjectJson, ParsedGitHubUrl, ParsedInclude, ProjectConfig, ProjectConfigResolver, type ProjectConfigResult, ProjectFinderService, ProjectType, ResolvedFallbackConfig, ScaffoldMcpConfig, ScaffoldProcessingService, ScaffoldResult, TemplatesManagerService, ToolkitConfig, accessSync, cloneRepository, cloneSubdirectory, copy, detectProjectType, ensureDir, fetchGitHubDirectoryContents, findWorkspaceRoot, generateStableId, gitInit, icons, log, logger, messages, mkdir, mkdirSync, move, parseGitHubUrl, pathExists, pathExistsSync, print, readFile, readFileSync, readJson, readJsonSync, readdir, remove, resolveFallbackConfig, sections, stat, statSync, writeFile, writeFileSync };
package/dist/index.d.mts CHANGED
@@ -46,6 +46,15 @@ interface NxProjectJson {
46
46
  //#endregion
47
47
  //#region src/types/index.d.ts
48
48
 
49
+ /**
50
+ * Ordered fallback LLM entry used in toolkit settings.
51
+ */
52
+ interface FallbackConfigEntry {
53
+ /** Fallback LLM tool identifier. */
54
+ tool: string;
55
+ /** Config object forwarded when this fallback tool is selected. */
56
+ config?: Record<string, unknown>;
57
+ }
49
58
  /**
50
59
  * Configuration for the scaffold-mcp mcp-serve command.
51
60
  * Keys map 1-to-1 with CLI flags (camelCase).
@@ -65,6 +74,8 @@ interface McpServeConfig {
65
74
  fallbackTool?: string;
66
75
  /** Config passed to the fallback LLM tool. */
67
76
  fallbackToolConfig?: Record<string, unknown>;
77
+ /** Ordered fallback LLM chain consulted when fallbackTool is not set. */
78
+ fallbacks?: FallbackConfigEntry[];
68
79
  /** Extra CLI args merged into the mcp-serve command (key → --key value). */
69
80
  args?: Record<string, string | boolean | number>;
70
81
  }
@@ -81,6 +92,10 @@ interface HookMethodConfig {
81
92
  'fallback-tool'?: string;
82
93
  /** Config object forwarded to the fallback LLM tool. */
83
94
  'fallback-tool-config'?: Record<string, unknown>;
95
+ /** Ordered fallback LLM chain consulted when fallback-tool is not set. */
96
+ fallbacks?: FallbackConfigEntry[];
97
+ /** Optional Claude Code tool matcher written to .claude/settings.json. */
98
+ matcher?: string;
84
99
  /** Extra CLI args appended to the generated hook command (key → --key value). */
85
100
  args?: Record<string, string | boolean | number>;
86
101
  }
@@ -134,6 +149,8 @@ interface ArchitectMcpServeConfig {
134
149
  fallbackTool?: string;
135
150
  /** Config passed to the fallback LLM tool. */
136
151
  fallbackToolConfig?: Record<string, unknown>;
152
+ /** Ordered fallback LLM chain consulted when fallbackTool is not set. */
153
+ fallbacks?: FallbackConfigEntry[];
137
154
  /** LLM tool used specifically for get-file-design-pattern analysis. */
138
155
  designPatternTool?: string;
139
156
  /** Config passed to the design-pattern LLM tool. */
@@ -154,6 +171,14 @@ interface ArchitectHookMethodConfig {
154
171
  'llm-tool'?: string;
155
172
  /** Config object forwarded to the LLM tool. */
156
173
  'tool-config'?: Record<string, unknown>;
174
+ /** Fallback LLM tool used when llm-tool is not set. */
175
+ 'fallback-tool'?: string;
176
+ /** Config object forwarded to the fallback LLM tool. */
177
+ 'fallback-tool-config'?: Record<string, unknown>;
178
+ /** Ordered fallback LLM chain consulted when fallback-tool is not set. */
179
+ fallbacks?: FallbackConfigEntry[];
180
+ /** Optional Claude Code tool matcher written to .claude/settings.json. */
181
+ matcher?: string;
157
182
  /** Extra CLI args appended to the generated hook command (key → --key value). */
158
183
  args?: Record<string, string | boolean | number>;
159
184
  }
@@ -653,6 +678,24 @@ declare const readdir: typeof fs.readdir;
653
678
  declare const mkdir: typeof fs.mkdir;
654
679
  declare const stat: typeof fs.stat;
655
680
  //#endregion
681
+ //#region src/utils/fallbacks.d.ts
682
+ interface SingleFallbackShape {
683
+ fallbackTool?: string;
684
+ fallbackToolConfig?: Record<string, unknown>;
685
+ }
686
+ interface HookFallbackShape {
687
+ 'fallback-tool'?: string;
688
+ 'fallback-tool-config'?: Record<string, unknown>;
689
+ }
690
+ interface FallbackListShape {
691
+ fallbacks?: FallbackConfigEntry[];
692
+ }
693
+ interface ResolvedFallbackConfig {
694
+ tool?: string;
695
+ config?: Record<string, unknown>;
696
+ }
697
+ declare function resolveFallbackConfig(config?: (SingleFallbackShape | HookFallbackShape) & FallbackListShape): ResolvedFallbackConfig;
698
+ //#endregion
656
699
  //#region src/utils/generateStableId.d.ts
657
700
  /**
658
701
  * Generate a stable, random ID string
@@ -956,4 +999,4 @@ interface ProjectTypeDetectionResult {
956
999
  */
957
1000
  declare function detectProjectType(workspaceRoot: string): Promise<ProjectTypeDetectionResult>;
958
1001
  //#endregion
959
- export { ArchitectHookAgentConfig, ArchitectHookConfig, ArchitectHookMethodConfig, ArchitectMcpConfig, ArchitectMcpServeConfig, ConfigSource, FileStat, GeneratorContext, GeneratorFunction, type GitHubDirectoryEntry, HookAgentConfig, HookConfig, HookMethodConfig, IFileSystemService, IVariableReplacementService, McpServeConfig, type NxProjectJson, type ParsedGitHubUrl, ParsedInclude, ProjectConfig, ProjectConfigResolver, type ProjectConfigResult, ProjectFinderService, ProjectType, ScaffoldMcpConfig, ScaffoldProcessingService, ScaffoldResult, TemplatesManagerService, ToolkitConfig, accessSync, cloneRepository, cloneSubdirectory, copy, detectProjectType, ensureDir, fetchGitHubDirectoryContents, findWorkspaceRoot, generateStableId, gitInit, icons, log, logger, messages, mkdir, mkdirSync, move, parseGitHubUrl, pathExists, pathExistsSync, print, readFile, readFileSync, readJson, readJsonSync, readdir, remove, sections, stat, statSync, writeFile, writeFileSync };
1002
+ export { ArchitectHookAgentConfig, ArchitectHookConfig, ArchitectHookMethodConfig, ArchitectMcpConfig, ArchitectMcpServeConfig, ConfigSource, FallbackConfigEntry, FileStat, GeneratorContext, GeneratorFunction, type GitHubDirectoryEntry, HookAgentConfig, HookConfig, HookMethodConfig, IFileSystemService, IVariableReplacementService, McpServeConfig, type NxProjectJson, type ParsedGitHubUrl, ParsedInclude, ProjectConfig, ProjectConfigResolver, type ProjectConfigResult, ProjectFinderService, ProjectType, type ResolvedFallbackConfig, ScaffoldMcpConfig, ScaffoldProcessingService, ScaffoldResult, TemplatesManagerService, ToolkitConfig, accessSync, cloneRepository, cloneSubdirectory, copy, detectProjectType, ensureDir, fetchGitHubDirectoryContents, findWorkspaceRoot, generateStableId, gitInit, icons, log, logger, messages, mkdir, mkdirSync, move, parseGitHubUrl, pathExists, pathExistsSync, print, readFile, readFileSync, readJson, readJsonSync, readdir, remove, resolveFallbackConfig, sections, stat, statSync, writeFile, writeFileSync };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
- import * as path from "node:path";
3
- import nodePath from "node:path";
2
+ import * as path$1 from "node:path";
3
+ import path from "node:path";
4
4
  import * as fs from "node:fs/promises";
5
5
  import { accessSync, mkdirSync, readFileSync, readFileSync as readFileSync$1, statSync, writeFileSync } from "node:fs";
6
6
  import * as os from "node:os";
@@ -84,7 +84,7 @@ async function copy(src, dest) {
84
84
  * Move a file or directory
85
85
  */
86
86
  async function move(src, dest) {
87
- await ensureDir(nodePath.dirname(dest));
87
+ await ensureDir(path.dirname(dest));
88
88
  await fs.rename(src, dest);
89
89
  }
90
90
  /**
@@ -120,12 +120,12 @@ const cp = fs.cp;
120
120
 
121
121
  //#endregion
122
122
  //#region src/utils/logger.ts
123
- const logsDir = path.join(os.tmpdir(), "scaffold-mcp-logs");
123
+ const logsDir = path$1.join(os.tmpdir(), "scaffold-mcp-logs");
124
124
  const logger = pino({
125
125
  level: process.env.LOG_LEVEL || "debug",
126
126
  timestamp: pino.stdTimeFunctions.isoTime
127
127
  }, pino.destination({
128
- dest: path.join(logsDir, "scaffold-mcp.log"),
128
+ dest: path$1.join(logsDir, "scaffold-mcp.log"),
129
129
  mkdir: true,
130
130
  sync: true
131
131
  }));
@@ -208,11 +208,11 @@ var TemplatesManagerService = class TemplatesManagerService {
208
208
  const workspaceRoot = await TemplatesManagerService.findWorkspaceRoot(startPath);
209
209
  const config = await TemplatesManagerService.readToolkitConfig(startPath);
210
210
  if (config?.templatesPath) {
211
- const templatesPath$1 = nodePath.isAbsolute(config.templatesPath) ? config.templatesPath : nodePath.join(workspaceRoot, config.templatesPath);
211
+ const templatesPath$1 = path.isAbsolute(config.templatesPath) ? config.templatesPath : path.join(workspaceRoot, config.templatesPath);
212
212
  if (await pathExists(templatesPath$1)) return templatesPath$1;
213
213
  return null;
214
214
  }
215
- const templatesPath = nodePath.join(workspaceRoot, TemplatesManagerService.TEMPLATES_FOLDER);
215
+ const templatesPath = path.join(workspaceRoot, TemplatesManagerService.TEMPLATES_FOLDER);
216
216
  if (await pathExists(templatesPath)) return templatesPath;
217
217
  return null;
218
218
  }
@@ -220,12 +220,12 @@ var TemplatesManagerService = class TemplatesManagerService {
220
220
  * Find the workspace root by searching upwards for .git folder
221
221
  */
222
222
  static async findWorkspaceRoot(startPath) {
223
- let currentPath = nodePath.resolve(startPath);
224
- const rootPath = nodePath.parse(currentPath).root;
223
+ let currentPath = path.resolve(startPath);
224
+ const rootPath = path.parse(currentPath).root;
225
225
  while (true) {
226
- if (await pathExists(nodePath.join(currentPath, ".git"))) return currentPath;
226
+ if (await pathExists(path.join(currentPath, ".git"))) return currentPath;
227
227
  if (currentPath === rootPath) return process.cwd();
228
- currentPath = nodePath.dirname(currentPath);
228
+ currentPath = path.dirname(currentPath);
229
229
  }
230
230
  }
231
231
  /**
@@ -239,11 +239,11 @@ var TemplatesManagerService = class TemplatesManagerService {
239
239
  const workspaceRoot = TemplatesManagerService.findWorkspaceRootSync(startPath);
240
240
  const config = TemplatesManagerService.readToolkitConfigSync(startPath);
241
241
  if (config?.templatesPath) {
242
- const templatesPath$1 = nodePath.isAbsolute(config.templatesPath) ? config.templatesPath : nodePath.join(workspaceRoot, config.templatesPath);
242
+ const templatesPath$1 = path.isAbsolute(config.templatesPath) ? config.templatesPath : path.join(workspaceRoot, config.templatesPath);
243
243
  if (pathExistsSync(templatesPath$1)) return templatesPath$1;
244
244
  return null;
245
245
  }
246
- const templatesPath = nodePath.join(workspaceRoot, TemplatesManagerService.TEMPLATES_FOLDER);
246
+ const templatesPath = path.join(workspaceRoot, TemplatesManagerService.TEMPLATES_FOLDER);
247
247
  if (pathExistsSync(templatesPath)) return templatesPath;
248
248
  return null;
249
249
  }
@@ -251,12 +251,12 @@ var TemplatesManagerService = class TemplatesManagerService {
251
251
  * Find the workspace root synchronously by searching upwards for .git folder
252
252
  */
253
253
  static findWorkspaceRootSync(startPath) {
254
- let currentPath = nodePath.resolve(startPath);
255
- const rootPath = nodePath.parse(currentPath).root;
254
+ let currentPath = path.resolve(startPath);
255
+ const rootPath = path.parse(currentPath).root;
256
256
  while (true) {
257
- if (pathExistsSync(nodePath.join(currentPath, ".git"))) return currentPath;
257
+ if (pathExistsSync(path.join(currentPath, ".git"))) return currentPath;
258
258
  if (currentPath === rootPath) return process.cwd();
259
- currentPath = nodePath.dirname(currentPath);
259
+ currentPath = path.dirname(currentPath);
260
260
  }
261
261
  }
262
262
  /**
@@ -295,9 +295,9 @@ var TemplatesManagerService = class TemplatesManagerService {
295
295
  static async readToolkitConfig(startPath = process.cwd()) {
296
296
  const workspaceRoot = await TemplatesManagerService.findWorkspaceRoot(startPath);
297
297
  const yaml$1 = await import("js-yaml");
298
- const toolkitFolder = nodePath.join(workspaceRoot, TemplatesManagerService.TOOLKIT_FOLDER);
299
- const settingsPath = nodePath.join(toolkitFolder, TemplatesManagerService.SETTINGS_FILE);
300
- const settingsLocalPath = nodePath.join(toolkitFolder, TemplatesManagerService.SETTINGS_LOCAL_FILE);
298
+ const toolkitFolder = path.join(workspaceRoot, TemplatesManagerService.TOOLKIT_FOLDER);
299
+ const settingsPath = path.join(toolkitFolder, TemplatesManagerService.SETTINGS_FILE);
300
+ const settingsLocalPath = path.join(toolkitFolder, TemplatesManagerService.SETTINGS_LOCAL_FILE);
301
301
  if (await pathExists(settingsPath)) {
302
302
  const baseContent = await fs.readFile(settingsPath, "utf-8");
303
303
  const base = yaml$1.load(baseContent);
@@ -308,7 +308,7 @@ var TemplatesManagerService = class TemplatesManagerService {
308
308
  }
309
309
  return base;
310
310
  }
311
- const legacyConfigPath = nodePath.join(workspaceRoot, TemplatesManagerService.TOOLKIT_CONFIG_FILE);
311
+ const legacyConfigPath = path.join(workspaceRoot, TemplatesManagerService.TOOLKIT_CONFIG_FILE);
312
312
  if (!await pathExists(legacyConfigPath)) return null;
313
313
  const content = await fs.readFile(legacyConfigPath, "utf-8");
314
314
  return yaml$1.load(content);
@@ -327,9 +327,9 @@ var TemplatesManagerService = class TemplatesManagerService {
327
327
  static readToolkitConfigSync(startPath = process.cwd()) {
328
328
  const workspaceRoot = TemplatesManagerService.findWorkspaceRootSync(startPath);
329
329
  const yaml$1 = __require("js-yaml");
330
- const toolkitFolder = nodePath.join(workspaceRoot, TemplatesManagerService.TOOLKIT_FOLDER);
331
- const settingsPath = nodePath.join(toolkitFolder, TemplatesManagerService.SETTINGS_FILE);
332
- const settingsLocalPath = nodePath.join(toolkitFolder, TemplatesManagerService.SETTINGS_LOCAL_FILE);
330
+ const toolkitFolder = path.join(workspaceRoot, TemplatesManagerService.TOOLKIT_FOLDER);
331
+ const settingsPath = path.join(toolkitFolder, TemplatesManagerService.SETTINGS_FILE);
332
+ const settingsLocalPath = path.join(toolkitFolder, TemplatesManagerService.SETTINGS_LOCAL_FILE);
333
333
  if (pathExistsSync(settingsPath)) {
334
334
  const base = yaml$1.load(readFileSync$1(settingsPath, "utf-8"));
335
335
  if (pathExistsSync(settingsLocalPath)) {
@@ -338,7 +338,7 @@ var TemplatesManagerService = class TemplatesManagerService {
338
338
  }
339
339
  return base;
340
340
  }
341
- const legacyConfigPath = nodePath.join(workspaceRoot, TemplatesManagerService.TOOLKIT_CONFIG_FILE);
341
+ const legacyConfigPath = path.join(workspaceRoot, TemplatesManagerService.TOOLKIT_CONFIG_FILE);
342
342
  if (!pathExistsSync(legacyConfigPath)) return null;
343
343
  return yaml$1.load(readFileSync$1(legacyConfigPath, "utf-8"));
344
344
  }
@@ -351,8 +351,8 @@ var TemplatesManagerService = class TemplatesManagerService {
351
351
  */
352
352
  static async writeToolkitConfig(config, startPath = process.cwd()) {
353
353
  const workspaceRoot = await TemplatesManagerService.findWorkspaceRoot(startPath);
354
- const toolkitFolder = nodePath.join(workspaceRoot, TemplatesManagerService.TOOLKIT_FOLDER);
355
- const settingsPath = nodePath.join(toolkitFolder, TemplatesManagerService.SETTINGS_FILE);
354
+ const toolkitFolder = path.join(workspaceRoot, TemplatesManagerService.TOOLKIT_FOLDER);
355
+ const settingsPath = path.join(toolkitFolder, TemplatesManagerService.SETTINGS_FILE);
356
356
  await fs.mkdir(toolkitFolder, { recursive: true });
357
357
  const content = (await import("js-yaml")).dump(config, { indent: 2 });
358
358
  await fs.writeFile(settingsPath, content, "utf-8");
@@ -424,13 +424,13 @@ var ProjectConfigResolver = class ProjectConfigResolver {
424
424
  */
425
425
  static async resolveProjectConfig(projectPath, explicitTemplate) {
426
426
  try {
427
- const absolutePath = nodePath.resolve(projectPath);
427
+ const absolutePath = path.resolve(projectPath);
428
428
  if (explicitTemplate) return {
429
429
  type: ProjectType.MONOLITH,
430
430
  sourceTemplate: explicitTemplate,
431
431
  configSource: ConfigSource.TOOLKIT_YAML
432
432
  };
433
- const projectJsonPath = nodePath.join(absolutePath, "project.json");
433
+ const projectJsonPath = path.join(absolutePath, "project.json");
434
434
  if (await pathExists(projectJsonPath)) {
435
435
  const projectJson = await readJson(projectJsonPath);
436
436
  if (projectJson.sourceTemplate && typeof projectJson.sourceTemplate === "string" && projectJson.sourceTemplate.trim()) return {
@@ -521,12 +521,12 @@ Run 'scaffold-mcp scaffold list --help' for more info.`;
521
521
  * @param sourceTemplate - The template identifier
522
522
  */
523
523
  static async createProjectJson(projectPath, projectName, sourceTemplate) {
524
- const projectJsonPath = nodePath.join(projectPath, "project.json");
524
+ const projectJsonPath = path.join(projectPath, "project.json");
525
525
  try {
526
526
  let projectJson;
527
527
  if (await pathExists(projectJsonPath)) projectJson = await readJson(projectJsonPath);
528
528
  else {
529
- const relativePath = nodePath.relative(projectPath, process.cwd());
529
+ const relativePath = path.relative(projectPath, process.cwd());
530
530
  projectJson = {
531
531
  name: projectName,
532
532
  $schema: relativePath ? `${relativePath}/node_modules/nx/schemas/project-schema.json` : "node_modules/nx/schemas/project-schema.json",
@@ -578,15 +578,15 @@ var ProjectFinderService = class {
578
578
  * @returns Project configuration or null if not found
579
579
  */
580
580
  async findProjectForFile(filePath) {
581
- const normalizedPath = nodePath.isAbsolute(filePath) ? filePath : nodePath.join(this.workspaceRoot, filePath);
582
- let currentDir = nodePath.dirname(normalizedPath);
581
+ const normalizedPath = path.isAbsolute(filePath) ? filePath : path.join(this.workspaceRoot, filePath);
582
+ let currentDir = path.dirname(normalizedPath);
583
583
  while (currentDir !== "/" && currentDir.startsWith(this.workspaceRoot)) {
584
- const projectJsonPath = nodePath.join(currentDir, "project.json");
584
+ const projectJsonPath = path.join(currentDir, "project.json");
585
585
  try {
586
586
  const project = await this.loadProjectConfig(projectJsonPath);
587
587
  if (project) return project;
588
588
  } catch {}
589
- currentDir = nodePath.dirname(currentDir);
589
+ currentDir = path.dirname(currentDir);
590
590
  }
591
591
  return null;
592
592
  }
@@ -597,15 +597,15 @@ var ProjectFinderService = class {
597
597
  * @returns Project configuration or null if not found
598
598
  */
599
599
  findProjectForFileSync(filePath) {
600
- const normalizedPath = nodePath.isAbsolute(filePath) ? filePath : nodePath.join(this.workspaceRoot, filePath);
601
- let currentDir = nodePath.dirname(normalizedPath);
600
+ const normalizedPath = path.isAbsolute(filePath) ? filePath : path.join(this.workspaceRoot, filePath);
601
+ let currentDir = path.dirname(normalizedPath);
602
602
  while (currentDir !== "/" && currentDir.startsWith(this.workspaceRoot)) {
603
- const projectJsonPath = nodePath.join(currentDir, "project.json");
603
+ const projectJsonPath = path.join(currentDir, "project.json");
604
604
  try {
605
605
  const project = this.loadProjectConfigSync(projectJsonPath);
606
606
  if (project) return project;
607
607
  } catch {}
608
- currentDir = nodePath.dirname(currentDir);
608
+ currentDir = path.dirname(currentDir);
609
609
  }
610
610
  return null;
611
611
  }
@@ -618,8 +618,8 @@ var ProjectFinderService = class {
618
618
  const content = await fs.readFile(projectJsonPath, "utf-8");
619
619
  const config = JSON.parse(content);
620
620
  const projectConfig = {
621
- name: config.name || nodePath.basename(nodePath.dirname(projectJsonPath)),
622
- root: nodePath.dirname(projectJsonPath),
621
+ name: config.name || path.basename(path.dirname(projectJsonPath)),
622
+ root: path.dirname(projectJsonPath),
623
623
  sourceTemplate: config.sourceTemplate,
624
624
  projectType: config.projectType
625
625
  };
@@ -638,8 +638,8 @@ var ProjectFinderService = class {
638
638
  const content = readFileSync$1(projectJsonPath, "utf-8");
639
639
  const config = JSON.parse(content);
640
640
  const projectConfig = {
641
- name: config.name || nodePath.basename(nodePath.dirname(projectJsonPath)),
642
- root: nodePath.dirname(projectJsonPath),
641
+ name: config.name || path.basename(path.dirname(projectJsonPath)),
642
+ root: path.dirname(projectJsonPath),
643
643
  sourceTemplate: config.sourceTemplate,
644
644
  projectType: config.projectType
645
645
  };
@@ -699,7 +699,7 @@ var ScaffoldProcessingService = class {
699
699
  * Now supports tracking existing files separately from created files
700
700
  */
701
701
  async copyAndProcess(sourcePath, targetPath, variables, createdFiles, existingFiles) {
702
- await this.fileSystem.ensureDir(nodePath.dirname(targetPath));
702
+ await this.fileSystem.ensureDir(path.dirname(targetPath));
703
703
  if (await this.fileSystem.pathExists(targetPath) && existingFiles) {
704
704
  await this.trackExistingFiles(targetPath, existingFiles);
705
705
  return;
@@ -721,7 +721,7 @@ var ScaffoldProcessingService = class {
721
721
  }
722
722
  for (const item of items) {
723
723
  if (!item) continue;
724
- const itemPath = nodePath.join(dirPath, item);
724
+ const itemPath = path.join(dirPath, item);
725
725
  try {
726
726
  const stat$1 = await this.fileSystem.stat(itemPath);
727
727
  if (stat$1.isDirectory()) await this.trackCreatedFilesRecursive(itemPath, createdFiles);
@@ -744,7 +744,7 @@ var ScaffoldProcessingService = class {
744
744
  }
745
745
  for (const item of items) {
746
746
  if (!item) continue;
747
- const itemPath = nodePath.join(dirPath, item);
747
+ const itemPath = path.join(dirPath, item);
748
748
  try {
749
749
  const stat$1 = await this.fileSystem.stat(itemPath);
750
750
  if (stat$1.isDirectory()) await this.trackExistingFilesRecursive(itemPath, existingFiles);
@@ -756,6 +756,27 @@ var ScaffoldProcessingService = class {
756
756
  }
757
757
  };
758
758
 
759
+ //#endregion
760
+ //#region src/utils/fallbacks.ts
761
+ function resolveFallbackConfig(config) {
762
+ if (!config) return {};
763
+ const tool = ("fallbackTool" in config ? config.fallbackTool : void 0) ?? ("fallback-tool" in config ? config["fallback-tool"] : void 0);
764
+ const fallbackConfig = ("fallbackToolConfig" in config ? config.fallbackToolConfig : void 0) ?? ("fallback-tool-config" in config ? config["fallback-tool-config"] : void 0);
765
+ if (tool) return {
766
+ tool,
767
+ config: fallbackConfig
768
+ };
769
+ const firstValidFallback = config.fallbacks?.find((entry) => isFallbackConfigEntry(entry));
770
+ if (!firstValidFallback) return {};
771
+ return {
772
+ tool: firstValidFallback.tool,
773
+ config: firstValidFallback.config
774
+ };
775
+ }
776
+ function isFallbackConfigEntry(entry) {
777
+ return Boolean(entry?.tool && typeof entry.tool === "string");
778
+ }
779
+
759
780
  //#endregion
760
781
  //#region src/utils/generateStableId.ts
761
782
  /**
@@ -871,12 +892,12 @@ async function gitInit(projectPath) {
871
892
  * }
872
893
  */
873
894
  async function findWorkspaceRoot(startPath = process.cwd()) {
874
- let currentPath = nodePath.resolve(startPath);
875
- const rootPath = nodePath.parse(currentPath).root;
895
+ let currentPath = path.resolve(startPath);
896
+ const rootPath = path.parse(currentPath).root;
876
897
  while (true) {
877
- if (await pathExists(nodePath.join(currentPath, ".git"))) return currentPath;
898
+ if (await pathExists(path.join(currentPath, ".git"))) return currentPath;
878
899
  if (currentPath === rootPath) return null;
879
- currentPath = nodePath.dirname(currentPath);
900
+ currentPath = path.dirname(currentPath);
880
901
  }
881
902
  }
882
903
  /**
@@ -958,7 +979,7 @@ async function cloneSubdirectory(repoUrl, branch, subdirectory, targetFolder) {
958
979
  "core.sparseCheckout",
959
980
  "true"
960
981
  ], tempFolder);
961
- await writeFile(nodePath.join(tempFolder, ".git", "info", "sparse-checkout"), `${subdirectory}\n`);
982
+ await writeFile(path.join(tempFolder, ".git", "info", "sparse-checkout"), `${subdirectory}\n`);
962
983
  await execGit([
963
984
  "pull",
964
985
  "--depth=1",
@@ -966,7 +987,7 @@ async function cloneSubdirectory(repoUrl, branch, subdirectory, targetFolder) {
966
987
  "--",
967
988
  branch
968
989
  ], tempFolder);
969
- const sourceDir = nodePath.join(tempFolder, subdirectory);
990
+ const sourceDir = path.join(tempFolder, subdirectory);
970
991
  if (!await pathExists(sourceDir)) throw new Error(`Subdirectory '${subdirectory}' not found in repository at branch '${branch}'`);
971
992
  if (await pathExists(targetFolder)) throw new Error(`Target folder already exists: ${targetFolder}`);
972
993
  await move(sourceDir, targetFolder);
@@ -993,7 +1014,7 @@ async function cloneRepository(repoUrl, targetFolder) {
993
1014
  repoUrl,
994
1015
  targetFolder
995
1016
  ]);
996
- const gitFolder = nodePath.join(targetFolder, ".git");
1017
+ const gitFolder = path.join(targetFolder, ".git");
997
1018
  if (await pathExists(gitFolder)) await remove(gitFolder);
998
1019
  }
999
1020
  /**
@@ -1210,7 +1231,7 @@ const MONOREPO_INDICATOR_FILES = [
1210
1231
  */
1211
1232
  async function detectProjectType(workspaceRoot) {
1212
1233
  const indicators = [];
1213
- const toolkitYamlPath = nodePath.join(workspaceRoot, "toolkit.yaml");
1234
+ const toolkitYamlPath = path.join(workspaceRoot, "toolkit.yaml");
1214
1235
  if (await pathExists(toolkitYamlPath)) try {
1215
1236
  const content = await fs.readFile(toolkitYamlPath, "utf-8");
1216
1237
  const config = yaml.load(content);
@@ -1222,14 +1243,14 @@ async function detectProjectType(workspaceRoot) {
1222
1243
  };
1223
1244
  }
1224
1245
  } catch {}
1225
- for (const filename of MONOREPO_INDICATOR_FILES) if (await pathExists(nodePath.join(workspaceRoot, filename))) {
1246
+ for (const filename of MONOREPO_INDICATOR_FILES) if (await pathExists(path.join(workspaceRoot, filename))) {
1226
1247
  indicators.push(`${filename} found`);
1227
1248
  return {
1228
1249
  projectType: ProjectType.MONOREPO,
1229
1250
  indicators
1230
1251
  };
1231
1252
  }
1232
- const packageJsonPath = nodePath.join(workspaceRoot, "package.json");
1253
+ const packageJsonPath = path.join(workspaceRoot, "package.json");
1233
1254
  if (await pathExists(packageJsonPath)) try {
1234
1255
  if ((await readJson(packageJsonPath)).workspaces) {
1235
1256
  indicators.push("package.json with workspaces found");
@@ -1247,4 +1268,4 @@ async function detectProjectType(workspaceRoot) {
1247
1268
  }
1248
1269
 
1249
1270
  //#endregion
1250
- export { ConfigSource, ProjectConfigResolver, ProjectFinderService, ProjectType, ScaffoldProcessingService, TemplatesManagerService, accessSync, cloneRepository, cloneSubdirectory, copy, detectProjectType, ensureDir, fetchGitHubDirectoryContents, findWorkspaceRoot, generateStableId, gitInit, icons, log, logger, messages, mkdir, mkdirSync, move, parseGitHubUrl, pathExists, pathExistsSync, print, readFile, readFileSync, readJson, readJsonSync, readdir, remove, sections, stat, statSync, writeFile, writeFileSync };
1271
+ export { ConfigSource, ProjectConfigResolver, ProjectFinderService, ProjectType, ScaffoldProcessingService, TemplatesManagerService, accessSync, cloneRepository, cloneSubdirectory, copy, detectProjectType, ensureDir, fetchGitHubDirectoryContents, findWorkspaceRoot, generateStableId, gitInit, icons, log, logger, messages, mkdir, mkdirSync, move, parseGitHubUrl, pathExists, pathExistsSync, print, readFile, readFileSync, readJson, readJsonSync, readdir, remove, resolveFallbackConfig, sections, stat, statSync, writeFile, writeFileSync };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agiflowai/aicode-utils",
3
3
  "description": "Shared utilities and types for AI-powered code generation, scaffolding, and analysis",
4
- "version": "1.0.17",
4
+ "version": "1.0.19",
5
5
  "license": "AGPL-3.0",
6
6
  "author": "AgiflowIO",
7
7
  "repository": {