@magic-ingredients/tiny-brain-local 0.21.1 → 0.22.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 (38) hide show
  1. package/dist/core/mcp-server.d.ts +3 -5
  2. package/dist/core/mcp-server.d.ts.map +1 -1
  3. package/dist/core/mcp-server.js +17 -38
  4. package/dist/index.d.ts +0 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/prompts/rules/rules.prompt.d.ts.map +1 -1
  8. package/dist/prompts/rules/rules.prompt.js +1 -7
  9. package/dist/services/analyse-service.js +1 -1
  10. package/dist/services/credential-storage.service.d.ts +5 -44
  11. package/dist/services/credential-storage.service.d.ts.map +1 -1
  12. package/dist/services/credential-storage.service.js +4 -197
  13. package/dist/services/remote/auth-token-service.d.ts +10 -47
  14. package/dist/services/remote/auth-token-service.d.ts.map +1 -1
  15. package/dist/services/remote/auth-token-service.js +13 -188
  16. package/dist/services/repo-service.d.ts.map +1 -1
  17. package/dist/services/repo-service.js +14 -13
  18. package/dist/tools/config/config.tool.d.ts.map +1 -1
  19. package/dist/tools/config/config.tool.js +18 -0
  20. package/dist/tools/persona/as.tool.d.ts +0 -12
  21. package/dist/tools/persona/as.tool.d.ts.map +1 -1
  22. package/dist/tools/persona/as.tool.js +13 -199
  23. package/dist/tools/plan/plan.tool.d.ts.map +1 -1
  24. package/dist/tools/plan/plan.tool.js +28 -40
  25. package/dist/tools/quality/quality.tool.js +4 -4
  26. package/dist/tools/recommendations/recommendations.tool.d.ts.map +1 -1
  27. package/dist/tools/recommendations/recommendations.tool.js +11 -1
  28. package/dist/tools/tool-registry.d.ts.map +1 -1
  29. package/dist/tools/tool-registry.js +0 -4
  30. package/dist/types/local-context.d.ts +0 -2
  31. package/dist/types/local-context.d.ts.map +1 -1
  32. package/package.json +2 -2
  33. package/dist/services/dashboard-launcher.service.d.ts +0 -21
  34. package/dist/services/dashboard-launcher.service.d.ts.map +0 -1
  35. package/dist/services/dashboard-launcher.service.js +0 -33
  36. package/dist/tools/dashboard/dashboard.tool.d.ts +0 -15
  37. package/dist/tools/dashboard/dashboard.tool.d.ts.map +0 -1
  38. package/dist/tools/dashboard/dashboard.tool.js +0 -81
@@ -1,194 +1,19 @@
1
1
  /**
2
- * Auth Token Service
2
+ * Auth Token Service (re-exported from core)
3
3
  *
4
- * Exchanges credentials for authentication tokens to access protected APIs
4
+ * This module re-exports the core AuthTokenService and adapts
5
+ * it for MCP usage with MCPConfig.
5
6
  */
6
- /* global fetch, AbortSignal */
7
- import { BaseService, getTBSUrl } from '@magic-ingredients/tiny-brain-core';
8
- export class AuthTokenService extends BaseService {
9
- baseUrl;
10
- authToken = null;
11
- config;
7
+ import { AuthTokenService as CoreAuthTokenService } from '@magic-ingredients/tiny-brain-core';
8
+ /**
9
+ * MCP-specific AuthTokenService that accepts MCPConfig
10
+ * and extracts credentials from config.account
11
+ */
12
+ export class AuthTokenService extends CoreAuthTokenService {
12
13
  constructor(context, config) {
13
- super(context);
14
- this.config = config;
15
- // Use central configuration for URL
16
- this.baseUrl = getTBSUrl();
17
- // Debug logging
18
- this.log('debug', '[AuthTokenService] Constructor initialized', {
19
- baseUrl: this.baseUrl,
20
- fromEnv: !!process.env.TBS_URL,
21
- hasConfig: !!config,
22
- hasAccountConfig: !!config?.account
23
- });
24
- }
25
- /**
26
- * Validate that credentials are properly configured
27
- */
28
- validateCredentials() {
29
- // Check if config exists
30
- if (!this.config) {
31
- return {
32
- isValid: false,
33
- error: 'No configuration provided'
34
- };
35
- }
36
- // Check if account section exists
37
- if (!this.config.account) {
38
- return {
39
- isValid: false,
40
- error: 'No account configuration found'
41
- };
42
- }
43
- const { clientId, clientSecret } = this.config.account;
44
- // Check if clientId exists and is non-empty
45
- if (!clientId || clientId.trim() === '') {
46
- return {
47
- isValid: false,
48
- error: 'Client ID is missing or empty'
49
- };
50
- }
51
- // Check if clientSecret exists and is non-empty
52
- if (!clientSecret || clientSecret.trim() === '') {
53
- return {
54
- isValid: false,
55
- error: 'Client secret is missing or empty'
56
- };
57
- }
58
- // Valid credentials found
59
- return {
60
- isValid: true,
61
- clientId,
62
- clientSecret
63
- };
64
- }
65
- /**
66
- * Connect to the auth service and obtain a token
67
- * Assumes credentials have been validated
68
- */
69
- async connectToAuthService(clientId, clientSecret) {
70
- try {
71
- this.log('debug', 'Attempting to connect to tiny-brain service', {
72
- url: this.baseUrl,
73
- clientId: clientId.substring(0, 4) + '***' // Log partial ID for debugging
74
- });
75
- // Create Basic Auth credentials
76
- const authCredentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
77
- // Attempt to get auth token from TBS using Basic Auth
78
- const response = await fetch(`${this.baseUrl}/api/auth/token`, {
79
- method: 'POST',
80
- headers: {
81
- 'Authorization': `Basic ${authCredentials}`,
82
- 'Content-Type': 'application/json',
83
- },
84
- // Add timeout
85
- signal: AbortSignal.timeout(10000) // 10 second timeout
86
- });
87
- if (!response.ok) {
88
- this.log('warn', 'Authentication failed', {
89
- status: response.status,
90
- statusText: response.statusText
91
- });
92
- // Provide specific error messages
93
- if (response.status === 401) {
94
- this.log('error', 'Invalid credentials - please check your clientId and clientSecret');
95
- }
96
- else if (response.status === 403) {
97
- this.log('error', 'Access forbidden - your account may be suspended');
98
- }
99
- else if (response.status >= 500) {
100
- this.log('error', 'Tiny-brain service is temporarily unavailable');
101
- }
102
- return null;
103
- }
104
- const tokenData = await response.json();
105
- // Validate token response
106
- if (!tokenData.access_token || !tokenData.expires_in) {
107
- this.log('error', 'Invalid token response from auth service');
108
- return null;
109
- }
110
- // Store token with expiry
111
- this.authToken = {
112
- token: tokenData.access_token,
113
- expiresIn: tokenData.expires_in,
114
- expiresAt: new Date(Date.now() + tokenData.expires_in * 1000)
115
- };
116
- this.log('info', 'Successfully authenticated with tiny-brain service', {
117
- expiresIn: tokenData.expires_in,
118
- userId: tokenData.user_id
119
- });
120
- return this.authToken;
121
- }
122
- catch (error) {
123
- // Handle different error types
124
- if (error instanceof Error) {
125
- if (error.name === 'AbortError') {
126
- this.log('error', 'Connection timeout - tiny-brain service took too long to respond');
127
- }
128
- else if (error.message.includes('fetch')) {
129
- this.log('error', 'Network error - unable to reach tiny-brain service');
130
- }
131
- else {
132
- this.log('error', 'Failed to connect to auth service', error);
133
- }
134
- }
135
- else {
136
- this.log('error', 'Unknown error during authentication', error);
137
- }
138
- return null;
139
- }
140
- }
141
- /**
142
- * Main authentication flow - validate and connect if valid
143
- */
144
- async authenticate() {
145
- // First validate credentials
146
- const validation = this.validateCredentials();
147
- if (!validation.isValid) {
148
- this.log('info', 'Skipping authentication: ' + validation.error);
149
- return null;
150
- }
151
- // Credentials are valid, attempt to connect
152
- this.log('info', 'Valid credentials found, attempting authentication');
153
- // We know these are defined when isValid is true, use type assertion
154
- return await this.connectToAuthService(validation.clientId, validation.clientSecret);
155
- }
156
- /**
157
- * Get auth headers for API requests
158
- */
159
- getAuthHeaders() {
160
- if (!this.authToken) {
161
- return null;
162
- }
163
- // Check if token is expired
164
- if (this.authToken.expiresAt && this.authToken.expiresAt < new Date()) {
165
- this.log('debug', 'Auth token expired');
166
- this.authToken = null;
167
- return null;
168
- }
169
- return {
170
- 'Authorization': `Bearer ${this.authToken.token}`,
171
- 'Content-Type': 'application/json'
172
- };
173
- }
174
- /**
175
- * Check if currently authenticated
176
- */
177
- isAuthenticated() {
178
- return !!(this.authToken &&
179
- (!this.authToken.expiresAt || this.authToken.expiresAt > new Date()));
180
- }
181
- /**
182
- * Clear authentication
183
- */
184
- clearAuth() {
185
- this.authToken = null;
186
- this.log('debug', 'Authentication cleared');
187
- }
188
- /**
189
- * Get current token (for debugging/testing)
190
- */
191
- getToken() {
192
- return this.authToken;
14
+ const credentials = config?.account
15
+ ? { clientId: config.account.clientId, clientSecret: config.account.clientSecret }
16
+ : undefined;
17
+ super(context, credentials);
193
18
  }
194
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"repo-service.d.ts","sourceRoot":"","sources":["../../src/services/repo-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAIlE,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,qBAAa,WAAW;IAMV,OAAO,CAAC,OAAO;IAL3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAA2B;IACnE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAyB;IAE/D,OAAO,CAAC,aAAa,CAAgB;gBAEjB,OAAO,EAAE,cAAc;IAS3C;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACG,4BAA4B,CAChC,eAAe,GAAE,MAAoB,GACpC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAmCzB;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAOvD;;OAEG;IACH,cAAc,IAAI,OAAO;IAUzB;;;OAGG;IACG,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;IAcjD;;OAEG;IACH,kBAAkB,IAAI,MAAM;IAa5B;;;;;;;;OAQG;IACG,qBAAqB,CAAC,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkGtF;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IA6BhC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAgGlC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA4ChC;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAiErC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA6BlC;;OAEG;IACH,OAAO,CAAC,gCAAgC;CAyCzC"}
1
+ {"version":3,"file":"repo-service.d.ts","sourceRoot":"","sources":["../../src/services/repo-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAIlE,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,qBAAa,WAAW;IAMV,OAAO,CAAC,OAAO;IAL3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAA2B;IACnE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAyB;IAE/D,OAAO,CAAC,aAAa,CAAgB;gBAEjB,OAAO,EAAE,cAAc;IAS3C;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACG,4BAA4B,CAChC,eAAe,GAAE,MAAoB,GACpC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAmCzB;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAOvD;;OAEG;IACH,cAAc,IAAI,OAAO;IAUzB;;;OAGG;IACG,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;IAcjD;;OAEG;IACH,kBAAkB,IAAI,MAAM;IAa5B;;;;;;;;OAQG;IACG,qBAAqB,CAAC,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkGtF;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IA6BhC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAgGlC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA6ChC;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAiErC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA6BlC;;OAEG;IACH,OAAO,CAAC,gCAAgC;CAyCzC"}
@@ -266,7 +266,7 @@ Only skip delegation for trivial tasks (single-line fixes, config changes).
266
266
  **BEFORE EVERY COMMIT**, check if you're working on tracked work:
267
267
 
268
268
  1. **Active PRDs?** Check \`.tiny-brain/progress/\` for \`in_progress\` status
269
- 2. **Open Fixes?** Check \`.tiny-brain/fixes/progress.json\` for \`documented\` or \`in_progress\` status
269
+ 2. **Open Fixes?** Check \`.tiny-brain/fixes/progress.json\` for \`not_started\` or \`in_progress\` status
270
270
 
271
271
  **If YES, you MUST include tracking headers or the commit will be REJECTED.**
272
272
 
@@ -305,7 +305,7 @@ Description of changes...
305
305
 
306
306
  ### Fix Status Workflow
307
307
 
308
- Fix documents have three statuses: \`documented\` → \`in_progress\` → \`resolved\`
308
+ Fix documents have four statuses: \`not_started\` → \`in_progress\` → \`completed\` | \`superseded\`
309
309
 
310
310
  **When starting work on a fix:**
311
311
  1. Open the fix file: \`.tiny-brain/fixes/{fix-id}.md\`
@@ -329,12 +329,12 @@ Fix documents have three statuses: \`documented\` → \`in_progress\` → \`reso
329
329
  4. Run: \`npx tiny-brain sync-progress .tiny-brain/fixes/{fix-id}.md\`
330
330
 
331
331
  **When all tasks are complete:**
332
- 1. **ONLY set \`status: resolved\`** when ALL tasks are accounted for:
332
+ 1. **ONLY set \`status: completed\`** when ALL tasks are accounted for:
333
333
  - 100% of tasks must have either \`status: completed\` (with commitSha) or \`status: superseded\`
334
- - Example: A fix with 5 tasks could be: 3 completed + 2 superseded = resolved
334
+ - Example: A fix with 5 tasks could be: 3 completed + 2 superseded = completed
335
335
  - A fix with incomplete tasks stays \`in_progress\`
336
336
  2. Update YAML frontmatter:
337
- - Set \`status: resolved\`
337
+ - Set \`status: completed\`
338
338
  - Set \`resolved: YYYY-MM-DDTHH:mm:ss.sssZ\` (ISO timestamp)
339
339
  - Add \`resolution\` object:
340
340
  \`\`\`yaml
@@ -365,37 +365,38 @@ IMPORTANT: This repository follows strict Test-Driven Development (TDD) with a 3
365
365
 
366
366
  1. **Red Phase** (\`test:\` or \`test(scope):\` commits):
367
367
  - **CRITICAL — you MUST run before writing tests:**
368
- \`npx tiny-brain update-phase --phase red --event start --task '...' [--fix ID | --prd ID --feature ID]\`
368
+ \`npx tiny-brain task-start --task '...' [--fix ID | --prd ID --feature ID]\`
369
+ - For tasks that don't need tests (config, shell scripts, docs, templates), skip RED:
370
+ \`npx tiny-brain task-start --task '...' [--fix ID | --prd ID --feature ID] --phase green\`
371
+ This sets \`testCommitSha: 'skipped'\` and goes straight to Green phase.
369
372
  - Write failing tests first
370
373
  - Tests SHOULD fail (that's the point!)
371
374
  - Use: \`git commit -m "test: ..."\` or \`git commit -m "test(api): ..."\`
372
375
  - Git hook automatically runs typecheck + lint but SKIPS tests
373
376
  - Tracked in: \`testCommitSha\` field
374
- - **After commit:** post-commit hook automatically transitions to \`green:start\`
375
377
 
376
378
  2. **Green Phase** (\`feat:\` or \`feat(scope):\` commits):
377
- - Phase started automatically by post-commit hook after \`test:\` commit (no manual step needed)
379
+ - Phase is derived automatically from commit SHAs (no manual step needed)
378
380
  - Implement minimum code to make tests pass
379
381
  - Use: \`git commit -m "feat: ..."\` or \`git commit -m "feat(auth): ..."\`
380
382
  - Git hook automatically runs full checks (typecheck + lint + test)
381
383
  - Tracked in: \`commitSha\` field
382
- - **Marks task as COMPLETED**
383
- - **After commit:** adversarial review hook sets \`adversarial:start\` and outputs review instructions
384
+ - **After commit:** adversarial review starts automatically
384
385
 
385
386
  3. **Refactor Phase** (\`refactor:\` or \`refactor(scope):\` commits - optional):
386
387
  - Triggered by adversarial review suggestions
387
388
  - Improve code quality without changing behavior
388
389
  - Use: \`git commit -m "refactor: ..."\` or \`git commit -m "refactor(plan): ..."\`
389
390
  - Git hook automatically runs full checks (typecheck + lint + test)
390
- - **After commit:** post-commit hook automatically transitions \`adversarial-refactor:complete\`
391
+ - **After commit:** post-commit hook automatically records refactor completion
391
392
 
392
393
  4. **Progress Commit** (end of cycle):
393
394
  - After the full RED→GREEN→REFACTOR cycle is complete
394
395
  - Run: \`npx tiny-brain commit-progress\`
395
396
 
396
- **What you must do manually:** Only \`update-phase --phase red --event start\` before writing tests. Everything else is automated by hooks.
397
+ **What you must do manually:** Only \`task-start\` before writing tests. Everything else is automated by hooks.
397
398
 
398
- **What you must NOT do:** Do not manually call \`update-phase\` for green:start, adversarial:start, or adversarial-refactor:complete the hooks handle these.
399
+ **What you must NOT do:** Do not manually manage phase transitions they are derived from commit SHAs.
399
400
 
400
401
  `;
401
402
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/config/config.tool.ts"],"names":[],"mappings":"AACA,OAAO,EAA0C,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAUrE,qBAAa,UAAU;IACrB,MAAM,CAAC,iBAAiB,IAAI,OAAO;WA4DtB,OAAO,CAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC;mBA6BD,UAAU;mBAwCV,SAAS;mBAoCT,iBAAiB;mBAgEjB,SAAS;mBAuDT,WAAW;CAqBjC"}
1
+ {"version":3,"file":"config.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/config/config.tool.ts"],"names":[],"mappings":"AACA,OAAO,EAA0C,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAUrE,qBAAa,UAAU;IACrB,MAAM,CAAC,iBAAiB,IAAI,OAAO;WAkEtB,OAAO,CAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC;mBA6BD,UAAU;mBA8CV,SAAS;mBAoCT,iBAAiB;mBAsEjB,SAAS;mBAuDT,WAAW;CAqBjC"}
@@ -24,10 +24,16 @@ export class ConfigTool {
24
24
 
25
25
  📊 PREFERENCES:
26
26
  • autoCommitProgress - Auto-commit progress.json changes
27
+ • autoArchive - Auto-archive completed PRDs and resolved fixes during sync
27
28
  • enableAgenticCoding - Enable AI-driven development mode
28
29
  • enableSDD - Enable Suggestion-Driven Development
29
30
  • enableTDD - Enable Test-Driven Development
31
+ • combineRedGreenCommits - Combine red+green into a single feat: commit
32
+ • enableAdversarial - Enable adversarial review phase in TDD pipeline
30
33
  • enableADR - Enable Architecture Decision Records
34
+ • enableQuality - Enable Quality Analysis
35
+ • manageAgentsMd - Manage AGENTS.md file during analyse
36
+ • defaultPersona - Default persona to activate at session start
31
37
  • docsDirectory - Documentation directory path
32
38
  • adrDirectory - ADR directory path
33
39
  • prdDirectory - PRD directory path
@@ -102,10 +108,16 @@ export class ConfigTool {
102
108
  '',
103
109
  '## Feature Flags',
104
110
  `- **autoCommitProgress**: ${repo.autoCommitProgress}`,
111
+ `- **autoArchive**: ${repo.autoArchive}`,
105
112
  `- **enableAgenticCoding**: ${repo.enableAgenticCoding}`,
106
113
  `- **enableSDD**: ${repo.enableSDD}`,
107
114
  `- **enableTDD**: ${repo.enableTDD}`,
115
+ `- **combineRedGreenCommits**: ${repo.combineRedGreenCommits}`,
116
+ `- **enableAdversarial**: ${repo.enableAdversarial}`,
108
117
  `- **enableADR**: ${repo.enableADR}`,
118
+ `- **enableQuality**: ${repo.enableQuality}`,
119
+ `- **manageAgentsMd**: ${repo.manageAgentsMd}`,
120
+ `- **defaultPersona**: ${repo.defaultPersona}`,
109
121
  '',
110
122
  '## Directory Paths',
111
123
  `- **docsDirectory**: \`${repo.directories.docs}\``,
@@ -188,10 +200,16 @@ export class ConfigTool {
188
200
  '',
189
201
  '## Feature Flags',
190
202
  `- **autoCommitProgress**: ${merged.repo.autoCommitProgress} _(${getSource('autoCommitProgress')})_`,
203
+ `- **autoArchive**: ${merged.repo.autoArchive} _(${getSource('autoArchive')})_`,
191
204
  `- **enableAgenticCoding**: ${merged.repo.enableAgenticCoding} _(${getSource('enableAgenticCoding')})_`,
192
205
  `- **enableSDD**: ${merged.repo.enableSDD} _(${getSource('enableSDD')})_`,
193
206
  `- **enableTDD**: ${merged.repo.enableTDD} _(${getSource('enableTDD')})_`,
207
+ `- **combineRedGreenCommits**: ${merged.repo.combineRedGreenCommits} _(${getSource('combineRedGreenCommits')})_`,
208
+ `- **enableAdversarial**: ${merged.repo.enableAdversarial} _(${getSource('enableAdversarial')})_`,
194
209
  `- **enableADR**: ${merged.repo.enableADR} _(${getSource('enableADR')})_`,
210
+ `- **enableQuality**: ${merged.repo.enableQuality} _(${getSource('enableQuality')})_`,
211
+ `- **manageAgentsMd**: ${merged.repo.manageAgentsMd} _(${getSource('manageAgentsMd')})_`,
212
+ `- **defaultPersona**: ${merged.repo.defaultPersona} _(${getSource('defaultPersona')})_`,
195
213
  '',
196
214
  '## Directory Paths',
197
215
  `- **docsDirectory**: \`${merged.repo.directories.docs}\` _(${getDirSource('docs')})_`,
@@ -37,18 +37,6 @@ export declare class AsTool {
37
37
  * Check if agents are installed by checking the platform-specific agents directory
38
38
  */
39
39
  private static checkHasAgents;
40
- /**
41
- * Resolve feature flags from ConfigService
42
- */
43
- private static resolveFeatureFlags;
44
- /**
45
- * Build persona response data structure
46
- */
47
- private static buildResponse;
48
- /**
49
- * Format the persona response as human-readable markdown with enforced workflow
50
- */
51
- private static formatResponse;
52
40
  /**
53
41
  * Handle renaming an existing persona
54
42
  */
@@ -1 +1 @@
1
- {"version":3,"file":"as.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/persona/as.tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAYrE,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAyCrE;;;;;;;;;GASG;AACH,qBAAa,MAAM;IACjB,MAAM,CAAC,iBAAiB,IAAI,OAAO;IAqDnC;;OAEG;WACU,OAAO,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IA4BvF;;OAEG;mBACkB,wBAAwB;IAsE7C;;OAEG;mBACkB,mBAAmB;IA8ExC;;OAEG;mBACkB,oBAAoB;IAsGzC;;OAEG;mBACkB,eAAe;IAsBpC;;OAEG;mBACkB,cAAc;IAWnC;;OAEG;mBACkB,mBAAmB;IAexC;;OAEG;mBACkB,aAAa;IA6DlC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IA8H7B;;OAEG;mBACkB,mBAAmB;mBA4DnB,qBAAqB;CAsC3C"}
1
+ {"version":3,"file":"as.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/persona/as.tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAWrE,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAYrE;;;;;;;;;GASG;AACH,qBAAa,MAAM;IACjB,MAAM,CAAC,iBAAiB,IAAI,OAAO;IAqDnC;;OAEG;WACU,OAAO,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IA4BvF;;OAEG;mBACkB,wBAAwB;IAsE7C;;OAEG;mBACkB,mBAAmB;IA8ExC;;OAEG;mBACkB,oBAAoB;IAsGzC;;OAEG;mBACkB,eAAe;IAsBpC;;OAEG;mBACkB,cAAc;IAYnC;;OAEG;mBACkB,mBAAmB;mBA4DnB,qBAAqB;CAsC3C"}
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { createErrorResult } from '../index.js';
3
- import { createDefaultProfile, PlanningService, RulesService, LibraryClient, ConfigService, parsePersonaMarkdown } from '@magic-ingredients/tiny-brain-core';
3
+ import { createDefaultProfile, LibraryClient, parsePersonaMarkdown, buildPersonaContext, formatPersonaContext, resolveFeatureFlags, } from '@magic-ingredients/tiny-brain-core';
4
4
  import { PersonaService } from '../../services/persona-service.js';
5
5
  import { RepoService } from '../../services/repo-service.js';
6
6
  import { AgentService } from '../../services/agent-service.js';
@@ -143,15 +143,15 @@ export class AsTool {
143
143
  const agentService = new AgentService(context);
144
144
  const formatter = agentService.getDefaultFormatter();
145
145
  const contextFilePath = formatter.getRepoContextFilePath();
146
- const response = await AsTool.buildResponse(context, parsedPersona, contextFilePath);
146
+ const response = await buildPersonaContext(context, parsedPersona);
147
147
  const [hasAgents, featureFlags] = await Promise.all([
148
148
  AsTool.checkHasAgents(context, contextFilePath),
149
- AsTool.resolveFeatureFlags(context),
149
+ resolveFeatureFlags(context),
150
150
  ]);
151
151
  return {
152
152
  content: [{
153
153
  type: 'text',
154
- text: AsTool.formatResponse(response, contextFilePath, hasAgents, featureFlags, context.libraryAuth),
154
+ text: formatPersonaContext(response, contextFilePath, hasAgents, featureFlags, context.libraryAuth),
155
155
  }],
156
156
  isError: false,
157
157
  };
@@ -200,15 +200,15 @@ export class AsTool {
200
200
  const contextFilePath = formatter?.getRepoContextFilePath();
201
201
  // Activate persona and return response with updated context
202
202
  const updatedContext = await AsTool.activatePersona(personaName, profile, context);
203
- const response = await AsTool.buildResponse(updatedContext, parsedPersona, contextFilePath);
203
+ const response = await buildPersonaContext(updatedContext, parsedPersona);
204
204
  const [hasAgents, featureFlags] = await Promise.all([
205
205
  AsTool.checkHasAgents(updatedContext, contextFilePath),
206
- AsTool.resolveFeatureFlags(updatedContext),
206
+ resolveFeatureFlags(updatedContext),
207
207
  ]);
208
208
  return {
209
209
  content: [{
210
210
  type: 'text',
211
- text: AsTool.formatResponse(response, contextFilePath, hasAgents, featureFlags, updatedContext.libraryAuth),
211
+ text: formatPersonaContext(response, contextFilePath, hasAgents, featureFlags, updatedContext.libraryAuth),
212
212
  }],
213
213
  isError: false,
214
214
  };
@@ -282,15 +282,15 @@ export class AsTool {
282
282
  const formatter = agentService.getDefaultFormatter();
283
283
  const contextFilePath = formatter?.getRepoContextFilePath();
284
284
  // Step 8: Build and return response
285
- const response = await AsTool.buildResponse(updatedContext, parsedPersona, contextFilePath);
285
+ const response = await buildPersonaContext(updatedContext, parsedPersona);
286
286
  const [hasAgents, featureFlags] = await Promise.all([
287
287
  AsTool.checkHasAgents(updatedContext, contextFilePath),
288
- AsTool.resolveFeatureFlags(updatedContext),
288
+ resolveFeatureFlags(updatedContext),
289
289
  ]);
290
290
  return {
291
291
  content: [{
292
292
  type: 'text',
293
- text: AsTool.formatResponse(response, contextFilePath, hasAgents, featureFlags, updatedContext.libraryAuth) + needsInitMessage,
293
+ text: formatPersonaContext(response, contextFilePath, hasAgents, featureFlags, updatedContext.libraryAuth) + needsInitMessage,
294
294
  }],
295
295
  isError: false,
296
296
  };
@@ -333,192 +333,6 @@ export class AsTool {
333
333
  return false;
334
334
  }
335
335
  }
336
- /**
337
- * Resolve feature flags from ConfigService
338
- */
339
- static async resolveFeatureFlags(context) {
340
- try {
341
- const configService = new ConfigService(context);
342
- const [sddEnabled, tddEnabled, adrEnabled, agenticCodingEnabled] = await Promise.all([
343
- configService.isSDDEnabled(),
344
- configService.isTDDEnabled(),
345
- configService.isADREnabled(),
346
- configService.isAgenticCodingEnabled(),
347
- ]);
348
- return { sddEnabled, tddEnabled, adrEnabled, agenticCodingEnabled };
349
- }
350
- catch {
351
- return { sddEnabled: false, tddEnabled: false, adrEnabled: false, agenticCodingEnabled: false };
352
- }
353
- }
354
- /**
355
- * Build persona response data structure
356
- */
357
- static async buildResponse(context, parsedPersona, _contextFilePath // Used in formatResponse, not here
358
- ) {
359
- const rulesService = new RulesService(context);
360
- const planningService = new PlanningService(context);
361
- // Get additional rules from rules service
362
- const allRules = await rulesService.getAllRules(parsedPersona.name);
363
- const additionalUserRules = allRules?.rules || [];
364
- // Get active plan if any
365
- let activePlan = null;
366
- try {
367
- activePlan = await planningService.getActivePlan();
368
- }
369
- catch {
370
- context.logger.debug('No active persona for plan lookup');
371
- }
372
- // Note: contextFilePath is passed but not used here since we removed AGENT USAGE INSTRUCTIONS
373
- // It's used later in formatResponse to add the reminder to re-read CLAUDE.md
374
- const response = {
375
- PERSONA: {
376
- name: parsedPersona.name,
377
- "GOLDEN RULES": additionalUserRules,
378
- "USER RULES": parsedPersona.user?.rules || [],
379
- "USER DETAILS": parsedPersona.user?.details || {},
380
- "SYSTEM RULES": parsedPersona.system?.rules || [],
381
- "SYSTEM DETAILS": parsedPersona.system?.details || {}
382
- }
383
- };
384
- if (activePlan) {
385
- // Extract just the first line or a short summary of the plan
386
- let goalSummary = '';
387
- if (activePlan.overview) {
388
- const lines = activePlan.overview.split('\n').filter(l => l.trim());
389
- goalSummary = lines[0] || activePlan.title;
390
- // Truncate if still too long
391
- if (goalSummary.length > 200) {
392
- goalSummary = goalSummary.substring(0, 197) + '...';
393
- }
394
- }
395
- else {
396
- goalSummary = activePlan.title;
397
- }
398
- response.ACTIVE_PLAN = {
399
- id: activePlan.id,
400
- title: activePlan.title,
401
- goal: goalSummary,
402
- current_phase: activePlan.currentState?.featureTitle || '',
403
- next_actions: activePlan.currentState?.nextAction ? [activePlan.currentState.nextAction.description] : [],
404
- blockers: activePlan.metadata?.blockers?.map((b) => b.description)
405
- };
406
- }
407
- return response;
408
- }
409
- /**
410
- * Format the persona response as human-readable markdown with enforced workflow
411
- */
412
- static formatResponse(response, contextFilePath, hasAgents, featureFlags, libraryAuth) {
413
- let formatted = `# THIS IS YOUR CONTEXT FOR THIS SESSION - YOU MUST USE IT\n\n`;
414
- formatted += `IMPORTANT: USER RULES and USER DETAILS ALWAYS take precedence over SYSTEM RULES and SYSTEM DETAILS. When there are conflicts, follow the user's preferences.\n\n`;
415
- formatted += `## PERSONA: ${response.PERSONA.name}\n\n`;
416
- // Golden Rules
417
- if (response.PERSONA["GOLDEN RULES"] && response.PERSONA["GOLDEN RULES"].length > 0) {
418
- formatted += `### GOLDEN RULES\n`;
419
- response.PERSONA["GOLDEN RULES"].forEach(rule => {
420
- formatted += `- ${rule}\n`;
421
- });
422
- formatted += '\n';
423
- }
424
- // User Rules
425
- if (response.PERSONA["USER RULES"] && response.PERSONA["USER RULES"].length > 0) {
426
- formatted += `### USER RULES\n`;
427
- response.PERSONA["USER RULES"].forEach(rule => {
428
- formatted += `- ${rule}\n`;
429
- });
430
- formatted += '\n';
431
- }
432
- // User Details
433
- const userDetails = response.PERSONA["USER DETAILS"];
434
- if (userDetails && Object.keys(userDetails).length > 0) {
435
- formatted += `### USER DETAILS\n`;
436
- Object.entries(userDetails).forEach(([key, value]) => {
437
- formatted += `#### ${key}\n${value}\n\n`;
438
- });
439
- }
440
- // System Rules
441
- if (response.PERSONA["SYSTEM RULES"] && response.PERSONA["SYSTEM RULES"].length > 0) {
442
- formatted += `### SYSTEM RULES\n`;
443
- response.PERSONA["SYSTEM RULES"].forEach(rule => {
444
- formatted += `- ${rule}\n`;
445
- });
446
- formatted += '\n';
447
- }
448
- // System Details
449
- const systemDetails = response.PERSONA["SYSTEM DETAILS"];
450
- if (systemDetails && Object.keys(systemDetails).length > 0) {
451
- formatted += `### SYSTEM DETAILS\n`;
452
- Object.entries(systemDetails).forEach(([key, value]) => {
453
- formatted += `#### ${key}\n${value}\n\n`;
454
- });
455
- }
456
- // Active Plan - Keep it concise
457
- if (response.ACTIVE_PLAN) {
458
- formatted += `### ACTIVE PLAN\n`;
459
- formatted += `**${response.ACTIVE_PLAN.title}**\n`;
460
- if (response.ACTIVE_PLAN.goal && response.ACTIVE_PLAN.goal !== response.ACTIVE_PLAN.title) {
461
- formatted += `${response.ACTIVE_PLAN.goal}\n`;
462
- }
463
- formatted += `\n`;
464
- formatted += `- Current Phase: ${response.ACTIVE_PLAN.current_phase || 'Not specified'}\n`;
465
- if (response.ACTIVE_PLAN.next_actions && response.ACTIVE_PLAN.next_actions.length > 0) {
466
- formatted += `- Next Action: ${response.ACTIVE_PLAN.next_actions[0]}\n`;
467
- }
468
- if (response.ACTIVE_PLAN.blockers && response.ACTIVE_PLAN.blockers.length > 0) {
469
- formatted += `- Blockers: ${response.ACTIVE_PLAN.blockers.length} active\n`;
470
- }
471
- }
472
- // REFLECTION REQUIREMENT
473
- formatted += `\n## 🔍 MANDATORY REFLECTION - AFTER EVERY TOOL USE\n\n`;
474
- formatted += `After using ANY tool, you MUST ask yourself:\n`;
475
- formatted += `- Was this the right phase for this action?\n`;
476
- formatted += `- Should I have used a specialized agent instead?\n`;
477
- formatted += `- Am I following the checklist workflow?\n`;
478
- formatted += `- Does this align with the user's actual request?\n\n`;
479
- // Add reminder to re-read context file if in a repo with agents installed
480
- if (contextFilePath) {
481
- formatted += `---\n\n`;
482
- formatted += `**⚡ AGENT ORCHESTRATION REQUIRED**\n\n`;
483
- formatted += `**RE-READ ${contextFilePath} to understand available agents for this repository.**\n`;
484
- formatted += `**DEFAULT BEHAVIOR**: Use Task tool with specialized agents\n`;
485
- formatted += `**EXCEPTION**: Only skip agents if you can explicitly justify why none apply\n\n`;
486
- formatted += `**MANDATORY CONFIRMATION:** When ready to proceed, you MUST respond with exactly this format (copy it exactly, do not modify):\n\n`;
487
- formatted += `\`\`\`\n`;
488
- formatted += ` 🧠 tiny brain\n\n`;
489
- formatted += ` ✅ Received context for persona\n`;
490
- formatted += ` ✅ Re-read ${contextFilePath}\n`;
491
- if (featureFlags?.sddEnabled) {
492
- formatted += ` ✅ Enabled Spec Driven Design workflow with Test Driven Development\n`;
493
- }
494
- if (featureFlags?.adrEnabled) {
495
- formatted += ` ✅ Enabled Architecture Decision Records\n`;
496
- }
497
- if (featureFlags?.agenticCodingEnabled) {
498
- formatted += ` ✅ Enabled agentic coding\n`;
499
- }
500
- if (libraryAuth?.token) {
501
- formatted += ` 🔑 Connected to Tiny Brain Remote\n`;
502
- }
503
- if (libraryAuth?.hasClaudeCli) {
504
- formatted += ` 🤖 Claude CLI authenticated\n`;
505
- }
506
- // Only show pre-flight and agent-first workflow if agents are installed
507
- if (hasAgents) {
508
- formatted += ` ✅ Completed pre-flight checklist\n`;
509
- formatted += ` ✅ Ready for agent-first workflow\n`;
510
- }
511
- formatted += `\`\`\`\n\n`;
512
- formatted += `I've switched to your **${response.PERSONA.name}** persona.\n\n`;
513
- formatted += `🧠 Dashboard available at: [http://localhost:8765](http://localhost:8765)\n`;
514
- }
515
- else {
516
- // Add persona switch confirmation message when no context file
517
- formatted += `I've switched to your **${response.PERSONA.name}** persona.\n\n`;
518
- formatted += `🧠 Dashboard available at: [http://localhost:8765](http://localhost:8765)\n`;
519
- }
520
- return formatted;
521
- }
522
336
  /**
523
337
  * Handle renaming an existing persona
524
338
  */
@@ -546,15 +360,15 @@ export class AsTool {
546
360
  const updatedContext = await AsTool.activatePersona(newName, profile, context);
547
361
  context.logger.info(`Renamed persona '${oldName}' to '${newName}'`);
548
362
  // Return response showing the renamed persona is now active with updated context
549
- const response = await AsTool.buildResponse(updatedContext, parsedPersona, contextFilePath);
363
+ const response = await buildPersonaContext(updatedContext, parsedPersona);
550
364
  const [hasAgents, featureFlags] = await Promise.all([
551
365
  AsTool.checkHasAgents(updatedContext, contextFilePath),
552
- AsTool.resolveFeatureFlags(updatedContext),
366
+ resolveFeatureFlags(updatedContext),
553
367
  ]);
554
368
  return {
555
369
  content: [{
556
370
  type: 'text',
557
- text: AsTool.formatResponse(response, contextFilePath, hasAgents, featureFlags, updatedContext.libraryAuth),
371
+ text: formatPersonaContext(response, contextFilePath, hasAgents, featureFlags, updatedContext.libraryAuth),
558
372
  }],
559
373
  isError: false,
560
374
  };
@@ -1 +1 @@
1
- {"version":3,"file":"plan.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/plan/plan.tool.ts"],"names":[],"mappings":"AACA,OAAO,EAA0C,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAsLrE,qBAAa,QAAQ;IACnB,MAAM,CAAC,iBAAiB,IAAI,OAAO;WAgLtB,OAAO,CAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC;mBAyED,YAAY;mBA+LZ,aAAa;mBAyCb,eAAe;mBA0Df,YAAY;mBA+CZ,YAAY;mBAsDZ,YAAY;mBAiCZ,UAAU;mBA2DV,oBAAoB;mBA+CpB,mBAAmB;mBAsBnB,qBAAqB;mBAqCrB,SAAS;mBAgGT,eAAe;IAuFpC;;OAEG;mBACkB,UAAU;CA2DhC"}
1
+ {"version":3,"file":"plan.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/plan/plan.tool.ts"],"names":[],"mappings":"AACA,OAAO,EAA0C,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAsLrE,qBAAa,QAAQ;IACnB,MAAM,CAAC,iBAAiB,IAAI,OAAO;WAgLtB,OAAO,CAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC;mBAyED,YAAY;mBA+LZ,aAAa;mBAyCb,eAAe;mBA0Df,YAAY;mBA+CZ,YAAY;mBAsDZ,YAAY;mBAiCZ,UAAU;mBA2DV,oBAAoB;mBAsBpB,mBAAmB;mBAkBnB,qBAAqB;mBA4CrB,SAAS;mBAgGT,eAAe;IAuFpC;;OAEG;mBACkB,UAAU;CA2DhC"}