kankaku-claude 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +5 -0
- package/commands/task.md +2 -2
- package/dist/catalog-refresh.js +21 -1
- package/dist/task-cli.js +18 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## Unreleased
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `/kankaku:task new <title> [--ref <externalRef>]` creates an open task in
|
|
12
|
+
the resolved project and links it session-only, caching the returned task
|
|
13
|
+
immediately. Creation stays out of hooks.
|
|
14
|
+
|
|
7
15
|
## 1.4.0 — 2026-09-30
|
|
8
16
|
|
|
9
17
|
### Added
|
package/README.md
CHANGED
|
@@ -297,6 +297,11 @@ for the folder, so the records written from then on carry that task:
|
|
|
297
297
|
`/kankaku:task <id>` by hub id, and `/kankaku:task <text>` by a unique
|
|
298
298
|
case-insensitive part of the title (several matches are listed and nothing
|
|
299
299
|
changes). A purely numeric argument is always a list number.
|
|
300
|
+
- `/kankaku:task new <title> [--ref <externalRef>]` creates an open task
|
|
301
|
+
in the resolved project and links it to this session. The title is required;
|
|
302
|
+
`--ref` takes one argument (quote references containing spaces). Creation
|
|
303
|
+
requires a reachable hub; failure leaves the existing link unchanged.
|
|
304
|
+
The returned task is cached immediately so record resolution retains its id.
|
|
300
305
|
- `/kankaku:task clear` removes the link.
|
|
301
306
|
|
|
302
307
|
The link is session-only, like `/kankaku task pick` in pi: it is stored in
|
package/commands/task.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
3
|
-
argument-hint: [number | id | text | clear]
|
|
2
|
+
description: Create or link a hub task for this session, list open tasks, or clear the link
|
|
3
|
+
argument-hint: [number | id | text | clear | new <title> [--ref <externalRef>]]
|
|
4
4
|
allowed-tools: Bash(node:*)
|
|
5
5
|
---
|
|
6
6
|
|
package/dist/catalog-refresh.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import { CachedCatalog, PocketBaseClient, createPocketBaseCatalogFetcher, resolveHubCredentials } from "kankaku-pi/hub";
|
|
2
|
+
import { CachedCatalog, PocketBaseClient, createHubTask, createPocketBaseCatalogFetcher, resolveHubCredentials } from "kankaku-pi/hub";
|
|
3
3
|
const DEFAULT_REFRESH_MS = 3000;
|
|
4
4
|
/**
|
|
5
5
|
* Best-effort refresh bounded by `catalogTimeoutMs`; on failure the cached
|
|
@@ -32,6 +32,26 @@ export async function refreshCatalog(deps, homeDir) {
|
|
|
32
32
|
return undefined;
|
|
33
33
|
return { snapshot: cached, text: `catalog from cache, ${formatAge(deps.now() - cached.fetchedAt)} old` };
|
|
34
34
|
}
|
|
35
|
+
/** Explicit task command only: POST and adopt before the session link is written. */
|
|
36
|
+
export async function createCatalogTask(deps, homeDir, projectId, title, externalRef) {
|
|
37
|
+
const hub = resolveHubCredentials({ env: deps.env, homeDir: () => homeDir });
|
|
38
|
+
if (!hub.credentials)
|
|
39
|
+
throw new Error("hub credentials are not configured");
|
|
40
|
+
const client = new PocketBaseClient({ ...hub.credentials, ...(deps.fetch ? { fetch: deps.fetch } : {}) });
|
|
41
|
+
const catalog = new CachedCatalog({ filePath: join(homeDir, ".kankaku", "catalog.json"), url: hub.credentials.url, clock: { now: deps.now }, fetchCatalog: createPocketBaseCatalogFetcher(client) });
|
|
42
|
+
if (!catalog.read())
|
|
43
|
+
throw new Error("no catalog cache");
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const timer = setTimeout(() => controller.abort(), deps.catalogTimeoutMs ?? DEFAULT_REFRESH_MS);
|
|
46
|
+
try {
|
|
47
|
+
const task = await createHubTask(client, { projectId, title, ...(externalRef ? { externalRef } : {}) }, controller.signal);
|
|
48
|
+
catalog.adoptTask(task);
|
|
49
|
+
return task;
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
35
55
|
function formatAge(ms) {
|
|
36
56
|
const seconds = Math.max(0, Math.round(ms / 1000));
|
|
37
57
|
if (seconds < 60)
|
package/dist/task-cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { refreshCatalog } from "./catalog-refresh.js";
|
|
3
|
+
import { createCatalogTask, refreshCatalog } from "./catalog-refresh.js";
|
|
4
4
|
import { findSession } from "./find-session.js";
|
|
5
5
|
import { resolveKankakuDir, resolveTargetFile } from "./paths.js";
|
|
6
6
|
import { readSessionTarget, sessionInputs, withoutTaskLink, writeSessionTarget } from "./session-target-store.js";
|
|
@@ -28,6 +28,13 @@ export async function runTaskCli(args, deps) {
|
|
|
28
28
|
const file = resolveTargetFile(claudeDir, session);
|
|
29
29
|
const stored = readSessionTarget(file) ?? { lastList: { kind: "tasks", ids: [] } };
|
|
30
30
|
const arg = args.join(" ").trim();
|
|
31
|
+
const creating = args[0] === "new";
|
|
32
|
+
const refIndex = args.indexOf("--ref");
|
|
33
|
+
const title = creating ? args.slice(1, refIndex === -1 ? undefined : refIndex).join(" ").trim() : "";
|
|
34
|
+
const externalRef = refIndex === -1 ? undefined : args[refIndex + 1]?.trim();
|
|
35
|
+
if (creating && (!title || (refIndex !== -1 && (!externalRef || refIndex !== args.length - 2)))) {
|
|
36
|
+
return say(["usage: /kankaku:task new <title> [--ref <externalRef>]"], 1);
|
|
37
|
+
}
|
|
31
38
|
if (arg === "clear") {
|
|
32
39
|
if (stored.hubTaskId === undefined)
|
|
33
40
|
return say(["no task linked"], 0);
|
|
@@ -50,6 +57,16 @@ export async function runTaskCli(args, deps) {
|
|
|
50
57
|
}
|
|
51
58
|
const projectName = resolved.target.projectName ?? projectId;
|
|
52
59
|
const open = listOpenTasks(tasks, projectId);
|
|
60
|
+
if (creating) {
|
|
61
|
+
try {
|
|
62
|
+
const task = await createCatalogTask(deps, homeDir, projectId, title, externalRef);
|
|
63
|
+
writeSessionTarget(file, { ...stored, hubTaskId: task.id, hubTaskTitle: task.title, projectId, pickedAt: deps.now() });
|
|
64
|
+
return say([...prefix, `created and linked: ${task.title}`], 0);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return say([...prefix, "task creation failed; existing session link unchanged"], 1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
53
70
|
if (arg === "" || arg === "list") {
|
|
54
71
|
if (open.length === 0)
|
|
55
72
|
return say([...prefix, `no open tasks in project ${projectName}`], 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kankaku-claude",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Claude Code plugin that records how long the agent works on each user prompt, in kankaku's worklog format.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"prepublishOnly": "npm run check"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"kankaku-pi": "^1.4.
|
|
30
|
+
"kankaku-pi": "^1.4.1"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^24.13.4",
|