Files
hikari-desktop/src/lib/stores/projectContext.ts
T
hikari e6e9f7ae59
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
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>
2026-03-07 03:08:33 -08:00

218 lines
6.3 KiB
TypeScript

import { writable } from "svelte/store";
import { invoke } from "@tauri-apps/api/core";
export type ProjectFile = "PROJECT" | "REQUIREMENTS" | "ROADMAP" | "STATE" | "CODEBASE";
export const PROJECT_FILE_NAMES: Record<ProjectFile, string> = {
PROJECT: "PROJECT.md",
REQUIREMENTS: "REQUIREMENTS.md",
ROADMAP: "ROADMAP.md",
STATE: "STATE.md",
CODEBASE: "CODEBASE.md",
};
export const PROJECT_TEMPLATES: Record<ProjectFile, string> = {
PROJECT: `# Project Overview
## What is this project?
## Goals
## Tech Stack
## Architecture
`,
REQUIREMENTS: `# Requirements
## Functional Requirements
## Non-Functional Requirements
## Out of Scope
`,
ROADMAP: `# Roadmap
## Current Sprint
## Next Sprint
## Backlog
## Completed
`,
STATE: `# Current State
## Last Updated
## What's Working
## In Progress
## Known Issues
## Next Steps
`,
CODEBASE: "",
};
const PROJECT_FILES = Object.keys(PROJECT_FILE_NAMES) as ProjectFile[];
export interface ProjectScan {
working_dir: string;
file_tree: string;
detected_type: string;
key_files: string[];
}
function createProjectContextStore() {
const contents = writable<Record<ProjectFile, string | null>>({
PROJECT: null,
REQUIREMENTS: null,
ROADMAP: null,
STATE: null,
CODEBASE: null,
});
const isLoading = writable<Record<ProjectFile, boolean>>({
PROJECT: false,
REQUIREMENTS: false,
ROADMAP: false,
STATE: false,
CODEBASE: false,
});
const isSaving = writable<Record<ProjectFile, boolean>>({
PROJECT: false,
REQUIREMENTS: false,
ROADMAP: false,
STATE: false,
CODEBASE: false,
});
const activeFile = writable<ProjectFile>("PROJECT");
const isMappingCodebase = writable<boolean>(false);
async function loadFile(file: ProjectFile, workingDirectory: string): Promise<void> {
isLoading.update((state) => ({ ...state, [file]: true }));
try {
const path = `${workingDirectory}/${PROJECT_FILE_NAMES[file]}`;
const content = await invoke<string>("read_file_content", { path });
contents.update((state) => ({ ...state, [file]: content }));
} catch {
contents.update((state) => ({ ...state, [file]: null }));
} finally {
isLoading.update((state) => ({ ...state, [file]: false }));
}
}
async function saveFile(
file: ProjectFile,
content: string,
workingDirectory: string
): Promise<boolean> {
isSaving.update((state) => ({ ...state, [file]: true }));
try {
const path = `${workingDirectory}/${PROJECT_FILE_NAMES[file]}`;
await invoke("write_file_content", { path, content });
contents.update((state) => ({ ...state, [file]: content }));
return true;
} catch (error) {
console.error("Failed to save project context file:", error);
return false;
} finally {
isSaving.update((state) => ({ ...state, [file]: false }));
}
}
async function loadAll(workingDirectory: string): Promise<void> {
await Promise.all(PROJECT_FILES.map((file) => loadFile(file, workingDirectory)));
}
function setActiveFile(file: ProjectFile): void {
activeFile.set(file);
}
function getTemplate(file: ProjectFile): string {
return PROJECT_TEMPLATES[file];
}
async function mapCodebase(workingDirectory: string, conversationId: string): Promise<void> {
isMappingCodebase.set(true);
try {
const scan = await invoke<ProjectScan>("scan_project", {
workingDir: workingDirectory,
});
const prompt = buildCodebaseMapPrompt(scan);
await invoke("send_prompt", { conversationId, message: prompt });
} catch (error) {
console.error("Failed to map codebase:", error);
isMappingCodebase.set(false);
}
}
function finishMapping(): void {
isMappingCodebase.set(false);
}
return {
contents: { subscribe: contents.subscribe },
isLoading: { subscribe: isLoading.subscribe },
isSaving: { subscribe: isSaving.subscribe },
activeFile: { subscribe: activeFile.subscribe },
isMappingCodebase: { subscribe: isMappingCodebase.subscribe },
loadFile,
saveFile,
loadAll,
setActiveFile,
getTemplate,
mapCodebase,
finishMapping,
};
}
function buildCodebaseMapPrompt(scan: ProjectScan): string {
const keyFilesSection =
scan.key_files.length > 0
? `\n\nKey files detected:\n${scan.key_files.map((f) => `- ${f}`).join("\n")}`
: "";
return `Please analyse this codebase and generate a comprehensive \`CODEBASE.md\` file in the working directory (${scan.working_dir}).
Project type detected: **${scan.detected_type}**${keyFilesSection}
Directory structure:
\`\`\`
${scan.file_tree}
\`\`\`
The CODEBASE.md file should include:
1. **Overview** — what the project does and its purpose
2. **Architecture** — key directories, how the code is organised, and the overall structure
3. **Key Components** — the most important files and modules, what they do, and how they interact
4. **Data Flow** — how data moves through the system (if applicable)
5. **Dependencies** — notable external dependencies and why they are used
6. **Development Notes** — anything helpful for a developer new to the codebase
Write the file concisely but thoroughly. Focus on information that helps a developer understand the codebase quickly. Use the actual file structure above to inform your analysis — read the key files as needed before writing.`;
}
export const projectContextStore = createProjectContextStore();
// Signal store for injecting context into the active InputBar.
// StatusBar sets this; InputBar subscribes and applies it to inputValue directly,
// then resets it to null so the signal only fires once.
export const injectTextStore = writable<string | null>(null);
// Appended silently to custom_instructions at connection time (never saved to config).
// Mirrors how CLAUDE.md works natively — Claude checks the files itself if they exist.
export const PROJECT_CONTEXT_SYSTEM_ADDENDUM = `
---
The following project context files may exist in your working directory. If they exist, read and refer to them as needed:
- PROJECT.md — project overview, goals, and architecture
- REQUIREMENTS.md — functional and non-functional requirements
- ROADMAP.md — current sprint, backlog, and completed work
- STATE.md — current state, known issues, and next steps
- CODEBASE.md — auto-generated codebase map and architecture overview`;