@hypercli/gen 0.1.1
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/LICENSE +21 -0
- package/README.md +24 -0
- package/dist/actions/communication.d.ts +201 -0
- package/dist/actions/communication.d.ts.map +1 -0
- package/dist/actions/communication.js +515 -0
- package/dist/actions/communication.js.map +1 -0
- package/dist/actions/decorator.d.ts +22 -0
- package/dist/actions/decorator.d.ts.map +1 -0
- package/dist/actions/decorator.js +110 -0
- package/dist/actions/decorator.js.map +1 -0
- package/dist/actions/executor.d.ts +85 -0
- package/dist/actions/executor.d.ts.map +1 -0
- package/dist/actions/executor.js +289 -0
- package/dist/actions/executor.js.map +1 -0
- package/dist/actions/index.d.ts +14 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +15 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/actions/parameter-resolver.d.ts +54 -0
- package/dist/actions/parameter-resolver.d.ts.map +1 -0
- package/dist/actions/parameter-resolver.js +300 -0
- package/dist/actions/parameter-resolver.js.map +1 -0
- package/dist/actions/registry.d.ts +78 -0
- package/dist/actions/registry.d.ts.map +1 -0
- package/dist/actions/registry.js +221 -0
- package/dist/actions/registry.js.map +1 -0
- package/dist/actions/types.d.ts +109 -0
- package/dist/actions/types.d.ts.map +1 -0
- package/dist/actions/types.js +31 -0
- package/dist/actions/types.js.map +1 -0
- package/dist/actions/utils.d.ts +42 -0
- package/dist/actions/utils.d.ts.map +1 -0
- package/dist/actions/utils.js +144 -0
- package/dist/actions/utils.js.map +1 -0
- package/dist/ai/ai-collector.d.ts +52 -0
- package/dist/ai/ai-collector.d.ts.map +1 -0
- package/dist/ai/ai-collector.js +64 -0
- package/dist/ai/ai-collector.js.map +1 -0
- package/dist/ai/ai-config.d.ts +230 -0
- package/dist/ai/ai-config.d.ts.map +1 -0
- package/dist/ai/ai-config.js +8 -0
- package/dist/ai/ai-config.js.map +1 -0
- package/dist/ai/ai-service.d.ts +66 -0
- package/dist/ai/ai-service.d.ts.map +1 -0
- package/dist/ai/ai-service.js +198 -0
- package/dist/ai/ai-service.js.map +1 -0
- package/dist/ai/ai-variable-resolver.d.ts +59 -0
- package/dist/ai/ai-variable-resolver.d.ts.map +1 -0
- package/dist/ai/ai-variable-resolver.js +219 -0
- package/dist/ai/ai-variable-resolver.js.map +1 -0
- package/dist/ai/context-collector.d.ts +30 -0
- package/dist/ai/context-collector.d.ts.map +1 -0
- package/dist/ai/context-collector.js +158 -0
- package/dist/ai/context-collector.js.map +1 -0
- package/dist/ai/cost-tracker.d.ts +41 -0
- package/dist/ai/cost-tracker.d.ts.map +1 -0
- package/dist/ai/cost-tracker.js +131 -0
- package/dist/ai/cost-tracker.js.map +1 -0
- package/dist/ai/env.d.ts +36 -0
- package/dist/ai/env.d.ts.map +1 -0
- package/dist/ai/env.js +100 -0
- package/dist/ai/env.js.map +1 -0
- package/dist/ai/index.d.ts +17 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +25 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/model-router.d.ts +32 -0
- package/dist/ai/model-router.d.ts.map +1 -0
- package/dist/ai/model-router.js +113 -0
- package/dist/ai/model-router.js.map +1 -0
- package/dist/ai/output-validator.d.ts +24 -0
- package/dist/ai/output-validator.d.ts.map +1 -0
- package/dist/ai/output-validator.js +279 -0
- package/dist/ai/output-validator.js.map +1 -0
- package/dist/ai/prompt-assembler.d.ts +30 -0
- package/dist/ai/prompt-assembler.d.ts.map +1 -0
- package/dist/ai/prompt-assembler.js +93 -0
- package/dist/ai/prompt-assembler.js.map +1 -0
- package/dist/ai/prompt-pipeline.d.ts +63 -0
- package/dist/ai/prompt-pipeline.d.ts.map +1 -0
- package/dist/ai/prompt-pipeline.js +119 -0
- package/dist/ai/prompt-pipeline.js.map +1 -0
- package/dist/ai/prompt-template.jig +88 -0
- package/dist/ai/transports/api-transport.d.ts +12 -0
- package/dist/ai/transports/api-transport.d.ts.map +1 -0
- package/dist/ai/transports/api-transport.js +86 -0
- package/dist/ai/transports/api-transport.js.map +1 -0
- package/dist/ai/transports/command-transport.d.ts +20 -0
- package/dist/ai/transports/command-transport.d.ts.map +1 -0
- package/dist/ai/transports/command-transport.js +203 -0
- package/dist/ai/transports/command-transport.js.map +1 -0
- package/dist/ai/transports/index.d.ts +11 -0
- package/dist/ai/transports/index.d.ts.map +1 -0
- package/dist/ai/transports/index.js +10 -0
- package/dist/ai/transports/index.js.map +1 -0
- package/dist/ai/transports/resolve-transport.d.ts +15 -0
- package/dist/ai/transports/resolve-transport.d.ts.map +1 -0
- package/dist/ai/transports/resolve-transport.js +96 -0
- package/dist/ai/transports/resolve-transport.js.map +1 -0
- package/dist/ai/transports/stdout-transport.d.ts +14 -0
- package/dist/ai/transports/stdout-transport.d.ts.map +1 -0
- package/dist/ai/transports/stdout-transport.js +27 -0
- package/dist/ai/transports/stdout-transport.js.map +1 -0
- package/dist/ai/transports/types.d.ts +77 -0
- package/dist/ai/transports/types.d.ts.map +1 -0
- package/dist/ai/transports/types.js +8 -0
- package/dist/ai/transports/types.js.map +1 -0
- package/dist/commands/cookbook/info.d.ts +22 -0
- package/dist/commands/cookbook/info.d.ts.map +1 -0
- package/dist/commands/cookbook/info.js +217 -0
- package/dist/commands/cookbook/info.js.map +1 -0
- package/dist/commands/cookbook/list.d.ts +20 -0
- package/dist/commands/cookbook/list.d.ts.map +1 -0
- package/dist/commands/cookbook/list.js +133 -0
- package/dist/commands/cookbook/list.js.map +1 -0
- package/dist/commands/gen.d.ts +65 -0
- package/dist/commands/gen.d.ts.map +1 -0
- package/dist/commands/gen.js +478 -0
- package/dist/commands/gen.js.map +1 -0
- package/dist/commands/recipe/info.d.ts +18 -0
- package/dist/commands/recipe/info.d.ts.map +1 -0
- package/dist/commands/recipe/info.js +89 -0
- package/dist/commands/recipe/info.js.map +1 -0
- package/dist/commands/recipe/list.d.ts +29 -0
- package/dist/commands/recipe/list.d.ts.map +1 -0
- package/dist/commands/recipe/list.js +215 -0
- package/dist/commands/recipe/list.js.map +1 -0
- package/dist/commands/recipe/run.d.ts +44 -0
- package/dist/commands/recipe/run.d.ts.map +1 -0
- package/dist/commands/recipe/run.js +239 -0
- package/dist/commands/recipe/run.js.map +1 -0
- package/dist/commands/recipe/validate.d.ts +19 -0
- package/dist/commands/recipe/validate.d.ts.map +1 -0
- package/dist/commands/recipe/validate.js +66 -0
- package/dist/commands/recipe/validate.js.map +1 -0
- package/dist/discovery/generator-discovery.d.ts +130 -0
- package/dist/discovery/generator-discovery.d.ts.map +1 -0
- package/dist/discovery/generator-discovery.js +674 -0
- package/dist/discovery/generator-discovery.js.map +1 -0
- package/dist/discovery/index.d.ts +8 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +7 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/hooks/command-not-found.d.ts +18 -0
- package/dist/hooks/command-not-found.d.ts.map +1 -0
- package/dist/hooks/command-not-found.js +182 -0
- package/dist/hooks/command-not-found.js.map +1 -0
- package/dist/hooks/suggest.d.ts +13 -0
- package/dist/hooks/suggest.d.ts.map +1 -0
- package/dist/hooks/suggest.js +28 -0
- package/dist/hooks/suggest.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/base-command.d.ts +26 -0
- package/dist/lib/base-command.d.ts.map +1 -0
- package/dist/lib/base-command.js +24 -0
- package/dist/lib/base-command.js.map +1 -0
- package/dist/lib/flags.d.ts +33 -0
- package/dist/lib/flags.d.ts.map +1 -0
- package/dist/lib/flags.js +64 -0
- package/dist/lib/flags.js.map +1 -0
- package/dist/ops/add.d.ts +4 -0
- package/dist/ops/add.d.ts.map +1 -0
- package/dist/ops/add.js +85 -0
- package/dist/ops/add.js.map +1 -0
- package/dist/ops/inject.d.ts +4 -0
- package/dist/ops/inject.d.ts.map +1 -0
- package/dist/ops/inject.js +28 -0
- package/dist/ops/inject.js.map +1 -0
- package/dist/ops/injector.d.ts +4 -0
- package/dist/ops/injector.d.ts.map +1 -0
- package/dist/ops/injector.js +68 -0
- package/dist/ops/injector.js.map +1 -0
- package/dist/ops/result.d.ts +3 -0
- package/dist/ops/result.d.ts.map +1 -0
- package/dist/ops/result.js +8 -0
- package/dist/ops/result.js.map +1 -0
- package/dist/prompts/interactive-prompts.d.ts +152 -0
- package/dist/prompts/interactive-prompts.d.ts.map +1 -0
- package/dist/prompts/interactive-prompts.js +574 -0
- package/dist/prompts/interactive-prompts.js.map +1 -0
- package/dist/recipe-engine/group-executor.d.ts +97 -0
- package/dist/recipe-engine/group-executor.d.ts.map +1 -0
- package/dist/recipe-engine/group-executor.js +293 -0
- package/dist/recipe-engine/group-executor.js.map +1 -0
- package/dist/recipe-engine/index.d.ts +112 -0
- package/dist/recipe-engine/index.d.ts.map +1 -0
- package/dist/recipe-engine/index.js +223 -0
- package/dist/recipe-engine/index.js.map +1 -0
- package/dist/recipe-engine/output-evaluator.d.ts +28 -0
- package/dist/recipe-engine/output-evaluator.d.ts.map +1 -0
- package/dist/recipe-engine/output-evaluator.js +78 -0
- package/dist/recipe-engine/output-evaluator.js.map +1 -0
- package/dist/recipe-engine/recipe-engine.d.ts +227 -0
- package/dist/recipe-engine/recipe-engine.d.ts.map +1 -0
- package/dist/recipe-engine/recipe-engine.js +1036 -0
- package/dist/recipe-engine/recipe-engine.js.map +1 -0
- package/dist/recipe-engine/step-executor.d.ts +172 -0
- package/dist/recipe-engine/step-executor.d.ts.map +1 -0
- package/dist/recipe-engine/step-executor.js +802 -0
- package/dist/recipe-engine/step-executor.js.map +1 -0
- package/dist/recipe-engine/tools/action-tool.d.ts +103 -0
- package/dist/recipe-engine/tools/action-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/action-tool.js +473 -0
- package/dist/recipe-engine/tools/action-tool.js.map +1 -0
- package/dist/recipe-engine/tools/ai-tool.d.ts +26 -0
- package/dist/recipe-engine/tools/ai-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/ai-tool.js +233 -0
- package/dist/recipe-engine/tools/ai-tool.js.map +1 -0
- package/dist/recipe-engine/tools/base.d.ts +214 -0
- package/dist/recipe-engine/tools/base.d.ts.map +1 -0
- package/dist/recipe-engine/tools/base.js +397 -0
- package/dist/recipe-engine/tools/base.js.map +1 -0
- package/dist/recipe-engine/tools/codemod-tool.d.ts +130 -0
- package/dist/recipe-engine/tools/codemod-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/codemod-tool.js +786 -0
- package/dist/recipe-engine/tools/codemod-tool.js.map +1 -0
- package/dist/recipe-engine/tools/ensure-dirs-tool.d.ts +21 -0
- package/dist/recipe-engine/tools/ensure-dirs-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/ensure-dirs-tool.js +130 -0
- package/dist/recipe-engine/tools/ensure-dirs-tool.js.map +1 -0
- package/dist/recipe-engine/tools/index.d.ts +126 -0
- package/dist/recipe-engine/tools/index.d.ts.map +1 -0
- package/dist/recipe-engine/tools/index.js +290 -0
- package/dist/recipe-engine/tools/index.js.map +1 -0
- package/dist/recipe-engine/tools/install-tool.d.ts +20 -0
- package/dist/recipe-engine/tools/install-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/install-tool.js +194 -0
- package/dist/recipe-engine/tools/install-tool.js.map +1 -0
- package/dist/recipe-engine/tools/parallel-tool.d.ts +21 -0
- package/dist/recipe-engine/tools/parallel-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/parallel-tool.js +134 -0
- package/dist/recipe-engine/tools/parallel-tool.js.map +1 -0
- package/dist/recipe-engine/tools/patch-tool.d.ts +21 -0
- package/dist/recipe-engine/tools/patch-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/patch-tool.js +248 -0
- package/dist/recipe-engine/tools/patch-tool.js.map +1 -0
- package/dist/recipe-engine/tools/prompt-tool.d.ts +25 -0
- package/dist/recipe-engine/tools/prompt-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/prompt-tool.js +162 -0
- package/dist/recipe-engine/tools/prompt-tool.js.map +1 -0
- package/dist/recipe-engine/tools/query-tool.d.ts +21 -0
- package/dist/recipe-engine/tools/query-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/query-tool.js +249 -0
- package/dist/recipe-engine/tools/query-tool.js.map +1 -0
- package/dist/recipe-engine/tools/recipe-tool.d.ts +103 -0
- package/dist/recipe-engine/tools/recipe-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/recipe-tool.js +617 -0
- package/dist/recipe-engine/tools/recipe-tool.js.map +1 -0
- package/dist/recipe-engine/tools/registry.d.ts +151 -0
- package/dist/recipe-engine/tools/registry.d.ts.map +1 -0
- package/dist/recipe-engine/tools/registry.js +244 -0
- package/dist/recipe-engine/tools/registry.js.map +1 -0
- package/dist/recipe-engine/tools/sequence-tool.d.ts +22 -0
- package/dist/recipe-engine/tools/sequence-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/sequence-tool.js +122 -0
- package/dist/recipe-engine/tools/sequence-tool.js.map +1 -0
- package/dist/recipe-engine/tools/shell-tool.d.ts +25 -0
- package/dist/recipe-engine/tools/shell-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/shell-tool.js +149 -0
- package/dist/recipe-engine/tools/shell-tool.js.map +1 -0
- package/dist/recipe-engine/tools/template-tool.d.ts +88 -0
- package/dist/recipe-engine/tools/template-tool.d.ts.map +1 -0
- package/dist/recipe-engine/tools/template-tool.js +613 -0
- package/dist/recipe-engine/tools/template-tool.js.map +1 -0
- package/dist/recipe-engine/types.d.ts +963 -0
- package/dist/recipe-engine/types.d.ts.map +1 -0
- package/dist/recipe-engine/types.js +94 -0
- package/dist/recipe-engine/types.js.map +1 -0
- package/dist/template-engines/ai-tags.d.ts +26 -0
- package/dist/template-engines/ai-tags.d.ts.map +1 -0
- package/dist/template-engines/ai-tags.js +233 -0
- package/dist/template-engines/ai-tags.js.map +1 -0
- package/dist/template-engines/index.d.ts +8 -0
- package/dist/template-engines/index.d.ts.map +1 -0
- package/dist/template-engines/index.js +8 -0
- package/dist/template-engines/index.js.map +1 -0
- package/dist/template-engines/jig-engine.d.ts +47 -0
- package/dist/template-engines/jig-engine.d.ts.map +1 -0
- package/dist/template-engines/jig-engine.js +173 -0
- package/dist/template-engines/jig-engine.js.map +1 -0
- package/dist/utils/coerce-value.d.ts +7 -0
- package/dist/utils/coerce-value.d.ts.map +1 -0
- package/dist/utils/coerce-value.js +18 -0
- package/dist/utils/coerce-value.js.map +1 -0
- package/dist/utils/diff.d.ts +8 -0
- package/dist/utils/diff.d.ts.map +1 -0
- package/dist/utils/diff.js +10 -0
- package/dist/utils/diff.js.map +1 -0
- package/dist/utils/global-packages.d.ts +11 -0
- package/dist/utils/global-packages.d.ts.map +1 -0
- package/dist/utils/global-packages.js +88 -0
- package/dist/utils/global-packages.js.map +1 -0
- package/dist/utils/pager.d.ts +6 -0
- package/dist/utils/pager.d.ts.map +1 -0
- package/dist/utils/pager.js +41 -0
- package/dist/utils/pager.js.map +1 -0
- package/help/cookbook/info.md +35 -0
- package/help/cookbook/list.md +37 -0
- package/help/gen.md +26 -0
- package/help/recipe/run.md +52 -0
- package/help/recipe/validate.md +51 -0
- package/oclif.manifest.json +580 -0
- package/package.json +120 -0
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generator Auto-Discovery
|
|
3
|
+
*
|
|
4
|
+
* Automatically discovers and registers generators from various sources
|
|
5
|
+
*/
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { findProjectRoot } from "@hypercli/core";
|
|
8
|
+
import { parseKitFile } from "@hypercli/core";
|
|
9
|
+
import { discoverCookbooksInKit } from "@hypercli/core";
|
|
10
|
+
import createDebug from "debug";
|
|
11
|
+
import fs from "fs-extra";
|
|
12
|
+
import { glob } from "glob";
|
|
13
|
+
import { isActionFunction } from "#actions/decorator";
|
|
14
|
+
import { getGlobalPackages } from "#utils/global-packages";
|
|
15
|
+
const debug = createDebug("hypergen:discovery");
|
|
16
|
+
export class GeneratorDiscovery {
|
|
17
|
+
options;
|
|
18
|
+
discoveredGenerators = new Map();
|
|
19
|
+
projectRoot;
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.options = options;
|
|
22
|
+
// Find project root with monorepo detection
|
|
23
|
+
const projectInfo = findProjectRoot(this.options.startDir);
|
|
24
|
+
this.projectRoot = projectInfo.workspaceRoot;
|
|
25
|
+
debug("Using project root: %s (isMonorepo: %s)", this.projectRoot, projectInfo.isMonorepo);
|
|
26
|
+
// Always include .hyper/kits in addition to any configured directories
|
|
27
|
+
const defaultDirs = ["recipes", "cookbooks"];
|
|
28
|
+
const configDirs = this.options.directories || [];
|
|
29
|
+
const allDirs = [...new Set([...defaultDirs, ...configDirs, ".hyper/kits"])];
|
|
30
|
+
this.options = {
|
|
31
|
+
patterns: ["**/*.{js,ts,mjs}", "**/template.yml", "**/generator.{js,ts,mjs}"],
|
|
32
|
+
excludePatterns: ["**/node_modules/**", "**/dist/**", "**/*.test.*", "**/*.spec.*"],
|
|
33
|
+
enabledSources: ["local", "workspace", "global"],
|
|
34
|
+
...this.options,
|
|
35
|
+
directories: allDirs, // Always use merged directories
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Discover all generators from enabled sources
|
|
40
|
+
*/
|
|
41
|
+
async discoverAll() {
|
|
42
|
+
debug("Starting generator discovery with sources: %o", this.options.enabledSources);
|
|
43
|
+
const discoveries = [];
|
|
44
|
+
if (this.options.enabledSources?.includes("local")) {
|
|
45
|
+
const localGenerators = await this.discoverLocal();
|
|
46
|
+
discoveries.push(...localGenerators);
|
|
47
|
+
}
|
|
48
|
+
if (this.options.enabledSources?.includes("workspace")) {
|
|
49
|
+
const workspaceGenerators = await this.discoverWorkspace();
|
|
50
|
+
discoveries.push(...workspaceGenerators);
|
|
51
|
+
}
|
|
52
|
+
if (this.options.enabledSources?.includes("npm")) {
|
|
53
|
+
const npmGenerators = await this.discoverNpm();
|
|
54
|
+
discoveries.push(...npmGenerators);
|
|
55
|
+
}
|
|
56
|
+
if (this.options.enabledSources?.includes("git")) {
|
|
57
|
+
const gitGenerators = await this.discoverGit();
|
|
58
|
+
discoveries.push(...gitGenerators);
|
|
59
|
+
}
|
|
60
|
+
if (this.options.enabledSources?.includes("global")) {
|
|
61
|
+
const globalGenerators = await this.discoverGlobal();
|
|
62
|
+
discoveries.push(...globalGenerators);
|
|
63
|
+
}
|
|
64
|
+
// Create virtual "workspace" kit for standalone cookbooks/recipes
|
|
65
|
+
const workspaceKit = await this.createWorkspaceKit(discoveries);
|
|
66
|
+
if (workspaceKit) {
|
|
67
|
+
discoveries.push(workspaceKit);
|
|
68
|
+
}
|
|
69
|
+
// Enrich all discovered generators with kit metadata
|
|
70
|
+
await Promise.all(discoveries.map((gen) => this.enrichWithKitMetadata(gen)));
|
|
71
|
+
// Store discovered generators
|
|
72
|
+
for (const generator of discoveries) {
|
|
73
|
+
this.discoveredGenerators.set(generator.name, generator);
|
|
74
|
+
}
|
|
75
|
+
debug("Discovery complete: found %d generators", discoveries.length);
|
|
76
|
+
return discoveries;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Discover local generators in template directories
|
|
80
|
+
*/
|
|
81
|
+
async discoverLocal() {
|
|
82
|
+
debug("Discovering local generators in directories: %o", this.options.directories);
|
|
83
|
+
const generators = [];
|
|
84
|
+
const discoveredKitPaths = new Set();
|
|
85
|
+
for (const dir of this.options.directories || []) {
|
|
86
|
+
const fullPath = path.resolve(this.projectRoot, dir);
|
|
87
|
+
if (!(await fs.pathExists(fullPath))) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
debug("Scanning directory: %s", fullPath);
|
|
91
|
+
// Look for kit.yml files first (recipe-based kits)
|
|
92
|
+
const kitYmlFiles = await this.findKitYmlFiles(fullPath);
|
|
93
|
+
for (const kitYmlPath of kitYmlFiles) {
|
|
94
|
+
const kitPath = path.dirname(kitYmlPath);
|
|
95
|
+
const kitName = path.basename(kitPath);
|
|
96
|
+
// Avoid duplicates
|
|
97
|
+
if (discoveredKitPaths.has(kitPath)) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
discoveredKitPaths.add(kitPath);
|
|
101
|
+
try {
|
|
102
|
+
const parsedKit = await parseKitFile(kitYmlPath);
|
|
103
|
+
if (parsedKit.isValid) {
|
|
104
|
+
generators.push({
|
|
105
|
+
name: kitName,
|
|
106
|
+
source: "local",
|
|
107
|
+
path: kitPath,
|
|
108
|
+
actions: [],
|
|
109
|
+
metadata: {
|
|
110
|
+
description: parsedKit.config.description,
|
|
111
|
+
version: parsedKit.config.version,
|
|
112
|
+
author: parsedKit.config.author,
|
|
113
|
+
license: parsedKit.config.license,
|
|
114
|
+
keywords: parsedKit.config.keywords,
|
|
115
|
+
tags: parsedKit.config.tags,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
debug("Discovered kit from kit.yml: %s at %s", kitName, kitPath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
debug("Failed to parse kit.yml at %s: %s", kitYmlPath, error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Look for action files (traditional action-based generators)
|
|
126
|
+
const actionFiles = await this.findActionFiles(fullPath);
|
|
127
|
+
// Look for template.yml files
|
|
128
|
+
const templateFiles = await this.findTemplateFiles(fullPath);
|
|
129
|
+
// Group by generator name (typically directory name)
|
|
130
|
+
const generatorGroups = this.groupFilesByGenerator(actionFiles, templateFiles, fullPath);
|
|
131
|
+
for (const [generatorName, files] of generatorGroups) {
|
|
132
|
+
const generatorPath = path.join(fullPath, generatorName);
|
|
133
|
+
// Skip if already discovered as a kit
|
|
134
|
+
if (discoveredKitPaths.has(generatorPath)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const actions = await this.extractActionsFromFiles(files.actions);
|
|
138
|
+
generators.push({
|
|
139
|
+
name: generatorName,
|
|
140
|
+
source: "local",
|
|
141
|
+
path: generatorPath,
|
|
142
|
+
actions,
|
|
143
|
+
metadata: await this.extractGeneratorMetadata(files.templates[0]),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
debug("Found %d local generators", generators.length);
|
|
148
|
+
return generators;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Find kit.yml files in a directory
|
|
152
|
+
*/
|
|
153
|
+
async findKitYmlFiles(directory) {
|
|
154
|
+
const matches = await glob("**/kit.yml", {
|
|
155
|
+
cwd: directory,
|
|
156
|
+
ignore: this.options.excludePatterns,
|
|
157
|
+
});
|
|
158
|
+
return matches.map((f) => path.resolve(directory, f));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Discover workspace generators (monorepo packages)
|
|
162
|
+
*/
|
|
163
|
+
async discoverWorkspace() {
|
|
164
|
+
debug("Discovering workspace generators");
|
|
165
|
+
const generators = [];
|
|
166
|
+
const cwd = process.cwd();
|
|
167
|
+
// Look for workspace packages that might contain generators
|
|
168
|
+
const workspacePatterns = [
|
|
169
|
+
"packages/*/generators/**",
|
|
170
|
+
"apps/*/generators/**",
|
|
171
|
+
"tools/generators/**",
|
|
172
|
+
];
|
|
173
|
+
for (const pattern of workspacePatterns) {
|
|
174
|
+
const matches = await glob(pattern, {
|
|
175
|
+
cwd,
|
|
176
|
+
});
|
|
177
|
+
for (const match of matches) {
|
|
178
|
+
const fullPath = path.resolve(cwd, match);
|
|
179
|
+
const packageName = this.extractPackageNameFromPath(match);
|
|
180
|
+
const actionFiles = await this.findActionFiles(fullPath);
|
|
181
|
+
const actions = await this.extractActionsFromFiles(actionFiles);
|
|
182
|
+
if (actions.length > 0) {
|
|
183
|
+
generators.push({
|
|
184
|
+
name: packageName,
|
|
185
|
+
source: "workspace",
|
|
186
|
+
path: fullPath,
|
|
187
|
+
actions,
|
|
188
|
+
metadata: {
|
|
189
|
+
description: `Workspace generator from ${match}`,
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
debug("Found %d workspace generators", generators.length);
|
|
196
|
+
return generators;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Discover npm-installed generators
|
|
200
|
+
*/
|
|
201
|
+
async discoverNpm() {
|
|
202
|
+
debug("Discovering npm generators");
|
|
203
|
+
const generators = [];
|
|
204
|
+
const nodeModulesPath = path.resolve(process.cwd(), "node_modules");
|
|
205
|
+
if (!(await fs.pathExists(nodeModulesPath))) {
|
|
206
|
+
return generators;
|
|
207
|
+
}
|
|
208
|
+
// Look for packages that follow hypergen generator conventions
|
|
209
|
+
const packageDirs = await fs.readdir(nodeModulesPath);
|
|
210
|
+
for (const packageDir of packageDirs) {
|
|
211
|
+
if (packageDir.startsWith("."))
|
|
212
|
+
continue;
|
|
213
|
+
const packagePath = path.join(nodeModulesPath, packageDir);
|
|
214
|
+
const packageJsonPath = path.join(packagePath, "package.json");
|
|
215
|
+
if (!(await fs.pathExists(packageJsonPath)))
|
|
216
|
+
continue;
|
|
217
|
+
try {
|
|
218
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
219
|
+
// Check if package is a hypergen generator
|
|
220
|
+
if (this.isHypergenPackage(packageJson)) {
|
|
221
|
+
const generatorPath = path.join(packagePath, "generators");
|
|
222
|
+
if (await fs.pathExists(generatorPath)) {
|
|
223
|
+
const actionFiles = await this.findActionFiles(generatorPath);
|
|
224
|
+
const actions = await this.extractActionsFromFiles(actionFiles);
|
|
225
|
+
generators.push({
|
|
226
|
+
name: packageJson.name,
|
|
227
|
+
source: "npm",
|
|
228
|
+
path: generatorPath,
|
|
229
|
+
actions,
|
|
230
|
+
metadata: {
|
|
231
|
+
description: packageJson.description,
|
|
232
|
+
version: packageJson.version,
|
|
233
|
+
author: packageJson.author,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
debug("Failed to read package.json for %s: %s", packageDir, error);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
debug("Found %d npm generators", generators.length);
|
|
244
|
+
return generators;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Discover git-based generators (placeholder for future implementation)
|
|
248
|
+
*/
|
|
249
|
+
async discoverGit() {
|
|
250
|
+
debug("Git discovery not yet implemented");
|
|
251
|
+
return [];
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Discover global packages (npm/bun global installs)
|
|
255
|
+
*/
|
|
256
|
+
async discoverGlobal() {
|
|
257
|
+
debug("Discovering global generators via engine");
|
|
258
|
+
const generators = [];
|
|
259
|
+
try {
|
|
260
|
+
const globalPackages = await getGlobalPackages();
|
|
261
|
+
debug("Found %d global packages installed: %o", globalPackages.length, globalPackages.map((p) => p.name));
|
|
262
|
+
for (const pkg of globalPackages) {
|
|
263
|
+
// Filter by naming convention
|
|
264
|
+
const isHyperKit = pkg.name.endsWith("-hyper-kit") || pkg.name.startsWith("@kit/");
|
|
265
|
+
if (isHyperKit) {
|
|
266
|
+
debug("Checking potential kit: %s at %s", pkg.name, pkg.path);
|
|
267
|
+
await this.checkAndAddGlobalPackage(pkg.path, generators);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
debug("Global discovery failed: %s", error);
|
|
273
|
+
}
|
|
274
|
+
debug("Found %d global generators", generators.length);
|
|
275
|
+
return generators;
|
|
276
|
+
}
|
|
277
|
+
async checkAndAddGlobalPackage(packagePath, generators) {
|
|
278
|
+
try {
|
|
279
|
+
const packageJsonPath = path.join(packagePath, "package.json");
|
|
280
|
+
if (!(await fs.pathExists(packageJsonPath)))
|
|
281
|
+
return;
|
|
282
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
283
|
+
const packageName = packageJson.name;
|
|
284
|
+
// Check name requirement: ends with -hyper-kit or starts with @kit/
|
|
285
|
+
const isHyperKit = packageName.endsWith("-hyper-kit") || packageName.startsWith("@kit/");
|
|
286
|
+
if (!isHyperKit)
|
|
287
|
+
return;
|
|
288
|
+
// It's a match! Check for generator content (actions/templates)
|
|
289
|
+
// Usually in a 'generators' dir or root?
|
|
290
|
+
// The `discoverNpm` logic checks for `generators` dir.
|
|
291
|
+
// The prompt didn't specify structure, but "hypergen kit my-templates" implies it acts like a kit.
|
|
292
|
+
// We'll stick to the convention of looking for a 'generators' folder OR
|
|
293
|
+
// just treat the root as a potential generator if it has actions/templates.
|
|
294
|
+
// Let's try `generators` dir first to match npm logic, and fall back to root?
|
|
295
|
+
// Actually `npm` discovery explicitly looks for `generators` subdir.
|
|
296
|
+
// "Will hypergen be able to find the kit...?"
|
|
297
|
+
// If it's a kit package, it probably follows the kit structure.
|
|
298
|
+
// Let's look for `generators` folder logic first, similar to discoverNpm
|
|
299
|
+
const generatorPath = path.join(packagePath, "generators");
|
|
300
|
+
const hasGeneratorsDir = await fs.pathExists(generatorPath);
|
|
301
|
+
if (!hasGeneratorsDir) {
|
|
302
|
+
// Fallback: maybe the package IS the generator (root) if it has template.yml or actions
|
|
303
|
+
// But `discoverNpm` enforces `generators` subdir.
|
|
304
|
+
// Let's be a bit more flexible for global kits or stick to `npm` convention?
|
|
305
|
+
// existing `discoverNpm` logic:
|
|
306
|
+
// if (await fs.pathExists(generatorPath)) { ... }
|
|
307
|
+
// Let's stick to that for consistency, but maybe allow root if template.yml exists?
|
|
308
|
+
// For now, I'll match `discoverNpm` logic but applying to these filtered packages.
|
|
309
|
+
// Actually, if the package name is explicitly `@kits/kit`, the user command `hypergen kit my-templates`
|
|
310
|
+
// implies we are looking for a generator named `kit`.
|
|
311
|
+
// If the package is `my-hyper-kit`, and inside it has `generators/my-templates`...
|
|
312
|
+
// The user said `hypergen kit my-templates`.
|
|
313
|
+
// If the package IS the kit, maybe the generator name is the package name?
|
|
314
|
+
// Or if the package contains multiple generators?
|
|
315
|
+
// `discoverNpm` uses `packageJson.name` as the generator name!
|
|
316
|
+
// `generators.push({ name: packageJson.name ... })`
|
|
317
|
+
// And it sets path to `path.join(packagePath, 'generators')`.
|
|
318
|
+
// This implies the `generators` folder contains the actions/code.
|
|
319
|
+
// I will follow the same pattern.
|
|
320
|
+
if (!hasGeneratorsDir)
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (hasGeneratorsDir) {
|
|
324
|
+
const actionFiles = await this.findActionFiles(generatorPath);
|
|
325
|
+
const actions = await this.extractActionsFromFiles(actionFiles);
|
|
326
|
+
generators.push({
|
|
327
|
+
name: packageName,
|
|
328
|
+
source: "global",
|
|
329
|
+
path: generatorPath,
|
|
330
|
+
actions,
|
|
331
|
+
metadata: {
|
|
332
|
+
description: packageJson.description,
|
|
333
|
+
version: packageJson.version,
|
|
334
|
+
author: packageJson.author,
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
catch (e) {
|
|
340
|
+
debug("Error checking global package %s: %s", packagePath, e);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Get a discovered generator by name
|
|
345
|
+
*/
|
|
346
|
+
getGenerator(name) {
|
|
347
|
+
return this.discoveredGenerators.get(name);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Get all discovered generators
|
|
351
|
+
*/
|
|
352
|
+
getGenerators() {
|
|
353
|
+
return Array.from(this.discoveredGenerators.values());
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Get generators by source
|
|
357
|
+
*/
|
|
358
|
+
getGeneratorsBySource(source) {
|
|
359
|
+
return this.getGenerators().filter((g) => g.source === source);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Find action files in a directory
|
|
363
|
+
*/
|
|
364
|
+
async findActionFiles(directory) {
|
|
365
|
+
debug("Finding action files in directory: %s", directory);
|
|
366
|
+
// Filter patterns that match JavaScript/TypeScript files
|
|
367
|
+
const patterns = this.options.patterns?.filter((p) => p.includes(".js") || p.includes(".ts") || p.includes(".mjs") || p.includes("{js,ts,mjs}")) || ["**/*.{js,ts,mjs}"];
|
|
368
|
+
debug("Using patterns: %o", patterns);
|
|
369
|
+
debug("Exclude patterns: %o", this.options.excludePatterns);
|
|
370
|
+
const files = [];
|
|
371
|
+
for (const pattern of patterns) {
|
|
372
|
+
debug("Searching with pattern: %s in directory: %s", pattern, directory);
|
|
373
|
+
const matches = await glob(pattern, {
|
|
374
|
+
cwd: directory,
|
|
375
|
+
ignore: this.options.excludePatterns,
|
|
376
|
+
});
|
|
377
|
+
debug("Pattern %s found %d matches: %o", pattern, matches.length, matches);
|
|
378
|
+
files.push(...matches.map((f) => path.resolve(directory, f)));
|
|
379
|
+
}
|
|
380
|
+
debug("Total action files found: %d", files.length);
|
|
381
|
+
return files;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Find template.yml files in a directory
|
|
385
|
+
*/
|
|
386
|
+
async findTemplateFiles(directory) {
|
|
387
|
+
const matches = await glob("**/template.yml", {
|
|
388
|
+
cwd: directory,
|
|
389
|
+
ignore: this.options.excludePatterns,
|
|
390
|
+
});
|
|
391
|
+
return matches.map((f) => path.resolve(directory, f));
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Group files by generator name
|
|
395
|
+
*/
|
|
396
|
+
groupFilesByGenerator(actionFiles, templateFiles, baseDir) {
|
|
397
|
+
const groups = new Map();
|
|
398
|
+
// Group action files
|
|
399
|
+
for (const file of actionFiles) {
|
|
400
|
+
const relativePath = path.relative(baseDir, file);
|
|
401
|
+
const generatorName = relativePath.split(path.sep)[0];
|
|
402
|
+
if (!groups.has(generatorName)) {
|
|
403
|
+
groups.set(generatorName, { actions: [], templates: [] });
|
|
404
|
+
}
|
|
405
|
+
groups.get(generatorName)?.actions.push(file);
|
|
406
|
+
}
|
|
407
|
+
// Group template files
|
|
408
|
+
for (const file of templateFiles) {
|
|
409
|
+
const relativePath = path.relative(baseDir, file);
|
|
410
|
+
const generatorName = relativePath.split(path.sep)[0];
|
|
411
|
+
if (!groups.has(generatorName)) {
|
|
412
|
+
groups.set(generatorName, { actions: [], templates: [] });
|
|
413
|
+
}
|
|
414
|
+
groups.get(generatorName)?.templates.push(file);
|
|
415
|
+
}
|
|
416
|
+
return groups;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Extract action names from files by loading and checking for @action decorators
|
|
420
|
+
*/
|
|
421
|
+
async extractActionsFromFiles(files) {
|
|
422
|
+
const actions = [];
|
|
423
|
+
for (const file of files) {
|
|
424
|
+
try {
|
|
425
|
+
// Import the module and check for decorated actions
|
|
426
|
+
// This will also register the actions via their decorators
|
|
427
|
+
const module = await this.importModule(file);
|
|
428
|
+
for (const [exportName, exportValue] of Object.entries(module)) {
|
|
429
|
+
if (typeof exportValue === "function" && isActionFunction(exportValue)) {
|
|
430
|
+
actions.push(exportName);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
debug("Failed to load module %s: %s", file, error);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return actions;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Import module with proper handling for TypeScript files
|
|
442
|
+
*/
|
|
443
|
+
async importModule(filePath) {
|
|
444
|
+
try {
|
|
445
|
+
// Convert to absolute path for consistent importing
|
|
446
|
+
const absolutePath = path.resolve(filePath);
|
|
447
|
+
// Use Bun's built-in TypeScript support with file:// protocol
|
|
448
|
+
const fileUrl = `file://${absolutePath}`;
|
|
449
|
+
debug("Importing module: %s", fileUrl);
|
|
450
|
+
const module = await import(fileUrl);
|
|
451
|
+
debug("Successfully imported module with exports: %o", Object.keys(module));
|
|
452
|
+
return module;
|
|
453
|
+
}
|
|
454
|
+
catch (error) {
|
|
455
|
+
debug("Failed to import module %s: %s", filePath, error.message);
|
|
456
|
+
// For debugging, let's still try the old way as fallback
|
|
457
|
+
try {
|
|
458
|
+
return await import(filePath);
|
|
459
|
+
}
|
|
460
|
+
catch (fallbackError) {
|
|
461
|
+
debug("Fallback import also failed: %s", fallbackError.message);
|
|
462
|
+
throw error; // throw original error
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Register all discovered actions with the ActionRegistry
|
|
468
|
+
* This should be called after discovery to ensure actions are available
|
|
469
|
+
*/
|
|
470
|
+
async registerDiscoveredActions() {
|
|
471
|
+
debug("Registering discovered actions...");
|
|
472
|
+
const generators = this.getGenerators();
|
|
473
|
+
debug("Found %d generators to register", generators.length);
|
|
474
|
+
for (const generator of generators) {
|
|
475
|
+
debug("Looking for action files in generator: %s at path: %s", generator.name, generator.path);
|
|
476
|
+
const actionFiles = await this.findActionFiles(generator.path);
|
|
477
|
+
debug("Found %d action files for generator %s: %o", actionFiles.length, generator.name, actionFiles);
|
|
478
|
+
for (const file of actionFiles) {
|
|
479
|
+
try {
|
|
480
|
+
debug("Attempting to import action file: %s", file);
|
|
481
|
+
// Import the file to trigger decorator registration
|
|
482
|
+
await this.importModule(file);
|
|
483
|
+
debug("Successfully loaded action file: %s", file);
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
debug("Failed to load action file %s: %s", file, error.message);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
debug("Action registration complete");
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Extract generator metadata from template.yml
|
|
494
|
+
*/
|
|
495
|
+
async extractGeneratorMetadata(templateFile) {
|
|
496
|
+
if (!templateFile || !(await fs.pathExists(templateFile))) {
|
|
497
|
+
return undefined;
|
|
498
|
+
}
|
|
499
|
+
try {
|
|
500
|
+
// This would integrate with the template.yml parser from Phase 1
|
|
501
|
+
// For now, return basic metadata
|
|
502
|
+
return {
|
|
503
|
+
description: "Local generator",
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
catch (error) {
|
|
507
|
+
debug("Failed to parse template metadata from %s: %s", templateFile, error);
|
|
508
|
+
return undefined;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Extract package name from workspace path
|
|
513
|
+
*/
|
|
514
|
+
extractPackageNameFromPath(workspacePath) {
|
|
515
|
+
const parts = workspacePath.split(path.sep);
|
|
516
|
+
return parts.slice(0, 2).join("-"); // e.g., "packages-ui" or "apps-web"
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Check if package.json indicates a hypergen generator package
|
|
520
|
+
*/
|
|
521
|
+
isHypergenPackage(packageJson) {
|
|
522
|
+
return (packageJson.keywords?.includes("hypergen") ||
|
|
523
|
+
packageJson.keywords?.includes("generator") ||
|
|
524
|
+
packageJson.name?.includes("hypergen-") ||
|
|
525
|
+
packageJson.hypergen !== undefined);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Create a virtual "workspace" kit containing standalone cookbooks and recipes
|
|
529
|
+
* that don't belong to any installed kit
|
|
530
|
+
*/
|
|
531
|
+
async createWorkspaceKit(discoveries) {
|
|
532
|
+
debug("Creating virtual workspace kit for standalone cookbooks/recipes");
|
|
533
|
+
const workspaceItems = {
|
|
534
|
+
actions: [],
|
|
535
|
+
templates: [],
|
|
536
|
+
};
|
|
537
|
+
// Check default workspace directories (excluding .hyper/kits)
|
|
538
|
+
const workspaceDirs = this.options.directories?.filter((dir) => dir !== ".hyper/kits") || [];
|
|
539
|
+
for (const dir of workspaceDirs) {
|
|
540
|
+
const fullPath = path.resolve(this.projectRoot, dir);
|
|
541
|
+
if (!(await fs.pathExists(fullPath))) {
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
debug("Scanning for standalone items in: %s", fullPath);
|
|
545
|
+
// Find cookbooks and recipes that are not part of any kit
|
|
546
|
+
const actionFiles = await this.findActionFiles(fullPath);
|
|
547
|
+
const templateFiles = await this.findTemplateFiles(fullPath);
|
|
548
|
+
// Check if these files belong to a discovered kit
|
|
549
|
+
const kitPaths = discoveries.map((d) => d.path);
|
|
550
|
+
const standaloneActions = actionFiles.filter((file) => !this.belongsToKit(file, kitPaths));
|
|
551
|
+
const standaloneTemplates = templateFiles.filter((file) => !this.belongsToKit(file, kitPaths));
|
|
552
|
+
workspaceItems.actions.push(...standaloneActions);
|
|
553
|
+
workspaceItems.templates.push(...standaloneTemplates);
|
|
554
|
+
}
|
|
555
|
+
// If we found standalone items, create the workspace kit
|
|
556
|
+
if (workspaceItems.actions.length > 0 || workspaceItems.templates.length > 0) {
|
|
557
|
+
const actions = await this.extractActionsFromFiles(workspaceItems.actions);
|
|
558
|
+
debug("Created workspace kit with %d actions and %d templates", actions.length, workspaceItems.templates.length);
|
|
559
|
+
return {
|
|
560
|
+
name: "workspace",
|
|
561
|
+
source: "local",
|
|
562
|
+
path: this.projectRoot,
|
|
563
|
+
actions,
|
|
564
|
+
metadata: {
|
|
565
|
+
description: "Standalone cookbooks and recipes not belonging to any kit",
|
|
566
|
+
},
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
debug("No standalone workspace items found");
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Check if a file path belongs to any of the given kit paths
|
|
574
|
+
*/
|
|
575
|
+
belongsToKit(filePath, kitPaths) {
|
|
576
|
+
for (const kitPath of kitPaths) {
|
|
577
|
+
if (filePath.startsWith(kitPath)) {
|
|
578
|
+
return true;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Enrich a discovered generator with kit metadata
|
|
585
|
+
*/
|
|
586
|
+
async enrichWithKitMetadata(generator) {
|
|
587
|
+
// Look for kit.yml in the generator path
|
|
588
|
+
const kitYmlPath = path.join(generator.path, "kit.yml");
|
|
589
|
+
if (await fs.pathExists(kitYmlPath)) {
|
|
590
|
+
try {
|
|
591
|
+
const parsedKit = await parseKitFile(kitYmlPath);
|
|
592
|
+
if (parsedKit.isValid) {
|
|
593
|
+
const { config } = parsedKit;
|
|
594
|
+
// Update metadata
|
|
595
|
+
generator.metadata = {
|
|
596
|
+
...generator.metadata,
|
|
597
|
+
description: config.description || generator.metadata?.description,
|
|
598
|
+
version: config.version || generator.metadata?.version,
|
|
599
|
+
author: config.author || generator.metadata?.author,
|
|
600
|
+
tags: config.tags || generator.metadata?.tags,
|
|
601
|
+
license: config.license,
|
|
602
|
+
keywords: config.keywords,
|
|
603
|
+
};
|
|
604
|
+
// Discover cookbooks
|
|
605
|
+
if (config.cookbooks && config.cookbooks.length > 0) {
|
|
606
|
+
const cookbooks = await discoverCookbooksInKit(generator.path, config.cookbooks);
|
|
607
|
+
generator.cookbooks = Array.from(cookbooks.values()).map((c) => c.config.name);
|
|
608
|
+
}
|
|
609
|
+
// Discover recipes (direct recipes not in cookbooks)
|
|
610
|
+
if (config.recipes && config.recipes.length > 0) {
|
|
611
|
+
const recipePatterns = config.recipes;
|
|
612
|
+
const recipeFiles = [];
|
|
613
|
+
for (const pattern of recipePatterns) {
|
|
614
|
+
const matches = await glob(pattern, {
|
|
615
|
+
cwd: generator.path,
|
|
616
|
+
ignore: this.options.excludePatterns,
|
|
617
|
+
});
|
|
618
|
+
recipeFiles.push(...matches);
|
|
619
|
+
}
|
|
620
|
+
// Extract recipe names from paths
|
|
621
|
+
const recipeNames = recipeFiles
|
|
622
|
+
.map((f) => {
|
|
623
|
+
const dir = path.dirname(f);
|
|
624
|
+
const parts = dir.split(path.sep);
|
|
625
|
+
return parts[parts.length - 1];
|
|
626
|
+
})
|
|
627
|
+
.filter((name, index, self) => self.indexOf(name) === index); // unique
|
|
628
|
+
generator.recipes = recipeNames;
|
|
629
|
+
}
|
|
630
|
+
// Check for helpers
|
|
631
|
+
if (config.helpers) {
|
|
632
|
+
const helpersPath = path.join(generator.path, config.helpers);
|
|
633
|
+
if (await fs.pathExists(helpersPath)) {
|
|
634
|
+
const stats = await fs.stat(helpersPath);
|
|
635
|
+
if (stats.isDirectory()) {
|
|
636
|
+
// List helper files
|
|
637
|
+
const helperFiles = await fs.readdir(helpersPath);
|
|
638
|
+
generator.helpers = helperFiles
|
|
639
|
+
.filter((f) => f.endsWith(".js") || f.endsWith(".ts") || f.endsWith(".mjs"))
|
|
640
|
+
.map((f) => path.basename(f, path.extname(f)));
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
// Single helper file
|
|
644
|
+
generator.helpers = [path.basename(config.helpers, path.extname(config.helpers))];
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
catch (error) {
|
|
651
|
+
debug("Failed to parse kit.yml for %s: %s", generator.name, error);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
// Also check package.json for additional metadata
|
|
655
|
+
const packageJsonPath = path.join(generator.path, "package.json");
|
|
656
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
657
|
+
try {
|
|
658
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
659
|
+
generator.metadata = {
|
|
660
|
+
...generator.metadata,
|
|
661
|
+
version: generator.metadata?.version || packageJson.version,
|
|
662
|
+
author: generator.metadata?.author || packageJson.author,
|
|
663
|
+
description: generator.metadata?.description || packageJson.description,
|
|
664
|
+
license: generator.metadata?.license || packageJson.license,
|
|
665
|
+
keywords: generator.metadata?.keywords || packageJson.keywords,
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
catch (error) {
|
|
669
|
+
debug("Failed to read package.json for %s: %s", generator.name, error);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
//# sourceMappingURL=generator-discovery.js.map
|