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.
- package/.github/workflows/publish.yml +8 -8
- package/.github/workflows/test.yml +12 -6
- package/.husky/pre-commit +1 -0
- package/.prettierignore +15 -0
- package/.prettierrc +5 -3
- package/CONTRIBUTING.md +38 -0
- package/README.md +2 -148
- package/dist/index.js +21 -21
- package/dist/package.json +30 -2
- package/dist/src/agents/app.js +28 -0
- package/dist/src/agents/backend.js +25 -0
- package/dist/src/agents/devops.js +34 -0
- package/dist/src/agents/frontend.js +25 -0
- package/dist/src/agents/fullstack.js +25 -0
- package/dist/src/agents/index.js +61 -0
- package/dist/src/agents/quality.js +70 -0
- package/dist/src/agents/security.js +26 -0
- package/dist/src/agents/types.js +2 -0
- package/dist/src/client.js +54 -62
- package/dist/src/commands/api-keys.js +132 -140
- package/dist/src/commands/auth.js +9 -9
- package/dist/src/commands/autocomplete.js +9 -9
- package/dist/src/commands/billing.js +7 -9
- package/dist/src/commands/chat.js +90 -92
- package/dist/src/commands/clusters.js +12 -12
- package/dist/src/commands/code/__tests__/auth-sync.test.js +348 -0
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +23 -0
- package/dist/src/commands/code/__tests__/fake-auth-service.js +55 -0
- package/dist/src/commands/code/__tests__/fake-command-runner.js +50 -0
- package/dist/src/commands/code/__tests__/fake-file-store.js +55 -0
- package/dist/src/commands/code/__tests__/fake-prompter.js +133 -0
- package/dist/src/commands/code/__tests__/setup-flow.test.js +505 -0
- package/dist/src/commands/code/adapters/clack-prompter.js +81 -0
- package/dist/src/commands/code/adapters/fs-file-store.js +80 -0
- package/dist/src/commands/code/adapters/spawn-command-runner.js +53 -0
- package/dist/src/commands/code/auth-sync.js +283 -0
- package/dist/src/commands/code/errors.js +27 -0
- package/dist/src/commands/code/ports/auth-services.js +2 -0
- package/dist/src/commands/code/ports/command-runner.js +2 -0
- package/dist/src/commands/code/ports/file-store.js +2 -0
- package/dist/src/commands/code/ports/prompter.js +2 -0
- package/dist/src/commands/code/setup.js +533 -0
- package/dist/src/commands/code.js +223 -779
- package/dist/src/commands/models.js +13 -15
- package/dist/src/commands/users.js +6 -8
- package/dist/src/constants/command-structure.js +116 -114
- package/dist/src/services/api-key-service.js +43 -48
- package/dist/src/services/auth-service.js +60 -299
- package/dist/src/services/browser-auth.js +278 -0
- package/dist/src/services/chat-service.js +78 -91
- package/dist/src/services/cluster-service.js +6 -6
- package/dist/src/services/collaborator-service.js +5 -8
- package/dist/src/services/flux-service.js +5 -8
- package/dist/src/services/helm-service.js +5 -8
- package/dist/src/services/kubectl-service.js +7 -10
- package/dist/src/utils/config-checker.js +5 -5
- package/dist/src/utils/config-loader.js +25 -25
- package/dist/src/utils/default-api-key.js +23 -23
- package/dist/src/utils/env-manager.js +7 -7
- package/dist/src/utils/error-handler.js +60 -61
- package/dist/src/utils/logger.js +7 -7
- package/dist/src/utils/markdown-renderer.js +2 -2
- package/dist/src/utils/opencode-validator.js +17 -20
- package/dist/src/utils/token-manager.js +38 -11
- package/dist/tests/commands/chat.test.js +24 -24
- package/dist/tests/commands/code.test.js +169 -138
- package/dist/tests/utils/config-loader.test.js +114 -114
- package/dist/tests/utils/env-manager.test.js +57 -57
- package/dist/tests/utils/opencode-validator.test.js +44 -43
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +47 -0
- package/index.ts +42 -48
- package/package.json +30 -2
- package/src/agents/app.ts +27 -0
- package/src/agents/backend.ts +24 -0
- package/src/agents/devops.ts +33 -0
- package/src/agents/frontend.ts +24 -0
- package/src/agents/fullstack.ts +24 -0
- package/src/agents/index.ts +71 -0
- package/src/agents/quality.ts +69 -0
- package/src/agents/security.ts +26 -0
- package/src/agents/types.ts +17 -0
- package/src/client.ts +125 -167
- package/src/commands/api-keys.ts +261 -358
- package/src/commands/auth.ts +24 -30
- package/src/commands/autocomplete.ts +12 -12
- package/src/commands/billing.ts +22 -27
- package/src/commands/chat.ts +230 -323
- package/src/commands/clusters.ts +33 -33
- package/src/commands/code/__tests__/auth-sync.test.ts +481 -0
- package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
- package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
- package/src/commands/code/__tests__/fake-command-runner.ts +44 -0
- package/src/commands/code/__tests__/fake-file-store.ts +44 -0
- package/src/commands/code/__tests__/fake-prompter.ts +121 -0
- package/src/commands/code/__tests__/setup-flow.test.ts +628 -0
- package/src/commands/code/adapters/clack-prompter.ts +55 -0
- package/src/commands/code/adapters/fs-file-store.ts +37 -0
- package/src/commands/code/adapters/spawn-command-runner.ts +40 -0
- package/src/commands/code/auth-sync.ts +329 -0
- package/src/commands/code/errors.ts +23 -0
- package/src/commands/code/ports/auth-services.ts +14 -0
- package/src/commands/code/ports/command-runner.ts +10 -0
- package/src/commands/code/ports/file-store.ts +7 -0
- package/src/commands/code/ports/prompter.ts +29 -0
- package/src/commands/code/setup.ts +630 -0
- package/src/commands/code.ts +335 -1074
- package/src/commands/index.ts +19 -19
- package/src/commands/models.ts +32 -37
- package/src/commands/users.ts +15 -22
- package/src/constants/command-structure.ts +120 -140
- package/src/services/api-key-service.ts +96 -113
- package/src/services/auth-service.ts +92 -339
- package/src/services/browser-auth.ts +296 -0
- package/src/services/chat-service.ts +246 -279
- package/src/services/cluster-service.ts +29 -32
- package/src/services/collaborator-service.ts +13 -18
- package/src/services/flux-service.ts +16 -18
- package/src/services/helm-service.ts +16 -18
- package/src/services/kubectl-service.ts +12 -14
- package/src/types/api.d.ts +924 -926
- package/src/types/json.d.ts +3 -3
- package/src/utils/config-checker.ts +10 -10
- package/src/utils/config-loader.ts +110 -127
- package/src/utils/default-api-key.ts +81 -93
- package/src/utils/env-manager.ts +36 -40
- package/src/utils/error-handler.ts +83 -78
- package/src/utils/logger.ts +41 -41
- package/src/utils/markdown-renderer.ts +11 -11
- package/src/utils/opencode-validator.ts +51 -56
- package/src/utils/token-manager.ts +84 -64
- package/templates/agents/app.md +23 -0
- package/templates/agents/backend.md +23 -0
- package/templates/agents/devops.md +30 -0
- package/templates/agents/frontend.md +25 -0
- package/templates/agents/fullstack.md +23 -0
- package/templates/agents/quality.md +69 -0
- package/templates/agents/security.md +21 -0
- package/tests/commands/chat.test.ts +60 -70
- package/tests/commands/code.test.ts +346 -345
- package/tests/utils/config-loader.test.ts +260 -260
- package/tests/utils/env-manager.test.ts +127 -134
- package/tests/utils/opencode-validator.test.ts +65 -69
- package/tsconfig.json +2 -2
- package/vitest.config.ts +3 -3
- package/AGENTS.md +0 -374
- package/TODO.md +0 -19
- package/opencode.json +0 -146
|
@@ -1,38 +1,57 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import * as path from
|
|
3
|
-
import * as os from
|
|
4
|
-
import
|
|
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(),
|
|
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,
|
|
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,
|
|
45
|
-
this.tokenData = JSON.parse(data)
|
|
63
|
+
const data = fs.readFileSync(this.tokenFilePath, "utf8");
|
|
64
|
+
this.tokenData = JSON.parse(data);
|
|
46
65
|
}
|
|
47
|
-
} catch
|
|
48
|
-
logger.error(
|
|
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
|
|
72
|
-
logger.error(
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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:
|
|
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 =
|
|
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
|
|
2
|
-
import { Command } from
|
|
3
|
-
import { registerChatCommands } from
|
|
4
|
-
import { ChatService } from
|
|
5
|
-
import { DefaultApiKeyManager } from
|
|
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(
|
|
9
|
-
vi.mock(
|
|
10
|
-
vi.mock(
|
|
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(
|
|
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(
|
|
49
|
-
it(
|
|
50
|
-
const chatCommand = program.commands.find(
|
|
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(
|
|
60
|
-
})
|
|
54
|
+
const helpText = runCommand?.helpInformation();
|
|
55
|
+
expect(helpText).toContain("glm-4.7");
|
|
56
|
+
});
|
|
61
57
|
|
|
62
|
-
it(
|
|
63
|
-
const chatCommand = program.commands.find(
|
|
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
|
-
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
|
|
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 =
|
|
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:
|
|
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(
|
|
102
|
-
it(
|
|
93
|
+
describe("chat list command", () => {
|
|
94
|
+
it("should list available models", async () => {
|
|
103
95
|
const mockModels = {
|
|
104
96
|
data: [
|
|
105
97
|
{
|
|
106
|
-
id:
|
|
107
|
-
owned_by:
|
|
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(
|
|
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(
|
|
127
|
-
})
|
|
128
|
-
})
|
|
129
|
-
})
|
|
115
|
+
expect(listCommand).toBeDefined();
|
|
116
|
+
expect(listCommand?.description()).toBe("List available chat models");
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|