merge: integrate v0.13.0rc with React Query refactor

Resolved conflict in use-project-settings-loader.ts:
- Keep React Query approach from upstream
- Add phaseModelOverrides loading for provider model persistence
- Update both currentProject and projects array to keep in sync
This commit is contained in:
Stefan de Vogelaere
2026-01-20 19:36:30 +01:00
133 changed files with 9283 additions and 3269 deletions

View File

@@ -7,6 +7,8 @@ export {
resolveDependencies,
areDependenciesSatisfied,
getBlockingDependencies,
createFeatureMap,
getBlockingDependenciesFromMap,
wouldCreateCircularDependency,
dependencyExists,
getAncestors,

View File

@@ -229,6 +229,49 @@ export function getBlockingDependencies(feature: Feature, allFeatures: Feature[]
});
}
/**
* Builds a lookup map for features by id.
*
* @param features - Features to index
* @returns Map keyed by feature id
*/
export function createFeatureMap(features: Feature[]): Map<string, Feature> {
const featureMap = new Map<string, Feature>();
for (const feature of features) {
if (feature?.id) {
featureMap.set(feature.id, feature);
}
}
return featureMap;
}
/**
* Gets the blocking dependencies using a precomputed feature map.
*
* @param feature - Feature to check
* @param featureMap - Map of all features by id
* @returns Array of feature IDs that are blocking this feature
*/
export function getBlockingDependenciesFromMap(
feature: Feature,
featureMap: Map<string, Feature>
): string[] {
const dependencies = feature.dependencies;
if (!dependencies || dependencies.length === 0) {
return [];
}
const blockingDependencies: string[] = [];
for (const depId of dependencies) {
const dep = featureMap.get(depId);
if (dep && dep.status !== 'completed' && dep.status !== 'verified') {
blockingDependencies.push(depId);
}
}
return blockingDependencies;
}
/**
* Checks if adding a dependency from sourceId to targetId would create a circular dependency.
* When we say "targetId depends on sourceId", we add sourceId to targetId.dependencies.

View File

@@ -3,6 +3,8 @@ import {
resolveDependencies,
areDependenciesSatisfied,
getBlockingDependencies,
createFeatureMap,
getBlockingDependenciesFromMap,
wouldCreateCircularDependency,
dependencyExists,
} from '../src/resolver';
@@ -351,6 +353,21 @@ describe('resolver.ts', () => {
});
});
describe('getBlockingDependenciesFromMap', () => {
it('should match getBlockingDependencies when using a feature map', () => {
const dep1 = createFeature('Dep1', { status: 'pending' });
const dep2 = createFeature('Dep2', { status: 'completed' });
const dep3 = createFeature('Dep3', { status: 'running' });
const feature = createFeature('A', { dependencies: ['Dep1', 'Dep2', 'Dep3'] });
const allFeatures = [dep1, dep2, dep3, feature];
const featureMap = createFeatureMap(allFeatures);
expect(getBlockingDependenciesFromMap(feature, featureMap)).toEqual(
getBlockingDependencies(feature, allFeatures)
);
});
});
describe('wouldCreateCircularDependency', () => {
it('should return false for features with no existing dependencies', () => {
const features = [createFeature('A'), createFeature('B')];

View File

@@ -339,7 +339,7 @@ IMPORTANT CONTEXT (automatically injected):
- When deleting a feature, identify which other features depend on it
Your task is to analyze the request and produce a structured JSON plan with:
1. Features to ADD (include title, description, category, and dependencies)
1. Features to ADD (include id, title, description, category, and dependencies)
2. Features to UPDATE (specify featureId and the updates)
3. Features to DELETE (specify featureId)
4. A summary of the changes
@@ -352,6 +352,7 @@ Respond with ONLY a JSON object in this exact format:
{
"type": "add",
"feature": {
"id": "descriptive-kebab-case-id",
"title": "Feature title",
"description": "Feature description",
"category": "feature" | "bug" | "enhancement" | "refactor",
@@ -386,6 +387,8 @@ Respond with ONLY a JSON object in this exact format:
\`\`\`
Important rules:
- CRITICAL: For new features, always include a descriptive "id" in kebab-case (e.g., "user-authentication", "design-system-foundation")
- Dependencies must reference these exact IDs - both for existing features and new features being added in the same plan
- Only include fields that need to change in updates
- Ensure dependency references are valid (don't reference deleted features)
- Provide clear, actionable descriptions

View File

@@ -1029,6 +1029,18 @@ export interface GlobalSettings {
* Each PhaseModelEntry can specify a providerId for provider-specific models.
*/
activeClaudeApiProfileId?: string | null;
/**
* Per-worktree auto mode settings
* Key: "${projectId}::${branchName ?? '__main__'}"
*/
autoModeByWorktree?: Record<
string,
{
maxConcurrency: number;
branchName: string | null;
}
>;
}
/**
@@ -1308,6 +1320,7 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = {
// Deprecated - kept for migration
claudeApiProfiles: [],
activeClaudeApiProfileId: null,
autoModeByWorktree: {},
};
/** Default credentials (empty strings - user must provide API keys) */