pi-harness-runtime 0.10.26 → 0.10.27

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 (92) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/package.json +1 -1
  3. package/packages/architecture-generator/dist/adr.d.ts +15 -0
  4. package/packages/architecture-generator/dist/adr.d.ts.map +1 -0
  5. package/packages/architecture-generator/dist/adr.js +42 -0
  6. package/packages/architecture-generator/dist/adr.js.map +1 -0
  7. package/packages/architecture-generator/dist/index.d.ts +9 -0
  8. package/packages/architecture-generator/dist/index.d.ts.map +1 -0
  9. package/packages/architecture-generator/dist/index.js +9 -0
  10. package/packages/architecture-generator/dist/index.js.map +1 -0
  11. package/packages/architecture-generator/dist/mermaid.d.ts +30 -0
  12. package/packages/architecture-generator/dist/mermaid.d.ts.map +1 -0
  13. package/packages/architecture-generator/dist/mermaid.js +58 -0
  14. package/packages/architecture-generator/dist/mermaid.js.map +1 -0
  15. package/packages/architecture-generator/dist/types.d.ts +30 -0
  16. package/packages/architecture-generator/dist/types.d.ts.map +1 -0
  17. package/packages/architecture-generator/dist/types.js +7 -0
  18. package/packages/architecture-generator/dist/types.js.map +1 -0
  19. package/packages/architecture-generator/src/adr.ts +51 -0
  20. package/packages/architecture-generator/src/index.ts +9 -0
  21. package/packages/architecture-generator/src/mermaid.ts +82 -0
  22. package/packages/architecture-generator/src/types.ts +30 -0
  23. package/packages/knowledge-graph/dist/graph-extractor.d.ts +27 -0
  24. package/packages/knowledge-graph/dist/graph-extractor.d.ts.map +1 -0
  25. package/packages/knowledge-graph/dist/graph-extractor.js +285 -0
  26. package/packages/knowledge-graph/dist/graph-extractor.js.map +1 -0
  27. package/packages/knowledge-graph/dist/index.d.ts +27 -0
  28. package/packages/knowledge-graph/dist/index.d.ts.map +1 -0
  29. package/packages/knowledge-graph/dist/index.js +27 -0
  30. package/packages/knowledge-graph/dist/index.js.map +1 -0
  31. package/packages/knowledge-graph/dist/mcp-client.d.ts +38 -0
  32. package/packages/knowledge-graph/dist/mcp-client.d.ts.map +1 -0
  33. package/packages/knowledge-graph/dist/mcp-client.js +157 -0
  34. package/packages/knowledge-graph/dist/mcp-client.js.map +1 -0
  35. package/packages/knowledge-graph/dist/provenance-linker.d.ts +19 -0
  36. package/packages/knowledge-graph/dist/provenance-linker.d.ts.map +1 -0
  37. package/packages/knowledge-graph/dist/provenance-linker.js +92 -0
  38. package/packages/knowledge-graph/dist/provenance-linker.js.map +1 -0
  39. package/packages/knowledge-graph/dist/types.d.ts +53 -0
  40. package/packages/knowledge-graph/dist/types.d.ts.map +1 -0
  41. package/packages/knowledge-graph/dist/types.js +7 -0
  42. package/packages/knowledge-graph/dist/types.js.map +1 -0
  43. package/packages/knowledge-graph/src/graph-extractor.ts +355 -0
  44. package/packages/knowledge-graph/src/index.ts +27 -0
  45. package/packages/knowledge-graph/src/mcp-client.ts +246 -0
  46. package/packages/knowledge-graph/src/provenance-linker.ts +105 -0
  47. package/packages/knowledge-graph/src/types.ts +86 -0
  48. package/packages/milestone-manager/dist/engine.d.ts +27 -0
  49. package/packages/milestone-manager/dist/engine.d.ts.map +1 -0
  50. package/packages/milestone-manager/dist/engine.js +80 -0
  51. package/packages/milestone-manager/dist/engine.js.map +1 -0
  52. package/packages/milestone-manager/dist/index.d.ts +6 -0
  53. package/packages/milestone-manager/dist/index.d.ts.map +1 -0
  54. package/packages/milestone-manager/dist/index.js +6 -0
  55. package/packages/milestone-manager/dist/index.js.map +1 -0
  56. package/packages/milestone-manager/dist/types.d.ts +25 -0
  57. package/packages/milestone-manager/dist/types.d.ts.map +1 -0
  58. package/packages/milestone-manager/dist/types.js +7 -0
  59. package/packages/milestone-manager/dist/types.js.map +1 -0
  60. package/packages/milestone-manager/src/engine.ts +113 -0
  61. package/packages/milestone-manager/src/index.ts +6 -0
  62. package/packages/milestone-manager/src/types.ts +28 -0
  63. package/packages/project-bootstrap/dist/bootstrap.d.ts +23 -0
  64. package/packages/project-bootstrap/dist/bootstrap.d.ts.map +1 -0
  65. package/packages/project-bootstrap/dist/bootstrap.js +136 -0
  66. package/packages/project-bootstrap/dist/bootstrap.js.map +1 -0
  67. package/packages/project-bootstrap/dist/frappe.d.ts +8 -0
  68. package/packages/project-bootstrap/dist/frappe.d.ts.map +1 -0
  69. package/packages/project-bootstrap/dist/frappe.js +82 -0
  70. package/packages/project-bootstrap/dist/frappe.js.map +1 -0
  71. package/packages/project-bootstrap/dist/index.d.ts +27 -0
  72. package/packages/project-bootstrap/dist/index.d.ts.map +1 -0
  73. package/packages/project-bootstrap/dist/index.js +27 -0
  74. package/packages/project-bootstrap/dist/index.js.map +1 -0
  75. package/packages/project-bootstrap/dist/minimal.d.ts +8 -0
  76. package/packages/project-bootstrap/dist/minimal.d.ts.map +1 -0
  77. package/packages/project-bootstrap/dist/minimal.js +35 -0
  78. package/packages/project-bootstrap/dist/minimal.js.map +1 -0
  79. package/packages/project-bootstrap/dist/types.d.ts +61 -0
  80. package/packages/project-bootstrap/dist/types.d.ts.map +1 -0
  81. package/packages/project-bootstrap/dist/types.js +7 -0
  82. package/packages/project-bootstrap/dist/types.js.map +1 -0
  83. package/packages/project-bootstrap/dist/typescript.d.ts +8 -0
  84. package/packages/project-bootstrap/dist/typescript.d.ts.map +1 -0
  85. package/packages/project-bootstrap/dist/typescript.js +103 -0
  86. package/packages/project-bootstrap/dist/typescript.js.map +1 -0
  87. package/packages/project-bootstrap/src/bootstrap.ts +181 -0
  88. package/packages/project-bootstrap/src/frappe.ts +103 -0
  89. package/packages/project-bootstrap/src/index.ts +27 -0
  90. package/packages/project-bootstrap/src/minimal.ts +45 -0
  91. package/packages/project-bootstrap/src/types.ts +66 -0
  92. package/packages/project-bootstrap/src/typescript.ts +123 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Project Bootstrap - RFC-0072
3
+ *
4
+ * Node.js + TypeScript template
5
+ */
6
+ export const nodeTypescriptTemplate = {
7
+ name: "node-typescript",
8
+ description: "Node.js + TypeScript + ESLint + Jest",
9
+ async apply(spec, root) {
10
+ const { writeFile, mkdir } = await import("fs/promises");
11
+ const { join } = await import("path");
12
+ const files = [];
13
+ // package.json
14
+ const packageJson = {
15
+ name: spec.name,
16
+ version: "0.1.0",
17
+ description: spec.description || "",
18
+ type: "module",
19
+ scripts: {
20
+ build: "tsc",
21
+ test: "jest",
22
+ lint: "eslint src --ext .ts",
23
+ typecheck: "tsc --noEmit",
24
+ },
25
+ dependencies: {},
26
+ devDependencies: {
27
+ typescript: "^5.4.0",
28
+ "@types/node": "^20.0.0",
29
+ jest: "^29.7.0",
30
+ "@types/jest": "^29.5.0",
31
+ tsj: "^4.0.0",
32
+ eslint: "^8.57.0",
33
+ "@typescript-eslint/parser": "^6.0.0",
34
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
35
+ },
36
+ };
37
+ await writeFile(join(root, "package.json"), JSON.stringify(packageJson, null, 2) + "\n");
38
+ files.push("package.json");
39
+ // tsconfig.json
40
+ const tsconfig = {
41
+ compilerOptions: {
42
+ target: "ES2022",
43
+ module: "ESNext",
44
+ moduleResolution: "bundler",
45
+ lib: ["ES2022"],
46
+ outDir: "./dist",
47
+ rootDir: "./src",
48
+ strict: true,
49
+ esModuleInterop: true,
50
+ skipLibCheck: true,
51
+ forceConsistentCasingInFileNames: true,
52
+ declaration: true,
53
+ declarationMap: true,
54
+ sourceMap: true,
55
+ },
56
+ include: ["src/**/*"],
57
+ exclude: ["node_modules", "dist"],
58
+ };
59
+ await writeFile(join(root, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n");
60
+ files.push("tsconfig.json");
61
+ // jest.config.js
62
+ const jestConfig = `export default {
63
+ preset: 'tsj',
64
+ testEnvironment: 'node',
65
+ extensionsToTreatAsEsm: ['.ts'],
66
+ moduleNameMapper: {
67
+ '^(\\\\.{1,2}/.*)\\.js$': '$1',
68
+ },
69
+ transform: {
70
+ '^.+\\.ts$': ['tsj', { useESM: true }],
71
+ },
72
+ };
73
+ `;
74
+ await writeFile(join(root, "jest.config.js"), jestConfig);
75
+ files.push("jest.config.js");
76
+ // src directory
77
+ await mkdir(join(root, "src"), { recursive: true });
78
+ await writeFile(join(root, "src/index.ts"), 'export const main = () => {\n console.log("Hello!");\n};\n');
79
+ files.push("src/index.ts");
80
+ // .eslintrc.cjs
81
+ const eslintConfig = `module.exports = {
82
+ parser: '@typescript-eslint/parser',
83
+ plugins: ['@typescript-eslint'],
84
+ extends: [
85
+ 'eslint:recommended',
86
+ 'plugin:@typescript-eslint/recommended',
87
+ ],
88
+ root: true,
89
+ env: {
90
+ node: true,
91
+ es2022: true,
92
+ },
93
+ parserOptions: {
94
+ ecmaVersion: 'latest',
95
+ },
96
+ };
97
+ `;
98
+ await writeFile(join(root, ".eslintrc.cjs"), eslintConfig);
99
+ files.push(".eslintrc.cjs");
100
+ return files;
101
+ },
102
+ };
103
+ //# sourceMappingURL=typescript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript.js","sourceRoot":"","sources":["../src/typescript.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Project Bootstrap - RFC-0072
3
+ *
4
+ * Main bootstrap orchestration.
5
+ */
6
+
7
+ import type {
8
+ ProjectSpec,
9
+ BootstrapOptions,
10
+ BootstrapResult,
11
+ ValidationError,
12
+ Template,
13
+ } from "./types.js";
14
+ import { minimalTemplate } from "./minimal.js";
15
+ import { nodeTypescriptTemplate } from "./typescript.js";
16
+ import { frappeTemplate } from "./frappe.js";
17
+
18
+ // Built-in templates
19
+ const TEMPLATES: Record<string, Template> = {
20
+ minimal: minimalTemplate,
21
+ "node-typescript": nodeTypescriptTemplate,
22
+ "frappe-app": frappeTemplate,
23
+ };
24
+
25
+ /**
26
+ * Validate a project spec
27
+ */
28
+ export function validateSpec(spec: ProjectSpec): ValidationError[] {
29
+ const errors: ValidationError[] = [];
30
+
31
+ if (!spec.name || spec.name.trim() === "") {
32
+ errors.push({ field: "name", message: "Name is required" });
33
+ }
34
+
35
+ if (
36
+ !spec.type ||
37
+ !["monorepo", "single-package", "multi-package"].includes(spec.type)
38
+ ) {
39
+ errors.push({
40
+ field: "type",
41
+ message: "Type must be monorepo, single-package, or multi-package",
42
+ });
43
+ }
44
+
45
+ if (!spec.frameworks || spec.frameworks.length === 0) {
46
+ errors.push({
47
+ field: "frameworks",
48
+ message: "At least one framework is required",
49
+ });
50
+ }
51
+
52
+ return errors;
53
+ }
54
+
55
+ /**
56
+ * Get template for a spec
57
+ */
58
+ export function getTemplateForSpec(
59
+ spec: ProjectSpec,
60
+ override?: string,
61
+ ): Template {
62
+ if (override && TEMPLATES[override]) {
63
+ return TEMPLATES[override];
64
+ }
65
+
66
+ // Auto-detect based on frameworks
67
+ if (spec.frameworks.includes("frappe")) {
68
+ return TEMPLATES["frappe-app"];
69
+ }
70
+
71
+ if (
72
+ spec.frameworks.includes("node") ||
73
+ spec.frameworks.includes("typescript")
74
+ ) {
75
+ return TEMPLATES["node-typescript"];
76
+ }
77
+
78
+ return TEMPLATES["minimal"];
79
+ }
80
+
81
+ /**
82
+ * Bootstrap a project from a spec
83
+ */
84
+ export async function bootstrap(
85
+ spec: ProjectSpec,
86
+ targetDir: string,
87
+ options: BootstrapOptions = {},
88
+ ): Promise<BootstrapResult> {
89
+ const startTime = Date.now();
90
+ const errors = validateSpec(spec);
91
+
92
+ if (errors.length > 0) {
93
+ throw new Error(`Invalid spec: ${errors.map((e) => e.message).join(", ")}`);
94
+ }
95
+
96
+ const { mkdir, writeFile } = await import("fs/promises");
97
+ const { join } = await import("path");
98
+
99
+ // Create target directory
100
+ await mkdir(targetDir, { recursive: true });
101
+
102
+ // Get template
103
+ const template = getTemplateForSpec(spec, options.template);
104
+
105
+ // Emit started event
106
+ if (options.verbose) {
107
+ console.log(`Bootstrap: ${spec.name} using template: ${template.name}`);
108
+ }
109
+
110
+ // Apply template
111
+ const files = await template.apply(spec, targetDir);
112
+
113
+ // Create gitignore if gitInit
114
+ if (spec.gitInit) {
115
+ const gitignore = `node_modules/\ndist/\n.env\n*.log\n`;
116
+ await writeFile(join(targetDir, ".gitignore"), gitignore);
117
+ files.push(".gitignore");
118
+ }
119
+
120
+ const result: BootstrapResult = {
121
+ root: targetDir,
122
+ files,
123
+ packages: spec.packages?.map((p) => p.name) || [],
124
+ duration: Date.now() - startTime,
125
+ };
126
+
127
+ return result;
128
+ }
129
+
130
+ /**
131
+ * Detect framework and suggest a spec
132
+ */
133
+ export async function detectAndSuggest(
134
+ projectRoot: string,
135
+ ): Promise<ProjectSpec | null> {
136
+ const { readdir } = await import("fs/promises");
137
+ const { join } = await import("path");
138
+
139
+ try {
140
+ const entries = await readdir(projectRoot, { withFileTypes: true });
141
+ const files = entries.filter((e) => e.isFile()).map((e) => e.name);
142
+ const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
143
+
144
+ // Detect Frappe
145
+ if (dirs.includes("sites") && dirs.includes("apps")) {
146
+ const appName = join(projectRoot).split("/").pop() || "frappe-app";
147
+ return {
148
+ name: appName,
149
+ type: "single-package",
150
+ frameworks: ["frappe"],
151
+ gitInit: true,
152
+ };
153
+ }
154
+
155
+ // Detect Next.js
156
+ if (files.includes("next.config.js") || files.includes("next.config.mjs")) {
157
+ return {
158
+ name: "nextjs-app",
159
+ type: "single-package",
160
+ frameworks: ["nextjs"],
161
+ features: { typescript: true, testing: true },
162
+ gitInit: true,
163
+ };
164
+ }
165
+
166
+ // Detect Node TypeScript
167
+ if (files.includes("package.json") && files.includes("tsconfig.json")) {
168
+ return {
169
+ name: "node-app",
170
+ type: "single-package",
171
+ frameworks: ["node", "typescript"],
172
+ features: { typescript: true, testing: true, eslint: true },
173
+ gitInit: true,
174
+ };
175
+ }
176
+
177
+ return null;
178
+ } catch {
179
+ return null;
180
+ }
181
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Project Bootstrap - RFC-0072
3
+ *
4
+ * Frappe app template
5
+ */
6
+
7
+ import type { ProjectSpec, Template } from "./types.js";
8
+
9
+ export const frappeTemplate: Template = {
10
+ name: "frappe-app",
11
+ description: "Frappe app with modules, doctypes, web pages",
12
+ async apply(spec: ProjectSpec, root: string): Promise<string[]> {
13
+ const { writeFile, mkdir } = await import("fs/promises");
14
+ const { join } = await import("path");
15
+
16
+ const files: string[] = [];
17
+ const appName = spec.name.replace(/[^a-zA-Z0-9_]/g, "_");
18
+
19
+ // MANIFEST.json
20
+ const manifest = {
21
+ name: appName,
22
+ app_name: appName,
23
+ app_title: spec.description || spec.name,
24
+ app_publisher: spec.author || "Author",
25
+ app_email: "author@example.com",
26
+ app_description: spec.description || "",
27
+ app_version: "0.0.1",
28
+ app_license: spec.license || "MIT",
29
+ hooks: {},
30
+ doctypes: [],
31
+ fixtures: [],
32
+ };
33
+
34
+ await writeFile(
35
+ join(root, "MANIFEST.json"),
36
+ JSON.stringify(manifest, null, 2),
37
+ );
38
+ files.push("MANIFEST.json");
39
+
40
+ // pyproject.toml
41
+ const pyproject = `[build-system]
42
+ requires = ["flit_core >=3.2,<4"]
43
+ build-backend = "flit_core.buildapi"
44
+
45
+ [project]
46
+ name = "${appName}"
47
+ version = "0.0.1"
48
+ description = "${spec.description || ""}"
49
+ authors = [{name = "${spec.author || "Author"}", email = "author@example.com"}]
50
+ license = {text = "${spec.license || "MIT"}"}
51
+
52
+ [tool.frappe]
53
+ app_title = "${spec.name}"
54
+ app_publisher = "${spec.author || "Author"}"
55
+ app_email = "author@example.com"
56
+ app_description = "${spec.description || ""}"
57
+ app_version = "0.0.1"
58
+ app_license = "${spec.license || "MIT"}"
59
+ app_modules = []
60
+ `;
61
+ await writeFile(join(root, "pyproject.toml"), pyproject);
62
+ files.push("pyproject.toml");
63
+
64
+ // app/__init__.py
65
+ await mkdir(join(root, appName), { recursive: true });
66
+ await writeFile(
67
+ join(root, appName, "__init__.py"),
68
+ `__version__ = "0.0.1"\n`,
69
+ );
70
+ files.push(`${appName}/__init__.py`);
71
+
72
+ // hooks.py
73
+ const hooksPy = [
74
+ "# App hooks",
75
+ `app_name = "${appName}"`,
76
+ `app_title = "${spec.name}"`,
77
+ `app_publisher = "${spec.author || "Author"}"`,
78
+ 'app_email = "author@example.com"',
79
+ `app_description = "${spec.description || ""}"`,
80
+ 'app_version = "0.0.1"',
81
+ `app_license = "${spec.license || "MIT"}"`,
82
+ "",
83
+ "# hooks",
84
+ "doc_events = {}",
85
+ ].join("\n");
86
+
87
+ await writeFile(join(root, appName, "hooks.py"), hooksPy);
88
+ files.push(`${appName}/hooks.py`);
89
+
90
+ // modules.txt (required for Frappe)
91
+ await writeFile(join(root, "modules.txt"), "");
92
+ files.push("modules.txt");
93
+
94
+ // README
95
+ await writeFile(
96
+ join(root, "README.md"),
97
+ `# ${spec.name}\n\n${spec.description || "A Frappe app."}\n`,
98
+ );
99
+ files.push("README.md");
100
+
101
+ return files;
102
+ },
103
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Project Bootstrap - RFC-0072
3
+ *
4
+ * Create project scaffolds from typed ProjectSpec.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { bootstrap, detectAndSuggest } from '@pi-harness/project-bootstrap';
9
+ *
10
+ * // Bootstrap a new project
11
+ * const result = await bootstrap({
12
+ * name: 'my-app',
13
+ * type: 'single-package',
14
+ * frameworks: ['nextjs'],
15
+ * features: { typescript: true, testing: true }
16
+ * }, '/path/to/create');
17
+ *
18
+ * // Auto-detect framework and suggest spec
19
+ * const spec = await detectAndSuggest('/path/to/existing');
20
+ * ```
21
+ */
22
+
23
+ export * from "./types.js";
24
+ export * from "./bootstrap.js";
25
+ export { minimalTemplate } from "./minimal.js";
26
+ export { nodeTypescriptTemplate } from "./typescript.js";
27
+ export { frappeTemplate } from "./frappe.js";
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Project Bootstrap - RFC-0072
3
+ *
4
+ * Minimal template - bare project with package.json
5
+ */
6
+
7
+ import type { ProjectSpec, Template } from "./types.js";
8
+
9
+ export const minimalTemplate: Template = {
10
+ name: "minimal",
11
+ description: "Bare project with package.json",
12
+ async apply(spec: ProjectSpec, root: string): Promise<string[]> {
13
+ const { writeFile, mkdir } = await import("fs/promises");
14
+ const { join } = await import("path");
15
+
16
+ const files: string[] = [];
17
+
18
+ // package.json
19
+ const packageJson = {
20
+ name: spec.name,
21
+ version: "0.1.0",
22
+ description: spec.description || "",
23
+ author: spec.author,
24
+ license: spec.license || "MIT",
25
+ private: spec.type === "monorepo",
26
+ scripts: {
27
+ build: 'echo "No build script"',
28
+ test: 'echo "No tests"',
29
+ },
30
+ };
31
+
32
+ await writeFile(
33
+ join(root, "package.json"),
34
+ JSON.stringify(packageJson, null, 2) + "\n",
35
+ );
36
+ files.push("package.json");
37
+
38
+ // README.md
39
+ const readme = `# ${spec.name}\n\n${spec.description || "A project."}\n`;
40
+ await writeFile(join(root, "README.md"), readme);
41
+ files.push("README.md");
42
+
43
+ return files;
44
+ },
45
+ };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Project Bootstrap - RFC-0072
3
+ *
4
+ * Typed definitions for project scaffolding.
5
+ */
6
+
7
+ export type ProjectType = "monorepo" | "single-package" | "multi-package";
8
+
9
+ export interface ProjectSpec {
10
+ name: string;
11
+ type: ProjectType;
12
+ description?: string;
13
+ author?: string;
14
+ license?: string;
15
+ frameworks: string[]; // e.g. ["frappe", "nextjs"]
16
+ packages?: { name: string; type: string }[];
17
+ features?: {
18
+ ci?: boolean;
19
+ cd?: boolean;
20
+ testing?: boolean;
21
+ typescript?: boolean;
22
+ eslint?: boolean;
23
+ prettier?: boolean;
24
+ docs?: boolean;
25
+ };
26
+ gitInit?: boolean;
27
+ }
28
+
29
+ export interface BootstrapOptions {
30
+ dryRun?: boolean; // Don't actually create files
31
+ force?: boolean; // Overwrite existing files
32
+ targetDir?: string; // Default: current directory
33
+ template?: string; // Override auto-detected template
34
+ verbose?: boolean; // Log progress
35
+ }
36
+
37
+ export interface BootstrapResult {
38
+ root: string;
39
+ files: string[];
40
+ packages: string[];
41
+ duration: number;
42
+ }
43
+
44
+ export interface ValidationError {
45
+ field: string;
46
+ message: string;
47
+ }
48
+
49
+ // Template types
50
+ export interface Template {
51
+ name: string;
52
+ description: string;
53
+ apply: (spec: ProjectSpec, root: string) => Promise<string[]>;
54
+ }
55
+
56
+ export interface BootstrapContext {
57
+ spec: ProjectSpec;
58
+ root: string;
59
+ files: string[];
60
+ events: EventEmitter;
61
+ }
62
+
63
+ export interface EventEmitter {
64
+ emit(event: string, data: unknown): void;
65
+ on(event: string, handler: (data: unknown) => void): void;
66
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Project Bootstrap - RFC-0072
3
+ *
4
+ * Node.js + TypeScript template
5
+ */
6
+
7
+ import type { ProjectSpec, Template } from "./types.js";
8
+
9
+ export const nodeTypescriptTemplate: Template = {
10
+ name: "node-typescript",
11
+ description: "Node.js + TypeScript + ESLint + Jest",
12
+ async apply(spec: ProjectSpec, root: string): Promise<string[]> {
13
+ const { writeFile, mkdir } = await import("fs/promises");
14
+ const { join } = await import("path");
15
+
16
+ const files: string[] = [];
17
+
18
+ // package.json
19
+ const packageJson = {
20
+ name: spec.name,
21
+ version: "0.1.0",
22
+ description: spec.description || "",
23
+ type: "module",
24
+ scripts: {
25
+ build: "tsc",
26
+ test: "jest",
27
+ lint: "eslint src --ext .ts",
28
+ typecheck: "tsc --noEmit",
29
+ },
30
+ dependencies: {},
31
+ devDependencies: {
32
+ typescript: "^5.4.0",
33
+ "@types/node": "^20.0.0",
34
+ jest: "^29.7.0",
35
+ "@types/jest": "^29.5.0",
36
+ tsj: "^4.0.0",
37
+ eslint: "^8.57.0",
38
+ "@typescript-eslint/parser": "^6.0.0",
39
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
40
+ },
41
+ };
42
+
43
+ await writeFile(
44
+ join(root, "package.json"),
45
+ JSON.stringify(packageJson, null, 2) + "\n",
46
+ );
47
+ files.push("package.json");
48
+
49
+ // tsconfig.json
50
+ const tsconfig = {
51
+ compilerOptions: {
52
+ target: "ES2022",
53
+ module: "ESNext",
54
+ moduleResolution: "bundler",
55
+ lib: ["ES2022"],
56
+ outDir: "./dist",
57
+ rootDir: "./src",
58
+ strict: true,
59
+ esModuleInterop: true,
60
+ skipLibCheck: true,
61
+ forceConsistentCasingInFileNames: true,
62
+ declaration: true,
63
+ declarationMap: true,
64
+ sourceMap: true,
65
+ },
66
+ include: ["src/**/*"],
67
+ exclude: ["node_modules", "dist"],
68
+ };
69
+
70
+ await writeFile(
71
+ join(root, "tsconfig.json"),
72
+ JSON.stringify(tsconfig, null, 2) + "\n",
73
+ );
74
+ files.push("tsconfig.json");
75
+
76
+ // jest.config.js
77
+ const jestConfig = `export default {
78
+ preset: 'tsj',
79
+ testEnvironment: 'node',
80
+ extensionsToTreatAsEsm: ['.ts'],
81
+ moduleNameMapper: {
82
+ '^(\\\\.{1,2}/.*)\\.js$': '$1',
83
+ },
84
+ transform: {
85
+ '^.+\\.ts$': ['tsj', { useESM: true }],
86
+ },
87
+ };
88
+ `;
89
+ await writeFile(join(root, "jest.config.js"), jestConfig);
90
+ files.push("jest.config.js");
91
+
92
+ // src directory
93
+ await mkdir(join(root, "src"), { recursive: true });
94
+ await writeFile(
95
+ join(root, "src/index.ts"),
96
+ 'export const main = () => {\n console.log("Hello!");\n};\n',
97
+ );
98
+ files.push("src/index.ts");
99
+
100
+ // .eslintrc.cjs
101
+ const eslintConfig = `module.exports = {
102
+ parser: '@typescript-eslint/parser',
103
+ plugins: ['@typescript-eslint'],
104
+ extends: [
105
+ 'eslint:recommended',
106
+ 'plugin:@typescript-eslint/recommended',
107
+ ],
108
+ root: true,
109
+ env: {
110
+ node: true,
111
+ es2022: true,
112
+ },
113
+ parserOptions: {
114
+ ecmaVersion: 'latest',
115
+ },
116
+ };
117
+ `;
118
+ await writeFile(join(root, ".eslintrc.cjs"), eslintConfig);
119
+ files.push(".eslintrc.cjs");
120
+
121
+ return files;
122
+ },
123
+ };