From 292efc8c03d109e7d11aa06fe6de8ad6c4d9702d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 00:33:44 +0000 Subject: [PATCH 01/10] Initial plan From 26a0480a8de8fb98b47f5be2aecf17f82bc1ce8d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 00:41:19 +0000 Subject: [PATCH 02/10] fix(ids): use crypto-based collision-safe ID generation Co-authored-by: marcuscastelo <27441558+marcuscastelo@users.noreply.github.com> Agent-Logs-Url: https://github.com/marcuscastelo/macroflows/sessions/e1cd678e-e254-49ee-9b3e-0efbe4cb0a58 --- .../clipboard/domain/clipboardEntry.ts | 3 +- .../domain/tests/clipboardPayloadExt.test.ts | 51 +++++--------- .../application/idRegeneration.ts | 7 +- .../import-export/tests/importExport.test.ts | 11 +-- src/modules/toast/domain/toastTypes.ts | 4 +- src/shared/modal/core/modalManager.ts | 3 +- src/shared/utils/idUtils.ts | 4 +- src/shared/utils/uniqueId.ts | 70 +++++++++++++++++++ 8 files changed, 107 insertions(+), 46 deletions(-) create mode 100644 src/shared/utils/uniqueId.ts diff --git a/src/modules/clipboard/domain/clipboardEntry.ts b/src/modules/clipboard/domain/clipboardEntry.ts index 5281b20b0..7a0da94c8 100644 --- a/src/modules/clipboard/domain/clipboardEntry.ts +++ b/src/modules/clipboard/domain/clipboardEntry.ts @@ -3,6 +3,7 @@ import { z } from 'zod/v4' import { type Item, itemSchema } from '~/modules/diet/item/schema/itemSchema' import { type Meal, mealSchema } from '~/modules/diet/meal/domain/meal' import { type Recipe, recipeSchema } from '~/modules/diet/recipe/domain/recipe' +import { generateUuid } from '~/shared/utils/uniqueId' export const clipboardPayloadSchema = z.union([ itemSchema, @@ -38,7 +39,7 @@ export function createClipboardEntry( options?: { pinned?: boolean }, ): ClipboardEntry { return { - id: `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`, + id: generateUuid(), payload, createdAt: Date.now(), pinned: options?.pinned ?? false, diff --git a/src/modules/clipboard/domain/tests/clipboardPayloadExt.test.ts b/src/modules/clipboard/domain/tests/clipboardPayloadExt.test.ts index 1d1be466f..b2482b3ef 100644 --- a/src/modules/clipboard/domain/tests/clipboardPayloadExt.test.ts +++ b/src/modules/clipboard/domain/tests/clipboardPayloadExt.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' +import { describe, expect, it } from 'vitest' import { ClipboardPayloadExt } from '~/modules/clipboard/domain/clipboardPayloadExt' import { createItem, type Item } from '~/modules/diet/item/schema/itemSchema' @@ -25,34 +25,23 @@ const createFoodItem = (id: number, name: string, quantity: number): Item => }, }) -const expectRegeneratedId = ( - actualId: number, - originalId: number, - randomValue: number, -): void => { - expect(actualId).toBe(Math.round(randomValue * 1000000)) +const expectRegeneratedId = (actualId: number, originalId: number): void => { expect(actualId).not.toBe(originalId) - expect(Number.isInteger(actualId)).toBe(true) + expect(Number.isSafeInteger(actualId)).toBe(true) } describe('ClipboardPayloadExt', () => { - afterEach(() => { - vi.restoreAllMocks() - }) - describe('extractItems', () => { it('extracts a single item and regenerates its id while preserving content', () => { const payload = createFoodItem(10, 'Banana', 150) - vi.spyOn(Math, 'random').mockReturnValueOnce(0.123456) - const extractedItems = ClipboardPayloadExt.extractItems(payload) expect(extractedItems).toHaveLength(1) - expectRegeneratedId(extractedItems[0]!.id, payload.id, 0.123456) + expectRegeneratedId(extractedItems[0]!.id, payload.id) expect(extractedItems[0]).toEqual({ ...payload, - id: 123456, + id: extractedItems[0]!.id, }) expect(payload.id).toBe(10) }) @@ -66,18 +55,17 @@ describe('ClipboardPayloadExt', () => { id: 1, }) - vi.spyOn(Math, 'random') - .mockReturnValueOnce(0.111111) - .mockReturnValueOnce(0.222222) - const extractedItems = ClipboardPayloadExt.extractItems(payload) expect(extractedItems).toHaveLength(2) - expectRegeneratedId(extractedItems[0]!.id, items[0]!.id, 0.111111) - expectRegeneratedId(extractedItems[1]!.id, items[1]!.id, 0.222222) + expectRegeneratedId(extractedItems[0]!.id, items[0]!.id) + expectRegeneratedId(extractedItems[1]!.id, items[1]!.id) + expect(new Set(extractedItems.map((item) => item.id)).size).toBe( + extractedItems.length, + ) expect(extractedItems).toEqual([ - { ...items[0], id: 111111 }, - { ...items[1], id: 222222 }, + { ...items[0], id: extractedItems[0]!.id }, + { ...items[1], id: extractedItems[1]!.id }, ]) expect(payload.items).toEqual(items) }) @@ -97,18 +85,17 @@ describe('ClipboardPayloadExt', () => { { id: 2 }, ) - vi.spyOn(Math, 'random') - .mockReturnValueOnce(0.333333) - .mockReturnValueOnce(0.444444) - const extractedItems = ClipboardPayloadExt.extractItems(payload) expect(extractedItems).toHaveLength(2) - expectRegeneratedId(extractedItems[0]!.id, items[0]!.id, 0.333333) - expectRegeneratedId(extractedItems[1]!.id, items[1]!.id, 0.444444) + expectRegeneratedId(extractedItems[0]!.id, items[0]!.id) + expectRegeneratedId(extractedItems[1]!.id, items[1]!.id) + expect(new Set(extractedItems.map((item) => item.id)).size).toBe( + extractedItems.length, + ) expect(extractedItems).toEqual([ - { ...items[0], id: 333333 }, - { ...items[1], id: 444444 }, + { ...items[0], id: extractedItems[0]!.id }, + { ...items[1], id: extractedItems[1]!.id }, ]) expect(payload.items).toEqual(items) }) diff --git a/src/modules/import-export/application/idRegeneration.ts b/src/modules/import-export/application/idRegeneration.ts index 792026d76..fcd24b8ac 100644 --- a/src/modules/import-export/application/idRegeneration.ts +++ b/src/modules/import-export/application/idRegeneration.ts @@ -9,16 +9,13 @@ import { isMealExportPayload, isRecipeExportPayload, } from '~/modules/import-export/domain/exportPayload' +import { generateNumericId } from '~/shared/utils/uniqueId' /** * Generates a unique ID for imported data to avoid collisions. - * Uses timestamp + random component for uniqueness. */ export function generateUniqueId(): number { - // Use last 9 digits of timestamp + 3 random digits - const timestamp = Date.now() % 1_000_000_000 - const random = Math.floor(Math.random() * 1000) - return timestamp * 1000 + random + return generateNumericId() } /** diff --git a/src/modules/import-export/tests/importExport.test.ts b/src/modules/import-export/tests/importExport.test.ts index fbb7faf6e..f3ec003ef 100644 --- a/src/modules/import-export/tests/importExport.test.ts +++ b/src/modules/import-export/tests/importExport.test.ts @@ -23,11 +23,12 @@ import { EXPORT_SCHEMA_VERSION } from '~/modules/import-export/domain/exportPayl describe('Import/Export Module', () => { describe('ID Regeneration', () => { it('should generate unique IDs', () => { - const id1 = generateUniqueId() - const id2 = generateUniqueId() - expect(id1).not.toBe(id2) - expect(typeof id1).toBe('number') - expect(typeof id2).toBe('number') + const generatedIds = Array.from({ length: 100_000 }, () => + generateUniqueId(), + ) + + expect(new Set(generatedIds).size).toBe(generatedIds.length) + expect(generatedIds.every((id) => Number.isSafeInteger(id))).toBe(true) }) it('should regenerate meal IDs', () => { diff --git a/src/modules/toast/domain/toastTypes.ts b/src/modules/toast/domain/toastTypes.ts index 30152d9ad..783b80611 100644 --- a/src/modules/toast/domain/toastTypes.ts +++ b/src/modules/toast/domain/toastTypes.ts @@ -1,3 +1,5 @@ +import { generateUuid } from '~/shared/utils/uniqueId' + /** * Toast System Configuration * @@ -157,7 +159,7 @@ export function createToastItem( options: ToastItem['options'], ): ToastItem { return { - id: `toast_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`, + id: `toast_${generateUuid()}`, message, options, timestamp: Date.now(), diff --git a/src/shared/modal/core/modalManager.ts b/src/shared/modal/core/modalManager.ts index e66b85817..44286f156 100644 --- a/src/shared/modal/core/modalManager.ts +++ b/src/shared/modal/core/modalManager.ts @@ -7,11 +7,12 @@ import type { ModalState, } from '~/shared/modal/types/modalTypes' import { logging } from '~/shared/utils/logging' +import { generateUuid } from '~/shared/utils/uniqueId' export const [modals, setModals] = createSignal([]) function generateModalId(): ModalId { - return `modal-${Date.now()}-${Math.random().toString(36).slice(2, 11)}` + return `modal-${generateUuid()}` } function performClose(id: ModalId, modal: ModalState): void { diff --git a/src/shared/utils/idUtils.ts b/src/shared/utils/idUtils.ts index 6ebd82c3b..54cbc40f1 100644 --- a/src/shared/utils/idUtils.ts +++ b/src/shared/utils/idUtils.ts @@ -1,12 +1,14 @@ // TODO: remove id utils and find a way to generate ids in the database +import { generateNumericId } from '~/shared/utils/uniqueId' + type Identifiable = { id: number } /** * @deprecated Probably should be done by database */ export function generateId(): number { - return Math.round(Math.random() * 1000000) + return generateNumericId() } /** diff --git a/src/shared/utils/uniqueId.ts b/src/shared/utils/uniqueId.ts new file mode 100644 index 000000000..b5ecdbba8 --- /dev/null +++ b/src/shared/utils/uniqueId.ts @@ -0,0 +1,70 @@ +const NUMERIC_ID_COUNTER_LIMIT = 2 ** 29 +const NUMERIC_ID_PREFIX_LIMIT = 2 ** 24 + +let numericIdCounter = 0 +let numericIdPrefix: number | undefined + +function getCryptoApi(): Crypto { + if (!('crypto' in globalThis)) { + throw new Error('Crypto API is required for ID generation') + } + + return globalThis.crypto +} + +function createNumericIdPrefix(): number { + const values = new Uint32Array(1) + getCryptoApi().getRandomValues(values) + + return values[0]! % NUMERIC_ID_PREFIX_LIMIT +} + +function formatUuidV4(bytes: Uint8Array): string { + bytes[6] = (bytes[6]! & 0x0f) | 0x40 + bytes[8] = (bytes[8]! & 0x3f) | 0x80 + + const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')) + + return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}` +} + +/** + * Generates a collision-safe UUID string using the Web Crypto API. + * + * @returns An RFC 4122 version 4 UUID. + */ +export function generateUuid(): string { + const cryptoApi = getCryptoApi() + + if (typeof cryptoApi.randomUUID === 'function') { + return cryptoApi.randomUUID() + } + + const bytes = new Uint8Array(16) + cryptoApi.getRandomValues(bytes) + + return formatUuidV4(bytes) +} + +/** + * Generates a collision-safe numeric identifier for client-side entities that + * still require `number` IDs. + * + * @returns A safe integer unique for this runtime, with a cryptographically + * random prefix to reduce cross-runtime collisions. + */ +export function generateNumericId(): number { + if (numericIdCounter >= NUMERIC_ID_COUNTER_LIMIT) { + throw new Error('Numeric ID counter exhausted') + } + + if (numericIdPrefix === undefined) { + numericIdPrefix = createNumericIdPrefix() + } + + const nextId = + numericIdPrefix * NUMERIC_ID_COUNTER_LIMIT + numericIdCounter + 1 + numericIdCounter += 1 + + return nextId +} From 26c7bc0f3dacbb6c6d7a32ee68d13a02538393db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 00:45:50 +0000 Subject: [PATCH 03/10] chore: finalize collision-safe ID generation follow-ups Co-authored-by: marcuscastelo <27441558+marcuscastelo@users.noreply.github.com> Agent-Logs-Url: https://github.com/marcuscastelo/macroflows/sessions/e1cd678e-e254-49ee-9b3e-0efbe4cb0a58 --- src/shared/utils/uniqueId.ts | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/shared/utils/uniqueId.ts b/src/shared/utils/uniqueId.ts index b5ecdbba8..18700784f 100644 --- a/src/shared/utils/uniqueId.ts +++ b/src/shared/utils/uniqueId.ts @@ -1,4 +1,17 @@ +/** + * Reserve 29 bits for the per-runtime counter, which allows more than + * 500 million unique numeric IDs before exhaustion while keeping the final + * value inside JavaScript's safe integer range once combined with the + * runtime prefix. + */ const NUMERIC_ID_COUNTER_LIMIT = 2 ** 29 + +/** + * Reserve the remaining 24 safe-integer bits for a cryptographically-random + * runtime prefix so separate execution contexts are unlikely to share the same + * numeric ID space. Together with the counter bits above, this keeps + * `prefix * NUMERIC_ID_COUNTER_LIMIT + counter` below `Number.MAX_SAFE_INTEGER`. + */ const NUMERIC_ID_PREFIX_LIMIT = 2 ** 24 let numericIdCounter = 0 @@ -6,7 +19,9 @@ let numericIdPrefix: number | undefined function getCryptoApi(): Crypto { if (!('crypto' in globalThis)) { - throw new Error('Crypto API is required for ID generation') + throw new Error( + 'Crypto API is required for ID generation. Use a modern browser or Node.js runtime with Web Crypto support.', + ) } return globalThis.crypto @@ -14,7 +29,14 @@ function getCryptoApi(): Crypto { function createNumericIdPrefix(): number { const values = new Uint32Array(1) - getCryptoApi().getRandomValues(values) + const maxUnbiasedValue = + Math.floor(2 ** 32 / NUMERIC_ID_PREFIX_LIMIT) * NUMERIC_ID_PREFIX_LIMIT + + do { + // Reject the top incomplete range so the modulo result stays uniform across + // the full prefix space instead of favoring lower values. + getCryptoApi().getRandomValues(values) + } while (values[0]! >= maxUnbiasedValue) return values[0]! % NUMERIC_ID_PREFIX_LIMIT } @@ -55,7 +77,9 @@ export function generateUuid(): string { */ export function generateNumericId(): number { if (numericIdCounter >= NUMERIC_ID_COUNTER_LIMIT) { - throw new Error('Numeric ID counter exhausted') + throw new Error( + 'Numeric ID counter exhausted after more than 500 million IDs in one runtime. Restart the application to reset the local counter.', + ) } if (numericIdPrefix === undefined) { From 2e285a02d53b15b15d428549360a57f4b0e0a89b Mon Sep 17 00:00:00 2001 From: marcuscastelo Date: Wed, 25 Mar 2026 07:21:35 -0300 Subject: [PATCH 04/10] feat(ids): introduce zero-based counter and safe integer composition --- src/shared/utils/uniqueId.test.ts | 64 +++++++++++++++++++++++++++++++ src/shared/utils/uniqueId.ts | 19 +++++++-- 2 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 src/shared/utils/uniqueId.test.ts diff --git a/src/shared/utils/uniqueId.test.ts b/src/shared/utils/uniqueId.test.ts new file mode 100644 index 000000000..dd905ac4a --- /dev/null +++ b/src/shared/utils/uniqueId.test.ts @@ -0,0 +1,64 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +import { composeNumericId } from '~/shared/utils/uniqueId' + +let previousCrypto: Crypto | undefined + +describe('composeNumericId', () => { + it('keeps the largest possible numeric ID within the safe integer range', () => { + const maxPrefix = 2 ** 24 - 1 + const maxCounter = 2 ** 29 - 1 + + const numericId = composeNumericId(maxPrefix, maxCounter) + + expect(numericId).toBe(Number.MAX_SAFE_INTEGER) + expect(Number.isSafeInteger(numericId)).toBe(true) + }) + + it('allows zero as the first numeric ID', () => { + expect(composeNumericId(0, 0)).toBe(0) + }) +}) + +describe('generateNumericId', () => { + beforeEach(() => { + previousCrypto = globalThis.crypto + vi.resetModules() + + const cryptoStub: Pick = { + getRandomValues: (values: T): T => { + if (values instanceof Uint32Array) { + values[0] = 2 ** 24 - 1 + } + + return values + }, + randomUUID: vi.fn(), + } + + vi.stubGlobal('crypto', cryptoStub) + }) + + afterEach(() => { + if (previousCrypto === undefined) { + Reflect.deleteProperty(globalThis, 'crypto') + } else { + vi.stubGlobal('crypto', previousCrypto) + } + + vi.resetModules() + }) + + it('uses a zero-based counter for generated values', async () => { + const { generateNumericId } = await import('~/shared/utils/uniqueId') + const maxPrefix = 2 ** 24 - 1 + + const firstId = generateNumericId() + const secondId = generateNumericId() + + expect(firstId).toBe(composeNumericId(maxPrefix, 0)) + expect(secondId).toBe(firstId + 1) + expect(Number.isSafeInteger(firstId)).toBe(true) + expect(Number.isSafeInteger(secondId)).toBe(true) + }) +}) diff --git a/src/shared/utils/uniqueId.ts b/src/shared/utils/uniqueId.ts index 18700784f..991e2d2ef 100644 --- a/src/shared/utils/uniqueId.ts +++ b/src/shared/utils/uniqueId.ts @@ -10,7 +10,8 @@ const NUMERIC_ID_COUNTER_LIMIT = 2 ** 29 * Reserve the remaining 24 safe-integer bits for a cryptographically-random * runtime prefix so separate execution contexts are unlikely to share the same * numeric ID space. Together with the counter bits above, this keeps - * `prefix * NUMERIC_ID_COUNTER_LIMIT + counter` below `Number.MAX_SAFE_INTEGER`. + * `prefix * NUMERIC_ID_COUNTER_LIMIT + counter` at or below + * `Number.MAX_SAFE_INTEGER`. */ const NUMERIC_ID_PREFIX_LIMIT = 2 ** 24 @@ -50,6 +51,19 @@ function formatUuidV4(bytes: Uint8Array): string { return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}` } +/** + * Combines the runtime prefix and the per-runtime counter into a safe integer. + * + * The counter is zero-based so the largest possible value stays within + * `Number.MAX_SAFE_INTEGER`. + */ +export function composeNumericId( + numericIdPrefix: number, + numericIdCounter: number, +): number { + return numericIdPrefix * NUMERIC_ID_COUNTER_LIMIT + numericIdCounter +} + /** * Generates a collision-safe UUID string using the Web Crypto API. * @@ -86,8 +100,7 @@ export function generateNumericId(): number { numericIdPrefix = createNumericIdPrefix() } - const nextId = - numericIdPrefix * NUMERIC_ID_COUNTER_LIMIT + numericIdCounter + 1 + const nextId = composeNumericId(numericIdPrefix, numericIdCounter) numericIdCounter += 1 return nextId From 272a25d9692dd127fd13dff3fc7fba10a0ba1168 Mon Sep 17 00:00:00 2001 From: marcuscastelo Date: Wed, 25 Mar 2026 07:45:55 -0300 Subject: [PATCH 05/10] chore(serena): remove all serena agent memory and configuration files --- .serena/.gitignore | 1 - .../agent-context-handoff-protocol.md | 309 ------------------ .../memories/architecture_and_structure.md | 54 --- .../memories/code_style_and_conventions.md | 46 --- .serena/memories/developer-guidelines.md | 32 -- .serena/memories/development_workflow.md | 52 --- .serena/memories/diet_module_structure.md | 104 ------ .../error-analysis-test-structure-fix.md | 34 -- .serena/memories/file-movement-policy.md | 21 -- .../issue-creation-workflow-optimization.md | 32 -- .serena/memories/lessons-learned.md | 12 - .serena/memories/module-map.md | 25 -- .serena/memories/other_modules_structure.md | 86 ----- .../memories/project-context-macroflows.md | 14 - .serena/memories/project_overview.md | 27 -- .../memories/recipe-editing-limitations.md | 27 -- .serena/memories/repository-pattern.md | 150 --------- .serena/memories/sections_ui_structure.md | 122 ------- .serena/memories/suggested_commands.md | 97 ------ .../memories/test_documentation_framework.md | 155 --------- .../memories/testing_and_quality_commands.md | 42 --- .../todo-issue-relationship-pattern.md | 20 -- .serena/memories/typescript-patterns.md | 56 ---- .../usecase-pattern-migration-complete.md | 100 ------ .serena/memories/workflow-and-commands.md | 39 --- .../workflow-optimization-patterns.md | 136 -------- .serena/project.yml | 150 --------- 27 files changed, 1943 deletions(-) delete mode 100644 .serena/.gitignore delete mode 100644 .serena/memories/agent-context-handoff-protocol.md delete mode 100644 .serena/memories/architecture_and_structure.md delete mode 100644 .serena/memories/code_style_and_conventions.md delete mode 100644 .serena/memories/developer-guidelines.md delete mode 100644 .serena/memories/development_workflow.md delete mode 100644 .serena/memories/diet_module_structure.md delete mode 100644 .serena/memories/error-analysis-test-structure-fix.md delete mode 100644 .serena/memories/file-movement-policy.md delete mode 100644 .serena/memories/issue-creation-workflow-optimization.md delete mode 100644 .serena/memories/lessons-learned.md delete mode 100644 .serena/memories/module-map.md delete mode 100644 .serena/memories/other_modules_structure.md delete mode 100644 .serena/memories/project-context-macroflows.md delete mode 100644 .serena/memories/project_overview.md delete mode 100644 .serena/memories/recipe-editing-limitations.md delete mode 100644 .serena/memories/repository-pattern.md delete mode 100644 .serena/memories/sections_ui_structure.md delete mode 100644 .serena/memories/suggested_commands.md delete mode 100644 .serena/memories/test_documentation_framework.md delete mode 100644 .serena/memories/testing_and_quality_commands.md delete mode 100644 .serena/memories/todo-issue-relationship-pattern.md delete mode 100644 .serena/memories/typescript-patterns.md delete mode 100644 .serena/memories/usecase-pattern-migration-complete.md delete mode 100644 .serena/memories/workflow-and-commands.md delete mode 100644 .serena/memories/workflow-optimization-patterns.md delete mode 100644 .serena/project.yml diff --git a/.serena/.gitignore b/.serena/.gitignore deleted file mode 100644 index 14d86ad62..000000000 --- a/.serena/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/cache diff --git a/.serena/memories/agent-context-handoff-protocol.md b/.serena/memories/agent-context-handoff-protocol.md deleted file mode 100644 index cd764e7ed..000000000 --- a/.serena/memories/agent-context-handoff-protocol.md +++ /dev/null @@ -1,309 +0,0 @@ -# Agent Context Handoff Protocol - -## Context Preservation Strategy - -### Problem Statement -Agent handoffs result in information loss, redundant discovery, and workflow inefficiency. Each specialized agent starts fresh without leveraging previous analysis or findings. - -### Solution Framework - -#### Context Structure -```typescript -interface AgentHandoffContext { - // Agent identification - sourceAgent: string - targetAgent: string - handoffTimestamp: string - - // Task context - originalUserIntent: string - currentPhase: WorkflowPhase - completedActions: string[] - pendingActions: string[] - - // Discovery results - codeAnalysisFindings: { - relevantFiles: string[] - todoPatterns: TODOPattern[] - issueCorrelations: IssueCorrelation[] - architecturalInsights: string[] - } - - // Implementation context - modificationScope: { - targetModules: string[] - affectedLayers: ('domain' | 'application' | 'infrastructure')[] - testRequirements: string[] - qualityGates: string[] - } - - // Quality context - validationResults: { - lintingIssues: string[] - typeErrors: string[] - testFailures: string[] - performanceConsiderations: string[] - } - - // Optimization context - workflowOptimizations: { - effectiveTools: string[] - avoidedPatterns: string[] - timeOptimizations: string[] - memoryUsagePatterns: string[] - } -} -``` - -### Handoff Protocols by Agent Type - -#### General-Purpose → Specialized Agent -```typescript -// Context preparation before specialized agent invocation -const contextHandoff = { - discoveryResults: { - searchStrategies: ['TODO patterns', 'GitHub issue correlation'], - codeAreas: ['recipe/components', 'recipe/domain'], - relevantIssues: [695, 123, 456], - architectural: ['clean architecture violations detected'] - }, - workScope: { - primaryObjective: 'Recipe editing limitation analysis', - secondaryTasks: ['validation improvements', 'error handling'] - }, - constraints: { - riskLevel: 'medium', - timeEstimate: '1-2 hours', - qualityRequirements: ['pnpm check must pass'] - } -} -``` - -#### Specialized Agent → General-Purpose -```typescript -// Results consolidation when returning to general-purpose agent -const returnContext = { - completedAnalysis: { - issuesFound: ['Recipe editing tracked in #695', 'Validation gaps identified'], - recommendations: ['Create validation improvement issue', 'Link to existing #456'], - riskAssessment: 'Low risk - existing issue tracks main functionality' - }, - optimizationResults: { - memoryCreated: ['workflow-optimization-patterns'], - workflowImprovements: ['Automated issue discovery patterns'], - futureEfficiency: '50% faster similar operations' - }, - nextSteps: { - immediate: ['Implement /discover-issues command'], - medium: ['Enhance memory integration'], - strategic: ['Build workflow orchestration'] - } -} -``` - -### Context Handoff Implementation - -#### Memory-Optimization-Engineer Handoff -```typescript -// When calling memory-optimization-engineer -const memoryContext = { - sourceWorkflow: { - operation: 'issue discovery automation', - patterns: ['TODO-to-issue correlation', 'codebase search optimization'], - repetitiveOperations: ['manual issue searches', 'TODO pattern discovery'] - }, - optimizationScope: { - targetFrequency: 'weekly development workflow', - impactArea: 'development efficiency', - measureableOutcome: 'reduced tool calls for equivalent outcomes' - }, - expectedDeliverables: { - memoryEntries: ['workflow optimization patterns', 'issue discovery templates'], - workflowImprovements: ['automated correlation', 'context preservation'], - efficiencyGains: ['50% faster issue discovery', 'reduced redundant searches'] - } -} -``` - -#### AI-Workflow-Optimizer Handoff -```typescript -// When calling ai-workflow-optimizer -const workflowContext = { - systemInefficiencies: { - redundantOperations: ['multiple agents doing similar discovery'], - contextLoss: ['agent handoffs without state preservation'], - toolMisuse: ['generic tools when project-specific available'] - }, - optimizationTarget: { - workflowType: 'development task automation', - userWorkflow: 'solo project development', - toolEcosystem: 'Claude Code + project commands' - }, - expectedAnalysis: { - inefficiencyPatterns: ['cross-agent communication gaps'], - solutionFramework: ['context preservation', 'tool optimization'], - implementationPlan: ['risk-ordered improvements', 'measurable outcomes'] - } -} -``` - -#### GitHub-Issue-Manager Handoff -```typescript -// When calling github-issue-manager -const issueContext = { - userIntent: { - primaryGoal: 'check for existing issues', - specificQuery: 'recipe editing functionality limitations', - preventDuplication: true - }, - searchScope: { - keywords: ['recipe edit', 'receitas dentro de receitas', 'TODO comments'], - issueStates: ['open', 'closed'], - correlationNeeded: ['TODO comments to GitHub issues'] - }, - expectedOutput: { - existingIssues: ['issue numbers', 'status', 'relationship to TODOs'], - recommendations: ['create new issue', 'reference existing', 'no action needed'], - workflowContinuation: ['next command suggestions'] - } -} -``` - -### Context Preservation Mechanisms - -#### Session State Management -```typescript -// Maintained throughout workflow session -interface SessionState { - workflowId: string - startTimestamp: string - userObjective: string - - agentHistory: AgentInteraction[] - cumulativeFindings: Record - workflowDecisions: Decision[] - - qualityGateStatus: { - lastCheck: string - passingTests: boolean - lintingClean: boolean - typeCheckClean: boolean - } - - progressTracking: { - completedPhases: WorkflowPhase[] - currentPhase: WorkflowPhase - estimatedTimeRemaining: string - } -} -``` - -#### Memory Integration Points -```typescript -// Strategic memory usage during handoffs -const memoryIntegrationStrategy = { - preHandoff: { - loadRelevantMemories: ['workflow-optimization-patterns', 'project-architecture'], - consolidateContext: 'merge session findings with historical patterns', - prepareHandoffPackage: 'structured context for target agent' - }, - - postHandoff: { - consolidateResults: 'merge agent findings with session context', - updateMemories: 'improve patterns based on new learnings', - prepareNextPhase: 'context preparation for workflow continuation' - }, - - errorRecovery: { - preserveContext: 'maintain session state during failures', - provideRollback: 'restore previous stable context', - learnFromFailure: 'update patterns to prevent similar issues' - } -} -``` - -### Implementation Patterns - -#### Context Validation -```typescript -// Ensure context quality during handoffs -const contextValidation = { - completeness: { - required: ['user intent', 'current phase', 'relevant findings'], - optional: ['optimization suggestions', 'risk assessments'], - validation: 'check all required fields present and meaningful' - }, - - consistency: { - crossReference: 'validate findings against previous context', - temporalConsistency: 'ensure timeline and phase alignment', - scopeConsistency: 'verify handoff scope matches original intent' - }, - - actionability: { - nextSteps: 'clear, specific actions for receiving agent', - constraints: 'limitations and requirements clearly specified', - success: 'measurable outcomes and completion criteria' - } -} -``` - -#### Error Handling in Handoffs -```typescript -// Robust error handling for context preservation -const errorHandlingStrategy = { - partialFailure: { - preserveSuccessful: 'save successful parts of context', - identifyFailure: 'isolate failed handoff components', - recoverGracefully: 'continue with available context' - }, - - completeFailure: { - rollbackToStable: 'restore last known good context', - preserveLearnings: 'save failure patterns for optimization', - userCommunication: 'clear explanation of failure and recovery' - }, - - prevention: { - validateBeforeHandoff: 'check context completeness and validity', - incrementalSaving: 'preserve context at multiple checkpoints', - redundantStorage: 'multiple preservation mechanisms' - } -} -``` - -### Success Metrics - -#### Efficiency Improvements -- **Context Reuse Rate**: Percentage of previous findings reused in handoffs -- **Redundant Operation Reduction**: Decrease in repeated discovery tasks -- **Handoff Speed**: Time from agent handoff to productive work -- **Information Retention**: Percentage of context preserved across handoffs - -#### Quality Improvements -- **Decision Consistency**: Alignment of decisions with previous context -- **Error Reduction**: Fewer mistakes due to missing context -- **Workflow Continuity**: Smoother transitions between workflow phases -- **User Experience**: Reduced need for user re-explanation - -#### Learning and Optimization -- **Pattern Recognition**: Improved identification of effective workflows -- **Memory Consolidation**: Better long-term pattern storage -- **Workflow Evolution**: Continuous improvement of handoff protocols -- **Predictive Capability**: Better anticipation of workflow needs - -### Integration with Project Standards - -#### Solo Project Adaptations -- **No team handoffs**: Focus on individual workflow continuity -- **Technical context**: Emphasize code and architecture over business context -- **Quality integration**: Maintain integration with `pnpm check` workflows -- **Self-review patterns**: Context for individual validation processes - -#### Clean Architecture Compliance -- **Layer awareness**: Preserve architectural decisions across handoffs -- **Domain purity**: Maintain domain layer isolation context -- **Error handling**: Consistent `showError` and `logging` pattern application -- **Import standards**: Preserve absolute import requirement context - -This protocol ensures that the AI workflow optimization benefits are realized through systematic context preservation and intelligent agent coordination. \ No newline at end of file diff --git a/.serena/memories/architecture_and_structure.md b/.serena/memories/architecture_and_structure.md deleted file mode 100644 index 20309725f..000000000 --- a/.serena/memories/architecture_and_structure.md +++ /dev/null @@ -1,54 +0,0 @@ -# Architecture and Code Structure - -## Clean Architecture Pattern -The project follows a strict 3-layer Domain-Driven Design (DDD) architecture: - -### 1. Domain Layer (`modules/*/domain/`) -- **Purpose**: Pure business logic, types, and repository interfaces -- **Characteristics**: - - Framework-agnostic - - Uses Zod schemas for validation and type inference - - Entities have `__type` discriminators for type safety - - NEVER imports side-effect utilities (showError, logging, toasts) - - Only throws pure errors with descriptive messages and context via `cause` - -### 2. Application Layer (`modules/*/application/`) -- **Purpose**: SolidJS resources, signals, and orchestration logic -- **Characteristics**: - - Catches domain errors and uses `showError` for toasts and `logging` for telemetry - - Manages global reactive state using `createSignal`/`createEffect` - - Coordinates between UI and infrastructure layers - - Handles all side effects and user feedback (toasts, notifications) - -### 3. Infrastructure Layer (`modules/*/infrastructure/`) -- **Purpose**: Supabase repositories implementing domain interfaces -- **Characteristics**: - - DAOs for data transformation and legacy migration - - External API integrations and data access - - Only layer allowed to use `any` types when necessary for external APIs - -## Directory Structure -``` -src/ -├── modules/ # Domain modules (diet, measure, user, weight, etc.) -│ └── / -│ ├── domain/ # Pure business logic, types, repository interfaces -│ ├── application/ # SolidJS resources, orchestration, error handling -│ ├── infrastructure/ # Supabase implementations, DAOs, external APIs -│ └── tests/ # Module-specific tests -├── sections/ # Page-level UI components and user-facing features -├── routes/ # SolidJS router pages and API endpoints -├── shared/ # Cross-cutting concerns and utilities -└── assets/ # Static assets (locales, images) -``` - -## Key Domain Modules -- **diet**: Core nutrition tracking (day-diet, food, item, meal, recipe, unified-item) -- **measure**: Body measurements and metrics -- **user**: User management and authentication -- **weight**: Weight tracking and evolution -- **toast**: Notification system -- **search**: Search functionality with caching -- **recent-food**: Recently used food items -- **profile**: User profile management -- **theme**: Theme management \ No newline at end of file diff --git a/.serena/memories/code_style_and_conventions.md b/.serena/memories/code_style_and_conventions.md deleted file mode 100644 index 74236c8e7..000000000 --- a/.serena/memories/code_style_and_conventions.md +++ /dev/null @@ -1,46 +0,0 @@ -# Code Style and Conventions - -## Import and Module System -- **REQUIRED**: Always use absolute imports with `~/` prefix -- **FORBIDDEN**: Relative imports (`../`, `./`) -- **FORBIDDEN**: Barrel files (`index.ts` re-exports) -- **REQUIRED**: Static imports at top of file -- **REQUIRED**: Type imports with inline syntax: `import { type Foo } from '~/module'` - -## Language Policy -- **All code, comments, JSDoc, and commit messages in English** -- **UI text only may be in Portuguese (pt-BR) when required** -- **Never use Portuguese for identifiers, variables, functions, or comments** - -## Naming Conventions -- **Descriptive, action-based names**: `isRecipedGroupUpToDate()` not `checkGroup()` -- **Specific file names**: `macroOverflow.ts` not `utils.ts` -- **Complete component names**: `ItemGroupEditModal` not `GroupModal` -- **Avoid generic names**: Never use `utils.ts`, `helper.ts`, `common.ts` - -## Type Safety Requirements -- **NEVER use `any`, `as any`, or `@ts-ignore`** (except infrastructure layer for external APIs) -- **Always prefer type aliases over interfaces** for data shapes -- **Use Zod schemas for runtime validation and type inference** -- **Prefer `readonly` arrays**: `readonly Item[]` over `Item[]` -- **Use intersection types for extending**: `ConfigType = PropsType & { extraProps }` -- **Default parameters over nullish coalescing**: `{ param = 'default' }` instead of `param ?? 'default'` - -## Error Handling Standards -- **Domain Layer**: Only throw pure domain errors with descriptive messages and context via `cause` -- **Application Layer**: Catch domain errors and use `showError` for toasts and `logging` for telemetry -- **Error Context Requirements**: - - `component`: Specific component/module name - - `operation`: Specific operation being performed - - `additionalData`: Relevant IDs, state, or debugging info - -## ESLint Rules -- **Consistent type definitions**: Use `type` not `interface` -- **Import organization**: Automatic sorting with `simple-import-sort` -- **No relative imports**: Enforced by `no-restricted-imports` rule -- **Strict boolean expressions**: Enforced for better type safety -- **Custom parsing requirement**: Use `parseWithStack` instead of direct `JSON.parse` - -## CSS and Styling -- **Always use `cn` function to merge Tailwind classes** for proper deduplication -- **TailwindCSS v4.1.8** with DaisyUI v5.0.43 for component styling \ No newline at end of file diff --git a/.serena/memories/developer-guidelines.md b/.serena/memories/developer-guidelines.md deleted file mode 100644 index cb031339e..000000000 --- a/.serena/memories/developer-guidelines.md +++ /dev/null @@ -1,32 +0,0 @@ -# Developer Guidelines (consolidated) - -This memory consolidates code style, TypeScript patterns, repository patterns, and file movement policies for the macroflows project. - -## Key Rules -- Barrel files (`index.ts`) are strictly forbidden. Always import directly from the specific file. -- Use absolute imports with the `~/` prefix; avoid relative imports. -- Use `type` aliases instead of `interface` and avoid `class`/`implements` entirely. Prefer factory functions that return plain objects. -- JSDoc: add JSDoc to all exported TypeScript types and exported functions describing purpose, parameters, and return values. -- Avoid `any` except in infrastructure when interacting with external APIs. -- Always use static imports at the top of files. - -## File Movement Policy -- When moving content from one file to another, DELETE the original file completely. Do not leave placeholder comments or stubs like `// This file has been moved to...`. - -## Error Handling -- Domain layer: throw pure errors only. -- Application layer: catch domain errors and surface user-friendly messages with `showError`; log telemetry with `logging` and `src/modules/observability`. -- Attach context to errors using `cause` or a `context` property where available. - -## Tests & Quality -- Update tests when changing code; remove orphaned tests for deleted functionality. -- Run `pnpm check` and `pnpm test` during validation. - -## Where to find source rules -This memory draws primary rules from: -- `code_style_and_conventions` -- `typescript-patterns` -- `repository-pattern` -- `file-movement-policy` - -Keep this memory as the canonical quick reference for daily development decisions. For full rationale and examples, see the archived originals in `archive/` memories. diff --git a/.serena/memories/development_workflow.md b/.serena/memories/development_workflow.md deleted file mode 100644 index d23435a2e..000000000 --- a/.serena/memories/development_workflow.md +++ /dev/null @@ -1,52 +0,0 @@ -# Development Workflow - -## Daily Workflow Commands -The project includes optimized Claude commands in `.claude/commands/` directory: - -### Workflow Commands -- `/commit` - Generate conventional commit messages and execute commits -- `/pull-request` or `/pr` - Create pull requests with proper formatting - -### Quality Assurance -- `/fix` - Automated codebase checks and error correction -- `/review` - Comprehensive code review for PR changes - -### Issue Management -- `/create-issue [type]` - Create GitHub issues using proper templates -- `/implement ` - Autonomous issue implementation - -### Refactoring -- `/refactor [target]` - Clean architecture refactoring and modularization - -### Session Management -- `/end-session` or `/end` - Session summary and knowledge export - -## Example Daily Workflow -```bash -/fix # Ensure clean codebase -/create-issue feature # Create feature request -/implement 123 # Implement issue #123 -/commit # Generate and execute commit -/pull-request # Create PR for review -``` - -## Git Workflow -- **Main branch**: `stable` (used for PRs) -- **Current branch**: `marcuscastelo/issue730-v2` -- **Solo project**: Remove team coordination/approval processes while maintaining quality - -## Environment Setup -- **Package Manager**: pnpm v10.12.1 (REQUIRED) -- **Environment File**: Copy `.env.example` to `.env.local` -- **Never commit secrets** or keys to repository - -## Commit Standards -- Use conventional commits style -- Prefer small, atomic commits -- All commit messages in English -- **STRICTLY FORBIDDEN**: Any "Generated with Claude Code" or "Co-Authored-By: Claude" text - -## Branch Management -- Feature branches for new development -- Clean history with meaningful commits -- Squash merge for feature completion \ No newline at end of file diff --git a/.serena/memories/diet_module_structure.md b/.serena/memories/diet_module_structure.md deleted file mode 100644 index 6e0dcf728..000000000 --- a/.serena/memories/diet_module_structure.md +++ /dev/null @@ -1,104 +0,0 @@ -# Diet Module Structure - -The diet module is the core of the Macroflows application, containing all nutrition-related functionality. It's the largest and most complex module with multiple sub-modules. - -## Sub-modules Overview - -### 1. **day-diet** - Daily diet management -- **Purpose**: Manages daily nutrition tracking and meal organization -- **Key Files**: - - `domain/dayDiet.ts` - DayDiet entity and schemas - - `domain/dayDietOperations.ts` - Business logic for day diet operations - - `application/dayDiet.ts` - SolidJS reactive state management - - `infrastructure/supabaseDayRepository.ts` - Database integration -- **Features**: Day creation, meal management, macro tracking, day change detection - -### 2. **food** - Food database management -- **Purpose**: Core food entity management with external API integration -- **Key Files**: - - `domain/food.ts` - Food entity and validation - - `application/food.ts` - Food fetching and caching - - `infrastructure/supabaseFoodRepository.ts` - Database operations - - `infrastructure/api/` - External food API integration -- **Features**: Food search, EAN scanning, API food imports, caching - -### 3. **unified-item** - Complex item hierarchy system -- **Purpose**: Unified type system for foods, recipes, and groups -- **Key Files**: - - `schema/unifiedItemSchema.ts` - Discriminated union types - - `domain/conversionUtils.ts` - Type conversions - - `domain/treeUtils.ts` - Tree traversal utilities - - `domain/validateItemHierarchy.ts` - Validation logic -- **Features**: Type-safe item hierarchy, tree operations, validation - -### 4. **recipe** - Recipe management -- **Purpose**: Recipe creation, management, and scaling -- **Key Files**: - - `domain/recipe.ts` - Recipe entities (legacy and unified) - - `domain/recipeOperations.ts` - Recipe business logic - - `application/recipe.ts` - Recipe state management - - `infrastructure/supabaseRecipeRepository.ts` - Database operations -- **Features**: Recipe CRUD, scaling, unified recipe system - -### 5. **item** - Individual food items -- **Purpose**: Individual item management within meals -- **Key Files**: - - `domain/item.ts` - Item entity definition - - `application/item.ts` - Item operations -- **Features**: Item quantity updates, meal integration - -### 6. **meal** - Meal management -- **Purpose**: Meal structure and item organization -- **Key Files**: - - `domain/meal.ts` - Meal entity and schemas - - `domain/mealOperations.ts` - Meal business logic - - `application/meal.ts` - Meal state management -- **Features**: Meal CRUD, item management, group operations - -### 7. **item-group** - Item grouping system -- **Purpose**: Grouping items within meals for organization -- **Key Files**: - - `domain/itemGroup.ts` - Group entity types - - `domain/itemGroupOperations.ts` - Group operations - - `application/itemGroupService.ts` - Group services -- **Features**: Simple and reciped groups, item management - -### 8. **macro-nutrients** - Macro nutrient calculations -- **Purpose**: Macro nutrient validation and calculations -- **Key Files**: - - `domain/macroNutrients.ts` - MacroNutrients entity - - `domain/macroNutrientsErrors.ts` - Validation errors -- **Features**: Macro validation, calculation logic - -### 9. **macro-profile** - User macro profiles -- **Purpose**: User-specific macro targets and profiles -- **Key Files**: - - `domain/macroProfile.ts` - MacroProfile entity - - `application/macroProfile.ts` - Profile management - - `infrastructure/supabaseMacroProfileRepository.ts` - Database operations -- **Features**: Profile CRUD, macro target calculation - -### 10. **macro-target** - Macro target calculations -- **Purpose**: Calculate daily macro targets based on profiles -- **Key Files**: - - `application/macroTarget.ts` - Target calculation logic -- **Features**: Daily macro target calculation - -### 11. **template** - Template system -- **Purpose**: Template-based item creation -- **Key Files**: - - `domain/template.ts` - Template types - - `application/templateToItem.ts` - Template to item conversion -- **Features**: Template creation, item generation - -### 12. **template-item** - Template item types -- **Purpose**: Template item type definitions -- **Key Files**: - - `domain/templateItem.ts` - Template item types -- **Features**: Template item validation - -### 13. **api** - External API integration -- **Purpose**: External food API constants and configuration -- **Key Files**: - - `constants/apiSecrets.ts` - API configuration -- **Features**: API endpoints, authentication \ No newline at end of file diff --git a/.serena/memories/error-analysis-test-structure-fix.md b/.serena/memories/error-analysis-test-structure-fix.md deleted file mode 100644 index 0999029d5..000000000 --- a/.serena/memories/error-analysis-test-structure-fix.md +++ /dev/null @@ -1,34 +0,0 @@ -# Análise de Erro: Correção de Estrutura de Testes - -## Erro Cometido -Quando o usuário disse "fix tests after staged changes", interpretei incorretamente como "corrigir código para fazer testes passarem" ao invés de "corrigir a estrutura dos testes para refletir mudanças na organização dos arquivos". - -## Contexto do Erro -- **Situação**: Usuário havia refatorado código separando responsabilidades em dois arquivos (dayDiet.ts e dayDietStore.ts) -- **Problema Real**: Testes estavam importando de locais incorretos após a refatoração -- **Minha Ação Incorreta**: Modifiquei código de produção para fazer testes passarem -- **Ação Correta**: Deveria ter movido/ajustado os testes para refletir a nova estrutura - -## Sinais que Deveria Ter Percebido -1. **Comando específico**: "fix tests" - foco explícito nos testes, não no código -2. **Contexto de staged changes**: Mudanças já feitas pelo usuário, não para eu alterar -3. **Estrutura de arquivos nova**: Separação clara de responsabilidades já implementada -4. **Erro de import**: Teste importando de local que não existe mais - -## Princípios para Evitar Repetir -1. **"Fix tests" significa ajustar testes, não código de produção** -2. **Quando há staged changes, o código já está como deve estar** -3. **Import errors em testes = mover imports, não recriar exports** -4. **Sempre perguntar quando ambíguo entre "fix code" vs "fix tests"** - -## Diretrizes de Interpretação -- **"Fix tests"** = Ajustar estrutura, imports, mocks dos testes -- **"Fix code"** = Alterar lógica de produção -- **"Fix both"** = Só quando explicitamente mencionado - -## Ação Correta para Este Caso -1. Analisar estrutura atual (dayDiet.ts vs dayDietStore.ts) -2. Identificar responsabilidades de cada arquivo -3. Mover testes para arquivos corretos conforme responsabilidades -4. Ajustar imports nos testes -5. Não tocar no código de produção \ No newline at end of file diff --git a/.serena/memories/file-movement-policy.md b/.serena/memories/file-movement-policy.md deleted file mode 100644 index 4f28c3a20..000000000 --- a/.serena/memories/file-movement-policy.md +++ /dev/null @@ -1,21 +0,0 @@ -# File Movement Policy - -## Critical Rule: Never Leave Empty Files with Comments - -**ABSOLUTELY FORBIDDEN:** -- Leaving files with "// This file has been moved to..." comments -- Creating placeholder files after moving content -- Any form of file stub or redirect comments - -**CORRECT APPROACH:** -- When moving content from one file to another, DELETE the original file completely -- No comments, no placeholders, no traces -- Clean deletion is the only acceptable approach - -## Context -This rule was established after creating a comment placeholder in `src/modules/diet/day-diet/tests/dayDiet.test.ts` instead of properly deleting the file after moving its content to `dayDietOperations.test.ts`. - -## Implementation -- Move content to new location -- DELETE original file completely -- No intermediate steps or placeholders \ No newline at end of file diff --git a/.serena/memories/issue-creation-workflow-optimization.md b/.serena/memories/issue-creation-workflow-optimization.md deleted file mode 100644 index ee0a7b35d..000000000 --- a/.serena/memories/issue-creation-workflow-optimization.md +++ /dev/null @@ -1,32 +0,0 @@ -# Optimized Issue Creation Workflow - -## User's Consistent Pattern -1. **Discovery**: Find TODO comments or identify limitations -2. **Research**: Ask "are there existing GitHub issues for this?" -3. **Verification**: Search codebase and existing issues -4. **Action**: Create new issue or reference existing one - -## Optimization Strategy -When user asks about existing issues: - -### Quick Assessment Steps -1. **Search TODO patterns**: Use `search_for_pattern` with relevant keywords -2. **Check issue titles**: Look for similar functionality in existing issues -3. **Identify code areas**: Point to specific modules/files involved -4. **Suggest issue scope**: Break large features into manageable pieces - -### Common Search Patterns -- Feature requests: `search_for_pattern` with `TODO.*feature|enhancement` -- Bug tracking: `search_for_pattern` with `FIXME|BUG|XXX` -- Performance: `search_for_pattern` with `TODO.*performance|optimize` - -### Code Area Mapping -- **Recipe functionality**: `modules/diet/`, `sections/unified-item/` -- **Search features**: `modules/*/infrastructure/` + search-related files -- **UI components**: `sections/common/`, component directories -- **Data validation**: Domain layer files with Zod schemas - -## Efficiency Gains -- Reduce repetitive searching by having mapped code areas -- Quick TODO-to-issue correlation workflow -- Faster identification of related existing functionality \ No newline at end of file diff --git a/.serena/memories/lessons-learned.md b/.serena/memories/lessons-learned.md deleted file mode 100644 index 8fec7fe69..000000000 --- a/.serena/memories/lessons-learned.md +++ /dev/null @@ -1,12 +0,0 @@ -# Lessons Learned & Archived Post-Mortems - -This memory collects post-mortems, migration snapshots and lessons learned that are useful for historical context. - -## Entries -- `usecase-pattern-migration-complete` — migration snapshot and verification (tests passing) — archived -- `error-analysis-test-structure-fix` — post-mortem describing a mistaken fix to production instead of fixing tests — archived - -## Guidance -- Keep archived entries concise and searchable for future audits. -- Convert concrete actionable items found here into issues if not yet tracked. - diff --git a/.serena/memories/module-map.md b/.serena/memories/module-map.md deleted file mode 100644 index 3fcc30783..000000000 --- a/.serena/memories/module-map.md +++ /dev/null @@ -1,25 +0,0 @@ -# Module Map - -This memory consolidates module structure information for quick navigation and discovery. - -## Core Modules -- `diet`: day-diet, food, item, meal, recipe, unified-item -- `measure`: body measurements -- `user`: user management -- `weight`: weight tracking -- `toast`: notification system -- `search`: search infrastructure -- `recent-food`: recent foods -- `profile`: profile and charts -- `theme`: theme management - -## UI Sections -Refer to `sections_ui_structure` for components per section (day-diet, unified-item, recipe, etc.) - -## Where to look -- Domain layer: `src/modules//domain` -- Application layer: `src/modules//application` -- Infrastructure: `src/modules//infrastructure` -- UI sections: `src/sections` - -Keep this memory as a quick index for module discovery and TODO-to-issue mapping. \ No newline at end of file diff --git a/.serena/memories/other_modules_structure.md b/.serena/memories/other_modules_structure.md deleted file mode 100644 index 9d620a1c9..000000000 --- a/.serena/memories/other_modules_structure.md +++ /dev/null @@ -1,86 +0,0 @@ -# Other Modules Structure - -## Core Modules (Non-Diet) - -### 1. **user** - User management -- **Purpose**: User authentication, profile management, and preferences -- **Key Files**: - - `domain/user.ts` - User entity and validation - - `application/user.ts` - User state management with SolidJS - - `infrastructure/supabaseUserRepository.ts` - Database operations - - `infrastructure/localStorageUserRepository.ts` - Local storage integration -- **Features**: User CRUD, authentication, favorite foods, profile management - -### 2. **weight** - Weight tracking -- **Purpose**: Weight measurement tracking and evolution analysis -- **Key Files**: - - `domain/weight.ts` - Weight entity and schemas - - `application/weight.ts` - Weight state management - - `application/weightChartUtils.ts` - Chart data processing - - `domain/weightEvolutionDomain.ts` - Evolution calculations - - `infrastructure/supabaseWeightRepository.ts` - Database operations -- **Features**: Weight CRUD, chart generation, moving averages, evolution tracking - -### 3. **measure** - Body measurements -- **Purpose**: Body measurement tracking (height, waist, hip, neck, etc.) -- **Key Files**: - - `domain/measure.ts` - BodyMeasure entity and validation - - `application/measure.ts` - Measure state management - - `application/measureUtils.ts` - Calculation utilities - - `infrastructure/measures.ts` - Database operations -- **Features**: Body measure CRUD, body fat calculation, measurement averages - -### 4. **toast** - Notification system -- **Purpose**: Comprehensive toast notification system with queue management -- **Key Files**: - - `domain/toastTypes.ts` - Toast types and configuration - - `application/toastManager.ts` - Toast display management - - `application/toastQueue.ts` - Queue processing - - `domain/errorMessageHandler.ts` - Error message processing - - `infrastructure/toastSettings.ts` - Settings management - - `ui/ExpandableErrorToast.tsx` - Error toast component -- **Features**: Toast queue, error handling, expandable errors, settings - -### 5. **search** - Search functionality -- **Purpose**: Search functionality with caching and optimization -- **Key Files**: - - `application/search.ts` - Search logic - - `application/cachedSearch.ts` - Cached search implementation - - `application/searchLogic.ts` - Search algorithms - - `application/searchCache.ts` - Cache management -- **Features**: Cached search, diacritic-insensitive search, search optimization - -### 6. **recent-food** - Recent food tracking -- **Purpose**: Track recently used food items for quick access -- **Key Files**: - - `domain/recentFood.ts` - RecentFood entity - - `application/recentFood.ts` - Recent food management - - `infrastructure/supabaseRecentFoodRepository.ts` - Database operations -- **Features**: Recent food tracking, quick access, persistence - -### 7. **profile** - User profile management -- **Purpose**: User profile display and management -- **Key Files**: - - `application/profile.ts` - Profile state management -- **Features**: Profile data aggregation, user information management - -### 8. **theme** - Theme management -- **Purpose**: Application theme configuration -- **Key Files**: - - `constants.ts` - Theme constants -- **Features**: Theme switching, color schemes - -## Shared Utilities - -### **shared/** - Cross-cutting concerns -- **Purpose**: Framework-agnostic utilities and shared functionality -- **Key Areas**: - - `error/` - Error handling utilities - - `utils/` - Pure utility functions - - `modal/` - Modal management system - - `supabase/` - Supabase client and utilities - - `config/` - Configuration management - - `domain/` - Shared domain types and validation - - `hooks/` - Reusable SolidJS hooks - - `solid/` - SolidJS-specific utilities -- **Features**: Error handling, modal system, utilities, validation \ No newline at end of file diff --git a/.serena/memories/project-context-macroflows.md b/.serena/memories/project-context-macroflows.md deleted file mode 100644 index 480068674..000000000 --- a/.serena/memories/project-context-macroflows.md +++ /dev/null @@ -1,14 +0,0 @@ -# Contexto do projeto: macroflows - -- Responsável esperado: marcuscastelo -- Repositório: macroflows (https://github.com/marcuscastelo/macroflows) -- Preferências do agente: - - Ferramentas preferidas: `read_file`, `grep`, `find_path`, `edit_file`, `replace_content`, `diagnostics`. - - Regra importante: barrel `index.ts` files são proibidos no repositório. - - JSDoc: obrigatório para tipos e funções exportadas em TypeScript. -- Boas práticas a lembrar: - - Não armazenar segredos nesta memória. - - Use nomes de memória claros e em markdown. - - Rodar `diagnostics` após alterações significativas. - -> Nota: esta memória é um exemplo demonstrativo com informações úteis para assistências futuras. Atualize ou apague conforme necessário. \ No newline at end of file diff --git a/.serena/memories/project_overview.md b/.serena/memories/project_overview.md deleted file mode 100644 index eacd1ded3..000000000 --- a/.serena/memories/project_overview.md +++ /dev/null @@ -1,27 +0,0 @@ -# Macroflows Project Overview - -## Project Purpose -Macroflows is a comprehensive nutrition tracking platform designed to help users monitor their daily diet, macronutrient intake, and weight management. The application provides detailed tracking of meals, recipes, food items, and macro calculations with real-time synchronization. - -## Tech Stack -- **Frontend Framework**: SolidJS with SolidStart (v1.9.5) -- **Database**: Supabase (PostgreSQL + Realtime) -- **Build Tool**: Vinxi (Vite-based) -- **Styling**: TailwindCSS v4.1.8 + DaisyUI v5.0.43 -- **Validation**: Zod v3.25.75 for runtime validation and type inference -- **Testing**: Vitest v3.2.2 with jsdom environment -- **Charts**: ApexCharts with solid-apexcharts -- **HTTP Client**: Axios with rate limiting -- **Package Manager**: pnpm v10.12.1 -- **Language**: TypeScript v5.3.0 - -## Key Features -- Daily diet tracking with meal management -- Recipe creation and management -- Food item database with EAN scanning -- Macro nutrient calculations and targets -- Weight tracking with charts -- User profile management -- Real-time synchronization via Supabase -- Offline-capable with local storage fallbacks -- Portuguese (pt-BR) localization support \ No newline at end of file diff --git a/.serena/memories/recipe-editing-limitations.md b/.serena/memories/recipe-editing-limitations.md deleted file mode 100644 index d301e9afa..000000000 --- a/.serena/memories/recipe-editing-limitations.md +++ /dev/null @@ -1,27 +0,0 @@ -# Recipe Editing Known Limitations - -## Current State -- Recipe editing functionality exists but has **known limitations** -- Users cannot fully edit all recipe properties through UI -- This is a **documented limitation** that comes up repeatedly in development discussions - -## Key Areas Needing Improvement -- Recipe ingredient modification -- Recipe metadata editing (name, description, etc.) -- Recipe sharing and collaboration features -- Recipe versioning/history - -## Code Locations -- Recipe management: `modules/*/domain/` and `modules/*/application/` layers -- UI components: `sections/unified-item/` area -- Repository pattern: `modules/*/infrastructure/` for data persistence - -## Development Priority -- High user demand for improved recipe editing -- Should be prioritized for future development cycles -- Consider breaking into smaller, manageable issues - -## Search Patterns for Related Code -- `search_for_pattern` with `recipe.*edit|edit.*recipe` -- Look in `modules/diet/` and `sections/unified-item/` directories -- Check for existing TODO comments around recipe functionality \ No newline at end of file diff --git a/.serena/memories/repository-pattern.md b/.serena/memories/repository-pattern.md deleted file mode 100644 index aef952bf9..000000000 --- a/.serena/memories/repository-pattern.md +++ /dev/null @@ -1,150 +0,0 @@ -# Repository Pattern - Macroflows - -## New Day-Diet Architecture Standard - -### ✅ Gateway + Repository + Cache Pattern - -**Three-Layer Structure:** -1. **Gateway Layer** (`infrastructure/supabase/`) - Direct Supabase interaction -2. **Repository Layer** (`infrastructure/`) - Cache management + error handling -3. **Store Layer** (`infrastructure/signals/`) - Reactive state management - -**Gateway Pattern:** -```typescript -// infrastructure/supabase/supabaseDayGateway.ts -export function createSupabaseDayGateway(): DayRepository { - return { - fetchDayDietByUserIdAndTargetDay, - fetchDayDietsByUserIdBeforeDate, - fetchDayDietById, - insertDayDiet, - updateDayDietById, - deleteDayDietById, - } -} - -async function fetchDayDietByUserIdAndTargetDay( - userId: User['uuid'], - targetDay: string, -): Promise { - const { data, error } = await supabase - .from(SUPABASE_TABLE_DAYS) - .select() - .eq('owner', userId) - .eq('target_day', targetDay) - .single() - - if (error?.code === 'PGRST116') return null - if (error) throw error - return dayDietSchema.parse(data) -} -``` - -**Repository with Cache & Error Handling:** -```typescript -// infrastructure/dayDietRepository.ts -const supabaseGateway = createSupabaseDayGateway() -const errorHandler = createErrorHandler('application', 'DayDiet') - -export function createDayDietRepository(): DayRepository { - return { - fetchDayDietById, - fetchDayDietByUserIdAndTargetDay, - // ... other methods - } -} - -export async function fetchDayDietByUserIdAndTargetDay( - userId: User['uuid'], - targetDay: string, -): Promise { - try { - const dayDiet = await supabaseGateway.fetchDayDietByUserIdAndTargetDay(userId, targetDay) - if (dayDiet) { - dayCacheStore.upsertToCache(dayDiet) - } else { - dayCacheStore.removeFromCache({ by: 'target_day', value: targetDay }) - } - return dayDiet - } catch (error) { - errorHandler.error(error) - dayCacheStore.removeFromCache({ by: 'target_day', value: targetDay }) - return null - } -} -``` - -**Store Pattern:** -```typescript -// infrastructure/signals/dayCacheStore.ts -const [dayDiets, setDayDiets] = createSignal([]) - -function upsertToCache(dayDiet: DayDiet) { - const existingDayIndex = untrack(dayDiets).findIndex( - (d) => d.target_day === dayDiet.target_day, - ) - setDayDiets((existingDays) => { - const days = [...existingDays] - if (existingDayIndex >= 0) { - days[existingDayIndex] = dayDiet - } else { - days.push(dayDiet) - days.sort((a, b) => a.target_day.localeCompare(b.target_day)) - } - return days - }) -} - -export const dayCacheStore = { - dayDiets, - setDayDiets, - clearCache: () => setDayDiets([]), - upsertToCache, - removeFromCache, -} -``` - -**Service Pattern:** -```typescript -// application/services/cacheManagement.ts -export function createCacheManagementService(deps: { - getExistingDays: () => readonly DayDiet[] - getCurrentDayDiet: () => DayDiet | null - clearCache: () => void -}) { - return ({ currentTargetDay, userId }: { - currentTargetDay: string - userId: User['uuid'] - }) => { - // Complex business logic with injected dependencies - } -} -``` - -**UseCase Pattern:** -```typescript -// application/usecases/dayCrud.ts -const dayRepository = createDayDietRepository() - -export async function insertDayDiet(dayDiet: NewDayDiet): Promise { - await showPromise( - dayRepository.insertDayDiet(dayDiet), - { - loading: 'Criando dia de dieta...', - success: 'Dia de dieta criado com sucesso', - error: 'Erro ao criar dia de dieta', - }, - { context: 'user-action', audience: 'user' }, - ) -} -``` - -### New Architecture Rules -- **Gateway naming**: Use `createSupabase*Gateway()` for Supabase layer -- **Repository naming**: Use `create*Repository()` for cache + error handling layer -- **Store naming**: Use `*Store` objects with reactive signals -- **Service pattern**: Dependency injection with explicit parameters -- **UseCase pattern**: Toast integration for user operations -- **Naming convention**: `fetch*By*` patterns (e.g., `fetchDayDietByUserIdAndTargetDay`) -- **Error handling**: Always use `createErrorHandler` in repository layer -- **Cache integration**: Repository layer manages cache upsert/remove operations \ No newline at end of file diff --git a/.serena/memories/sections_ui_structure.md b/.serena/memories/sections_ui_structure.md deleted file mode 100644 index df16cec0b..000000000 --- a/.serena/memories/sections_ui_structure.md +++ /dev/null @@ -1,122 +0,0 @@ -# Sections (UI Components) Structure - -The sections directory contains page-level UI components and user-facing features organized by functional areas. - -## UI Sections Overview - -### 1. **common** - Shared UI components -- **Purpose**: Reusable UI components used across the application -- **Key Components**: - - `components/Modal.tsx` - Modal component system - - `components/buttons/` - Button components - - `components/charts/` - Chart components - - `components/icons/` - Icon components - - `context/Providers.tsx` - Context providers - - `hooks/` - Reusable UI hooks -- **Features**: Modal system, buttons, charts, icons, context providers - -### 2. **day-diet** - Daily diet UI -- **Purpose**: UI components for daily diet management -- **Key Components**: - - `components/DayMeals.tsx` - Day meals display - - `components/DayMacros.tsx` - Day macros summary - - `components/TopBar.tsx` - Day navigation bar - - `components/CopyLastDayModal.tsx` - Copy previous day functionality - - `components/DayChangeModal.tsx` - Day change detection -- **Features**: Day overview, meal display, macro summary, navigation - -### 3. **unified-item** - Complex item UI -- **Purpose**: UI components for the unified item system -- **Key Components**: - - `components/UnifiedItemView.tsx` - Item display - - `components/UnifiedItemEditModal.tsx` - Item editing - - `components/UnifiedItemActions.tsx` - Item actions - - `components/QuantityControls.tsx` - Quantity management - - `components/UnifiedItemChildren.tsx` - Child item display -- **Features**: Item display, editing, actions, quantity controls - -### 4. **meal** - Meal management UI -- **Purpose**: UI components for meal management -- **Key Components**: - - `components/MealEditView.tsx` - Meal editing interface - - `context/MealContext.tsx` - Meal context provider -- **Features**: Meal editing, context management - -### 5. **recipe** - Recipe management UI -- **Purpose**: UI components for recipe management -- **Key Components**: - - `components/RecipeEditModal.tsx` - Recipe editing modal - - `components/RecipeEditView.tsx` - Recipe editing interface - - `components/UnifiedRecipeEditView.tsx` - Unified recipe editing - - `context/RecipeEditContext.tsx` - Recipe context -- **Features**: Recipe editing, unified recipe system - -### 6. **search** - Search UI -- **Purpose**: Search interface components -- **Key Components**: - - `components/TemplateSearchBar.tsx` - Search input - - `components/TemplateSearchModal.tsx` - Search modal - - `components/TemplateSearchResults.tsx` - Search results display - - `components/TemplateSearchTabs.tsx` - Search tabs -- **Features**: Search interface, results display, tabbed search - -### 7. **profile** - User profile UI -- **Purpose**: User profile and statistics display -- **Key Components**: - - `components/UserInfo.tsx` - User information display - - `components/MacroEvolution.tsx` - Macro evolution charts - - `components/WeightChartSection.tsx` - Weight chart display - - `components/ProfileChartTabs.tsx` - Profile chart tabs - - `measure/components/` - Body measurement components -- **Features**: Profile display, charts, measurements, evolution tracking - -### 8. **weight** - Weight tracking UI -- **Purpose**: Weight tracking and chart display -- **Key Components**: - - `components/WeightChart.tsx` - Weight chart display - - `components/WeightView.tsx` - Weight management interface - - `components/WeightEvolution.tsx` - Weight evolution display - - `components/WeightProgress.tsx` - Weight progress tracking -- **Features**: Weight charts, evolution display, progress tracking - -### 9. **macro-nutrients** - Macro nutrient UI -- **Purpose**: Macro nutrient display and management -- **Key Components**: - - `components/MacroNutrientsView.tsx` - Macro display - - `components/MacroTargets.tsx` - Macro targets display -- **Features**: Macro display, target visualization - -### 10. **ean** - EAN scanning UI -- **Purpose**: EAN barcode scanning interface -- **Key Components**: - - `components/EANReader.tsx` - Barcode scanner - - `components/EANSearch.tsx` - EAN search interface - - `components/EANInsertModal.tsx` - EAN insertion modal -- **Features**: Barcode scanning, EAN search, food insertion - -### 11. **settings** - Application settings UI -- **Purpose**: Application settings and preferences -- **Key Components**: - - `components/ToastSettings.tsx` - Toast notification settings - - `components/Toggle.tsx` - Toggle component -- **Features**: Settings management, toast configuration - -### 12. **datepicker** - Date picker component -- **Purpose**: Custom date picker implementation -- **Key Components**: - - `components/Datepicker.tsx` - Main datepicker - - `components/Calendar/` - Calendar components - - `contexts/DatepickerContext.ts` - Date picker context -- **Features**: Date selection, calendar display, context management - -## Routes Structure - -### **routes/** - Page routing -- **Purpose**: SolidJS routing and page components -- **Key Files**: - - `diet.tsx` - Main diet page - - `profile.tsx` - Profile page - - `settings.tsx` - Settings page - - `index.tsx` - Landing page - - `api/` - API endpoints -- **Features**: Page routing, API endpoints \ No newline at end of file diff --git a/.serena/memories/suggested_commands.md b/.serena/memories/suggested_commands.md deleted file mode 100644 index 83551d5e0..000000000 --- a/.serena/memories/suggested_commands.md +++ /dev/null @@ -1,97 +0,0 @@ -# Suggested Commands for Macroflows Development - -## Essential Development Commands -```bash -# Start development server -pnpm dev - -# Quality gate (MANDATORY before completion) -pnpm check - -# Fix ESLint issues automatically -pnpm fix - -# Production build -pnpm build - -# Type checking -pnpm type-check - -# Run tests -pnpm test - -# Run single test file -pnpm test - -# Test with coverage -pnpm test --coverage - -# Generate app version -pnpm gen-app-version -``` - -## System Commands (Linux) -```bash -# Basic file operations -ls -la -cd -find . -name "*.ts" -type f -grep -r "pattern" src/ -tree -I node_modules - -# Git operations -git status -git add . -git commit -m "message" -git push origin -git branch -a -git checkout -``` - -## Claude Commands (from .claude/commands/) -```bash -# Quality and fixes -/fix # Automated codebase checks -/review # Code review for PR changes - -# Issue management -/create-issue feature # Create feature issue -/implement 123 # Implement issue #123 - -# Git workflow -/commit # Generate and execute commit -/pull-request # Create PR - -# Refactoring -/refactor # Clean architecture refactoring - -# Session management -/end-session # Session summary -``` - -## Debugging Commands -```bash -# Check TypeScript errors -pnpm type-check - -# Check ESLint errors -pnpm lint - -# Run specific test -pnpm test src/modules/diet/day-diet/domain/dayDietOperations.test.ts - -# Build and check for errors -pnpm build -``` - -## Environment Setup -```bash -# Install dependencies -pnpm install - -# Copy environment file -cp .env.example .env.local - -# Check pnpm version -pnpm --version -``` \ No newline at end of file diff --git a/.serena/memories/test_documentation_framework.md b/.serena/memories/test_documentation_framework.md deleted file mode 100644 index 8314b7175..000000000 --- a/.serena/memories/test_documentation_framework.md +++ /dev/null @@ -1,155 +0,0 @@ -# Test Documentation Framework - -## Testing Strategy Overview - -Macroflows uses a comprehensive testing strategy with Vitest and jsdom for unit and integration tests. Tests are organized by module and layer, following the clean architecture pattern. - -## Test Organization Structure - -### 1. **Test Location Patterns** -- **Module tests**: `src/modules/{module}/tests/` or alongside source files with `.test.ts` suffix -- **Domain tests**: Focus on business logic validation and error handling -- **Application tests**: Test SolidJS resources, state management, and orchestration -- **Infrastructure tests**: Test database operations, migrations, and external integrations - -### 2. **Test Types by Layer** - -#### Domain Layer Tests -- **Focus**: Pure business logic, validation, and domain operations -- **Examples**: - - `dayDietOperations.test.ts` - Day diet business logic - - `recipeOperations.test.ts` - Recipe scaling and operations - - `itemGroupOperations.test.ts` - Item group management - - `mealOperations.test.ts` - Meal operations -- **Patterns**: Pure function testing, validation testing, error condition testing - -#### Application Layer Tests -- **Focus**: SolidJS reactive state, orchestration, and error handling -- **Examples**: - - `dayDiet.test.ts` - Day diet state management - - `item.test.ts` - Item application services - - `template.test.ts` - Template application services -- **Patterns**: Signal testing, effect testing, error handling validation - -#### Infrastructure Layer Tests -- **Focus**: Database operations, migrations, and external API integration -- **Examples**: - - `dayDietDAO.test.ts` - DAO conversions and legacy migration - - `migrationUtils.test.ts` - Data migration utilities -- **Patterns**: DAO testing, migration testing, repository testing - -### 3. **Specialized Test Categories** - -#### Schema and Validation Tests -- **Examples**: - - `unifiedItemSchema.test.ts` - Complex type validation - - `validateItemHierarchy.test.ts` - Hierarchy validation -- **Focus**: Zod schema validation, type guards, data integrity - -#### Utility Function Tests -- **Examples**: - - `measureUtils.test.ts` - Measurement calculations - - `conversionUtils.test.ts` - Type conversions - - `treeUtils.test.ts` - Tree operations -- **Focus**: Pure utility functions, calculations, transformations - -#### Error Handling Tests -- **Examples**: - - `errorMessageHandler.test.ts` - Error message processing - - `clipboardErrorUtils.test.ts` - Clipboard error handling -- **Focus**: Error processing, message formatting, error recovery - -#### Toast System Tests -- **Examples**: - - `toastManager.test.ts` - Toast display management - - `toastQueue.test.ts` - Queue processing - - `toastSettings.test.ts` - Settings management -- **Focus**: Notification system, queue management, user feedback - -## Test Documentation Requirements - -### 1. **Test File Documentation** -Each test file should include: -- **Purpose**: Clear description of what functionality is being tested -- **Setup**: Any required setup or mock configuration -- **Test Cases**: Comprehensive coverage of success and failure scenarios -- **Edge Cases**: Boundary conditions and error states - -### 2. **Test Case Documentation** -Each test case should have: -- **Descriptive names**: Clear, action-based test descriptions -- **Given-When-Then structure**: Clear test organization -- **Expected behavior**: Explicit assertions and expectations -- **Error scenarios**: Expected error conditions and messages - -### 3. **Mock and Setup Documentation** -- **Mock purposes**: Why mocks are used and what they simulate -- **Setup functions**: Reusable test setup utilities -- **Test data**: Well-structured test data factories - -## Testing Standards - -### 1. **Coverage Requirements** -- **Domain layer**: 100% coverage of business logic -- **Application layer**: Focus on error handling and state management -- **Infrastructure layer**: Test DAO conversions and migrations - -### 2. **Test Quality Standards** -- **Isolated tests**: Each test should be independent -- **Deterministic**: Tests should produce consistent results -- **Fast execution**: Tests should run quickly -- **Clear assertions**: Explicit and meaningful assertions - -### 3. **Test Maintenance** -- **Update with changes**: Tests must be updated when code changes -- **Remove orphaned tests**: Delete tests for removed functionality -- **Refactor with code**: Keep tests aligned with code structure - -## Common Test Patterns - -### 1. **Domain Entity Testing** -```typescript -describe('Entity Operations', () => { - it('should create valid entity', () => { - // Test entity creation - }) - - it('should validate entity constraints', () => { - // Test validation rules - }) - - it('should handle invalid input', () => { - // Test error conditions - }) -}) -``` - -### 2. **Application State Testing** -```typescript -describe('Application State', () => { - beforeEach(() => { - // Setup mocks and initial state - }) - - it('should handle state updates', () => { - // Test reactive state changes - }) - - it('should handle errors correctly', () => { - // Test error handling and recovery - }) -}) -``` - -### 3. **Infrastructure Testing** -```typescript -describe('Infrastructure Operations', () => { - it('should convert DAO to entity', () => { - // Test data conversion - }) - - it('should handle migration scenarios', () => { - // Test data migrations - }) -}) -``` \ No newline at end of file diff --git a/.serena/memories/testing_and_quality_commands.md b/.serena/memories/testing_and_quality_commands.md deleted file mode 100644 index f0f2e968e..000000000 --- a/.serena/memories/testing_and_quality_commands.md +++ /dev/null @@ -1,42 +0,0 @@ -# Testing and Quality Commands - -## 🚨 MANDATORY Quality Gate -**CRITICAL REQUIREMENT: ALWAYS RUN `pnpm check` BEFORE DECLARING ANY TASK COMPLETE** - -## Essential Commands -- `pnpm check` - **MANDATORY** quality gate (lint, type-check, test) - MUST PASS before any completion -- `pnpm fix` - Auto-fix ESLint issues -- `pnpm flint` - Fix then lint (fix + lint) - -## Granular Commands -- `pnpm build` - Production build (runs gen-app-version first) -- `pnpm type-check` - TypeScript type checking -- `pnpm test` - Run all tests with Vitest -- `pnpm lint` - ESLint checking (quiet mode) - -## Development Commands -- `pnpm dev` - Start development server -- `pnpm gen-app-version` - Generate app version from git - -## Testing Framework -- **Testing Library**: Vitest v3.2.2 with jsdom environment -- **Test Location**: Module `tests/` folder or alongside source with `.test.ts` suffix -- **Coverage**: `pnpm test --coverage` -- **Single Test**: `pnpm test ` - -## Testing Requirements -- **Always update tests when changing code** -- **Remove orphaned tests** - no tests for deleted functionality -- Mock dependencies explicitly for domain/application logic -- Tests must pass before any task completion - -## Pre-Commit Requirements -**⛔ CRITICAL RULE: NEVER declare any implementation, fix, or feature "complete" without:** -1. Running `pnpm check` and verifying ALL checks pass -2. Confirming NO TypeScript errors -3. Confirming NO ESLint errors -4. Confirming ALL tests pass -5. Only then can you say "✅ COMPLETE" - -## Comprehensive Validation -- `pnpm copilot:check` - Must show "COPILOT: All checks passed!" for full validation \ No newline at end of file diff --git a/.serena/memories/todo-issue-relationship-pattern.md b/.serena/memories/todo-issue-relationship-pattern.md deleted file mode 100644 index 5bd0a34d7..000000000 --- a/.serena/memories/todo-issue-relationship-pattern.md +++ /dev/null @@ -1,20 +0,0 @@ -# TODO Comments and GitHub Issues Relationship - -## Key Pattern -- TODO comments in codebase are **NOT automatically linked** to GitHub issues -- Users consistently ask about existing issues before creating new ones -- Common workflow: TODO discovered → Check for existing issue → Create issue if needed - -## Search Strategy for TODO-Issue Correlation -1. Search codebase for TODO comments: `search_for_pattern` with `TODO|FIXME|XXX` -2. Check GitHub issues manually - no automated mapping exists -3. Look for issue references in commit messages related to TODO areas - -## Common TODO Areas Requiring Issue Tracking -- Recipe editing functionality (known limitation) -- Performance optimizations in search -- UI/UX improvements in food/recipe management -- Data validation and error handling - -## Efficiency Tip -When user asks "are there existing issues for X functionality", always search for relevant TODO comments first to understand scope before suggesting issue creation. \ No newline at end of file diff --git a/.serena/memories/typescript-patterns.md b/.serena/memories/typescript-patterns.md deleted file mode 100644 index 997b2b0f9..000000000 --- a/.serena/memories/typescript-patterns.md +++ /dev/null @@ -1,56 +0,0 @@ -# TypeScript Patterns - Macroflows - -## Critical Rules - -### ❌ FORBIDDEN Patterns -- **No `implements` keyword**: Never use class implements interface -- **No `class` keyword**: Never use classes at all -- **No `interface` keyword**: Always use `type` instead - -### ✅ Required Patterns - -**Factory Functions with Object Returns:** -```typescript -// ✅ Good: Factory function returning object -export function createLocalStorageRepository(): StorageRepository { - return { - getCachedWeights: (userId: User['uuid']) => { - // implementation - }, - setCachedWeights: (userId: User['uuid'], weights: readonly unknown[]) => { - // implementation - } - } -} - -// ❌ Forbidden: Classes -export class LocalStorageRepository implements StorageRepository { - // NEVER DO THIS -} - -// ❌ Forbidden: implements keyword -export class Repository implements Interface { - // NEVER DO THIS -} -``` - -**Type Definitions:** -```typescript -// ✅ Always use `type` -export type StorageRepository = { - getCachedWeights(userId: User['uuid']): readonly unknown[] - setCachedWeights(userId: User['uuid'], weights: readonly unknown[]): void -} - -// ❌ Never use interface -export interface StorageRepository { - // NEVER DO THIS -} -``` - -## Architecture Principles -- **Pure functional patterns** -- **Factory functions only** -- **Object returns, not class instances** -- **Type contracts without inheritance** -- **Composition over any OOP patterns** \ No newline at end of file diff --git a/.serena/memories/usecase-pattern-migration-complete.md b/.serena/memories/usecase-pattern-migration-complete.md deleted file mode 100644 index 1b5b33ea3..000000000 --- a/.serena/memories/usecase-pattern-migration-complete.md +++ /dev/null @@ -1,100 +0,0 @@ -# Usecase Pattern Migration - Complete Implementation - -## Summary - -Successfully applied the new usecase pattern from day-diets module to all recently refactored modules in the codebase, following the standardized architecture established in recent commits. - -## Analysis Results - -### Modules Already Following Usecase Pattern - -1. **day-diet** ✅ (Reference Implementation) - - `usecases/dayChange.ts` - Day change operations - - `usecases/dayCrud.ts` - CRUD operations - - `usecases/dayState.ts` - State management with re-exports - -2. **macro-profile** ✅ (Already Compliant) - - `usecases/macroProfileCrud.ts` - CRUD operations - - `usecases/macroProfileState.ts` - State management with re-exports - -3. **recent-food** ✅ (Already Compliant) - - `usecases/recentFoodCrud.ts` - CRUD operations only - -4. **recipe** ✅ (Already Compliant) - - `usecases/recipeCrud.ts` - CRUD operations only - -### Modules Requiring Refactoring - -#### 1. **measure** Module - REFACTORED ✅ - -**Before:** -- Single file `measureCrud.ts` with mixed concerns (state + CRUD + realtime) - -**After:** -- `usecases/measureCrud.ts` - Pure CRUD operations -- `usecases/measureState.ts` - State management with re-exports and realtime initialization - -**Changes:** -- Separated state management from CRUD operations -- Moved `createResource` and `refetchBodyMeasures` to state file -- Removed manual refetch calls from CRUD functions -- Updated all imports across codebase to use state file for reactive data - -#### 2. **food** Module - REFACTORED ✅ - -**Before:** -- Single file `food.ts` with all operations mixed together - -**After:** -- `usecases/foodCrud.ts` - All CRUD operations -- `food.ts` - Re-export file following the pattern - -**Changes:** -- Moved all functions to dedicated CRUD file -- Maintained all existing functionality and error handling -- Updated main application file to re-export from usecases - -## Pattern Consistency - -All modules now follow the standardized usecase pattern: - -``` -application/ -├── usecases/ -│ ├── [module]Crud.ts - Pure CRUD operations -│ └── [module]State.ts - State management + re-exports (if needed) -└── [module].ts - Re-export file (legacy compatibility) -``` - -## Quality Assurance - -- ✅ All TypeScript checks pass -- ✅ All ESLint checks pass -- ✅ All tests pass (310/310) -- ✅ No breaking changes to existing APIs -- ✅ Maintained backward compatibility through re-exports - -## Architectural Benefits - -1. **Clear Separation of Concerns**: CRUD operations separated from state management -2. **Consistent Patterns**: All modules follow the same organizational structure -3. **Maintainability**: Easier to locate and modify specific functionality -4. **Testability**: Pure CRUD functions easier to test in isolation -5. **Scalability**: Clear patterns for future module development - -## Files Modified - -### New Files Created: -- `src/modules/measure/application/usecases/measureState.ts` -- `src/modules/diet/food/application/usecases/foodCrud.ts` - -### Files Modified: -- `src/modules/measure/application/usecases/measureCrud.ts` -- `src/modules/diet/food/application/food.ts` -- `src/modules/measure/infrastructure/supabase/realtime.ts` -- `src/sections/profile/measure/components/BodyMeasureView.tsx` -- `src/sections/profile/measure/components/BodyMeasuresEvolution.tsx` - -## Migration Complete - -All recently refactored modules now consistently follow the usecase pattern established in day-diets. The codebase maintains architectural consistency while preserving all existing functionality. \ No newline at end of file diff --git a/.serena/memories/workflow-and-commands.md b/.serena/memories/workflow-and-commands.md deleted file mode 100644 index c94af4f3e..000000000 --- a/.serena/memories/workflow-and-commands.md +++ /dev/null @@ -1,39 +0,0 @@ -# Workflow and Commands (consolidated) - -This memory consolidates development workflow, commands, quality checks, and workflow optimization patterns. - -## Essential Commands -- `pnpm dev` — Start development server (local) -- `pnpm check` — Mandatory quality gate (lint, type-check, test) -- `pnpm fix` — Auto-fix ESLint issues -- `pnpm build` — Production build -- `pnpm type-check` — TypeScript checking -- `pnpm test` — Run Vitest tests -- `pnpm gen-app-version` — Generate app version -- `npm run copilot:check` — Run copilot checks as described in `copilot-instructions.md` - -## Claude Commands (helpers) -- `/fix` — Automated codebase checks and fixes -- `/review` — Code review helper -- `/commit` — Generate and execute conventional commit -- `/pull-request` or `/pr` — Create PR - -## Quality Gate & Check Strategy -1. Run `npm run copilot:check` when requested by workflows. -2. If failures occur: re-run up to 2 times, then collect logs and perform targeted diagnostics. -3. Always ensure `pnpm check` passes before declaring a task complete. - -## Optimization Patterns -- Batch related operations when safe (e.g., lint + typecheck + test). -- Use memory entries to cache common search patterns and workflow context for faster discovery. -- For solo projects, simplify coordination steps and rely on self-review + automated checks. - -## Issue Discovery -- Search TODO/FIXME/XXX patterns first (`search_for_pattern`). -- Verify existing issues before creating new ones. - -## Agent Handoff -- Load relevant memories before handing tasks to specialized agents (e.g., `workflow-optimization-patterns`, `project-context-macroflows`). -- Preserve context with a short summary and pointers to related memories. - -For detailed examples and specific shell commands, see `suggested_commands` and `development_workflow` memories. \ No newline at end of file diff --git a/.serena/memories/workflow-optimization-patterns.md b/.serena/memories/workflow-optimization-patterns.md deleted file mode 100644 index b1d9258b2..000000000 --- a/.serena/memories/workflow-optimization-patterns.md +++ /dev/null @@ -1,136 +0,0 @@ -# Workflow Optimization Patterns - -## Current Command Structure Analysis - -### Available Commands by Category - -**Workflow Commands:** -- `/commit` - Conventional commit generation and execution -- `/pull-request` (`/pr`) - PR creation with metadata - -**Quality Commands:** -- `/fix` - Comprehensive codebase validation and fixes -- `/review` - Code review for PR changes - -**Issue Commands:** -- `/create-issue [type]` - GitHub issue creation with templates -- `/implement ` - Autonomous issue implementation -- `/breakdown ` - Issue analysis for subdivision -- `/prioritize-milestone` - Milestone capacity optimization - -**Refactoring:** -- `/refactor` - Clean architecture improvements - -**Session:** -- `/end-session` (`/end`) - Knowledge export - -### Optimization Opportunities Identified - -#### 1. Issue Discovery Automation (Missing) -**Current Gap:** Manual search for existing issues before creating new ones -**Solution:** Create `/discover-issues` command that: -- Searches TODO comments for issue patterns -- Correlates with existing GitHub issues -- Provides consolidated discovery results -- Suggests next actions based on findings - -#### 2. Memory-Driven Command Enhancement -**Pattern:** Commands should proactively load relevant memories -**Implementation:** -- Issue commands → Load issue creation patterns -- Refactor commands → Load architecture guidelines -- Quality commands → Load code style standards - -#### 3. Context Preservation Between Agent Handoffs -**Problem:** Each agent starts fresh without previous context -**Solution:** Standardized context handoff protocol: -```typescript -interface WorkflowContext { - phase: 'discovery' | 'analysis' | 'implementation' | 'optimization' - codeAreas: string[] - relatedIssues: number[] - todoPatterns: string[] - previousFindings: Record -} -``` - -#### 4. Batched Operations -**Current:** Sequential tool execution -**Optimized:** Group related operations: -- Discovery: TODOs + GitHub search + code analysis -- Quality: lint + typecheck + test in parallel -- Implementation: code + tests + validation - -### Risk Assessment by Improvement Type - -#### **Low Risk Improvements:** -- ✅ Documentation and memory creation -- ✅ New convenience commands (non-breaking) -- ✅ Command parameter additions (optional) - -#### **Medium Risk Improvements:** -- ⚠️ Existing command modifications -- ⚠️ New workflow dependencies -- ⚠️ Template structure changes - -#### **High Risk Improvements:** -- 🔴 Command structure modifications -- 🔴 Breaking workflow changes -- 🔴 Integration point modifications - -### Recommended Implementation Order - -1. **Memory Creation** (Low Risk) - - Workflow patterns documentation - - Command optimization guidelines - - Context handoff protocols - -2. **New Commands** (Low Risk) - - `/discover-issues` for automation - - `/workflow-status` for context awareness - - `/memory-load` for context preparation - -3. **Command Enhancement** (Medium Risk) - - Add memory loading to existing commands - - Enhance with context awareness - - Improve error handling and recovery - -4. **Workflow Orchestration** (High Risk) - - Implement cross-command state management - - Create intelligent command routing - - Build predictive workflow assistance - -### Success Metrics - -**Efficiency Improvements:** -- Reduced tool calls for equivalent outcomes -- Faster task completion through batching -- Higher first-attempt success rates - -**Context Retention:** -- Better information reuse between commands -- Reduced redundant discovery operations -- Improved workflow continuity - -**User Experience:** -- More intuitive command suggestions -- Better error recovery and guidance -- Clearer progress tracking - -### Technical Implementation Notes - -**Command Structure:** -- All commands are Markdown files in `.claude/commands/` -- Organized by category (workflow, quality, issues, etc.) -- Include usage, description, and integration details - -**Quality Integration:** -- Commands integrate with `pnpm check` validation -- Support project-specific quality gates -- Handle error recovery and retry logic - -**Project Specifics:** -- Solo project focus (no team coordination) -- SolidJS patterns and reactive programming -- Supabase integration patterns -- Clean architecture enforcement \ No newline at end of file diff --git a/.serena/project.yml b/.serena/project.yml deleted file mode 100644 index 9cf3f76ee..000000000 --- a/.serena/project.yml +++ /dev/null @@ -1,150 +0,0 @@ -# whether to use the project's gitignore file to ignore files -# Added on 2025-04-07 -ignore_all_files_in_gitignore: true -# list of additional paths to ignore -# same syntax as gitignore, so you can use * and ** -# Was previously called `ignored_dirs`, please update your config if you are using that. -# Added (renamed)on 2025-04-07 -ignored_paths: [] - -# whether the project is in read-only mode -# If set to true, all editing tools will be disabled and attempts to use them will result in an error -# Added on 2025-04-18 -read_only: false - - -# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details. -# Below is the complete list of tools for convenience. -# To make sure you have the latest list of tools, and to view their descriptions, -# execute `uv run scripts/print_tool_overview.py`. -# -# * `activate_project`: Activates a project by name. -# * `check_onboarding_performed`: Checks whether project onboarding was already performed. -# * `create_text_file`: Creates/overwrites a file in the project directory. -# * `delete_lines`: Deletes a range of lines within a file. -# * `delete_memory`: Deletes a memory from Serena's project-specific memory store. -# * `execute_shell_command`: Executes a shell command. -# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced. -# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type). -# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type). -# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes. -# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file or directory. -# * `initial_instructions`: Gets the initial instructions for the current project. -# Should only be used in settings where the system prompt cannot be set, -# e.g. in clients you have no control over, like Claude Desktop. -# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol. -# * `insert_at_line`: Inserts content at a given line in a file. -# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol. -# * `list_dir`: Lists files and directories in the given directory (optionally with recursion). -# * `list_memories`: Lists memories in Serena's project-specific memory store. -# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building). -# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context). -# * `read_file`: Reads a file within the project directory. -# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store. -# * `remove_project`: Removes a project from the Serena configuration. -# * `replace_lines`: Replaces a range of lines within a file with new content. -# * `replace_symbol_body`: Replaces the full definition of a symbol. -# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen. -# * `search_for_pattern`: Performs a search for a pattern in the project. -# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase. -# * `switch_modes`: Activates modes by providing a list of their names -# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information. -# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task. -# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed. -# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store. -excluded_tools: [] - -# initial prompt for the project. It will always be given to the LLM upon activating the project -# (contrary to the memories, which are loaded on demand). -initial_prompt: "" -# the name by which the project can be referenced within Serena -project_name: "macroflows" - -# list of mode names to that are always to be included in the set of active modes -# The full set of modes to be activated is base_modes + default_modes. -# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply. -# Otherwise, this setting overrides the global configuration. -# Set this to [] to disable base modes for this project. -# Set this to a list of mode names to always include the respective modes for this project. -base_modes: - -# list of mode names that are to be activated by default. -# The full set of modes to be activated is base_modes + default_modes. -# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply. -# Otherwise, this overrides the setting from the global configuration (serena_config.yml). -# This setting can, in turn, be overridden by CLI parameters (--mode). -default_modes: - -# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default) -included_optional_tools: [] - -# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools. -# This cannot be combined with non-empty excluded_tools or included_optional_tools. -fixed_tools: [] - -# the encoding used by text files in the project -# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings -encoding: utf-8 - - -# list of languages for which language servers are started; choose from: -# al bash clojure cpp csharp -# csharp_omnisharp dart elixir elm erlang -# fortran fsharp go groovy haskell -# java julia kotlin lua markdown -# matlab nix pascal perl php -# powershell python python_jedi r rego -# ruby ruby_solargraph rust scala swift -# terraform toml typescript typescript_vts vue -# yaml zig -# (This list may be outdated. For the current list, see values of Language enum here: -# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py -# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) -# Note: -# - For C, use cpp -# - For JavaScript, use typescript -# - For Free Pascal/Lazarus, use pascal -# Special requirements: -# Some languages require additional setup/installations. -# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers -# When using multiple languages, the first language server that supports a given file will be used for that file. -# The first language is the default language and the respective language server will be used as a fallback. -# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. -languages: -- typescript - -# time budget (seconds) per tool call for the retrieval of additional symbol information -# such as docstrings or parameter information. -# This overrides the corresponding setting in the global configuration; see the documentation there. -# If null or missing, use the setting from the global configuration. -symbol_info_budget: - -# The language backend to use for this project. -# If not set, the global setting from serena_config.yml is used. -# Valid values: LSP, JetBrains -# Note: the backend is fixed at startup. If a project with a different backend -# is activated post-init, an error will be returned. -language_backend: - -# list of regex patterns which, when matched, mark a memory entry as read‑only. -# Extends the list from the global configuration, merging the two lists. -read_only_memory_patterns: [] - -# line ending convention to use when writing source files. -# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) -# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. -line_ending: - -# list of regex patterns for memories to completely ignore. -# Matching memories will not appear in list_memories or activate_project output -# and cannot be accessed via read_memory or write_memory. -# To access ignored memory files, use the read_file tool on the raw file path. -# Extends the list from the global configuration, merging the two lists. -# Example: ["_archive/.*", "_episodes/.*"] -ignored_memory_patterns: [] - -# advanced configuration option allowing to configure language server-specific options. -# Maps the language key to the options. -# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available. -# No documentation on options means no options are available. -ls_specific_settings: {} From 7cfbe09f04aaae586292a43a47c69c2ad17afdbf Mon Sep 17 00:00:00 2001 From: marcuscastelo Date: Wed, 25 Mar 2026 12:33:36 -0300 Subject: [PATCH 06/10] docs(agents): add serena mcp usage guidelines and tool glossary --- .github/copilot-instructions.md | 2 ++ AGENTS.md | 40 +++++++++++++++++++++++++++++++++ CLAUDE.md | 2 ++ GEMINI.md | 2 ++ 4 files changed, 46 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 0943f0efc..b34d527a2 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -10,6 +10,8 @@ Canonical repo policy lives in [../AGENTS.md](../AGENTS.md). If anything in this file conflicts with `AGENTS.md` or the canonical docs, the canonical docs win. +Prefer Serena MCP whenever it can handle project activation, onboarding, file or symbol work, memory operations, refactors, edits, or shell execution. The brief Serena tool glossary lives in `AGENTS.md`; follow the project naming rules from there at the start of each chat. + ## Copilot-specific notes - Keep the `applyTo` frontmatter so Copilot discovers this file automatically. diff --git a/AGENTS.md b/AGENTS.md index 5bcf92ab0..69664856a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,6 +35,46 @@ Only canonical docs belong in read-first lists. - Use `pnpm check` as the default repository quality gate. - Use `pnpm build`, `pnpm test`, and `pnpm lint` for targeted validation when needed. +## Serena MCP + +- Prefer Serena MCP tools whenever they can perform the task. Use them first for project activation, onboarding, file and symbol discovery, memory operations, refactors, edits, and shell execution. +- At the start of each chat, determine the active project from the current `pwd` and activate it before doing other project-aware work. +- Keep one Serena project per worktree, with the project name derived from the base repo name: + - `/home/marucs/Development/macroflows/macroflows/` -> `macroflows` + - `/home/marucs/Development/macroflows/wt//` -> `macroflows-` +- After activation, check whether onboarding was already performed before doing anything substantial. +- Use `delete_memory` only when the user explicitly asks for it. +- If Serena is unavailable or a task is outside Serena's scope, fall back to the next-best local tool. + +### Serena tool glossary + +- `activate_project`: opens the Serena project for the current repo or worktree. +- `check_onboarding_performed`: verifies whether the project already has onboarding data. +- `create_text_file`: creates or overwrites a text file inside the project. +- `delete_memory`: deletes a memory file, only when the user explicitly asks. +- `edit_memory`: updates a memory file by replacing text that matches a pattern. +- `execute_shell_command`: runs a shell command inside the project context. +- `find_file`: finds files by name or path pattern. +- `find_referencing_symbols`: finds code symbols that reference another symbol. +- `find_symbol`: finds code symbols by name, scope, or path. +- `get_current_config`: shows the active Serena configuration, projects, tools, contexts, and modes. +- `get_symbols_overview`: lists the top-level symbols in a file. +- `initial_instructions`: shows Serena usage instructions when the client does not load them automatically. +- `insert_after_symbol`: inserts new content after a symbol definition. +- `insert_before_symbol`: inserts new content before a symbol definition. +- `list_dir`: lists files and folders in a directory. +- `list_memories`: lists available memory files for the project. +- `onboarding`: records project structure and basic working knowledge for Serena. +- `prepare_for_new_conversation`: prepares the project context for a new chat. +- `read_file`: reads a file from the project directory. +- `read_memory`: reads a memory file when it is relevant to the task. +- `rename_memory`: renames or moves a memory file. +- `rename_symbol`: renames a code symbol across the codebase. +- `replace_content`: replaces text in a file, optionally with a regex. +- `replace_symbol_body`: replaces the full body of a symbol. +- `search_for_pattern`: searches the project for arbitrary text or regex patterns. +- `write_memory`: writes a new memory file with useful project context. + ### Repo-wide defaults - Prefer clear, explicit code over scaffolding-heavy abstractions. diff --git a/CLAUDE.md b/CLAUDE.md index bbd42d63e..b3570a9ab 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,6 +7,8 @@ Canonical repo policy lives in [AGENTS.md](./AGENTS.md). If anything in this file conflicts with `AGENTS.md` or the canonical docs, the canonical docs win. +Prefer Serena MCP whenever it can handle project activation, onboarding, file or symbol work, memory operations, refactors, edits, or shell execution. The brief Serena tool glossary lives in `AGENTS.md`; follow the project naming rules from there at the start of each chat. + ## Claude-specific notes - Claude transport and permissions for this repo live in `.claude/settings.json`. diff --git a/GEMINI.md b/GEMINI.md index 71e536011..a2b757c4f 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -10,6 +10,8 @@ Canonical repo policy lives in [AGENTS.md](./AGENTS.md). If anything in this file conflicts with `AGENTS.md` or the canonical docs, the canonical docs win. +Prefer Serena MCP whenever it can handle project activation, onboarding, file or symbol work, memory operations, refactors, edits, or shell execution. The brief Serena tool glossary lives in `AGENTS.md`; follow the project naming rules from there at the start of each chat. + ## Gemini-specific notes - Gemini transport and MCP configuration for this repo live in `.gemini/settings.json`. From a04e018473628d41b791acab70812c8adb06fb15 Mon Sep 17 00:00:00 2001 From: marcuscastelo Date: Wed, 25 Mar 2026 12:36:57 -0300 Subject: [PATCH 07/10] refactor(mcp): streamline serena server configuration --- .gemini/settings.json | 61 ----------------------------- .mcp.json | 45 ---------------------- .vscode/mcp.json | 70 +++++++++++++++++++--------------- scripts/start-serena-server.sh | 1 - 4 files changed, 39 insertions(+), 138 deletions(-) delete mode 100644 .gemini/settings.json delete mode 100644 .mcp.json delete mode 100755 scripts/start-serena-server.sh diff --git a/.gemini/settings.json b/.gemini/settings.json deleted file mode 100644 index 9478aa0c4..000000000 --- a/.gemini/settings.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "coreTools": [ - "run_shell_command", - "google_web_search", - "web_fetch" - ], - "sequential-thinking": { - "command": "npx", - "args": [ - "-y", - "@modelcontextprotocol/server-sequential-thinking" - ] - }, - "mcpServers": { - "serena": { - "command": "bash", - "args": [ - "scripts/start-serena-server.sh" - ] - }, - "playwright": { - "command": "npx", - "args": [ - "@playwright/mcp@latest" - ] - }, - "github": { - "command": "bash", - "args": [ - "scripts/start-github-mcp-server.sh" - ] - }, - "context7": { - "command": "npx", - "args": [ - "-y", - "@upstash/context7-mcp" - ] - }, - "mcp-compass": { - "command": "npx", - "args": [ - "-y", - "@liuyoshio/mcp-compass" - ] - }, - "time": { - "command": "uvx", - "args": [ - "mcp-server-time", - "--local-timezone=America/Sao_Paulo" - ] - }, - "language-server": { - "command": "bash", - "args": [ - "scripts/start-language-server.sh" - ] - } - } -} \ No newline at end of file diff --git a/.mcp.json b/.mcp.json deleted file mode 100644 index 10ec3f5b6..000000000 --- a/.mcp.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "mcpServers": { - "serena": { - "type": "stdio", - "command": "bash", - "args": [ - "scripts/start-serena-server.sh" - ], - "env": {} - }, - "playwright": { - "command": "npx", - "args": [ - "@playwright/mcp@latest" - ] - }, - "context7": { - "command": "npx", - "args": [ - "-y", - "@upstash/context7-mcp" - ] - }, - "mcp-compass": { - "command": "npx", - "args": [ - "-y", - "@liuyoshio/mcp-compass" - ] - }, - "time": { - "command": "uvx", - "args": [ - "mcp-server-time", - "--local-timezone=America/Sao_Paulo" - ] - }, - "language-server": { - "command": "bash", - "args": [ - "scripts/start-language-server.sh" - ] - } - } -} \ No newline at end of file diff --git a/.vscode/mcp.json b/.vscode/mcp.json index fa56c013f..a623ead65 100644 --- a/.vscode/mcp.json +++ b/.vscode/mcp.json @@ -1,32 +1,40 @@ { - "inputs": [ - { - "type": "promptString", - "id": "github_token", - "description": "GitHub Personal Access Token", - "password": true - } - ], - "servers": { - "memory": { - "command": "npx", - "args": [ - "-y", - "@modelcontextprotocol/server-memory" - ], - "env": { - "MEMORY_FILE_PATH": "${workspaceFolder}/memory.jsonl" - } - }, - "playwright": { - "command": "npx", - "args": [ - "@playwright/mcp@latest" - ] - }, - "github": { - "type": "http", - "url": "https://api.githubcopilot.com/mcp/" - } - } -} \ No newline at end of file + "inputs": [ + { + "type": "promptString", + "id": "github_token", + "description": "GitHub Personal Access Token", + "password": true + } + ], + "servers": { + "serena": { + "command": "uvx", + "args": [ + "--from", + "git+https://github.com/oraios/serena", + "serena", + "start-mcp-server", + "--context", + "ide", + "--project", + "${workspaceFolder}" + ] + }, + "memory": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-memory"], + "env": { + "MEMORY_FILE_PATH": "${workspaceFolder}/memory.jsonl" + } + }, + "playwright": { + "command": "npx", + "args": ["@playwright/mcp@latest"] + }, + "github": { + "type": "http", + "url": "https://api.githubcopilot.com/mcp/" + } + } +} diff --git a/scripts/start-serena-server.sh b/scripts/start-serena-server.sh deleted file mode 100755 index bc67efe57..000000000 --- a/scripts/start-serena-server.sh +++ /dev/null @@ -1 +0,0 @@ -uvx --from git+https://github.com/oraios/serena serena-mcp-server --context agent --transport stdio --enable-web-dashboard false --project $(pwd) \ No newline at end of file From 87026301808798322cdbf71c581e960b94bdeced Mon Sep 17 00:00:00 2001 From: marcuscastelo Date: Wed, 25 Mar 2026 12:52:21 -0300 Subject: [PATCH 08/10] chore(gitignore): add .gitignore to exclude cache and project configuration files --- .serena/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .serena/.gitignore diff --git a/.serena/.gitignore b/.serena/.gitignore new file mode 100644 index 000000000..d8f93077c --- /dev/null +++ b/.serena/.gitignore @@ -0,0 +1,4 @@ +/cache +/project.local.yml +# Ignore project.yml to allow multi-worktree support without conflicts. Each worktree can have its own project.yml file. +/project.yml \ No newline at end of file From 0b069f1eb4781df221a7acaf01d6054c90152af1 Mon Sep 17 00:00:00 2001 From: marcuscastelo Date: Wed, 25 Mar 2026 12:53:28 -0300 Subject: [PATCH 09/10] feat(worktrees): add scripts for managing git worktrees and PRD files --- tools/worktrees/lib/common.sh | 374 ++++++++++++++++++++++++++++++++ tools/worktrees/lib/usage.sh | 20 ++ tools/worktrees/seed.allowlist | 4 + tools/worktrees/wt-create.sh | 231 ++++++++++++++++++++ tools/worktrees/wt-implement.sh | 134 ++++++++++++ tools/worktrees/wt-prune.sh | 327 ++++++++++++++++++++++++++++ 6 files changed, 1090 insertions(+) create mode 100755 tools/worktrees/lib/common.sh create mode 100755 tools/worktrees/lib/usage.sh create mode 100644 tools/worktrees/seed.allowlist create mode 100755 tools/worktrees/wt-create.sh create mode 100755 tools/worktrees/wt-implement.sh create mode 100755 tools/worktrees/wt-prune.sh diff --git a/tools/worktrees/lib/common.sh b/tools/worktrees/lib/common.sh new file mode 100755 index 000000000..b7a2da435 --- /dev/null +++ b/tools/worktrees/lib/common.sh @@ -0,0 +1,374 @@ +#!/usr/bin/env bash + +wt_info() { + printf '%s\n' "$*" +} + +wt_error() { + printf '%s\n' "$*" >&2 +} + +wt_require_value() { + local flag="$1" + local value="${2:-}" + + if [ -z "$value" ]; then + wt_error "Missing value for $flag" + return 1 + fi +} + +wt_is_truthy_env() { + local value="${1:-}" + + case "$value" in + 1|true|TRUE|yes|YES|on|ON) + return 0 + ;; + *) + return 1 + ;; + esac +} + +wt_is_devcontainer() { + if wt_is_truthy_env "${DEVCONTAINER:-}"; then + return 0 + fi + + if wt_is_truthy_env "${REMOTE_CONTAINERS:-}"; then + return 0 + fi + + if [ -n "${REMOTE_CONTAINERS_IPC:-}" ]; then + return 0 + fi + + if [ -n "${CODESPACES:-}" ]; then + return 0 + fi + + return 1 +} + +wt_default_root() { + if wt_is_devcontainer && [ -n "${HOME:-}" ]; then + printf '%s\n' "${HOME%/}/wt" + return 0 + fi + + printf '%s\n' "../wt" +} + +wt_repo_root() { + if ! git rev-parse --show-toplevel >/dev/null 2>&1; then + wt_error "Could not detect repository root from current directory." + return 1 + fi + + git rev-parse --show-toplevel +} + +wt_abs_path() { + local raw_path="$1" + local file_dir + local file_name + + file_dir="$(cd "$(dirname "$raw_path")" && pwd -P)" + file_name="$(basename "$raw_path")" + printf '%s/%s\n' "$file_dir" "$file_name" +} + +wt_validate_prd_path() { + local raw_path="$1" + local prd_abs_path + local repo_root + local tasks_root + + if [ ! -e "$raw_path" ]; then + wt_error "PRD file not found: $raw_path" + return 1 + fi + + if [ ! -f "$raw_path" ]; then + wt_error "PRD path must be a file: $raw_path" + return 1 + fi + + case "$raw_path" in + *.md) ;; + *) + wt_error "PRD file must use .md extension: $raw_path" + return 1 + ;; + esac + + prd_abs_path="$(wt_abs_path "$raw_path")" + repo_root="$(wt_repo_root)" || return 1 + tasks_root="$repo_root/tasks" + + case "$prd_abs_path" in + "$tasks_root"/*) + printf '%s\n' "$prd_abs_path" + ;; + *) + wt_error "PRD file must be under tasks/: $raw_path" + return 1 + ;; + esac +} + +wt_slugify() { + local raw="$1" + local normalized + + normalized="$( + printf '%s' "$raw" \ + | tr '[:upper:]' '[:lower:]' \ + | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//; s/-+/-/g' + )" + + if [ -z "$normalized" ]; then + wt_error "Could not derive a valid slug from input: $raw" + return 1 + fi + + printf '%s\n' "$normalized" +} + +wt_derive_slug_from_prd() { + local prd_path="$1" + local filename + local stem + + filename="$(basename "$prd_path")" + stem="${filename%.md}" + wt_slugify "$stem" +} + +wt_resolve_slug() { + local prd_path="$1" + local slug_override="$2" + + if [ -n "$slug_override" ]; then + wt_slugify "$slug_override" + return + fi + + wt_derive_slug_from_prd "$prd_path" +} + +wt_build_branch_name() { + local branch_prefix="$1" + local slug="$2" + + if [ -z "$branch_prefix" ]; then + wt_error "Branch prefix must not be empty." + return 1 + fi + + case "$branch_prefix" in + */) printf '%s%s\n' "$branch_prefix" "$slug" ;; + *) printf '%s/%s\n' "$branch_prefix" "$slug" ;; + esac +} + +wt_join_path() { + local root="$1" + local leaf="$2" + local trimmed_root + + if [ "$root" = "/" ]; then + printf '/%s\n' "$leaf" + return + fi + + trimmed_root="${root%/}" + printf '%s/%s\n' "$trimmed_root" "$leaf" +} + +wt_branch_exists() { + local branch_name="$1" + git show-ref --verify --quiet "refs/heads/$branch_name" +} + +wt_worktree_branch() { + local worktree_path="$1" + git -C "$worktree_path" rev-parse --abbrev-ref HEAD 2>/dev/null +} + +wt_create_worktree() { + local worktree_path="$1" + local branch_name="$2" + local checked_branch + + if [ -e "$worktree_path" ]; then + wt_error "Worktree path already exists: $worktree_path" + wt_error "Choose a different slug (--slug) or worktree root (--wt-root)." + return 1 + fi + + if wt_branch_exists "$branch_name"; then + wt_error "Branch already exists: $branch_name" + wt_error "By default, existing branches are not reused." + wt_error "Next step: git worktree add \"$worktree_path\" \"$branch_name\"" + return 1 + fi + + if ! git worktree add -b "$branch_name" "$worktree_path"; then + wt_error "Failed to create worktree at $worktree_path on branch $branch_name." + return 1 + fi + + if ! checked_branch="$(wt_worktree_branch "$worktree_path")"; then + wt_error "Created worktree but could not read current branch: $worktree_path" + return 1 + fi + + if [ "$checked_branch" != "$branch_name" ]; then + wt_error "Worktree branch mismatch. Expected $branch_name, got $checked_branch." + return 1 + fi +} + +wt_copy_prd_to_worktree() { + local source_prd_path="$1" + local worktree_path="$2" + local repo_root + local source_abs_path + local rel_path + local target_path + local target_dir + + repo_root="$(wt_repo_root)" || return 1 + source_abs_path="$(wt_abs_path "$source_prd_path")" + + case "$source_abs_path" in + "$repo_root"/tasks/*) + rel_path="${source_abs_path#"$repo_root/"}" + ;; + *) + wt_error "PRD source must be under tasks/: $source_prd_path" + return 1 + ;; + esac + + target_path="$worktree_path/$rel_path" + target_dir="$(dirname "$target_path")" + + mkdir -p "$target_dir" + + if ! cp -f "$source_abs_path" "$target_path"; then + wt_error "Failed to copy PRD into worktree: $rel_path" + return 1 + fi + + wt_info "PRD copied: $rel_path" +} + +wt_seed_allowlist_path() { + local repo_root + + repo_root="$(wt_repo_root)" || return 1 + printf '%s\n' "$repo_root/tools/worktrees/seed.allowlist" +} + +wt_trim_whitespace() { + local value="$1" + printf '%s' "$value" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//' +} + +wt_seed_from_allowlist() { + local worktree_path="$1" + local force_seed="${2:-0}" + local repo_root + local allowlist + + repo_root="$(wt_repo_root)" || return 1 + allowlist="$(wt_seed_allowlist_path)" || return 1 + + if [ ! -f "$allowlist" ]; then + wt_error "Seed allowlist not found: $allowlist" + return 1 + fi + + while IFS= read -r line || [ -n "$line" ]; do + local item + local source_path + local target_path + local target_dir + + item="${line%%#*}" + item="$(wt_trim_whitespace "$item")" + + if [ -z "$item" ]; then + continue + fi + + source_path="$repo_root/$item" + target_path="$worktree_path/$item" + target_dir="$(dirname "$target_path")" + + if [ ! -f "$source_path" ]; then + wt_info "Seed skip (missing source): $item" + continue + fi + + if [ -e "$target_path" ] && [ "$force_seed" -ne 1 ]; then + wt_info "Seed skip (already exists, use --force-seed): $item" + continue + fi + + mkdir -p "$target_dir" + if ! cp -f "$source_path" "$target_path"; then + wt_error "Failed to seed file: $item" + return 1 + fi + + wt_info "Seed copied: $item" + done < "$allowlist" +} + +wt_run_pnpm_install() { + local worktree_path="$1" + + if ! command -v pnpm >/dev/null 2>&1; then + wt_error "pnpm command not found in PATH." + return 1 + fi + + wt_info "Running pnpm install in $worktree_path" + + if ! (cd "$worktree_path" && pnpm install); then + wt_error "pnpm install failed in worktree: $worktree_path" + return 1 + fi +} + +wt_init_ralph_submodule() { + local worktree_path="$1" + + wt_info "Initializing submodule: tools/ralph-loop" + + if ! git -C "$worktree_path" submodule update --init --recursive tools/ralph-loop; then + wt_error "Failed to initialize tools/ralph-loop in worktree: $worktree_path" + return 1 + fi +} + +wt_open_vscode() { + local worktree_path="$1" + + if ! command -v code >/dev/null 2>&1; then + wt_info "VS Code CLI not found. Open manually: code -n \"$worktree_path\"" + return 0 + fi + + if code -n "$worktree_path"; then + wt_info "Opened VS Code: $worktree_path" + return 0 + fi + + wt_info "Could not open VS Code automatically. Open manually: code -n \"$worktree_path\"" + return 0 +} diff --git a/tools/worktrees/lib/usage.sh b/tools/worktrees/lib/usage.sh new file mode 100755 index 000000000..3bfac6297 --- /dev/null +++ b/tools/worktrees/lib/usage.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +wt_usage() { + local default_wt_root + default_wt_root="$(wt_default_root)" + + cat < [options] + must point to an existing tasks/*.md file + +Options: + --wt-root Worktree root directory (default: $default_wt_root) + --branch-prefix Branch prefix (default: feat/) + --slug Override inferred slug + --force-seed Allow reseeding files from allowlist + --no-open Skip VS Code open attempt + --print-only Print computed plan without writing files + -h, --help Show this help message +EOF +} diff --git a/tools/worktrees/seed.allowlist b/tools/worktrees/seed.allowlist new file mode 100644 index 000000000..e9485929f --- /dev/null +++ b/tools/worktrees/seed.allowlist @@ -0,0 +1,4 @@ +# Paths are relative to repository root. +# Copy only files listed here from main worktree to the new worktree. + +.env diff --git a/tools/worktrees/wt-create.sh b/tools/worktrees/wt-create.sh new file mode 100755 index 000000000..45e2c64c4 --- /dev/null +++ b/tools/worktrees/wt-create.sh @@ -0,0 +1,231 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=./lib/common.sh +source "$SCRIPT_DIR/lib/common.sh" +# shellcheck source=./lib/usage.sh +source "$SCRIPT_DIR/lib/usage.sh" + +wt_create_usage() { + cat <<'EOF' +Usage: + wt-create.sh [options] + +Description: + Creates a git worktree for a new or existing branch, seeds allowed files, + copies .env, runs pnpm install + +Arguments: + Full branch name to create/use (examples: feat/foo, fix/bar) + +Options: + --wt-root Override worktree root + --slug Override worktree folder slug + --force-seed Overwrite seedable files + --no-open Do not open VS Code + --print-only Print resolved values without changing anything + -h, --help Show this help +EOF +} + +wt_branch_exists_local() { + local branch_name="$1" + git show-ref --verify --quiet "refs/heads/$branch_name" +} + +wt_branch_exists_remote() { + local branch_name="$1" + git show-ref --verify --quiet "refs/remotes/origin/$branch_name" +} + +wt_branch_checked_out_elsewhere() { + local branch_name="$1" + + git worktree list --porcelain | awk -v branch="refs/heads/$branch_name" ' + $1 == "branch" && $2 == branch { found=1 } + END { exit(found ? 0 : 1) } + ' +} + +wt_resolve_slug_from_branch() { + local branch_name="$1" + local slug + + slug="${branch_name##*/}" + slug="$(printf '%s' "$slug" \ + | tr '[:upper:]' '[:lower:]' \ + | sed -E 's/[^a-z0-9._-]+/-/g; s/^-+//; s/-+$//; s/-{2,}/-/g')" + + if [ -z "$slug" ]; then + wt_error "Could not derive a valid slug from branch: $branch_name" + return 1 + fi + + printf '%s\n' "$slug" +} + +wt_copy_root_env_to_worktree() { + local repo_root="$1" + local worktree_path="$2" + local force_seed="$3" + local source_env="$repo_root/.env" + local target_env="$worktree_path/.env" + + if [ ! -f "$source_env" ]; then + wt_info "No root .env found at $source_env. Skipping .env copy." + return 0 + fi + + if [ -f "$target_env" ] && [ "$force_seed" -ne 1 ]; then + wt_info ".env already exists in worktree. Skipping copy." + return 0 + fi + + cp "$source_env" "$target_env" + wt_info "Copied .env to worktree." +} + +wt_create_or_attach_worktree() { + local repo_root="$1" + local worktree_path="$2" + local branch_name="$3" + + if [ -e "$worktree_path" ]; then + wt_error "Worktree path already exists: $worktree_path" + return 1 + fi + + if wt_branch_exists_local "$branch_name"; then + if wt_branch_checked_out_elsewhere "$branch_name"; then + wt_error "Branch is already checked out in another worktree: $branch_name" + return 1 + fi + + git -C "$repo_root" worktree add "$worktree_path" "$branch_name" + return 0 + fi + + if wt_branch_exists_remote "$branch_name"; then + git -C "$repo_root" worktree add -b "$branch_name" "$worktree_path" "origin/$branch_name" + git -C "$worktree_path" branch --set-upstream-to="origin/$branch_name" "$branch_name" >/dev/null 2>&1 || true + return 0 + fi + + git -C "$repo_root" worktree add -b "$branch_name" "$worktree_path" +} + +if [ "${1:-}" = "--" ]; then + shift +fi + +if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then + wt_create_usage + exit 0 +fi + +if [ $# -lt 1 ]; then + wt_error "Missing required argument: " + wt_create_usage + exit 1 +fi + +branch_name="$1" +shift + +wt_root="$(wt_default_root)" +slug="" +force_seed=0 +no_open=0 +print_only=0 + +while [ $# -gt 0 ]; do + case "$1" in + --wt-root) + wt_require_value "--wt-root" "${2:-}" + wt_root="$2" + shift 2 + ;; + --slug) + wt_require_value "--slug" "${2:-}" + slug="$2" + shift 2 + ;; + --force-seed) + force_seed=1 + shift + ;; + --no-open) + no_open=1 + shift + ;; + --print-only) + print_only=1 + shift + ;; + -h|--help) + wt_create_usage + exit 0 + ;; + *) + wt_error "Unknown option: $1" + wt_create_usage + exit 1 + ;; + esac +done + +repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)" +if [ -z "$repo_root" ]; then + wt_error "Current directory is not inside a git repository." + exit 1 +fi + +if [ -z "$slug" ]; then + if ! slug="$(wt_resolve_slug_from_branch "$branch_name")"; then + exit 1 + fi +fi + +worktree_path="$(wt_join_path "$wt_root" "$slug")" + +wt_info "wt-create scaffold initialized." +wt_info "Repo root: $repo_root" +wt_info "Worktree root: $wt_root" +wt_info "Slug: $slug" +wt_info "Branch: $branch_name" +wt_info "Worktree path: $worktree_path" +wt_info "force-seed: $force_seed" +wt_info "no-open: $no_open" +wt_info "print-only: $print_only" + +if [ "$print_only" -eq 1 ]; then + wt_info "Print-only mode enabled. No git worktree changes were made." + exit 0 +fi + +if ! wt_create_or_attach_worktree "$repo_root" "$worktree_path" "$branch_name"; then + exit 1 +fi + +wt_info "Created git worktree: $worktree_path" +wt_info "Checked out branch: $branch_name" + +if ! wt_seed_from_allowlist "$worktree_path" "$force_seed"; then + exit 1 +fi + +if ! wt_copy_root_env_to_worktree "$repo_root" "$worktree_path" "$force_seed"; then + exit 1 +fi + +if ! wt_run_pnpm_install "$worktree_path"; then + exit 1 +fi + +if [ "$no_open" -eq 1 ]; then + wt_info "Skipping VS Code open (--no-open)." + exit 0 +fi + +wt_open_vscode "$worktree_path" \ No newline at end of file diff --git a/tools/worktrees/wt-implement.sh b/tools/worktrees/wt-implement.sh new file mode 100755 index 000000000..4f538cb99 --- /dev/null +++ b/tools/worktrees/wt-implement.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=./lib/common.sh +source "$SCRIPT_DIR/lib/common.sh" +# shellcheck source=./lib/usage.sh +source "$SCRIPT_DIR/lib/usage.sh" + +if [ "${1:-}" = "--" ]; then + shift +fi + +if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then + wt_usage + exit 0 +fi + +if [ $# -lt 1 ]; then + wt_error "Missing required argument: " + wt_usage + exit 1 +fi + +prd_path="$1" +shift + +if ! prd_path="$(wt_validate_prd_path "$prd_path")"; then + exit 1 +fi + +wt_root="$(wt_default_root)" +branch_prefix="feat/" +slug="" +force_seed=0 +no_open=0 +print_only=0 + +while [ $# -gt 0 ]; do + case "$1" in + --wt-root) + wt_require_value "--wt-root" "${2:-}" + wt_root="$2" + shift 2 + ;; + --branch-prefix) + wt_require_value "--branch-prefix" "${2:-}" + branch_prefix="$2" + shift 2 + ;; + --slug) + wt_require_value "--slug" "${2:-}" + slug="$2" + shift 2 + ;; + --force-seed) + force_seed=1 + shift + ;; + --no-open) + no_open=1 + shift + ;; + --print-only) + print_only=1 + shift + ;; + -h|--help) + wt_usage + exit 0 + ;; + *) + wt_error "Unknown option: $1" + wt_usage + exit 1 + ;; + esac +done + +if ! slug="$(wt_resolve_slug "$prd_path" "$slug")"; then + exit 1 +fi + +if ! branch_name="$(wt_build_branch_name "$branch_prefix" "$slug")"; then + exit 1 +fi + +worktree_path="$(wt_join_path "$wt_root" "$slug")" + +wt_info "wt-implement scaffold initialized." +wt_info "PRD path: $prd_path" +wt_info "Worktree root: $wt_root" +wt_info "Branch prefix: $branch_prefix" +wt_info "Slug: $slug" +wt_info "Branch: $branch_name" +wt_info "Worktree path: $worktree_path" +wt_info "force-seed: $force_seed" +wt_info "no-open: $no_open" +wt_info "print-only: $print_only" + +if [ "$print_only" -eq 1 ]; then + wt_info "Print-only mode enabled. No git worktree changes were made." + exit 0 +fi + +if ! wt_create_worktree "$worktree_path" "$branch_name"; then + exit 1 +fi + +wt_info "Created git worktree: $worktree_path" +wt_info "Checked out branch: $branch_name" + +if ! wt_copy_prd_to_worktree "$prd_path" "$worktree_path"; then + exit 1 +fi + +if ! wt_seed_from_allowlist "$worktree_path" "$force_seed"; then + exit 1 +fi + +if ! wt_run_pnpm_install "$worktree_path"; then + exit 1 +fi + +if ! wt_init_ralph_submodule "$worktree_path"; then + exit 1 +fi + +if [ "$no_open" -eq 1 ]; then + wt_info "Skipping VS Code open (--no-open)." + exit 0 +fi + +wt_open_vscode "$worktree_path" diff --git a/tools/worktrees/wt-prune.sh b/tools/worktrees/wt-prune.sh new file mode 100755 index 000000000..4e597c96d --- /dev/null +++ b/tools/worktrees/wt-prune.sh @@ -0,0 +1,327 @@ +#!/usr/bin/env bash +set -euo pipefail + +# prune-worktrees.sh +# +# Remove git worktrees that are "safe-to-remove": +# A) Clean (ignoring seeded files) AND no unique commits vs base (never started) +# B) Clean (ignoring seeded files) AND merged into base +# C) Clean (ignoring seeded files) AND fully pushed (no local commits ahead of upstream) +# +# Notes: +# - Handles submodules: refuses to prune if any submodule has changes/untracked. +# - Seeded files ignored in cleanliness check: +# - .env +# - tasks/prd*.md +# +# Usage: +# ./prune-worktrees.sh # dry-run (default) +# ./prune-worktrees.sh --apply # actually remove +# ./prune-worktrees.sh --apply --delete-branch +# ./prune-worktrees.sh --base origin/main +# +# Run from anywhere inside the repo. + +APPLY=0 +DELETE_BRANCH=0 +VERBOSE=0 +BASE_REF="develop" + +while [ $# -gt 0 ]; do + case "$1" in + --apply) APPLY=1; shift ;; + --dry-run) APPLY=0; shift ;; + --delete-branch) DELETE_BRANCH=1; shift ;; + --base) + [ $# -ge 2 ] || { echo "Missing value for --base" >&2; exit 2; } + BASE_REF="$2"; shift 2 ;; + -v|--verbose) VERBOSE=1; shift ;; + -h|--help) + sed -n '1,120p' "$0" + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + exit 2 + ;; + esac +done + +repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)" +if [ -z "$repo_root" ]; then + echo "Not inside a git repo." >&2 + exit 1 +fi + +log() { echo "$*" >&2; } +vlog() { if [ "$VERBOSE" -eq 1 ]; then echo "$*" >&2; fi } + +# Resolve base ref (default: origin/HEAD -> origin/main, fallback: origin/main, origin/master, main, master) +resolve_base_ref() { + if [ -n "$BASE_REF" ]; then + echo "$BASE_REF" + return 0 + fi + + local sym + sym="$(git symbolic-ref -q refs/remotes/origin/HEAD 2>/dev/null || true)" + if [ -n "$sym" ]; then + # refs/remotes/origin/main -> origin/main + echo "${sym#refs/remotes/}" + return 0 + fi + + for cand in origin/main origin/master main master; do + if git show-ref --verify --quiet "refs/remotes/$cand" || git show-ref --verify --quiet "refs/heads/$cand"; then + echo "$cand" + return 0 + fi + done + + echo "main" +} + +BASE="$(resolve_base_ref)" +log "Base ref: $BASE" +log "Mode: $([ "$APPLY" -eq 1 ] && echo APPLY || echo DRY-RUN)" +log "Ignore for cleanliness: .env, tasks/prd*.md" +log "Submodules: must be clean (no changes/untracked) to prune" + +# Pathspec excludes for status checks +EXCLUDES=( + ":(exclude).env" + ":(exclude)tasks/prd*.md" +) + +is_clean_ignoring_seed() { + local wt="$1" + + # Root repo clean? + local out + out="$(git -C "$wt" status --porcelain --untracked-files=all -- . "${EXCLUDES[@]}" 2>/dev/null || true)" + if [ -n "$out" ]; then + vlog "DIRTY (root) in $wt:" + vlog "$out" + return 1 + fi + + # Submodules: if any submodule has changes/untracked -> dirty. + # If submodules are not initialized, treat as clean (we're only pruning clutter). + if [ -f "$wt/.gitmodules" ] && git -C "$wt" submodule status --recursive >/dev/null 2>&1; then + # Check each initialized submodule + # (skip those with '-' status = not initialized) + local sm_paths + sm_paths="$(git -C "$wt" submodule status --recursive | awk '$1 ~ /^[+U]/ {print $2}')" + if [ -n "$sm_paths" ]; then + # '+' means submodule HEAD differs from index, 'U' conflicts -> dirty + vlog "DIRTY (submodule pointer) in $wt:" + vlog "$sm_paths" + return 1 + fi + + # Now check inside each initialized submodule working dir for changes/untracked + # (foreach only runs on initialized submodules) + local sm_dirty=0 + # shellcheck disable=SC2016 + git -C "$wt" submodule foreach --quiet --recursive ' + d="$(git status --porcelain --untracked-files=all 2>/dev/null || true)" + if [ -n "$d" ]; then + echo "DIRTY submodule: $name ($path)" + echo "$d" + exit 11 + fi + ' >/tmp/prune_wt_submodule_check.$$ 2>&1 || sm_dirty=$? + + if [ "$sm_dirty" -ne 0 ]; then + vlog "$(cat /tmp/prune_wt_submodule_check.$$ || true)" + rm -f /tmp/prune_wt_submodule_check.$$ + return 1 + fi + rm -f /tmp/prune_wt_submodule_check.$$ + fi + + return 0 +} + +is_merged_into_base() { + local wt="$1" + # HEAD is ancestor of BASE => merged + git -C "$wt" merge-base --is-ancestor HEAD "$BASE" >/dev/null 2>&1 +} + +ahead_counts() { + # prints: " " for A...B where "ahead" is commits in B not in A + local wt="$1" + local a="$2" + local b="$3" + git -C "$wt" rev-list --left-right --count "$a...$b" 2>/dev/null | awk '{print $1" "$2}' +} + +get_upstream() { + local wt="$1" + git -C "$wt" rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' 2>/dev/null || true +} + +# Parse worktrees via porcelain (robust) +# We skip: +# - main worktree (repo_root) +# - detached worktrees (no clear branch semantics) +declare -a PRUNE_PATHS=() +declare -a PRUNE_REASONS=() +declare -a PRUNE_BRANCHES=() + +current_path="" +current_branch_ref="" +current_detached=0 + +flush_entry() { + local p="$current_path" + local bref="$current_branch_ref" + local det="$current_detached" + + current_path="" + current_branch_ref="" + current_detached=0 + + [ -n "$p" ] || return 0 + + # Skip main/root worktree + if [ "$p" = "$repo_root" ]; then + vlog "Skip root worktree: $p" + return 0 + fi + + # Skip detached + if [ "$det" -eq 1 ]; then + vlog "Skip detached worktree: $p" + return 0 + fi + + # Need a local branch ref + if [[ "$bref" != refs/heads/* ]]; then + vlog "Skip non-local-branch worktree ($bref): $p" + return 0 + fi + + local branch="${bref#refs/heads/}" + + # Sanity: avoid pruning base branch worktree + if [ "$branch" = "${BASE#origin/}" ] || [ "$branch" = "$BASE" ]; then + vlog "Skip base branch worktree ($branch): $p" + return 0 + fi + + # Clean? + if ! is_clean_ignoring_seed "$p"; then + vlog "KEEP (dirty): $p ($branch)" + return 0 + fi + + # Rule B: merged into base + if is_merged_into_base "$p"; then + PRUNE_PATHS+=("$p") + PRUNE_BRANCHES+=("$branch") + PRUNE_REASONS+=("merged into $BASE + clean") + return 0 + fi + + # Rule C: fully pushed (no local commits ahead of upstream) + local up + up="$(get_upstream "$p")" + if [ -n "$up" ]; then + local counts behind_up ahead_up + counts="$(ahead_counts "$p" "$up" "HEAD")" + behind_up="$(awk '{print $1}' <<<"$counts")" + ahead_up="$(awk '{print $2}' <<<"$counts")" + vlog "Upstream for $branch: $up (behind=$behind_up ahead=$ahead_up)" + if [ "$ahead_up" -eq 0 ]; then + PRUNE_PATHS+=("$p") + PRUNE_BRANCHES+=("$branch") + PRUNE_REASONS+=("fully pushed to $up + clean") + return 0 + fi + fi + + # Rule A: never started (no unique commits vs base) + local counts2 behind_base ahead_base + counts2="$(ahead_counts "$p" "$BASE" "HEAD")" + behind_base="$(awk '{print $1}' <<<"$counts2")" + ahead_base="$(awk '{print $2}' <<<"$counts2")" + vlog "Vs base for $branch: behind=$behind_base ahead=$ahead_base" + if [ "$ahead_base" -eq 0 ]; then + PRUNE_PATHS+=("$p") + PRUNE_BRANCHES+=("$branch") + PRUNE_REASONS+=("no unique commits vs $BASE + clean") + return 0 + fi + + vlog "KEEP (has unmerged/unpushed commits): $p ($branch)" + return 0 +} + +while IFS= read -r line || [ -n "$line" ]; do + case "$line" in + worktree\ *) + flush_entry + current_path="${line#worktree }" + ;; + branch\ *) + current_branch_ref="${line#branch }" + ;; + detached) + current_detached=1 + ;; + "") + flush_entry + ;; + *) + : # ignore other fields + ;; + esac +done < <(git worktree list --porcelain) +flush_entry + +if [ "${#PRUNE_PATHS[@]}" -eq 0 ]; then + log "No safe-to-remove worktrees found." + exit 0 +fi + +log "" +log "Safe-to-remove worktrees:" +for i in "${!PRUNE_PATHS[@]}"; do + printf -- " - %s [%s] (%s)\n" "${PRUNE_PATHS[$i]}" "${PRUNE_BRANCHES[$i]}" "${PRUNE_REASONS[$i]}" >&2 +done + +if [ "$APPLY" -eq 0 ]; then + log "" + log "Dry-run only. Re-run with: --apply" + exit 0 +fi + +log "" +log "Removing worktrees..." +for i in "${!PRUNE_PATHS[@]}"; do + p="${PRUNE_PATHS[$i]}" + br="${PRUNE_BRANCHES[$i]}" + reason="${PRUNE_REASONS[$i]}" + + log "-> git worktree remove --force \"$p\" # $br ($reason)" + git worktree remove --force "$p" + + if [ "$DELETE_BRANCH" -eq 1 ]; then + # Only delete local branch if: + # - merged into base OR no unique commits vs base (never started) + if [[ "$reason" == merged\ into* ]] || [[ "$reason" == no\ unique\ commits* ]]; then + log " git branch -D \"$br\"" + git branch -D "$br" >/dev/null 2>&1 || true + else + log " (skip branch delete: $br is only 'pushed', not necessarily merged)" + fi + fi +done + +log "" +log "Pruning stale worktree metadata..." +git worktree prune + +log "Done." From 3765b478b4fe118aae3e2bdeee5a29da3e0b0768 Mon Sep 17 00:00:00 2001 From: marcuscastelo Date: Wed, 25 Mar 2026 12:55:08 -0300 Subject: [PATCH 10/10] feat(worktrees): prepend repository name to slug in worktree scripts --- tools/worktrees/wt-create.sh | 6 ++++++ tools/worktrees/wt-implement.sh | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/tools/worktrees/wt-create.sh b/tools/worktrees/wt-create.sh index 45e2c64c4..70064315d 100755 --- a/tools/worktrees/wt-create.sh +++ b/tools/worktrees/wt-create.sh @@ -187,6 +187,12 @@ if [ -z "$slug" ]; then fi fi +# Prepend repository name to slug (e.g., macroflows-issue10) +repo_name="$(basename "$repo_root")" +if [ -n "$repo_name" ] && [ "${slug#${repo_name}-}" = "$slug" ]; then + slug="${repo_name}-${slug}" +fi + worktree_path="$(wt_join_path "$wt_root" "$slug")" wt_info "wt-create scaffold initialized." diff --git a/tools/worktrees/wt-implement.sh b/tools/worktrees/wt-implement.sh index 4f538cb99..4f44b17c0 100755 --- a/tools/worktrees/wt-implement.sh +++ b/tools/worktrees/wt-implement.sh @@ -81,6 +81,13 @@ if ! slug="$(wt_resolve_slug "$prd_path" "$slug")"; then exit 1 fi +# Determine repo root and prepend repository name to slug (e.g., macroflows-feature) +repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)" +repo_name="$(basename "$repo_root")" +if [ -n "$repo_name" ] && [ "${slug#${repo_name}-}" = "$slug" ]; then + slug="${repo_name}-${slug}" +fi + if ! branch_name="$(wt_build_branch_name "$branch_prefix" "$slug")"; then exit 1 fi