generated from nhcarrigan/template
feat: productivity suite — task loop, workflow, theming, docs & more (#197)
## Summary A large productivity-focused feature branch delivering a suite of improvements across automation, project management, theming, performance, and documentation. ### Features - **Guided Project Workflow** (#189) — Four-phase workflow panel (Discuss → Plan → Execute → Verify) to keep projects structured from idea to completion - **Automated Task Loop** (#179) — Per-task conversation orchestration with wave-based parallel execution, blocked-task detection, and concurrency control - **Wave-Based Parallel Execution** (#191) — Tasks run in dependency-aware waves with configurable concurrency; independent tasks execute in parallel - **Auto-Commit After Task Completion** (#192) — Task Loop optionally commits after each completed task so progress is never lost - **PRD Creator** (#180) — AI-assisted PRD and task list panel that outputs `hikari-tasks.json` for the Task Loop to consume - **Project Context Panel** (#188) — Persistent `PROJECT.md`, `REQUIREMENTS.md`, `ROADMAP.md`, and `STATE.md` files injected into Claude's context automatically - **Codebase Mapper** (#190) — Generates a `CODEBASE.md` architectural summary so Claude always understands the project structure - **Community Preset Themes** (#181) — Six built-in community themes: Dracula, Catppuccin Mocha, Nord, Solarized Dark, Gruvbox Dark, and Rosé Pine - **In-App Changelog Panel** (#193) — Fetches release notes from GitHub at runtime and displays them inside the app - **Full Embedded Documentation** (#196) — Replaced the single-page help modal with a 12-page paginated docs browser featuring a sidebar TOC, prev/next navigation, keyboard navigation (arrow keys, `?` shortcut), and comprehensive coverage of every feature ### Performance & Fixes - **Lazy Loading & Virtualisation** (#194) — Virtual windowing for conversation history, markdown memoisation, and debounced search for smooth rendering of large sessions - **Ctrl+C Copy Fix** (#195) — `Ctrl+C` now copies selected text as expected; interrupt-Claude behaviour only fires when no text is selected ### UX - Back-to-workflow button in PRD Creator and Task Loop panels for easy navigation - Navigation icon cluster replaced with a single clean dropdown menu ## Closes Closes #179 Closes #180 Closes #181 Closes #188 Closes #189 Closes #190 Closes #191 Closes #192 Closes #193 Closes #194 Closes #195 Closes #196 --- ✨ This PR was created with help from Hikari~ 🌸 Reviewed-on: #197 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #197.
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
import { writable, get } from "svelte/store";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
export type WorkflowPhase = 1 | 2 | 3 | 4;
|
||||
export type CriterionStatus = "pending" | "pass" | "fail" | "partial";
|
||||
|
||||
export interface VerifyCriterion {
|
||||
id: string;
|
||||
text: string;
|
||||
status: CriterionStatus;
|
||||
}
|
||||
|
||||
export interface WorkflowState {
|
||||
version: 1;
|
||||
currentPhase: WorkflowPhase;
|
||||
quickMode: boolean;
|
||||
discuss: {
|
||||
description: string;
|
||||
contextCaptured: boolean;
|
||||
};
|
||||
plan: {
|
||||
tasksApproved: boolean;
|
||||
};
|
||||
execute: {
|
||||
completed: boolean;
|
||||
};
|
||||
verify: {
|
||||
criteria: VerifyCriterion[];
|
||||
verificationComplete: boolean;
|
||||
report: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const WORKFLOW_STATE_FILENAME = "workflow-state.json";
|
||||
|
||||
const DEFAULT_STATE: WorkflowState = {
|
||||
version: 1,
|
||||
currentPhase: 1,
|
||||
quickMode: false,
|
||||
discuss: { description: "", contextCaptured: false },
|
||||
plan: { tasksApproved: false },
|
||||
execute: { completed: false },
|
||||
verify: { criteria: [], verificationComplete: false, report: "" },
|
||||
};
|
||||
|
||||
// ─── Pure functions (exported for testing) ───────────────────────────────────
|
||||
|
||||
export function buildDiscussPrompt(description: string): string {
|
||||
return `Please help me clarify and document the following project goal, then write a \`CONTEXT.md\` file in the working directory.
|
||||
|
||||
Project description:
|
||||
${description}
|
||||
|
||||
The \`CONTEXT.md\` file should include:
|
||||
|
||||
## Goal
|
||||
A clear, one-paragraph statement of what we are building and why.
|
||||
|
||||
## Scope
|
||||
What is in scope and what is explicitly out of scope.
|
||||
|
||||
## Acceptance Criteria
|
||||
A numbered list of concrete, verifiable criteria that must be met for this project to be considered complete. Each criterion should be specific and testable.
|
||||
|
||||
## Key Assumptions
|
||||
Any assumptions being made about the implementation, environment, or user needs.
|
||||
|
||||
## Open Questions
|
||||
Any questions that need to be resolved before or during development.
|
||||
|
||||
Write the file concisely but thoroughly. Focus on information that guides implementation and defines success.`;
|
||||
}
|
||||
|
||||
export function buildVerifyPrompt(criteria: VerifyCriterion[]): string {
|
||||
if (criteria.length === 0) {
|
||||
return `Please review the project implementation and write a \`VERIFY.md\` file in the working directory with your overall assessment.
|
||||
|
||||
Include:
|
||||
## Summary
|
||||
Overall pass/fail assessment.
|
||||
|
||||
## Findings
|
||||
What you found when reviewing the implementation.
|
||||
|
||||
## Recommendation
|
||||
Whether the project is ready to ship or what remains to be done.`;
|
||||
}
|
||||
|
||||
const criteriaList = criteria.map((c, i) => `${i + 1}. ${c.text}`).join("\n");
|
||||
|
||||
return `Please verify the project implementation against the following acceptance criteria and write a \`VERIFY.md\` file in the working directory.
|
||||
|
||||
Acceptance criteria:
|
||||
${criteriaList}
|
||||
|
||||
For each criterion, check whether it is met by examining the codebase and any relevant files. Then write \`VERIFY.md\` with:
|
||||
|
||||
## Summary
|
||||
Overall PASSED / FAILED / PARTIAL status.
|
||||
|
||||
## Criterion Results
|
||||
For each criterion: state whether it PASSES, FAILS, or is PARTIAL, with a brief explanation.
|
||||
|
||||
## Findings
|
||||
Any notable issues, edge cases, or improvements spotted during review.
|
||||
|
||||
## Recommendation
|
||||
Whether the project is ready to ship or what remains to be done.`;
|
||||
}
|
||||
|
||||
export function canAdvancePhase(state: WorkflowState): boolean {
|
||||
switch (state.currentPhase) {
|
||||
case 1:
|
||||
return state.quickMode || state.discuss.contextCaptured;
|
||||
case 2:
|
||||
return state.plan.tasksApproved;
|
||||
case 3:
|
||||
return state.execute.completed;
|
||||
case 4:
|
||||
return state.verify.verificationComplete;
|
||||
}
|
||||
}
|
||||
|
||||
export function canGoBack(phase: WorkflowPhase): boolean {
|
||||
return phase > 1;
|
||||
}
|
||||
|
||||
export function getPhaseLabel(phase: WorkflowPhase): string {
|
||||
switch (phase) {
|
||||
case 1:
|
||||
return "Discuss";
|
||||
case 2:
|
||||
return "Plan";
|
||||
case 3:
|
||||
return "Execute";
|
||||
case 4:
|
||||
return "Verify";
|
||||
}
|
||||
}
|
||||
|
||||
export function generateCriterionId(): string {
|
||||
return `criterion-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
||||
}
|
||||
|
||||
// ─── Store ────────────────────────────────────────────────────────────────────
|
||||
|
||||
function createWorkflowStore() {
|
||||
const state = writable<WorkflowState>({ ...DEFAULT_STATE });
|
||||
|
||||
async function loadState(workingDirectory: string): Promise<void> {
|
||||
try {
|
||||
const path = `${workingDirectory}/${WORKFLOW_STATE_FILENAME}`;
|
||||
const content = await invoke<string>("read_file_content", { path });
|
||||
const parsed = JSON.parse(content) as WorkflowState;
|
||||
state.set(parsed);
|
||||
} catch {
|
||||
state.set({ ...DEFAULT_STATE });
|
||||
}
|
||||
}
|
||||
|
||||
async function saveState(workingDirectory: string): Promise<void> {
|
||||
try {
|
||||
const path = `${workingDirectory}/${WORKFLOW_STATE_FILENAME}`;
|
||||
const current = get(state);
|
||||
await invoke("write_file_content", { path, content: JSON.stringify(current, null, 2) });
|
||||
} catch (error) {
|
||||
console.error("Failed to save workflow state:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function setPhase(phase: WorkflowPhase): void {
|
||||
state.update((s) => ({ ...s, currentPhase: phase }));
|
||||
}
|
||||
|
||||
function setQuickMode(value: boolean): void {
|
||||
state.update((s) => ({ ...s, quickMode: value }));
|
||||
}
|
||||
|
||||
function reset(): void {
|
||||
state.set({ ...DEFAULT_STATE });
|
||||
}
|
||||
|
||||
function setDiscussDescription(text: string): void {
|
||||
state.update((s) => ({ ...s, discuss: { ...s.discuss, description: text } }));
|
||||
}
|
||||
|
||||
function markContextCaptured(): void {
|
||||
state.update((s) => ({ ...s, discuss: { ...s.discuss, contextCaptured: true } }));
|
||||
}
|
||||
|
||||
function approvePlan(): void {
|
||||
state.update((s) => ({ ...s, plan: { tasksApproved: true } }));
|
||||
}
|
||||
|
||||
function completeExecution(): void {
|
||||
state.update((s) => ({ ...s, execute: { completed: true } }));
|
||||
}
|
||||
|
||||
function addCriterion(text: string): void {
|
||||
const criterion: VerifyCriterion = {
|
||||
id: generateCriterionId(),
|
||||
text,
|
||||
status: "pending",
|
||||
};
|
||||
state.update((s) => ({
|
||||
...s,
|
||||
verify: { ...s.verify, criteria: [...s.verify.criteria, criterion] },
|
||||
}));
|
||||
}
|
||||
|
||||
function removeCriterion(id: string): void {
|
||||
state.update((s) => ({
|
||||
...s,
|
||||
verify: { ...s.verify, criteria: s.verify.criteria.filter((c) => c.id !== id) },
|
||||
}));
|
||||
}
|
||||
|
||||
function updateCriterionStatus(id: string, status: CriterionStatus): void {
|
||||
state.update((s) => ({
|
||||
...s,
|
||||
verify: {
|
||||
...s.verify,
|
||||
criteria: s.verify.criteria.map((c) => (c.id === id ? { ...c, status } : c)),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
function completeVerification(report: string): void {
|
||||
state.update((s) => ({
|
||||
...s,
|
||||
verify: { ...s.verify, verificationComplete: true, report },
|
||||
}));
|
||||
}
|
||||
|
||||
return {
|
||||
state: { subscribe: state.subscribe },
|
||||
loadState,
|
||||
saveState,
|
||||
setPhase,
|
||||
setQuickMode,
|
||||
reset,
|
||||
setDiscussDescription,
|
||||
markContextCaptured,
|
||||
approvePlan,
|
||||
completeExecution,
|
||||
addCriterion,
|
||||
removeCriterion,
|
||||
updateCriterionStatus,
|
||||
completeVerification,
|
||||
};
|
||||
}
|
||||
|
||||
export const workflowStore = createWorkflowStore();
|
||||
Reference in New Issue
Block a user