@iconsulting-dev/forgekit 1.6.2 → 1.7.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/README.md +33 -6
- package/dist/.metadata_never_index +0 -0
- package/dist/generators/claude-code/__tests__/claude-code.test.d.ts +2 -0
- package/dist/generators/claude-code/__tests__/claude-code.test.d.ts.map +1 -0
- package/dist/generators/claude-code/__tests__/claude-code.test.js +116 -0
- package/dist/generators/claude-code/__tests__/claude-code.test.js.map +1 -0
- package/dist/generators/claude-code/index.d.ts +1 -1
- package/dist/generators/claude-code/index.d.ts.map +1 -1
- package/dist/generators/claude-code/index.js +57 -4
- package/dist/generators/claude-code/index.js.map +1 -1
- package/dist/templates/claude-code/CLAUDE.md.hbs +14 -9
- package/dist/templates/claude-code/hookify/block-dangerous-rm.local.md +11 -0
- package/dist/templates/claude-code/hookify/block-force-push.local.md +11 -0
- package/dist/templates/claude-code/hookify/block-no-verify.local.md +11 -0
- package/dist/templates/claude-code/hookify/stop-verify-tests.local.md.hbs +21 -0
- package/dist/templates/claude-code/hookify/warn-console-log.local.md +11 -0
- package/dist/templates/claude-code/hookify/warn-env-edit.local.md +11 -0
- package/dist/templates/claude-code/hookify/warn-no-test-before-commit.local.md.hbs +18 -0
- package/dist/templates/claude-code/hookify/warn-todo-fixme.local.md +11 -0
- package/dist/templates/claude-code/hooks/pre-bash.sh.hbs +36 -0
- package/dist/templates/claude-code/hooks/session-start.sh.hbs +15 -0
- package/dist/templates/claude-code/settings.json.hbs +45 -2
- package/dist/templates/claude-code/specify/constitution.md.hbs +31 -0
- package/dist/utils/template-engine.d.ts +1 -0
- package/dist/utils/template-engine.d.ts.map +1 -1
- package/dist/utils/template-engine.js +1 -1
- package/dist/utils/template-engine.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,8 +90,9 @@ mon-projet/
|
|
|
90
90
|
├── frontend/ # Angular 21 / PrimeNG 21
|
|
91
91
|
├── docker-compose.yml # PostgreSQL 17 + pgAdmin (+ service api si FastAPI)
|
|
92
92
|
├── .github/workflows/ # CI GitHub Actions
|
|
93
|
-
├── CLAUDE.md # Conventions
|
|
94
|
-
├── .claude/
|
|
93
|
+
├── CLAUDE.md # Conventions, workflow routing, constitution ref
|
|
94
|
+
├── .claude/ # Hooks, hookify guards, skills, settings
|
|
95
|
+
├── .specify/memory/ # Constitution architecturale
|
|
95
96
|
├── .gitignore
|
|
96
97
|
└── README.md
|
|
97
98
|
```
|
|
@@ -196,9 +197,35 @@ frontend/src/app/
|
|
|
196
197
|
|
|
197
198
|
### Claude Code
|
|
198
199
|
|
|
199
|
-
Génère automatiquement :
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
Génère automatiquement une configuration complète et prête à l'emploi :
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
mon-projet/
|
|
204
|
+
├── CLAUDE.md # Workflow routing, TDD rules, constitution ref
|
|
205
|
+
├── .claudeignore # Fichiers exclus du contexte Claude
|
|
206
|
+
├── .claude/
|
|
207
|
+
│ ├── settings.json # Permissions + hooks (SessionStart, PreToolUse, PreCompact)
|
|
208
|
+
│ ├── hooks/
|
|
209
|
+
│ │ ├── pre-bash.sh # Guard bash (ex: bloque npm install hors frontend/)
|
|
210
|
+
│ │ └── session-start.sh # Auto-charge la constitution au démarrage
|
|
211
|
+
│ ├── hookify.block-dangerous-rm.local.md # Bloque rm -rf
|
|
212
|
+
│ ├── hookify.block-force-push.local.md # Bloque git push --force
|
|
213
|
+
│ ├── hookify.block-no-verify.local.md # Bloque --no-verify
|
|
214
|
+
│ ├── hookify.stop-verify-tests.local.md # Rappel TDD à la fin de session
|
|
215
|
+
│ ├── hookify.warn-console-log.local.md # Avertit sur console.log
|
|
216
|
+
│ ├── hookify.warn-env-edit.local.md # Avertit sur édition .env
|
|
217
|
+
│ ├── hookify.warn-no-test-before-commit.local.md # Rappel tests avant commit
|
|
218
|
+
│ ├── hookify.warn-todo-fixme.local.md # Avertit sur TODO/FIXME
|
|
219
|
+
│ └── skills/ # Skills copiés selon le stack
|
|
220
|
+
│ ├── applying-angular-conventions/SKILL.md # si frontend Angular
|
|
221
|
+
│ ├── applying-python-conventions/SKILL.md # si backend FastAPI
|
|
222
|
+
│ └── applying-java-conventions/SKILL.md # si backend Spring Boot
|
|
223
|
+
└── .specify/
|
|
224
|
+
└── memory/
|
|
225
|
+
└── constitution.md # Constitution architecturale (auto-chargée)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Les skills sont copiés depuis `~/.claude/skills/` selon le stack sélectionné — chaque dev qui clone le projet dispose des mêmes conventions.
|
|
202
229
|
|
|
203
230
|
## Versions dynamiques
|
|
204
231
|
|
|
@@ -232,7 +259,7 @@ src/
|
|
|
232
259
|
│ ├── fastapi/ # FastAPI (8 templates)
|
|
233
260
|
│ ├── frontend/ # Angular (21 templates)
|
|
234
261
|
│ ├── docker/ # Docker Compose
|
|
235
|
-
│ ├── claude-code/ # CLAUDE.md
|
|
262
|
+
│ ├── claude-code/ # CLAUDE.md, settings.json, hooks, hookify, specify
|
|
236
263
|
│ ├── ci/ # GitHub Actions
|
|
237
264
|
│ └── root/ # README + .gitignore
|
|
238
265
|
├── utils/
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.test.d.ts","sourceRoot":"","sources":["../../../../src/generators/claude-code/__tests__/claude-code.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { generateClaudeCode } from "../index.js";
|
|
6
|
+
// Fake global skills dir — seeded in beforeEach, used in skill tests
|
|
7
|
+
let fakeSkillsDir;
|
|
8
|
+
const baseConfig = {
|
|
9
|
+
name: "test-project",
|
|
10
|
+
groupId: "com.example",
|
|
11
|
+
description: "Test",
|
|
12
|
+
backendType: null,
|
|
13
|
+
frontend: false,
|
|
14
|
+
flyway: false,
|
|
15
|
+
openapi: false,
|
|
16
|
+
auth: false,
|
|
17
|
+
mapstruct: false,
|
|
18
|
+
uiFramework: "none",
|
|
19
|
+
primeNGPreset: "Aura",
|
|
20
|
+
ngrx: false,
|
|
21
|
+
docker: false,
|
|
22
|
+
ci: false,
|
|
23
|
+
claudeCode: true,
|
|
24
|
+
gitInit: false,
|
|
25
|
+
};
|
|
26
|
+
const baseVersions = {
|
|
27
|
+
springBoot: "3.4.0",
|
|
28
|
+
springDoc: "2.8.0",
|
|
29
|
+
mapstruct: "1.6.3",
|
|
30
|
+
angular: "19.0.0",
|
|
31
|
+
primeng: "19.0.0",
|
|
32
|
+
primeuixThemes: "2.0.3",
|
|
33
|
+
primeicons: "7.0.0",
|
|
34
|
+
primeflex: "3.3.1",
|
|
35
|
+
ngrxSignals: "19.0.0",
|
|
36
|
+
rxjs: "7.8.0",
|
|
37
|
+
zoneJs: "0.15.0",
|
|
38
|
+
typescript: "5.6.0",
|
|
39
|
+
tailwind: "4.0.0",
|
|
40
|
+
};
|
|
41
|
+
describe("ClaudeCodeGenerator", () => {
|
|
42
|
+
let tmpDir;
|
|
43
|
+
beforeEach(async () => {
|
|
44
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "forgekit-test-"));
|
|
45
|
+
fakeSkillsDir = path.join(tmpDir, "fake-skills");
|
|
46
|
+
for (const skill of [
|
|
47
|
+
"applying-angular-conventions",
|
|
48
|
+
"applying-python-conventions",
|
|
49
|
+
"applying-java-conventions",
|
|
50
|
+
]) {
|
|
51
|
+
await fs.ensureDir(path.join(fakeSkillsDir, skill));
|
|
52
|
+
await fs.writeFile(path.join(fakeSkillsDir, skill, "SKILL.md"), `# ${skill}`);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
afterEach(async () => {
|
|
56
|
+
await fs.remove(tmpDir);
|
|
57
|
+
});
|
|
58
|
+
it("generates hooks directory with scripts", async () => {
|
|
59
|
+
await generateClaudeCode(tmpDir, baseConfig, baseVersions);
|
|
60
|
+
expect(await fs.pathExists(path.join(tmpDir, ".claude", "hooks", "pre-bash.sh"))).toBe(true);
|
|
61
|
+
expect(await fs.pathExists(path.join(tmpDir, ".claude", "hooks", "session-start.sh"))).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
it("makes hook scripts executable", async () => {
|
|
64
|
+
await generateClaudeCode(tmpDir, baseConfig, baseVersions);
|
|
65
|
+
const stat = await fs.stat(path.join(tmpDir, ".claude", "hooks", "pre-bash.sh"));
|
|
66
|
+
expect(stat.mode & 0o100).toBeTruthy();
|
|
67
|
+
});
|
|
68
|
+
it("generates hookify guard files", async () => {
|
|
69
|
+
await generateClaudeCode(tmpDir, baseConfig, baseVersions);
|
|
70
|
+
const hookifyFiles = [
|
|
71
|
+
"block-dangerous-rm.local.md",
|
|
72
|
+
"block-force-push.local.md",
|
|
73
|
+
"block-no-verify.local.md",
|
|
74
|
+
"stop-verify-tests.local.md",
|
|
75
|
+
"warn-console-log.local.md",
|
|
76
|
+
"warn-env-edit.local.md",
|
|
77
|
+
"warn-no-test-before-commit.local.md",
|
|
78
|
+
"warn-todo-fixme.local.md",
|
|
79
|
+
];
|
|
80
|
+
for (const f of hookifyFiles) {
|
|
81
|
+
expect(await fs.pathExists(path.join(tmpDir, ".claude", `hookify.${f}`))).toBe(true);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
it("generates .specify/memory/constitution.md", async () => {
|
|
85
|
+
await generateClaudeCode(tmpDir, baseConfig, baseVersions);
|
|
86
|
+
expect(await fs.pathExists(path.join(tmpDir, ".specify", "memory", "constitution.md"))).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
it("settings.json contains hooks configuration", async () => {
|
|
89
|
+
await generateClaudeCode(tmpDir, baseConfig, baseVersions);
|
|
90
|
+
const settings = await fs.readJson(path.join(tmpDir, ".claude", "settings.json"));
|
|
91
|
+
expect(settings.hooks).toBeDefined();
|
|
92
|
+
expect(settings.hooks.SessionStart).toBeDefined();
|
|
93
|
+
expect(settings.hooks.PreToolUse).toBeDefined();
|
|
94
|
+
expect(settings.hooks.PreCompact).toBeDefined();
|
|
95
|
+
});
|
|
96
|
+
it("generates angular skill when frontend is enabled", async () => {
|
|
97
|
+
const config = { ...baseConfig, frontend: true };
|
|
98
|
+
await generateClaudeCode(tmpDir, config, baseVersions, fakeSkillsDir);
|
|
99
|
+
expect(await fs.pathExists(path.join(tmpDir, ".claude", "skills", "applying-angular-conventions", "SKILL.md"))).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
it("generates python skill when fastapi backend", async () => {
|
|
102
|
+
const config = { ...baseConfig, backendType: "fastapi" };
|
|
103
|
+
await generateClaudeCode(tmpDir, config, baseVersions, fakeSkillsDir);
|
|
104
|
+
expect(await fs.pathExists(path.join(tmpDir, ".claude", "skills", "applying-python-conventions", "SKILL.md"))).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
it("generates java skill when spring-boot backend", async () => {
|
|
107
|
+
const config = { ...baseConfig, backendType: "spring-boot" };
|
|
108
|
+
await generateClaudeCode(tmpDir, config, baseVersions, fakeSkillsDir);
|
|
109
|
+
expect(await fs.pathExists(path.join(tmpDir, ".claude", "skills", "applying-java-conventions", "SKILL.md"))).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
it("generates no stack skills when claude-only (no backend, no frontend)", async () => {
|
|
112
|
+
await generateClaudeCode(tmpDir, baseConfig, baseVersions, fakeSkillsDir);
|
|
113
|
+
expect(await fs.pathExists(path.join(tmpDir, ".claude", "skills"))).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
//# sourceMappingURL=claude-code.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.test.js","sourceRoot":"","sources":["../../../../src/generators/claude-code/__tests__/claude-code.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ProjectConfig } from "../../types.js";
|
|
2
2
|
import type { ResolvedVersions } from "../../versions.js";
|
|
3
|
-
export declare function generateClaudeCode(projectDir: string, config: ProjectConfig, versions: ResolvedVersions): Promise<void>;
|
|
3
|
+
export declare function generateClaudeCode(projectDir: string, config: ProjectConfig, versions: ResolvedVersions, globalSkillsBase?: string): Promise<void>;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/generators/claude-code/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/generators/claude-code/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAkM1D,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,gBAAgB,EAC1B,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC,CAQf"}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import fs from "fs-extra";
|
|
3
|
-
import { renderAndWrite } from "../../utils/template-engine.js";
|
|
4
|
+
import { renderAndWrite, TEMPLATES_DIR } from "../../utils/template-engine.js";
|
|
4
5
|
import { BaseGenerator } from "../base-generator.js";
|
|
5
6
|
class ClaudeCodeGenerator extends BaseGenerator {
|
|
6
7
|
versions;
|
|
7
|
-
|
|
8
|
+
globalSkillsBase;
|
|
9
|
+
constructor(projectDir, config, versions, globalSkillsBase) {
|
|
8
10
|
super(projectDir, config);
|
|
9
11
|
this.versions = versions;
|
|
12
|
+
this.globalSkillsBase =
|
|
13
|
+
globalSkillsBase ?? path.join(os.homedir(), ".claude", "skills");
|
|
10
14
|
}
|
|
11
15
|
async generate() {
|
|
12
16
|
const claudeDir = path.join(this.projectDir, ".claude");
|
|
@@ -15,6 +19,9 @@ class ClaudeCodeGenerator extends BaseGenerator {
|
|
|
15
19
|
const springBoot = this.config.backendType === "spring-boot";
|
|
16
20
|
const fastapi = this.config.backendType === "fastapi";
|
|
17
21
|
const backend = this.config.backendType !== null;
|
|
22
|
+
const hooksDir = path.join(claudeDir, "hooks");
|
|
23
|
+
await fs.ensureDir(hooksDir);
|
|
24
|
+
await fs.ensureDir(path.join(this.projectDir, ".specify", "memory"));
|
|
18
25
|
const data = {
|
|
19
26
|
name: this.config.name,
|
|
20
27
|
description: this.config.description,
|
|
@@ -28,12 +35,58 @@ class ClaudeCodeGenerator extends BaseGenerator {
|
|
|
28
35
|
auth: this.config.auth,
|
|
29
36
|
versions: this.versions,
|
|
30
37
|
allowedCommands,
|
|
38
|
+
claudeDir: ".claude",
|
|
31
39
|
};
|
|
40
|
+
// Static hookify files (no templating needed)
|
|
41
|
+
const staticHookifyFiles = [
|
|
42
|
+
"block-dangerous-rm.local.md",
|
|
43
|
+
"block-force-push.local.md",
|
|
44
|
+
"block-no-verify.local.md",
|
|
45
|
+
"warn-console-log.local.md",
|
|
46
|
+
"warn-env-edit.local.md",
|
|
47
|
+
"warn-todo-fixme.local.md",
|
|
48
|
+
];
|
|
32
49
|
await Promise.all([
|
|
50
|
+
// Existing files
|
|
33
51
|
renderAndWrite("claude-code/CLAUDE.md.hbs", path.join(this.projectDir, "CLAUDE.md"), data),
|
|
34
52
|
renderAndWrite("claude-code/settings.json.hbs", path.join(claudeDir, "settings.json"), data),
|
|
35
53
|
renderAndWrite("claude-code/claudeignore.hbs", path.join(this.projectDir, ".claudeignore"), data),
|
|
54
|
+
// Hook scripts (templated, then chmod)
|
|
55
|
+
renderAndWrite("claude-code/hooks/pre-bash.sh.hbs", path.join(hooksDir, "pre-bash.sh"), data, { mode: 0o755 }),
|
|
56
|
+
renderAndWrite("claude-code/hooks/session-start.sh.hbs", path.join(hooksDir, "session-start.sh"), data, { mode: 0o755 }),
|
|
57
|
+
// Templated hookify files
|
|
58
|
+
renderAndWrite("claude-code/hookify/stop-verify-tests.local.md.hbs", path.join(claudeDir, "hookify.stop-verify-tests.local.md"), data),
|
|
59
|
+
renderAndWrite("claude-code/hookify/warn-no-test-before-commit.local.md.hbs", path.join(claudeDir, "hookify.warn-no-test-before-commit.local.md"), data),
|
|
60
|
+
// .specify constitution
|
|
61
|
+
renderAndWrite("claude-code/specify/constitution.md.hbs", path.join(this.projectDir, ".specify", "memory", "constitution.md"), data),
|
|
62
|
+
// Static hookify files
|
|
63
|
+
...staticHookifyFiles.map((f) => fs.copy(path.join(TEMPLATES_DIR, "claude-code", "hookify", f), path.join(claudeDir, `hookify.${f}`))),
|
|
36
64
|
]);
|
|
65
|
+
await this.generateSkills();
|
|
66
|
+
}
|
|
67
|
+
async generateSkills() {
|
|
68
|
+
const globalSkillsBase = this.globalSkillsBase;
|
|
69
|
+
const skillsToGenerate = [
|
|
70
|
+
{ name: "applying-angular-conventions", condition: this.config.frontend },
|
|
71
|
+
{
|
|
72
|
+
name: "applying-python-conventions",
|
|
73
|
+
condition: this.config.backendType === "fastapi",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "applying-java-conventions",
|
|
77
|
+
condition: this.config.backendType === "spring-boot",
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
for (const { name, condition } of skillsToGenerate) {
|
|
81
|
+
if (!condition)
|
|
82
|
+
continue;
|
|
83
|
+
const src = path.join(globalSkillsBase, name, "SKILL.md");
|
|
84
|
+
const dest = path.join(this.projectDir, ".claude", "skills", name, "SKILL.md");
|
|
85
|
+
if (await fs.pathExists(src)) {
|
|
86
|
+
await fs.ensureDir(path.dirname(dest));
|
|
87
|
+
await fs.copy(src, dest);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
37
90
|
}
|
|
38
91
|
buildAllowedCommands() {
|
|
39
92
|
const commands = [];
|
|
@@ -54,8 +107,8 @@ class ClaudeCodeGenerator extends BaseGenerator {
|
|
|
54
107
|
return commands;
|
|
55
108
|
}
|
|
56
109
|
}
|
|
57
|
-
export async function generateClaudeCode(projectDir, config, versions) {
|
|
58
|
-
const generator = new ClaudeCodeGenerator(projectDir, config, versions);
|
|
110
|
+
export async function generateClaudeCode(projectDir, config, versions, globalSkillsBase) {
|
|
111
|
+
const generator = new ClaudeCodeGenerator(projectDir, config, versions, globalSkillsBase);
|
|
59
112
|
await generator.generate();
|
|
60
113
|
}
|
|
61
114
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/generators/claude-code/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/generators/claude-code/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAIrD,MAAM,mBAAoB,SAAQ,aAAa;IAC5B,QAAQ,CAAmB;IAC3B,gBAAgB,CAAS;IAE1C,YACE,UAAkB,EAClB,MAAqB,EACrB,QAA0B,EAC1B,gBAAyB;QAEzB,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,gBAAgB;YACnB,gBAAgB,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACxD,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAE9B,MAAM,eAAe,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAEpD,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,aAAa,CAAC;QAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,SAAS,CAAC;QACtD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,IAAI,CAAC;QAEjD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;QAErE,MAAM,IAAI,GAAG;YACX,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;YACtB,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;YACpC,OAAO;YACP,UAAU;YACV,OAAO;YACP,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC9B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;YAC1B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;YAC1B,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;YACtB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;YACtB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,eAAe;YACf,SAAS,EAAE,SAAS;SACrB,CAAC;QAEF,8CAA8C;QAC9C,MAAM,kBAAkB,GAAG;YACzB,6BAA6B;YAC7B,2BAA2B;YAC3B,0BAA0B;YAC1B,2BAA2B;YAC3B,wBAAwB;YACxB,0BAA0B;SAC3B,CAAC;QAEF,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,iBAAiB;YACjB,cAAc,CACZ,2BAA2B,EAC3B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,EACvC,IAAI,CACL;YACD,cAAc,CACZ,+BAA+B,EAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,EACrC,IAAI,CACL;YACD,cAAc,CACZ,8BAA8B,EAC9B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,EAC3C,IAAI,CACL;YACD,uCAAuC;YACvC,cAAc,CACZ,mCAAmC,EACnC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,EAClC,IAAI,EACJ,EAAE,IAAI,EAAE,KAAK,EAAE,CAChB;YACD,cAAc,CACZ,wCAAwC,EACxC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,kBAAkB,CAAC,EACvC,IAAI,EACJ,EAAE,IAAI,EAAE,KAAK,EAAE,CAChB;YACD,0BAA0B;YAC1B,cAAc,CACZ,oDAAoD,EACpD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,oCAAoC,CAAC,EAC1D,IAAI,CACL;YACD,cAAc,CACZ,6DAA6D,EAC7D,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,6CAA6C,CAAC,EACnE,IAAI,CACL;YACD,wBAAwB;YACxB,cAAc,CACZ,yCAAyC,EACzC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,iBAAiB,CAAC,EACnE,IAAI,CACL;YACD,uBAAuB;YACvB,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC9B,EAAE,CAAC,IAAI,CACL,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,CAAC,EACrD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC,CACrC,CACF;SACF,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;IAC9B,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC;QAC/C,MAAM,gBAAgB,GAAgD;YACpE,EAAE,IAAI,EAAE,8BAA8B,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;YACzE;gBACE,IAAI,EAAE,6BAA6B;gBACnC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,SAAS;aACjD;YACD;gBACE,IAAI,EAAE,2BAA2B;gBACjC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,aAAa;aACrD;SACF,CAAC;QAEF,KAAK,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,gBAAgB,EAAE,CAAC;YACnD,IAAI,CAAC,SAAS;gBAAE,SAAS;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;YAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CACpB,IAAI,CAAC,UAAU,EACf,SAAS,EACT,QAAQ,EACR,IAAI,EACJ,UAAU,CACX,CAAC;YACF,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;gBACvC,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAEO,oBAAoB;QAC1B,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,aAAa,CAAC;QAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,SAAS,CAAC;QAEtD,IAAI,UAAU,EAAE,CAAC;YACf,QAAQ,CAAC,IAAI,CACX,8BAA8B,EAC9B,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,CACrB,CAAC;QACJ,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,QAAQ,CAAC,IAAI,CACX,qCAAqC,EACrC,cAAc,EACd,mBAAmB,EACnB,kBAAkB,CACnB,CAAC;QACJ,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACzB,QAAQ,CAAC,IAAI,CACX,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,CAChB,CAAC;QACJ,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACvB,QAAQ,CAAC,IAAI,CACX,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,CAC1B,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,UAAkB,EAClB,MAAqB,EACrB,QAA0B,EAC1B,gBAAyB;IAEzB,MAAM,SAAS,GAAG,IAAI,mBAAmB,CACvC,UAAU,EACV,MAAM,EACN,QAAQ,EACR,gBAAgB,CACjB,CAAC;IACF,MAAM,SAAS,CAAC,QAAQ,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -18,6 +18,20 @@
|
|
|
18
18
|
└── README.md
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
## Workflow Routing (mandatory)
|
|
22
|
+
|
|
23
|
+
| Scenario | Action |
|
|
24
|
+
|---|---|
|
|
25
|
+
| New feature / non-trivial change | `/speckit.workflow` |
|
|
26
|
+
| Small fix (1-2 lines, docs-only) | Direct edit, no spec |
|
|
27
|
+
| Bug | Apply `superpowers:systematic-debugging` |
|
|
28
|
+
| UI component | Apply `frontend-design:frontend-design` skill |
|
|
29
|
+
| Commit / PR | Use `commit-commands:commit-push-pr` skill |
|
|
30
|
+
|
|
31
|
+
## Architecture Constitution
|
|
32
|
+
|
|
33
|
+
Read `.specify/memory/constitution.md` before any architectural decision. It is auto-loaded at session start.
|
|
34
|
+
|
|
21
35
|
{{#if springBoot}}
|
|
22
36
|
## Backend — Spring Boot {{versions.springBoot}}
|
|
23
37
|
|
|
@@ -90,15 +104,6 @@ docker compose down # Stop services
|
|
|
90
104
|
- **pgAdmin:** localhost:5050 (admin@admin.com / admin)
|
|
91
105
|
|
|
92
106
|
{{/if}}
|
|
93
|
-
## Workflow
|
|
94
|
-
|
|
95
|
-
| Scenario | Action |
|
|
96
|
-
|---|---|
|
|
97
|
-
| New feature | `/speckit.workflow` — routes automatiquement (fast track ou full workflow spec→PR) |
|
|
98
|
-
| Bug | Describe to Claude → apply `superpowers:systematic-debugging` |
|
|
99
|
-
| UI component | Apply `frontend-design:frontend-design` skill |
|
|
100
|
-
| Commit / PR | Use `commit-commands:commit-push-pr` skill |
|
|
101
|
-
|
|
102
107
|
## TDD
|
|
103
108
|
|
|
104
109
|
Write the failing test first — no implementation code before a red test.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: block-dangerous-rm
|
|
3
|
+
enabled: true
|
|
4
|
+
event: bash
|
|
5
|
+
pattern: rm\s+(-rf|-fr)
|
|
6
|
+
action: block
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
⚠️ **Commande rm -rf détectée**
|
|
10
|
+
|
|
11
|
+
Cette commande peut supprimer des fichiers de façon irréversible. Vérifie bien le chemin avant de continuer.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: block-force-push
|
|
3
|
+
enabled: true
|
|
4
|
+
event: bash
|
|
5
|
+
pattern: git\s+push\s+.*--force(?!-with-lease)|git\s+push\s+.*\s-f\b
|
|
6
|
+
action: block
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
🚫 **git push --force bloqué**
|
|
10
|
+
|
|
11
|
+
Le force push est interdit sur ce projet. Utilise `--force-with-lease` si tu sais ce que tu fais, ou ouvre une PR à la place.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stop-verify-tests
|
|
3
|
+
enabled: true
|
|
4
|
+
event: stop
|
|
5
|
+
pattern: .*
|
|
6
|
+
action: warn
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
⚠️ **Avant de terminer — TDD check**
|
|
10
|
+
|
|
11
|
+
As-tu :
|
|
12
|
+
- [ ] Lancé les tests et vérifié qu'ils passent ?
|
|
13
|
+
{{#if fastapi}} - `cd backend && .venv/bin/pytest tests/ -v`
|
|
14
|
+
{{/if}}{{#if springBoot}} - `cd backend && ./mvnw test`
|
|
15
|
+
{{/if}}{{#if frontend}} - `cd frontend && npm test -- --watch=false`
|
|
16
|
+
{{/if}}- [ ] Lancé le lint ?
|
|
17
|
+
{{#if fastapi}} - `cd backend && .venv/bin/ruff check .`
|
|
18
|
+
{{/if}}{{#if springBoot}} - `cd backend && ./mvnw checkstyle:check` (if configured)
|
|
19
|
+
{{/if}}{{#if frontend}} - `cd frontend && npm run lint`
|
|
20
|
+
{{/if}}
|
|
21
|
+
Iron law : aucun commit sans tests verts.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: warn-env-edit
|
|
3
|
+
enabled: true
|
|
4
|
+
event: file
|
|
5
|
+
pattern: (^|/)\.env(\.|$)
|
|
6
|
+
action: warn
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
⚠️ **Fichier .env détecté**
|
|
10
|
+
|
|
11
|
+
Tu es sur le point de modifier un fichier d'environnement. Assure-toi de ne pas committer de secrets — utilise `.env.example` pour les valeurs de référence.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: warn-no-test-before-commit
|
|
3
|
+
enabled: true
|
|
4
|
+
event: bash
|
|
5
|
+
pattern: git\s+commit
|
|
6
|
+
action: warn
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
⚠️ **Commit détecté — tests lancés ?**
|
|
10
|
+
|
|
11
|
+
Avant de committer, confirme :
|
|
12
|
+
{{#if fastapi}}- [ ] `cd backend && .venv/bin/pytest tests/ -v` → vert
|
|
13
|
+
{{/if}}{{#if springBoot}}- [ ] `cd backend && ./mvnw test` → vert
|
|
14
|
+
{{/if}}{{#if frontend}}- [ ] `cd frontend && npm test -- --watch=false` → vert
|
|
15
|
+
{{/if}}{{#if fastapi}}- [ ] `cd backend && .venv/bin/ruff check .` → propre
|
|
16
|
+
{{/if}}{{#if frontend}}- [ ] `cd frontend && npm run lint` → propre
|
|
17
|
+
{{/if}}
|
|
18
|
+
Iron law : aucun commit sans tests verts.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Hook: PreToolUse (Bash) — {{name}} project guards.
|
|
3
|
+
# Exit 2 = block | Exit 0 = allow
|
|
4
|
+
|
|
5
|
+
COMMAND=$(jq -r '.tool_input.command // ""')
|
|
6
|
+
|
|
7
|
+
# Strip heredoc bodies so patterns don't match inside commit messages.
|
|
8
|
+
COMMAND_STRIPPED=$(echo "$COMMAND" | awk '
|
|
9
|
+
/<</ {
|
|
10
|
+
tmp = $0
|
|
11
|
+
gsub(/^.*<<'"'"'?/, "", tmp)
|
|
12
|
+
gsub(/[^A-Za-z_0-9].*$/, "", tmp)
|
|
13
|
+
delim = tmp
|
|
14
|
+
in_hd = 1; print; next
|
|
15
|
+
}
|
|
16
|
+
in_hd { if ($0 == delim) in_hd = 0; next }
|
|
17
|
+
{ print }
|
|
18
|
+
')
|
|
19
|
+
{{#if frontend}}
|
|
20
|
+
|
|
21
|
+
# Block: npm install / uninstall outside frontend/
|
|
22
|
+
if echo "$COMMAND_STRIPPED" | grep -qE '(^|[;&|[:space:]])npm[[:space:]]+(install|i|uninstall|un)(\s|$)'; then
|
|
23
|
+
current_dir=$(pwd)
|
|
24
|
+
in_frontend=0
|
|
25
|
+
if echo "$current_dir" | grep -qE '/frontend(/|$)'; then
|
|
26
|
+
in_frontend=1
|
|
27
|
+
fi
|
|
28
|
+
if echo "$COMMAND_STRIPPED" | grep -qE '(^|[;&|[:space:]])cd[[:space:]]+([^;&|]*\/)?frontend([[:space:]]|$|/)'; then
|
|
29
|
+
in_frontend=1
|
|
30
|
+
fi
|
|
31
|
+
if [ "$in_frontend" -eq 0 ]; then
|
|
32
|
+
echo "BLOCKED: 'npm install' is only allowed inside frontend/. Run: cd frontend && npm install" >&2
|
|
33
|
+
exit 2
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
36
|
+
{{/if}}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Hook: SessionStart (once: true) — loads project constitution into context once per session.
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
+
REPO_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null)"
|
|
6
|
+
if [ -z "$REPO_ROOT" ]; then
|
|
7
|
+
exit 0
|
|
8
|
+
fi
|
|
9
|
+
CONSTITUTION="$REPO_ROOT/.specify/memory/constitution.md"
|
|
10
|
+
|
|
11
|
+
if [ -f "$CONSTITUTION" ]; then
|
|
12
|
+
echo "=== {{name}} Architecture Constitution (auto-loaded) ==="
|
|
13
|
+
cat "$CONSTITUTION"
|
|
14
|
+
echo "=== End Constitution ==="
|
|
15
|
+
fi
|
|
@@ -2,9 +2,52 @@
|
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
4
|
{{#each allowedCommands}}
|
|
5
|
-
"{{this}}"
|
|
5
|
+
"{{this}}",
|
|
6
6
|
{{/each}}
|
|
7
|
+
"Bash(.specify/scripts/bash/*)",
|
|
8
|
+
"Bash(git commit*)",
|
|
9
|
+
"Bash(gh *)",
|
|
10
|
+
"Skill(commit-commands:commit)",
|
|
11
|
+
"Skill(commit-commands:commit:*)",
|
|
12
|
+
"Skill(commit-commands:commit-push-pr)",
|
|
13
|
+
"WebFetch(domain:github.com)",
|
|
14
|
+
"WebFetch(domain:resources.anthropic.com)"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"hooks": {
|
|
18
|
+
"SessionStart": [
|
|
19
|
+
{
|
|
20
|
+
"matcher": "",
|
|
21
|
+
"hooks": [
|
|
22
|
+
{
|
|
23
|
+
"type": "command",
|
|
24
|
+
"command": "{{claudeDir}}/hooks/session-start.sh",
|
|
25
|
+
"once": true
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
7
29
|
],
|
|
8
|
-
"
|
|
30
|
+
"PreToolUse": [
|
|
31
|
+
{
|
|
32
|
+
"matcher": "Bash",
|
|
33
|
+
"hooks": [
|
|
34
|
+
{
|
|
35
|
+
"type": "command",
|
|
36
|
+
"command": "{{claudeDir}}/hooks/pre-bash.sh"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"PreCompact": [
|
|
42
|
+
{
|
|
43
|
+
"matcher": "",
|
|
44
|
+
"hooks": [
|
|
45
|
+
{
|
|
46
|
+
"type": "command",
|
|
47
|
+
"command": "echo 'Compaction: KEEP: file diffs, failing test errors, architecture decisions from constitution.md. DROP: passing test output, startup logs, install output, resolved discussions.'"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
]
|
|
9
52
|
}
|
|
10
53
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# {{name}} — Architecture Constitution
|
|
2
|
+
|
|
3
|
+
> Auto-loaded at session start via `.claude/hooks/session-start.sh`.
|
|
4
|
+
> Edit this file to document hard architectural constraints for your project.
|
|
5
|
+
|
|
6
|
+
## Core Principles
|
|
7
|
+
|
|
8
|
+
### I. [Your first principle]
|
|
9
|
+
|
|
10
|
+
Describe a non-negotiable architectural rule here. Example: "All data access MUST go through the repository layer. Direct DB queries in controllers are forbidden."
|
|
11
|
+
|
|
12
|
+
### II. [Your second principle]
|
|
13
|
+
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
## Scope
|
|
17
|
+
|
|
18
|
+
### In Scope
|
|
19
|
+
- [Feature A]
|
|
20
|
+
- [Feature B]
|
|
21
|
+
|
|
22
|
+
### Out of Scope
|
|
23
|
+
- [Feature C — why excluded]
|
|
24
|
+
|
|
25
|
+
## Key Decisions
|
|
26
|
+
|
|
27
|
+
Document architectural decisions here as they are made (or link to DECISIONS.md).
|
|
28
|
+
|
|
29
|
+
| # | Decision | Rationale |
|
|
30
|
+
|---|----------|-----------|
|
|
31
|
+
| 1 | Example: PostgreSQL over SQLite | Production-grade from day 1 |
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const TEMPLATES_DIR: string;
|
|
1
2
|
export declare function renderTemplate(templatePath: string, data: Record<string, unknown>): string;
|
|
2
3
|
export declare function renderAndWrite(templatePath: string, outputPath: string, data: Record<string, unknown>, options?: {
|
|
3
4
|
mode?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-engine.d.ts","sourceRoot":"","sources":["../../src/utils/template-engine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"template-engine.d.ts","sourceRoot":"","sources":["../../src/utils/template-engine.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,aAAa,QAA0C,CAAC;AAIrE,wBAAgB,cAAc,CAC5B,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAKR;AAED,wBAAsB,cAAc,CAClC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CAOf"}
|
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
|
-
const TEMPLATES_DIR = path.join(__dirname, "..", "templates");
|
|
7
|
+
export const TEMPLATES_DIR = path.join(__dirname, "..", "templates");
|
|
8
8
|
Handlebars.registerHelper("lowerCase", (str) => str.toLowerCase());
|
|
9
9
|
export function renderTemplate(templatePath, data) {
|
|
10
10
|
const fullPath = path.join(TEMPLATES_DIR, templatePath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-engine.js","sourceRoot":"","sources":["../../src/utils/template-engine.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"template-engine.js","sourceRoot":"","sources":["../../src/utils/template-engine.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAC3C,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAErE,UAAU,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;AAE3E,MAAM,UAAU,cAAc,CAC5B,YAAoB,EACpB,IAA6B;IAE7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,YAAoB,EACpB,UAAkB,EAClB,IAA6B,EAC7B,OAA2B;IAE3B,MAAM,OAAO,GAAG,cAAc,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IACnD,IAAI,OAAO,EAAE,IAAI,EAAE,CAAC;QAClB,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;SAAM,CAAC;QACN,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;AACH,CAAC"}
|