@podosoft/podokit 0.5.0 → 0.6.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 +37 -0
- package/dist/add.d.ts +2 -0
- package/dist/add.js +6 -1
- package/dist/assemble.js +4 -1
- package/dist/create.d.ts +2 -0
- package/dist/create.js +6 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -1
- package/dist/lib.d.ts +14 -0
- package/dist/lib.js +34 -0
- package/dist/lockfile.js +15 -0
- 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/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/components/app-sidebar.svelte +13 -0
- 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 +46 -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 +28 -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 +24 -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/organizations.ui.spec.ts +8 -5
- package/dist/templates/modules/admin-dashboard/files/tests/ui/settings.ui.spec.ts +42 -1
- package/dist/templates/modules/admin-dashboard/module.manifest.json +20 -2
- package/dist/templates/modules/auth/files/apps/api/src/auth/feature-gate.ts +27 -0
- package/dist/templates/modules/auth/module.manifest.json +6 -0
- 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/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/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
|
@@ -23,6 +23,18 @@ The `todo` template (`--template todo`) generates a working todo app (SvelteKit
|
|
|
23
23
|
|
|
24
24
|

|
|
25
25
|
|
|
26
|
+
## Commands
|
|
27
|
+
|
|
28
|
+
| Command | What it does |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `podo create <name>` | Scaffold a new project from a template |
|
|
31
|
+
| `podo add <module>` | Add a feature module (auth, admin-dashboard, redis, …) to a project |
|
|
32
|
+
| `podo status` | Show the PodoKit version, modules, file tiers, and local edits |
|
|
33
|
+
| `podo diff` | List the PodoKit-managed files you've edited since generation |
|
|
34
|
+
| `podo doctor` | Check framework versions against the supported ranges |
|
|
35
|
+
| `podo update [--apply]` | Preview (or apply) what a version update would change |
|
|
36
|
+
| `podo eject <path…>` | Take ownership of a managed file so updates skip it |
|
|
37
|
+
|
|
26
38
|
## Usage
|
|
27
39
|
|
|
28
40
|
```
|
|
@@ -35,10 +47,16 @@ Options:
|
|
|
35
47
|
- base : minimal npm workspace
|
|
36
48
|
--dir <path> Target directory (default: ./<name>)
|
|
37
49
|
--pm <name> Package manager: npm | pnpm | yarn (default: npm)
|
|
50
|
+
--no-ai Skip AI agent guidance (AGENTS.md, CLAUDE.md, editor rules)
|
|
38
51
|
-y, --yes Skip prompts and accept defaults
|
|
39
52
|
-h, --help Show help
|
|
40
53
|
```
|
|
41
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
|
+
|
|
42
60
|
Examples:
|
|
43
61
|
|
|
44
62
|
```bash
|
|
@@ -61,6 +79,25 @@ npx @podosoft/podokit add auth # full auth (better-auth): email/password, s
|
|
|
61
79
|
|
|
62
80
|
`podo add <module>` overlays files, merges dependencies, appends env vars, and wires the module into the NestJS app. Run `podo add` with no argument to list available modules.
|
|
63
81
|
|
|
82
|
+
The **`admin-dashboard`** module adds a ready-made admin console on top of `auth` — login/signup pages, user & session management, an audit log, and a Settings page where OAuth providers, SMTP, and server toggles are configured at runtime (stored encrypted in the DB, applied without a restart):
|
|
83
|
+
|
|
84
|
+

|
|
85
|
+
|
|
86
|
+
## Keep your project up to date
|
|
87
|
+
|
|
88
|
+
Every generated project records how it was assembled in a committed `.podokit/`
|
|
89
|
+
directory, so it can receive template and module improvements later without
|
|
90
|
+
losing your work:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
podo status # version, modules, and which managed files you've edited
|
|
94
|
+
podo update # preview what a newer PodoKit version would change
|
|
95
|
+
podo update --apply # apply it: clean updates are written, your edits are 3-way merged
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Files you own (routes, your components, shadcn UI) are never touched. See the
|
|
99
|
+
[updating guide](https://github.com/podosoft-dev/podokit/blob/main/docs/updating.md).
|
|
100
|
+
|
|
64
101
|
## What you get (`fullstack-nest-svelte`)
|
|
65
102
|
|
|
66
103
|
```
|
package/dist/add.d.ts
CHANGED
package/dist/add.js
CHANGED
|
@@ -109,9 +109,14 @@ function applyModule(projectRoot, module, modulesDir, applied) {
|
|
|
109
109
|
for (const injection of manifest.inject ?? []) {
|
|
110
110
|
const target = (0, node_path_1.join)(projectRoot, injection.file);
|
|
111
111
|
if (!(0, node_fs_1.existsSync)(target)) {
|
|
112
|
+
if (injection.optional)
|
|
113
|
+
continue;
|
|
112
114
|
throw new Error(`Cannot wire module: ${injection.file} not found.`);
|
|
113
115
|
}
|
|
114
|
-
|
|
116
|
+
const content = (0, node_fs_1.readFileSync)(target, "utf8");
|
|
117
|
+
if (injection.optional && !content.includes(injection.marker))
|
|
118
|
+
continue;
|
|
119
|
+
(0, node_fs_1.writeFileSync)(target, (0, podokit_template_engine_1.insertAtMarker)(content, injection.marker, injection.text));
|
|
115
120
|
}
|
|
116
121
|
const instructions = (manifest.instructions ?? []).map((line) => line.replace(/<app>/g, appName));
|
|
117
122
|
return { module, instructions, added };
|
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/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
|
}
|
|
@@ -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.js
CHANGED
|
@@ -44,6 +44,21 @@ const WALK_IGNORE = new Set([
|
|
|
44
44
|
exports.DEFAULT_OWNED_GLOBS = [
|
|
45
45
|
"apps/web/src/routes/**",
|
|
46
46
|
"apps/web/src/lib/components/ui/**",
|
|
47
|
+
// AI agent guidance — yours to customize; update never touches it.
|
|
48
|
+
"AGENTS.md",
|
|
49
|
+
"CLAUDE.md",
|
|
50
|
+
".claude/**",
|
|
51
|
+
".cursor/**",
|
|
52
|
+
".github/copilot-instructions.md",
|
|
53
|
+
".mcp.json",
|
|
54
|
+
// Containerized dev-environment scaffolding: users tweak ports/services, so
|
|
55
|
+
// updates never overwrite these.
|
|
56
|
+
"compose.dev.yaml",
|
|
57
|
+
"Dockerfile.dev",
|
|
58
|
+
".dockerignore",
|
|
59
|
+
".devcontainer/**",
|
|
60
|
+
".env.docker",
|
|
61
|
+
"infra/traefik/**",
|
|
47
62
|
];
|
|
48
63
|
const TEXT_EXT = /\.(json|md|ts|tsx|js|mjs|cjs|css|html|yml|yaml|txt|env|svelte|gitignore|example)$/i;
|
|
49
64
|
function podokitDir(projectRoot) {
|
|
@@ -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, and this `AGENTS.md`) — edit freely.
|
|
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": [
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>{{projectName}}</title>
|
|
7
|
+
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
|
6
8
|
%sveltekit.head%
|
|
7
9
|
</head>
|
|
8
10
|
<body data-sveltekit-preload-data="hover" class="h-full">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Root from "./tabs.svelte";
|
|
2
2
|
import Content from "./tabs-content.svelte";
|
|
3
|
-
import List
|
|
3
|
+
import List from "./tabs-list.svelte";
|
|
4
4
|
import Trigger from "./tabs-trigger.svelte";
|
|
5
5
|
|
|
6
6
|
export {
|
|
@@ -8,8 +8,6 @@ export {
|
|
|
8
8
|
Content,
|
|
9
9
|
List,
|
|
10
10
|
Trigger,
|
|
11
|
-
tabsListVariants,
|
|
12
|
-
type TabsListVariant,
|
|
13
11
|
//
|
|
14
12
|
Root as Tabs,
|
|
15
13
|
Content as TabsContent,
|
package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs-list.svelte
CHANGED
|
@@ -1,40 +1,20 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
import { tv, type VariantProps } from "tailwind-variants";
|
|
3
|
-
|
|
4
|
-
export const tabsListVariants = tv({
|
|
5
|
-
base: "rounded-lg p-[3px] group-data-horizontal/tabs:h-8 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col",
|
|
6
|
-
variants: {
|
|
7
|
-
variant: {
|
|
8
|
-
default: "cn-tabs-list-variant-default bg-muted",
|
|
9
|
-
line: "cn-tabs-list-variant-line gap-1 bg-transparent",
|
|
10
|
-
},
|
|
11
|
-
},
|
|
12
|
-
defaultVariants: {
|
|
13
|
-
variant: "default",
|
|
14
|
-
},
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
export type TabsListVariant = VariantProps<typeof tabsListVariants>["variant"];
|
|
18
|
-
</script>
|
|
19
|
-
|
|
20
1
|
<script lang="ts">
|
|
21
2
|
import { Tabs as TabsPrimitive } from "bits-ui";
|
|
22
3
|
import { cn } from "$lib/utils.js";
|
|
23
4
|
|
|
24
5
|
let {
|
|
25
6
|
ref = $bindable(null),
|
|
26
|
-
variant = "default",
|
|
27
7
|
class: className,
|
|
28
8
|
...restProps
|
|
29
|
-
}: TabsPrimitive.ListProps
|
|
30
|
-
variant?: TabsListVariant;
|
|
31
|
-
} = $props();
|
|
9
|
+
}: TabsPrimitive.ListProps = $props();
|
|
32
10
|
</script>
|
|
33
11
|
|
|
34
12
|
<TabsPrimitive.List
|
|
35
13
|
bind:ref
|
|
36
14
|
data-slot="tabs-list"
|
|
37
|
-
|
|
38
|
-
|
|
15
|
+
class={cn(
|
|
16
|
+
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
39
19
|
{...restProps}
|
|
40
20
|
/>
|
package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/tabs/tabs-trigger.svelte
CHANGED
|
@@ -13,10 +13,7 @@
|
|
|
13
13
|
bind:ref
|
|
14
14
|
data-slot="tabs-trigger"
|
|
15
15
|
class={cn(
|
|
16
|
-
"
|
|
17
|
-
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
|
|
18
|
-
"data-active:bg-background dark:data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 data-active:text-foreground",
|
|
19
|
-
"after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
|
|
16
|
+
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
20
17
|
className
|
|
21
18
|
)}
|
|
22
19
|
{...restProps}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="PodoKit">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="pk" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#8b5cf6"/>
|
|
5
|
+
<stop offset="1" stop-color="#4f46e5"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect width="64" height="64" rx="15" fill="url(#pk)"/>
|
|
9
|
+
<!-- layered stack: full-stack toolkit -->
|
|
10
|
+
<path d="M32 14 50 25 32 36 14 25Z" fill="#fff" fill-opacity="0.96"/>
|
|
11
|
+
<path d="M16 31 32 40.5 48 31" fill="none" stroke="#fff" stroke-opacity="0.78" stroke-width="3.4" stroke-linecap="round" stroke-linejoin="round"/>
|
|
12
|
+
<path d="M16 38 32 47.5 48 38" fill="none" stroke="#fff" stroke-opacity="0.5" stroke-width="3.4" stroke-linecap="round" stroke-linejoin="round"/>
|
|
13
|
+
</svg>
|
|
@@ -2,6 +2,18 @@ import { sveltekit } from "@sveltejs/kit/vite";
|
|
|
2
2
|
import tailwindcss from "@tailwindcss/vite";
|
|
3
3
|
import { defineConfig } from "vite";
|
|
4
4
|
|
|
5
|
+
// Inside the dev container (compose.dev.yaml sets VITE_DOCKER=1): bind to all
|
|
6
|
+
// interfaces, allow the Traefik hostname, and route HMR through Traefik on :80.
|
|
7
|
+
const inDocker = process.env.VITE_DOCKER === "1";
|
|
8
|
+
|
|
5
9
|
export default defineConfig({
|
|
6
10
|
plugins: [tailwindcss(), sveltekit()],
|
|
11
|
+
server: inDocker
|
|
12
|
+
? {
|
|
13
|
+
host: true,
|
|
14
|
+
port: 5001,
|
|
15
|
+
allowedHosts: true,
|
|
16
|
+
hmr: { clientPort: 80 },
|
|
17
|
+
}
|
|
18
|
+
: undefined,
|
|
7
19
|
});
|