diff --git a/src/services/git/aiCommitMessage.ts b/src/services/git/aiCommitMessage.ts index dbeccb36..27645fab 100644 --- a/src/services/git/aiCommitMessage.ts +++ b/src/services/git/aiCommitMessage.ts @@ -8,6 +8,14 @@ import { exec as gitExec } from 'dugite'; import * as fs from 'node:fs/promises'; import * as path from 'node:path'; +// Limits for how much git content we send to the AI service. +// These values are chosen to balance useful context vs. token / payload size: +// - MAX_UNTRACKED_FILES: cap the number of untracked files included so a large +// number of new files does not overwhelm the prompt or increase latency. +// - MAX_FILE_CONTENT_LENGTH: truncate individual file contents to keep the +// prompt small while still giving the model enough context about changes. +// - MAX_DIFF_LENGTH: hard cap on total diff length to stay within typical +// model/token limits and avoid request failures due to oversized payloads. const MAX_UNTRACKED_FILES = 5; const MAX_FILE_CONTENT_LENGTH = 500; const MAX_DIFF_LENGTH = 3000; diff --git a/src/services/git/gitOperations.ts b/src/services/git/gitOperations.ts index 8911ed63..f862a006 100644 --- a/src/services/git/gitOperations.ts +++ b/src/services/git/gitOperations.ts @@ -10,6 +10,9 @@ import * as path from 'node:path'; import { defaultGitInfo } from './defaultGitInfo'; import type { GitFileStatus, IFileDiffResult, IGitLogOptions, IGitLogResult } from './interface'; +/** Prefix for temporary Git index directories used during amend/undo operations */ +const TEMP_GIT_INDEX_PREFIX = 'tidgi-git-index-'; + /** * Helper to create git environment variables for commit operations * This ensures commits work in environments without git config (like CI) @@ -181,7 +184,8 @@ function parseGitStatusCode(statusCode: string): GitFileStatus { export async function getCommitFiles(repoPath: string, commitHash: string): Promise> { // Handle uncommitted changes if (!commitHash || commitHash === '') { - // Use -uall to show all untracked files, not just directories + // Use -uall to show all untracked files, not just directories. + // This is important for AI commit message generation to see the full context. const result = await gitExec(['-c', 'core.quotePath=false', 'status', '--porcelain', '-uall'], repoPath); if (result.exitCode !== 0) { @@ -710,7 +714,7 @@ export async function addToGitignore(repoPath: string, pattern: string): Promise export async function amendCommitMessage(repoPath: string, newMessage: string): Promise { // Use a temporary index so we amend only the commit message and do not // accidentally include user's staged changes or alter their index state. - const temporaryDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'tidgi-git-index-')); + const temporaryDirectory = await fs.mkdtemp(path.join(os.tmpdir(), TEMP_GIT_INDEX_PREFIX)); const temporaryIndex = path.join(temporaryDirectory, 'index'); try { const baseEnvironment = { ...process.env, GIT_INDEX_FILE: temporaryIndex } as NodeJS.ProcessEnv; diff --git a/src/services/wiki/plugin/watchFileSystemAdaptor/FileSystemWatcher.ts b/src/services/wiki/plugin/watchFileSystemAdaptor/FileSystemWatcher.ts index 9b5ad6be..1b302cfb 100644 --- a/src/services/wiki/plugin/watchFileSystemAdaptor/FileSystemWatcher.ts +++ b/src/services/wiki/plugin/watchFileSystemAdaptor/FileSystemWatcher.ts @@ -152,11 +152,13 @@ export class FileSystemWatcher { */ async initialize(): Promise { if (!this.watchPathBase) { + this.logger.log('[test-id-WATCH_FS_STABILIZED] Watcher has stabilized (no watch path)'); return; } // Check if file system watch is enabled for this workspace if (this.workspaceConfig && !this.workspaceConfig.enableFileSystemWatch) { + this.logger.log('[test-id-WATCH_FS_STABILIZED] Watcher has stabilized (disabled by config)'); this.logger.log('FileSystemWatcher File system watching is disabled for this workspace'); return; } @@ -412,6 +414,9 @@ export class FileSystemWatcher { this.logger.log('[test-id-WATCH_FS_STABILIZED] Watcher has stabilized'); } catch (error) { this.logger.alert('FileSystemWatcher Failed to initialize file watching:', error); + // Still log stabilized marker even if initialization failed + // This prevents tests from hanging waiting for the marker + this.logger.log('[test-id-WATCH_FS_STABILIZED] Watcher has stabilized (with errors)'); } } diff --git a/src/services/wiki/plugin/watchFileSystemAdaptor/WatchFileSystemAdaptor.ts b/src/services/wiki/plugin/watchFileSystemAdaptor/WatchFileSystemAdaptor.ts index 9a1abb35..6cc7c8ae 100644 --- a/src/services/wiki/plugin/watchFileSystemAdaptor/WatchFileSystemAdaptor.ts +++ b/src/services/wiki/plugin/watchFileSystemAdaptor/WatchFileSystemAdaptor.ts @@ -38,20 +38,24 @@ export class WatchFileSystemAdaptor extends FileSystemAdaptor { } private async initializeAsync(): Promise { + this.logger.log('WatchFileSystemAdaptor initializeAsync starting'); try { const workspaceId = this.workspaceID; + this.logger.log(`WatchFileSystemAdaptor loading workspace config for ${workspaceId}`); if (workspaceId) { const loadedWorkspaceData = await workspace.get(workspaceId); if (!loadedWorkspaceData || typeof loadedWorkspaceData !== 'object' || !isWikiWorkspace(loadedWorkspaceData)) { throw new Error('Invalid workspace data'); } this.workspace = loadedWorkspaceData; + this.logger.log(`WatchFileSystemAdaptor workspace config loaded, enableFileSystemWatch=${this.workspace.enableFileSystemWatch}`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logger.log(`Failed to load workspace data: ${errorMessage}`); } + this.logger.log('WatchFileSystemAdaptor creating FileSystemWatcher'); // Create and initialize the file watcher this.watcher = new FileSystemWatcher({ wiki: this.wiki, @@ -61,7 +65,9 @@ export class WatchFileSystemAdaptor extends FileSystemAdaptor { workspaceConfig: this.workspace, }); + this.logger.log('WatchFileSystemAdaptor calling watcher.initialize()'); await this.watcher.initialize(); + this.logger.log('WatchFileSystemAdaptor initialization complete'); } /** diff --git a/src/services/wiki/plugin/watchFileSystemAdaptor/comparison.ts b/src/services/wiki/plugin/watchFileSystemAdaptor/comparison.ts deleted file mode 100644 index 024af718..00000000 --- a/src/services/wiki/plugin/watchFileSystemAdaptor/comparison.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * High-performance tiddler comparison utilities - * Used to detect if a tiddler has actually changed to prevent unnecessary saves - */ - -/** - * Fields that change automatically during save and should be excluded from comparison - */ -const EXCLUDED_FIELDS = new Set([ - 'modified', // Always updated on save - 'revision', // Internal TiddlyWiki revision counter - 'bag', // TiddlyWeb specific - 'created', // May be auto-generated -]); - -/** - * Fast comparison of two tiddlers to detect real content changes - * Strategy: - * 1. Quick length check on text field (most common change) - * 2. Compare field counts - * 3. Deep compare only if needed - * - * @param oldTiddler - Existing tiddler in wiki - * @param newTiddler - New tiddler from file - * @returns true if tiddlers are meaningfully different - */ -export function hasMeaningfulChanges( - oldTiddler: Record, - newTiddler: Record, -): boolean { - // Fast path: Compare text field length first (most common change) - const oldText = oldTiddler.text as string | undefined; - const newText = newTiddler.text as string | undefined; - - if ((oldText?.length ?? 0) !== (newText?.length ?? 0)) { - return true; // Text length changed - definitely different - } - - // Get field keys excluding auto-generated ones - const oldKeys = new Set(Object.keys(oldTiddler)).difference(EXCLUDED_FIELDS); - const newKeys = new Set(Object.keys(newTiddler)).difference(EXCLUDED_FIELDS); - - // Quick check: Different number of fields - if (oldKeys.size !== newKeys.size) { - return true; - } - - // Check if there are any different keys (fields added or removed) - if (oldKeys.difference(newKeys).size > 0) { - return true; - } - - // Deep comparison: Check each field value (keys are the same at this point) - for (const key of oldKeys) { - if (oldTiddler[key] !== newTiddler[key]) { - return true; // Field value changed - } - } - - return false; // No meaningful changes -}