oh-my-opencode-slim 2.0.0-beta.10 → 2.0.0-beta.12
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/dist/config/constants.d.ts +3 -0
- package/dist/index.js +199 -62
- package/dist/tui.js +15 -0
- package/dist/utils/background-job-board.d.ts +1 -0
- package/package.json +1 -1
- package/src/skills/deepwork/SKILL.md +22 -0
|
@@ -21,6 +21,9 @@ export declare const MAX_POLL_TIME_MS: number;
|
|
|
21
21
|
export declare const FALLBACK_FAILOVER_TIMEOUT_MS = 15000;
|
|
22
22
|
export declare const DEFAULT_MAX_SUBAGENT_DEPTH = 3;
|
|
23
23
|
export declare const PHASE_REMINDER_TEXT = "!IMPORTANT! Scheduler workflow: plan lanes/dependencies \u2192 dispatch background specialists \u2192 track task IDs \u2192 wait for hook-driven completion or use task_status only when needed \u2192 reconcile terminal results \u2192 verify. Do not consume running-job output or advance dependent work. !END!";
|
|
24
|
+
export declare const WRITABLE_FILE_OPERATIONS_RULES = "**File Operations Rules**:\n- Prefer dedicated file tools for normal code work: glob/grep/ast_grep_search for discovery, read for file contents, and edit/write/apply_patch for targeted source changes.\n- Use bash for execution and automation: git, package managers, tests, builds, scripts, diagnostics, and shell-native filesystem operations.\n- Shell is acceptable for bulk or mechanical filesystem changes when it is clearer or safer than many individual edits (for example: truncate generated logs, remove build artifacts, batch rename/move files), especially when the user explicitly asks for that shell operation.\n- Before destructive or broad shell operations, verify the target set and quote paths. Prefer a dry-run/listing first when practical.\n- Do not use cat/head/tail/sed/awk only to read code into context; use read/grep unless a shell pipeline is genuinely the better diagnostic.";
|
|
25
|
+
export declare const READONLY_FILE_OPERATIONS_RULES = "**File Operations Rules**:\n- READ-ONLY: inspect and report; do not modify files.\n- Prefer dedicated file tools for codebase inspection: glob/grep/ast_grep_search for discovery and read for file contents.\n- Bash is allowed for non-mutating diagnostics and shell-native inspection when it is the clearest tool, but not for modifying files.\n- Do not use cat/head/tail/sed/awk only to read code into context; use read/grep unless a shell pipeline is genuinely the better diagnostic.";
|
|
26
|
+
export declare const NO_SHELL_READONLY_FILE_OPERATIONS_RULES = "**File Operations Rules**:\n- READ-ONLY: inspect and report; do not modify files.\n- Use glob/grep/ast_grep_search for discovery and read for file contents.\n- Do not use bash or shell commands.";
|
|
24
27
|
export declare const TMUX_SPAWN_DELAY_MS = 500;
|
|
25
28
|
export declare const COUNCILLOR_STAGGER_MS = 250;
|
|
26
29
|
export declare const STABLE_POLLS_THRESHOLD = 3;
|
package/dist/index.js
CHANGED
|
@@ -18309,6 +18309,21 @@ var DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
|
|
|
18309
18309
|
var MAX_POLL_TIME_MS = 5 * 60 * 1000;
|
|
18310
18310
|
var DEFAULT_MAX_SUBAGENT_DEPTH = 3;
|
|
18311
18311
|
var PHASE_REMINDER_TEXT = `!IMPORTANT! Scheduler workflow: plan lanes/dependencies → dispatch background specialists → track task IDs → wait for hook-driven completion or use task_status only when needed → reconcile terminal results → verify. Do not consume running-job output or advance dependent work. !END!`;
|
|
18312
|
+
var WRITABLE_FILE_OPERATIONS_RULES = `**File Operations Rules**:
|
|
18313
|
+
- Prefer dedicated file tools for normal code work: glob/grep/ast_grep_search for discovery, read for file contents, and edit/write/apply_patch for targeted source changes.
|
|
18314
|
+
- Use bash for execution and automation: git, package managers, tests, builds, scripts, diagnostics, and shell-native filesystem operations.
|
|
18315
|
+
- Shell is acceptable for bulk or mechanical filesystem changes when it is clearer or safer than many individual edits (for example: truncate generated logs, remove build artifacts, batch rename/move files), especially when the user explicitly asks for that shell operation.
|
|
18316
|
+
- Before destructive or broad shell operations, verify the target set and quote paths. Prefer a dry-run/listing first when practical.
|
|
18317
|
+
- Do not use cat/head/tail/sed/awk only to read code into context; use read/grep unless a shell pipeline is genuinely the better diagnostic.`;
|
|
18318
|
+
var READONLY_FILE_OPERATIONS_RULES = `**File Operations Rules**:
|
|
18319
|
+
- READ-ONLY: inspect and report; do not modify files.
|
|
18320
|
+
- Prefer dedicated file tools for codebase inspection: glob/grep/ast_grep_search for discovery and read for file contents.
|
|
18321
|
+
- Bash is allowed for non-mutating diagnostics and shell-native inspection when it is the clearest tool, but not for modifying files.
|
|
18322
|
+
- Do not use cat/head/tail/sed/awk only to read code into context; use read/grep unless a shell pipeline is genuinely the better diagnostic.`;
|
|
18323
|
+
var NO_SHELL_READONLY_FILE_OPERATIONS_RULES = `**File Operations Rules**:
|
|
18324
|
+
- READ-ONLY: inspect and report; do not modify files.
|
|
18325
|
+
- Use glob/grep/ast_grep_search for discovery and read for file contents.
|
|
18326
|
+
- Do not use bash or shell commands.`;
|
|
18312
18327
|
var TMUX_SPAWN_DELAY_MS = 500;
|
|
18313
18328
|
var COUNCILLOR_STAGGER_MS = 250;
|
|
18314
18329
|
var DEFAULT_DISABLED_AGENTS = ["observer"];
|
|
@@ -18990,8 +19005,9 @@ var AGENT_DESCRIPTIONS = {
|
|
|
18990
19005
|
- Lane: UI/UX design, related edits, design polish and review
|
|
18991
19006
|
- Permissions: read_files, write_files
|
|
18992
19007
|
- Stats: 10x better UI/UX than orchestrator
|
|
18993
|
-
- Capabilities:
|
|
18994
|
-
-
|
|
19008
|
+
- Capabilities: Good design taste, visual relevant edits, interactions, responsive layouts, design systems with aesthetic intent, deep UI/UX knowledge.
|
|
19009
|
+
- Owns visual and interaction quality: layout, hierarchy, spacing, motion, affordances, responsive behavior, and overall feel.
|
|
19010
|
+
- Weakness: copywriting. Ask designer to use grounded, normal wording, then have orchestrator review/fix copy after design work without changing visual or interaction intent.
|
|
18995
19011
|
- Avoid: "Let me us designer how it should look and implement yourself" → instead: "Let me ask designer to design and implement the UI/UX changes for me"
|
|
18996
19012
|
- **Delegate when:** User-facing interfaces needing polish • Responsive layouts • UX-critical components (forms, nav, dashboards) • Visual consistency systems • Animations/micro-interactions • Landing/marketing pages • Refining functional→delightful • Reviewing existing UI/UX quality
|
|
18997
19013
|
- **Don't delegate when:** Backend/logic with no visual • Quick prototypes where design doesn't matter yet.
|
|
@@ -19004,8 +19020,8 @@ var AGENT_DESCRIPTIONS = {
|
|
|
19004
19020
|
- Weakness: design, taste
|
|
19005
19021
|
- Tools/Constraints: Execution-focused—no research, no architectural decisions
|
|
19006
19022
|
- **Delegate when:** For implementation work, think and triage first. If the change is non-trivial or multi-file, hand bounded execution to @fixer • Parallelization benefits: Task involves multiple folders and multiple files modification, scoping work per folder and spawning parallel @fixers for each folder.
|
|
19007
|
-
- **Don't delegate when:** Needs discovery/research/decisions • Single small change (<20 lines, one file) • Unclear requirements needing iteration • Explaining to fixer > doing • Tight integration with your current work
|
|
19008
|
-
- **Rule of thumb:**
|
|
19023
|
+
- **Don't delegate when:** Needs discovery/research/decisions • Single small change (<20 lines, one file) • Unclear requirements needing iteration • Explaining to fixer > doing • Tight integration with your current work • Requires design taste, visual hierarchy, interaction polish, responsive layout decisions, animation/motion, component feel, or UI copy/design trade-offs
|
|
19024
|
+
- **Rule of thumb:** Headless/mechanical implementation → @fixer. User-visible design or polish → @designer. If @designer already set direction, @fixer may only do bounded mechanical follow-up that preserves that design exactly.`,
|
|
19009
19025
|
council: `@council
|
|
19010
19026
|
- Lane: High-stakes multi-model decision support
|
|
19011
19027
|
- Role: Multi-LLM consensus engine that runs several councillors, synthesizes their views, and returns a structured council report.
|
|
@@ -19092,12 +19108,7 @@ Review available agents and lane rules.
|
|
|
19092
19108
|
- Do not immediately wait after spawning independent background tasks unless the next step truly depends on their result
|
|
19093
19109
|
- Reconcile results, resolve conflicts, and gate dependent lanes
|
|
19094
19110
|
|
|
19095
|
-
|
|
19096
|
-
- Always use dedicated file tools for file I/O.
|
|
19097
|
-
- Search files/code with \`glob\`, \`grep\`, or \`ast_grep_search\`.
|
|
19098
|
-
- Read files with \`read\`. Never use \`cat\`, \`head\`, \`tail\`, \`sed\`, \`awk\`, or bash commands to read file contents.
|
|
19099
|
-
- Edit files with \`apply_patch\`. Never use shell redirection, \`echo\`, \`printf\`, or heredocs for file content unless no file tool can do the job.
|
|
19100
|
-
- Use \`bash\` only for execution: git, package managers, tests, builds, scripts, or diagnostics.
|
|
19111
|
+
${WRITABLE_FILE_OPERATIONS_RULES}
|
|
19101
19112
|
|
|
19102
19113
|
## 4. Plan and Parallelize
|
|
19103
19114
|
Build a short work graph before dispatching:
|
|
@@ -19119,12 +19130,15 @@ Balance: respect dependencies, avoid parallelizing what must be sequential, and
|
|
|
19119
19130
|
- Continue orchestration while tasks run only when useful: planning, scheduling independent lanes, preparing synthesis, or asking needed user questions.
|
|
19120
19131
|
- If no useful independent work remains, stop after a brief status response; do not call \`task_status\` just to wait. OpenCode will resume you when the background completion event arrives.
|
|
19121
19132
|
- Use \`task_status(wait: true, timeout_ms: ...)\` only when you actively need a result before a dependent step or final response and no completion event has arrived yet.
|
|
19133
|
+
- If \`task_status(wait: true)\` times out and reports the task still \`running\`, the delegated lane is still owned by that specialist. Do not treat the timeout as failure, cancellation, or permission to do the same work yourself.
|
|
19134
|
+
- For dependent work, either call \`task_status(wait: true)\` again with the same reasonable interval, or stop with a brief waiting status and let the completion event resume you.
|
|
19122
19135
|
- Parallel background tasks are allowed only when their write scopes do not conflict.
|
|
19123
19136
|
- Final response requires relevant tasks to be terminal and reconciled.
|
|
19124
19137
|
|
|
19125
19138
|
### Background Job Discipline
|
|
19126
19139
|
- Every background task owns its declared lane until terminal.
|
|
19127
19140
|
- Do not duplicate, undermine, or race a running lane.
|
|
19141
|
+
- A polling timeout is not terminal. The lane remains running until a terminal completion/error/cancel event is observed or the user explicitly cancels it.
|
|
19128
19142
|
- After dispatch, classify the next step:
|
|
19129
19143
|
1. independent: continue,
|
|
19130
19144
|
2. dependent: wait/poll,
|
|
@@ -19134,6 +19148,13 @@ Balance: respect dependencies, avoid parallelizing what must be sequential, and
|
|
|
19134
19148
|
- Cancellation is not rollback: if cancelling a writer, inspect and reconcile partial file changes before launching a replacement lane.
|
|
19135
19149
|
- Never finalize work that depends on unresolved background jobs.
|
|
19136
19150
|
|
|
19151
|
+
### Design Handoff Discipline
|
|
19152
|
+
- When @designer completes UI/UX work, treat layout, spacing, hierarchy, motion, color, affordances, and component feel as intentional design output.
|
|
19153
|
+
- Do not later simplify, normalize, or refactor it in ways that flatten the design.
|
|
19154
|
+
- The orchestrator should review and improve user-facing copy after designer work, because designer copy may be weak.
|
|
19155
|
+
- Copy edits must preserve the designer's visual structure and interaction intent.
|
|
19156
|
+
- If follow-up work is purely mechanical and preserves the design exactly, @fixer can handle it. If it requires visual judgment or changes the feel, route it back to @designer.
|
|
19157
|
+
|
|
19137
19158
|
### Session Reuse
|
|
19138
19159
|
- Smartly reuse an available specialist session - context reuse saves time and tokens
|
|
19139
19160
|
- When too much unrelated, and really needed, start a fresh session with the specialist
|
|
@@ -19247,11 +19268,7 @@ var COUNCIL_AGENT_PROMPT = `You are the Council agent — a multi-LLM orchestrat
|
|
|
19247
19268
|
- Be transparent about trade-offs when different approaches have valid pros/cons
|
|
19248
19269
|
- Don't just average responses — choose the best approach and improve upon it
|
|
19249
19270
|
|
|
19250
|
-
|
|
19251
|
-
- Use dedicated tools for file I/O if local files must be inspected
|
|
19252
|
-
- Search files/code with glob, grep, or ast_grep_search
|
|
19253
|
-
- Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
|
|
19254
|
-
- Use bash only for execution/diagnostics, never for file I/O
|
|
19271
|
+
${READONLY_FILE_OPERATIONS_RULES}
|
|
19255
19272
|
|
|
19256
19273
|
**Required Output Format**:
|
|
19257
19274
|
Always include these sections in your final response:
|
|
@@ -19364,11 +19381,7 @@ var COUNCILLOR_PROMPT = `You are a councillor in a multi-model council.
|
|
|
19364
19381
|
|
|
19365
19382
|
You CANNOT edit files, write files, run shell commands, or delegate to other agents. You are an advisor, not an implementer.
|
|
19366
19383
|
|
|
19367
|
-
|
|
19368
|
-
- READ-ONLY: do not modify files
|
|
19369
|
-
- Search files/code with glob, grep, or ast_grep_search
|
|
19370
|
-
- Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
|
|
19371
|
-
- Do not use bash or shell commands
|
|
19384
|
+
${NO_SHELL_READONLY_FILE_OPERATIONS_RULES}
|
|
19372
19385
|
|
|
19373
19386
|
**Behavior**:
|
|
19374
19387
|
- **Examine the codebase** before answering — your read access is what makes council valuable. Don't guess at code you can see.
|
|
@@ -19456,13 +19469,9 @@ var DESIGNER_PROMPT = `You are a Designer - a frontend UI/UX specialist who crea
|
|
|
19456
19469
|
- Respect existing design systems when present
|
|
19457
19470
|
- Leverage component libraries where available
|
|
19458
19471
|
- Prioritize visual excellence—code perfection comes second
|
|
19472
|
+
- Use grounded, normal, regular english - don't use jargon or overly technical language
|
|
19459
19473
|
|
|
19460
|
-
|
|
19461
|
-
- Always use dedicated file tools for file I/O
|
|
19462
|
-
- Search files/code with glob, grep, or ast_grep_search
|
|
19463
|
-
- Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
|
|
19464
|
-
- Edit/write files with write, edit, or apply_patch. Never use shell redirection, echo, printf, or heredocs for file content unless no file tool can do the job
|
|
19465
|
-
- Use bash only for execution: git, package managers, tests, builds, scripts, or diagnostics
|
|
19474
|
+
${WRITABLE_FILE_OPERATIONS_RULES}
|
|
19466
19475
|
|
|
19467
19476
|
## Review Responsibilities
|
|
19468
19477
|
- Review existing UI for usability, responsiveness, visual consistency, and polish when asked
|
|
@@ -19501,11 +19510,7 @@ var EXPLORER_PROMPT = `You are Explorer - a fast codebase navigation specialist.
|
|
|
19501
19510
|
- **Structural patterns** (function shapes, class structures): ast_grep_search
|
|
19502
19511
|
- **File discovery** (find by name/extension): glob
|
|
19503
19512
|
|
|
19504
|
-
|
|
19505
|
-
- READ-ONLY: Search and report, don't modify files
|
|
19506
|
-
- Search files/code with glob, grep, or ast_grep_search
|
|
19507
|
-
- Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
|
|
19508
|
-
- Use bash only for execution/diagnostics, never for file I/O
|
|
19513
|
+
${READONLY_FILE_OPERATIONS_RULES}
|
|
19509
19514
|
|
|
19510
19515
|
**Behavior**:
|
|
19511
19516
|
- Be fast and thorough
|
|
@@ -19561,12 +19566,7 @@ var FIXER_PROMPT = `You are Fixer - a fast, focused implementation specialist.
|
|
|
19561
19566
|
- Run relevant validation when requested or clearly applicable (otherwise note as skipped with reason)
|
|
19562
19567
|
- Report completion with summary of changes
|
|
19563
19568
|
|
|
19564
|
-
|
|
19565
|
-
- Always use dedicated file tools for file I/O
|
|
19566
|
-
- Search files/code with glob, grep, or ast_grep_search
|
|
19567
|
-
- Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
|
|
19568
|
-
- Edit/write files with write, edit, or apply_patch. Never use shell redirection, echo, printf, or heredocs for file content unless no file tool can do the job
|
|
19569
|
-
- Use bash only for execution: git, package managers, tests, builds, scripts, or diagnostics
|
|
19569
|
+
${WRITABLE_FILE_OPERATIONS_RULES}
|
|
19570
19570
|
|
|
19571
19571
|
**Constraints**:
|
|
19572
19572
|
- NO external research (no websearch, context7, grep_app)
|
|
@@ -19633,11 +19633,7 @@ var LIBRARIAN_PROMPT = `You are Librarian - a research specialist for codebases
|
|
|
19633
19633
|
- grep_app: Search GitHub repositories
|
|
19634
19634
|
- websearch: General web search for docs
|
|
19635
19635
|
|
|
19636
|
-
|
|
19637
|
-
- Use dedicated tools for file I/O when local files must be inspected
|
|
19638
|
-
- Search files/code with glob, grep, or ast_grep_search
|
|
19639
|
-
- Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
|
|
19640
|
-
- Use bash only for execution/diagnostics, never for file I/O
|
|
19636
|
+
${READONLY_FILE_OPERATIONS_RULES}
|
|
19641
19637
|
|
|
19642
19638
|
**Behavior**:
|
|
19643
19639
|
- Provide evidence-based answers with sources
|
|
@@ -19684,10 +19680,7 @@ var OBSERVER_PROMPT = `You are Observer — a visual analysis specialist.
|
|
|
19684
19680
|
- Match the language of the request
|
|
19685
19681
|
- If info not found, state clearly what's missing
|
|
19686
19682
|
|
|
19687
|
-
|
|
19688
|
-
- READ-ONLY: do not modify files
|
|
19689
|
-
- Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
|
|
19690
|
-
- Use bash only for execution/diagnostics, never for file I/O
|
|
19683
|
+
${READONLY_FILE_OPERATIONS_RULES}
|
|
19691
19684
|
`;
|
|
19692
19685
|
function createObserverAgent(model, customPrompt, customAppendPrompt) {
|
|
19693
19686
|
let prompt = OBSERVER_PROMPT;
|
|
@@ -19733,11 +19726,7 @@ var ORACLE_PROMPT = `You are Oracle - a strategic technical advisor and code rev
|
|
|
19733
19726
|
- Focus on strategy, not execution
|
|
19734
19727
|
- Point to specific files/lines when relevant
|
|
19735
19728
|
|
|
19736
|
-
|
|
19737
|
-
- READ-ONLY: do not modify files
|
|
19738
|
-
- Search files/code with glob, grep, or ast_grep_search
|
|
19739
|
-
- Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
|
|
19740
|
-
- Use bash only for execution/diagnostics, never for file I/O
|
|
19729
|
+
${READONLY_FILE_OPERATIONS_RULES}
|
|
19741
19730
|
`;
|
|
19742
19731
|
function createOracleAgent(model, customPrompt, customAppendPrompt) {
|
|
19743
19732
|
let prompt = ORACLE_PROMPT;
|
|
@@ -22810,6 +22799,26 @@ class BackgroundJobBoard {
|
|
|
22810
22799
|
resultSummary: status.result
|
|
22811
22800
|
});
|
|
22812
22801
|
}
|
|
22802
|
+
markRunningFromLiveSession(taskID, now = Date.now()) {
|
|
22803
|
+
const existing = this.jobs.get(taskID);
|
|
22804
|
+
if (!existing)
|
|
22805
|
+
return;
|
|
22806
|
+
const isStaleCancellation = existing.state === "cancelled" || existing.state === "reconciled" && existing.terminalState === "cancelled";
|
|
22807
|
+
if (!isStaleCancellation)
|
|
22808
|
+
return existing;
|
|
22809
|
+
const updated = {
|
|
22810
|
+
...existing,
|
|
22811
|
+
state: "running",
|
|
22812
|
+
timedOut: false,
|
|
22813
|
+
terminalUnreconciled: false,
|
|
22814
|
+
updatedAt: now,
|
|
22815
|
+
completedAt: undefined,
|
|
22816
|
+
terminalState: undefined,
|
|
22817
|
+
resultSummary: undefined
|
|
22818
|
+
};
|
|
22819
|
+
this.jobs.set(taskID, updated);
|
|
22820
|
+
return updated;
|
|
22821
|
+
}
|
|
22813
22822
|
markReconciled(taskID, now = Date.now()) {
|
|
22814
22823
|
const existing = this.jobs.get(taskID);
|
|
22815
22824
|
if (!existing)
|
|
@@ -22916,7 +22925,7 @@ class BackgroundJobBoard {
|
|
|
22916
22925
|
return [
|
|
22917
22926
|
"### Background Job Board",
|
|
22918
22927
|
"SENTINEL: background-job-board-v2",
|
|
22919
|
-
"Use task_status for running jobs. Reconcile terminal jobs before final response. Reuse
|
|
22928
|
+
"Use task_status for running jobs. Reconcile terminal jobs before final response. Reuse any non-running session for the same specialist/context.",
|
|
22920
22929
|
"",
|
|
22921
22930
|
"#### Active / Unreconciled",
|
|
22922
22931
|
...active.length > 0 ? active.map((job) => formatJob(job, now)) : ["- none"],
|
|
@@ -22944,8 +22953,10 @@ class BackgroundJobBoard {
|
|
|
22944
22953
|
}
|
|
22945
22954
|
}
|
|
22946
22955
|
formatReusableJob(job) {
|
|
22956
|
+
const terminal = job.terminalState ?? terminalStateOf(job.state);
|
|
22957
|
+
const reconciliation = job.terminalUnreconciled ? "unreconciled" : "reconciled";
|
|
22947
22958
|
const lines = [
|
|
22948
|
-
`- ${job.alias} / ${job.taskID} / ${job.agent} /
|
|
22959
|
+
`- ${job.alias} / ${job.taskID} / ${job.agent} / ${terminal ?? job.state}, ${reconciliation}`,
|
|
22949
22960
|
` Objective: ${job.objective || job.description}`
|
|
22950
22961
|
];
|
|
22951
22962
|
const context = formatContextFiles(job.contextFiles, this.readContextMaxFiles);
|
|
@@ -22970,7 +22981,7 @@ function deriveTaskSessionLabel(input) {
|
|
|
22970
22981
|
return firstPromptLine ? firstPromptLine.slice(0, 48) : `recent ${input.agentType} task`;
|
|
22971
22982
|
}
|
|
22972
22983
|
function isReusable(job) {
|
|
22973
|
-
return job.state
|
|
22984
|
+
return job.state !== "running";
|
|
22974
22985
|
}
|
|
22975
22986
|
function terminalStateOf(state) {
|
|
22976
22987
|
return state === "completed" || state === "error" || state === "cancelled" ? state : undefined;
|
|
@@ -24077,14 +24088,33 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24077
24088
|
const status = parseTaskStatusOutput(output);
|
|
24078
24089
|
if (!status)
|
|
24079
24090
|
return;
|
|
24091
|
+
log("[task-session-manager] parsed task status output", {
|
|
24092
|
+
taskID: status.taskID,
|
|
24093
|
+
state: status.state,
|
|
24094
|
+
timedOut: status.timedOut,
|
|
24095
|
+
hasResult: Boolean(status.result)
|
|
24096
|
+
});
|
|
24080
24097
|
const updated = backgroundJobBoard.updateStatus({
|
|
24081
24098
|
taskID: status.taskID,
|
|
24082
24099
|
state: status.state,
|
|
24083
24100
|
timedOut: status.timedOut,
|
|
24084
24101
|
resultSummary: status.result
|
|
24085
24102
|
});
|
|
24086
|
-
if (!updated)
|
|
24103
|
+
if (!updated) {
|
|
24104
|
+
log("[task-session-manager] ignored status for unknown background job", {
|
|
24105
|
+
taskID: status.taskID,
|
|
24106
|
+
state: status.state
|
|
24107
|
+
});
|
|
24087
24108
|
return;
|
|
24109
|
+
}
|
|
24110
|
+
log("[task-session-manager] background job status updated", {
|
|
24111
|
+
taskID: updated.taskID,
|
|
24112
|
+
alias: updated.alias,
|
|
24113
|
+
parentSessionID: updated.parentSessionID,
|
|
24114
|
+
state: updated.state,
|
|
24115
|
+
terminalUnreconciled: updated.terminalUnreconciled,
|
|
24116
|
+
timedOut: updated.timedOut
|
|
24117
|
+
});
|
|
24088
24118
|
if (updated.terminalUnreconciled) {
|
|
24089
24119
|
pendingManagedTaskIds.delete(updated.taskID);
|
|
24090
24120
|
backgroundJobBoard.addContext(updated.taskID, contextFilesForPrompt(contextByTask.get(updated.taskID)));
|
|
@@ -24114,6 +24144,13 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24114
24144
|
const updated = updateBackgroundJobFromOutput(part.text);
|
|
24115
24145
|
if (!updated)
|
|
24116
24146
|
return;
|
|
24147
|
+
log("[task-session-manager] processed injected background completion", {
|
|
24148
|
+
taskID: updated.taskID,
|
|
24149
|
+
alias: updated.alias,
|
|
24150
|
+
parentSessionID: updated.parentSessionID,
|
|
24151
|
+
state: updated.state,
|
|
24152
|
+
occurrenceId
|
|
24153
|
+
});
|
|
24117
24154
|
rememberProcessedInjectedCompletion(occurrenceId);
|
|
24118
24155
|
return updated;
|
|
24119
24156
|
}
|
|
@@ -24170,6 +24207,10 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24170
24207
|
const taskIDs = backgroundJobBoard.list(parentSessionID).filter((job) => job.terminalUnreconciled).map((job) => job.taskID);
|
|
24171
24208
|
if (taskIDs.length === 0)
|
|
24172
24209
|
return;
|
|
24210
|
+
log("[task-session-manager] terminal jobs injected for reconciliation", {
|
|
24211
|
+
parentSessionID,
|
|
24212
|
+
taskIDs
|
|
24213
|
+
});
|
|
24173
24214
|
const existing = terminalJobsInjectedByParent.get(parentSessionID) ?? new Set;
|
|
24174
24215
|
for (const taskID of taskIDs) {
|
|
24175
24216
|
existing.add(taskID);
|
|
@@ -24180,6 +24221,10 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24180
24221
|
const taskIDs = terminalJobsInjectedByParent.get(parentSessionID);
|
|
24181
24222
|
if (!taskIDs)
|
|
24182
24223
|
return;
|
|
24224
|
+
log("[task-session-manager] reconciling injected terminal jobs", {
|
|
24225
|
+
parentSessionID,
|
|
24226
|
+
taskIDs: [...taskIDs]
|
|
24227
|
+
});
|
|
24183
24228
|
for (const taskID of taskIDs) {
|
|
24184
24229
|
backgroundJobBoard.markReconciled(taskID);
|
|
24185
24230
|
}
|
|
@@ -24263,13 +24308,21 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24263
24308
|
return;
|
|
24264
24309
|
const launch = parseTaskLaunchOutput(output.output);
|
|
24265
24310
|
if (launch) {
|
|
24266
|
-
backgroundJobBoard.registerLaunch({
|
|
24311
|
+
const record = backgroundJobBoard.registerLaunch({
|
|
24267
24312
|
taskID: launch.taskID,
|
|
24268
24313
|
parentSessionID: pending.parentSessionId,
|
|
24269
24314
|
agent: pending.agentType,
|
|
24270
24315
|
description: pending.label,
|
|
24271
24316
|
objective: pending.label
|
|
24272
24317
|
});
|
|
24318
|
+
log("[task-session-manager] background task launch registered", {
|
|
24319
|
+
taskID: record.taskID,
|
|
24320
|
+
alias: record.alias,
|
|
24321
|
+
parentSessionID: record.parentSessionID,
|
|
24322
|
+
agent: record.agent,
|
|
24323
|
+
description: record.description,
|
|
24324
|
+
state: record.state
|
|
24325
|
+
});
|
|
24273
24326
|
backgroundJobBoard.addContext(launch.taskID, contextFilesForPrompt(contextByTask.get(launch.taskID)));
|
|
24274
24327
|
pendingManagedTaskIds.add(launch.taskID);
|
|
24275
24328
|
return;
|
|
@@ -24335,6 +24388,11 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24335
24388
|
event: async (input) => {
|
|
24336
24389
|
if (input.event.type === "session.created") {
|
|
24337
24390
|
const info = input.event.properties?.info;
|
|
24391
|
+
log("[task-session-manager] session.created observed", {
|
|
24392
|
+
sessionID: info?.id,
|
|
24393
|
+
parentSessionID: info?.parentID,
|
|
24394
|
+
managesParent: info?.parentID ? options.shouldManageSession(info.parentID) : false
|
|
24395
|
+
});
|
|
24338
24396
|
if (info?.id && info.parentID && options.shouldManageSession(info.parentID)) {
|
|
24339
24397
|
pendingManagedTaskIds.add(info.id);
|
|
24340
24398
|
}
|
|
@@ -24342,6 +24400,11 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24342
24400
|
}
|
|
24343
24401
|
if (input.event.type === "session.idle" || input.event.type === "session.status" && input.event.properties?.status?.type === "idle") {
|
|
24344
24402
|
const sessionId2 = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
|
|
24403
|
+
log("[task-session-manager] idle/status idle observed", {
|
|
24404
|
+
sessionID: sessionId2,
|
|
24405
|
+
managesSession: sessionId2 ? options.shouldManageSession(sessionId2) : false,
|
|
24406
|
+
terminalJobsPending: sessionId2 ? terminalJobsInjectedByParent.get(sessionId2)?.size ?? 0 : 0
|
|
24407
|
+
});
|
|
24345
24408
|
if (sessionId2 && options.shouldManageSession(sessionId2)) {
|
|
24346
24409
|
reconcileInjectedTerminalJobs(sessionId2);
|
|
24347
24410
|
}
|
|
@@ -24354,11 +24417,34 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24354
24417
|
}
|
|
24355
24418
|
return;
|
|
24356
24419
|
}
|
|
24420
|
+
if (input.event.type === "session.status" && input.event.properties?.status?.type === "busy") {
|
|
24421
|
+
const sessionId2 = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
|
|
24422
|
+
const before = sessionId2 ? backgroundJobBoard.get(sessionId2) : undefined;
|
|
24423
|
+
const updated = sessionId2 ? backgroundJobBoard.markRunningFromLiveSession(sessionId2) : undefined;
|
|
24424
|
+
log("[task-session-manager] busy/status busy observed", {
|
|
24425
|
+
sessionID: sessionId2,
|
|
24426
|
+
managesSession: sessionId2 ? options.shouldManageSession(sessionId2) : false,
|
|
24427
|
+
previousState: before?.state,
|
|
24428
|
+
previousTerminalState: before?.terminalState,
|
|
24429
|
+
updatedState: updated?.state
|
|
24430
|
+
});
|
|
24431
|
+
return;
|
|
24432
|
+
}
|
|
24357
24433
|
if (input.event.type !== "session.deleted")
|
|
24358
24434
|
return;
|
|
24359
24435
|
const sessionId = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
|
|
24360
24436
|
if (!sessionId)
|
|
24361
24437
|
return;
|
|
24438
|
+
log("[task-session-manager] session.deleted observed; clearing job state", {
|
|
24439
|
+
sessionID: sessionId,
|
|
24440
|
+
deletedJob: backgroundJobBoard.get(sessionId) ? {
|
|
24441
|
+
state: backgroundJobBoard.get(sessionId)?.state,
|
|
24442
|
+
parentSessionID: backgroundJobBoard.get(sessionId)?.parentSessionID,
|
|
24443
|
+
alias: backgroundJobBoard.get(sessionId)?.alias
|
|
24444
|
+
} : undefined,
|
|
24445
|
+
childJobCount: backgroundJobBoard.list(sessionId).length,
|
|
24446
|
+
managesSession: options.shouldManageSession(sessionId)
|
|
24447
|
+
});
|
|
24362
24448
|
backgroundJobBoard.drop(sessionId);
|
|
24363
24449
|
backgroundJobBoard.clearParent(sessionId);
|
|
24364
24450
|
terminalJobsInjectedByParent.delete(sessionId);
|
|
@@ -29681,6 +29767,7 @@ class MultiplexerSessionManager {
|
|
|
29681
29767
|
parentId,
|
|
29682
29768
|
title,
|
|
29683
29769
|
directory,
|
|
29770
|
+
ownerInstanceId: this.instanceId,
|
|
29684
29771
|
createdAt: now,
|
|
29685
29772
|
lastSeenAt: now,
|
|
29686
29773
|
seenInStatus: false
|
|
@@ -29706,7 +29793,9 @@ class MultiplexerSessionManager {
|
|
|
29706
29793
|
instanceId: this.instanceId,
|
|
29707
29794
|
sessionId: sessionId2,
|
|
29708
29795
|
tracked: this.sessions.has(sessionId2),
|
|
29709
|
-
known: this.knownSessions.has(sessionId2)
|
|
29796
|
+
known: this.knownSessions.has(sessionId2),
|
|
29797
|
+
ownerInstanceId: this.sessions.get(sessionId2)?.ownerInstanceId,
|
|
29798
|
+
backgroundJobState: this.backgroundJobBoard?.get(sessionId2)?.state
|
|
29710
29799
|
});
|
|
29711
29800
|
await this.closeSession(sessionId2, "idle");
|
|
29712
29801
|
return;
|
|
@@ -29717,6 +29806,14 @@ class MultiplexerSessionManager {
|
|
|
29717
29806
|
if (!sessionId)
|
|
29718
29807
|
return;
|
|
29719
29808
|
if (event.properties?.status?.type === "idle") {
|
|
29809
|
+
log("[multiplexer-session-manager] session status idle received", {
|
|
29810
|
+
instanceId: this.instanceId,
|
|
29811
|
+
sessionId,
|
|
29812
|
+
tracked: this.sessions.has(sessionId),
|
|
29813
|
+
known: this.knownSessions.has(sessionId),
|
|
29814
|
+
ownerInstanceId: this.sessions.get(sessionId)?.ownerInstanceId,
|
|
29815
|
+
backgroundJobState: this.backgroundJobBoard?.get(sessionId)?.state
|
|
29816
|
+
});
|
|
29720
29817
|
await this.closeSession(sessionId, "idle");
|
|
29721
29818
|
return;
|
|
29722
29819
|
}
|
|
@@ -29725,7 +29822,9 @@ class MultiplexerSessionManager {
|
|
|
29725
29822
|
instanceId: this.instanceId,
|
|
29726
29823
|
sessionId,
|
|
29727
29824
|
tracked: this.sessions.has(sessionId),
|
|
29728
|
-
known: this.knownSessions.has(sessionId)
|
|
29825
|
+
known: this.knownSessions.has(sessionId),
|
|
29826
|
+
ownerInstanceId: this.sessions.get(sessionId)?.ownerInstanceId,
|
|
29827
|
+
backgroundJobState: this.backgroundJobBoard?.get(sessionId)?.state
|
|
29729
29828
|
});
|
|
29730
29829
|
await this.respawnIfKnown(sessionId);
|
|
29731
29830
|
}
|
|
@@ -29740,7 +29839,11 @@ class MultiplexerSessionManager {
|
|
|
29740
29839
|
return;
|
|
29741
29840
|
log("[multiplexer-session-manager] session deleted, closing pane", {
|
|
29742
29841
|
instanceId: this.instanceId,
|
|
29743
|
-
sessionId
|
|
29842
|
+
sessionId,
|
|
29843
|
+
tracked: this.sessions.has(sessionId),
|
|
29844
|
+
known: this.knownSessions.has(sessionId),
|
|
29845
|
+
ownerInstanceId: this.sessions.get(sessionId)?.ownerInstanceId,
|
|
29846
|
+
backgroundJobState: this.backgroundJobBoard?.get(sessionId)?.state
|
|
29744
29847
|
});
|
|
29745
29848
|
await this.closeSession(sessionId, "deleted");
|
|
29746
29849
|
}
|
|
@@ -29771,6 +29874,15 @@ class MultiplexerSessionManager {
|
|
|
29771
29874
|
const now = Date.now();
|
|
29772
29875
|
const sessionsToClose = [];
|
|
29773
29876
|
for (const [sessionId, tracked] of this.sessions.entries()) {
|
|
29877
|
+
if (tracked.ownerInstanceId !== this.instanceId) {
|
|
29878
|
+
log("[multiplexer-session-manager] skipping non-owner poll close", {
|
|
29879
|
+
instanceId: this.instanceId,
|
|
29880
|
+
ownerInstanceId: tracked.ownerInstanceId,
|
|
29881
|
+
sessionId,
|
|
29882
|
+
paneId: tracked.paneId
|
|
29883
|
+
});
|
|
29884
|
+
continue;
|
|
29885
|
+
}
|
|
29774
29886
|
const status = allStatuses[sessionId];
|
|
29775
29887
|
const isIdle = status?.type === "idle";
|
|
29776
29888
|
if (status) {
|
|
@@ -29781,7 +29893,7 @@ class MultiplexerSessionManager {
|
|
|
29781
29893
|
tracked.missingSince = now;
|
|
29782
29894
|
}
|
|
29783
29895
|
const missingTooLong = !!tracked.missingSince && now - tracked.missingSince >= SESSION_MISSING_GRACE_MS;
|
|
29784
|
-
const shouldKeepRunningBackgroundJob = missingTooLong && this.isRunningBackgroundJob(sessionId);
|
|
29896
|
+
const shouldKeepRunningBackgroundJob = (isIdle || missingTooLong) && this.isRunningBackgroundJob(sessionId);
|
|
29785
29897
|
if (isIdle || missingTooLong) {
|
|
29786
29898
|
if (shouldKeepRunningBackgroundJob) {
|
|
29787
29899
|
log("[multiplexer-session-manager] keeping running background pane", {
|
|
@@ -29839,12 +29951,35 @@ class MultiplexerSessionManager {
|
|
|
29839
29951
|
});
|
|
29840
29952
|
return;
|
|
29841
29953
|
}
|
|
29954
|
+
if (tracked.ownerInstanceId !== this.instanceId) {
|
|
29955
|
+
log("[multiplexer-session-manager] close skipped; non-owner instance", {
|
|
29956
|
+
instanceId: this.instanceId,
|
|
29957
|
+
ownerInstanceId: tracked.ownerInstanceId,
|
|
29958
|
+
sessionId,
|
|
29959
|
+
paneId: tracked.paneId,
|
|
29960
|
+
reason
|
|
29961
|
+
});
|
|
29962
|
+
return;
|
|
29963
|
+
}
|
|
29964
|
+
if (reason === "idle" && this.isRunningBackgroundJob(sessionId)) {
|
|
29965
|
+
log("[multiplexer-session-manager] close skipped; background job running", {
|
|
29966
|
+
instanceId: this.instanceId,
|
|
29967
|
+
sessionId,
|
|
29968
|
+
paneId: tracked.paneId,
|
|
29969
|
+
reason,
|
|
29970
|
+
backgroundJobState: this.backgroundJobBoard?.get(sessionId)?.state
|
|
29971
|
+
});
|
|
29972
|
+
return;
|
|
29973
|
+
}
|
|
29842
29974
|
this.sessions.delete(sessionId);
|
|
29843
29975
|
log("[multiplexer-session-manager] closing session pane", {
|
|
29844
29976
|
instanceId: this.instanceId,
|
|
29845
29977
|
sessionId,
|
|
29846
29978
|
paneId: tracked.paneId,
|
|
29847
|
-
reason
|
|
29979
|
+
reason,
|
|
29980
|
+
backgroundJobState: this.backgroundJobBoard?.get(sessionId)?.state,
|
|
29981
|
+
parentId: tracked.parentId,
|
|
29982
|
+
title: tracked.title
|
|
29848
29983
|
});
|
|
29849
29984
|
const closePromise = this.multiplexer.closePane(tracked.paneId).then(() => {
|
|
29850
29985
|
return;
|
|
@@ -29918,6 +30053,7 @@ class MultiplexerSessionManager {
|
|
|
29918
30053
|
parentId: known.parentId,
|
|
29919
30054
|
title: known.title,
|
|
29920
30055
|
directory: known.directory,
|
|
30056
|
+
ownerInstanceId: this.instanceId,
|
|
29921
30057
|
createdAt: now,
|
|
29922
30058
|
lastSeenAt: now,
|
|
29923
30059
|
seenInStatus: false
|
|
@@ -30577,7 +30713,8 @@ Use only for obsolete, wrong, conflicting, or user-requested cancellation. Accep
|
|
|
30577
30713
|
].join(`
|
|
30578
30714
|
`);
|
|
30579
30715
|
}
|
|
30580
|
-
|
|
30716
|
+
const shouldAbort = job.state === "running" || job.state === "cancelled" || job.state === "reconciled" && job.terminalState === "cancelled";
|
|
30717
|
+
if (!shouldAbort) {
|
|
30581
30718
|
return [
|
|
30582
30719
|
`task_id: ${job.taskID}`,
|
|
30583
30720
|
`state: ${job.state}`,
|
package/dist/tui.js
CHANGED
|
@@ -69,6 +69,21 @@ var DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
|
|
|
69
69
|
var MAX_POLL_TIME_MS = 5 * 60 * 1000;
|
|
70
70
|
var DEFAULT_MAX_SUBAGENT_DEPTH = 3;
|
|
71
71
|
var PHASE_REMINDER_TEXT = `!IMPORTANT! Scheduler workflow: plan lanes/dependencies → dispatch background specialists → track task IDs → wait for hook-driven completion or use task_status only when needed → reconcile terminal results → verify. Do not consume running-job output or advance dependent work. !END!`;
|
|
72
|
+
var WRITABLE_FILE_OPERATIONS_RULES = `**File Operations Rules**:
|
|
73
|
+
- Prefer dedicated file tools for normal code work: glob/grep/ast_grep_search for discovery, read for file contents, and edit/write/apply_patch for targeted source changes.
|
|
74
|
+
- Use bash for execution and automation: git, package managers, tests, builds, scripts, diagnostics, and shell-native filesystem operations.
|
|
75
|
+
- Shell is acceptable for bulk or mechanical filesystem changes when it is clearer or safer than many individual edits (for example: truncate generated logs, remove build artifacts, batch rename/move files), especially when the user explicitly asks for that shell operation.
|
|
76
|
+
- Before destructive or broad shell operations, verify the target set and quote paths. Prefer a dry-run/listing first when practical.
|
|
77
|
+
- Do not use cat/head/tail/sed/awk only to read code into context; use read/grep unless a shell pipeline is genuinely the better diagnostic.`;
|
|
78
|
+
var READONLY_FILE_OPERATIONS_RULES = `**File Operations Rules**:
|
|
79
|
+
- READ-ONLY: inspect and report; do not modify files.
|
|
80
|
+
- Prefer dedicated file tools for codebase inspection: glob/grep/ast_grep_search for discovery and read for file contents.
|
|
81
|
+
- Bash is allowed for non-mutating diagnostics and shell-native inspection when it is the clearest tool, but not for modifying files.
|
|
82
|
+
- Do not use cat/head/tail/sed/awk only to read code into context; use read/grep unless a shell pipeline is genuinely the better diagnostic.`;
|
|
83
|
+
var NO_SHELL_READONLY_FILE_OPERATIONS_RULES = `**File Operations Rules**:
|
|
84
|
+
- READ-ONLY: inspect and report; do not modify files.
|
|
85
|
+
- Use glob/grep/ast_grep_search for discovery and read for file contents.
|
|
86
|
+
- Do not use bash or shell commands.`;
|
|
72
87
|
var TMUX_SPAWN_DELAY_MS = 500;
|
|
73
88
|
var COUNCILLOR_STAGGER_MS = 250;
|
|
74
89
|
var DEFAULT_DISABLED_AGENTS = ["observer"];
|
|
@@ -55,6 +55,7 @@ export declare class BackgroundJobBoard {
|
|
|
55
55
|
registerLaunch(input: BackgroundJobLaunchInput): BackgroundJobRecord;
|
|
56
56
|
updateStatus(input: BackgroundJobStatusInput): BackgroundJobRecord | undefined;
|
|
57
57
|
updateFromStatusOutput(output: string): BackgroundJobRecord | undefined;
|
|
58
|
+
markRunningFromLiveSession(taskID: string, now?: number): BackgroundJobRecord | undefined;
|
|
58
59
|
markReconciled(taskID: string, now?: number): BackgroundJobRecord | undefined;
|
|
59
60
|
markCancelled(taskID: string, reason?: string, now?: number): BackgroundJobRecord | undefined;
|
|
60
61
|
get(taskID: string): BackgroundJobRecord | undefined;
|
package/package.json
CHANGED
|
@@ -33,8 +33,30 @@ Required behavior:
|
|
|
33
33
|
- after each phase, validate, update the deepwork file, prepare the plan file
|
|
34
34
|
for oracle review and ask `@oracle` to review the phase result, fix
|
|
35
35
|
actionable issues, then continue;
|
|
36
|
+
- when a phase includes `@designer`, preserve designer intent across later
|
|
37
|
+
phases. Use `@fixer` only for mechanical follow-up that does not alter the
|
|
38
|
+
UI/UX;
|
|
36
39
|
- finish with final validation and a concise summary.
|
|
37
40
|
|
|
41
|
+
## Designer Handoff Guardrail
|
|
42
|
+
|
|
43
|
+
When a deepwork phase includes `@designer`, treat the delivered UI/UX as
|
|
44
|
+
accepted design intent for later phases. Record any important design decisions in
|
|
45
|
+
the deepwork file before continuing.
|
|
46
|
+
|
|
47
|
+
After designer work:
|
|
48
|
+
|
|
49
|
+
- preserve layout, rhythm, hierarchy, motion, spacing, color, affordances,
|
|
50
|
+
responsiveness, and component feel;
|
|
51
|
+
- review and improve user-facing copy with grounded, normal wording, but do not
|
|
52
|
+
change visual structure or interaction intent;
|
|
53
|
+
- route follow-up visual, responsive, motion, hierarchy, polish, or
|
|
54
|
+
component-feel changes back to `@designer`;
|
|
55
|
+
- use `@fixer` only for bounded mechanical follow-up that preserves the design
|
|
56
|
+
exactly, such as wiring, tests, type fixes, or non-visual behavior changes;
|
|
57
|
+
- if design intent must change, record why in the deepwork file before changing
|
|
58
|
+
it.
|
|
59
|
+
|
|
38
60
|
## Deepwork File
|
|
39
61
|
|
|
40
62
|
Create a task-specific file such as:
|