feat: productivity suite — task loop, workflow, theming, docs & more (#197)
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m39s
CI / Build Linux (push) Has been cancelled
CI / Build Windows (cross-compile) (push) Has been cancelled
CI / Lint & Test (push) Has been cancelled

## 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:
2026-03-07 03:08:33 -08:00
committed by Naomi Carrigan
parent 1ae440659c
commit e6e9f7ae59
52 changed files with 8865 additions and 529 deletions
+253
View File
@@ -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();