feat(projects): add setup command suggestion#2513
Conversation
Greptile SummaryThis PR introduces a one-click setup-command suggestion toast for newly added projects. When a project is created, the app detects common tooling markers (lockfiles, manifests) in the repo root and offers the matching install command as a pre-configured
Confidence Score: 5/5Safe to merge — the feature is well-scoped, detection is purely read-only, the apply path has a server-side guard against overwriting existing setup scripts, and the token-based cancellation in the toast handles project-switch races correctly. The change adds a new, self-contained feature path with no modifications to existing behaviour. Detection errors are silently swallowed, apply conflicts are handled with a typed error variant, and the localStorage state machine correctly limits the toast to first-time project creation. The only gap is a test that does not fully exercise its claimed behaviour.
|
| Filename | Overview |
|---|---|
| apps/emdash-desktop/src/main/core/projects/setup-suggestion/detect-setup-suggestion.ts | Well-structured ordered rule table with error-safe markerExists wrapper; anyOf semantics are clear and the sequential short-circuit is correct. |
| apps/emdash-desktop/src/main/core/projects/setup-suggestion/detect-setup-suggestion.test.ts | Good overall coverage but the error-handling test does not exercise the anyOf fallthrough after a failing exists() call; the test passes trivially because nothing else matches in an empty file set. |
| apps/emdash-desktop/src/main/core/projects/settings/project-settings-service.ts | New setSetupScript method follows the existing read-then-write pattern and correctly guards against overwriting an existing setup script; TOCTOU window is consistent with other methods in this service. |
| apps/emdash-desktop/src/renderer/features/projects/components/setup-suggestion/setup-suggestion-toast.tsx | Token-based cancellation correctly prevents stale setPhase calls on project switches during the async apply flow; dismiss and apply paths are well guarded. |
| apps/emdash-desktop/src/renderer/features/projects/components/setup-suggestion/setup-suggestion-state.ts | Lightweight localStorage facade with try/catch on every operation; eligibility + dismiss key separation is clean and avoids showing the toast on app restart for already-dismissed projects. |
| apps/emdash-desktop/src/renderer/features/projects/stores/project-manager.ts | markSetupSuggestionEligible is correctly placed inside _setAndOpenProject, which is only called on project creation — so the suggestion only fires for new projects, not on every app restart. |
| apps/emdash-desktop/src/shared/core/projects/setup-suggestion.ts | Clean shared type definition for SetupScriptSuggestion; well-documented fields. |
| apps/emdash-desktop/src/shared/projects.ts | Adds setup-script-already-configured to the UpdateProjectSettingsError union; correctly handled in the toast component. |
| apps/emdash-desktop/src/main/core/projects/controller.ts | Exposes suggestSetupScript and applySetupScript via the RPC controller; applySetupScript is a thin inline wrapper that correctly delegates to setSetupScript. |
| apps/emdash-desktop/src/renderer/features/projects/components/main-panel/active-project.tsx | Minimal one-line addition mounting SetupSuggestionToast inside the active project view; no impact on existing layout logic. |
| apps/emdash-desktop/src/main/core/projects/setup-suggestion/suggest-setup-script.ts | Clean entry point: guards for missing project, pre-existing setup script, and detection errors are all handled gracefully with fallthrough logging. |
Sequence Diagram
sequenceDiagram
participant PM as ProjectManagerStore
participant LS as localStorage
participant Toast as SetupSuggestionToast
participant RPC as rpc.projects
participant Main as Main Process
PM->>LS: markSetupSuggestionEligible(projectId)
Note over PM,LS: only on project creation
Toast->>LS: shouldShowSetupSuggestion(projectId)
LS-->>Toast: "true (eligible && !dismissed)"
Toast->>RPC: suggestSetupScript(projectId)
RPC->>Main: suggestSetupScript(projectId)
Main->>Main: check scripts.setup configured?
Main->>Main: detectSetupSuggestion(project.fs)
Main-->>RPC: "SetupScriptSuggestion | null"
RPC-->>Toast: suggestion
alt suggestion found
Toast->>Toast: setPhase('visible')
Note over Toast: user sees dismissible toast
alt user clicks Add setup script
Toast->>RPC: applySetupScript(projectId, command)
RPC->>Main: setSetupScript(projectId, command)
Main->>Main: guard: setup already set?
Main->>Main: "settings.update({...current, scripts.setup})"
Main->>Main: emitSettingsChanged
Main-->>RPC: ok(updatedSettings)
RPC-->>Toast: result
Toast->>LS: dismissSetupSuggestion(projectId)
Toast->>Toast: setPhase('applied') then 'hidden'
else user dismisses
Toast->>LS: dismissSetupSuggestion(projectId)
Toast->>Toast: setPhase('hidden')
end
else no suggestion
Toast->>LS: clearSetupSuggestionEligibility(projectId)
end
Reviews (2): Last reviewed commit: "fix(projects): harden setup suggestion a..." | Re-trigger Greptile
Description
Screenshot/Recording (if applicable)
https://streamable.com/p9tsc1
Checklist
messages and, when possible, the PR title