kankaku 0.6.5 → 0.7.0
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/README.md +59 -11
- package/package.json +1 -1
- package/src/adapters/hub-actions.ts +83 -0
- package/src/adapters/kankaku-command.ts +193 -243
- package/src/adapters/panel/kankaku-panel.ts +274 -0
- package/src/adapters/panel/panel-items.ts +77 -0
- package/src/adapters/panel/panel-lines.ts +13 -0
- package/src/adapters/panel/panel-theme.ts +32 -0
- package/src/adapters/panel/screens/about.ts +69 -0
- package/src/adapters/panel/screens/doctor.ts +85 -0
- package/src/adapters/panel/screens/export.ts +119 -0
- package/src/adapters/panel/screens/report.ts +141 -0
- package/src/adapters/panel/screens/sync.ts +126 -0
- package/src/adapters/panel/screens/target.ts +367 -0
- package/src/adapters/pi-tracker.ts +108 -6
- package/src/adapters/report-views.ts +98 -0
- package/src/adapters/session-target.ts +51 -4
- package/src/domain/panel-model.ts +267 -0
- package/src/extension.ts +18 -8
package/README.md
CHANGED
|
@@ -687,17 +687,53 @@ the future, this is the signal that would surface it.
|
|
|
687
687
|
|
|
688
688
|
## The `/kankaku` command
|
|
689
689
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
`
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
the
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
-
|
|
690
|
+
`/kankaku` with no arguments, run inside pi's TUI, opens an overlay panel
|
|
691
|
+
modelled on pi's own `/settings` (the same pi-tui `SettingsList`/
|
|
692
|
+
`SelectList` widgets, the same keys) from which every kankaku view and
|
|
693
|
+
action is reachable:
|
|
694
|
+
|
|
695
|
+
- **Target** — billing client, project, hub task, and the legacy label
|
|
696
|
+
(see "Billing labels" and "Hub (PocketBase)" below). The Task row lists
|
|
697
|
+
the open/doing hub tasks of the current project; picking one links the
|
|
698
|
+
session, exactly like `/kankaku task pick` below — **session-only**,
|
|
699
|
+
never persisted (see "Linking to a hub task").
|
|
700
|
+
- **Report** — today/all totals, tasks, sessions, clients, and projects:
|
|
701
|
+
the same five views `/kankaku`'s subcommands produce.
|
|
702
|
+
- **Sync** (hub only) — status, sync now, sync all, backfill, and a
|
|
703
|
+
catalog refresh.
|
|
704
|
+
- **Export** — write today's or every task as csv/json.
|
|
705
|
+
- **Doctor** — orphan/uncertain subagent counts and ancestor-detection
|
|
706
|
+
availability.
|
|
707
|
+
- **About** — versions, the resolved `KANKAKU_DIR`, the hub URL, and every
|
|
708
|
+
env-only setting, read-only.
|
|
709
|
+
|
|
710
|
+
Keys: `↑↓` move, `Enter` open a section or select a value, `Esc` go back
|
|
711
|
+
(or close the panel at the root), `q` close from anywhere. Mouse: the
|
|
712
|
+
footer hints and list rows are clickable, but only in pi's fullscreen
|
|
713
|
+
mode — pi does not dispatch mouse events in its regular (non-fullscreen)
|
|
714
|
+
mode, so there the panel is keyboard-only.
|
|
715
|
+
|
|
716
|
+
Every subcommand below is unchanged, and is exactly what headless (print
|
|
717
|
+
or RPC) mode still uses — `/kankaku` there keeps showing today's totals
|
|
718
|
+
directly, never the panel, since there is no UI to open one in. Run
|
|
719
|
+
`/kankaku <subcommand>` (with arguments) in the TUI to skip the panel and
|
|
720
|
+
go straight to that subcommand's output, exactly as before the panel
|
|
721
|
+
existed.
|
|
722
|
+
|
|
723
|
+
Plain `/kankaku` (headless, or any subcommand below) shows today's totals
|
|
724
|
+
(work, waiting, record count) per role, plus a union-based tasks segment.
|
|
725
|
+
Each totals line also shows `cache hit NN%` when tokens were recorded: the
|
|
726
|
+
share of prompt input tokens served from the provider's prompt cache, cache
|
|
727
|
+
reads over input plus cache reads plus cache writes; the segment is
|
|
728
|
+
omitted, not shown as `0%`, when no tokens were recorded. In the
|
|
729
|
+
interactive TUI the report is appended to the chat transcript as a durable
|
|
730
|
+
card that is never sent to the LLM; without a UI (print or RPC mode) it
|
|
731
|
+
falls back to a notification. Arguments are whitespace-separated and
|
|
732
|
+
order-insensitive:
|
|
733
|
+
|
|
734
|
+
- `/kankaku` (headless only — the TUI opens the panel instead, whose
|
|
735
|
+
Report screen defaults to the same view) — today's role totals and
|
|
736
|
+
tasks segment, each with its estimated cost.
|
|
701
737
|
- `/kankaku all` — same, but across every record.
|
|
702
738
|
- `/kankaku tasks` — one line per task (time, union wall/work, cost,
|
|
703
739
|
subagent count, truncated prompt) for the **current pi session**. Add `all` for
|
|
@@ -834,6 +870,12 @@ task view exposes it from the orchestrator record only.
|
|
|
834
870
|
The status bar shows `💼 <client> · <project>` (or just `💼 <client>` without
|
|
835
871
|
a project) in place of the legacy client label, both idle and during a run.
|
|
836
872
|
|
|
873
|
+
Mid-session, the `/kankaku` panel's Target screen (see "The `/kankaku`
|
|
874
|
+
command" above) is the interactive way to change the client or project
|
|
875
|
+
without going through `/kankaku target pick`'s picker dialog — it edits the
|
|
876
|
+
same session-level target, applies through the same `SessionTarget`, and
|
|
877
|
+
has its own "Remember" row for `<KANKAKU_DIR>/config.json`.
|
|
878
|
+
|
|
837
879
|
### Linking to a hub task
|
|
838
880
|
|
|
839
881
|
`/kankaku task` (or `/kankaku task pick`) links the current session to one
|
|
@@ -861,6 +903,12 @@ the task view exposes it from the orchestrator record only, and
|
|
|
861
903
|
`formatWorkTargetLabel` appends it to the status-bar/report label as
|
|
862
904
|
`<client> · <project> › <task title>`.
|
|
863
905
|
|
|
906
|
+
The `/kankaku` panel's Target screen's Task row is the interactive
|
|
907
|
+
equivalent of `/kankaku task pick`/`clear`: it lists the same open/doing
|
|
908
|
+
tasks, applies the link through the same `SessionTarget.setTask`, and
|
|
909
|
+
stays just as session-only — picking a task there is never persisted to
|
|
910
|
+
`config.json` either.
|
|
911
|
+
|
|
864
912
|
### Caching and offline behaviour
|
|
865
913
|
|
|
866
914
|
The catalog (clients/projects) is cached machine-wide at
|
package/package.json
CHANGED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared line-building for the hub-facing `/kankaku` subcommands (`sync`,
|
|
3
|
+
* `sync all`, `sync status`, `backfill`, `catalog refresh`) and the panel's
|
|
4
|
+
* `sync`/`export` screens (`adapters/panel/screens/sync.ts`), extracted from
|
|
5
|
+
* `kankaku-command.ts` so the subcommands and the panel can never drift —
|
|
6
|
+
* see odd/tasks/kankaku-panel.md P4.
|
|
7
|
+
*/
|
|
8
|
+
import type { CatalogSnapshot } from "../ports/catalog.ts";
|
|
9
|
+
import type { SyncCommandDeps } from "./kankaku-command.ts";
|
|
10
|
+
import type { SyncSummary } from "./sync-runner.ts";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `/kankaku sync status`'s report lines: the watermark, the pending count,
|
|
14
|
+
* how many tasks fell outside this run's revisit window (R3), and the last
|
|
15
|
+
* error, when any. Exact body of `handleSyncCommand`'s `status` branch.
|
|
16
|
+
*/
|
|
17
|
+
export function buildSyncStatusLines(status: ReturnType<SyncCommandDeps["status"]>): string[] {
|
|
18
|
+
const { state, pending, staleOutsideWindow } = status;
|
|
19
|
+
const lines = [state?.syncedThrough ? `synced through ${state.syncedThrough}` : "never synced", `pending: ${pending}`];
|
|
20
|
+
if (staleOutsideWindow > 0) {
|
|
21
|
+
lines.push(`${staleOutsideWindow} task(s) never synced fall outside the sync window — run '/kankaku sync all' to upload them`);
|
|
22
|
+
}
|
|
23
|
+
if (state?.lastError) lines.push(`last error: ${state.lastError.message} (at ${state.lastError.at})`);
|
|
24
|
+
return lines;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Render a {@link SyncSummary} as report lines: counts, any stop reason, the
|
|
29
|
+
* new watermark, and the unassigned/failed breakdowns. Used by `/kankaku
|
|
30
|
+
* sync`/`sync all` and the panel's sync screen.
|
|
31
|
+
*/
|
|
32
|
+
export function formatSyncSummaryLines(summary: SyncSummary): string[] {
|
|
33
|
+
const lines = [`uploaded ${summary.uploaded}, updated ${summary.updated}, skipped ${summary.skipped}, failed ${summary.failed.length}`];
|
|
34
|
+
|
|
35
|
+
if (summary.locked) lines.push("another sync is already in progress; nothing was attempted");
|
|
36
|
+
if (summary.error) lines.push(`stopped early: ${summary.error}`);
|
|
37
|
+
if (summary.syncedThrough) lines.push(`synced through ${summary.syncedThrough}`);
|
|
38
|
+
|
|
39
|
+
const unassignedEntries = Object.entries(summary.unassigned).sort(([a], [b]) => a.localeCompare(b));
|
|
40
|
+
if (unassignedEntries.length > 0) {
|
|
41
|
+
lines.push("unassigned (Sin determinar):");
|
|
42
|
+
for (const [label, count] of unassignedEntries) lines.push(` ${label}: ${count}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (summary.failed.length > 0) {
|
|
46
|
+
lines.push("failed:");
|
|
47
|
+
for (const entry of summary.failed) lines.push(` ${entry.id}: ${entry.reason}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return lines;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* `/kankaku backfill`'s report lines: tasks routed to Sin determinar this
|
|
55
|
+
* run, grouped by their old free-text label, or "no unassigned tasks" when
|
|
56
|
+
* none were. Exact body of `handleBackfillCommand`.
|
|
57
|
+
*/
|
|
58
|
+
export function formatBackfillLines(summary: SyncSummary): string[] {
|
|
59
|
+
const unassignedEntries = Object.entries(summary.unassigned).sort(([a], [b]) => a.localeCompare(b));
|
|
60
|
+
|
|
61
|
+
const lines =
|
|
62
|
+
unassignedEntries.length > 0
|
|
63
|
+
? [
|
|
64
|
+
...unassignedEntries.map(([label, count]) => `${label}: ${count} task(s) -> Sin determinar`),
|
|
65
|
+
"reassign these in the hub web app's unassigned queue",
|
|
66
|
+
]
|
|
67
|
+
: ["no unassigned tasks"];
|
|
68
|
+
|
|
69
|
+
if (summary.error) lines.push(`stopped early: ${summary.error}`);
|
|
70
|
+
return lines;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* `/kankaku catalog refresh`'s report lines: client/project counts, or an
|
|
75
|
+
* unreachable notice when the refresh failed (`snapshot` is `undefined`).
|
|
76
|
+
* The subcommand still notifies that failure as an error rather than a
|
|
77
|
+
* report (see `handleCatalogCommand`); the panel's sync screen shows
|
|
78
|
+
* whatever this returns either way.
|
|
79
|
+
*/
|
|
80
|
+
export function formatCatalogRefreshLines(snapshot: CatalogSnapshot | undefined): string[] {
|
|
81
|
+
if (!snapshot) return ["hub unreachable; catalog not refreshed"];
|
|
82
|
+
return [`refreshed: ${snapshot.clients.length} client(s), ${snapshot.projects.length} project(s)`];
|
|
83
|
+
}
|