@podosoft/podokit 0.5.1 → 0.7.0
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 +6 -0
- package/dist/add.d.ts +24 -2
- package/dist/add.js +77 -20
- package/dist/assemble.js +4 -1
- package/dist/create.d.ts +2 -0
- package/dist/create.js +6 -0
- package/dist/eject.js +9 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -2
- package/dist/lib.d.ts +14 -0
- package/dist/lib.js +34 -0
- package/dist/lockfile.d.ts +7 -2
- package/dist/lockfile.js +37 -2
- package/dist/templates/base/AGENTS.md +30 -0
- package/dist/templates/base/CLAUDE.md +6 -0
- package/dist/templates/fullstack-nest-svelte/AGENTS.md +79 -0
- package/dist/templates/fullstack-nest-svelte/CLAUDE.md +6 -0
- package/dist/templates/fullstack-nest-svelte/Dockerfile.dev +25 -0
- package/dist/templates/fullstack-nest-svelte/apps/api/package.json +4 -2
- package/dist/templates/fullstack-nest-svelte/apps/api/src/app.extensions.ts +21 -0
- package/dist/templates/fullstack-nest-svelte/apps/api/src/app.module.ts +3 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/app.html +2 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/index.ts +1 -3
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs-content.svelte +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs-list.svelte +5 -25
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs-trigger.svelte +1 -4
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs.svelte +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/static/favicon.svg +13 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/vite.config.ts +12 -0
- package/dist/templates/fullstack-nest-svelte/compose.dev.yaml +150 -0
- package/dist/templates/fullstack-nest-svelte/dot-claude/skills/podokit-add-module/SKILL.md +27 -0
- package/dist/templates/fullstack-nest-svelte/dot-claude/skills/podokit-nest-endpoint/SKILL.md +23 -0
- package/dist/templates/fullstack-nest-svelte/dot-claude/skills/podokit-sveltekit-route/SKILL.md +23 -0
- package/dist/templates/fullstack-nest-svelte/dot-claude/skills/podokit-update/SKILL.md +26 -0
- package/dist/templates/fullstack-nest-svelte/dot-cursor/rules/podokit.mdc +11 -0
- package/dist/templates/fullstack-nest-svelte/dot-devcontainer/devcontainer.json +20 -0
- package/dist/templates/fullstack-nest-svelte/dot-dockerignore +8 -0
- package/dist/templates/fullstack-nest-svelte/dot-env.docker +26 -0
- package/dist/templates/fullstack-nest-svelte/dot-github/copilot-instructions.md +6 -0
- package/dist/templates/fullstack-nest-svelte/dot-mcp.json +8 -0
- package/dist/templates/fullstack-nest-svelte/infra/traefik/dynamic.yml +13 -0
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/site-settings/site-settings.controller.ts +87 -0
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/site-settings/site-settings.module.ts +14 -0
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/site-settings/site-settings.service.ts +84 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/app.d.ts +2 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.ts +12 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/admin/registry.svelte.ts +37 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/app-sidebar.svelte +25 -11
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/site-footer.svelte +32 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/index.ts +16 -3
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/messages.ts +52 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/site.svelte.ts +50 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/+page.svelte +38 -13
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/general-settings.svelte +203 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/+layout.svelte +7 -3
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.svelte +8 -3
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+layout.server.ts +34 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+layout.svelte +41 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+page.svelte +2 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/maintenance/+page.svelte +25 -0
- package/dist/templates/modules/admin-dashboard/files/dot-claude/skills/podokit-data-table/SKILL.md +43 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/general-effects.ui.spec.ts +81 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/settings.ui.spec.ts +42 -1
- package/dist/templates/modules/admin-dashboard/module.manifest.json +22 -4
- package/dist/templates/modules/auth/files/apps/api/src/auth/feature-gate.ts +27 -0
- package/dist/templates/modules/auth/module.manifest.json +9 -8
- package/dist/templates/modules/bullmq/module.manifest.json +1 -1
- package/dist/templates/modules/file-upload/files/apps/api/src/files/files.controller.ts +3 -0
- package/dist/templates/modules/mailer/files/apps/api/src/mail/db.ts +13 -0
- package/dist/templates/modules/{auth → mailer}/files/apps/api/src/mail/mailer.ts +41 -7
- package/dist/templates/modules/mailer/module.manifest.json +38 -0
- package/dist/templates/todo/AGENTS.md +80 -0
- package/dist/templates/todo/CLAUDE.md +6 -0
- package/dist/templates/todo/Dockerfile.dev +25 -0
- package/dist/templates/todo/apps/api/package.json +4 -2
- package/dist/templates/todo/apps/api/src/app.extensions.ts +21 -0
- package/dist/templates/todo/apps/api/src/app.module.ts +3 -0
- package/dist/templates/todo/apps/web/src/app.html +2 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/index.ts +1 -3
- package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/tabs-content.svelte +1 -1
- package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/tabs-list.svelte +5 -25
- package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/tabs-trigger.svelte +1 -4
- package/dist/templates/todo/apps/web/src/lib/components/ui/tabs/tabs.svelte +1 -1
- package/dist/templates/todo/apps/web/static/favicon.svg +13 -0
- package/dist/templates/todo/apps/web/vite.config.ts +12 -0
- package/dist/templates/todo/compose.dev.yaml +150 -0
- package/dist/templates/todo/dot-claude/skills/podokit-add-module/SKILL.md +27 -0
- package/dist/templates/todo/dot-claude/skills/podokit-nest-endpoint/SKILL.md +23 -0
- package/dist/templates/todo/dot-claude/skills/podokit-sveltekit-route/SKILL.md +23 -0
- package/dist/templates/todo/dot-claude/skills/podokit-update/SKILL.md +26 -0
- package/dist/templates/todo/dot-cursor/rules/podokit.mdc +11 -0
- package/dist/templates/todo/dot-devcontainer/devcontainer.json +20 -0
- package/dist/templates/todo/dot-dockerignore +8 -0
- package/dist/templates/todo/dot-env.docker +26 -0
- package/dist/templates/todo/dot-github/copilot-instructions.md +6 -0
- package/dist/templates/todo/dot-mcp.json +8 -0
- package/dist/templates/todo/infra/traefik/dynamic.yml +13 -0
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -47,10 +47,16 @@ Options:
|
|
|
47
47
|
- base : minimal npm workspace
|
|
48
48
|
--dir <path> Target directory (default: ./<name>)
|
|
49
49
|
--pm <name> Package manager: npm | pnpm | yarn (default: npm)
|
|
50
|
+
--no-ai Skip AI agent guidance (AGENTS.md, CLAUDE.md, editor rules)
|
|
50
51
|
-y, --yes Skip prompts and accept defaults
|
|
51
52
|
-h, --help Show help
|
|
52
53
|
```
|
|
53
54
|
|
|
55
|
+
Generated projects include agent guidance for AI coding tools — an
|
|
56
|
+
[`AGENTS.md`](https://agents.md) (the open standard), a `CLAUDE.md` that imports
|
|
57
|
+
it, `.cursor`/`.github` pointers, and Claude Code skills under `.claude/skills/`.
|
|
58
|
+
Modules extend `AGENTS.md` as you add them. Use `--no-ai` to skip.
|
|
59
|
+
|
|
54
60
|
Examples:
|
|
55
61
|
|
|
56
62
|
```bash
|
package/dist/add.d.ts
CHANGED
|
@@ -2,8 +2,15 @@ interface Injection {
|
|
|
2
2
|
file: string;
|
|
3
3
|
marker: string;
|
|
4
4
|
text: string;
|
|
5
|
+
/** Skip (instead of failing) if the target file or marker is absent. */
|
|
6
|
+
optional?: boolean;
|
|
5
7
|
}
|
|
8
|
+
/** Highest `module.manifest.json` schema version this CLI understands. A package
|
|
9
|
+
* module declaring a higher version is rejected rather than mis-applied. */
|
|
10
|
+
export declare const SUPPORTED_MANIFEST_VERSION = 1;
|
|
6
11
|
export interface ModuleManifest {
|
|
12
|
+
/** Manifest schema version. Absent (bundled modules) means "same as the CLI". */
|
|
13
|
+
manifestVersion?: number;
|
|
7
14
|
name: string;
|
|
8
15
|
description: string;
|
|
9
16
|
requires?: string[];
|
|
@@ -14,6 +21,11 @@ export interface ModuleManifest {
|
|
|
14
21
|
env?: string[];
|
|
15
22
|
inject?: Injection[];
|
|
16
23
|
instructions?: string[];
|
|
24
|
+
/** App-relative globs this module's files should own (durably user-editable,
|
|
25
|
+
* never touched by `podo update`). Merged into the project's ownedGlobs so
|
|
26
|
+
* they survive lock recompute. Use for public presentation pages a consumer
|
|
27
|
+
* restyles, while keeping the module's `$lib` logic managed. */
|
|
28
|
+
ownedGlobs?: string[];
|
|
17
29
|
}
|
|
18
30
|
export interface AddOptions {
|
|
19
31
|
projectRoot: string;
|
|
@@ -27,9 +39,19 @@ export interface AddResult {
|
|
|
27
39
|
instructions: string[];
|
|
28
40
|
/** Required modules that were auto-added because they were missing. */
|
|
29
41
|
added: string[];
|
|
42
|
+
/** ownedGlobs declared by this module and every module it pulled in. */
|
|
43
|
+
ownedGlobs: string[];
|
|
30
44
|
}
|
|
31
|
-
/**
|
|
32
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a module's directory by name: first the bundled `modulesDir`, then a
|
|
47
|
+
* node-resolvable npm package `@podosoft/podokit-module-<name>` installed in the
|
|
48
|
+
* project (or a fully-qualified `@scope/pkg` name). Returns null if not found.
|
|
49
|
+
*/
|
|
50
|
+
export declare function resolveModuleDir(name: string, modulesDir: string, projectRoot: string): string | null;
|
|
51
|
+
/** List modules available to a project: the bundled ones under `modulesDir` plus,
|
|
52
|
+
* when `projectRoot` is given, any `@podosoft/podokit-module-*` packages installed
|
|
53
|
+
* in it. Bundled names take precedence on a clash. */
|
|
54
|
+
export declare function listModules(modulesDir: string, projectRoot?: string): {
|
|
33
55
|
name: string;
|
|
34
56
|
description: string;
|
|
35
57
|
}[];
|
package/dist/add.js
CHANGED
|
@@ -1,21 +1,69 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SUPPORTED_MANIFEST_VERSION = void 0;
|
|
4
|
+
exports.resolveModuleDir = resolveModuleDir;
|
|
3
5
|
exports.listModules = listModules;
|
|
4
6
|
exports.addModule = addModule;
|
|
5
7
|
const node_fs_1 = require("node:fs");
|
|
6
8
|
const node_path_1 = require("node:path");
|
|
9
|
+
const node_module_1 = require("node:module");
|
|
7
10
|
const podokit_template_engine_1 = require("@podosoft/podokit-template-engine");
|
|
8
11
|
const lockfile_1 = require("./lockfile");
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
/** Highest `module.manifest.json` schema version this CLI understands. A package
|
|
13
|
+
* module declaring a higher version is rejected rather than mis-applied. */
|
|
14
|
+
exports.SUPPORTED_MANIFEST_VERSION = 1;
|
|
15
|
+
const PACKAGE_PREFIX = "@podosoft/podokit-module-";
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a module's directory by name: first the bundled `modulesDir`, then a
|
|
18
|
+
* node-resolvable npm package `@podosoft/podokit-module-<name>` installed in the
|
|
19
|
+
* project (or a fully-qualified `@scope/pkg` name). Returns null if not found.
|
|
20
|
+
*/
|
|
21
|
+
function resolveModuleDir(name, modulesDir, projectRoot) {
|
|
22
|
+
const bundled = (0, node_path_1.join)(modulesDir, name);
|
|
23
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(bundled, "module.manifest.json")))
|
|
24
|
+
return bundled;
|
|
25
|
+
const pkg = name.includes("/") ? name : `${PACKAGE_PREFIX}${name}`;
|
|
26
|
+
try {
|
|
27
|
+
const req = (0, node_module_1.createRequire)((0, node_path_1.join)(projectRoot, "package.json"));
|
|
28
|
+
return (0, node_path_1.dirname)(req.resolve(`${pkg}/module.manifest.json`));
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function assertManifestVersion(name, manifest) {
|
|
35
|
+
if ((manifest.manifestVersion ?? exports.SUPPORTED_MANIFEST_VERSION) > exports.SUPPORTED_MANIFEST_VERSION) {
|
|
36
|
+
throw new Error(`Module "${name}" needs a newer PodoKit (manifest v${manifest.manifestVersion}, this CLI supports v${exports.SUPPORTED_MANIFEST_VERSION}). Update @podosoft/podokit.`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** List modules available to a project: the bundled ones under `modulesDir` plus,
|
|
40
|
+
* when `projectRoot` is given, any `@podosoft/podokit-module-*` packages installed
|
|
41
|
+
* in it. Bundled names take precedence on a clash. */
|
|
42
|
+
function listModules(modulesDir, projectRoot) {
|
|
43
|
+
const out = new Map();
|
|
44
|
+
if ((0, node_fs_1.existsSync)(modulesDir)) {
|
|
45
|
+
for (const name of (0, node_fs_1.readdirSync)(modulesDir)) {
|
|
46
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(modulesDir, name, "module.manifest.json"))) {
|
|
47
|
+
out.set(name, readManifest((0, node_path_1.join)(modulesDir, name)).description);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (projectRoot) {
|
|
52
|
+
const scope = (0, node_path_1.join)(projectRoot, "node_modules", "@podosoft");
|
|
53
|
+
if ((0, node_fs_1.existsSync)(scope)) {
|
|
54
|
+
for (const dir of (0, node_fs_1.readdirSync)(scope)) {
|
|
55
|
+
if (!dir.startsWith("podokit-module-"))
|
|
56
|
+
continue;
|
|
57
|
+
const manifestPath = (0, node_path_1.join)(scope, dir, "module.manifest.json");
|
|
58
|
+
if (!(0, node_fs_1.existsSync)(manifestPath))
|
|
59
|
+
continue;
|
|
60
|
+
const manifest = readManifest((0, node_path_1.join)(scope, dir));
|
|
61
|
+
if (!out.has(manifest.name))
|
|
62
|
+
out.set(manifest.name, manifest.description);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return [...out].map(([name, description]) => ({ name, description }));
|
|
19
67
|
}
|
|
20
68
|
function readManifest(moduleDir) {
|
|
21
69
|
return JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(moduleDir, "module.manifest.json"), "utf8"));
|
|
@@ -40,10 +88,10 @@ function appendEnv(projectRoot, lines) {
|
|
|
40
88
|
}
|
|
41
89
|
/** Heuristic: is `module` already applied to the project? */
|
|
42
90
|
function isApplied(projectRoot, modulesDir, module) {
|
|
43
|
-
const
|
|
44
|
-
if (!
|
|
91
|
+
const moduleDir = resolveModuleDir(module, modulesDir, projectRoot);
|
|
92
|
+
if (!moduleDir)
|
|
45
93
|
return false;
|
|
46
|
-
const manifest =
|
|
94
|
+
const manifest = readManifest(moduleDir);
|
|
47
95
|
const firstInject = manifest.inject?.[0];
|
|
48
96
|
if (firstInject) {
|
|
49
97
|
const target = (0, node_path_1.join)(projectRoot, firstInject.file);
|
|
@@ -58,17 +106,19 @@ function isApplied(projectRoot, modulesDir, module) {
|
|
|
58
106
|
*/
|
|
59
107
|
function addModule(options) {
|
|
60
108
|
const result = applyModule(options.projectRoot, options.module, options.modulesDir, new Set());
|
|
61
|
-
// Record the module (and any auto-added requirements)
|
|
62
|
-
|
|
109
|
+
// Record the module (and any auto-added requirements), fold in the modules'
|
|
110
|
+
// declared ownedGlobs, and refresh the lock.
|
|
111
|
+
(0, lockfile_1.recordModules)(options.projectRoot, [...result.added, result.module], options.podokitVersion, result.ownedGlobs);
|
|
63
112
|
return result;
|
|
64
113
|
}
|
|
65
114
|
function applyModule(projectRoot, module, modulesDir, applied) {
|
|
66
|
-
const moduleDir = (
|
|
67
|
-
if (!
|
|
68
|
-
const available = listModules(modulesDir).map((m) => m.name);
|
|
115
|
+
const moduleDir = resolveModuleDir(module, modulesDir, projectRoot);
|
|
116
|
+
if (!moduleDir) {
|
|
117
|
+
const available = listModules(modulesDir, projectRoot).map((m) => m.name);
|
|
69
118
|
throw new Error(`Unknown module "${module}".${available.length ? ` Available: ${available.join(", ")}.` : ""}`);
|
|
70
119
|
}
|
|
71
120
|
const manifest = readManifest(moduleDir);
|
|
121
|
+
assertManifestVersion(module, manifest);
|
|
72
122
|
const appPkgPath = (0, node_path_1.join)(projectRoot, "apps", manifest.targetApp, "package.json");
|
|
73
123
|
if (!(0, node_fs_1.existsSync)(appPkgPath)) {
|
|
74
124
|
throw new Error(`This does not look like a PodoKit project: ${(0, node_path_1.join)("apps", manifest.targetApp, "package.json")} not found. Run inside a generated project.`);
|
|
@@ -76,11 +126,13 @@ function applyModule(projectRoot, module, modulesDir, applied) {
|
|
|
76
126
|
applied.add(module);
|
|
77
127
|
// 0) apply required modules first (auto-add if missing)
|
|
78
128
|
const added = [];
|
|
129
|
+
const ownedGlobs = [...(manifest.ownedGlobs ?? [])];
|
|
79
130
|
for (const required of manifest.requires ?? []) {
|
|
80
131
|
if (applied.has(required) || isApplied(projectRoot, modulesDir, required))
|
|
81
132
|
continue;
|
|
82
133
|
const result = applyModule(projectRoot, required, modulesDir, applied);
|
|
83
134
|
added.push(required, ...result.added);
|
|
135
|
+
ownedGlobs.push(...result.ownedGlobs);
|
|
84
136
|
}
|
|
85
137
|
const appName = projectName(projectRoot);
|
|
86
138
|
const vars = { projectName: appName };
|
|
@@ -109,10 +161,15 @@ function applyModule(projectRoot, module, modulesDir, applied) {
|
|
|
109
161
|
for (const injection of manifest.inject ?? []) {
|
|
110
162
|
const target = (0, node_path_1.join)(projectRoot, injection.file);
|
|
111
163
|
if (!(0, node_fs_1.existsSync)(target)) {
|
|
164
|
+
if (injection.optional)
|
|
165
|
+
continue;
|
|
112
166
|
throw new Error(`Cannot wire module: ${injection.file} not found.`);
|
|
113
167
|
}
|
|
114
|
-
|
|
168
|
+
const content = (0, node_fs_1.readFileSync)(target, "utf8");
|
|
169
|
+
if (injection.optional && !content.includes(injection.marker))
|
|
170
|
+
continue;
|
|
171
|
+
(0, node_fs_1.writeFileSync)(target, (0, podokit_template_engine_1.insertAtMarker)(content, injection.marker, injection.text));
|
|
115
172
|
}
|
|
116
173
|
const instructions = (manifest.instructions ?? []).map((line) => line.replace(/<app>/g, appName));
|
|
117
|
-
return { module, instructions, added };
|
|
174
|
+
return { module, instructions, added, ownedGlobs };
|
|
118
175
|
}
|
package/dist/assemble.js
CHANGED
|
@@ -47,7 +47,10 @@ function applyModuleToTree(tree, moduleDir, vars) {
|
|
|
47
47
|
// 4) inject wiring at markers
|
|
48
48
|
for (const injection of manifest.inject ?? []) {
|
|
49
49
|
const target = injection.file;
|
|
50
|
-
|
|
50
|
+
const content = textOf(tree, target);
|
|
51
|
+
if (injection.optional && !content.includes(injection.marker))
|
|
52
|
+
continue;
|
|
53
|
+
setText(tree, target, (0, podokit_template_engine_1.insertAtMarker)(content, injection.marker, injection.text));
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
/** Assemble the project tree for a template + ordered modules, in memory. */
|
package/dist/create.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface CreateOptions {
|
|
|
14
14
|
packageManager?: PackageManager;
|
|
15
15
|
/** PodoKit version stamped into the lockfile. Defaults to the CLI version. */
|
|
16
16
|
podokitVersion?: string;
|
|
17
|
+
/** Include AI agent guidance (AGENTS.md, CLAUDE.md, editor rules). Defaults to true. */
|
|
18
|
+
ai?: boolean;
|
|
17
19
|
}
|
|
18
20
|
export interface CreateResult {
|
|
19
21
|
projectDir: string;
|
package/dist/create.js
CHANGED
|
@@ -9,6 +9,8 @@ const podokit_template_engine_1 = require("@podosoft/podokit-template-engine");
|
|
|
9
9
|
const templates_1 = require("./templates");
|
|
10
10
|
Object.defineProperty(exports, "DEFAULT_TEMPLATE", { enumerable: true, get: function () { return templates_1.DEFAULT_TEMPLATE; } });
|
|
11
11
|
const lockfile_1 = require("./lockfile");
|
|
12
|
+
/** AI agent guidance files, removed when scaffolding with `--no-ai`. */
|
|
13
|
+
const AI_ARTIFACTS = ["AGENTS.md", "CLAUDE.md", ".claude", ".cursor", ".github/copilot-instructions.md"];
|
|
12
14
|
const NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-._]*[a-z0-9])?$/i;
|
|
13
15
|
/** Validate a project name: no path separators, npm-friendly characters. */
|
|
14
16
|
function assertValidName(name) {
|
|
@@ -42,6 +44,10 @@ function create(options) {
|
|
|
42
44
|
}
|
|
43
45
|
const vars = { projectName: name, packageManager };
|
|
44
46
|
(0, podokit_template_engine_1.copyTemplate)(templateDir, projectDir, vars);
|
|
47
|
+
if (options.ai === false) {
|
|
48
|
+
for (const artifact of AI_ARTIFACTS)
|
|
49
|
+
(0, node_fs_1.rmSync)((0, node_path_1.join)(projectDir, artifact), { recursive: true, force: true });
|
|
50
|
+
}
|
|
45
51
|
(0, lockfile_1.initLockfile)(projectDir, {
|
|
46
52
|
template,
|
|
47
53
|
packageManager,
|
package/dist/eject.js
CHANGED
|
@@ -21,7 +21,15 @@ function eject(projectRoot, paths) {
|
|
|
21
21
|
result.ejected.push(path);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
if (result.ejected.length)
|
|
24
|
+
if (result.ejected.length) {
|
|
25
25
|
(0, lockfile_1.writeFilesLock)(projectRoot, lock);
|
|
26
|
+
// Persist ejected paths into the manifest's ownedGlobs so they stay owned
|
|
27
|
+
// across the next add/update, which rebuilds every tier from ownedGlobs.
|
|
28
|
+
const manifest = (0, lockfile_1.readManifest)(projectRoot);
|
|
29
|
+
if (manifest) {
|
|
30
|
+
manifest.ownedGlobs = (0, lockfile_1.mergeOwnedGlobs)(manifest.ownedGlobs, result.ejected);
|
|
31
|
+
(0, lockfile_1.writeManifest)(projectRoot, manifest);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
26
34
|
return result;
|
|
27
35
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -26,6 +26,7 @@ Options:
|
|
|
26
26
|
--template <t> Template to scaffold (see below)
|
|
27
27
|
--dir <path> Target directory (default: ./<name>)
|
|
28
28
|
--pm <name> Package manager: npm | pnpm | yarn (default: npm)
|
|
29
|
+
--no-ai Skip AI agent guidance (AGENTS.md, CLAUDE.md, editor rules)
|
|
29
30
|
-y, --yes Skip prompts and accept defaults
|
|
30
31
|
-h, --help Show this help
|
|
31
32
|
|
|
@@ -38,7 +39,7 @@ Example:
|
|
|
38
39
|
cd my-app && npx @podosoft/podokit add auth
|
|
39
40
|
`;
|
|
40
41
|
function parseArgs(argv) {
|
|
41
|
-
const parsed = { help: false, yes: false, apply: false };
|
|
42
|
+
const parsed = { help: false, yes: false, apply: false, ai: true };
|
|
42
43
|
const positionals = [];
|
|
43
44
|
for (let i = 0; i < argv.length; i += 1) {
|
|
44
45
|
const arg = argv[i];
|
|
@@ -48,6 +49,9 @@ function parseArgs(argv) {
|
|
|
48
49
|
else if (arg === "-y" || arg === "--yes") {
|
|
49
50
|
parsed.yes = true;
|
|
50
51
|
}
|
|
52
|
+
else if (arg === "--no-ai") {
|
|
53
|
+
parsed.ai = false;
|
|
54
|
+
}
|
|
51
55
|
else if (arg === "--apply") {
|
|
52
56
|
parsed.apply = true;
|
|
53
57
|
}
|
|
@@ -85,7 +89,7 @@ async function main(argv) {
|
|
|
85
89
|
if (args.command === "add") {
|
|
86
90
|
const moduleName = args.name;
|
|
87
91
|
if (!moduleName) {
|
|
88
|
-
const available = (0, add_1.listModules)(modulesDir);
|
|
92
|
+
const available = (0, add_1.listModules)(modulesDir, process.cwd());
|
|
89
93
|
const list = available.length
|
|
90
94
|
? available.map((m) => ` ${m.name} ${m.description}`).join("\n")
|
|
91
95
|
: " (none available)";
|
|
@@ -242,6 +246,7 @@ async function main(argv) {
|
|
|
242
246
|
template: resolved.template,
|
|
243
247
|
targetDir: args.dir,
|
|
244
248
|
packageManager: resolved.packageManager,
|
|
249
|
+
ai: args.ai,
|
|
245
250
|
});
|
|
246
251
|
const relPath = (0, node_path_1.relative)(process.cwd(), result.projectDir) || ".";
|
|
247
252
|
const rel = relPath.startsWith("..") ? result.projectDir : relPath;
|
package/dist/lib.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { create } from "./create";
|
|
2
|
+
export type { CreateOptions, CreateResult } from "./create";
|
|
3
|
+
export { TEMPLATES, DEFAULT_TEMPLATE, TEMPLATE_NAMES, isKnownTemplate } from "./templates";
|
|
4
|
+
export type { TemplateInfo } from "./templates";
|
|
5
|
+
export { listModules, addModule } from "./add";
|
|
6
|
+
export type { ModuleManifest, AddOptions, AddResult } from "./add";
|
|
7
|
+
export { status, diff, doctor, SUPPORTED_FRAMEWORKS } from "./inspect";
|
|
8
|
+
export type { StatusReport, DoctorFinding } from "./inspect";
|
|
9
|
+
export { planUpdate, summarize } from "./update";
|
|
10
|
+
export type { UpdatePlan, FileChange } from "./update";
|
|
11
|
+
/** Absolute path to the templates bundled in this installed package. */
|
|
12
|
+
export declare function builtinTemplatesDir(): string;
|
|
13
|
+
/** Absolute path to the modules bundled in this installed package. */
|
|
14
|
+
export declare function builtinModulesDir(): string;
|
package/dist/lib.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.summarize = exports.planUpdate = exports.SUPPORTED_FRAMEWORKS = exports.doctor = exports.diff = exports.status = exports.addModule = exports.listModules = exports.isKnownTemplate = exports.TEMPLATE_NAMES = exports.DEFAULT_TEMPLATE = exports.TEMPLATES = exports.create = void 0;
|
|
4
|
+
exports.builtinTemplatesDir = builtinTemplatesDir;
|
|
5
|
+
exports.builtinModulesDir = builtinModulesDir;
|
|
6
|
+
// Library entry point: the reusable functions behind the `podo` CLI, for other
|
|
7
|
+
// packages (e.g. @podosoft/podokit-mcp) to call without spawning the binary.
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
var create_1 = require("./create");
|
|
10
|
+
Object.defineProperty(exports, "create", { enumerable: true, get: function () { return create_1.create; } });
|
|
11
|
+
var templates_1 = require("./templates");
|
|
12
|
+
Object.defineProperty(exports, "TEMPLATES", { enumerable: true, get: function () { return templates_1.TEMPLATES; } });
|
|
13
|
+
Object.defineProperty(exports, "DEFAULT_TEMPLATE", { enumerable: true, get: function () { return templates_1.DEFAULT_TEMPLATE; } });
|
|
14
|
+
Object.defineProperty(exports, "TEMPLATE_NAMES", { enumerable: true, get: function () { return templates_1.TEMPLATE_NAMES; } });
|
|
15
|
+
Object.defineProperty(exports, "isKnownTemplate", { enumerable: true, get: function () { return templates_1.isKnownTemplate; } });
|
|
16
|
+
var add_1 = require("./add");
|
|
17
|
+
Object.defineProperty(exports, "listModules", { enumerable: true, get: function () { return add_1.listModules; } });
|
|
18
|
+
Object.defineProperty(exports, "addModule", { enumerable: true, get: function () { return add_1.addModule; } });
|
|
19
|
+
var inspect_1 = require("./inspect");
|
|
20
|
+
Object.defineProperty(exports, "status", { enumerable: true, get: function () { return inspect_1.status; } });
|
|
21
|
+
Object.defineProperty(exports, "diff", { enumerable: true, get: function () { return inspect_1.diff; } });
|
|
22
|
+
Object.defineProperty(exports, "doctor", { enumerable: true, get: function () { return inspect_1.doctor; } });
|
|
23
|
+
Object.defineProperty(exports, "SUPPORTED_FRAMEWORKS", { enumerable: true, get: function () { return inspect_1.SUPPORTED_FRAMEWORKS; } });
|
|
24
|
+
var update_1 = require("./update");
|
|
25
|
+
Object.defineProperty(exports, "planUpdate", { enumerable: true, get: function () { return update_1.planUpdate; } });
|
|
26
|
+
Object.defineProperty(exports, "summarize", { enumerable: true, get: function () { return update_1.summarize; } });
|
|
27
|
+
/** Absolute path to the templates bundled in this installed package. */
|
|
28
|
+
function builtinTemplatesDir() {
|
|
29
|
+
return (0, node_path_1.join)(__dirname, "templates");
|
|
30
|
+
}
|
|
31
|
+
/** Absolute path to the modules bundled in this installed package. */
|
|
32
|
+
function builtinModulesDir() {
|
|
33
|
+
return (0, node_path_1.join)(__dirname, "templates", "modules");
|
|
34
|
+
}
|
package/dist/lockfile.d.ts
CHANGED
|
@@ -72,9 +72,14 @@ export interface InitLockOptions {
|
|
|
72
72
|
}
|
|
73
73
|
/** Write the initial `.podokit/` lockfile for a freshly created project. */
|
|
74
74
|
export declare function initLockfile(projectRoot: string, options: InitLockOptions): void;
|
|
75
|
+
/** Union two glob lists, preserving order and dropping duplicates. Used to fold
|
|
76
|
+
* module-declared and ejected owned paths into the project's `ownedGlobs`, which
|
|
77
|
+
* is the durable source of truth `computeFilesLock` re-reads on every recompute. */
|
|
78
|
+
export declare function mergeOwnedGlobs(existing: string[], add: string[]): string[];
|
|
75
79
|
/**
|
|
76
80
|
* Record applied modules in the manifest and recompute the files.lock. Modules
|
|
77
81
|
* already present are left in place (idempotent); the lock always reflects disk.
|
|
78
|
-
*
|
|
82
|
+
* `ownedGlobs` a module declares are merged into the manifest so its owned paths
|
|
83
|
+
* survive every future recompute. No-op on projects without a manifest.
|
|
79
84
|
*/
|
|
80
|
-
export declare function recordModules(projectRoot: string, moduleNames: string[], version?: string): void;
|
|
85
|
+
export declare function recordModules(projectRoot: string, moduleNames: string[], version?: string, ownedGlobs?: string[]): void;
|
package/dist/lockfile.js
CHANGED
|
@@ -14,6 +14,7 @@ exports.computeFilesLock = computeFilesLock;
|
|
|
14
14
|
exports.readFilesLock = readFilesLock;
|
|
15
15
|
exports.computeDrift = computeDrift;
|
|
16
16
|
exports.initLockfile = initLockfile;
|
|
17
|
+
exports.mergeOwnedGlobs = mergeOwnedGlobs;
|
|
17
18
|
exports.recordModules = recordModules;
|
|
18
19
|
const node_fs_1 = require("node:fs");
|
|
19
20
|
const node_path_1 = require("node:path");
|
|
@@ -44,6 +45,23 @@ const WALK_IGNORE = new Set([
|
|
|
44
45
|
exports.DEFAULT_OWNED_GLOBS = [
|
|
45
46
|
"apps/web/src/routes/**",
|
|
46
47
|
"apps/web/src/lib/components/ui/**",
|
|
48
|
+
// Owned Nest DI extension slot — override providers here; update never touches it.
|
|
49
|
+
"apps/api/src/app.extensions.ts",
|
|
50
|
+
// AI agent guidance — yours to customize; update never touches it.
|
|
51
|
+
"AGENTS.md",
|
|
52
|
+
"CLAUDE.md",
|
|
53
|
+
".claude/**",
|
|
54
|
+
".cursor/**",
|
|
55
|
+
".github/copilot-instructions.md",
|
|
56
|
+
".mcp.json",
|
|
57
|
+
// Containerized dev-environment scaffolding: users tweak ports/services, so
|
|
58
|
+
// updates never overwrite these.
|
|
59
|
+
"compose.dev.yaml",
|
|
60
|
+
"Dockerfile.dev",
|
|
61
|
+
".dockerignore",
|
|
62
|
+
".devcontainer/**",
|
|
63
|
+
".env.docker",
|
|
64
|
+
"infra/traefik/**",
|
|
47
65
|
];
|
|
48
66
|
const TEXT_EXT = /\.(json|md|ts|tsx|js|mjs|cjs|css|html|yml|yaml|txt|env|svelte|gitignore|example)$/i;
|
|
49
67
|
function podokitDir(projectRoot) {
|
|
@@ -171,12 +189,27 @@ function initLockfile(projectRoot, options) {
|
|
|
171
189
|
writeManifest(projectRoot, manifest);
|
|
172
190
|
writeFilesLock(projectRoot, computeFilesLock(projectRoot, ownedGlobs));
|
|
173
191
|
}
|
|
192
|
+
/** Union two glob lists, preserving order and dropping duplicates. Used to fold
|
|
193
|
+
* module-declared and ejected owned paths into the project's `ownedGlobs`, which
|
|
194
|
+
* is the durable source of truth `computeFilesLock` re-reads on every recompute. */
|
|
195
|
+
function mergeOwnedGlobs(existing, add) {
|
|
196
|
+
const seen = new Set(existing);
|
|
197
|
+
const out = [...existing];
|
|
198
|
+
for (const glob of add) {
|
|
199
|
+
if (seen.has(glob))
|
|
200
|
+
continue;
|
|
201
|
+
seen.add(glob);
|
|
202
|
+
out.push(glob);
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
174
206
|
/**
|
|
175
207
|
* Record applied modules in the manifest and recompute the files.lock. Modules
|
|
176
208
|
* already present are left in place (idempotent); the lock always reflects disk.
|
|
177
|
-
*
|
|
209
|
+
* `ownedGlobs` a module declares are merged into the manifest so its owned paths
|
|
210
|
+
* survive every future recompute. No-op on projects without a manifest.
|
|
178
211
|
*/
|
|
179
|
-
function recordModules(projectRoot, moduleNames, version) {
|
|
212
|
+
function recordModules(projectRoot, moduleNames, version, ownedGlobs) {
|
|
180
213
|
const manifest = readManifest(projectRoot);
|
|
181
214
|
if (!manifest)
|
|
182
215
|
return;
|
|
@@ -188,6 +221,8 @@ function recordModules(projectRoot, moduleNames, version) {
|
|
|
188
221
|
manifest.modules.push({ name, order: manifest.modules.length, addedWith: stampedWith });
|
|
189
222
|
known.add(name);
|
|
190
223
|
}
|
|
224
|
+
if (ownedGlobs?.length)
|
|
225
|
+
manifest.ownedGlobs = mergeOwnedGlobs(manifest.ownedGlobs, ownedGlobs);
|
|
191
226
|
writeManifest(projectRoot, manifest);
|
|
192
227
|
writeFilesLock(projectRoot, computeFilesLock(projectRoot, manifest.ownedGlobs));
|
|
193
228
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# AGENTS.md — {{projectName}}
|
|
2
|
+
|
|
3
|
+
Guidance for AI coding agents (Claude Code, Codex, Cursor, Copilot, …). Generated
|
|
4
|
+
by [PodoKit](https://github.com/podosoft-dev/podokit).
|
|
5
|
+
|
|
6
|
+
## Project overview
|
|
7
|
+
|
|
8
|
+
A minimal npm-workspace starter. Build it up from here — add features with the
|
|
9
|
+
`podo` CLI (`podo add <module>`; run `podo add` with no argument to list them).
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install
|
|
15
|
+
npm run dev # runs workspace dev scripts
|
|
16
|
+
npm run build
|
|
17
|
+
npm run lint # type-check
|
|
18
|
+
npm test
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Code style
|
|
22
|
+
|
|
23
|
+
- TypeScript `strict`. **No `any`** (use `unknown` + narrowing), no `@ts-ignore`.
|
|
24
|
+
- Explicit function return types. Conventional Commits, imperative mood, no emojis.
|
|
25
|
+
|
|
26
|
+
## PodoKit tooling
|
|
27
|
+
|
|
28
|
+
This project is managed by the `podo` CLI; `.podokit/` records how it was
|
|
29
|
+
assembled (do not edit by hand). Use `podo status`/`podo diff` to see your local
|
|
30
|
+
edits and `podo update` to pull in improvements without losing your work.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# AGENTS.md — {{projectName}}
|
|
2
|
+
|
|
3
|
+
Guidance for AI coding agents (Claude Code, Codex, Cursor, Copilot, …) working in
|
|
4
|
+
this repository. It was generated by [PodoKit](https://github.com/podosoft-dev/podokit).
|
|
5
|
+
Follow it so your changes match the project's conventions.
|
|
6
|
+
|
|
7
|
+
## Project overview
|
|
8
|
+
|
|
9
|
+
A full-stack TypeScript monorepo (npm workspaces):
|
|
10
|
+
|
|
11
|
+
- `apps/api` — **NestJS** backend (`{{projectName}}-api`), TypeORM + PostgreSQL, Swagger at `/api-docs`.
|
|
12
|
+
- `apps/web` — **SvelteKit** frontend (`{{projectName}}-web`), Tailwind v4 + shadcn-svelte + typesafe-i18n.
|
|
13
|
+
- `infra/` — Docker Compose (PostgreSQL/Redis) and example k3s manifests.
|
|
14
|
+
- `.podokit/` — PodoKit's generation lockfile (see "PodoKit tooling" below). Do not edit by hand.
|
|
15
|
+
|
|
16
|
+
## Setup & commands
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install
|
|
20
|
+
cp .env.example .env
|
|
21
|
+
docker compose -f infra/docker/docker-compose.yml up -d # PostgreSQL (+ Redis)
|
|
22
|
+
npm run dev # api on http://localhost:5002, web on http://localhost:5001
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- **Ports: web = 5001, api = 5002.** The web app reaches the API only through its
|
|
26
|
+
SvelteKit server proxy (`/api/*`), never with a direct browser `fetch`.
|
|
27
|
+
- Build: `npm run build` · Type-check/lint: `npm run lint` · Unit tests: `npm test`
|
|
28
|
+
- Per workspace: `npm run build -w {{projectName}}-api` / `-w {{projectName}}-web`.
|
|
29
|
+
- DB migrations (TypeORM): `npm run migration:run -w {{projectName}}-api`.
|
|
30
|
+
- e2e (Playwright, ships in `tests/`): `npm run test:e2e`.
|
|
31
|
+
|
|
32
|
+
**Always** verify a change by building the affected workspace (and `svelte-check`
|
|
33
|
+
for web) before considering it done. UI changes need a Playwright test; API
|
|
34
|
+
changes need an e2e/unit test.
|
|
35
|
+
|
|
36
|
+
## Code style
|
|
37
|
+
|
|
38
|
+
### TypeScript (both apps)
|
|
39
|
+
- `strict` mode. **No `any`** — use `unknown` + narrowing. **No `@ts-ignore`/`@ts-expect-error`** and no `eslint-disable`.
|
|
40
|
+
- Explicit function return types. DTOs use `class-validator` decorators.
|
|
41
|
+
- Keep `noUnusedLocals`, `noUnusedParameters`, `noImplicitReturns`, `noUncheckedIndexedAccess`.
|
|
42
|
+
|
|
43
|
+
### Svelte 5 (web)
|
|
44
|
+
- **Runes only**: `$state()`, `$derived()`, `$effect()`, `$props()`. **Never** use `$:` (deprecated).
|
|
45
|
+
- Reactive stores go in `.svelte.ts` files; don't destructure a getter-exported rune (it loses reactivity) — wrap in `$derived`.
|
|
46
|
+
|
|
47
|
+
### UI — Tailwind + shadcn-svelte (web)
|
|
48
|
+
- Prefer **shadcn-svelte components** over raw HTML (`<button>`, `<input>`, `<select>`, …).
|
|
49
|
+
- **Do not edit `src/lib/components/ui/**`** (the shadcn primitives). Customize in a separate wrapper.
|
|
50
|
+
- Tailwind v4 (CSS-first, no `tailwind.config`). Use semantic classes: `bg-background`, `text-foreground`, `border-border`. Dark mode via `mode-watcher`. Responsive (`sm:`/`md:`/`lg:`).
|
|
51
|
+
|
|
52
|
+
### i18n & errors
|
|
53
|
+
- **Every user-facing string goes through i18n** (`src/lib/i18n/messages.ts`) — no hardcoded text. Add keys to **all** locales at once.
|
|
54
|
+
- The backend returns a stable **error envelope**: `{ success: false, error: { code, message, statusCode, path, timestamp, details? } }`. Throw `AppException("SOME_CODE", ...)` on the backend; **branch on `error.code`, not the message,** on the frontend.
|
|
55
|
+
- Logging is structured; no emojis/icons. Start messages with an action verb.
|
|
56
|
+
|
|
57
|
+
## PodoKit tooling & file ownership
|
|
58
|
+
|
|
59
|
+
This project is managed by the `podo` CLI. Files have an ownership tier recorded
|
|
60
|
+
in `.podokit/files.lock` — respect it:
|
|
61
|
+
|
|
62
|
+
- **owned** (your code: `apps/web/src/routes/**`, your components/services, `apps/api/src/app.extensions.ts`, and this `AGENTS.md`) — edit freely. To add or override Nest providers/modules, use `app.extensions.ts` (spread into `AppModule` after the module-wired providers, so a same-token override wins) rather than editing `app.module.ts`.
|
|
63
|
+
- **assembled** (e.g. `apps/api/src/app.module.ts`, `auth.ts`) — contain `// podokit:begin:… / …:end` fenced regions that the toolkit recomputes. Edit outside the fences only; **never hand-edit inside the fences**.
|
|
64
|
+
- **managed** (bootstrap/config like `main.ts`) — the toolkit may update these; avoid gratuitous edits.
|
|
65
|
+
|
|
66
|
+
Useful commands:
|
|
67
|
+
|
|
68
|
+
- `podo add <module>` — add a feature (auth, admin-dashboard, redis, bullmq, …). It wires itself into the app; run `podo add` with no argument to list modules.
|
|
69
|
+
- `podo status` / `podo diff` — see your local edits vs. what PodoKit generated.
|
|
70
|
+
- `podo update` — pull in template/module improvements (3-way merges your edits; never touches owned files).
|
|
71
|
+
|
|
72
|
+
## Commit conventions
|
|
73
|
+
|
|
74
|
+
Conventional Commits (`feat|fix|docs|refactor|test|chore`), imperative mood,
|
|
75
|
+
≤50-char subject, no trailing period, no emojis. One logical change per commit.
|
|
76
|
+
|
|
77
|
+
<!-- podokit:begin:agents-modules -->
|
|
78
|
+
<!-- Module-specific guidance is injected here by `podo add`. -->
|
|
79
|
+
<!-- podokit:end:agents-modules -->
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Development image for the PodoKit monorepo (web + api).
|
|
2
|
+
# Dependencies and source are baked in; `docker compose watch` syncs your
|
|
3
|
+
# edits into the running container so the native file watchers fire.
|
|
4
|
+
FROM node:22-bookworm-slim
|
|
5
|
+
|
|
6
|
+
# git for the toolchain (editors, AI agents); build tools for native modules.
|
|
7
|
+
RUN apt-get update \
|
|
8
|
+
&& apt-get install -y --no-install-recommends git python3 make g++ ca-certificates \
|
|
9
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
10
|
+
|
|
11
|
+
WORKDIR /app
|
|
12
|
+
|
|
13
|
+
# Install workspace dependencies (cached unless a package.json changes).
|
|
14
|
+
# NB: the host package-lock.json is intentionally NOT copied — a lockfile
|
|
15
|
+
# generated on macOS/Windows prunes platform-specific optional deps and can drop
|
|
16
|
+
# packages (e.g. @swc/cli) on linux. Resolving fresh in-container avoids that.
|
|
17
|
+
COPY package.json ./
|
|
18
|
+
COPY apps/api/package.json ./apps/api/package.json
|
|
19
|
+
COPY apps/web/package.json ./apps/web/package.json
|
|
20
|
+
RUN npm install
|
|
21
|
+
|
|
22
|
+
# Baked source (kept in sync during development by `docker compose watch`).
|
|
23
|
+
COPY . .
|
|
24
|
+
|
|
25
|
+
EXPOSE 5001 5002
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"version": "0.0.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"scripts": {
|
|
6
|
-
"dev": "nest start --watch",
|
|
6
|
+
"dev": "nest start --watch --builder swc",
|
|
7
7
|
"build": "nest build",
|
|
8
8
|
"start": "node dist/main",
|
|
9
9
|
"lint": "tsc -p tsconfig.json --noEmit",
|
|
@@ -40,7 +40,9 @@
|
|
|
40
40
|
"supertest": "^7.0.0",
|
|
41
41
|
"ts-jest": "^29.2.5",
|
|
42
42
|
"ts-node": "^10.9.2",
|
|
43
|
-
"typescript": "^6.0.3"
|
|
43
|
+
"typescript": "^6.0.3",
|
|
44
|
+
"@swc/core": "^1.7.0",
|
|
45
|
+
"@swc/cli": "^0.4.0"
|
|
44
46
|
},
|
|
45
47
|
"jest": {
|
|
46
48
|
"moduleFileExtensions": [
|