throughline 0.10.5 → 0.10.7
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 +21 -1
- package/README.ja.md +5 -0
- package/README.md +6 -1
- package/bin/throughline.mjs +8 -0
- package/docs/04_public_release_plan.md +5 -1
- package/package.json +1 -1
- package/src/cli/help.test.mjs +2 -0
- package/src/cli/latest-session.mjs +70 -0
- package/src/cli/latest-session.test.mjs +64 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,24 @@ shipped to npm but were not individually tagged on GitHub.
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.10.7] — 2026-09-01
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `throughline latest-session --project <absolute-path> --json`で、指定した
|
|
18
|
+
projectだけの直近session IDをread-onlyで取得できるようにした。
|
|
19
|
+
BellTeamのような外部ランチャーは、別projectの記憶を混ぜずに既存の
|
|
20
|
+
`handoff-context`境界へ接続できる。
|
|
21
|
+
|
|
22
|
+
## [0.10.6] — 2026-09-01
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Document the one-time official npm bootstrap required when upgrading from
|
|
27
|
+
v0.10.4 or earlier, whose CLI predates `throughline self-update`. Once the
|
|
28
|
+
current CLI is installed, all later updates continue through the single
|
|
29
|
+
`throughline self-update` entry.
|
|
30
|
+
|
|
13
31
|
## [0.10.5] — 2026-09-01
|
|
14
32
|
|
|
15
33
|
### Added
|
|
@@ -1361,7 +1379,9 @@ two attempts, instrument first instead of patching again.
|
|
|
1361
1379
|
|
|
1362
1380
|
---
|
|
1363
1381
|
|
|
1364
|
-
[Unreleased]: https://github.com/kitepon/Throughline/compare/v0.10.
|
|
1382
|
+
[Unreleased]: https://github.com/kitepon/Throughline/compare/v0.10.7...HEAD
|
|
1383
|
+
[0.10.7]: https://github.com/kitepon/Throughline/compare/v0.10.6...v0.10.7
|
|
1384
|
+
[0.10.6]: https://github.com/kitepon/Throughline/compare/v0.10.5...v0.10.6
|
|
1365
1385
|
[0.10.5]: https://github.com/kitepon/Throughline/compare/v0.10.4...v0.10.5
|
|
1366
1386
|
[0.10.4]: https://github.com/kitepon/Throughline/compare/v0.10.3...v0.10.4
|
|
1367
1387
|
[0.10.3]: https://github.com/kitepon/Throughline/compare/v0.10.2...v0.10.3
|
package/README.ja.md
CHANGED
|
@@ -375,6 +375,10 @@ Throughline state をまだ書いていない現在セッションも表示で
|
|
|
375
375
|
| `throughline install --project` | 現リポジトリの `.claude/settings.json` だけに hook を登録 |
|
|
376
376
|
| `throughline self-update [--json]` | 公式npm package更新、公開PATHがその新CLI・versionを指すことの確認、製品所有連携の再適用、既存DB migration、公開diagnosticsの確認までを一回で実行 |
|
|
377
377
|
| `throughline uninstall` | hook を削除 |
|
|
378
|
+
|
|
379
|
+
v0.10.4以前には `self-update` が存在しない。該当版からの初回だけ
|
|
380
|
+
`npm install --global throughline@latest` を実行し、続けて
|
|
381
|
+
`throughline self-update` を実行する。以後の更新は `throughline self-update` だけで完結する。
|
|
378
382
|
| `throughline monitor` | マルチセッション監視を起動 |
|
|
379
383
|
| `throughline monitor --diag` | TTY/columns/env 診断ダンプ (描画バグ切り分け用) |
|
|
380
384
|
| `throughline detail <時刻>` | あるターンの L2 本文と L3 ツール I/O を取得 (Claude が使う) |
|
|
@@ -387,6 +391,7 @@ Throughline state をまだ書いていない現在セッションも表示で
|
|
|
387
391
|
| `throughline runtime-errors diagnostics --json` | collection/store状態をpathやraw errorなしで診断 |
|
|
388
392
|
| `throughline handoff-preview --session <id>` | Codex 向け `throughline_handoff` JSON projection を表示 |
|
|
389
393
|
| `throughline handoff-context --session <id> --json` | SessionStart と同じ引き継ぎ文脈を versioned JSON で取得。記憶行の `session_id` と `sessions.merged_into` は変更せず、同一端末内の別ベンダーランチャーから使える |
|
|
394
|
+
| `throughline latest-session --project <absolute-path> --json` | 指定した1プロジェクトだけを対象に直近セッションIDを読み取る。既存DBをread-onlyで開き、記録がなければ`empty`を返す |
|
|
390
395
|
| `throughline grok-continue --session <id>` | handoff-context を初手 user 文にした対話 Grok 席を立てる。cwd は源の `project_path`。ready でなければ spawn しない。`--rules` なし。macOS Terminal のみ |
|
|
391
396
|
| `throughline codex-sidecar-diagnostics` | この project の `codex-sidecar` diagnostics status を確認 |
|
|
392
397
|
| `throughline codex-sidecar-dry-run` | App Server を呼ばずに read-only sidecar request を正規化表示 |
|
package/README.md
CHANGED
|
@@ -817,7 +817,7 @@ entry to the `tasks` array yourself:
|
|
|
817
817
|
|
|
818
818
|
## Commands
|
|
819
819
|
|
|
820
|
-
**The current release is v0.10.
|
|
820
|
+
**The current release is v0.10.7.** Throughline supports Claude Code, Codex,
|
|
821
821
|
Grok, and Cursor as documented host adapters. The versioned, read-only
|
|
822
822
|
`handoff-context` boundary opens only an existing database and leaves baton
|
|
823
823
|
state, session ownership, and memory rows unchanged. Factory diagnostics,
|
|
@@ -830,6 +830,10 @@ under the same explicit-failure and local-only contracts.
|
|
|
830
830
|
| `throughline install --project` | Register Claude hooks/slash commands in this repo only |
|
|
831
831
|
| `throughline self-update [--json]` | Update the official npm package, verify that public PATH resolves to that new CLI/version, reapply product-owned integrations, migrate an existing database, and verify public diagnostics in one call |
|
|
832
832
|
| `throughline uninstall` | Remove Throughline-managed Claude hooks/slash commands, only the Throughline-managed Codex hook, and the `$throughline` Codex skill |
|
|
833
|
+
|
|
834
|
+
Versions before v0.10.5 do not contain `self-update`. Upgrade those versions
|
|
835
|
+
once with `npm install --global throughline@latest`, then run
|
|
836
|
+
`throughline self-update`. Later updates use `throughline self-update` alone.
|
|
833
837
|
| `throughline monitor [--all] [--session <id>]` | Run the multi-session token monitor |
|
|
834
838
|
| `throughline monitor --diag` | Dump TTY/columns/env diagnostics (for debugging monitor render bugs) |
|
|
835
839
|
| `throughline detail <time>` | Retrieve L2 body text and L3 tool I/O for a turn (see below) |
|
|
@@ -852,6 +856,7 @@ under the same explicit-failure and local-only contracts.
|
|
|
852
856
|
| `throughline runtime-errors compact --json` | Remove only acknowledged, resolved aggregates after retention; open or unacknowledged records remain |
|
|
853
857
|
| `throughline handoff-preview --session <id>` | Print a Codex-facing `throughline_handoff` JSON projection |
|
|
854
858
|
| `throughline handoff-context --session <id> --json` | Print the exact SessionStart inheritance context as versioned JSON without moving memory rows or changing `sessions.merged_into`; intended for local launchers that need portable cross-harness context |
|
|
859
|
+
| `throughline latest-session --project <absolute-path> --json` | Read the latest session id strictly scoped to one project; opens the existing database read-only and returns `empty` when the project has no captured session |
|
|
855
860
|
| `throughline grok-continue --session <id>` | Spawn a person-facing Grok seat whose first user text is the handoff-context body. cwd is the source session `project_path`. Does not spawn without ready context. No `--rules`. macOS Terminal only |
|
|
856
861
|
| `throughline codex-capture --codex-thread-id <id>` | Capture active Codex rollout turns into a `codex:<thread_id>` DB session |
|
|
857
862
|
| `throughline codex-summarize --session codex:<id>` | Summarize captured Codex L2 into L1 with the Codex CLI backend |
|
package/bin/throughline.mjs
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* throughline recall --l2|--l1 # 注入案内から辿る pull 用 read-only 記憶取得
|
|
13
13
|
* throughline handoff-preview # Codex-facing throughline_handoff JSON preview
|
|
14
14
|
* throughline handoff-context --session <id> --json # Read-only inheritance context JSON
|
|
15
|
+
* throughline latest-session --project <path> --json # Latest session strictly scoped to one project
|
|
15
16
|
* throughline grok-continue --session <id> # Spawn a Grok seat whose first user text is handoff-context
|
|
16
17
|
* throughline auditor-context --json # Read-only bounded auditor context JSON
|
|
17
18
|
* throughline factory-diagnostics --json # Native factory read-only readiness JSON
|
|
@@ -85,6 +86,11 @@ switch (cmd) {
|
|
|
85
86
|
if (exitCode !== 0) process.exitCode = exitCode;
|
|
86
87
|
break;
|
|
87
88
|
}
|
|
89
|
+
case 'latest-session': {
|
|
90
|
+
const exitCode = (await import('../src/cli/latest-session.mjs')).run(rest);
|
|
91
|
+
if (exitCode !== 0) process.exitCode = exitCode;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
88
94
|
case 'grok-continue': {
|
|
89
95
|
const exitCode = (await import('../src/cli/grok-continue.mjs')).run(rest);
|
|
90
96
|
if (exitCode !== 0) process.exitCode = exitCode;
|
|
@@ -233,6 +239,8 @@ Usage:
|
|
|
233
239
|
throughline handoff-context --session <id> --json
|
|
234
240
|
Print the exact inheritance context without
|
|
235
241
|
changing database ownership
|
|
242
|
+
throughline latest-session --project <absolute-path> --json
|
|
243
|
+
Read the latest session id for exactly one project
|
|
236
244
|
throughline grok-continue --session <id>
|
|
237
245
|
Spawn a person-facing Grok seat whose first
|
|
238
246
|
user text is the handoff-context body. Does not
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## 現行状態
|
|
8
8
|
|
|
9
|
-
- release candidateのpackage版は `0.10.
|
|
9
|
+
- release candidateのpackage版は `0.10.7`。tagとnpmの公開確認はpublish後に行う。
|
|
10
10
|
- Claude Code、Codex、Grok、Cursorをfirst-class hostとして扱う。
|
|
11
11
|
- 現行DB schemaはv9。schemaの正本は [src/db.mjs](../src/db.mjs) の
|
|
12
12
|
`CURRENT_VERSION`、二相handoffの判断は
|
|
@@ -29,6 +29,10 @@ Throughline は次の入口を自分で所有し、dotagentsが無くても利
|
|
|
29
29
|
| 復旧 | READMEのTroubleshootingと、診断が返す明示的な修復手順 |
|
|
30
30
|
| 更新 | `throughline self-update`。公式package更新、更新先global rootと公開PATHが同じ新CLI・versionを指すことの確認、host配線の再適用、既存DB migration、公開diagnosticsの`ready`確認までを一回で行う |
|
|
31
31
|
| 削除 | `throughline uninstall`。Throughline管理面だけを除去する |
|
|
32
|
+
|
|
33
|
+
v0.10.4以前からの初回更新だけは、その版に `self-update` が無いため
|
|
34
|
+
`npm install --global throughline@latest` の後に `throughline self-update` を実行する。
|
|
35
|
+
v0.10.5以降は `throughline self-update` だけを公開更新入口とする。
|
|
32
36
|
| release判断 | この文書のrelease gateと `scripts/verify-release-commit.mjs` |
|
|
33
37
|
|
|
34
38
|
dotagentsは工場への配線と統合結果を所有するが、ThroughlineのDB、schema、migration、
|
package/package.json
CHANGED
package/src/cli/help.test.mjs
CHANGED
|
@@ -52,6 +52,7 @@ test('CLI help exposes guided Codex handoff and guarded execute semantics', () =
|
|
|
52
52
|
assert.match(result.stdout, /--open-host auto\|desktop\|vscode\|cli\|none/);
|
|
53
53
|
assert.match(result.stdout, /throughline trim --execute/);
|
|
54
54
|
assert.match(result.stdout, /throughline handoff-context --session <id> --json/);
|
|
55
|
+
assert.match(result.stdout, /throughline latest-session --project <absolute-path> --json/);
|
|
55
56
|
assert.match(result.stdout, /without\s+changing database ownership/);
|
|
56
57
|
assert.match(result.stdout, /injectable DB memory/);
|
|
57
58
|
assert.match(result.stdout, /matching/);
|
|
@@ -77,4 +78,5 @@ test('CLI help Codex commands are dispatchable', () => {
|
|
|
77
78
|
test('CLI help exposes the dispatchable read-only handoff context boundary', () => {
|
|
78
79
|
const bin = readFileSync(BIN_PATH, 'utf8');
|
|
79
80
|
assert.match(bin, /case 'handoff-context':/);
|
|
81
|
+
assert.match(bin, /case 'latest-session':/);
|
|
80
82
|
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { sameProjectPath } from '../project-path.mjs';
|
|
7
|
+
|
|
8
|
+
export const LATEST_SESSION_SCHEMA = 'throughline.latest_session.v1';
|
|
9
|
+
|
|
10
|
+
export function parseArgs(argv = []) {
|
|
11
|
+
if (
|
|
12
|
+
argv.length !== 3 ||
|
|
13
|
+
argv[0] !== '--project' ||
|
|
14
|
+
typeof argv[1] !== 'string' ||
|
|
15
|
+
argv[1].length === 0 ||
|
|
16
|
+
argv[2] !== '--json'
|
|
17
|
+
) {
|
|
18
|
+
throw new TypeError('usage error');
|
|
19
|
+
}
|
|
20
|
+
return { projectPath: resolve(argv[1]) };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function findLatestSession(projectPath, {
|
|
24
|
+
dbPath = join(homedir(), '.throughline', 'throughline.db'),
|
|
25
|
+
} = {}) {
|
|
26
|
+
if (!existsSync(dbPath)) return null;
|
|
27
|
+
|
|
28
|
+
const db = new DatabaseSync(dbPath, { readOnly: true });
|
|
29
|
+
try {
|
|
30
|
+
const rows = db.prepare(
|
|
31
|
+
`SELECT session_id, project_path, updated_at
|
|
32
|
+
FROM sessions
|
|
33
|
+
ORDER BY updated_at DESC`,
|
|
34
|
+
).all();
|
|
35
|
+
return rows.find((row) => sameProjectPath(row.project_path, projectPath)) ?? null;
|
|
36
|
+
} finally {
|
|
37
|
+
db.close();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function run(argv = [], {
|
|
42
|
+
stdout = process.stdout,
|
|
43
|
+
stderr = process.stderr,
|
|
44
|
+
lookup = findLatestSession,
|
|
45
|
+
} = {}) {
|
|
46
|
+
let projectPath;
|
|
47
|
+
try {
|
|
48
|
+
({ projectPath } = parseArgs(argv));
|
|
49
|
+
} catch {
|
|
50
|
+
stderr.write('Usage: throughline latest-session --project <absolute-path> --json\n');
|
|
51
|
+
return 2;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let session;
|
|
55
|
+
try {
|
|
56
|
+
session = lookup(projectPath);
|
|
57
|
+
} catch {
|
|
58
|
+
stderr.write('Throughline latest session could not be read.\n');
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
stdout.write(`${JSON.stringify({
|
|
63
|
+
schema: LATEST_SESSION_SCHEMA,
|
|
64
|
+
status: session ? 'ready' : 'empty',
|
|
65
|
+
projectPath,
|
|
66
|
+
sessionId: session?.session_id ?? null,
|
|
67
|
+
updatedAt: session?.updated_at ?? null,
|
|
68
|
+
})}\n`);
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
6
|
+
import test from 'node:test';
|
|
7
|
+
|
|
8
|
+
import { findLatestSession, parseArgs, run } from './latest-session.mjs';
|
|
9
|
+
|
|
10
|
+
function output() {
|
|
11
|
+
let value = '';
|
|
12
|
+
return {
|
|
13
|
+
stream: { write(chunk) { value += chunk; } },
|
|
14
|
+
read() { return value; },
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
test('project pathを絶対化して受け取る', () => {
|
|
19
|
+
assert.equal(parseArgs(['--project', '.', '--json']).projectPath, process.cwd());
|
|
20
|
+
assert.throws(() => parseArgs(['--project', '.']));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('同じprojectの最新sessionだけを返す', () => {
|
|
24
|
+
const root = mkdtempSync(join(tmpdir(), 'throughline-latest-session-'));
|
|
25
|
+
const dbPath = join(root, 'throughline.db');
|
|
26
|
+
const db = new DatabaseSync(dbPath);
|
|
27
|
+
db.exec('CREATE TABLE sessions (session_id TEXT, project_path TEXT, updated_at TEXT)');
|
|
28
|
+
db.prepare('INSERT INTO sessions VALUES (?, ?, ?)').run('other-new', join(root, 'other'), '2026-09-01T03:00:00Z');
|
|
29
|
+
db.prepare('INSERT INTO sessions VALUES (?, ?, ?)').run('same-old', join(root, 'bot'), '2026-09-01T01:00:00Z');
|
|
30
|
+
db.prepare('INSERT INTO sessions VALUES (?, ?, ?)').run('same-new', join(root, 'bot'), '2026-09-01T02:00:00Z');
|
|
31
|
+
db.close();
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
assert.equal(findLatestSession(join(root, 'bot'), { dbPath }).session_id, 'same-new');
|
|
35
|
+
assert.equal(findLatestSession(join(root, 'missing'), { dbPath }), null);
|
|
36
|
+
} finally {
|
|
37
|
+
rmSync(root, { recursive: true, force: true });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('readyとemptyをJSONで返す', () => {
|
|
42
|
+
const stdout = output();
|
|
43
|
+
const stderr = output();
|
|
44
|
+
assert.equal(run(['--project', '/bots/a', '--json'], {
|
|
45
|
+
stdout: stdout.stream,
|
|
46
|
+
stderr: stderr.stream,
|
|
47
|
+
lookup: () => ({ session_id: 'claude:abc', updated_at: 'now' }),
|
|
48
|
+
}), 0);
|
|
49
|
+
assert.deepEqual(JSON.parse(stdout.read()), {
|
|
50
|
+
schema: 'throughline.latest_session.v1',
|
|
51
|
+
status: 'ready',
|
|
52
|
+
projectPath: '/bots/a',
|
|
53
|
+
sessionId: 'claude:abc',
|
|
54
|
+
updatedAt: 'now',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const empty = output();
|
|
58
|
+
assert.equal(run(['--project', '/bots/b', '--json'], {
|
|
59
|
+
stdout: empty.stream,
|
|
60
|
+
stderr: stderr.stream,
|
|
61
|
+
lookup: () => null,
|
|
62
|
+
}), 0);
|
|
63
|
+
assert.equal(JSON.parse(empty.read()).status, 'empty');
|
|
64
|
+
});
|