maestro-flow 0.3.12 → 0.3.13
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/.claude/CLAUDE.md +7 -0
- package/.claude/agents/workflow-analyzer.md +0 -1
- package/.claude/agents/workflow-collab-planner.md +0 -1
- package/.claude/agents/workflow-debugger.md +0 -1
- package/.claude/agents/workflow-integration-checker.md +2 -2
- package/.claude/agents/workflow-nyquist-auditor.md +0 -1
- package/.claude/agents/workflow-phase-researcher.md +2 -2
- package/.claude/agents/workflow-plan-checker.md +1 -1
- package/.claude/agents/workflow-planner.md +1 -2
- package/.claude/agents/workflow-roadmapper.md +1 -1
- package/.claude/agents/workflow-verifier.md +0 -1
- package/.claude/commands/learn-retro.md +2 -2
- package/.claude/commands/learn-second-opinion.md +2 -2
- package/.claude/commands/maestro-analyze.md +10 -2
- package/.claude/commands/maestro-brainstorm.md +1 -1
- package/.claude/commands/maestro-execute.md +21 -4
- package/.claude/commands/maestro-milestone-complete.md +14 -0
- package/.claude/commands/maestro-plan.md +12 -6
- package/.claude/commands/maestro-roadmap.md +1 -1
- package/.claude/commands/maestro-ui-design.md +7 -7
- package/.claude/commands/maestro-update.md +176 -0
- package/.claude/commands/maestro-verify.md +18 -3
- package/.claude/commands/manage-codebase-rebuild.md +0 -1
- package/.claude/commands/manage-harvest.md +1 -1
- package/.claude/commands/manage-learn.md +5 -5
- package/.claude/commands/manage-memory-capture.md +4 -4
- package/.claude/commands/manage-memory.md +1 -1
- package/.claude/commands/manage-wiki.md +62 -0
- package/.claude/commands/quality-business-test.md +2 -2
- package/.claude/commands/quality-debug.md +53 -7
- package/.claude/commands/quality-retrospective.md +5 -5
- package/.claude/commands/quality-review.md +39 -7
- package/.claude/commands/quality-sync.md +1 -1
- package/.claude/commands/quality-test-gen.md +1 -1
- package/.claude/commands/quality-test.md +45 -12
- package/.claude/commands/spec-remove.md +51 -0
- package/.claude/commands/spec-setup.md +1 -3
- package/.claude/commands/wiki-connect.md +9 -5
- package/.claude/commands/wiki-digest.md +6 -3
- package/.codex/skills/maestro/SKILL.md +2 -2
- package/.codex/skills/maestro-analyze/SKILL.md +4 -4
- package/.codex/skills/maestro-brainstorm/SKILL.md +4 -4
- package/.codex/skills/maestro-coordinate/SKILL.md +2 -2
- package/.codex/skills/maestro-execute/SKILL.md +15 -5
- package/.codex/skills/maestro-init/SKILL.md +1 -1
- package/.codex/skills/maestro-milestone-complete/SKILL.md +18 -1
- package/.codex/skills/maestro-plan/SKILL.md +6 -6
- package/.codex/skills/maestro-roadmap/SKILL.md +3 -4
- package/.codex/skills/maestro-spec-generate/SKILL.md +2 -2
- package/.codex/skills/maestro-ui-design/SKILL.md +6 -6
- package/.codex/skills/maestro-verify/SKILL.md +20 -11
- package/.codex/skills/manage-codebase-rebuild/SKILL.md +4 -4
- package/.codex/skills/manage-harvest/SKILL.md +10 -1
- package/.codex/skills/manage-issue-discover/SKILL.md +3 -3
- package/.codex/skills/manage-learn/SKILL.md +3 -2
- package/.codex/skills/manage-memory/SKILL.md +3 -3
- package/.codex/skills/manage-memory-capture/SKILL.md +8 -14
- package/.codex/skills/manage-status/SKILL.md +9 -4
- package/.codex/skills/manage-wiki/SKILL.md +55 -0
- package/.codex/skills/quality-business-test/SKILL.md +8 -6
- package/.codex/skills/quality-debug/SKILL.md +22 -9
- package/.codex/skills/quality-integration-test/SKILL.md +11 -7
- package/.codex/skills/quality-retrospective/SKILL.md +45 -26
- package/.codex/skills/quality-review/SKILL.md +10 -7
- package/.codex/skills/quality-test/SKILL.md +9 -4
- package/.codex/skills/quality-test-gen/SKILL.md +13 -9
- package/.codex/skills/spec-add/SKILL.md +11 -3
- package/.codex/skills/spec-load/SKILL.md +7 -0
- package/.codex/skills/spec-map/SKILL.md +2 -2
- package/.codex/skills/spec-remove/SKILL.md +101 -0
- package/.codex/skills/spec-setup/SKILL.md +4 -8
- package/.codex/skills/wiki-connect/SKILL.md +6 -5
- package/.codex/skills/wiki-digest/SKILL.md +2 -2
- package/dashboard/dist-server/dashboard/src/server/agents/claude-code-adapter.d.ts +9 -0
- package/dashboard/dist-server/dashboard/src/server/agents/claude-code-adapter.js +109 -9
- package/dashboard/dist-server/dashboard/src/server/agents/claude-code-adapter.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/agents/claude-code-adapter.test.js +49 -0
- package/dashboard/dist-server/dashboard/src/server/agents/claude-code-adapter.test.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/routes/index.js +5 -4
- package/dashboard/dist-server/dashboard/src/server/routes/index.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/routes/specs.d.ts +5 -13
- package/dashboard/dist-server/dashboard/src/server/routes/specs.js +97 -155
- package/dashboard/dist-server/dashboard/src/server/routes/specs.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/routes/wiki.d.ts +11 -1
- package/dashboard/dist-server/dashboard/src/server/routes/wiki.integration.test.js +27 -6
- package/dashboard/dist-server/dashboard/src/server/routes/wiki.integration.test.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/routes/wiki.js +25 -7
- package/dashboard/dist-server/dashboard/src/server/routes/wiki.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/graph-analysis.js +8 -0
- package/dashboard/dist-server/dashboard/src/server/wiki/graph-analysis.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/search.js +1 -0
- package/dashboard/dist-server/dashboard/src/server/wiki/search.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/spec-entry-parser.d.ts +29 -0
- package/dashboard/dist-server/dashboard/src/server/wiki/spec-entry-parser.js +148 -0
- package/dashboard/dist-server/dashboard/src/server/wiki/spec-entry-parser.js.map +1 -0
- package/dashboard/dist-server/dashboard/src/server/wiki/stress.test.js +4 -2
- package/dashboard/dist-server/dashboard/src/server/wiki/stress.test.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/virtual-wiki-adapters.js +8 -2
- package/dashboard/dist-server/dashboard/src/server/wiki/virtual-wiki-adapters.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.d.ts +5 -0
- package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.js +80 -38
- package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.test.js +8 -6
- package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.test.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/wiki-types.d.ts +40 -5
- package/dashboard/dist-server/dashboard/src/server/wiki/writer-stress.test.js +21 -23
- package/dashboard/dist-server/dashboard/src/server/wiki/writer-stress.test.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/writer.d.ts +33 -3
- package/dashboard/dist-server/dashboard/src/server/wiki/writer.js +184 -12
- package/dashboard/dist-server/dashboard/src/server/wiki/writer.js.map +1 -1
- package/dashboard/dist-server/src/commands/delegate.js +26 -0
- package/dashboard/dist-server/src/commands/delegate.js.map +1 -1
- package/dashboard/dist-server/src/coordinator/graph-types.d.ts +11 -1
- package/dashboard/dist-server/src/coordinator/graph-walker.js +29 -2
- package/dashboard/dist-server/src/coordinator/graph-walker.js.map +1 -1
- package/dashboard/dist-server/src/coordinator/prompt-assembler.js +3 -2
- package/dashboard/dist-server/src/coordinator/prompt-assembler.js.map +1 -1
- package/dashboard/dist-server/src/hooks/constants.d.ts +29 -60
- package/dashboard/dist-server/src/hooks/constants.js +105 -82
- package/dashboard/dist-server/src/hooks/constants.js.map +1 -1
- package/dashboard/dist-server/src/types/index.d.ts +2 -1
- package/dist/src/commands/delegate.d.ts.map +1 -1
- package/dist/src/commands/delegate.js +26 -0
- package/dist/src/commands/delegate.js.map +1 -1
- package/dist/src/commands/hooks.d.ts +2 -4
- package/dist/src/commands/hooks.d.ts.map +1 -1
- package/dist/src/commands/hooks.js +4 -7
- package/dist/src/commands/hooks.js.map +1 -1
- package/dist/src/commands/install-ui/InstallConfirm.d.ts +2 -3
- package/dist/src/commands/install-ui/InstallConfirm.d.ts.map +1 -1
- package/dist/src/commands/install-ui/InstallConfirm.js +1 -1
- package/dist/src/commands/install-ui/InstallConfirm.js.map +1 -1
- package/dist/src/commands/install-ui/InstallExecution.d.ts.map +1 -1
- package/dist/src/commands/install-ui/InstallExecution.js +1 -2
- package/dist/src/commands/install-ui/InstallExecution.js.map +1 -1
- package/dist/src/commands/install-ui/InstallFlow.d.ts.map +1 -1
- package/dist/src/commands/install-ui/InstallFlow.js +5 -7
- package/dist/src/commands/install-ui/InstallFlow.js.map +1 -1
- package/dist/src/commands/install-ui/StatuslineConfig.d.ts +3 -6
- package/dist/src/commands/install-ui/StatuslineConfig.d.ts.map +1 -1
- package/dist/src/commands/install-ui/StatuslineConfig.js +21 -17
- package/dist/src/commands/install-ui/StatuslineConfig.js.map +1 -1
- package/dist/src/commands/update.d.ts.map +1 -1
- package/dist/src/commands/update.js +95 -0
- package/dist/src/commands/update.js.map +1 -1
- package/dist/src/commands/wiki.d.ts.map +1 -1
- package/dist/src/commands/wiki.js +75 -11
- package/dist/src/commands/wiki.js.map +1 -1
- package/dist/src/coordinator/graph-types.d.ts +11 -1
- package/dist/src/coordinator/graph-types.d.ts.map +1 -1
- package/dist/src/coordinator/graph-walker.d.ts.map +1 -1
- package/dist/src/coordinator/graph-walker.js +29 -2
- package/dist/src/coordinator/graph-walker.js.map +1 -1
- package/dist/src/coordinator/prompt-assembler.d.ts.map +1 -1
- package/dist/src/coordinator/prompt-assembler.js +3 -2
- package/dist/src/coordinator/prompt-assembler.js.map +1 -1
- package/dist/src/hooks/__tests__/statusline-visual-test.d.ts +4 -1
- package/dist/src/hooks/__tests__/statusline-visual-test.d.ts.map +1 -1
- package/dist/src/hooks/__tests__/statusline-visual-test.js +55 -174
- package/dist/src/hooks/__tests__/statusline-visual-test.js.map +1 -1
- package/dist/src/hooks/constants.d.ts +29 -60
- package/dist/src/hooks/constants.d.ts.map +1 -1
- package/dist/src/hooks/constants.js +105 -82
- package/dist/src/hooks/constants.js.map +1 -1
- package/dist/src/hooks/skill-context.d.ts.map +1 -1
- package/dist/src/hooks/skill-context.js +54 -6
- package/dist/src/hooks/skill-context.js.map +1 -1
- package/dist/src/hooks/statusline.d.ts +11 -8
- package/dist/src/hooks/statusline.d.ts.map +1 -1
- package/dist/src/hooks/statusline.js +284 -182
- package/dist/src/hooks/statusline.js.map +1 -1
- package/dist/src/hooks/workspace.d.ts.map +1 -1
- package/dist/src/hooks/workspace.js +2 -1
- package/dist/src/hooks/workspace.js.map +1 -1
- package/dist/src/migrations/_template.d.ts +12 -0
- package/dist/src/migrations/_template.d.ts.map +1 -0
- package/dist/src/migrations/_template.js +55 -0
- package/dist/src/migrations/_template.js.map +1 -0
- package/dist/src/migrations/index.d.ts +14 -0
- package/dist/src/migrations/index.d.ts.map +1 -0
- package/dist/src/migrations/index.js +20 -0
- package/dist/src/migrations/index.js.map +1 -0
- package/dist/src/migrations/run.d.ts +12 -0
- package/dist/src/migrations/run.d.ts.map +1 -0
- package/dist/src/migrations/run.js +119 -0
- package/dist/src/migrations/run.js.map +1 -0
- package/dist/src/migrations/v1-to-v2.d.ts +10 -0
- package/dist/src/migrations/v1-to-v2.d.ts.map +1 -0
- package/dist/src/migrations/v1-to-v2.js +71 -0
- package/dist/src/migrations/v1-to-v2.js.map +1 -0
- package/dist/src/tools/team-activity.d.ts.map +1 -1
- package/dist/src/tools/team-activity.js +22 -0
- package/dist/src/tools/team-activity.js.map +1 -1
- package/dist/src/tools/transition-recorder.d.ts +2 -17
- package/dist/src/tools/transition-recorder.d.ts.map +1 -1
- package/dist/src/tools/transition-recorder.js +6 -3
- package/dist/src/tools/transition-recorder.js.map +1 -1
- package/dist/src/types/index.d.ts +2 -1
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/utils/migration-registry.d.ts +65 -0
- package/dist/src/utils/migration-registry.d.ts.map +1 -0
- package/dist/src/utils/migration-registry.js +117 -0
- package/dist/src/utils/migration-registry.js.map +1 -0
- package/dist/src/utils/state-schema.d.ts +153 -0
- package/dist/src/utils/state-schema.d.ts.map +1 -0
- package/dist/src/utils/state-schema.js +329 -0
- package/dist/src/utils/state-schema.js.map +1 -0
- package/package.json +1 -1
- package/templates/state.json +17 -39
- package/workflows/brainstorm.md +3 -3
- package/workflows/codebase-rebuild.md +2 -12
- package/workflows/debug.md +7 -8
- package/workflows/execute.md +18 -4
- package/workflows/fork.md +37 -86
- package/workflows/init.md +1 -4
- package/workflows/integration-test.md +4 -5
- package/workflows/issue.md +3 -9
- package/workflows/learn.md +20 -19
- package/workflows/maestro.codex.md +8 -1
- package/workflows/maestro.md +12 -3
- package/workflows/memory.md +26 -71
- package/workflows/merge.md +45 -107
- package/workflows/milestone-complete.md +24 -7
- package/workflows/retrospective.md +77 -109
- package/workflows/review.md +5 -12
- package/workflows/specs-remove.md +115 -0
- package/workflows/specs-setup.md +10 -32
- package/workflows/status.md +291 -290
- package/workflows/sync.md +5 -5
- package/workflows/test.md +4 -5
- package/workflows/ui-style.md +3 -4
- package/workflows/verify.md +2 -2
- package/workflows/wiki-connect.md +188 -0
- package/workflows/wiki-digest.md +221 -0
- package/workflows/wiki-manage.md +204 -0
|
@@ -1,55 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Maestro Statusline Hook —
|
|
2
|
+
* Maestro Statusline Hook — Two-line colored text
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Segments (left → right):
|
|
8
|
-
* Model | Phase | Coordinator | Task | Team | Directory+Git | Context bar
|
|
4
|
+
* Line 1: Model | Coordinator | Task | Team | Dir+Git | Context
|
|
5
|
+
* Line 2: Milestone ◆Phase | Session chain (ANL-001→PLN-001→EXC-001→VRF-001 ✓)
|
|
9
6
|
*
|
|
10
7
|
* Input (stdin JSON from Claude Code):
|
|
11
8
|
* { model, workspace, session_id, context_window }
|
|
12
9
|
*
|
|
13
|
-
* Output (stdout): formatted
|
|
10
|
+
* Output (stdout): formatted ANSI string (1 or 2 lines)
|
|
14
11
|
*/
|
|
15
12
|
import { readFileSync, readdirSync, statSync, existsSync, writeFileSync } from 'node:fs';
|
|
16
13
|
import { join, basename } from 'node:path';
|
|
17
14
|
import { homedir, tmpdir } from 'node:os';
|
|
18
15
|
import { execSync } from 'node:child_process';
|
|
19
|
-
import { AUTO_COMPACT_BUFFER_PCT, BRIDGE_PREFIX, ANSI_RESET,
|
|
16
|
+
import { AUTO_COMPACT_BUFFER_PCT, BRIDGE_PREFIX, ANSI_RESET, ICONS, GIT_ICONS, TEXT_COLORS, ansiFg, getCtxLevel, } from './constants.js';
|
|
20
17
|
import { readCoordBridge } from './coordinator-tracker.js';
|
|
21
18
|
import { resolveSelf } from '../tools/team-members.js';
|
|
22
19
|
import { readRecentActivity } from '../tools/team-activity.js';
|
|
23
20
|
import { findWorkspaceRoot } from './workspace.js';
|
|
24
21
|
// ---------------------------------------------------------------------------
|
|
25
|
-
//
|
|
22
|
+
// Renderer
|
|
26
23
|
// ---------------------------------------------------------------------------
|
|
27
|
-
/**
|
|
28
|
-
* Powerline mode: colored background segments with arrow separators.
|
|
29
|
-
* Separator uses bold ANSI to make the V-chevron more prominent.
|
|
30
|
-
*/
|
|
31
|
-
function renderPowerline(segments) {
|
|
32
|
-
if (segments.length === 0)
|
|
33
|
-
return '';
|
|
34
|
-
let out = '';
|
|
35
|
-
for (let i = 0; i < segments.length; i++) {
|
|
36
|
-
const seg = segments[i];
|
|
37
|
-
out += ansiBg(seg.bg) + ansiFg(seg.fg) + ` ${seg.text} `;
|
|
38
|
-
if (i < segments.length - 1) {
|
|
39
|
-
// Bold separator: fg = current bg, bg = next bg → Powerline dovetail
|
|
40
|
-
out += ANSI_BOLD + ansiFg(seg.bg) + ansiBg(segments[i + 1].bg) + PL_SEP + ANSI_RESET;
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
// Last segment: arrow fades into terminal background
|
|
44
|
-
out += ANSI_RESET + ANSI_BOLD + ansiFg(seg.bg) + PL_SEP + ANSI_RESET;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return out;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Colored-text mode: colored text on transparent background, pipe separators.
|
|
51
|
-
* Similar style to CCometixLine reference.
|
|
52
|
-
*/
|
|
24
|
+
/** Colored text on transparent background, pipe separators */
|
|
53
25
|
function renderColoredText(segments) {
|
|
54
26
|
if (segments.length === 0)
|
|
55
27
|
return '';
|
|
@@ -65,26 +37,26 @@ function renderColoredText(segments) {
|
|
|
65
37
|
// ---------------------------------------------------------------------------
|
|
66
38
|
// Context usage
|
|
67
39
|
// ---------------------------------------------------------------------------
|
|
68
|
-
/** Normalize remaining% to usable context (accounts for autocompact buffer) */
|
|
69
40
|
function normalizeUsage(remaining) {
|
|
70
41
|
const usableRemaining = Math.max(0, ((remaining - AUTO_COMPACT_BUFFER_PCT) / (100 - AUTO_COMPACT_BUFFER_PCT)) * 100);
|
|
71
42
|
return Math.max(0, Math.min(100, Math.round(100 - usableRemaining)));
|
|
72
43
|
}
|
|
73
|
-
/** Build context bar text: "icon ██████░░░░ 62%" */
|
|
74
44
|
function buildContextText(usedPct) {
|
|
75
45
|
const filled = Math.floor(usedPct / 10);
|
|
76
46
|
const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(10 - filled);
|
|
77
47
|
return `${ICONS.ctx} ${bar} ${usedPct}%`;
|
|
78
48
|
}
|
|
79
|
-
/**
|
|
80
|
-
function
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
49
|
+
/** Format token count: 1234 → "1.2k", 123456 → "123k" */
|
|
50
|
+
function formatTokens(n) {
|
|
51
|
+
if (n < 1000)
|
|
52
|
+
return String(n);
|
|
53
|
+
if (n < 10000)
|
|
54
|
+
return (n / 1000).toFixed(1) + 'k';
|
|
55
|
+
return Math.round(n / 1000) + 'k';
|
|
56
|
+
}
|
|
57
|
+
/** Build token usage text: "↑12k ↓3k Σ15k" */
|
|
58
|
+
function buildTokenText(input, output) {
|
|
59
|
+
return `↑${formatTokens(input)} ↓${formatTokens(output)} Σ${formatTokens(input + output)}`;
|
|
88
60
|
}
|
|
89
61
|
// ---------------------------------------------------------------------------
|
|
90
62
|
// Bridge
|
|
@@ -107,7 +79,6 @@ function writeBridge(session, remaining, usedPct) {
|
|
|
107
79
|
// ---------------------------------------------------------------------------
|
|
108
80
|
// Data readers
|
|
109
81
|
// ---------------------------------------------------------------------------
|
|
110
|
-
/** Read current in-progress task from Claude Code todos */
|
|
111
82
|
function readCurrentTask(session) {
|
|
112
83
|
const claudeDir = process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude');
|
|
113
84
|
const todosDir = join(claudeDir, 'todos');
|
|
@@ -133,8 +104,63 @@ function readCurrentTask(session) {
|
|
|
133
104
|
const emptyWf = {
|
|
134
105
|
milestone: '', currentPhase: 0, currentStep: 0, status: '',
|
|
135
106
|
total: 0, completed: 0, inProgress: 0, planned: 0, workspaceRoot: '',
|
|
107
|
+
chains: [], orphans: [], currentTaskId: '',
|
|
136
108
|
};
|
|
137
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* Build dependency chains from artifacts.
|
|
111
|
+
* Walk depends_on links: find roots (no depends_on), then follow forward.
|
|
112
|
+
*/
|
|
113
|
+
function buildChains(artifacts) {
|
|
114
|
+
if (artifacts.length === 0)
|
|
115
|
+
return { chains: [], orphans: [] };
|
|
116
|
+
const byId = new Map();
|
|
117
|
+
for (const a of artifacts)
|
|
118
|
+
byId.set(a.id, a);
|
|
119
|
+
// Build forward map: parent → children
|
|
120
|
+
const children = new Map();
|
|
121
|
+
const hasParent = new Set();
|
|
122
|
+
for (const a of artifacts) {
|
|
123
|
+
const deps = a.depends_on;
|
|
124
|
+
if (!deps)
|
|
125
|
+
continue;
|
|
126
|
+
const depIds = Array.isArray(deps) ? deps : [deps];
|
|
127
|
+
for (const depId of depIds) {
|
|
128
|
+
if (byId.has(depId)) {
|
|
129
|
+
hasParent.add(a.id);
|
|
130
|
+
const existing = children.get(depId) || [];
|
|
131
|
+
existing.push(a.id);
|
|
132
|
+
children.set(depId, existing);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Roots: artifacts with no parent in this set
|
|
137
|
+
const roots = artifacts.filter(a => !hasParent.has(a.id));
|
|
138
|
+
const visited = new Set();
|
|
139
|
+
const chains = [];
|
|
140
|
+
for (const root of roots) {
|
|
141
|
+
if (visited.has(root.id))
|
|
142
|
+
continue;
|
|
143
|
+
const chain = [];
|
|
144
|
+
let current = root.id;
|
|
145
|
+
while (current && !visited.has(current)) {
|
|
146
|
+
visited.add(current);
|
|
147
|
+
const art = byId.get(current);
|
|
148
|
+
if (art)
|
|
149
|
+
chain.push(art);
|
|
150
|
+
// Follow first child (linear chain)
|
|
151
|
+
const kids = children.get(current);
|
|
152
|
+
current = kids?.[0];
|
|
153
|
+
}
|
|
154
|
+
if (chain.length > 0) {
|
|
155
|
+
const allCompleted = chain.every(a => a.status === 'completed');
|
|
156
|
+
chains.push({ artifacts: chain, allCompleted });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Orphans: not visited by any chain walk
|
|
160
|
+
const orphans = artifacts.filter(a => !visited.has(a.id));
|
|
161
|
+
return { chains, orphans };
|
|
162
|
+
}
|
|
163
|
+
/** Read milestone + artifact chains from .workflow/state.json */
|
|
138
164
|
function readWorkflowState(dir) {
|
|
139
165
|
const root = findWorkspaceRoot(dir);
|
|
140
166
|
if (!root)
|
|
@@ -147,13 +173,67 @@ function readWorkflowState(dir) {
|
|
|
147
173
|
const result = { ...emptyWf, workspaceRoot: root };
|
|
148
174
|
if (state.current_milestone)
|
|
149
175
|
result.milestone = state.current_milestone;
|
|
150
|
-
if (state.current_phase)
|
|
151
|
-
result.currentPhase = state.current_phase;
|
|
152
|
-
if (state.current_step)
|
|
153
|
-
result.currentStep = state.current_step;
|
|
154
176
|
if (state.status)
|
|
155
177
|
result.status = state.status;
|
|
156
|
-
|
|
178
|
+
const rawArtifacts = Array.isArray(state.artifacts) ? state.artifacts : [];
|
|
179
|
+
const milestone = Array.isArray(state.milestones)
|
|
180
|
+
? state.milestones.find((m) => m.name === state.current_milestone || m.id === state.current_milestone)
|
|
181
|
+
: null;
|
|
182
|
+
const phases = milestone?.phases ?? [];
|
|
183
|
+
// Filter to current milestone artifacts
|
|
184
|
+
const msArtifacts = rawArtifacts
|
|
185
|
+
.filter(a => a.milestone === state.current_milestone && a.id && a.type && a.status)
|
|
186
|
+
.map(a => ({
|
|
187
|
+
id: a.id,
|
|
188
|
+
type: a.type,
|
|
189
|
+
status: a.status,
|
|
190
|
+
phase: a.phase ?? null,
|
|
191
|
+
path: a.path ?? '',
|
|
192
|
+
depends_on: a.depends_on ?? null,
|
|
193
|
+
}));
|
|
194
|
+
if (phases.length > 0 && msArtifacts.length > 0) {
|
|
195
|
+
result.total = phases.length;
|
|
196
|
+
let completed = 0, inProgress = 0, planned = 0;
|
|
197
|
+
for (const p of phases) {
|
|
198
|
+
const phaseArts = msArtifacts.filter(a => a.phase === p);
|
|
199
|
+
if (phaseArts.some(a => a.type === 'execute' && a.status === 'completed')) {
|
|
200
|
+
completed++;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (phaseArts.some(a => a.type === 'plan' && a.status === 'completed')) {
|
|
204
|
+
planned++;
|
|
205
|
+
inProgress++;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (phaseArts.length > 0) {
|
|
209
|
+
inProgress++;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
result.completed = completed;
|
|
213
|
+
result.inProgress = inProgress;
|
|
214
|
+
result.planned = planned;
|
|
215
|
+
// Current phase
|
|
216
|
+
for (const p of phases) {
|
|
217
|
+
if (msArtifacts.some(a => a.phase === p && a.status === 'in_progress')) {
|
|
218
|
+
result.currentPhase = p;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (!result.currentPhase) {
|
|
223
|
+
for (const p of phases) {
|
|
224
|
+
if (!msArtifacts.some(a => a.type === 'execute' && a.phase === p && a.status === 'completed')) {
|
|
225
|
+
result.currentPhase = p;
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Build chains
|
|
231
|
+
const { chains, orphans } = buildChains(msArtifacts);
|
|
232
|
+
result.chains = chains;
|
|
233
|
+
result.orphans = orphans;
|
|
234
|
+
}
|
|
235
|
+
else if (state.phases_summary) {
|
|
236
|
+
// v1 fallback
|
|
157
237
|
const s = state.phases_summary;
|
|
158
238
|
if (typeof s.total === 'number')
|
|
159
239
|
result.total = s.total;
|
|
@@ -161,17 +241,13 @@ function readWorkflowState(dir) {
|
|
|
161
241
|
result.completed = s.completed;
|
|
162
242
|
if (typeof s.in_progress === 'number')
|
|
163
243
|
result.inProgress = s.in_progress;
|
|
244
|
+
if (state.current_phase)
|
|
245
|
+
result.currentPhase = state.current_phase;
|
|
164
246
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const planPath = join(root, '.workflow', 'scratch', `P${p}`, 'plan.json');
|
|
170
|
-
if (existsSync(planPath))
|
|
171
|
-
planned++;
|
|
172
|
-
}
|
|
173
|
-
result.planned = planned;
|
|
174
|
-
}
|
|
247
|
+
if (state.current_step)
|
|
248
|
+
result.currentStep = state.current_step;
|
|
249
|
+
if (state.current_task_id)
|
|
250
|
+
result.currentTaskId = state.current_task_id;
|
|
175
251
|
return result;
|
|
176
252
|
}
|
|
177
253
|
catch {
|
|
@@ -207,21 +283,20 @@ function readGitInfo(dir) {
|
|
|
207
283
|
}
|
|
208
284
|
}
|
|
209
285
|
function formatGitSuffix(git) {
|
|
210
|
-
|
|
286
|
+
const parts = [];
|
|
211
287
|
if (git.conflict)
|
|
212
|
-
|
|
288
|
+
parts.push(GIT_ICONS.conflict);
|
|
213
289
|
else if (git.dirty)
|
|
214
|
-
|
|
215
|
-
else
|
|
216
|
-
status += GIT_ICONS.clean;
|
|
290
|
+
parts.push(GIT_ICONS.dirty);
|
|
217
291
|
if (git.ahead > 0)
|
|
218
|
-
|
|
292
|
+
parts.push(`${GIT_ICONS.ahead}${git.ahead}`);
|
|
219
293
|
if (git.behind > 0)
|
|
220
|
-
|
|
221
|
-
|
|
294
|
+
parts.push(`${GIT_ICONS.behind}${git.behind}`);
|
|
295
|
+
const suffix = parts.length > 0 ? ` ${parts.join('')}` : '';
|
|
296
|
+
return `${ICONS.git} ${git.branch}${suffix}`;
|
|
222
297
|
}
|
|
223
298
|
// ---------------------------------------------------------------------------
|
|
224
|
-
// Teammate activity segment
|
|
299
|
+
// Teammate activity segment
|
|
225
300
|
// ---------------------------------------------------------------------------
|
|
226
301
|
const TEAM_CACHE_TTL_MS = 10_000;
|
|
227
302
|
const TEAM_WINDOW_MIN = 30;
|
|
@@ -231,30 +306,23 @@ function teamCachePath(session) {
|
|
|
231
306
|
}
|
|
232
307
|
function writeTeamCache(path, segment) {
|
|
233
308
|
try {
|
|
234
|
-
|
|
235
|
-
writeFileSync(path, JSON.stringify(data));
|
|
236
|
-
}
|
|
237
|
-
catch {
|
|
238
|
-
// Best-effort
|
|
309
|
+
writeFileSync(path, JSON.stringify({ ts: Date.now(), segment }));
|
|
239
310
|
}
|
|
311
|
+
catch { /* best-effort */ }
|
|
240
312
|
return segment;
|
|
241
313
|
}
|
|
242
314
|
function shortTaskId(taskId) {
|
|
243
315
|
const idx = taskId.lastIndexOf('-');
|
|
244
|
-
|
|
245
|
-
return taskId;
|
|
246
|
-
return taskId.slice(idx + 1) || taskId;
|
|
316
|
+
return idx < 0 ? taskId : (taskId.slice(idx + 1) || taskId);
|
|
247
317
|
}
|
|
248
318
|
function formatTeammate(name, evt) {
|
|
249
319
|
if (typeof evt.phase_id === 'number' && typeof evt.task_id === 'string' && evt.task_id) {
|
|
250
320
|
return `${name} (P${evt.phase_id}/${shortTaskId(evt.task_id)})`;
|
|
251
321
|
}
|
|
252
|
-
if (typeof evt.phase_id === 'number')
|
|
322
|
+
if (typeof evt.phase_id === 'number')
|
|
253
323
|
return `${name} (P${evt.phase_id})`;
|
|
254
|
-
|
|
255
|
-
if (typeof evt.target === 'string' && evt.target) {
|
|
324
|
+
if (typeof evt.target === 'string' && evt.target)
|
|
256
325
|
return `${name} (${evt.target})`;
|
|
257
|
-
}
|
|
258
326
|
return name;
|
|
259
327
|
}
|
|
260
328
|
export function buildTeamSegment(session) {
|
|
@@ -263,16 +331,11 @@ export function buildTeamSegment(session) {
|
|
|
263
331
|
if (existsSync(cachePath)) {
|
|
264
332
|
try {
|
|
265
333
|
const cached = JSON.parse(readFileSync(cachePath, 'utf8'));
|
|
266
|
-
if (cached &&
|
|
267
|
-
typeof cached.ts === 'number' &&
|
|
268
|
-
typeof cached.segment === 'string' &&
|
|
269
|
-
Date.now() - cached.ts < TEAM_CACHE_TTL_MS) {
|
|
334
|
+
if (cached && typeof cached.ts === 'number' && typeof cached.segment === 'string' && Date.now() - cached.ts < TEAM_CACHE_TTL_MS) {
|
|
270
335
|
return cached.segment;
|
|
271
336
|
}
|
|
272
337
|
}
|
|
273
|
-
catch {
|
|
274
|
-
// Corrupt cache — recompute
|
|
275
|
-
}
|
|
338
|
+
catch { /* corrupt cache */ }
|
|
276
339
|
}
|
|
277
340
|
const self = resolveSelf();
|
|
278
341
|
if (!self)
|
|
@@ -294,9 +357,8 @@ export function buildTeamSegment(session) {
|
|
|
294
357
|
}
|
|
295
358
|
const prevT = Date.parse(prev.ts);
|
|
296
359
|
const curT = Date.parse(evt.ts);
|
|
297
|
-
if (!Number.isNaN(curT) && (Number.isNaN(prevT) || curT >= prevT))
|
|
360
|
+
if (!Number.isNaN(curT) && (Number.isNaN(prevT) || curT >= prevT))
|
|
298
361
|
latest.set(key, evt);
|
|
299
|
-
}
|
|
300
362
|
}
|
|
301
363
|
if (latest.size === 0)
|
|
302
364
|
return writeTeamCache(cachePath, '');
|
|
@@ -305,13 +367,12 @@ export function buildTeamSegment(session) {
|
|
|
305
367
|
const tb = Date.parse(b.ts);
|
|
306
368
|
return (Number.isNaN(tb) ? 0 : tb) - (Number.isNaN(ta) ? 0 : ta);
|
|
307
369
|
});
|
|
308
|
-
const inline = ordered.slice(0, TEAM_MAX_INLINE).map(
|
|
370
|
+
const inline = ordered.slice(0, TEAM_MAX_INLINE).map(evt => formatTeammate(evt.user, evt));
|
|
309
371
|
let body = inline.join(' | ');
|
|
310
372
|
const extra = ordered.length - inline.length;
|
|
311
373
|
if (extra > 0)
|
|
312
374
|
body += ` +${extra}`;
|
|
313
|
-
|
|
314
|
-
return writeTeamCache(cachePath, segment);
|
|
375
|
+
return writeTeamCache(cachePath, `\u{1F465} ${body}`);
|
|
315
376
|
}
|
|
316
377
|
catch {
|
|
317
378
|
return '';
|
|
@@ -333,7 +394,6 @@ export function buildCoordinatorSegment(session) {
|
|
|
333
394
|
const isPaused = status === 'paused' || status === 'step_paused';
|
|
334
395
|
const progress = isPaused ? 'P' : `${steps_completed}/${steps_total}`;
|
|
335
396
|
const stepLabel = current_step?.skill ?? '';
|
|
336
|
-
// chain_name → stepLabel [progress]
|
|
337
397
|
const parts = [];
|
|
338
398
|
if (chain_name)
|
|
339
399
|
parts.push(chain_name);
|
|
@@ -346,9 +406,73 @@ export function buildCoordinatorSegment(session) {
|
|
|
346
406
|
}
|
|
347
407
|
}
|
|
348
408
|
// ---------------------------------------------------------------------------
|
|
409
|
+
// Chain renderer — session chain for line 2+
|
|
410
|
+
// ---------------------------------------------------------------------------
|
|
411
|
+
/** Type abbreviation and color */
|
|
412
|
+
const TYPE_META = {
|
|
413
|
+
analyze: { abbr: 'A', color: TEXT_COLORS.model },
|
|
414
|
+
plan: { abbr: 'P', color: TEXT_COLORS.milestone },
|
|
415
|
+
execute: { abbr: 'E', color: TEXT_COLORS.phase },
|
|
416
|
+
verify: { abbr: 'V', color: TEXT_COLORS.coord },
|
|
417
|
+
brainstorm: { abbr: 'B', color: TEXT_COLORS.team },
|
|
418
|
+
spec: { abbr: 'S', color: TEXT_COLORS.dir },
|
|
419
|
+
review: { abbr: 'R', color: TEXT_COLORS.ctxAlert },
|
|
420
|
+
debug: { abbr: 'D', color: TEXT_COLORS.ctxCrit },
|
|
421
|
+
test: { abbr: 'T', color: TEXT_COLORS.ctxOk },
|
|
422
|
+
};
|
|
423
|
+
/** Color a type abbreviation */
|
|
424
|
+
function colorType(type) {
|
|
425
|
+
const meta = TYPE_META[type] ?? { abbr: type[0]?.toUpperCase() ?? '?', color: TEXT_COLORS.task };
|
|
426
|
+
return ansiFg(meta.color) + meta.abbr + ANSI_RESET;
|
|
427
|
+
}
|
|
428
|
+
/** Extract readable slug from artifact path */
|
|
429
|
+
function extractSlug(art) {
|
|
430
|
+
const b = basename(art.path || '');
|
|
431
|
+
// scratch/analyze-auth-2026-04-20 → auth
|
|
432
|
+
// phases/01-auth-multi-tenant → auth-multi-tenant
|
|
433
|
+
// scratch/20260421-review-P1-auth → auth
|
|
434
|
+
return b
|
|
435
|
+
.replace(/^\d+-/, '') // leading number prefix
|
|
436
|
+
.replace(/^\d{8}-/, '') // YYYYMMDD- prefix
|
|
437
|
+
.replace(/^(analyze|plan|execute|verify|brainstorm|spec|review|debug|test)-/, '') // type prefix
|
|
438
|
+
.replace(/-\d{4}-\d{2}-\d{2}$/, '') // trailing date
|
|
439
|
+
.replace(/-P\d+/, '') // -P1, -P2
|
|
440
|
+
|| art.type;
|
|
441
|
+
}
|
|
442
|
+
/** Status suffix */
|
|
443
|
+
function statusSuffix(status) {
|
|
444
|
+
const map = {
|
|
445
|
+
completed: { icon: '✓', color: TEXT_COLORS.ctxOk },
|
|
446
|
+
in_progress: { icon: '●', color: TEXT_COLORS.ctxWarn },
|
|
447
|
+
failed: { icon: '✗', color: TEXT_COLORS.ctxCrit },
|
|
448
|
+
pending: { icon: '○', color: TEXT_COLORS.separator },
|
|
449
|
+
};
|
|
450
|
+
const s = map[status];
|
|
451
|
+
return s ? ansiFg(s.color) + s.icon + ANSI_RESET : '';
|
|
452
|
+
}
|
|
453
|
+
/** Render chain: auth: A→P→E→R→D→T→V ✓ */
|
|
454
|
+
function renderChain(chain) {
|
|
455
|
+
const arrow = ansiFg(TEXT_COLORS.separator) + '→' + ANSI_RESET;
|
|
456
|
+
const slug = extractSlug(chain.artifacts[0]);
|
|
457
|
+
const types = chain.artifacts.map(a => colorType(a.type));
|
|
458
|
+
const lastArt = chain.artifacts[chain.artifacts.length - 1];
|
|
459
|
+
const slugLabel = ansiFg(TEXT_COLORS.task) + slug + ANSI_RESET;
|
|
460
|
+
const flow = types.join(arrow);
|
|
461
|
+
const suffix = chain.allCompleted
|
|
462
|
+
? ' ' + ansiFg(TEXT_COLORS.ctxOk) + '✓' + ANSI_RESET
|
|
463
|
+
: ' ' + statusSuffix(lastArt.status);
|
|
464
|
+
return `${slugLabel} ${flow}${suffix}`;
|
|
465
|
+
}
|
|
466
|
+
/** Render orphan: brainstorm-ux B ✓ */
|
|
467
|
+
function renderOrphan(art) {
|
|
468
|
+
const slug = extractSlug(art);
|
|
469
|
+
const slugLabel = ansiFg(TEXT_COLORS.task) + slug + ANSI_RESET;
|
|
470
|
+
return `${slugLabel} ${colorType(art.type)} ${statusSuffix(art.status)}`;
|
|
471
|
+
}
|
|
472
|
+
// ---------------------------------------------------------------------------
|
|
349
473
|
// Main formatter
|
|
350
474
|
// ---------------------------------------------------------------------------
|
|
351
|
-
/** Main statusline handler —
|
|
475
|
+
/** Main statusline handler — two-line output */
|
|
352
476
|
export function formatStatusline(data) {
|
|
353
477
|
const model = data.model?.display_name || 'Claude';
|
|
354
478
|
const dir = data.workspace?.current_dir || process.cwd();
|
|
@@ -366,98 +490,76 @@ export function formatStatusline(data) {
|
|
|
366
490
|
if (session)
|
|
367
491
|
writeBridge(session, remaining, usedPct);
|
|
368
492
|
}
|
|
369
|
-
// ----
|
|
493
|
+
// ---- Line 1: Model | Coord | Task | Team | Dir+Git | Context ----
|
|
370
494
|
const segments = [];
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
key: '
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
// 2. Milestone (conditional — shown when workflow has milestones)
|
|
379
|
-
if (wf.milestone) {
|
|
380
|
-
let msText = `${ICONS.milestone} ${wf.milestone}`;
|
|
381
|
-
if (wf.total > 0)
|
|
382
|
-
msText += ` ${wf.completed}/${wf.total}`;
|
|
383
|
-
segments.push({
|
|
384
|
-
key: 'milestone',
|
|
385
|
-
text: msText,
|
|
386
|
-
bg: SEGMENT_BG.milestone,
|
|
387
|
-
fg: SEGMENT_FG.milestone,
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
// 3. Phase (conditional — shows current phase + status detail)
|
|
391
|
-
if (wf.currentPhase) {
|
|
392
|
-
let phaseText = `${ICONS.phase} P${wf.currentPhase}`;
|
|
393
|
-
if (wf.currentStep)
|
|
394
|
-
phaseText += `.${wf.currentStep}`;
|
|
395
|
-
const tags = [];
|
|
396
|
-
if (wf.planned > 0)
|
|
397
|
-
tags.push(`${wf.planned}plan`);
|
|
398
|
-
if (wf.inProgress > 0)
|
|
399
|
-
tags.push(`${wf.inProgress}run`);
|
|
400
|
-
if (tags.length > 0)
|
|
401
|
-
phaseText += ` [${tags.join(' ')}]`;
|
|
402
|
-
segments.push({
|
|
403
|
-
key: 'phase',
|
|
404
|
-
text: phaseText,
|
|
405
|
-
bg: SEGMENT_BG.phase,
|
|
406
|
-
fg: SEGMENT_FG.phase,
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
// 4. Coordinator + chain (conditional)
|
|
410
|
-
if (coord) {
|
|
411
|
-
segments.push({
|
|
412
|
-
key: 'coord',
|
|
413
|
-
text: `${ICONS.coord} ${coord}`,
|
|
414
|
-
bg: SEGMENT_BG.coord,
|
|
415
|
-
fg: SEGMENT_FG.coord,
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
// 5. Task (conditional)
|
|
419
|
-
if (task) {
|
|
420
|
-
segments.push({
|
|
421
|
-
key: 'task',
|
|
422
|
-
text: `${ICONS.task} ${task}`,
|
|
423
|
-
bg: SEGMENT_BG.task,
|
|
424
|
-
fg: SEGMENT_FG.task,
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
// 6. Team (conditional)
|
|
428
|
-
if (team) {
|
|
429
|
-
segments.push({
|
|
430
|
-
key: 'team',
|
|
431
|
-
text: `${ICONS.team} ${team}`,
|
|
432
|
-
bg: SEGMENT_BG.team,
|
|
433
|
-
fg: SEGMENT_FG.team,
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
// 7. Directory + Git
|
|
495
|
+
segments.push({ key: 'model', text: `${ICONS.model} ${model}` });
|
|
496
|
+
if (coord)
|
|
497
|
+
segments.push({ key: 'coord', text: `${ICONS.coord} ${coord}` });
|
|
498
|
+
if (task)
|
|
499
|
+
segments.push({ key: 'task', text: `${ICONS.task} ${task}` });
|
|
500
|
+
if (team)
|
|
501
|
+
segments.push({ key: 'team', text: `${ICONS.team} ${team}` });
|
|
437
502
|
let dirText = `${ICONS.dir} ${basename(dir)}`;
|
|
438
503
|
if (git)
|
|
439
504
|
dirText += ` ${formatGitSuffix(git)}`;
|
|
440
|
-
segments.push({
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
505
|
+
segments.push({ key: 'dir', text: dirText });
|
|
506
|
+
// Token usage + lines changed
|
|
507
|
+
const inputTokens = data.context_window?.total_input_tokens;
|
|
508
|
+
const outputTokens = data.context_window?.total_output_tokens;
|
|
509
|
+
const linesAdded = data.cost?.total_lines_added ?? 0;
|
|
510
|
+
const linesRemoved = data.cost?.total_lines_removed ?? 0;
|
|
511
|
+
const statParts = [];
|
|
512
|
+
if (inputTokens != null && outputTokens != null && (inputTokens > 0 || outputTokens > 0)) {
|
|
513
|
+
statParts.push(buildTokenText(inputTokens, outputTokens));
|
|
514
|
+
}
|
|
515
|
+
if (linesAdded > 0 || linesRemoved > 0) {
|
|
516
|
+
const added = ansiFg(TEXT_COLORS.ctxOk) + `+${linesAdded}` + ANSI_RESET;
|
|
517
|
+
const removed = ansiFg(TEXT_COLORS.ctxCrit) + `-${linesRemoved}` + ANSI_RESET;
|
|
518
|
+
statParts.push(`${added} ${removed}`);
|
|
519
|
+
}
|
|
520
|
+
if (statParts.length > 0) {
|
|
521
|
+
segments.push({ key: 'task', text: statParts.join(' ') });
|
|
522
|
+
}
|
|
523
|
+
// Context bar
|
|
447
524
|
if (remaining != null) {
|
|
448
525
|
const level = getCtxLevel(usedPct);
|
|
449
|
-
const colors = getCtxColors(level);
|
|
450
526
|
const ctxKey = `ctx${level.charAt(0).toUpperCase()}${level.slice(1)}`;
|
|
451
|
-
segments.push({
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
527
|
+
segments.push({ key: ctxKey, text: buildContextText(usedPct) });
|
|
528
|
+
}
|
|
529
|
+
const line1 = renderColoredText(segments);
|
|
530
|
+
// ---- Line 2: Milestone ◆Phase | Session chains (conditional) ----
|
|
531
|
+
if (!wf.milestone)
|
|
532
|
+
return line1;
|
|
533
|
+
const sep = ansiFg(TEXT_COLORS.separator) + ' | ' + ANSI_RESET;
|
|
534
|
+
const dot = ansiFg(TEXT_COLORS.separator) + ' · ' + ANSI_RESET;
|
|
535
|
+
// Milestone + phase header
|
|
536
|
+
let header = ansiFg(TEXT_COLORS.milestone) + `${ICONS.milestone} ${wf.milestone}` + ANSI_RESET;
|
|
537
|
+
if (wf.total > 0)
|
|
538
|
+
header += ansiFg(TEXT_COLORS.milestone) + ` ${wf.completed}/${wf.total}` + ANSI_RESET;
|
|
539
|
+
if (wf.currentPhase)
|
|
540
|
+
header += ' ' + ansiFg(TEXT_COLORS.phase) + `${ICONS.phase} P${wf.currentPhase}` + ANSI_RESET;
|
|
541
|
+
// Session chains
|
|
542
|
+
const chainParts = [];
|
|
543
|
+
for (const chain of wf.chains) {
|
|
544
|
+
chainParts.push(renderChain(chain));
|
|
545
|
+
}
|
|
546
|
+
for (const orphan of wf.orphans) {
|
|
547
|
+
chainParts.push(renderOrphan(orphan));
|
|
548
|
+
}
|
|
549
|
+
if (chainParts.length === 0) {
|
|
550
|
+
return line1 + '\n' + header;
|
|
551
|
+
}
|
|
552
|
+
// Auto multi-line: ≤2 chains → single line, >2 → one chain per line
|
|
553
|
+
if (chainParts.length <= 2) {
|
|
554
|
+
const line2 = header + sep + chainParts.join(dot);
|
|
555
|
+
return line1 + '\n' + line2;
|
|
556
|
+
}
|
|
557
|
+
// Multi-line: header on line 2, each chain on its own line
|
|
558
|
+
const lines = [line1, header];
|
|
559
|
+
for (const part of chainParts) {
|
|
560
|
+
lines.push(' ' + part);
|
|
457
561
|
}
|
|
458
|
-
|
|
459
|
-
const style = getStatuslineStyle();
|
|
460
|
-
return style === 'powerline' ? renderPowerline(segments) : renderColoredText(segments);
|
|
562
|
+
return lines.join('\n');
|
|
461
563
|
}
|
|
462
564
|
/** Entry point — reads stdin JSON, writes formatted statusline to stdout */
|
|
463
565
|
export function runStatusline() {
|