@kolisachint/hoocode-agent 0.4.33 → 0.4.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/core/subagent-pool.d.ts +2 -0
- package/dist/core/subagent-pool.d.ts.map +1 -1
- package/dist/core/subagent-pool.js +2 -0
- package/dist/core/subagent-pool.js.map +1 -1
- package/dist/core/task-store.d.ts +7 -1
- package/dist/core/task-store.d.ts.map +1 -1
- package/dist/core/task-store.js +2 -0
- package/dist/core/task-store.js.map +1 -1
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +7 -4
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/extensions/core/hoo-core.d.ts.map +1 -1
- package/dist/extensions/core/hoo-core.js +41 -1
- package/dist/extensions/core/hoo-core.js.map +1 -1
- package/dist/init-templates.generated.d.ts.map +1 -1
- package/dist/init-templates.generated.js +4 -4
- package/dist/init-templates.generated.js.map +1 -1
- package/dist/modes/interactive/components/task-panel.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-panel.js +10 -3
- package/dist/modes/interactive/components/task-panel.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
- package/templates/agents/doc.md +1 -0
- package/templates/agents/explore.md +1 -0
- package/templates/agents/review.md +1 -0
- package/templates/agents/test.md +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init-templates.generated.js","sourceRoot":"","sources":["../src/init-templates.generated.ts"],"names":[],"mappings":"AAAA,iEAA+D;AAC/D,sDAAsD;AACtD,wCAAwC;AAExC,MAAM,CAAC,MAAM,uBAAuB,GACnC,ocAAoc,CAAC;AAEtc,MAAM,CAAC,MAAM,cAAc,GAA2B;IACrD,GAAG,EAAE,ogBAAkgB;IACvgB,KAAK,EAAE,unBAAmnB;IAC1nB,KAAK,EAAE,6pBAAipB;IACxpB,IAAI,EAAE,isBAAqrB;CAC3rB,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAA2B,EAAE,CAAC;AAE5D,MAAM,CAAC,MAAM,sBAAsB,GAA2B;IAC7D,GAAG,EAAE,g8CAAg8C;IACr8C,IAAI,EAAE,wmDAAsmD;IAC5mD,OAAO,EACN,u9CAAu9C;IACx9C,iBAAiB,EAChB,4yDAA4yD;IAC7yD,MAAM,EACL,i8CAAi8C;IACl8C,IAAI,EAAE,4hDAA4hD;CACliD,CAAC","sourcesContent":["// AUTO-GENERATED by scripts/embed-templates.mjs — do not edit.\n// Source of truth: packages/coding-agent/templates/**\n// Regenerated on every `npm run build`.\n\nexport const EMBEDDED_DEFAULT_CONFIG: string =\n\t'{\\n \"version\": \"1.0\",\\n \"active_mode\": \"build\",\\n \"llm\": {\\n \"default_provider\": \"anthropic\",\\n \"providers\": {\\n \"anthropic\": { \"api_key_env\": \"ANTHROPIC_API_KEY\" },\\n \"openai\": { \"api_key_env\": \"OPENAI_API_KEY\" }\\n }\\n },\\n \"modes\": {\\n \"ask\": { \"auto_allow\": [\"read\"] },\\n \"plan\": { \"auto_allow\": [\"read\", \"write\"] },\\n \"build\": { \"auto_allow\": [\"read\"] },\\n \"debug\": { \"auto_allow\": [\"read\", \"bash\"] }\\n }\\n}\\n';\n\nexport const EMBEDDED_MODES: Record<string, string> = {\n\task: \"You are in **ask mode** — read-only Q&A.\\n\\nPermitted: read files, run grep/find, explain code, trace logic, compare approaches, debug conceptually.\\nForbidden: edit files, write files, run commands that modify state.\\n\\nWhen answering:\\n- Cite file paths and line numbers.\\n- Prefer precise over verbose.\\n- If a question requires a code change to answer properly, describe the change; do not apply it.\\n- If the user asks you to edit something, decline and suggest switching to build mode with `/mode build`.\\n\",\n\tbuild: \"You are in **build mode** — implement carefully, one step at a time.\\n\\nRules:\\n- **One tool per turn.** Plan the action, call the tool, wait for the result before proceeding.\\n- **Read before editing.** Never write to a file you have not read in this session.\\n- **Show diffs** before applying non-trivial edits; wait for implicit acceptance.\\n- **Dangerous ops** (delete, force-push, drop table, rm -rf): state what you are about to do and wait for explicit confirmation.\\n- **Match existing style** — indentation, naming, import order.\\n- **Run tests** after every logical unit of change. Fix failures before continuing.\\n\",\n\tdebug: \"You are in **debug mode** — root-cause analysis only, no file modifications.\\n\\nProcess:\\n1. **Gather evidence** — read logs, error traces, and relevant source. Run safe diagnostic commands (grep, find, read, non-mutating shell commands).\\n2. **Reproduce** — identify the minimal condition that triggers the bug.\\n3. **Trace** — follow the full call path from entry point to failure site, citing file and line at each step.\\n4. **State the root cause** in one clear sentence.\\n5. **Describe the fix** — files, lines, and what to change — but do not apply it.\\n\\nForbidden: edit or write any file. To apply a fix, switch to build mode with `/mode build`.\\n\",\n\tplan: 'You are in **plan mode** — explore and design, no source edits.\\n\\nYour job: produce a complete, actionable implementation plan.\\n\\nSteps:\\n1. Read relevant files and ask clarifying questions before drafting.\\n2. Write the finished plan to `{{PLAN_PATH}}` with these sections:\\n - **Goal** — one sentence.\\n - **Files to modify** — path, line range, what changes.\\n - **New files** — path, purpose.\\n - **Tests** — what to add or update.\\n - **Verification** — commands to confirm correctness.\\n3. After writing the plan, tell the user: \"Plan written to `{{PLAN_PATH}}`. Run `/approve` to begin execution.\"\\n\\nForbidden: edit any source file. Only `{{PLAN_PATH}}` may be written.\\n',\n};\n\nexport const EMBEDDED_PROFILES: Record<string, string> = {};\n\nexport const EMBEDDED_AGENT_PROMPTS: Record<string, string> = {\n\tdoc: \"---\\nname: doc\\ndescription: |\\n Use this subagent ONLY when:\\n - Writing or updating README files\\n - Adding inline code comments or API documentation\\n - Creating user guides, tutorials, or changelogs\\n - Explaining architecture or design decisions\\n\\n DO NOT use for:\\n - Modifying source logic (use edit agent)\\n - Read-only exploration (use explore agent)\\n - Code review (use review agent)\\n\\n Output: Written documentation. Concise, accurate, and well-structured.\\n Cost: Low (read + write docs only)\\n Isolation: Can run in parallel with explore and review tasks\\ntools: read, write, edit, grep, find, ls\\nmodel: sonnet\\n---\\nYou are a documentation subagent running inside hoocode. You write and update documentation. You run in an isolated context and cannot see the parent conversation.\\n\\nScope:\\n- You may read files and write documentation files (README, comments, guides). Do not modify source logic.\\n- Produce concise, accurate, and well-structured documentation.\\n\\nMethod:\\n1. Read the relevant source files to understand what needs documenting.\\n2. Write or update the requested documentation.\\n3. Verify that the documentation is consistent with the code.\\n\\nGuidance:\\n- Focus on clarity and accuracy. Avoid unnecessary verbosity.\\n- Match the project's existing documentation style.\\n- Your final message must contain ONLY the documentation or a summary of what was written.\\n- Do not narrate intermediate steps or include tool logs.\\n\",\n\tedit: \"---\\nname: edit\\ndescription: |\\n Use this subagent ONLY when:\\n - Writing new code or creating new files\\n - Refactoring existing code across one or more files\\n - Fixing bugs or applying targeted corrections\\n - Migrating patterns or renaming symbols\\n\\n DO NOT use for:\\n - Read-only exploration\\n - Running tests (use test agent)\\n - Code review (use review agent)\\n\\n Output: Changed files with path:line descriptions. No narration.\\n Cost: Medium (read + write)\\n Isolation: Should not run concurrently with other edit tasks on the same files\\ntools: read, edit, write, grep, find, ls, bash\\nmodel: sonnet\\nmaxTurns: 25\\n---\\nYou are an edit subagent running inside hoocode. You implement one focused code change. You run in an isolated context and cannot see the parent conversation.\\n\\nScope:\\n- You may read, edit, and write files, and run commands needed to make the change.\\n- Stay strictly within the requested task. Do not refactor unrelated code.\\n\\nMethod:\\n1. Read the relevant files before changing them.\\n2. Match the existing style: indentation, naming, import order.\\n3. Make the smallest change that fully satisfies the task.\\n4. Verify your edits by re-reading the changed regions.\\n\\nGuidance:\\n- Break down multi-file tasks. Handle one logical unit at a time.\\n- Your final answer must contain ONLY your answer — it is the only thing the caller receives.\\n- Summarize what you changed and where (path:line), and any follow-up the caller should know.\\n- Do not narrate intermediate steps or include tool logs.\\n- If you hit a blocker, stop and report it. Do not leave the codebase in a broken state.\\n\",\n\texplore:\n\t\t\"---\\nname: explore\\ndescription: |\\n Use this subagent ONLY when:\\n - Reading or understanding code without changes\\n - Scouting a codebase for plans or maps\\n - Analyzing dependencies, imports, project structure\\n - Investigating errors or tracing execution flow\\n - Estimating scope before edits\\n\\n DO NOT use for:\\n - Writing or modifying code\\n - Running tests or linting\\n - Reviewing code quality\\n\\n Output: Concise summary, file list, or plan. No code changes.\\n Cost: Low (read-only)\\n Isolation: Can run in parallel with other explore tasks\\ntools: read, grep, find, ls, bash\\nmodel: haiku\\n---\\nYou are an explore-only agent running inside hoocode. You read code and produce summaries. You NEVER edit files. You run in an isolated context and cannot see the parent conversation.\\n\\nScope:\\n- Do not modify, create, or delete files. Use bash only for read-only inspection (e.g. git log, wc, tree).\\n- Use read, grep, find, and ls (and read-only shell commands) to locate and understand code.\\n\\nMethod:\\n1. Break the task into concrete questions.\\n2. Search broadly, then read the specific files that matter.\\n3. Trace logic across files; note exact paths and line numbers.\\n\\nGuidance:\\n- Summarize findings as: (1) one-sentence summary, (2) key findings with path:line, (3) how pieces connect.\\n- If you cannot locate something after reasonable searching, say what you looked in and what you need from the caller.\\n- Do not narrate your search or include tool logs.\\n\",\n\t\"general-purpose\":\n\t\t\"---\\nname: general-purpose\\ndescription: |\\n Use this subagent when:\\n - A task is multi-step or open-ended and needs both investigation and action\\n - No specialized agent (explore, edit, review, test, doc) clearly fits\\n - You are searching for code or a pattern and are unsure where it lives\\n - The work spans reading, editing, and running commands together\\n\\n Prefer a specialized agent when the task is clearly read-only (explore),\\n a focused code change (edit), a review (review), running tests (test), or\\n documentation (doc).\\n\\n Output: The completed result plus a summary of what was done and where.\\n Cost: Variable (read + write + run)\\n Isolation: Should not run concurrently with edit tasks on the same files\\ntools: read, bash, edit, write, grep, find, ls\\nmodel: sonnet\\nmaxTurns: 25\\n---\\nYou are a general-purpose subagent running inside hoocode. You handle multi-step and open-ended tasks end to end. You run in an isolated context and cannot see the parent conversation.\\n\\nScope:\\n- You may read, search, edit, and write files, and run commands needed to complete the task.\\n- Stay within the requested task. Do not refactor or change unrelated code.\\n\\nMethod:\\n1. Break the task into concrete steps.\\n2. Investigate before acting: read the relevant files and trace the logic.\\n3. Make the smallest changes that fully satisfy the task, matching existing style.\\n4. Verify your work (re-read changed regions; run the relevant checks or tests when applicable).\\n\\nGuidance:\\n- Your final answer is the only thing the caller receives. Make it self-contained.\\n- Summarize what you did and where (path:line), and any follow-up the caller should know.\\n- Do not narrate intermediate steps or include tool logs.\\n- If you hit a blocker, stop and report it. Do not leave the codebase in a broken state.\\n\",\n\treview:\n\t\t\"---\\nname: review\\ndescription: |\\n Use this subagent ONLY when:\\n - Reviewing code for correctness, clarity, or risk\\n - Auditing for security vulnerabilities\\n - Checking compliance with project conventions\\n - Evaluating performance or architecture concerns\\n\\n DO NOT use for:\\n - Making code changes (use edit agent)\\n - Running tests (use test agent)\\n - Read-only exploration (use explore agent)\\n\\n Output: Verdict + findings ordered by severity with path:line and suggestions.\\n Cost: Low (read-only)\\n Isolation: Can run in parallel with explore and doc tasks\\ntools: read, grep, find, ls\\nmodel: haiku\\n---\\nYou are a review subagent running inside hoocode. You review code and report issues. You run in an isolated context and cannot see the parent conversation.\\n\\nScope:\\n- READ ONLY. Do not modify files.\\n- Review the code or change named in the task for correctness, clarity, and risk.\\n\\nMethod:\\n1. Read the relevant code (and any diff or context provided).\\n2. Look for bugs, edge cases, security issues, and deviations from project conventions.\\n3. Prioritize correctness over style nits.\\n\\nGuidance:\\n- Start with an overall verdict (approve / approve with minor suggestions / needs changes).\\n- List findings ordered by severity, each with path:line and a concrete suggestion.\\n- If nothing is wrong, say so explicitly.\\n- Do not narrate your reading process or include tool logs.\\n- Your final message must contain ONLY your answer.\\n\",\n\ttest: \"---\\nname: test\\ndescription: |\\n Use this subagent ONLY when:\\n - Running test suites or individual tests\\n - Validating functionality after changes\\n - Checking test coverage or generating coverage reports\\n - Diagnosing test failures\\n\\n DO NOT use for:\\n - Modifying source code (use edit agent)\\n - Read-only exploration (use explore agent)\\n - Security audits (use review agent)\\n\\n Output: Pass/fail counts, failing test paths, and root causes.\\n Cost: Medium (read + run)\\n Isolation: Can run in parallel with explore tasks; should not run during active edits\\ntools: read, bash, grep, find, ls\\nmodel: sonnet\\n---\\nYou are a test subagent running inside hoocode. You run tests and report results. You run in an isolated context and cannot see the parent conversation.\\n\\nScope:\\n- You may read files and run commands (test runners, build, lint). Do not modify source files.\\n- Run the tests the task names; if unspecified, find and run the most relevant suite.\\n\\nMethod:\\n1. Locate the test command from package.json, config, or the task instructions.\\n2. Run it. Capture pass/fail counts and the first meaningful failures.\\n3. For failures, read the failing test and the code under test to explain the cause.\\n\\nGuidance:\\n- Report: (1) command(s) run, (2) overall result (pass/fail with counts), (3) for each failure: path:line, error message, and likely cause.\\n- Keep failure descriptions concise; do not dump full logs.\\n- If tests pass, confirm that the relevant area is covered.\\n- Your final message must contain ONLY your answer.\\n\",\n};\n"]}
|
|
1
|
+
{"version":3,"file":"init-templates.generated.js","sourceRoot":"","sources":["../src/init-templates.generated.ts"],"names":[],"mappings":"AAAA,iEAA+D;AAC/D,sDAAsD;AACtD,wCAAwC;AAExC,MAAM,CAAC,MAAM,uBAAuB,GACnC,ocAAoc,CAAC;AAEtc,MAAM,CAAC,MAAM,cAAc,GAA2B;IACrD,GAAG,EAAE,ogBAAkgB;IACvgB,KAAK,EAAE,unBAAmnB;IAC1nB,KAAK,EAAE,6pBAAipB;IACxpB,IAAI,EAAE,isBAAqrB;CAC3rB,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAA2B,EAAE,CAAC;AAE5D,MAAM,CAAC,MAAM,sBAAsB,GAA2B;IAC7D,GAAG,EAAE,k9CAAk9C;IACv9C,IAAI,EAAE,wmDAAsmD;IAC5mD,OAAO,EACN,y+CAAy+C;IAC1+C,iBAAiB,EAChB,4yDAA4yD;IAC7yD,MAAM,EACL,m9CAAm9C;IACp9C,IAAI,EAAE,8iDAA8iD;CACpjD,CAAC","sourcesContent":["// AUTO-GENERATED by scripts/embed-templates.mjs — do not edit.\n// Source of truth: packages/coding-agent/templates/**\n// Regenerated on every `npm run build`.\n\nexport const EMBEDDED_DEFAULT_CONFIG: string =\n\t'{\\n \"version\": \"1.0\",\\n \"active_mode\": \"build\",\\n \"llm\": {\\n \"default_provider\": \"anthropic\",\\n \"providers\": {\\n \"anthropic\": { \"api_key_env\": \"ANTHROPIC_API_KEY\" },\\n \"openai\": { \"api_key_env\": \"OPENAI_API_KEY\" }\\n }\\n },\\n \"modes\": {\\n \"ask\": { \"auto_allow\": [\"read\"] },\\n \"plan\": { \"auto_allow\": [\"read\", \"write\"] },\\n \"build\": { \"auto_allow\": [\"read\"] },\\n \"debug\": { \"auto_allow\": [\"read\", \"bash\"] }\\n }\\n}\\n';\n\nexport const EMBEDDED_MODES: Record<string, string> = {\n\task: \"You are in **ask mode** — read-only Q&A.\\n\\nPermitted: read files, run grep/find, explain code, trace logic, compare approaches, debug conceptually.\\nForbidden: edit files, write files, run commands that modify state.\\n\\nWhen answering:\\n- Cite file paths and line numbers.\\n- Prefer precise over verbose.\\n- If a question requires a code change to answer properly, describe the change; do not apply it.\\n- If the user asks you to edit something, decline and suggest switching to build mode with `/mode build`.\\n\",\n\tbuild: \"You are in **build mode** — implement carefully, one step at a time.\\n\\nRules:\\n- **One tool per turn.** Plan the action, call the tool, wait for the result before proceeding.\\n- **Read before editing.** Never write to a file you have not read in this session.\\n- **Show diffs** before applying non-trivial edits; wait for implicit acceptance.\\n- **Dangerous ops** (delete, force-push, drop table, rm -rf): state what you are about to do and wait for explicit confirmation.\\n- **Match existing style** — indentation, naming, import order.\\n- **Run tests** after every logical unit of change. Fix failures before continuing.\\n\",\n\tdebug: \"You are in **debug mode** — root-cause analysis only, no file modifications.\\n\\nProcess:\\n1. **Gather evidence** — read logs, error traces, and relevant source. Run safe diagnostic commands (grep, find, read, non-mutating shell commands).\\n2. **Reproduce** — identify the minimal condition that triggers the bug.\\n3. **Trace** — follow the full call path from entry point to failure site, citing file and line at each step.\\n4. **State the root cause** in one clear sentence.\\n5. **Describe the fix** — files, lines, and what to change — but do not apply it.\\n\\nForbidden: edit or write any file. To apply a fix, switch to build mode with `/mode build`.\\n\",\n\tplan: 'You are in **plan mode** — explore and design, no source edits.\\n\\nYour job: produce a complete, actionable implementation plan.\\n\\nSteps:\\n1. Read relevant files and ask clarifying questions before drafting.\\n2. Write the finished plan to `{{PLAN_PATH}}` with these sections:\\n - **Goal** — one sentence.\\n - **Files to modify** — path, line range, what changes.\\n - **New files** — path, purpose.\\n - **Tests** — what to add or update.\\n - **Verification** — commands to confirm correctness.\\n3. After writing the plan, tell the user: \"Plan written to `{{PLAN_PATH}}`. Run `/approve` to begin execution.\"\\n\\nForbidden: edit any source file. Only `{{PLAN_PATH}}` may be written.\\n',\n};\n\nexport const EMBEDDED_PROFILES: Record<string, string> = {};\n\nexport const EMBEDDED_AGENT_PROMPTS: Record<string, string> = {\n\tdoc: \"---\\nname: doc\\ndescription: |\\n Use this subagent ONLY when:\\n - Writing or updating README files\\n - Adding inline code comments or API documentation\\n - Creating user guides, tutorials, or changelogs\\n - Explaining architecture or design decisions\\n\\n DO NOT use for:\\n - Modifying source logic (use edit agent)\\n - Read-only exploration (use explore agent)\\n - Code review (use review agent)\\n\\n Output: Written documentation. Concise, accurate, and well-structured.\\n Cost: Low (read + write docs only)\\n Isolation: Can run in parallel with explore and review tasks\\ntools: read, write, edit, grep, find, ls\\nmodel: sonnet\\nbackground: true\\n---\\nYou are a documentation subagent running inside hoocode. You write and update documentation. You run in an isolated context and cannot see the parent conversation.\\n\\nScope:\\n- You may read files and write documentation files (README, comments, guides). Do not modify source logic.\\n- Produce concise, accurate, and well-structured documentation.\\n\\nMethod:\\n1. Read the relevant source files to understand what needs documenting.\\n2. Write or update the requested documentation.\\n3. Verify that the documentation is consistent with the code.\\n\\nGuidance:\\n- Focus on clarity and accuracy. Avoid unnecessary verbosity.\\n- Match the project's existing documentation style.\\n- Your final message must contain ONLY the documentation or a summary of what was written.\\n- Do not narrate intermediate steps or include tool logs.\\n\",\n\tedit: \"---\\nname: edit\\ndescription: |\\n Use this subagent ONLY when:\\n - Writing new code or creating new files\\n - Refactoring existing code across one or more files\\n - Fixing bugs or applying targeted corrections\\n - Migrating patterns or renaming symbols\\n\\n DO NOT use for:\\n - Read-only exploration\\n - Running tests (use test agent)\\n - Code review (use review agent)\\n\\n Output: Changed files with path:line descriptions. No narration.\\n Cost: Medium (read + write)\\n Isolation: Should not run concurrently with other edit tasks on the same files\\ntools: read, edit, write, grep, find, ls, bash\\nmodel: sonnet\\nmaxTurns: 25\\n---\\nYou are an edit subagent running inside hoocode. You implement one focused code change. You run in an isolated context and cannot see the parent conversation.\\n\\nScope:\\n- You may read, edit, and write files, and run commands needed to make the change.\\n- Stay strictly within the requested task. Do not refactor unrelated code.\\n\\nMethod:\\n1. Read the relevant files before changing them.\\n2. Match the existing style: indentation, naming, import order.\\n3. Make the smallest change that fully satisfies the task.\\n4. Verify your edits by re-reading the changed regions.\\n\\nGuidance:\\n- Break down multi-file tasks. Handle one logical unit at a time.\\n- Your final answer must contain ONLY your answer — it is the only thing the caller receives.\\n- Summarize what you changed and where (path:line), and any follow-up the caller should know.\\n- Do not narrate intermediate steps or include tool logs.\\n- If you hit a blocker, stop and report it. Do not leave the codebase in a broken state.\\n\",\n\texplore:\n\t\t\"---\\nname: explore\\ndescription: |\\n Use this subagent ONLY when:\\n - Reading or understanding code without changes\\n - Scouting a codebase for plans or maps\\n - Analyzing dependencies, imports, project structure\\n - Investigating errors or tracing execution flow\\n - Estimating scope before edits\\n\\n DO NOT use for:\\n - Writing or modifying code\\n - Running tests or linting\\n - Reviewing code quality\\n\\n Output: Concise summary, file list, or plan. No code changes.\\n Cost: Low (read-only)\\n Isolation: Can run in parallel with other explore tasks\\ntools: read, grep, find, ls, bash\\nmodel: haiku\\nbackground: true\\n---\\nYou are an explore-only agent running inside hoocode. You read code and produce summaries. You NEVER edit files. You run in an isolated context and cannot see the parent conversation.\\n\\nScope:\\n- Do not modify, create, or delete files. Use bash only for read-only inspection (e.g. git log, wc, tree).\\n- Use read, grep, find, and ls (and read-only shell commands) to locate and understand code.\\n\\nMethod:\\n1. Break the task into concrete questions.\\n2. Search broadly, then read the specific files that matter.\\n3. Trace logic across files; note exact paths and line numbers.\\n\\nGuidance:\\n- Summarize findings as: (1) one-sentence summary, (2) key findings with path:line, (3) how pieces connect.\\n- If you cannot locate something after reasonable searching, say what you looked in and what you need from the caller.\\n- Do not narrate your search or include tool logs.\\n\",\n\t\"general-purpose\":\n\t\t\"---\\nname: general-purpose\\ndescription: |\\n Use this subagent when:\\n - A task is multi-step or open-ended and needs both investigation and action\\n - No specialized agent (explore, edit, review, test, doc) clearly fits\\n - You are searching for code or a pattern and are unsure where it lives\\n - The work spans reading, editing, and running commands together\\n\\n Prefer a specialized agent when the task is clearly read-only (explore),\\n a focused code change (edit), a review (review), running tests (test), or\\n documentation (doc).\\n\\n Output: The completed result plus a summary of what was done and where.\\n Cost: Variable (read + write + run)\\n Isolation: Should not run concurrently with edit tasks on the same files\\ntools: read, bash, edit, write, grep, find, ls\\nmodel: sonnet\\nmaxTurns: 25\\n---\\nYou are a general-purpose subagent running inside hoocode. You handle multi-step and open-ended tasks end to end. You run in an isolated context and cannot see the parent conversation.\\n\\nScope:\\n- You may read, search, edit, and write files, and run commands needed to complete the task.\\n- Stay within the requested task. Do not refactor or change unrelated code.\\n\\nMethod:\\n1. Break the task into concrete steps.\\n2. Investigate before acting: read the relevant files and trace the logic.\\n3. Make the smallest changes that fully satisfy the task, matching existing style.\\n4. Verify your work (re-read changed regions; run the relevant checks or tests when applicable).\\n\\nGuidance:\\n- Your final answer is the only thing the caller receives. Make it self-contained.\\n- Summarize what you did and where (path:line), and any follow-up the caller should know.\\n- Do not narrate intermediate steps or include tool logs.\\n- If you hit a blocker, stop and report it. Do not leave the codebase in a broken state.\\n\",\n\treview:\n\t\t\"---\\nname: review\\ndescription: |\\n Use this subagent ONLY when:\\n - Reviewing code for correctness, clarity, or risk\\n - Auditing for security vulnerabilities\\n - Checking compliance with project conventions\\n - Evaluating performance or architecture concerns\\n\\n DO NOT use for:\\n - Making code changes (use edit agent)\\n - Running tests (use test agent)\\n - Read-only exploration (use explore agent)\\n\\n Output: Verdict + findings ordered by severity with path:line and suggestions.\\n Cost: Low (read-only)\\n Isolation: Can run in parallel with explore and doc tasks\\ntools: read, grep, find, ls\\nmodel: haiku\\nbackground: true\\n---\\nYou are a review subagent running inside hoocode. You review code and report issues. You run in an isolated context and cannot see the parent conversation.\\n\\nScope:\\n- READ ONLY. Do not modify files.\\n- Review the code or change named in the task for correctness, clarity, and risk.\\n\\nMethod:\\n1. Read the relevant code (and any diff or context provided).\\n2. Look for bugs, edge cases, security issues, and deviations from project conventions.\\n3. Prioritize correctness over style nits.\\n\\nGuidance:\\n- Start with an overall verdict (approve / approve with minor suggestions / needs changes).\\n- List findings ordered by severity, each with path:line and a concrete suggestion.\\n- If nothing is wrong, say so explicitly.\\n- Do not narrate your reading process or include tool logs.\\n- Your final message must contain ONLY your answer.\\n\",\n\ttest: \"---\\nname: test\\ndescription: |\\n Use this subagent ONLY when:\\n - Running test suites or individual tests\\n - Validating functionality after changes\\n - Checking test coverage or generating coverage reports\\n - Diagnosing test failures\\n\\n DO NOT use for:\\n - Modifying source code (use edit agent)\\n - Read-only exploration (use explore agent)\\n - Security audits (use review agent)\\n\\n Output: Pass/fail counts, failing test paths, and root causes.\\n Cost: Medium (read + run)\\n Isolation: Can run in parallel with explore tasks; should not run during active edits\\ntools: read, bash, grep, find, ls\\nmodel: sonnet\\nbackground: true\\n---\\nYou are a test subagent running inside hoocode. You run tests and report results. You run in an isolated context and cannot see the parent conversation.\\n\\nScope:\\n- You may read files and run commands (test runners, build, lint). Do not modify source files.\\n- Run the tests the task names; if unspecified, find and run the most relevant suite.\\n\\nMethod:\\n1. Locate the test command from package.json, config, or the task instructions.\\n2. Run it. Capture pass/fail counts and the first meaningful failures.\\n3. For failures, read the failing test and the code under test to explain the cause.\\n\\nGuidance:\\n- Report: (1) command(s) run, (2) overall result (pass/fail with counts), (3) for each failure: path:line, error message, and likely cause.\\n- Keep failure descriptions concise; do not dump full logs.\\n- If tests pass, confirm that the relevant area is covered.\\n- Your final message must contain ONLY your answer.\\n\",\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-panel.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/task-panel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAiP/D;;;;;;;;;;;;;;GAcG;AACH,qBAAa,kBAAmB,YAAW,SAAS;IACnD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAa;IAChC,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,cAAc,CAA+C;IAErE,YAAY,EAAE,CAAC,EAAE,GAAG,EAEnB;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,6EAA6E;IAC7E,OAAO,CAAC,eAAe;IAcvB,gDAAgD;IAChD,OAAO,IAAI,IAAI,CAKd;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAqB9B;CACD","sourcesContent":["import type { Component, TUI } from \"@kolisachint/hoocode-tui\";\nimport { truncateToWidth, visibleWidth } from \"@kolisachint/hoocode-tui\";\nimport type { Task, TaskStatus } from \"../../../core/task-store.js\";\nimport { taskStore } from \"../../../core/task-store.js\";\nimport { theme } from \"../theme/theme.js\";\n\nconst TASK_STATUS_ICON: Record<TaskStatus, string> = {\n\tpending: \"●\",\n\tin_progress: \"◐\",\n\tdone: \"✓\",\n\tfailed: \"✗\",\n};\n\n/** Braille spinner frames + cadence, matched to the TUI Loader so the active row animates in step. */\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\n/** A thin colored left rail groups the pane without a box, the way the design's `border-left` does. */\nconst RAIL = \"▎\";\n\n/** Cells in the deterministic progress bar (matches the design's 14-cell track). */\nconst PROGRESS_CELLS = 14;\n\n/** Overall pane state, derived from the task statuses. Drives the rail color + header stamp. */\ntype PanelState = \"working\" | \"reviewed\" | \"stopped\";\n\ninterface StatePresentation {\n\treadonly icon: string;\n\treadonly label: string;\n\treadonly color: \"warning\" | \"success\" | \"error\";\n}\n\nconst STATE_PRESENTATION: Record<PanelState, StatePresentation> = {\n\tworking: { icon: \"◐\", label: \"working\", color: \"warning\" },\n\treviewed: { icon: \"✓\", label: \"reviewed\", color: \"success\" },\n\tstopped: { icon: \"✗\", label: \"stopped\", color: \"error\" },\n};\n\nfunction panelState(tasks: readonly Task[]): PanelState {\n\tif (tasks.some((t) => t.status === \"failed\")) return \"stopped\";\n\tconst active = tasks.some((t) => t.status === \"in_progress\" || t.status === \"pending\");\n\treturn active ? \"working\" : \"reviewed\";\n}\n\nfunction taskStatusColor(status: TaskStatus): \"dim\" | \"warning\" | \"success\" | \"error\" {\n\tswitch (status) {\n\t\tcase \"in_progress\":\n\t\t\treturn \"warning\";\n\t\tcase \"done\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tdefault:\n\t\t\treturn \"dim\";\n\t}\n}\n\n/** Format a duration in seconds into a compact, terminal-friendly string. */\nfunction formatDuration(secs: number): string {\n\tconst s = Math.max(0, secs);\n\tif (s < 10) return `${s.toFixed(1)}s`;\n\tif (s < 60) return `${Math.round(s)}s`;\n\tconst mins = Math.floor(s / 60);\n\tconst rem = Math.round(s % 60);\n\treturn `${mins}m${rem.toString().padStart(2, \"0\")}s`;\n}\n\n/** Wall-clock time a task occupied, derived from its create/update stamps. */\nfunction taskElapsedSecs(task: Task): number {\n\treturn Math.max(0, (task.updatedAt - task.createdAt) / 1000);\n}\n\n/** Sum the token + cost usage reported by the tasks shown this turn. */\nfunction sumTurnUsage(tasks: readonly Task[]): { input: number; output: number; cost: number } | null {\n\tlet input = 0;\n\tlet output = 0;\n\tlet cost = 0;\n\tfor (const task of tasks) {\n\t\tif (!task.usage) continue;\n\t\tinput += task.usage.input;\n\t\toutput += task.usage.output;\n\t\tcost += task.usage.cost;\n\t}\n\tif (input === 0 && output === 0 && cost === 0) return null;\n\treturn { input, output, cost };\n}\n\n/**\n * Deterministic block-glyph progress bar: a heavy run (━) for the completed\n * fraction over a dim track. In-progress tasks count as half, so the bar moves\n * the moment work starts. Fraction is the only input — no animation, no guess.\n */\nfunction progressBar(done: number, active: number, total: number): { plain: string; styled: string } {\n\tconst ratio = total > 0 ? Math.max(0, Math.min(1, (done + active * 0.5) / total)) : 0;\n\tconst filled = Math.round(ratio * PROGRESS_CELLS);\n\tconst fill = \"━\".repeat(filled);\n\tconst track = \"━\".repeat(PROGRESS_CELLS - filled);\n\treturn {\n\t\tplain: fill + track,\n\t\tstyled: theme.fg(\"success\", fill) + theme.fg(\"dim\", track),\n\t};\n}\n\n/**\n * Ledger header: a state stamp (◐ working / ✓ reviewed / ✗ stopped) + a\n * deterministic progress bar and done/total count on the left, and the per-turn\n * token + elapsed + cost delta (summed across the tasks below) on the right.\n */\nfunction formatHeader(tasks: readonly Task[], width: number, state: PanelState, totalSecs: number): string {\n\tconst total = tasks.length;\n\tconst done = tasks.filter((t) => t.status === \"done\").length;\n\tconst active = tasks.filter((t) => t.status === \"in_progress\").length;\n\n\tconst { icon, label, color } = STATE_PRESENTATION[state];\n\tconst stampPlain = `${icon} ${label.toUpperCase()}`;\n\tconst stamp = `${theme.fg(color, icon)} ${theme.bold(theme.fg(color, label.toUpperCase()))}`;\n\n\tconst bar = progressBar(done, active, total);\n\tconst countPlain = `${done}/${total}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${total}`);\n\n\t// Left cluster has a full form (stamp · bar · count) and a compact fallback\n\t// (stamp · count) that drops the bar when the terminal is too narrow.\n\tconst leftFullPlain = `${stampPlain} ${bar.plain} ${countPlain}`;\n\tconst leftFull = `${stamp} ${bar.styled} ${count}`;\n\tconst leftMinPlain = `${stampPlain} ${countPlain}`;\n\tconst leftMin = `${stamp} ${count}`;\n\n\tconst turn = sumTurnUsage(tasks);\n\tlet turnPlain = \"\";\n\tlet turnText = \"\";\n\tif (turn) {\n\t\tconst inTok = formatTokens(turn.input);\n\t\tconst outTok = formatTokens(turn.output);\n\t\tconst elapsed = formatDuration(totalSecs);\n\t\tconst showCost = turn.cost > 0;\n\t\tconst costStr = showCost ? `$${turn.cost.toFixed(3)}` : \"\";\n\t\tturnPlain = `turn ↑${inTok} ↓${outTok} · ${elapsed}${showCost ? ` · ${costStr}` : \"\"}`;\n\t\t// Turn delta: muted framing, numbers one step brighter (bold), separators dim.\n\t\tturnText =\n\t\t\ttheme.fg(\"muted\", \"turn ↑\") +\n\t\t\ttheme.bold(inTok) +\n\t\t\ttheme.fg(\"muted\", \" ↓\") +\n\t\t\ttheme.bold(outTok) +\n\t\t\ttheme.fg(\"dim\", \" · \") +\n\t\t\ttheme.fg(\"muted\", elapsed) +\n\t\t\t(showCost ? theme.fg(\"dim\", \" · \") + theme.bold(costStr) : \"\");\n\t}\n\n\tif (turnPlain) {\n\t\tif (visibleWidth(leftFullPlain) + 2 + visibleWidth(turnPlain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftFullPlain) - visibleWidth(turnPlain));\n\t\t\treturn leftFull + \" \".repeat(pad) + turnText;\n\t\t}\n\t\tif (visibleWidth(leftMinPlain) + 2 + visibleWidth(turnPlain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftMinPlain) - visibleWidth(turnPlain));\n\t\t\treturn leftMin + \" \".repeat(pad) + turnText;\n\t\t}\n\t}\n\tif (visibleWidth(leftFullPlain) <= width) return leftFull;\n\treturn truncateToWidth(leftMin, width, \"…\");\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\treturn `${(count / 1000000).toFixed(1)}M`;\n}\n\nfunction formatTaskLine(task: Task, width: number, frame: number): string {\n\tconst isProgress = task.status === \"in_progress\";\n\tconst iconGlyph = isProgress\n\t\t? (SPINNER_FRAMES[frame] ?? TASK_STATUS_ICON.in_progress)\n\t\t: TASK_STATUS_ICON[task.status];\n\tconst icon = theme.fg(taskStatusColor(task.status), iconGlyph);\n\n\tconst idLabel = `#${task.id}`;\n\tconst title = task.title;\n\t// The id recedes (dim); the title carries the line. Done titles fade to muted\n\t// (settled work), pending dim (not started), active goes bold, failed turns red.\n\tconst styledId = theme.fg(\"dim\", idLabel);\n\tlet styledTitle: string;\n\tswitch (task.status) {\n\t\tcase \"done\":\n\t\t\tstyledTitle = theme.fg(\"muted\", title);\n\t\t\tbreak;\n\t\tcase \"pending\":\n\t\t\tstyledTitle = theme.fg(\"dim\", title);\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tstyledTitle = theme.fg(\"error\", title);\n\t\t\tbreak;\n\t\tcase \"in_progress\":\n\t\t\tstyledTitle = theme.bold(title);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tstyledTitle = title;\n\t}\n\n\t// Right column: settled rows carry their audit stamp (tokens + elapsed); the\n\t// active row reads `running…`, pending rows read `queued`.\n\tlet rightPlain = \"\";\n\tlet rightStyled = \"\";\n\tif (task.status === \"done\" || task.status === \"failed\") {\n\t\tconst parts: string[] = [];\n\t\tlet tokenText = \"\";\n\t\tif (task.usage) {\n\t\t\tconst totalTok = task.usage.input + task.usage.output;\n\t\t\tif (totalTok > 0) tokenText = formatTokens(totalTok);\n\t\t}\n\t\tconst elapsed = formatDuration(taskElapsedSecs(task));\n\t\tif (tokenText) {\n\t\t\tparts.push(tokenText, elapsed);\n\t\t\trightStyled = theme.fg(\"muted\", tokenText) + theme.fg(\"dim\", ` · ${elapsed}`);\n\t\t} else {\n\t\t\tparts.push(elapsed);\n\t\t\trightStyled = theme.fg(\"dim\", elapsed);\n\t\t}\n\t\trightPlain = parts.join(\" · \");\n\t} else if (task.status === \"in_progress\") {\n\t\trightPlain = \"running…\";\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t} else if (task.status === \"pending\") {\n\t\trightPlain = \"queued\";\n\t\trightStyled = theme.fg(\"dim\", rightPlain);\n\t}\n\n\tconst rightWidth = rightPlain ? visibleWidth(rightPlain) + 1 : 0;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\n\tconst plainText = `${iconGlyph} ${idLabel} ${title}`;\n\tconst available = Math.max(0, leftWidth - visibleWidth(plainText) + visibleWidth(title));\n\tconst left = truncateToWidth(`${icon} ${styledId} ${styledTitle}`, available, \"…\");\n\n\tif (!rightPlain) return left;\n\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Task panel rendered just above the editor prompt.\n *\n * - A state-colored left rail groups the pane (working=warning, reviewed=success,\n * stopped=error) without drawing a box.\n * - A ledger header tops the list: a state stamp + deterministic progress bar +\n * done/total count on the left, the per-turn token/elapsed/cost delta on the right.\n * - Shows all tasks with all statuses (pending / in_progress / done / failed).\n * The active row animates a braille spinner; pending rows read `queued`.\n * - Subagent mode is intentionally NOT shown here (e.g. no \"[explore]\" tag).\n * - LIFO within the window: newest tasks appear at the bottom (closest to the prompt).\n * - Finished tasks carry their wall-clock cost and stay visible until the next\n * user message arrives (see taskStore.reset()), not the moment they finish.\n * - Collapses to zero lines when there are no tasks.\n */\nexport class TaskPanelComponent implements Component {\n\tprivate readonly ui: TUI | null;\n\tprivate frame = 0;\n\tprivate animationTimer: ReturnType<typeof setInterval> | null = null;\n\n\tconstructor(ui?: TUI) {\n\t\tthis.ui = ui ?? null;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached rendering state.\n\t}\n\n\t/** Run the spinner timer only while a task is active, ticking re-renders. */\n\tprivate ensureAnimation(active: boolean): void {\n\t\tif (active && this.ui && !this.animationTimer) {\n\t\t\tthis.animationTimer = setInterval(() => {\n\t\t\t\tthis.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n\t\t\t\tthis.ui?.requestRender();\n\t\t\t}, SPINNER_INTERVAL_MS);\n\t\t\tthis.animationTimer.unref?.();\n\t\t} else if (!active && this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t\tthis.frame = 0;\n\t\t}\n\t}\n\n\t/** Stop the spinner timer. Call on teardown. */\n\tdispose(): void {\n\t\tif (this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tconst tasks = taskStore.list();\n\t\tif (tasks.length === 0) {\n\t\t\tthis.ensureAnimation(false);\n\t\t\treturn [];\n\t\t}\n\n\t\tconst hasActive = tasks.some((t) => t.status === \"in_progress\");\n\t\tthis.ensureAnimation(hasActive);\n\n\t\tconst state = panelState(tasks);\n\t\tconst totalSecs = tasks.reduce((sum, t) => sum + taskElapsedSecs(t), 0);\n\t\tconst railColor = STATE_PRESENTATION[state].color;\n\t\tconst gutter = `${theme.fg(railColor, RAIL)} `;\n\t\tconst inner = Math.max(0, width - visibleWidth(RAIL) - 1);\n\n\t\tconst lines: string[] = [gutter + formatHeader(tasks, inner, state, totalSecs)];\n\t\tfor (const task of tasks) {\n\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame));\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"task-panel.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/task-panel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAyP/D;;;;;;;;;;;;;;GAcG;AACH,qBAAa,kBAAmB,YAAW,SAAS;IACnD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAa;IAChC,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,cAAc,CAA+C;IAErE,YAAY,EAAE,CAAC,EAAE,GAAG,EAEnB;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,6EAA6E;IAC7E,OAAO,CAAC,eAAe;IAcvB,gDAAgD;IAChD,OAAO,IAAI,IAAI,CAKd;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAqB9B;CACD","sourcesContent":["import type { Component, TUI } from \"@kolisachint/hoocode-tui\";\nimport { truncateToWidth, visibleWidth } from \"@kolisachint/hoocode-tui\";\nimport type { Task, TaskStatus } from \"../../../core/task-store.js\";\nimport { taskStore } from \"../../../core/task-store.js\";\nimport { theme } from \"../theme/theme.js\";\n\nconst TASK_STATUS_ICON: Record<TaskStatus, string> = {\n\tpending: \"●\",\n\tin_progress: \"◐\",\n\tdone: \"✓\",\n\tfailed: \"✗\",\n};\n\n/** Braille spinner frames + cadence, matched to the TUI Loader so the active row animates in step. */\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\n/** A thin colored left rail groups the pane without a box, the way the design's `border-left` does. */\nconst RAIL = \"▎\";\n\n/** Cells in the deterministic progress bar (matches the design's 14-cell track). */\nconst PROGRESS_CELLS = 14;\n\n/** Overall pane state, derived from the task statuses. Drives the rail color + header stamp. */\ntype PanelState = \"working\" | \"reviewed\" | \"stopped\";\n\ninterface StatePresentation {\n\treadonly icon: string;\n\treadonly label: string;\n\treadonly color: \"warning\" | \"success\" | \"error\";\n}\n\nconst STATE_PRESENTATION: Record<PanelState, StatePresentation> = {\n\tworking: { icon: \"◐\", label: \"working\", color: \"warning\" },\n\treviewed: { icon: \"✓\", label: \"reviewed\", color: \"success\" },\n\tstopped: { icon: \"✗\", label: \"stopped\", color: \"error\" },\n};\n\nfunction panelState(tasks: readonly Task[]): PanelState {\n\tif (tasks.some((t) => t.status === \"failed\")) return \"stopped\";\n\tconst active = tasks.some((t) => t.status === \"in_progress\" || t.status === \"pending\");\n\treturn active ? \"working\" : \"reviewed\";\n}\n\nfunction taskStatusColor(status: TaskStatus): \"dim\" | \"warning\" | \"success\" | \"error\" {\n\tswitch (status) {\n\t\tcase \"in_progress\":\n\t\t\treturn \"warning\";\n\t\tcase \"done\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tdefault:\n\t\t\treturn \"dim\";\n\t}\n}\n\n/** Format a duration in seconds into a compact, terminal-friendly string. */\nfunction formatDuration(secs: number): string {\n\tconst s = Math.max(0, secs);\n\tif (s < 10) return `${s.toFixed(1)}s`;\n\tif (s < 60) return `${Math.round(s)}s`;\n\tconst mins = Math.floor(s / 60);\n\tconst rem = Math.round(s % 60);\n\treturn `${mins}m${rem.toString().padStart(2, \"0\")}s`;\n}\n\n/** Wall-clock time a task occupied, derived from its create/update stamps. */\nfunction taskElapsedSecs(task: Task): number {\n\treturn Math.max(0, (task.updatedAt - task.createdAt) / 1000);\n}\n\n/** Sum the token + cost usage reported by the tasks shown this turn. */\nfunction sumTurnUsage(tasks: readonly Task[]): { input: number; output: number; cost: number } | null {\n\tlet input = 0;\n\tlet output = 0;\n\tlet cost = 0;\n\tfor (const task of tasks) {\n\t\tif (!task.usage) continue;\n\t\tinput += task.usage.input;\n\t\toutput += task.usage.output;\n\t\tcost += task.usage.cost;\n\t}\n\tif (input === 0 && output === 0 && cost === 0) return null;\n\treturn { input, output, cost };\n}\n\n/**\n * Deterministic block-glyph progress bar: a heavy run (━) for the completed\n * fraction over a dim track. In-progress tasks count as half, so the bar moves\n * the moment work starts. Fraction is the only input — no animation, no guess.\n */\nfunction progressBar(done: number, active: number, total: number): { plain: string; styled: string } {\n\tconst ratio = total > 0 ? Math.max(0, Math.min(1, (done + active * 0.5) / total)) : 0;\n\tconst filled = Math.round(ratio * PROGRESS_CELLS);\n\tconst fill = \"━\".repeat(filled);\n\tconst track = \"━\".repeat(PROGRESS_CELLS - filled);\n\treturn {\n\t\tplain: fill + track,\n\t\tstyled: theme.fg(\"success\", fill) + theme.fg(\"dim\", track),\n\t};\n}\n\n/**\n * Ledger header: a state stamp (◐ working / ✓ reviewed / ✗ stopped) + a\n * deterministic progress bar and done/total count on the left, and the per-turn\n * token + elapsed + cost delta (summed across the tasks below) on the right.\n */\nfunction formatHeader(tasks: readonly Task[], width: number, state: PanelState, totalSecs: number): string {\n\tconst total = tasks.length;\n\tconst done = tasks.filter((t) => t.status === \"done\").length;\n\tconst active = tasks.filter((t) => t.status === \"in_progress\").length;\n\n\tconst { icon, label, color } = STATE_PRESENTATION[state];\n\tconst stampPlain = `${icon} ${label.toUpperCase()}`;\n\tconst stamp = `${theme.fg(color, icon)} ${theme.bold(theme.fg(color, label.toUpperCase()))}`;\n\n\tconst bar = progressBar(done, active, total);\n\tconst countPlain = `${done}/${total}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${total}`);\n\n\t// Left cluster has a full form (stamp · bar · count) and a compact fallback\n\t// (stamp · count) that drops the bar when the terminal is too narrow.\n\tconst leftFullPlain = `${stampPlain} ${bar.plain} ${countPlain}`;\n\tconst leftFull = `${stamp} ${bar.styled} ${count}`;\n\tconst leftMinPlain = `${stampPlain} ${countPlain}`;\n\tconst leftMin = `${stamp} ${count}`;\n\n\tconst turn = sumTurnUsage(tasks);\n\tlet turnPlain = \"\";\n\tlet turnText = \"\";\n\tif (turn) {\n\t\tconst inTok = formatTokens(turn.input);\n\t\tconst outTok = formatTokens(turn.output);\n\t\tconst elapsed = formatDuration(totalSecs);\n\t\tconst showCost = turn.cost > 0;\n\t\tconst costStr = showCost ? `$${turn.cost.toFixed(3)}` : \"\";\n\t\tturnPlain = `turn ↑${inTok} ↓${outTok} · ${elapsed}${showCost ? ` · ${costStr}` : \"\"}`;\n\t\t// Turn delta: muted framing, numbers one step brighter (bold), separators dim.\n\t\tturnText =\n\t\t\ttheme.fg(\"muted\", \"turn ↑\") +\n\t\t\ttheme.bold(inTok) +\n\t\t\ttheme.fg(\"muted\", \" ↓\") +\n\t\t\ttheme.bold(outTok) +\n\t\t\ttheme.fg(\"dim\", \" · \") +\n\t\t\ttheme.fg(\"muted\", elapsed) +\n\t\t\t(showCost ? theme.fg(\"dim\", \" · \") + theme.bold(costStr) : \"\");\n\t}\n\n\tif (turnPlain) {\n\t\tif (visibleWidth(leftFullPlain) + 2 + visibleWidth(turnPlain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftFullPlain) - visibleWidth(turnPlain));\n\t\t\treturn leftFull + \" \".repeat(pad) + turnText;\n\t\t}\n\t\tif (visibleWidth(leftMinPlain) + 2 + visibleWidth(turnPlain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftMinPlain) - visibleWidth(turnPlain));\n\t\t\treturn leftMin + \" \".repeat(pad) + turnText;\n\t\t}\n\t}\n\tif (visibleWidth(leftFullPlain) <= width) return leftFull;\n\treturn truncateToWidth(leftMin, width, \"…\");\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\treturn `${(count / 1000000).toFixed(1)}M`;\n}\n\nfunction formatTaskLine(task: Task, width: number, frame: number): string {\n\tconst isProgress = task.status === \"in_progress\";\n\tconst iconGlyph = isProgress\n\t\t? (SPINNER_FRAMES[frame] ?? TASK_STATUS_ICON.in_progress)\n\t\t: TASK_STATUS_ICON[task.status];\n\tconst icon = theme.fg(taskStatusColor(task.status), iconGlyph);\n\n\tconst idLabel = `#${task.id}`;\n\tconst title = task.title;\n\t// The id recedes (dim); the title carries the line. Done titles fade to muted\n\t// (settled work), pending dim (not started), active goes bold, failed turns red.\n\tconst styledId = theme.fg(\"dim\", idLabel);\n\tlet styledTitle: string;\n\tswitch (task.status) {\n\t\tcase \"done\":\n\t\t\tstyledTitle = theme.fg(\"muted\", title);\n\t\t\tbreak;\n\t\tcase \"pending\":\n\t\t\tstyledTitle = theme.fg(\"dim\", title);\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tstyledTitle = theme.fg(\"error\", title);\n\t\t\tbreak;\n\t\tcase \"in_progress\":\n\t\t\tstyledTitle = theme.bold(title);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tstyledTitle = title;\n\t}\n\n\t// Right column: settled rows carry their audit stamp (tokens + elapsed); the\n\t// active row reads `running…`, pending rows read `queued`.\n\tlet rightPlain = \"\";\n\tlet rightStyled = \"\";\n\tif (task.status === \"done\" || task.status === \"failed\") {\n\t\tconst parts: string[] = [];\n\t\tlet tokenText = \"\";\n\t\tif (task.usage) {\n\t\t\tconst totalTok = task.usage.input + task.usage.output;\n\t\t\tif (totalTok > 0) tokenText = formatTokens(totalTok);\n\t\t}\n\t\tconst elapsed = formatDuration(taskElapsedSecs(task));\n\t\tif (tokenText) {\n\t\t\tparts.push(tokenText, elapsed);\n\t\t\trightStyled = theme.fg(\"muted\", tokenText) + theme.fg(\"dim\", ` · ${elapsed}`);\n\t\t} else {\n\t\t\tparts.push(elapsed);\n\t\t\trightStyled = theme.fg(\"dim\", elapsed);\n\t\t}\n\t\trightPlain = parts.join(\" · \");\n\t} else if (task.status === \"in_progress\") {\n\t\trightPlain = \"running…\";\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t} else if (task.status === \"pending\") {\n\t\trightPlain = \"queued\";\n\t\trightStyled = theme.fg(\"dim\", rightPlain);\n\t}\n\n\t// A warning note (e.g. inherited-model fallback, exhaustion skip) takes over the\n\t// right column as a ⚠ cue, replacing the usage/status stamp for that row.\n\tif (task.note) {\n\t\trightPlain = `⚠ ${task.note}`;\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t}\n\n\tconst rightWidth = rightPlain ? visibleWidth(rightPlain) + 1 : 0;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\n\t// truncateToWidth measures visible width (ANSI-aware), so the styled left can be\n\t// truncated against the full left budget directly. Subtracting the prefix here\n\t// (as a prior version did) truncated titles early and unevenly per id width.\n\tconst left = truncateToWidth(`${icon} ${styledId} ${styledTitle}`, leftWidth, \"…\");\n\n\tif (!rightPlain) return left;\n\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Task panel rendered just above the editor prompt.\n *\n * - A state-colored left rail groups the pane (working=warning, reviewed=success,\n * stopped=error) without drawing a box.\n * - A ledger header tops the list: a state stamp + deterministic progress bar +\n * done/total count on the left, the per-turn token/elapsed/cost delta on the right.\n * - Shows all tasks with all statuses (pending / in_progress / done / failed).\n * The active row animates a braille spinner; pending rows read `queued`.\n * - Subagent mode is intentionally NOT shown here (e.g. no \"[explore]\" tag).\n * - LIFO within the window: newest tasks appear at the bottom (closest to the prompt).\n * - Finished tasks carry their wall-clock cost and stay visible until the next\n * user message arrives (see taskStore.reset()), not the moment they finish.\n * - Collapses to zero lines when there are no tasks.\n */\nexport class TaskPanelComponent implements Component {\n\tprivate readonly ui: TUI | null;\n\tprivate frame = 0;\n\tprivate animationTimer: ReturnType<typeof setInterval> | null = null;\n\n\tconstructor(ui?: TUI) {\n\t\tthis.ui = ui ?? null;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached rendering state.\n\t}\n\n\t/** Run the spinner timer only while a task is active, ticking re-renders. */\n\tprivate ensureAnimation(active: boolean): void {\n\t\tif (active && this.ui && !this.animationTimer) {\n\t\t\tthis.animationTimer = setInterval(() => {\n\t\t\t\tthis.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n\t\t\t\tthis.ui?.requestRender();\n\t\t\t}, SPINNER_INTERVAL_MS);\n\t\t\tthis.animationTimer.unref?.();\n\t\t} else if (!active && this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t\tthis.frame = 0;\n\t\t}\n\t}\n\n\t/** Stop the spinner timer. Call on teardown. */\n\tdispose(): void {\n\t\tif (this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tconst tasks = taskStore.list();\n\t\tif (tasks.length === 0) {\n\t\t\tthis.ensureAnimation(false);\n\t\t\treturn [];\n\t\t}\n\n\t\tconst hasActive = tasks.some((t) => t.status === \"in_progress\");\n\t\tthis.ensureAnimation(hasActive);\n\n\t\tconst state = panelState(tasks);\n\t\tconst totalSecs = tasks.reduce((sum, t) => sum + taskElapsedSecs(t), 0);\n\t\tconst railColor = STATE_PRESENTATION[state].color;\n\t\tconst gutter = `${theme.fg(railColor, RAIL)} `;\n\t\tconst inner = Math.max(0, width - visibleWidth(RAIL) - 1);\n\n\t\tconst lines: string[] = [gutter + formatHeader(tasks, inner, state, totalSecs)];\n\t\tfor (const task of tasks) {\n\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame));\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
|
|
@@ -206,11 +206,18 @@ function formatTaskLine(task, width, frame) {
|
|
|
206
206
|
rightPlain = "queued";
|
|
207
207
|
rightStyled = theme.fg("dim", rightPlain);
|
|
208
208
|
}
|
|
209
|
+
// A warning note (e.g. inherited-model fallback, exhaustion skip) takes over the
|
|
210
|
+
// right column as a ⚠ cue, replacing the usage/status stamp for that row.
|
|
211
|
+
if (task.note) {
|
|
212
|
+
rightPlain = `⚠ ${task.note}`;
|
|
213
|
+
rightStyled = theme.fg("warning", rightPlain);
|
|
214
|
+
}
|
|
209
215
|
const rightWidth = rightPlain ? visibleWidth(rightPlain) + 1 : 0;
|
|
210
216
|
const leftWidth = Math.max(0, width - rightWidth);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
217
|
+
// truncateToWidth measures visible width (ANSI-aware), so the styled left can be
|
|
218
|
+
// truncated against the full left budget directly. Subtracting the prefix here
|
|
219
|
+
// (as a prior version did) truncated titles early and unevenly per id width.
|
|
220
|
+
const left = truncateToWidth(`${icon} ${styledId} ${styledTitle}`, leftWidth, "…");
|
|
214
221
|
if (!rightPlain)
|
|
215
222
|
return left;
|
|
216
223
|
const pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-panel.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/task-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Component, TUI } from \"@kolisachint/hoocode-tui\";\nimport { truncateToWidth, visibleWidth } from \"@kolisachint/hoocode-tui\";\nimport type { Task, TaskStatus } from \"../../../core/task-store.js\";\nimport { taskStore } from \"../../../core/task-store.js\";\nimport { theme } from \"../theme/theme.js\";\n\nconst TASK_STATUS_ICON: Record<TaskStatus, string> = {\n\tpending: \"●\",\n\tin_progress: \"◐\",\n\tdone: \"✓\",\n\tfailed: \"✗\",\n};\n\n/** Braille spinner frames + cadence, matched to the TUI Loader so the active row animates in step. */\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\n/** A thin colored left rail groups the pane without a box, the way the design's `border-left` does. */\nconst RAIL = \"▎\";\n\n/** Cells in the deterministic progress bar (matches the design's 14-cell track). */\nconst PROGRESS_CELLS = 14;\n\n/** Overall pane state, derived from the task statuses. Drives the rail color + header stamp. */\ntype PanelState = \"working\" | \"reviewed\" | \"stopped\";\n\ninterface StatePresentation {\n\treadonly icon: string;\n\treadonly label: string;\n\treadonly color: \"warning\" | \"success\" | \"error\";\n}\n\nconst STATE_PRESENTATION: Record<PanelState, StatePresentation> = {\n\tworking: { icon: \"◐\", label: \"working\", color: \"warning\" },\n\treviewed: { icon: \"✓\", label: \"reviewed\", color: \"success\" },\n\tstopped: { icon: \"✗\", label: \"stopped\", color: \"error\" },\n};\n\nfunction panelState(tasks: readonly Task[]): PanelState {\n\tif (tasks.some((t) => t.status === \"failed\")) return \"stopped\";\n\tconst active = tasks.some((t) => t.status === \"in_progress\" || t.status === \"pending\");\n\treturn active ? \"working\" : \"reviewed\";\n}\n\nfunction taskStatusColor(status: TaskStatus): \"dim\" | \"warning\" | \"success\" | \"error\" {\n\tswitch (status) {\n\t\tcase \"in_progress\":\n\t\t\treturn \"warning\";\n\t\tcase \"done\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tdefault:\n\t\t\treturn \"dim\";\n\t}\n}\n\n/** Format a duration in seconds into a compact, terminal-friendly string. */\nfunction formatDuration(secs: number): string {\n\tconst s = Math.max(0, secs);\n\tif (s < 10) return `${s.toFixed(1)}s`;\n\tif (s < 60) return `${Math.round(s)}s`;\n\tconst mins = Math.floor(s / 60);\n\tconst rem = Math.round(s % 60);\n\treturn `${mins}m${rem.toString().padStart(2, \"0\")}s`;\n}\n\n/** Wall-clock time a task occupied, derived from its create/update stamps. */\nfunction taskElapsedSecs(task: Task): number {\n\treturn Math.max(0, (task.updatedAt - task.createdAt) / 1000);\n}\n\n/** Sum the token + cost usage reported by the tasks shown this turn. */\nfunction sumTurnUsage(tasks: readonly Task[]): { input: number; output: number; cost: number } | null {\n\tlet input = 0;\n\tlet output = 0;\n\tlet cost = 0;\n\tfor (const task of tasks) {\n\t\tif (!task.usage) continue;\n\t\tinput += task.usage.input;\n\t\toutput += task.usage.output;\n\t\tcost += task.usage.cost;\n\t}\n\tif (input === 0 && output === 0 && cost === 0) return null;\n\treturn { input, output, cost };\n}\n\n/**\n * Deterministic block-glyph progress bar: a heavy run (━) for the completed\n * fraction over a dim track. In-progress tasks count as half, so the bar moves\n * the moment work starts. Fraction is the only input — no animation, no guess.\n */\nfunction progressBar(done: number, active: number, total: number): { plain: string; styled: string } {\n\tconst ratio = total > 0 ? Math.max(0, Math.min(1, (done + active * 0.5) / total)) : 0;\n\tconst filled = Math.round(ratio * PROGRESS_CELLS);\n\tconst fill = \"━\".repeat(filled);\n\tconst track = \"━\".repeat(PROGRESS_CELLS - filled);\n\treturn {\n\t\tplain: fill + track,\n\t\tstyled: theme.fg(\"success\", fill) + theme.fg(\"dim\", track),\n\t};\n}\n\n/**\n * Ledger header: a state stamp (◐ working / ✓ reviewed / ✗ stopped) + a\n * deterministic progress bar and done/total count on the left, and the per-turn\n * token + elapsed + cost delta (summed across the tasks below) on the right.\n */\nfunction formatHeader(tasks: readonly Task[], width: number, state: PanelState, totalSecs: number): string {\n\tconst total = tasks.length;\n\tconst done = tasks.filter((t) => t.status === \"done\").length;\n\tconst active = tasks.filter((t) => t.status === \"in_progress\").length;\n\n\tconst { icon, label, color } = STATE_PRESENTATION[state];\n\tconst stampPlain = `${icon} ${label.toUpperCase()}`;\n\tconst stamp = `${theme.fg(color, icon)} ${theme.bold(theme.fg(color, label.toUpperCase()))}`;\n\n\tconst bar = progressBar(done, active, total);\n\tconst countPlain = `${done}/${total}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${total}`);\n\n\t// Left cluster has a full form (stamp · bar · count) and a compact fallback\n\t// (stamp · count) that drops the bar when the terminal is too narrow.\n\tconst leftFullPlain = `${stampPlain} ${bar.plain} ${countPlain}`;\n\tconst leftFull = `${stamp} ${bar.styled} ${count}`;\n\tconst leftMinPlain = `${stampPlain} ${countPlain}`;\n\tconst leftMin = `${stamp} ${count}`;\n\n\tconst turn = sumTurnUsage(tasks);\n\tlet turnPlain = \"\";\n\tlet turnText = \"\";\n\tif (turn) {\n\t\tconst inTok = formatTokens(turn.input);\n\t\tconst outTok = formatTokens(turn.output);\n\t\tconst elapsed = formatDuration(totalSecs);\n\t\tconst showCost = turn.cost > 0;\n\t\tconst costStr = showCost ? `$${turn.cost.toFixed(3)}` : \"\";\n\t\tturnPlain = `turn ↑${inTok} ↓${outTok} · ${elapsed}${showCost ? ` · ${costStr}` : \"\"}`;\n\t\t// Turn delta: muted framing, numbers one step brighter (bold), separators dim.\n\t\tturnText =\n\t\t\ttheme.fg(\"muted\", \"turn ↑\") +\n\t\t\ttheme.bold(inTok) +\n\t\t\ttheme.fg(\"muted\", \" ↓\") +\n\t\t\ttheme.bold(outTok) +\n\t\t\ttheme.fg(\"dim\", \" · \") +\n\t\t\ttheme.fg(\"muted\", elapsed) +\n\t\t\t(showCost ? theme.fg(\"dim\", \" · \") + theme.bold(costStr) : \"\");\n\t}\n\n\tif (turnPlain) {\n\t\tif (visibleWidth(leftFullPlain) + 2 + visibleWidth(turnPlain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftFullPlain) - visibleWidth(turnPlain));\n\t\t\treturn leftFull + \" \".repeat(pad) + turnText;\n\t\t}\n\t\tif (visibleWidth(leftMinPlain) + 2 + visibleWidth(turnPlain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftMinPlain) - visibleWidth(turnPlain));\n\t\t\treturn leftMin + \" \".repeat(pad) + turnText;\n\t\t}\n\t}\n\tif (visibleWidth(leftFullPlain) <= width) return leftFull;\n\treturn truncateToWidth(leftMin, width, \"…\");\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\treturn `${(count / 1000000).toFixed(1)}M`;\n}\n\nfunction formatTaskLine(task: Task, width: number, frame: number): string {\n\tconst isProgress = task.status === \"in_progress\";\n\tconst iconGlyph = isProgress\n\t\t? (SPINNER_FRAMES[frame] ?? TASK_STATUS_ICON.in_progress)\n\t\t: TASK_STATUS_ICON[task.status];\n\tconst icon = theme.fg(taskStatusColor(task.status), iconGlyph);\n\n\tconst idLabel = `#${task.id}`;\n\tconst title = task.title;\n\t// The id recedes (dim); the title carries the line. Done titles fade to muted\n\t// (settled work), pending dim (not started), active goes bold, failed turns red.\n\tconst styledId = theme.fg(\"dim\", idLabel);\n\tlet styledTitle: string;\n\tswitch (task.status) {\n\t\tcase \"done\":\n\t\t\tstyledTitle = theme.fg(\"muted\", title);\n\t\t\tbreak;\n\t\tcase \"pending\":\n\t\t\tstyledTitle = theme.fg(\"dim\", title);\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tstyledTitle = theme.fg(\"error\", title);\n\t\t\tbreak;\n\t\tcase \"in_progress\":\n\t\t\tstyledTitle = theme.bold(title);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tstyledTitle = title;\n\t}\n\n\t// Right column: settled rows carry their audit stamp (tokens + elapsed); the\n\t// active row reads `running…`, pending rows read `queued`.\n\tlet rightPlain = \"\";\n\tlet rightStyled = \"\";\n\tif (task.status === \"done\" || task.status === \"failed\") {\n\t\tconst parts: string[] = [];\n\t\tlet tokenText = \"\";\n\t\tif (task.usage) {\n\t\t\tconst totalTok = task.usage.input + task.usage.output;\n\t\t\tif (totalTok > 0) tokenText = formatTokens(totalTok);\n\t\t}\n\t\tconst elapsed = formatDuration(taskElapsedSecs(task));\n\t\tif (tokenText) {\n\t\t\tparts.push(tokenText, elapsed);\n\t\t\trightStyled = theme.fg(\"muted\", tokenText) + theme.fg(\"dim\", ` · ${elapsed}`);\n\t\t} else {\n\t\t\tparts.push(elapsed);\n\t\t\trightStyled = theme.fg(\"dim\", elapsed);\n\t\t}\n\t\trightPlain = parts.join(\" · \");\n\t} else if (task.status === \"in_progress\") {\n\t\trightPlain = \"running…\";\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t} else if (task.status === \"pending\") {\n\t\trightPlain = \"queued\";\n\t\trightStyled = theme.fg(\"dim\", rightPlain);\n\t}\n\n\tconst rightWidth = rightPlain ? visibleWidth(rightPlain) + 1 : 0;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\n\tconst plainText = `${iconGlyph} ${idLabel} ${title}`;\n\tconst available = Math.max(0, leftWidth - visibleWidth(plainText) + visibleWidth(title));\n\tconst left = truncateToWidth(`${icon} ${styledId} ${styledTitle}`, available, \"…\");\n\n\tif (!rightPlain) return left;\n\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Task panel rendered just above the editor prompt.\n *\n * - A state-colored left rail groups the pane (working=warning, reviewed=success,\n * stopped=error) without drawing a box.\n * - A ledger header tops the list: a state stamp + deterministic progress bar +\n * done/total count on the left, the per-turn token/elapsed/cost delta on the right.\n * - Shows all tasks with all statuses (pending / in_progress / done / failed).\n * The active row animates a braille spinner; pending rows read `queued`.\n * - Subagent mode is intentionally NOT shown here (e.g. no \"[explore]\" tag).\n * - LIFO within the window: newest tasks appear at the bottom (closest to the prompt).\n * - Finished tasks carry their wall-clock cost and stay visible until the next\n * user message arrives (see taskStore.reset()), not the moment they finish.\n * - Collapses to zero lines when there are no tasks.\n */\nexport class TaskPanelComponent implements Component {\n\tprivate readonly ui: TUI | null;\n\tprivate frame = 0;\n\tprivate animationTimer: ReturnType<typeof setInterval> | null = null;\n\n\tconstructor(ui?: TUI) {\n\t\tthis.ui = ui ?? null;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached rendering state.\n\t}\n\n\t/** Run the spinner timer only while a task is active, ticking re-renders. */\n\tprivate ensureAnimation(active: boolean): void {\n\t\tif (active && this.ui && !this.animationTimer) {\n\t\t\tthis.animationTimer = setInterval(() => {\n\t\t\t\tthis.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n\t\t\t\tthis.ui?.requestRender();\n\t\t\t}, SPINNER_INTERVAL_MS);\n\t\t\tthis.animationTimer.unref?.();\n\t\t} else if (!active && this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t\tthis.frame = 0;\n\t\t}\n\t}\n\n\t/** Stop the spinner timer. Call on teardown. */\n\tdispose(): void {\n\t\tif (this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tconst tasks = taskStore.list();\n\t\tif (tasks.length === 0) {\n\t\t\tthis.ensureAnimation(false);\n\t\t\treturn [];\n\t\t}\n\n\t\tconst hasActive = tasks.some((t) => t.status === \"in_progress\");\n\t\tthis.ensureAnimation(hasActive);\n\n\t\tconst state = panelState(tasks);\n\t\tconst totalSecs = tasks.reduce((sum, t) => sum + taskElapsedSecs(t), 0);\n\t\tconst railColor = STATE_PRESENTATION[state].color;\n\t\tconst gutter = `${theme.fg(railColor, RAIL)} `;\n\t\tconst inner = Math.max(0, width - visibleWidth(RAIL) - 1);\n\n\t\tconst lines: string[] = [gutter + formatHeader(tasks, inner, state, totalSecs)];\n\t\tfor (const task of tasks) {\n\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame));\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"task-panel.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/task-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Component, TUI } from \"@kolisachint/hoocode-tui\";\nimport { truncateToWidth, visibleWidth } from \"@kolisachint/hoocode-tui\";\nimport type { Task, TaskStatus } from \"../../../core/task-store.js\";\nimport { taskStore } from \"../../../core/task-store.js\";\nimport { theme } from \"../theme/theme.js\";\n\nconst TASK_STATUS_ICON: Record<TaskStatus, string> = {\n\tpending: \"●\",\n\tin_progress: \"◐\",\n\tdone: \"✓\",\n\tfailed: \"✗\",\n};\n\n/** Braille spinner frames + cadence, matched to the TUI Loader so the active row animates in step. */\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\n/** A thin colored left rail groups the pane without a box, the way the design's `border-left` does. */\nconst RAIL = \"▎\";\n\n/** Cells in the deterministic progress bar (matches the design's 14-cell track). */\nconst PROGRESS_CELLS = 14;\n\n/** Overall pane state, derived from the task statuses. Drives the rail color + header stamp. */\ntype PanelState = \"working\" | \"reviewed\" | \"stopped\";\n\ninterface StatePresentation {\n\treadonly icon: string;\n\treadonly label: string;\n\treadonly color: \"warning\" | \"success\" | \"error\";\n}\n\nconst STATE_PRESENTATION: Record<PanelState, StatePresentation> = {\n\tworking: { icon: \"◐\", label: \"working\", color: \"warning\" },\n\treviewed: { icon: \"✓\", label: \"reviewed\", color: \"success\" },\n\tstopped: { icon: \"✗\", label: \"stopped\", color: \"error\" },\n};\n\nfunction panelState(tasks: readonly Task[]): PanelState {\n\tif (tasks.some((t) => t.status === \"failed\")) return \"stopped\";\n\tconst active = tasks.some((t) => t.status === \"in_progress\" || t.status === \"pending\");\n\treturn active ? \"working\" : \"reviewed\";\n}\n\nfunction taskStatusColor(status: TaskStatus): \"dim\" | \"warning\" | \"success\" | \"error\" {\n\tswitch (status) {\n\t\tcase \"in_progress\":\n\t\t\treturn \"warning\";\n\t\tcase \"done\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tdefault:\n\t\t\treturn \"dim\";\n\t}\n}\n\n/** Format a duration in seconds into a compact, terminal-friendly string. */\nfunction formatDuration(secs: number): string {\n\tconst s = Math.max(0, secs);\n\tif (s < 10) return `${s.toFixed(1)}s`;\n\tif (s < 60) return `${Math.round(s)}s`;\n\tconst mins = Math.floor(s / 60);\n\tconst rem = Math.round(s % 60);\n\treturn `${mins}m${rem.toString().padStart(2, \"0\")}s`;\n}\n\n/** Wall-clock time a task occupied, derived from its create/update stamps. */\nfunction taskElapsedSecs(task: Task): number {\n\treturn Math.max(0, (task.updatedAt - task.createdAt) / 1000);\n}\n\n/** Sum the token + cost usage reported by the tasks shown this turn. */\nfunction sumTurnUsage(tasks: readonly Task[]): { input: number; output: number; cost: number } | null {\n\tlet input = 0;\n\tlet output = 0;\n\tlet cost = 0;\n\tfor (const task of tasks) {\n\t\tif (!task.usage) continue;\n\t\tinput += task.usage.input;\n\t\toutput += task.usage.output;\n\t\tcost += task.usage.cost;\n\t}\n\tif (input === 0 && output === 0 && cost === 0) return null;\n\treturn { input, output, cost };\n}\n\n/**\n * Deterministic block-glyph progress bar: a heavy run (━) for the completed\n * fraction over a dim track. In-progress tasks count as half, so the bar moves\n * the moment work starts. Fraction is the only input — no animation, no guess.\n */\nfunction progressBar(done: number, active: number, total: number): { plain: string; styled: string } {\n\tconst ratio = total > 0 ? Math.max(0, Math.min(1, (done + active * 0.5) / total)) : 0;\n\tconst filled = Math.round(ratio * PROGRESS_CELLS);\n\tconst fill = \"━\".repeat(filled);\n\tconst track = \"━\".repeat(PROGRESS_CELLS - filled);\n\treturn {\n\t\tplain: fill + track,\n\t\tstyled: theme.fg(\"success\", fill) + theme.fg(\"dim\", track),\n\t};\n}\n\n/**\n * Ledger header: a state stamp (◐ working / ✓ reviewed / ✗ stopped) + a\n * deterministic progress bar and done/total count on the left, and the per-turn\n * token + elapsed + cost delta (summed across the tasks below) on the right.\n */\nfunction formatHeader(tasks: readonly Task[], width: number, state: PanelState, totalSecs: number): string {\n\tconst total = tasks.length;\n\tconst done = tasks.filter((t) => t.status === \"done\").length;\n\tconst active = tasks.filter((t) => t.status === \"in_progress\").length;\n\n\tconst { icon, label, color } = STATE_PRESENTATION[state];\n\tconst stampPlain = `${icon} ${label.toUpperCase()}`;\n\tconst stamp = `${theme.fg(color, icon)} ${theme.bold(theme.fg(color, label.toUpperCase()))}`;\n\n\tconst bar = progressBar(done, active, total);\n\tconst countPlain = `${done}/${total}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${total}`);\n\n\t// Left cluster has a full form (stamp · bar · count) and a compact fallback\n\t// (stamp · count) that drops the bar when the terminal is too narrow.\n\tconst leftFullPlain = `${stampPlain} ${bar.plain} ${countPlain}`;\n\tconst leftFull = `${stamp} ${bar.styled} ${count}`;\n\tconst leftMinPlain = `${stampPlain} ${countPlain}`;\n\tconst leftMin = `${stamp} ${count}`;\n\n\tconst turn = sumTurnUsage(tasks);\n\tlet turnPlain = \"\";\n\tlet turnText = \"\";\n\tif (turn) {\n\t\tconst inTok = formatTokens(turn.input);\n\t\tconst outTok = formatTokens(turn.output);\n\t\tconst elapsed = formatDuration(totalSecs);\n\t\tconst showCost = turn.cost > 0;\n\t\tconst costStr = showCost ? `$${turn.cost.toFixed(3)}` : \"\";\n\t\tturnPlain = `turn ↑${inTok} ↓${outTok} · ${elapsed}${showCost ? ` · ${costStr}` : \"\"}`;\n\t\t// Turn delta: muted framing, numbers one step brighter (bold), separators dim.\n\t\tturnText =\n\t\t\ttheme.fg(\"muted\", \"turn ↑\") +\n\t\t\ttheme.bold(inTok) +\n\t\t\ttheme.fg(\"muted\", \" ↓\") +\n\t\t\ttheme.bold(outTok) +\n\t\t\ttheme.fg(\"dim\", \" · \") +\n\t\t\ttheme.fg(\"muted\", elapsed) +\n\t\t\t(showCost ? theme.fg(\"dim\", \" · \") + theme.bold(costStr) : \"\");\n\t}\n\n\tif (turnPlain) {\n\t\tif (visibleWidth(leftFullPlain) + 2 + visibleWidth(turnPlain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftFullPlain) - visibleWidth(turnPlain));\n\t\t\treturn leftFull + \" \".repeat(pad) + turnText;\n\t\t}\n\t\tif (visibleWidth(leftMinPlain) + 2 + visibleWidth(turnPlain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftMinPlain) - visibleWidth(turnPlain));\n\t\t\treturn leftMin + \" \".repeat(pad) + turnText;\n\t\t}\n\t}\n\tif (visibleWidth(leftFullPlain) <= width) return leftFull;\n\treturn truncateToWidth(leftMin, width, \"…\");\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\treturn `${(count / 1000000).toFixed(1)}M`;\n}\n\nfunction formatTaskLine(task: Task, width: number, frame: number): string {\n\tconst isProgress = task.status === \"in_progress\";\n\tconst iconGlyph = isProgress\n\t\t? (SPINNER_FRAMES[frame] ?? TASK_STATUS_ICON.in_progress)\n\t\t: TASK_STATUS_ICON[task.status];\n\tconst icon = theme.fg(taskStatusColor(task.status), iconGlyph);\n\n\tconst idLabel = `#${task.id}`;\n\tconst title = task.title;\n\t// The id recedes (dim); the title carries the line. Done titles fade to muted\n\t// (settled work), pending dim (not started), active goes bold, failed turns red.\n\tconst styledId = theme.fg(\"dim\", idLabel);\n\tlet styledTitle: string;\n\tswitch (task.status) {\n\t\tcase \"done\":\n\t\t\tstyledTitle = theme.fg(\"muted\", title);\n\t\t\tbreak;\n\t\tcase \"pending\":\n\t\t\tstyledTitle = theme.fg(\"dim\", title);\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tstyledTitle = theme.fg(\"error\", title);\n\t\t\tbreak;\n\t\tcase \"in_progress\":\n\t\t\tstyledTitle = theme.bold(title);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tstyledTitle = title;\n\t}\n\n\t// Right column: settled rows carry their audit stamp (tokens + elapsed); the\n\t// active row reads `running…`, pending rows read `queued`.\n\tlet rightPlain = \"\";\n\tlet rightStyled = \"\";\n\tif (task.status === \"done\" || task.status === \"failed\") {\n\t\tconst parts: string[] = [];\n\t\tlet tokenText = \"\";\n\t\tif (task.usage) {\n\t\t\tconst totalTok = task.usage.input + task.usage.output;\n\t\t\tif (totalTok > 0) tokenText = formatTokens(totalTok);\n\t\t}\n\t\tconst elapsed = formatDuration(taskElapsedSecs(task));\n\t\tif (tokenText) {\n\t\t\tparts.push(tokenText, elapsed);\n\t\t\trightStyled = theme.fg(\"muted\", tokenText) + theme.fg(\"dim\", ` · ${elapsed}`);\n\t\t} else {\n\t\t\tparts.push(elapsed);\n\t\t\trightStyled = theme.fg(\"dim\", elapsed);\n\t\t}\n\t\trightPlain = parts.join(\" · \");\n\t} else if (task.status === \"in_progress\") {\n\t\trightPlain = \"running…\";\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t} else if (task.status === \"pending\") {\n\t\trightPlain = \"queued\";\n\t\trightStyled = theme.fg(\"dim\", rightPlain);\n\t}\n\n\t// A warning note (e.g. inherited-model fallback, exhaustion skip) takes over the\n\t// right column as a ⚠ cue, replacing the usage/status stamp for that row.\n\tif (task.note) {\n\t\trightPlain = `⚠ ${task.note}`;\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t}\n\n\tconst rightWidth = rightPlain ? visibleWidth(rightPlain) + 1 : 0;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\n\t// truncateToWidth measures visible width (ANSI-aware), so the styled left can be\n\t// truncated against the full left budget directly. Subtracting the prefix here\n\t// (as a prior version did) truncated titles early and unevenly per id width.\n\tconst left = truncateToWidth(`${icon} ${styledId} ${styledTitle}`, leftWidth, \"…\");\n\n\tif (!rightPlain) return left;\n\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Task panel rendered just above the editor prompt.\n *\n * - A state-colored left rail groups the pane (working=warning, reviewed=success,\n * stopped=error) without drawing a box.\n * - A ledger header tops the list: a state stamp + deterministic progress bar +\n * done/total count on the left, the per-turn token/elapsed/cost delta on the right.\n * - Shows all tasks with all statuses (pending / in_progress / done / failed).\n * The active row animates a braille spinner; pending rows read `queued`.\n * - Subagent mode is intentionally NOT shown here (e.g. no \"[explore]\" tag).\n * - LIFO within the window: newest tasks appear at the bottom (closest to the prompt).\n * - Finished tasks carry their wall-clock cost and stay visible until the next\n * user message arrives (see taskStore.reset()), not the moment they finish.\n * - Collapses to zero lines when there are no tasks.\n */\nexport class TaskPanelComponent implements Component {\n\tprivate readonly ui: TUI | null;\n\tprivate frame = 0;\n\tprivate animationTimer: ReturnType<typeof setInterval> | null = null;\n\n\tconstructor(ui?: TUI) {\n\t\tthis.ui = ui ?? null;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached rendering state.\n\t}\n\n\t/** Run the spinner timer only while a task is active, ticking re-renders. */\n\tprivate ensureAnimation(active: boolean): void {\n\t\tif (active && this.ui && !this.animationTimer) {\n\t\t\tthis.animationTimer = setInterval(() => {\n\t\t\t\tthis.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n\t\t\t\tthis.ui?.requestRender();\n\t\t\t}, SPINNER_INTERVAL_MS);\n\t\t\tthis.animationTimer.unref?.();\n\t\t} else if (!active && this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t\tthis.frame = 0;\n\t\t}\n\t}\n\n\t/** Stop the spinner timer. Call on teardown. */\n\tdispose(): void {\n\t\tif (this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tconst tasks = taskStore.list();\n\t\tif (tasks.length === 0) {\n\t\t\tthis.ensureAnimation(false);\n\t\t\treturn [];\n\t\t}\n\n\t\tconst hasActive = tasks.some((t) => t.status === \"in_progress\");\n\t\tthis.ensureAnimation(hasActive);\n\n\t\tconst state = panelState(tasks);\n\t\tconst totalSecs = tasks.reduce((sum, t) => sum + taskElapsedSecs(t), 0);\n\t\tconst railColor = STATE_PRESENTATION[state].color;\n\t\tconst gutter = `${theme.fg(railColor, RAIL)} `;\n\t\tconst inner = Math.max(0, width - visibleWidth(RAIL) - 1);\n\n\t\tconst lines: string[] = [gutter + formatHeader(tasks, inner, state, totalSecs)];\n\t\tfor (const task of tasks) {\n\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame));\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolisachint/hoocode-agent",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.34",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"hoocodeConfig": {
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
"prepublishOnly": "npm run clean && npm run build"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@kolisachint/hoocode-agent-core": "^0.4.
|
|
48
|
-
"@kolisachint/hoocode-ai": "^0.4.
|
|
49
|
-
"@kolisachint/hoocode-tui": "^0.4.
|
|
47
|
+
"@kolisachint/hoocode-agent-core": "^0.4.34",
|
|
48
|
+
"@kolisachint/hoocode-ai": "^0.4.34",
|
|
49
|
+
"@kolisachint/hoocode-tui": "^0.4.34",
|
|
50
50
|
"@silvia-odwyer/photon-node": "^0.3.4",
|
|
51
51
|
"chalk": "^5.5.0",
|
|
52
52
|
"cli-highlight": "^2.1.11",
|
package/templates/agents/doc.md
CHANGED
|
@@ -17,6 +17,7 @@ description: |
|
|
|
17
17
|
Isolation: Can run in parallel with explore and review tasks
|
|
18
18
|
tools: read, write, edit, grep, find, ls
|
|
19
19
|
model: sonnet
|
|
20
|
+
background: true
|
|
20
21
|
---
|
|
21
22
|
You are a documentation subagent running inside hoocode. You write and update documentation. You run in an isolated context and cannot see the parent conversation.
|
|
22
23
|
|
|
@@ -18,6 +18,7 @@ description: |
|
|
|
18
18
|
Isolation: Can run in parallel with other explore tasks
|
|
19
19
|
tools: read, grep, find, ls, bash
|
|
20
20
|
model: haiku
|
|
21
|
+
background: true
|
|
21
22
|
---
|
|
22
23
|
You are an explore-only agent running inside hoocode. You read code and produce summaries. You NEVER edit files. You run in an isolated context and cannot see the parent conversation.
|
|
23
24
|
|
|
@@ -17,6 +17,7 @@ description: |
|
|
|
17
17
|
Isolation: Can run in parallel with explore and doc tasks
|
|
18
18
|
tools: read, grep, find, ls
|
|
19
19
|
model: haiku
|
|
20
|
+
background: true
|
|
20
21
|
---
|
|
21
22
|
You are a review subagent running inside hoocode. You review code and report issues. You run in an isolated context and cannot see the parent conversation.
|
|
22
23
|
|
package/templates/agents/test.md
CHANGED
|
@@ -17,6 +17,7 @@ description: |
|
|
|
17
17
|
Isolation: Can run in parallel with explore tasks; should not run during active edits
|
|
18
18
|
tools: read, bash, grep, find, ls
|
|
19
19
|
model: sonnet
|
|
20
|
+
background: true
|
|
20
21
|
---
|
|
21
22
|
You are a test subagent running inside hoocode. You run tests and report results. You run in an isolated context and cannot see the parent conversation.
|
|
22
23
|
|