@atlashub/smartstack-cli 1.36.0 → 2.0.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.
Files changed (103) hide show
  1. package/config/mcp-defaults.json +62 -0
  2. package/dist/index.js +57 -4
  3. package/dist/index.js.map +1 -1
  4. package/dist/mcp-entry.mjs +16984 -0
  5. package/dist/mcp-entry.mjs.map +1 -0
  6. package/package.json +14 -5
  7. package/templates/agents/gitflow/start.md +5 -4
  8. package/templates/agents/mcp-healthcheck.md +15 -13
  9. package/templates/mcp-scaffolding/component.tsx.hbs +298 -0
  10. package/templates/mcp-scaffolding/controller.cs.hbs +184 -0
  11. package/templates/mcp-scaffolding/entity-extension.cs.hbs +231 -0
  12. package/templates/mcp-scaffolding/frontend/api-client.ts.hbs +116 -0
  13. package/templates/mcp-scaffolding/frontend/nav-routes.ts.hbs +133 -0
  14. package/templates/mcp-scaffolding/frontend/routes.tsx.hbs +134 -0
  15. package/templates/mcp-scaffolding/migrations/seed-roles.cs.hbs +261 -0
  16. package/templates/mcp-scaffolding/service-extension.cs.hbs +53 -0
  17. package/templates/mcp-scaffolding/tests/controller.test.cs.hbs +413 -0
  18. package/templates/mcp-scaffolding/tests/entity.test.cs.hbs +239 -0
  19. package/templates/mcp-scaffolding/tests/repository.test.cs.hbs +441 -0
  20. package/templates/mcp-scaffolding/tests/security.test.cs.hbs +442 -0
  21. package/templates/mcp-scaffolding/tests/service.test.cs.hbs +390 -0
  22. package/templates/mcp-scaffolding/tests/validator.test.cs.hbs +428 -0
  23. package/templates/ralph/README.md +3 -3
  24. package/templates/ralph/ralph.config.yaml +2 -2
  25. package/templates/skills/admin/SKILL.md +42 -0
  26. package/templates/skills/application/steps/step-01-navigation.md +226 -43
  27. package/templates/skills/application/steps/step-03-roles.md +160 -38
  28. package/templates/skills/application/steps/step-04-backend.md +109 -2
  29. package/templates/skills/application/templates-seed.md +200 -1
  30. package/templates/skills/business-analyse/_shared.md +24 -1
  31. package/templates/skills/business-analyse/questionnaire/01-context.md +4 -4
  32. package/templates/skills/business-analyse/questionnaire/02-stakeholders.md +3 -3
  33. package/templates/skills/business-analyse/questionnaire/03-scope.md +4 -4
  34. package/templates/skills/business-analyse/questionnaire/04-data.md +7 -7
  35. package/templates/skills/business-analyse/questionnaire/05-integrations.md +1 -1
  36. package/templates/skills/business-analyse/questionnaire/06-security.md +3 -3
  37. package/templates/skills/business-analyse/questionnaire/07-ui.md +1 -1
  38. package/templates/skills/business-analyse/questionnaire/08-performance.md +3 -3
  39. package/templates/skills/business-analyse/questionnaire/09-constraints.md +4 -4
  40. package/templates/skills/business-analyse/questionnaire/10-documentation.md +2 -2
  41. package/templates/skills/business-analyse/questionnaire/11-data-lifecycle.md +2 -2
  42. package/templates/skills/business-analyse/questionnaire/12-migration.md +1 -1
  43. package/templates/skills/business-analyse/questionnaire/13-cross-module.md +2 -2
  44. package/templates/skills/business-analyse/steps/step-01-discover.md +50 -25
  45. package/templates/skills/business-analyse/steps/step-05-handoff.md +133 -34
  46. package/templates/skills/cc-agent/SKILL.md +129 -0
  47. package/templates/skills/cc-agent/references/agent-frontmatter.md +213 -0
  48. package/templates/skills/cc-agent/references/permission-modes.md +102 -0
  49. package/templates/skills/cc-agent/references/tools-reference.md +144 -0
  50. package/templates/skills/cc-agent/steps/step-00-init.md +134 -0
  51. package/templates/skills/cc-agent/steps/step-01-design.md +186 -0
  52. package/templates/skills/cc-agent/steps/step-02-generate.md +204 -0
  53. package/templates/skills/cc-agent/steps/step-03-validate.md +130 -0
  54. package/templates/skills/cc-agent/templates/agent-categorized.md +67 -0
  55. package/templates/skills/cc-agent/templates/agent-standalone.md +56 -0
  56. package/templates/skills/cc-agent/templates/agent-with-skills.md +94 -0
  57. package/templates/skills/cc-audit/SKILL.md +108 -0
  58. package/templates/skills/cc-audit/references/agent-checklist.md +91 -0
  59. package/templates/skills/cc-audit/references/hook-checklist.md +110 -0
  60. package/templates/skills/cc-audit/references/skill-checklist.md +70 -0
  61. package/templates/skills/cc-audit/steps/step-00-init.md +98 -0
  62. package/templates/skills/cc-audit/steps/step-01-scan.md +142 -0
  63. package/templates/skills/cc-audit/steps/step-02-analyze.md +158 -0
  64. package/templates/skills/cc-audit/steps/step-03-report.md +142 -0
  65. package/templates/skills/cc-skill/SKILL.md +134 -0
  66. package/templates/skills/cc-skill/references/best-practices.md +167 -0
  67. package/templates/skills/cc-skill/references/frontmatter-reference.md +182 -0
  68. package/templates/skills/cc-skill/references/skill-patterns.md +199 -0
  69. package/templates/skills/cc-skill/steps/step-00-init.md +119 -0
  70. package/templates/skills/cc-skill/steps/step-01-design.md +199 -0
  71. package/templates/skills/cc-skill/steps/step-02-generate.md +145 -0
  72. package/templates/skills/cc-skill/steps/step-03-steps.md +151 -0
  73. package/templates/skills/cc-skill/steps/step-04-validate.md +124 -0
  74. package/templates/skills/cc-skill/templates/skill-forked.md +85 -0
  75. package/templates/skills/cc-skill/templates/skill-progressive.md +102 -0
  76. package/templates/skills/cc-skill/templates/skill-simple.md +75 -0
  77. package/templates/skills/cc-skill/templates/step-template.md +82 -0
  78. package/templates/skills/check-version/SKILL.md +6 -0
  79. package/templates/skills/debug/SKILL.md +4 -0
  80. package/templates/skills/documentation/SKILL.md +1 -0
  81. package/templates/skills/efcore/SKILL.md +5 -0
  82. package/templates/skills/efcore/steps/db/step-deploy.md +26 -5
  83. package/templates/skills/efcore/steps/shared/step-00-init.md +21 -7
  84. package/templates/skills/explore/SKILL.md +28 -32
  85. package/templates/skills/feature-full/SKILL.md +1 -0
  86. package/templates/skills/gitflow/SKILL.md +8 -0
  87. package/templates/skills/gitflow/steps/step-start.md +45 -10
  88. package/templates/skills/mcp/SKILL.md +38 -18
  89. package/templates/skills/quick-search/SKILL.md +8 -1
  90. package/templates/skills/ralph-loop/SKILL.md +1 -1
  91. package/templates/skills/ralph-loop/steps/step-00-init.md +8 -68
  92. package/templates/skills/ralph-loop/steps/step-04-check.md +1 -1
  93. package/templates/skills/refactor/SKILL.md +1 -0
  94. package/templates/skills/review-code/SKILL.md +7 -1
  95. package/templates/skills/ui-components/SKILL.md +31 -438
  96. package/templates/skills/ui-components/accessibility.md +170 -0
  97. package/templates/skills/ui-components/patterns/data-table.md +39 -0
  98. package/templates/skills/ui-components/patterns/entity-card.md +77 -0
  99. package/templates/skills/ui-components/patterns/grid-layout.md +91 -0
  100. package/templates/skills/ui-components/patterns/kanban.md +43 -0
  101. package/templates/skills/ui-components/style-guide.md +86 -0
  102. package/templates/skills/utils/SKILL.md +1 -0
  103. package/templates/skills/validate/SKILL.md +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlashub/smartstack-cli",
3
- "version": "1.36.0",
3
+ "version": "2.0.0",
4
4
  "description": "SmartStack Claude Code automation toolkit - GitFlow, APEX, EF Core migrations, prompts and more",
5
5
  "author": {
6
6
  "name": "SmartStack",
@@ -14,7 +14,8 @@
14
14
  "types": "./dist/index.d.ts",
15
15
  "bin": {
16
16
  "smartstack": "./dist/index.js",
17
- "ss": "./dist/index.js"
17
+ "ss": "./dist/index.js",
18
+ "smartstack-mcp": "./dist/mcp-entry.mjs"
18
19
  },
19
20
  "files": [
20
21
  "dist",
@@ -69,7 +70,10 @@
69
70
  "ralph:start": "node dist/index.js ralph start",
70
71
  "ralph:status": "node dist/index.js ralph status",
71
72
  "ralph:logs": "node dist/index.js ralph logs",
72
- "health": "node dist/index.js doctor --json"
73
+ "health": "node dist/index.js doctor --json",
74
+ "test:mcp": "vitest run --config vitest.mcp.config.ts",
75
+ "test:mcp:watch": "vitest --config vitest.mcp.config.ts",
76
+ "test:mcp:coverage": "vitest run --config vitest.mcp.config.ts --coverage"
73
77
  },
74
78
  "dependencies": {
75
79
  "bcryptjs": "^2.4.3",
@@ -82,7 +86,10 @@
82
86
  "jsonwebtoken": "^9.0.3",
83
87
  "mssql": "^11.0.1",
84
88
  "ora": "^8.0.1",
85
- "zod": "^3.22.4"
89
+ "zod": "^3.25.0",
90
+ "@modelcontextprotocol/sdk": "^1.0.0",
91
+ "handlebars": "^4.7.8",
92
+ "axios": "^1.7.0"
86
93
  },
87
94
  "devDependencies": {
88
95
  "@types/bcryptjs": "^2.4.6",
@@ -96,6 +103,8 @@
96
103
  "eslint": "^8.56.0",
97
104
  "prettier": "^3.2.4",
98
105
  "tsup": "^8.0.1",
99
- "typescript": "^5.3.3"
106
+ "typescript": "^5.3.3",
107
+ "vitest": "^2.1.0",
108
+ "@vitest/coverage-v8": "^2.1.0"
100
109
  }
101
110
  }
@@ -15,9 +15,10 @@ Creation rapide de branche GitFlow avec worktree.
15
15
  1. **Analyser**: Contexte (branche, version, ahead/behind)
16
16
  2. **Choisir**: Type (feature/release/hotfix)
17
17
  3. **Creer**: Worktree ou checkout
18
- 4. **Configurer**: appsettings.Local.json si .NET
19
- 5. **Verifier ports**: Detecter conflits entre projets
20
- 6. **Resume**: Prochaines etapes
18
+ 4. **Configurer**: appsettings.Local.json + launchSettings.json Local profile si .NET
19
+ 5. **Configurer**: .env.local + npm run local si Web
20
+ 6. **Verifier ports**: Detecter conflits entre projets
21
+ 7. **Resume**: Prochaines etapes
21
22
 
22
23
  ## Commandes
23
24
 
@@ -48,7 +49,7 @@ BRANCH CREATED
48
49
  Name: {branch}
49
50
  Base: {develop|main}
50
51
  Worktree: ../worktrees/{type}s/{name}
51
- Config: {appsettings.Local.json created|skipped}
52
+ Config: {appsettings.Local.json + launchSettings.json Local|skipped}
52
53
  Ports: {✓ OK|⚠️ Conflicts resolved|⚠️ Conflicts ignored}
53
54
 
54
55
  NEXT STEPS:
@@ -10,6 +10,8 @@ tools: Bash, Read, Write, Glob
10
10
 
11
11
  Validates that SmartStack MCP is properly configured and up-to-date before any operation.
12
12
 
13
+ > **Note:** The MCP server is now bundled inside `@atlashub/smartstack-cli`. No separate package required.
14
+
13
15
  ## Status File
14
16
 
15
17
  Location: `.claude/mcp-status.json`
@@ -17,8 +19,8 @@ Location: `.claude/mcp-status.json`
17
19
  ```json
18
20
  {
19
21
  "lastCheck": "2024-01-15T10:30:00Z",
20
- "mcpVersion": "1.5.0",
21
- "mcpPath": "D:\\01 - projets\\SmartStack.mcp",
22
+ "mcpVersion": "2.0.0",
23
+ "bundled": true,
22
24
  "status": "ok",
23
25
  "tools": ["validate_conventions", "scaffold_extension", "check_migrations", "..."]
24
26
  }
@@ -55,7 +57,7 @@ If fails → Run full check
55
57
 
56
58
  ```bash
57
59
  # Check Claude Code MCP config
58
- cat ~/.claude/settings.json | grep -A 20 "smartstack"
60
+ claude mcp list 2>/dev/null | grep -i smartstack
59
61
  ```
60
62
 
61
63
  #### 3.2 Test MCP Tools Availability
@@ -71,15 +73,15 @@ Call each critical tool to verify availability:
71
73
  #### 3.3 Check MCP Version
72
74
 
73
75
  ```bash
74
- # Read MCP package.json
75
- cat "D:\\01 - projets\\SmartStack.mcp\\package.json" | grep version
76
+ # Read version from CLI package (MCP is bundled)
77
+ smartstack --version 2>/dev/null || npx @atlashub/smartstack-cli --version
76
78
  ```
77
79
 
78
80
  #### 3.4 Compare with Latest Release
79
81
 
80
82
  ```bash
81
83
  # Check npm registry for latest version
82
- npm view @anthropic/smartstack-mcp version 2>/dev/null || echo "Not published"
84
+ npm view @atlashub/smartstack-cli version 2>/dev/null || echo "Registry check failed"
83
85
  ```
84
86
 
85
87
  ### 4. Update Status File
@@ -88,7 +90,7 @@ npm view @anthropic/smartstack-mcp version 2>/dev/null || echo "Not published"
88
90
  Write(".claude/mcp-status.json", {
89
91
  lastCheck: new Date().toISOString(),
90
92
  mcpVersion: "<detected_version>",
91
- mcpPath: "<configured_path>",
93
+ bundled: true,
92
94
  status: "ok" | "error",
93
95
  tools: ["<available_tools>"],
94
96
  errors: ["<any_errors>"] // if status = error
@@ -101,18 +103,18 @@ Write(".claude/mcp-status.json", {
101
103
  ```
102
104
  MCP HEALTH CHECK
103
105
  Status: ✓ OK
104
- Version: 1.5.0 (latest)
106
+ Version: 2.0.0 (bundled in CLI)
105
107
  Last check: 2024-01-15
106
- Tools available: 12/12
108
+ Tools available: 19/19
107
109
  ```
108
110
 
109
111
  ### Warning (outdated)
110
112
  ```
111
113
  MCP HEALTH CHECK
112
114
  Status: ⚠️ UPDATE AVAILABLE
113
- Current: 1.4.0
114
- Latest: 1.5.0
115
- Action: Run `cd SmartStack.mcp && git pull`
115
+ Current: 1.38.0
116
+ Latest: 2.0.0
117
+ Action: npm update -g @atlashub/smartstack-cli
116
118
  ```
117
119
 
118
120
  ### Error
@@ -120,7 +122,7 @@ MCP HEALTH CHECK
120
122
  MCP HEALTH CHECK
121
123
  Status: ❌ ERROR
122
124
  Issue: MCP server not responding
123
- Action: Verify MCP configuration in Claude Code settings
125
+ Action: claude mcp add smartstack -- npx -p @atlashub/smartstack-cli smartstack-mcp
124
126
  ```
125
127
 
126
128
  ## Integration
@@ -0,0 +1,298 @@
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
+
3
+ // ============================================================================
4
+ // Types
5
+ // ============================================================================
6
+
7
+ export interface {{name}}Data {
8
+ id?: string;
9
+ createdAt?: string;
10
+ updatedAt?: string;
11
+ // TODO: Add {{name}} specific properties
12
+ }
13
+
14
+ export interface {{name}}Props {
15
+ /** Entity ID for edit mode */
16
+ id?: string;
17
+ /** Initial data */
18
+ initialData?: Partial<{{name}}Data>;
19
+ /** Callback when data is saved */
20
+ onSave?: (data: {{name}}Data) => void;
21
+ /** Callback when cancelled */
22
+ onCancel?: () => void;
23
+ /** Loading state from parent */
24
+ loading?: boolean;
25
+ /** Read-only mode */
26
+ readOnly?: boolean;
27
+ }
28
+
29
+ // ============================================================================
30
+ // Component
31
+ // ============================================================================
32
+
33
+ /**
34
+ * {{name}} component
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * <{{name}}
39
+ * id="123"
40
+ * onSave={(data) => console.log('Saved:', data)}
41
+ * onCancel={() => navigate(-1)}
42
+ * />
43
+ * ```
44
+ */
45
+ export const {{name}}: React.FC<{{name}}Props> = ({
46
+ id,
47
+ initialData,
48
+ onSave,
49
+ onCancel,
50
+ loading: externalLoading,
51
+ readOnly = false,
52
+ }) => {
53
+ // State
54
+ const [data, setData] = useState<{{name}}Data>(initialData || {});
55
+ const [loading, setLoading] = useState(false);
56
+ const [error, setError] = useState<string | null>(null);
57
+ const [isDirty, setIsDirty] = useState(false);
58
+
59
+ // Combined loading state
60
+ const isLoading = loading || externalLoading;
61
+
62
+ // Fetch data when ID changes
63
+ useEffect(() => {
64
+ if (id && !initialData) {
65
+ fetchData(id);
66
+ }
67
+ }, [id, initialData]);
68
+
69
+ // Fetch data from API
70
+ const fetchData = useCallback(async (fetchId: string) => {
71
+ setLoading(true);
72
+ setError(null);
73
+
74
+ try {
75
+ // TODO: Implement API call
76
+ // const response = await {{nameCamel}}Api.getById(fetchId);
77
+ // setData(response);
78
+
79
+ // Placeholder
80
+ setData({ id: fetchId });
81
+ } catch (e) {
82
+ setError(e instanceof Error ? e.message : 'Failed to load data');
83
+ } finally {
84
+ setLoading(false);
85
+ }
86
+ }, []);
87
+
88
+ // Handle field change
89
+ const handleChange = useCallback((field: keyof {{name}}Data, value: unknown) => {
90
+ setData(prev => ({ ...prev, [field]: value }));
91
+ setIsDirty(true);
92
+ }, []);
93
+
94
+ // Handle form submission
95
+ const handleSubmit = useCallback(async (e: React.FormEvent) => {
96
+ e.preventDefault();
97
+
98
+ if (readOnly) return;
99
+
100
+ setLoading(true);
101
+ setError(null);
102
+
103
+ try {
104
+ // TODO: Implement API call
105
+ // const result = data.id
106
+ // ? await {{nameCamel}}Api.update(data.id, data)
107
+ // : await {{nameCamel}}Api.create(data);
108
+
109
+ if (onSave) {
110
+ onSave(data);
111
+ }
112
+
113
+ setIsDirty(false);
114
+ } catch (e) {
115
+ setError(e instanceof Error ? e.message : 'Failed to save');
116
+ } finally {
117
+ setLoading(false);
118
+ }
119
+ }, [data, onSave, readOnly]);
120
+
121
+ // Handle cancel
122
+ const handleCancel = useCallback(() => {
123
+ if (isDirty) {
124
+ const confirmed = window.confirm('You have unsaved changes. Are you sure you want to cancel?');
125
+ if (!confirmed) return;
126
+ }
127
+
128
+ if (onCancel) {
129
+ onCancel();
130
+ }
131
+ }, [isDirty, onCancel]);
132
+
133
+ // Render loading state
134
+ if (isLoading && !data.id) {
135
+ return (
136
+ <div className="flex items-center justify-center p-8">
137
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500" />
138
+ <span className="ml-2 text-gray-600">Loading...</span>
139
+ </div>
140
+ );
141
+ }
142
+
143
+ // Render error state
144
+ if (error) {
145
+ return (
146
+ <div className="p-4 bg-red-50 border border-red-200 rounded-lg">
147
+ <div className="flex items-center">
148
+ <svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
149
+ <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
150
+ </svg>
151
+ <span className="ml-2 text-red-700">{error}</span>
152
+ </div>
153
+ <button
154
+ onClick={() => id && fetchData(id)}
155
+ className="mt-2 text-sm text-red-600 hover:text-red-800 underline"
156
+ >
157
+ Try again
158
+ </button>
159
+ </div>
160
+ );
161
+ }
162
+
163
+ return (
164
+ <div className="bg-white rounded-lg shadow-sm border border-gray-200">
165
+ {/* Header */}
166
+ <div className="px-6 py-4 border-b border-gray-200">
167
+ <h2 className="text-xl font-semibold text-gray-900">
168
+ {id ? 'Edit' : 'Create'} {{name}}
169
+ </h2>
170
+ {isDirty && (
171
+ <span className="text-sm text-amber-600">Unsaved changes</span>
172
+ )}
173
+ </div>
174
+
175
+ {/* Form */}
176
+ <form onSubmit={handleSubmit} className="p-6 space-y-6">
177
+ {/* TODO: Add form fields */}
178
+ <div className="text-gray-500 text-center py-8">
179
+ Add your form fields here
180
+ </div>
181
+
182
+ {/* Actions */}
183
+ {!readOnly && (
184
+ <div className="flex items-center justify-end gap-3 pt-4 border-t border-gray-200">
185
+ {onCancel && (
186
+ <button
187
+ type="button"
188
+ onClick={handleCancel}
189
+ disabled={isLoading}
190
+ className="px-4 py-2 text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 disabled:opacity-50"
191
+ >
192
+ Cancel
193
+ </button>
194
+ )}
195
+ <button
196
+ type="submit"
197
+ disabled={isLoading || !isDirty}
198
+ className="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
199
+ >
200
+ {isLoading ? 'Saving...' : 'Save'}
201
+ </button>
202
+ </div>
203
+ )}
204
+ </form>
205
+ </div>
206
+ );
207
+ };
208
+
209
+ export default {{name}};
210
+
211
+ // ============================================================================
212
+ // Hook
213
+ // ============================================================================
214
+
215
+ export interface Use{{name}}Options {
216
+ id?: string;
217
+ autoFetch?: boolean;
218
+ }
219
+
220
+ export function use{{name}}(options: Use{{name}}Options = {}) {
221
+ const { id, autoFetch = true } = options;
222
+
223
+ const [data, setData] = useState<{{name}}Data | null>(null);
224
+ const [loading, setLoading] = useState(false);
225
+ const [error, setError] = useState<Error | null>(null);
226
+
227
+ const fetch = useCallback(async (fetchId?: string) => {
228
+ const targetId = fetchId || id;
229
+ if (!targetId) return;
230
+
231
+ setLoading(true);
232
+ setError(null);
233
+
234
+ try {
235
+ // TODO: Implement API call
236
+ // const result = await {{nameCamel}}Api.getById(targetId);
237
+ // setData(result);
238
+ } catch (e) {
239
+ setError(e instanceof Error ? e : new Error('Unknown error'));
240
+ } finally {
241
+ setLoading(false);
242
+ }
243
+ }, [id]);
244
+
245
+ const save = useCallback(async (saveData: {{name}}Data) => {
246
+ setLoading(true);
247
+ setError(null);
248
+
249
+ try {
250
+ // TODO: Implement API call
251
+ // const result = saveData.id
252
+ // ? await {{nameCamel}}Api.update(saveData.id, saveData)
253
+ // : await {{nameCamel}}Api.create(saveData);
254
+ // setData(result);
255
+ // return result;
256
+ } catch (e) {
257
+ setError(e instanceof Error ? e : new Error('Unknown error'));
258
+ throw e;
259
+ } finally {
260
+ setLoading(false);
261
+ }
262
+ }, []);
263
+
264
+ const remove = useCallback(async (removeId?: string) => {
265
+ const targetId = removeId || id;
266
+ if (!targetId) return;
267
+
268
+ setLoading(true);
269
+ setError(null);
270
+
271
+ try {
272
+ // TODO: Implement API call
273
+ // await {{nameCamel}}Api.delete(targetId);
274
+ setData(null);
275
+ } catch (e) {
276
+ setError(e instanceof Error ? e : new Error('Unknown error'));
277
+ throw e;
278
+ } finally {
279
+ setLoading(false);
280
+ }
281
+ }, [id]);
282
+
283
+ useEffect(() => {
284
+ if (autoFetch && id) {
285
+ fetch();
286
+ }
287
+ }, [autoFetch, id, fetch]);
288
+
289
+ return {
290
+ data,
291
+ loading,
292
+ error,
293
+ fetch,
294
+ save,
295
+ remove,
296
+ setData,
297
+ };
298
+ }
@@ -0,0 +1,184 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Threading;
4
+ using System.Threading.Tasks;
5
+ using Microsoft.AspNetCore.Authorization;
6
+ using Microsoft.AspNetCore.Mvc;
7
+ using Microsoft.Extensions.Logging;
8
+
9
+ namespace {{namespace}}.Controllers;
10
+
11
+ /// <summary>
12
+ /// API controller for {{name}} operations
13
+ /// </summary>
14
+ [ApiController]
15
+ [Route("api/[controller]")]
16
+ {{#if navRoute}}
17
+ [NavRoute("{{navRoute}}")]
18
+ {{/if}}
19
+ [Authorize]
20
+ [Produces("application/json")]
21
+ public class {{name}}Controller : ControllerBase
22
+ {
23
+ private readonly ILogger<{{name}}Controller> _logger;
24
+ // private readonly I{{name}}Service _{{nameCamel}}Service;
25
+
26
+ public {{name}}Controller(
27
+ ILogger<{{name}}Controller> logger
28
+ // I{{name}}Service {{nameCamel}}Service
29
+ )
30
+ {
31
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
32
+ // _{{nameCamel}}Service = {{nameCamel}}Service ?? throw new ArgumentNullException(nameof({{nameCamel}}Service));
33
+ }
34
+
35
+ /// <summary>
36
+ /// Get all {{namePlural}}
37
+ /// </summary>
38
+ /// <param name="cancellationToken">Cancellation token</param>
39
+ /// <returns>List of {{namePlural}}</returns>
40
+ [HttpGet]
41
+ {{#if navRoute}}
42
+ [Authorize(Policy = "{{navRoute}}.read")]
43
+ {{/if}}
44
+ [ProducesResponseType(typeof(IEnumerable<{{name}}Dto>), 200)]
45
+ public async Task<ActionResult<IEnumerable<{{name}}Dto>>> GetAll(CancellationToken cancellationToken = default)
46
+ {
47
+ _logger.LogInformation("Getting all {{namePlural}}");
48
+
49
+ // TODO: Implement using service
50
+ // var result = await _{{nameCamel}}Service.GetAllAsync(cancellationToken);
51
+ // return Ok(result);
52
+
53
+ return Ok(Array.Empty<{{name}}Dto>());
54
+ }
55
+
56
+ /// <summary>
57
+ /// Get {{nameCamel}} by ID
58
+ /// </summary>
59
+ /// <param name="id">{{name}} ID</param>
60
+ /// <param name="cancellationToken">Cancellation token</param>
61
+ /// <returns>{{name}} details</returns>
62
+ [HttpGet("{id:guid}")]
63
+ {{#if navRoute}}
64
+ [Authorize(Policy = "{{navRoute}}.read")]
65
+ {{/if}}
66
+ [ProducesResponseType(typeof({{name}}Dto), 200)]
67
+ [ProducesResponseType(404)]
68
+ public async Task<ActionResult<{{name}}Dto>> GetById(
69
+ Guid id,
70
+ CancellationToken cancellationToken = default)
71
+ {
72
+ _logger.LogInformation("Getting {{nameCamel}} {Id}", id);
73
+
74
+ // TODO: Implement using service
75
+ // var result = await _{{nameCamel}}Service.GetByIdAsync(id, cancellationToken);
76
+ // if (result == null) return NotFound();
77
+ // return Ok(result);
78
+
79
+ return NotFound();
80
+ }
81
+
82
+ /// <summary>
83
+ /// Create new {{nameCamel}}
84
+ /// </summary>
85
+ /// <param name="request">Create request</param>
86
+ /// <param name="cancellationToken">Cancellation token</param>
87
+ /// <returns>Created {{nameCamel}}</returns>
88
+ [HttpPost]
89
+ {{#if navRoute}}
90
+ [Authorize(Policy = "{{navRoute}}.create")]
91
+ {{/if}}
92
+ [ProducesResponseType(typeof({{name}}Dto), 201)]
93
+ [ProducesResponseType(400)]
94
+ public async Task<ActionResult<{{name}}Dto>> Create(
95
+ [FromBody] Create{{name}}Request request,
96
+ CancellationToken cancellationToken = default)
97
+ {
98
+ _logger.LogInformation("Creating new {{nameCamel}}");
99
+
100
+ // TODO: Validate and create using service
101
+ // var result = await _{{nameCamel}}Service.CreateAsync(request, cancellationToken);
102
+ // return CreatedAtAction(nameof(GetById), new { id = result.Id }, result);
103
+
104
+ var id = Guid.NewGuid();
105
+ return CreatedAtAction(nameof(GetById), new { id }, null);
106
+ }
107
+
108
+ /// <summary>
109
+ /// Update {{nameCamel}}
110
+ /// </summary>
111
+ /// <param name="id">{{name}} ID</param>
112
+ /// <param name="request">Update request</param>
113
+ /// <param name="cancellationToken">Cancellation token</param>
114
+ [HttpPut("{id:guid}")]
115
+ {{#if navRoute}}
116
+ [Authorize(Policy = "{{navRoute}}.update")]
117
+ {{/if}}
118
+ [ProducesResponseType(204)]
119
+ [ProducesResponseType(404)]
120
+ [ProducesResponseType(400)]
121
+ public async Task<ActionResult> Update(
122
+ Guid id,
123
+ [FromBody] Update{{name}}Request request,
124
+ CancellationToken cancellationToken = default)
125
+ {
126
+ _logger.LogInformation("Updating {{nameCamel}} {Id}", id);
127
+
128
+ // TODO: Implement using service
129
+ // await _{{nameCamel}}Service.UpdateAsync(id, request, cancellationToken);
130
+
131
+ return NoContent();
132
+ }
133
+
134
+ /// <summary>
135
+ /// Delete {{nameCamel}}
136
+ /// </summary>
137
+ /// <param name="id">{{name}} ID</param>
138
+ /// <param name="cancellationToken">Cancellation token</param>
139
+ [HttpDelete("{id:guid}")]
140
+ {{#if navRoute}}
141
+ [Authorize(Policy = "{{navRoute}}.delete")]
142
+ {{/if}}
143
+ [ProducesResponseType(204)]
144
+ [ProducesResponseType(404)]
145
+ public async Task<ActionResult> Delete(
146
+ Guid id,
147
+ CancellationToken cancellationToken = default)
148
+ {
149
+ _logger.LogInformation("Deleting {{nameCamel}} {Id}", id);
150
+
151
+ // TODO: Implement using service
152
+ // await _{{nameCamel}}Service.DeleteAsync(id, cancellationToken);
153
+
154
+ return NoContent();
155
+ }
156
+ }
157
+
158
+ // ============================================================================
159
+ // DTOs
160
+ // ============================================================================
161
+
162
+ /// <summary>
163
+ /// {{name}} data transfer object
164
+ /// </summary>
165
+ public record {{name}}Dto(
166
+ Guid Id,
167
+ DateTime CreatedAt,
168
+ DateTime? UpdatedAt
169
+ // TODO: Add additional properties
170
+ );
171
+
172
+ /// <summary>
173
+ /// Request to create a new {{nameCamel}}
174
+ /// </summary>
175
+ public record Create{{name}}Request(
176
+ // TODO: Add creation properties
177
+ );
178
+
179
+ /// <summary>
180
+ /// Request to update a {{nameCamel}}
181
+ /// </summary>
182
+ public record Update{{name}}Request(
183
+ // TODO: Add update properties
184
+ );