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.
- package/CHANGELOG.md +22 -0
- package/package.json +1 -1
- package/packages/architecture-generator/dist/adr.d.ts +15 -0
- package/packages/architecture-generator/dist/adr.d.ts.map +1 -0
- package/packages/architecture-generator/dist/adr.js +42 -0
- package/packages/architecture-generator/dist/adr.js.map +1 -0
- package/packages/architecture-generator/dist/index.d.ts +9 -0
- package/packages/architecture-generator/dist/index.d.ts.map +1 -0
- package/packages/architecture-generator/dist/index.js +9 -0
- package/packages/architecture-generator/dist/index.js.map +1 -0
- package/packages/architecture-generator/dist/mermaid.d.ts +30 -0
- package/packages/architecture-generator/dist/mermaid.d.ts.map +1 -0
- package/packages/architecture-generator/dist/mermaid.js +58 -0
- package/packages/architecture-generator/dist/mermaid.js.map +1 -0
- package/packages/architecture-generator/dist/types.d.ts +30 -0
- package/packages/architecture-generator/dist/types.d.ts.map +1 -0
- package/packages/architecture-generator/dist/types.js +7 -0
- package/packages/architecture-generator/dist/types.js.map +1 -0
- package/packages/architecture-generator/src/adr.ts +51 -0
- package/packages/architecture-generator/src/index.ts +9 -0
- package/packages/architecture-generator/src/mermaid.ts +82 -0
- package/packages/architecture-generator/src/types.ts +30 -0
- package/packages/knowledge-graph/dist/graph-extractor.d.ts +27 -0
- package/packages/knowledge-graph/dist/graph-extractor.d.ts.map +1 -0
- package/packages/knowledge-graph/dist/graph-extractor.js +285 -0
- package/packages/knowledge-graph/dist/graph-extractor.js.map +1 -0
- package/packages/knowledge-graph/dist/index.d.ts +27 -0
- package/packages/knowledge-graph/dist/index.d.ts.map +1 -0
- package/packages/knowledge-graph/dist/index.js +27 -0
- package/packages/knowledge-graph/dist/index.js.map +1 -0
- package/packages/knowledge-graph/dist/mcp-client.d.ts +38 -0
- package/packages/knowledge-graph/dist/mcp-client.d.ts.map +1 -0
- package/packages/knowledge-graph/dist/mcp-client.js +157 -0
- package/packages/knowledge-graph/dist/mcp-client.js.map +1 -0
- package/packages/knowledge-graph/dist/provenance-linker.d.ts +19 -0
- package/packages/knowledge-graph/dist/provenance-linker.d.ts.map +1 -0
- package/packages/knowledge-graph/dist/provenance-linker.js +92 -0
- package/packages/knowledge-graph/dist/provenance-linker.js.map +1 -0
- package/packages/knowledge-graph/dist/types.d.ts +53 -0
- package/packages/knowledge-graph/dist/types.d.ts.map +1 -0
- package/packages/knowledge-graph/dist/types.js +7 -0
- package/packages/knowledge-graph/dist/types.js.map +1 -0
- package/packages/knowledge-graph/src/graph-extractor.ts +355 -0
- package/packages/knowledge-graph/src/index.ts +27 -0
- package/packages/knowledge-graph/src/mcp-client.ts +246 -0
- package/packages/knowledge-graph/src/provenance-linker.ts +105 -0
- package/packages/knowledge-graph/src/types.ts +86 -0
- package/packages/milestone-manager/dist/engine.d.ts +27 -0
- package/packages/milestone-manager/dist/engine.d.ts.map +1 -0
- package/packages/milestone-manager/dist/engine.js +80 -0
- package/packages/milestone-manager/dist/engine.js.map +1 -0
- package/packages/milestone-manager/dist/index.d.ts +6 -0
- package/packages/milestone-manager/dist/index.d.ts.map +1 -0
- package/packages/milestone-manager/dist/index.js +6 -0
- package/packages/milestone-manager/dist/index.js.map +1 -0
- package/packages/milestone-manager/dist/types.d.ts +25 -0
- package/packages/milestone-manager/dist/types.d.ts.map +1 -0
- package/packages/milestone-manager/dist/types.js +7 -0
- package/packages/milestone-manager/dist/types.js.map +1 -0
- package/packages/milestone-manager/src/engine.ts +113 -0
- package/packages/milestone-manager/src/index.ts +6 -0
- package/packages/milestone-manager/src/types.ts +28 -0
- package/packages/project-bootstrap/dist/bootstrap.d.ts +23 -0
- package/packages/project-bootstrap/dist/bootstrap.d.ts.map +1 -0
- package/packages/project-bootstrap/dist/bootstrap.js +136 -0
- package/packages/project-bootstrap/dist/bootstrap.js.map +1 -0
- package/packages/project-bootstrap/dist/frappe.d.ts +8 -0
- package/packages/project-bootstrap/dist/frappe.d.ts.map +1 -0
- package/packages/project-bootstrap/dist/frappe.js +82 -0
- package/packages/project-bootstrap/dist/frappe.js.map +1 -0
- package/packages/project-bootstrap/dist/index.d.ts +27 -0
- package/packages/project-bootstrap/dist/index.d.ts.map +1 -0
- package/packages/project-bootstrap/dist/index.js +27 -0
- package/packages/project-bootstrap/dist/index.js.map +1 -0
- package/packages/project-bootstrap/dist/minimal.d.ts +8 -0
- package/packages/project-bootstrap/dist/minimal.d.ts.map +1 -0
- package/packages/project-bootstrap/dist/minimal.js +35 -0
- package/packages/project-bootstrap/dist/minimal.js.map +1 -0
- package/packages/project-bootstrap/dist/types.d.ts +61 -0
- package/packages/project-bootstrap/dist/types.d.ts.map +1 -0
- package/packages/project-bootstrap/dist/types.js +7 -0
- package/packages/project-bootstrap/dist/types.js.map +1 -0
- package/packages/project-bootstrap/dist/typescript.d.ts +8 -0
- package/packages/project-bootstrap/dist/typescript.d.ts.map +1 -0
- package/packages/project-bootstrap/dist/typescript.js +103 -0
- package/packages/project-bootstrap/dist/typescript.js.map +1 -0
- package/packages/project-bootstrap/src/bootstrap.ts +181 -0
- package/packages/project-bootstrap/src/frappe.ts +103 -0
- package/packages/project-bootstrap/src/index.ts +27 -0
- package/packages/project-bootstrap/src/minimal.ts +45 -0
- package/packages/project-bootstrap/src/types.ts +66 -0
- 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
|
+
};
|