mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 22:32:04 +00:00
Compare commits
2 Commits
v0.7.3
...
coderabbit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e79252be5c | ||
|
|
2bbc8113c0 |
@@ -30,6 +30,29 @@ interface CardActionsProps {
|
|||||||
onApprovePlan?: () => void;
|
onApprovePlan?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render contextual action buttons for a feature row based on the feature's status and whether it is the current automated task.
|
||||||
|
*
|
||||||
|
* Renders an appropriate set of buttons (Approve Plan, Logs, Force Stop, Verify, Resume, Complete, Edit, View Plan, Make, Refine, etc.) depending on
|
||||||
|
* feature properties (status, planSpec, skipTests, prUrl, id), the isCurrentAutoTask flag, and which callback props are provided.
|
||||||
|
*
|
||||||
|
* @param feature - The feature object whose status and metadata determine which actions are shown.
|
||||||
|
* @param isCurrentAutoTask - When true, renders actions relevant to the currently running automated task.
|
||||||
|
* @param hasContext - If true, indicates the feature has surrounding context (affects layout/availability in some states).
|
||||||
|
* @param shortcutKey - Optional keyboard shortcut label shown next to the Logs button when present.
|
||||||
|
* @param onEdit - Invoked when the Edit button is clicked.
|
||||||
|
* @param onViewOutput - Invoked when a Logs/View Output button is clicked.
|
||||||
|
* @param onVerify - Invoked when the Verify button (verification pathway) is clicked.
|
||||||
|
* @param onResume - Invoked when the Resume button is clicked.
|
||||||
|
* @param onForceStop - Invoked when the Force Stop button is clicked.
|
||||||
|
* @param onManualVerify - Invoked when a manual verification button is clicked.
|
||||||
|
* @param onFollowUp - Invoked when the Refine/Follow-up button is clicked.
|
||||||
|
* @param onImplement - Invoked when the Make/Implement button is clicked.
|
||||||
|
* @param onComplete - Invoked when the Complete button is clicked.
|
||||||
|
* @param onViewPlan - Invoked when the View Plan button is clicked.
|
||||||
|
* @param onApprovePlan - Invoked when the Approve Plan button is clicked.
|
||||||
|
* @returns The JSX element containing the action buttons for the feature row.
|
||||||
|
*/
|
||||||
export function CardActions({
|
export function CardActions({
|
||||||
feature,
|
feature,
|
||||||
isCurrentAutoTask,
|
isCurrentAutoTask,
|
||||||
@@ -109,73 +132,90 @@ export function CardActions({
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!isCurrentAutoTask && feature.status === 'in_progress' && (
|
{!isCurrentAutoTask &&
|
||||||
<>
|
(feature.status === 'in_progress' ||
|
||||||
{/* Approve Plan button - shows when plan is generated and waiting for approval */}
|
(typeof feature.status === 'string' && feature.status.startsWith('pipeline_'))) && (
|
||||||
{feature.planSpec?.status === 'generated' && onApprovePlan && (
|
<>
|
||||||
<Button
|
{/* Approve Plan button - shows when plan is generated and waiting for approval */}
|
||||||
variant="default"
|
{feature.planSpec?.status === 'generated' && onApprovePlan && (
|
||||||
size="sm"
|
<Button
|
||||||
className="flex-1 h-7 text-[11px] bg-purple-600 hover:bg-purple-700 text-white animate-pulse"
|
variant="default"
|
||||||
onClick={(e) => {
|
size="sm"
|
||||||
e.stopPropagation();
|
className="flex-1 h-7 text-[11px] bg-purple-600 hover:bg-purple-700 text-white animate-pulse"
|
||||||
onApprovePlan();
|
onClick={(e) => {
|
||||||
}}
|
e.stopPropagation();
|
||||||
onPointerDown={(e) => e.stopPropagation()}
|
onApprovePlan();
|
||||||
data-testid={`approve-plan-${feature.id}`}
|
}}
|
||||||
>
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
<FileText className="w-3 h-3 mr-1" />
|
data-testid={`approve-plan-${feature.id}`}
|
||||||
Approve Plan
|
>
|
||||||
</Button>
|
<FileText className="w-3 h-3 mr-1" />
|
||||||
)}
|
Approve Plan
|
||||||
{feature.skipTests && onManualVerify ? (
|
</Button>
|
||||||
<Button
|
)}
|
||||||
variant="default"
|
{feature.skipTests && onManualVerify ? (
|
||||||
size="sm"
|
<Button
|
||||||
className="flex-1 h-7 text-[11px]"
|
variant="default"
|
||||||
onClick={(e) => {
|
size="sm"
|
||||||
e.stopPropagation();
|
className="flex-1 h-7 text-[11px]"
|
||||||
onManualVerify();
|
onClick={(e) => {
|
||||||
}}
|
e.stopPropagation();
|
||||||
onPointerDown={(e) => e.stopPropagation()}
|
onManualVerify();
|
||||||
data-testid={`manual-verify-${feature.id}`}
|
}}
|
||||||
>
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
<CheckCircle2 className="w-3 h-3 mr-1" />
|
data-testid={`manual-verify-${feature.id}`}
|
||||||
Verify
|
>
|
||||||
</Button>
|
<CheckCircle2 className="w-3 h-3 mr-1" />
|
||||||
) : onResume ? (
|
Verify
|
||||||
<Button
|
</Button>
|
||||||
variant="default"
|
) : onResume ? (
|
||||||
size="sm"
|
<Button
|
||||||
className="flex-1 h-7 text-[11px] bg-[var(--status-success)] hover:bg-[var(--status-success)]/90"
|
variant="default"
|
||||||
onClick={(e) => {
|
size="sm"
|
||||||
e.stopPropagation();
|
className="flex-1 h-7 text-[11px] bg-[var(--status-success)] hover:bg-[var(--status-success)]/90"
|
||||||
onResume();
|
onClick={(e) => {
|
||||||
}}
|
e.stopPropagation();
|
||||||
onPointerDown={(e) => e.stopPropagation()}
|
onResume();
|
||||||
data-testid={`resume-feature-${feature.id}`}
|
}}
|
||||||
>
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
<RotateCcw className="w-3 h-3 mr-1" />
|
data-testid={`resume-feature-${feature.id}`}
|
||||||
Resume
|
>
|
||||||
</Button>
|
<RotateCcw className="w-3 h-3 mr-1" />
|
||||||
) : null}
|
Resume
|
||||||
{onViewOutput && !feature.skipTests && (
|
</Button>
|
||||||
<Button
|
) : onVerify ? (
|
||||||
variant="secondary"
|
<Button
|
||||||
size="sm"
|
variant="default"
|
||||||
className="h-7 text-[11px] px-2"
|
size="sm"
|
||||||
onClick={(e) => {
|
className="flex-1 h-7 text-[11px] bg-[var(--status-success)] hover:bg-[var(--status-success)]/90"
|
||||||
e.stopPropagation();
|
onClick={(e) => {
|
||||||
onViewOutput();
|
e.stopPropagation();
|
||||||
}}
|
onVerify();
|
||||||
onPointerDown={(e) => e.stopPropagation()}
|
}}
|
||||||
data-testid={`view-output-inprogress-${feature.id}`}
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
>
|
data-testid={`verify-feature-${feature.id}`}
|
||||||
<FileText className="w-3 h-3" />
|
>
|
||||||
</Button>
|
<CheckCircle2 className="w-3 h-3 mr-1" />
|
||||||
)}
|
Verify
|
||||||
</>
|
</Button>
|
||||||
)}
|
) : null}
|
||||||
|
{onViewOutput && !feature.skipTests && (
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
className="h-7 text-[11px] px-2"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onViewOutput();
|
||||||
|
}}
|
||||||
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
|
data-testid={`view-output-inprogress-${feature.id}`}
|
||||||
|
>
|
||||||
|
<FileText className="w-3 h-3" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{!isCurrentAutoTask && feature.status === 'verified' && (
|
{!isCurrentAutoTask && feature.status === 'verified' && (
|
||||||
<>
|
<>
|
||||||
{/* Logs button */}
|
{/* Logs button */}
|
||||||
@@ -319,4 +359,4 @@ export function CardActions({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,23 @@ interface UseBoardEffectsProps {
|
|||||||
setFeaturesWithContext: (set: Set<string>) => void;
|
setFeaturesWithContext: (set: Set<string>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers and manages side effects for the board view (IPC/event listeners, global exposure, and context checks).
|
||||||
|
*
|
||||||
|
* Sets up event subscriptions to suggestions, spec regeneration, and auto-mode events; exposes the current project globally for modals; syncs running tasks from the backend; and maintains the set of feature IDs that have associated context files.
|
||||||
|
*
|
||||||
|
* @param currentProject - The active project object or `null`. Exposed globally for modal use and used when syncing backend state.
|
||||||
|
* @param specCreatingForProject - Project path currently undergoing spec regeneration, or `null`.
|
||||||
|
* @param setSpecCreatingForProject - Setter to clear or set the spec-regenerating project path.
|
||||||
|
* @param setSuggestionsCount - Setter for the persisted number of suggestion items.
|
||||||
|
* @param setFeatureSuggestions - Setter for the latest suggestion payload.
|
||||||
|
* @param setIsGeneratingSuggestions - Setter to mark whether suggestions are being generated.
|
||||||
|
* @param checkContextExists - Async function that returns whether a given feature ID has context files.
|
||||||
|
* @param features - Array of feature records to evaluate for potential context files.
|
||||||
|
* @param isLoading - Flag indicating whether features are still loading; context checks run only when loading is complete.
|
||||||
|
* @param featuresWithContext - Set of feature IDs currently known to have context files.
|
||||||
|
* @param setFeaturesWithContext - Setter that replaces the set of feature IDs that have context files.
|
||||||
|
*/
|
||||||
export function useBoardEffects({
|
export function useBoardEffects({
|
||||||
currentProject,
|
currentProject,
|
||||||
specCreatingForProject,
|
specCreatingForProject,
|
||||||
@@ -130,7 +147,10 @@ export function useBoardEffects({
|
|||||||
const checkAllContexts = async () => {
|
const checkAllContexts = async () => {
|
||||||
const featuresWithPotentialContext = features.filter(
|
const featuresWithPotentialContext = features.filter(
|
||||||
(f) =>
|
(f) =>
|
||||||
f.status === 'in_progress' || f.status === 'waiting_approval' || f.status === 'verified'
|
f.status === 'in_progress' ||
|
||||||
|
f.status === 'waiting_approval' ||
|
||||||
|
f.status === 'verified' ||
|
||||||
|
(typeof f.status === 'string' && f.status.startsWith('pipeline_'))
|
||||||
);
|
);
|
||||||
const contextChecks = await Promise.all(
|
const contextChecks = await Promise.all(
|
||||||
featuresWithPotentialContext.map(async (f) => ({
|
featuresWithPotentialContext.map(async (f) => ({
|
||||||
@@ -179,4 +199,4 @@ export function useBoardEffects({
|
|||||||
unsubscribe();
|
unsubscribe();
|
||||||
};
|
};
|
||||||
}, [checkContextExists, setFeaturesWithContext]);
|
}, [checkContextExists, setFeaturesWithContext]);
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
"test:server:coverage": "npm run test:cov --workspace=apps/server",
|
"test:server:coverage": "npm run test:cov --workspace=apps/server",
|
||||||
"test:packages": "npm run test -w @automaker/types -w @automaker/utils -w @automaker/prompts -w @automaker/platform -w @automaker/model-resolver -w @automaker/dependency-resolver -w @automaker/git-utils --if-present",
|
"test:packages": "npm run test -w @automaker/types -w @automaker/utils -w @automaker/prompts -w @automaker/platform -w @automaker/model-resolver -w @automaker/dependency-resolver -w @automaker/git-utils --if-present",
|
||||||
"test:all": "npm run test:packages && npm run test:server",
|
"test:all": "npm run test:packages && npm run test:server",
|
||||||
"lint:lockfile": "! grep -q 'git+ssh://' package-lock.json || (echo 'Error: package-lock.json contains git+ssh:// URLs. Run: git config --global url.\"https://github.com/\".insteadOf \"git@github.com:\"' && exit 1)",
|
"lint:lockfile": "node scripts/lint-lockfile.mjs",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"format:check": "prettier --check .",
|
"format:check": "prettier --check .",
|
||||||
"prepare": "husky && npm run build:packages"
|
"prepare": "husky && npm run build:packages"
|
||||||
|
|||||||
33
scripts/lint-lockfile.mjs
Normal file
33
scripts/lint-lockfile.mjs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script to check for git+ssh:// URLs in package-lock.json
|
||||||
|
* This ensures compatibility with CI/CD environments that don't support SSH.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
const lockfilePath = join(process.cwd(), 'package-lock.json');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = readFileSync(lockfilePath, 'utf8');
|
||||||
|
|
||||||
|
// Check for git+ssh:// URLs
|
||||||
|
if (content.includes('git+ssh://')) {
|
||||||
|
console.error('Error: package-lock.json contains git+ssh:// URLs.');
|
||||||
|
console.error('Run: git config --global url."https://github.com/".insteadOf "git@github.com:"');
|
||||||
|
console.error('Or run: npm run fix:lockfile');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✓ No git+ssh:// URLs found in package-lock.json');
|
||||||
|
process.exit(0);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'ENOENT') {
|
||||||
|
console.error('Error: package-lock.json not found');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
console.error('Error checking package-lock.json:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user