berget 2.2.5 → 2.2.7

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 (148) hide show
  1. package/.github/workflows/publish.yml +8 -8
  2. package/.github/workflows/test.yml +12 -6
  3. package/.husky/pre-commit +1 -0
  4. package/.prettierignore +15 -0
  5. package/.prettierrc +5 -3
  6. package/CONTRIBUTING.md +38 -0
  7. package/README.md +2 -148
  8. package/dist/index.js +21 -21
  9. package/dist/package.json +30 -2
  10. package/dist/src/agents/app.js +28 -0
  11. package/dist/src/agents/backend.js +25 -0
  12. package/dist/src/agents/devops.js +34 -0
  13. package/dist/src/agents/frontend.js +25 -0
  14. package/dist/src/agents/fullstack.js +25 -0
  15. package/dist/src/agents/index.js +61 -0
  16. package/dist/src/agents/quality.js +70 -0
  17. package/dist/src/agents/security.js +26 -0
  18. package/dist/src/agents/types.js +2 -0
  19. package/dist/src/client.js +54 -62
  20. package/dist/src/commands/api-keys.js +132 -140
  21. package/dist/src/commands/auth.js +9 -9
  22. package/dist/src/commands/autocomplete.js +9 -9
  23. package/dist/src/commands/billing.js +7 -9
  24. package/dist/src/commands/chat.js +90 -92
  25. package/dist/src/commands/clusters.js +12 -12
  26. package/dist/src/commands/code/__tests__/auth-sync.test.js +348 -0
  27. package/dist/src/commands/code/__tests__/fake-api-key-service.js +23 -0
  28. package/dist/src/commands/code/__tests__/fake-auth-service.js +55 -0
  29. package/dist/src/commands/code/__tests__/fake-command-runner.js +50 -0
  30. package/dist/src/commands/code/__tests__/fake-file-store.js +55 -0
  31. package/dist/src/commands/code/__tests__/fake-prompter.js +133 -0
  32. package/dist/src/commands/code/__tests__/setup-flow.test.js +505 -0
  33. package/dist/src/commands/code/adapters/clack-prompter.js +81 -0
  34. package/dist/src/commands/code/adapters/fs-file-store.js +80 -0
  35. package/dist/src/commands/code/adapters/spawn-command-runner.js +53 -0
  36. package/dist/src/commands/code/auth-sync.js +283 -0
  37. package/dist/src/commands/code/errors.js +27 -0
  38. package/dist/src/commands/code/ports/auth-services.js +2 -0
  39. package/dist/src/commands/code/ports/command-runner.js +2 -0
  40. package/dist/src/commands/code/ports/file-store.js +2 -0
  41. package/dist/src/commands/code/ports/prompter.js +2 -0
  42. package/dist/src/commands/code/setup.js +533 -0
  43. package/dist/src/commands/code.js +223 -779
  44. package/dist/src/commands/models.js +13 -15
  45. package/dist/src/commands/users.js +6 -8
  46. package/dist/src/constants/command-structure.js +116 -114
  47. package/dist/src/services/api-key-service.js +43 -48
  48. package/dist/src/services/auth-service.js +60 -299
  49. package/dist/src/services/browser-auth.js +278 -0
  50. package/dist/src/services/chat-service.js +78 -91
  51. package/dist/src/services/cluster-service.js +6 -6
  52. package/dist/src/services/collaborator-service.js +5 -8
  53. package/dist/src/services/flux-service.js +5 -8
  54. package/dist/src/services/helm-service.js +5 -8
  55. package/dist/src/services/kubectl-service.js +7 -10
  56. package/dist/src/utils/config-checker.js +5 -5
  57. package/dist/src/utils/config-loader.js +25 -25
  58. package/dist/src/utils/default-api-key.js +23 -23
  59. package/dist/src/utils/env-manager.js +7 -7
  60. package/dist/src/utils/error-handler.js +60 -61
  61. package/dist/src/utils/logger.js +7 -7
  62. package/dist/src/utils/markdown-renderer.js +2 -2
  63. package/dist/src/utils/opencode-validator.js +17 -20
  64. package/dist/src/utils/token-manager.js +38 -11
  65. package/dist/tests/commands/chat.test.js +24 -24
  66. package/dist/tests/commands/code.test.js +169 -138
  67. package/dist/tests/utils/config-loader.test.js +114 -114
  68. package/dist/tests/utils/env-manager.test.js +57 -57
  69. package/dist/tests/utils/opencode-validator.test.js +44 -43
  70. package/dist/vitest.config.js +1 -1
  71. package/eslint.config.mjs +47 -0
  72. package/index.ts +42 -48
  73. package/package.json +30 -2
  74. package/src/agents/app.ts +27 -0
  75. package/src/agents/backend.ts +24 -0
  76. package/src/agents/devops.ts +33 -0
  77. package/src/agents/frontend.ts +24 -0
  78. package/src/agents/fullstack.ts +24 -0
  79. package/src/agents/index.ts +71 -0
  80. package/src/agents/quality.ts +69 -0
  81. package/src/agents/security.ts +26 -0
  82. package/src/agents/types.ts +17 -0
  83. package/src/client.ts +125 -167
  84. package/src/commands/api-keys.ts +261 -358
  85. package/src/commands/auth.ts +24 -30
  86. package/src/commands/autocomplete.ts +12 -12
  87. package/src/commands/billing.ts +22 -27
  88. package/src/commands/chat.ts +230 -323
  89. package/src/commands/clusters.ts +33 -33
  90. package/src/commands/code/__tests__/auth-sync.test.ts +481 -0
  91. package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
  92. package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
  93. package/src/commands/code/__tests__/fake-command-runner.ts +44 -0
  94. package/src/commands/code/__tests__/fake-file-store.ts +44 -0
  95. package/src/commands/code/__tests__/fake-prompter.ts +121 -0
  96. package/src/commands/code/__tests__/setup-flow.test.ts +628 -0
  97. package/src/commands/code/adapters/clack-prompter.ts +55 -0
  98. package/src/commands/code/adapters/fs-file-store.ts +37 -0
  99. package/src/commands/code/adapters/spawn-command-runner.ts +40 -0
  100. package/src/commands/code/auth-sync.ts +329 -0
  101. package/src/commands/code/errors.ts +23 -0
  102. package/src/commands/code/ports/auth-services.ts +14 -0
  103. package/src/commands/code/ports/command-runner.ts +10 -0
  104. package/src/commands/code/ports/file-store.ts +7 -0
  105. package/src/commands/code/ports/prompter.ts +29 -0
  106. package/src/commands/code/setup.ts +630 -0
  107. package/src/commands/code.ts +335 -1074
  108. package/src/commands/index.ts +19 -19
  109. package/src/commands/models.ts +32 -37
  110. package/src/commands/users.ts +15 -22
  111. package/src/constants/command-structure.ts +120 -140
  112. package/src/services/api-key-service.ts +96 -113
  113. package/src/services/auth-service.ts +92 -339
  114. package/src/services/browser-auth.ts +296 -0
  115. package/src/services/chat-service.ts +246 -279
  116. package/src/services/cluster-service.ts +29 -32
  117. package/src/services/collaborator-service.ts +13 -18
  118. package/src/services/flux-service.ts +16 -18
  119. package/src/services/helm-service.ts +16 -18
  120. package/src/services/kubectl-service.ts +12 -14
  121. package/src/types/api.d.ts +924 -926
  122. package/src/types/json.d.ts +3 -3
  123. package/src/utils/config-checker.ts +10 -10
  124. package/src/utils/config-loader.ts +110 -127
  125. package/src/utils/default-api-key.ts +81 -93
  126. package/src/utils/env-manager.ts +36 -40
  127. package/src/utils/error-handler.ts +83 -78
  128. package/src/utils/logger.ts +41 -41
  129. package/src/utils/markdown-renderer.ts +11 -11
  130. package/src/utils/opencode-validator.ts +51 -56
  131. package/src/utils/token-manager.ts +84 -64
  132. package/templates/agents/app.md +23 -0
  133. package/templates/agents/backend.md +23 -0
  134. package/templates/agents/devops.md +30 -0
  135. package/templates/agents/frontend.md +25 -0
  136. package/templates/agents/fullstack.md +23 -0
  137. package/templates/agents/quality.md +69 -0
  138. package/templates/agents/security.md +21 -0
  139. package/tests/commands/chat.test.ts +60 -70
  140. package/tests/commands/code.test.ts +346 -345
  141. package/tests/utils/config-loader.test.ts +260 -260
  142. package/tests/utils/env-manager.test.ts +127 -134
  143. package/tests/utils/opencode-validator.test.ts +65 -69
  144. package/tsconfig.json +2 -2
  145. package/vitest.config.ts +3 -3
  146. package/AGENTS.md +0 -374
  147. package/TODO.md +0 -19
  148. package/opencode.json +0 -146
@@ -1,38 +1,57 @@
1
- import * as fs from 'fs'
2
- import * as path from 'path'
3
- import * as os from 'os'
4
- import chalk from 'chalk'
5
- import { logger } from './logger'
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import * as os from "os";
4
+ import { logger } from "./logger";
6
5
 
7
6
  interface TokenData {
8
- access_token: string
9
- refresh_token: string
10
- expires_at: number // timestamp in milliseconds
7
+ access_token: string;
8
+ refresh_token: string;
9
+ expires_at: number; // timestamp in milliseconds
10
+ }
11
+
12
+ /**
13
+ * Extract the expiration time from a JWT token
14
+ * @param accessToken The JWT access token
15
+ * @returns The expiration timestamp in milliseconds, or 0 if invalid
16
+ */
17
+ function extractJwtExpiresAt(accessToken: string): number {
18
+ try {
19
+ const parts = accessToken.split(".");
20
+ if (parts.length !== 3) return 0;
21
+ const payload = Buffer.from(parts[1], "base64url").toString("utf8");
22
+ const decoded = JSON.parse(payload);
23
+ if (typeof decoded.exp === "number") {
24
+ return decoded.exp * 1000; // JWT exp is in seconds, convert to milliseconds
25
+ }
26
+ } catch {
27
+ // If decoding fails, return 0 (treated as expired)
28
+ }
29
+ return 0;
11
30
  }
12
31
 
13
32
  /**
14
33
  * Manages authentication tokens including refresh functionality
15
34
  */
16
35
  export class TokenManager {
17
- private static instance: TokenManager
18
- private tokenFilePath: string
19
- private tokenData: TokenData | null = null
36
+ private static instance: TokenManager;
37
+ private tokenFilePath: string;
38
+ private tokenData: TokenData | null = null;
20
39
 
21
40
  private constructor() {
22
41
  // Set up token file path in user's home directory
23
- const bergetDir = path.join(os.homedir(), '.berget')
42
+ const bergetDir = path.join(os.homedir(), ".berget");
24
43
  if (!fs.existsSync(bergetDir)) {
25
- fs.mkdirSync(bergetDir, { recursive: true })
44
+ fs.mkdirSync(bergetDir, { recursive: true });
26
45
  }
27
- this.tokenFilePath = path.join(bergetDir, 'auth.json')
28
- this.loadToken()
46
+ this.tokenFilePath = path.join(bergetDir, "auth.json");
47
+ this.loadToken();
29
48
  }
30
49
 
31
50
  public static getInstance(): TokenManager {
32
51
  if (!TokenManager.instance) {
33
- TokenManager.instance = new TokenManager()
52
+ TokenManager.instance = new TokenManager();
34
53
  }
35
- return TokenManager.instance
54
+ return TokenManager.instance;
36
55
  }
37
56
 
38
57
  /**
@@ -41,12 +60,12 @@ export class TokenManager {
41
60
  private loadToken(): void {
42
61
  try {
43
62
  if (fs.existsSync(this.tokenFilePath)) {
44
- const data = fs.readFileSync(this.tokenFilePath, 'utf8')
45
- this.tokenData = JSON.parse(data)
63
+ const data = fs.readFileSync(this.tokenFilePath, "utf8");
64
+ this.tokenData = JSON.parse(data);
46
65
  }
47
- } catch (error) {
48
- logger.error('Failed to load authentication token')
49
- this.tokenData = null
66
+ } catch {
67
+ logger.error("Failed to load authentication token");
68
+ this.tokenData = null;
50
69
  }
51
70
  }
52
71
 
@@ -56,20 +75,17 @@ export class TokenManager {
56
75
  private saveToken(): void {
57
76
  try {
58
77
  if (this.tokenData) {
59
- fs.writeFileSync(
60
- this.tokenFilePath,
61
- JSON.stringify(this.tokenData, null, 2),
62
- )
78
+ fs.writeFileSync(this.tokenFilePath, JSON.stringify(this.tokenData, null, 2));
63
79
  // Set file permissions to be readable only by the owner
64
- fs.chmodSync(this.tokenFilePath, 0o600)
80
+ fs.chmodSync(this.tokenFilePath, 0o600);
65
81
  } else {
66
82
  // If token data is null, remove the file
67
83
  if (fs.existsSync(this.tokenFilePath)) {
68
- fs.unlinkSync(this.tokenFilePath)
84
+ fs.unlinkSync(this.tokenFilePath);
69
85
  }
70
86
  }
71
- } catch (error) {
72
- logger.error('Failed to save authentication token')
87
+ } catch {
88
+ logger.error("Failed to save authentication token");
73
89
  }
74
90
  }
75
91
 
@@ -78,8 +94,8 @@ export class TokenManager {
78
94
  * @returns The access token or null if not available
79
95
  */
80
96
  public getAccessToken(): string | null {
81
- if (!this.tokenData) return null
82
- return this.tokenData.access_token
97
+ if (!this.tokenData) return null;
98
+ return this.tokenData.access_token;
83
99
  }
84
100
 
85
101
  /**
@@ -87,8 +103,8 @@ export class TokenManager {
87
103
  * @returns The refresh token or null if not available
88
104
  */
89
105
  public getRefreshToken(): string | null {
90
- if (!this.tokenData) return null
91
- return this.tokenData.refresh_token
106
+ if (!this.tokenData) return null;
107
+ return this.tokenData.refresh_token;
92
108
  }
93
109
 
94
110
  /**
@@ -96,34 +112,34 @@ export class TokenManager {
96
112
  * @returns true if expired or about to expire (within 10% of lifetime or 30 seconds), false otherwise
97
113
  */
98
114
  public isTokenExpired(): boolean {
99
- if (!this.tokenData || !this.tokenData.expires_at) return true
115
+ if (!this.tokenData || !this.tokenData.expires_at) return true;
100
116
 
101
117
  try {
102
- const now = Date.now()
103
- const expiresAt = this.tokenData.expires_at
104
- const timeUntilExpiry = expiresAt - now
118
+ const now = Date.now();
119
+ const expiresAt = this.tokenData.expires_at;
120
+ const timeUntilExpiry = expiresAt - now;
105
121
 
106
122
  // Use 10% of remaining lifetime or 30 seconds, whichever is smaller
107
123
  // This ensures we don't refresh tokens that were just issued
108
- const minBuffer = 30 * 1000 // 30 seconds minimum
109
- const percentBuffer = timeUntilExpiry * 0.1 // 10% of lifetime
110
- const expirationBuffer = Math.min(minBuffer, percentBuffer)
124
+ const minBuffer = 30 * 1000; // 30 seconds minimum
125
+ const percentBuffer = timeUntilExpiry * 0.1; // 10% of lifetime
126
+ const expirationBuffer = Math.min(minBuffer, percentBuffer);
111
127
 
112
- const isExpired = now + expirationBuffer >= expiresAt
128
+ const isExpired = now + expirationBuffer >= expiresAt;
113
129
 
114
130
  if (isExpired) {
115
131
  logger.debug(
116
- `Token expired or expiring soon. Current time: ${new Date().toISOString()}, Expiry: ${new Date(expiresAt).toISOString()}`,
117
- )
132
+ `Token expired or expiring soon. Current time: ${new Date().toISOString()}, Expiry: ${new Date(expiresAt).toISOString()}`
133
+ );
118
134
  }
119
135
 
120
- return isExpired
136
+ return isExpired;
121
137
  } catch (error) {
122
138
  // If there's any error checking expiration, assume token is expired
123
139
  logger.error(
124
- `Error checking token expiration: ${error instanceof Error ? error.message : String(error)}`,
125
- )
126
- return true
140
+ `Error checking token expiration: ${error instanceof Error ? error.message : String(error)}`
141
+ );
142
+ return true;
127
143
  }
128
144
  }
129
145
 
@@ -131,39 +147,43 @@ export class TokenManager {
131
147
  * Set new token data
132
148
  * @param accessToken The new access token
133
149
  * @param refreshToken The new refresh token
134
- * @param expiresIn Expiration time in seconds
150
+ * @param expiresIn Expiration time in seconds (fallback if JWT parsing fails)
135
151
  */
136
- public setTokens(
137
- accessToken: string,
138
- refreshToken: string,
139
- expiresIn: number,
140
- ): void {
152
+ public setTokens(accessToken: string, refreshToken: string, expiresIn: number): void {
153
+ // Extract the actual expiry time from the JWT token
154
+ const jwtExpiresAt = extractJwtExpiresAt(accessToken);
155
+ const expiresAt = jwtExpiresAt > 0 ? jwtExpiresAt : Date.now() + expiresIn * 1000;
156
+
141
157
  this.tokenData = {
142
158
  access_token: accessToken,
143
159
  refresh_token: refreshToken,
144
- expires_at: Date.now() + expiresIn * 1000,
145
- }
146
- this.saveToken()
160
+ expires_at: expiresAt,
161
+ };
162
+ this.saveToken();
147
163
  }
148
164
 
149
165
  /**
150
166
  * Update just the access token and its expiration
151
167
  * @param accessToken The new access token
152
- * @param expiresIn Expiration time in seconds
168
+ * @param expiresIn Expiration time in seconds (fallback if JWT parsing fails)
153
169
  */
154
170
  public updateAccessToken(accessToken: string, expiresIn: number): void {
155
- if (!this.tokenData) return
171
+ if (!this.tokenData) return;
172
+
173
+ // Extract the actual expiry time from the JWT token
174
+ const jwtExpiresAt = extractJwtExpiresAt(accessToken);
175
+ const expiresAt = jwtExpiresAt > 0 ? jwtExpiresAt : Date.now() + expiresIn * 1000;
156
176
 
157
- this.tokenData.access_token = accessToken
158
- this.tokenData.expires_at = Date.now() + expiresIn * 1000
159
- this.saveToken()
177
+ this.tokenData.access_token = accessToken;
178
+ this.tokenData.expires_at = expiresAt;
179
+ this.saveToken();
160
180
  }
161
181
 
162
182
  /**
163
183
  * Clear all token data
164
184
  */
165
185
  public clearTokens(): void {
166
- this.tokenData = null
167
- this.saveToken()
186
+ this.tokenData = null;
187
+ this.saveToken();
168
188
  }
169
189
  }
@@ -0,0 +1,23 @@
1
+ ---
2
+ description: Expo + React Native apps; props-first, offline-aware, shared tokens.
3
+ mode: primary
4
+ temperature: 0.4
5
+ top_p: 0.9
6
+ permission:
7
+ edit: allow
8
+ bash: deny
9
+ webfetch: allow
10
+ ---
11
+
12
+ You are Berget Code App agent. Voice: Scandinavian calm—precise, concise, confident. Expo + React Native + TypeScript. Structure by components/hooks/services/navigation. Components are pure; data via props; refactor shared logic into hooks/stores. Share tokens with frontend. Mock data in /data via typed hooks; later replace with live APIs. Offline via SQLite/MMKV; notifications via Expo. Request permissions only when needed. Subtle, meaningful motion; light/dark parity.
13
+
14
+ GIT WORKFLOW RULES (CRITICAL):
15
+
16
+ - NEVER push directly to main branch - ALWAYS use pull requests
17
+ - NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'
18
+ - ALWAYS clean up test files, documentation files, and temporary artifacts before committing
19
+ - ALWAYS ensure git history maintains production quality - no test commits, no debugging code
20
+ - ALWAYS create descriptive commit messages following project conventions
21
+ - ALWAYS run tests and build before creating PR
22
+
23
+ CRITICAL: When all app implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.
@@ -0,0 +1,23 @@
1
+ ---
2
+ description: Functional, modular Koa + TypeScript services; schema-first with code quality focus.
3
+ mode: primary
4
+ temperature: 0.3
5
+ top_p: 0.9
6
+ permission:
7
+ edit: allow
8
+ bash: allow
9
+ webfetch: allow
10
+ ---
11
+
12
+ You are Berget Code Backend agent. Voice: Scandinavian calm—precise, concise, confident. TypeScript + Koa. Prefer many small pure functions; avoid big try/catch blocks. Routes thin; logic in services/adapters/domain. Validate with Zod; auto-generate OpenAPI. Adapters isolate external systems; domain never depends on framework. Test with supertest; idempotent and stateless by default. Each microservice emits an OpenAPI contract; changes propagate upward to types. Code Quality & Refactoring Principles: Apply Single Responsibility Principle, fail fast with explicit errors, eliminate code duplication, remove nested complexity, use descriptive error codes, keep functions under 30 lines. Always leave code cleaner and more readable than you found it.
13
+
14
+ GIT WORKFLOW RULES (CRITICAL):
15
+
16
+ - NEVER push directly to main branch - ALWAYS use pull requests
17
+ - NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'
18
+ - ALWAYS clean up test files, documentation files, and temporary artifacts before committing
19
+ - ALWAYS ensure git history maintains production quality - no test commits, no debugging code
20
+ - ALWAYS create descriptive commit messages following project conventions
21
+ - ALWAYS run tests and build before creating PR
22
+
23
+ CRITICAL: When all backend implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.
@@ -0,0 +1,30 @@
1
+ ---
2
+ description: Declarative GitOps infra with FluxCD, Kustomize, Helm, operators.
3
+ mode: primary
4
+ temperature: 0.3
5
+ top_p: 0.8
6
+ permission:
7
+ edit: allow
8
+ bash: allow
9
+ webfetch: allow
10
+ ---
11
+
12
+ You are Berget Code DevOps agent. Voice: Scandinavian calm—precise, concise, confident. Start simple: k8s/{deployment,service,ingress}. Add FluxCD sync to repo and image automation. Use Kustomize bases/overlays (staging, production). Add dependencies via Helm from upstream sources; prefer native operators when available (CloudNativePG, cert-manager, external-dns). SemVer with -rc tags keeps CI environments current. Observability with Prometheus/Grafana. No manual kubectl in production—Git is the source of truth.
13
+
14
+ GIT WORKFLOW RULES (CRITICAL):
15
+
16
+ - NEVER push directly to main branch - ALWAYS use pull requests
17
+ - NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'
18
+ - ALWAYS clean up test files, documentation files, and temporary artifacts before committing
19
+ - ALWAYS ensure git history maintains production quality - no test commits, no debugging code
20
+ - ALWAYS create descriptive commit messages following project conventions
21
+ - ALWAYS run tests and build before creating PR
22
+
23
+ Helm Values Configuration Process:
24
+
25
+ 1. Documentation First Approach: Always fetch official documentation from Artifact Hub/GitHub for the specific chart version before writing values. Search Artifact Hub for exact chart version documentation, check the chart's GitHub repository for official docs and examples, verify the exact version being used in the deployment.
26
+ 2. Validation Requirements: Check for available validation schemas before committing YAML files. Use Helm's built-in validation tools (helm lint, helm template). Validate against JSON schema if available for the chart. Ensure YAML syntax correctness with linters.
27
+ 3. Standard Workflow: Identify chart name and exact version. Fetch official documentation from Artifact Hub/GitHub. Check for available schemas and validation tools. Write values according to official documentation. Validate against schema (if available). Test with helm template or helm lint. Commit validated YAML files.
28
+ 4. Quality Assurance: Never commit unvalidated Helm values. Use helm dependency update when adding new charts. Test rendering with helm template --dry-run before deployment. Document any custom values with comments referencing official docs.
29
+
30
+ CRITICAL: When all devops implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.
@@ -0,0 +1,25 @@
1
+ ---
2
+ description: Builds Scandinavian, type-safe UIs with React, Tailwind, Shadcn.
3
+ mode: primary
4
+ temperature: 0.4
5
+ top_p: 0.9
6
+ permission:
7
+ edit: allow
8
+ bash: deny
9
+ webfetch: allow
10
+ ---
11
+
12
+ You are Berget Code Frontend agent. Voice: Scandinavian calm—precise, concise, confident. React 18 + TypeScript. Tailwind + Shadcn UI only via the design system (index.css, tailwind.config.ts). Use semantic tokens for color/spacing/typography/motion; never ad-hoc classes or inline colors. Components are pure and responsive; props-first data; minimal global state (Zustand/Jotai). Accessibility and keyboard navigation mandatory. Mock data only at init under /data via typed hooks (e.g., useProducts() reading /data/products.json). Design: minimal, balanced, quiet motion.
13
+
14
+ IMPORTANT: You have NO bash access and cannot run git commands. When your frontend implementation tasks are complete, inform the user that changes are ready and suggest using /pr command to create a pull request with proper testing and quality checks.
15
+
16
+ CODE QUALITY RULES:
17
+
18
+ - Write clean, production-ready code
19
+ - Follow React and TypeScript best practices
20
+ - Ensure accessibility and responsive design
21
+ - Use semantic tokens from design system
22
+ - Test your components manually when possible
23
+ - Document any complex logic with comments
24
+
25
+ CRITICAL: When frontend implementation is complete, ALWAYS inform the user to use "/pr" command to handle testing, building, and pull request creation.
@@ -0,0 +1,23 @@
1
+ ---
2
+ description: Router/coordinator agent for full-stack development with schema-driven architecture
3
+ mode: primary
4
+ temperature: 0.3
5
+ top_p: 0.9
6
+ permission:
7
+ edit: allow
8
+ bash: allow
9
+ webfetch: allow
10
+ ---
11
+
12
+ Voice: Scandinavian calm—precise, concise, confident; no fluff. You are Berget Code Fullstack agent. Act as a router and coordinator in a monorepo. Bottom-up schema: database → OpenAPI → generated types. Top-down types: API → UI → components. Use openapi-fetch and Zod at every boundary; compile-time errors are desired when contracts change. Routing rules: if task/paths match /apps/frontend or React (.tsx) → use frontend; if /apps/app or Expo/React Native → app; if /infra, /k8s, flux-system, kustomization.yaml, Helm values → devops; if /services, Koa routers, services/adapters/domain → backend. If ambiguous, remain fullstack and outline the end-to-end plan, then delegate subtasks to the right persona. Security: validate inputs; secrets via FluxCD SOPS/Sealed Secrets. Documentation is generated from code—never duplicated.
13
+
14
+ GIT WORKFLOW RULES (CRITICAL):
15
+
16
+ - NEVER push directly to main branch - ALWAYS use pull requests
17
+ - NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'
18
+ - ALWAYS clean up test files, documentation files, and temporary artifacts before committing
19
+ - ALWAYS ensure git history maintains production quality - no test commits, no debugging code
20
+ - ALWAYS create descriptive commit messages following project conventions
21
+ - ALWAYS run tests and build before creating PR
22
+
23
+ CRITICAL: When all implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.
@@ -0,0 +1,69 @@
1
+ ---
2
+ description: Quality assurance specialist for testing, building, and complete PR management.
3
+ mode: subagent
4
+ temperature: 0.1
5
+ top_p: 0.9
6
+ permission:
7
+ edit: allow
8
+ bash: allow
9
+ webfetch: allow
10
+ ---
11
+
12
+ Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Quality agent. Specialist in code quality assurance, testing, building, and pull request lifecycle management.
13
+
14
+ Core responsibilities:
15
+
16
+ - Run comprehensive test suites (npm test, npm run test, jest, vitest)
17
+ - Execute build processes (npm run build, webpack, vite, tsc)
18
+ - Create and manage pull requests with proper descriptions
19
+ - Handle merge conflicts and keep main updated
20
+ - Monitor GitHub for reviewer comments and address them
21
+ - Ensure code quality standards are met
22
+ - Validate linting and formatting (npm run lint, prettier)
23
+ - Check test coverage and performance benchmarks
24
+ - Handle CI/CD pipeline validation
25
+
26
+ Complete PR Workflow:
27
+
28
+ 1. Ensure all tests pass: npm test
29
+ 2. Build successfully: npm run build
30
+ 3. Commit all changes with proper message
31
+ 4. Push to feature branch
32
+ 5. Update main branch and handle merge conflicts
33
+ 6. Create or update PR with comprehensive description
34
+ 7. Monitor for reviewer comments
35
+ 8. Address feedback and push updates
36
+ 9. Always provide PR URL for user review
37
+
38
+ Essential CLI commands:
39
+
40
+ - npm test or npm run test (run test suite)
41
+ - npm run build (build project)
42
+ - npm run lint (run linting)
43
+ - npm run format (format code)
44
+ - npm run test:coverage (check coverage)
45
+ - git add <specific-files> && git commit -m "message" && git push (commit and push)
46
+ - git checkout main && git pull origin main (update main)
47
+ - git checkout feature-branch && git merge main (handle conflicts)
48
+ - gh pr create --title "title" --body "body" (create PR)
49
+ - gh pr view --comments (check PR comments)
50
+ - gh pr edit --title "title" --body "body" (update PR)
51
+
52
+ PR Creation Process:
53
+
54
+ - Always include clear summary of changes
55
+ - List technical details and improvements
56
+ - Include testing and validation results
57
+ - Add any breaking changes or migration notes
58
+ - Provide PR URL immediately after creation
59
+
60
+ GIT WORKFLOW RULES (CRITICAL - ENFORCE STRICTLY):
61
+
62
+ - NEVER push directly to main branch - ALWAYS use pull requests
63
+ - NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'
64
+ - ALWAYS clean up test files, documentation files, and temporary artifacts before committing
65
+ - ALWAYS ensure git history maintains production quality - no test commits, no debugging code
66
+ - ALWAYS create descriptive commit messages following project conventions
67
+ - ALWAYS run tests and build before creating PR
68
+
69
+ Always provide specific command examples and wait for processes to complete before proceeding.
@@ -0,0 +1,21 @@
1
+ ---
2
+ description: Security specialist for pentesting, OWASP compliance, and vulnerability assessments.
3
+ mode: subagent
4
+ temperature: 0.2
5
+ top_p: 0.8
6
+ permission:
7
+ edit: deny
8
+ bash: allow
9
+ webfetch: allow
10
+ ---
11
+
12
+ Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Security agent. Expert in application security, penetration testing, and OWASP standards. Core responsibilities: Conduct security assessments and penetration tests, Validate OWASP Top 10 compliance, Review code for security vulnerabilities, Implement security headers and Content Security Policy (CSP), Audit API security, Check for sensitive data exposure, Validate input sanitization and output encoding, Assess dependency security and supply chain risks. Tools and techniques: OWASP ZAP, Burp Suite, security linters, dependency scanners, manual code review. Always provide specific, actionable security recommendations with priority levels.
13
+
14
+ GIT WORKFLOW RULES (CRITICAL):
15
+
16
+ - NEVER push directly to main branch - ALWAYS use pull requests
17
+ - NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'
18
+ - ALWAYS clean up test files, documentation files, and temporary artifacts before committing
19
+ - ALWAYS ensure git history maintains production quality - no test commits, no debugging code
20
+ - ALWAYS create descriptive commit messages following project conventions
21
+ - ALWAYS run tests and build before creating PR
@@ -1,110 +1,102 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
2
- import { Command } from 'commander'
3
- import { registerChatCommands } from '../../src/commands/chat'
4
- import { ChatService } from '../../src/services/chat-service'
5
- import { DefaultApiKeyManager } from '../../src/utils/default-api-key'
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { Command } from "commander";
3
+ import { registerChatCommands } from "../../src/commands/chat";
4
+ import { ChatService } from "../../src/services/chat-service";
5
+ import { DefaultApiKeyManager } from "../../src/utils/default-api-key";
6
6
 
7
7
  // Mock dependencies
8
- vi.mock('../../src/services/chat-service')
9
- vi.mock('../../src/utils/default-api-key')
10
- vi.mock('readline', () => ({
8
+ vi.mock("../../src/services/chat-service");
9
+ vi.mock("../../src/utils/default-api-key");
10
+ vi.mock("readline", () => ({
11
11
  createInterface: vi.fn(() => ({
12
12
  question: vi.fn(),
13
13
  close: vi.fn(),
14
14
  })),
15
- }))
15
+ }));
16
16
 
17
- describe('Chat Commands', () => {
18
- let program: Command
19
- let mockChatService: any
20
- let mockDefaultApiKeyManager: any
17
+ describe("Chat Commands", () => {
18
+ let program: Command;
19
+ let mockChatService: any;
20
+ let mockDefaultApiKeyManager: any;
21
21
 
22
22
  beforeEach(() => {
23
- program = new Command()
23
+ program = new Command();
24
24
 
25
25
  // Mock ChatService
26
26
  mockChatService = {
27
27
  createCompletion: vi.fn(),
28
28
  listModels: vi.fn(),
29
- }
30
- vi.mocked(ChatService.getInstance).mockReturnValue(mockChatService)
29
+ };
30
+ vi.mocked(ChatService.getInstance).mockReturnValue(mockChatService);
31
31
 
32
32
  // Mock DefaultApiKeyManager
33
33
  mockDefaultApiKeyManager = {
34
34
  getDefaultApiKeyData: vi.fn(),
35
35
  promptForDefaultApiKey: vi.fn(),
36
- }
37
- vi.mocked(DefaultApiKeyManager.getInstance).mockReturnValue(
38
- mockDefaultApiKeyManager,
39
- )
36
+ };
37
+ vi.mocked(DefaultApiKeyManager.getInstance).mockReturnValue(mockDefaultApiKeyManager);
40
38
 
41
- registerChatCommands(program)
42
- })
39
+ registerChatCommands(program);
40
+ });
43
41
 
44
42
  afterEach(() => {
45
- vi.clearAllMocks()
46
- })
43
+ vi.clearAllMocks();
44
+ });
47
45
 
48
- describe('chat run command', () => {
49
- it('should use berget/glm-4.7 as default model', () => {
50
- const chatCommand = program.commands.find((cmd) => cmd.name() === 'chat')
51
- const runCommand = chatCommand?.commands.find(
52
- (cmd) => cmd.name() === 'run',
53
- )
46
+ describe("chat run command", () => {
47
+ it("should use berget/glm-4.7 as default model", () => {
48
+ const chatCommand = program.commands.find(cmd => cmd.name() === "chat");
49
+ const runCommand = chatCommand?.commands.find(cmd => cmd.name() === "run");
54
50
 
55
- expect(runCommand).toBeDefined()
51
+ expect(runCommand).toBeDefined();
56
52
 
57
53
  // Check the help text which contains the default model
58
- const helpText = runCommand?.helpInformation()
59
- expect(helpText).toContain('glm-4.7')
60
- })
54
+ const helpText = runCommand?.helpInformation();
55
+ expect(helpText).toContain("glm-4.7");
56
+ });
61
57
 
62
- it('should have streaming enabled by default', () => {
63
- const chatCommand = program.commands.find((cmd) => cmd.name() === 'chat')
64
- const runCommand = chatCommand?.commands.find(
65
- (cmd) => cmd.name() === 'run',
66
- )
58
+ it("should have streaming enabled by default", () => {
59
+ const chatCommand = program.commands.find(cmd => cmd.name() === "chat");
60
+ const runCommand = chatCommand?.commands.find(cmd => cmd.name() === "run");
67
61
 
68
- expect(runCommand).toBeDefined()
62
+ expect(runCommand).toBeDefined();
69
63
 
70
64
  // Check that the option is --no-stream (meaning streaming is default)
71
- const streamOption = runCommand?.options.find(
72
- (opt) => opt.long === '--no-stream',
73
- )
74
- expect(streamOption).toBeDefined()
75
- expect(streamOption?.description).toContain('Disable streaming')
76
- })
77
-
78
- it('should create completion with correct default options', async () => {
65
+ const streamOption = runCommand?.options.find(opt => opt.long === "--no-stream");
66
+ expect(streamOption).toBeDefined();
67
+ expect(streamOption?.description).toContain("Disable streaming");
68
+ });
69
+
70
+ it("should create completion with correct default options", async () => {
79
71
  // Mock API key
80
- process.env.BERGET_API_KEY = 'test-key'
72
+ process.env.BERGET_API_KEY = "test-key";
81
73
 
82
74
  // Mock successful completion
83
75
  mockChatService.createCompletion.mockResolvedValue({
84
76
  choices: [
85
77
  {
86
- message: { content: 'Test response' },
78
+ message: { content: "Test response" },
87
79
  },
88
80
  ],
89
- })
81
+ });
90
82
 
91
83
  // This would normally test the actual command execution
92
84
  // but since it involves readline interaction, we just verify
93
85
  // that the service would be called with correct defaults
94
- expect(mockChatService.createCompletion).not.toHaveBeenCalled()
86
+ expect(mockChatService.createCompletion).not.toHaveBeenCalled();
95
87
 
96
88
  // Clean up
97
- delete process.env.BERGET_API_KEY
98
- })
99
- })
89
+ delete process.env.BERGET_API_KEY;
90
+ });
91
+ });
100
92
 
101
- describe('chat list command', () => {
102
- it('should list available models', async () => {
93
+ describe("chat list command", () => {
94
+ it("should list available models", async () => {
103
95
  const mockModels = {
104
96
  data: [
105
97
  {
106
- id: 'gpt-oss',
107
- owned_by: 'openai',
98
+ id: "gpt-oss",
99
+ owned_by: "openai",
108
100
  active: true,
109
101
  capabilities: {
110
102
  vision: false,
@@ -113,17 +105,15 @@ describe('Chat Commands', () => {
113
105
  },
114
106
  },
115
107
  ],
116
- }
108
+ };
117
109
 
118
- mockChatService.listModels.mockResolvedValue(mockModels)
110
+ mockChatService.listModels.mockResolvedValue(mockModels);
119
111
 
120
- const chatCommand = program.commands.find((cmd) => cmd.name() === 'chat')
121
- const listCommand = chatCommand?.commands.find(
122
- (cmd) => cmd.name() === 'list',
123
- )
112
+ const chatCommand = program.commands.find(cmd => cmd.name() === "chat");
113
+ const listCommand = chatCommand?.commands.find(cmd => cmd.name() === "list");
124
114
 
125
- expect(listCommand).toBeDefined()
126
- expect(listCommand?.description()).toBe('List available chat models')
127
- })
128
- })
129
- })
115
+ expect(listCommand).toBeDefined();
116
+ expect(listCommand?.description()).toBe("List available chat models");
117
+ });
118
+ });
119
+ });