tim-hooks 0.1.0-beta.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.
Files changed (73) hide show
  1. package/README.md +31 -0
  2. package/dist/cadence-runner.d.ts +11 -0
  3. package/dist/cadence-runner.d.ts.map +1 -0
  4. package/dist/cadence-runner.js +30 -0
  5. package/dist/cadence-runner.js.map +1 -0
  6. package/dist/cadence.d.ts +10 -0
  7. package/dist/cadence.d.ts.map +1 -0
  8. package/dist/cadence.js +36 -0
  9. package/dist/cadence.js.map +1 -0
  10. package/dist/checkpoint.d.ts +56 -0
  11. package/dist/checkpoint.d.ts.map +1 -0
  12. package/dist/checkpoint.js +232 -0
  13. package/dist/checkpoint.js.map +1 -0
  14. package/dist/claude-stop.d.ts +32 -0
  15. package/dist/claude-stop.d.ts.map +1 -0
  16. package/dist/claude-stop.js +213 -0
  17. package/dist/claude-stop.js.map +1 -0
  18. package/dist/constants.d.ts +5 -0
  19. package/dist/constants.d.ts.map +1 -0
  20. package/dist/constants.js +8 -0
  21. package/dist/constants.js.map +1 -0
  22. package/dist/delta.d.ts +7 -0
  23. package/dist/delta.d.ts.map +1 -0
  24. package/dist/delta.js +53 -0
  25. package/dist/delta.js.map +1 -0
  26. package/dist/hooks.d.ts +39 -0
  27. package/dist/hooks.d.ts.map +1 -0
  28. package/dist/hooks.js +136 -0
  29. package/dist/hooks.js.map +1 -0
  30. package/dist/index.d.ts +13 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +88 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/marker.d.ts +179 -0
  35. package/dist/marker.d.ts.map +1 -0
  36. package/dist/marker.js +590 -0
  37. package/dist/marker.js.map +1 -0
  38. package/dist/phantom-recovery.d.ts +13 -0
  39. package/dist/phantom-recovery.d.ts.map +1 -0
  40. package/dist/phantom-recovery.js +113 -0
  41. package/dist/phantom-recovery.js.map +1 -0
  42. package/dist/project-creation.d.ts +48 -0
  43. package/dist/project-creation.d.ts.map +1 -0
  44. package/dist/project-creation.js +265 -0
  45. package/dist/project-creation.js.map +1 -0
  46. package/dist/prompt-submit.d.ts +16 -0
  47. package/dist/prompt-submit.d.ts.map +1 -0
  48. package/dist/prompt-submit.js +69 -0
  49. package/dist/prompt-submit.js.map +1 -0
  50. package/dist/rebalance.d.ts +17 -0
  51. package/dist/rebalance.d.ts.map +1 -0
  52. package/dist/rebalance.js +91 -0
  53. package/dist/rebalance.js.map +1 -0
  54. package/dist/session-hooks.d.ts +53 -0
  55. package/dist/session-hooks.d.ts.map +1 -0
  56. package/dist/session-hooks.js +167 -0
  57. package/dist/session-hooks.js.map +1 -0
  58. package/dist/update-check.d.ts +5 -0
  59. package/dist/update-check.d.ts.map +1 -0
  60. package/dist/update-check.js +66 -0
  61. package/dist/update-check.js.map +1 -0
  62. package/package.json +24 -0
  63. package/scripts/README-hermes-statusline.md +47 -0
  64. package/scripts/hermes-cli-tim-statusline.patch +75 -0
  65. package/scripts/lib/resolve-tim-cli.sh +30 -0
  66. package/scripts/post-commit.sh +30 -0
  67. package/scripts/tim-claude-session-start.sh +26 -0
  68. package/scripts/tim-cursor-inject.sh +17 -0
  69. package/scripts/tim-hermes-session-cache.sh +32 -0
  70. package/scripts/tim-hermes-statusline.sh +28 -0
  71. package/scripts/tim-post-commit.sh +14 -0
  72. package/scripts/tim-session-start.sh +104 -0
  73. package/scripts/tim-statusline.sh +16 -0
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rebalanceBatch = rebalanceBatch;
4
+ const tim_store_1 = require("tim-store");
5
+ const marker_js_1 = require("./marker.js");
6
+ const STOP_WORDS = new Set([
7
+ 'about', 'after', 'also', 'been', 'from', 'have', 'into', 'just', 'more', 'some',
8
+ 'than', 'that', 'the', 'their', 'them', 'then', 'there', 'these', 'they', 'this',
9
+ 'those', 'very', 'what', 'when', 'which', 'with', 'your',
10
+ ]);
11
+ function exchangeSummaryText(entry) {
12
+ return `${entry.title} ${entry.content}`.trim();
13
+ }
14
+ function keywords(text) {
15
+ const out = new Set();
16
+ for (const raw of text.toLowerCase().split(/[^a-z0-9]+/)) {
17
+ if (raw.length >= 4 && !STOP_WORDS.has(raw))
18
+ out.add(raw);
19
+ }
20
+ return out;
21
+ }
22
+ function shareKeywords(a, b) {
23
+ const ka = keywords(exchangeSummaryText(a));
24
+ const kb = keywords(exchangeSummaryText(b));
25
+ for (const k of ka) {
26
+ if (kb.has(k))
27
+ return true;
28
+ }
29
+ return false;
30
+ }
31
+ async function userExchanges(store, batchId) {
32
+ return (await store.getChildrenBySeq(batchId)).filter(u => u.metadata.role === 'user');
33
+ }
34
+ async function agentReply(store, user) {
35
+ const replies = await store.getChildren(user.id);
36
+ return replies.find(r => r.metadata.role === 'agent') ?? null;
37
+ }
38
+ /**
39
+ * Scan exchange-batch boundaries; move the first exchange of batch N (+ agent reply)
40
+ * into batch N-1 when it is topically related to the last exchange of N-1.
41
+ */
42
+ async function rebalanceBatch(store, sessionId, opts = {}) {
43
+ const skipped = [];
44
+ let resolvedCwd = opts.cwd;
45
+ if (!resolvedCwd) {
46
+ const session = await store.read(sessionId);
47
+ if (typeof session?.metadata.cwd === 'string')
48
+ resolvedCwd = session.metadata.cwd;
49
+ }
50
+ if (resolvedCwd && (0, marker_js_1.isSessionLocked)(resolvedCwd)) {
51
+ return { moved: 0, skipped: [{ reason: 'locked' }] };
52
+ }
53
+ const session = await store.read(sessionId);
54
+ if (!session || session.metadata.kind !== 'session') {
55
+ throw new Error(`Session not found: ${sessionId}`);
56
+ }
57
+ const exNode = await (0, tim_store_1.findChildByKind)(store, sessionId, tim_store_1.KIND_EXCHANGES_ROOT);
58
+ if (!exNode)
59
+ return { moved: 0, skipped };
60
+ const batches = await store.getChildByKind(exNode.id, tim_store_1.KIND_EXCHANGE_BATCH);
61
+ if (batches.length < 2) {
62
+ return { moved: 0, skipped: [{ reason: 'no-boundary' }] };
63
+ }
64
+ let moved = 0;
65
+ for (let i = 1; i < batches.length; i++) {
66
+ const prev = batches[i - 1];
67
+ const curr = batches[i];
68
+ const batchIndex = typeof curr.metadata.batch_index === 'number' ? curr.metadata.batch_index : i + 1;
69
+ const prevUsers = await userExchanges(store, prev.id);
70
+ const currUsers = await userExchanges(store, curr.id);
71
+ if (currUsers.length < 2) {
72
+ skipped.push({ reason: 'single-exchange-batch', batchIndex });
73
+ continue;
74
+ }
75
+ const lastPrev = prevUsers[prevUsers.length - 1];
76
+ const firstCurr = currUsers[0];
77
+ if (!lastPrev || !firstCurr)
78
+ continue;
79
+ if (!shareKeywords(lastPrev, firstCurr)) {
80
+ skipped.push({ reason: 'unrelated', batchIndex });
81
+ continue;
82
+ }
83
+ const agent = await agentReply(store, firstCurr);
84
+ store.curate().moveEntry(firstCurr.id, prev.id);
85
+ if (agent)
86
+ store.curate().moveEntry(agent.id, prev.id);
87
+ moved += 1;
88
+ }
89
+ return { moved, skipped };
90
+ }
91
+ //# sourceMappingURL=rebalance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebalance.js","sourceRoot":"","sources":["../src/rebalance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { TimStore } from 'tim-store';
2
+ export interface SpawnContext {
3
+ sessionId: string;
4
+ cwd: string;
5
+ }
6
+ export type Spawner = (command: string, ctx: SpawnContext) => void;
7
+ export type SessionStopReason = 'spawned' | 'no-marker' | 'below-threshold' | 'locked' | 'spawn-failed';
8
+ export interface SessionStopResult {
9
+ spawned: boolean;
10
+ reason: SessionStopReason;
11
+ pending?: number;
12
+ }
13
+ export { DEFAULT_SUMMARIZER_TIMEOUT_SEC } from './constants.js';
14
+ export declare function summarizerLogPath(cwd: string): string;
15
+ /** Shell snippet: trap lock release, timeout, run tim-summarizer CLI with log append. */
16
+ export declare function buildSummarizerCommand(sessionId: string, lockPath: string, logPath: string, timeoutSec?: number): string;
17
+ /** Detached spawn with log dir creation and spawn-error capture (does not throw). */
18
+ export declare const spawnSummarizer: Spawner;
19
+ /** @deprecated Use spawnSummarizer */
20
+ export declare const detachedSpawner: Spawner;
21
+ export interface MaybeSpawnSummarizerOptions {
22
+ spawn?: Spawner;
23
+ /** Skip pending threshold — use when a batch just filled (live trigger). */
24
+ batchFull?: boolean;
25
+ timeoutSec?: number;
26
+ }
27
+ /** Shared spawn gate for session-stop hook and live batch-full trigger. */
28
+ export declare function maybeSpawnSummarizer(store: TimStore, cwd: string, opts?: MaybeSpawnSummarizerOptions): Promise<SessionStopResult>;
29
+ export declare function onSessionStop(store: TimStore, cwd: string, opts?: {
30
+ spawn?: Spawner;
31
+ timeoutSec?: number;
32
+ }): Promise<SessionStopResult>;
33
+ export declare const DEFAULT_PROJECT_SUMMARY_THRESHOLD = 5;
34
+ /** Shell snippet: run tim-summarizer in --project-summary mode for a label. */
35
+ export declare function buildProjectSummaryCommand(label: string, logPath: string, timeoutSec?: number): string;
36
+ export type ProjectSummaryReason = 'spawned' | 'no-label' | 'no-sessions' | 'below-threshold' | 'spawn-failed';
37
+ export interface ProjectSummaryResult {
38
+ spawned: boolean;
39
+ reason: ProjectSummaryReason;
40
+ count?: number;
41
+ }
42
+ export interface MaybeSpawnProjectSummaryOptions {
43
+ spawn?: Spawner;
44
+ threshold?: number;
45
+ timeoutSec?: number;
46
+ }
47
+ /**
48
+ * Gate + detached spawn for periodic project-summary generation.
49
+ * Fires only when sessions-so-far is a positive multiple of the threshold.
50
+ * Fire-and-forget — never throws.
51
+ */
52
+ export declare function maybeSpawnProjectSummary(store: TimStore, cwd: string, label: string | null, opts?: MaybeSpawnProjectSummaryOptions): Promise<ProjectSummaryResult>;
53
+ //# sourceMappingURL=session-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-hooks.d.ts","sourceRoot":"","sources":["../src/session-hooks.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAU1C,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;AAEnE,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,WAAW,GACX,iBAAiB,GACjB,QAAQ,GACR,cAAc,CAAC;AAEnB,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,OAAO,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAC;AAEhE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,yFAAyF;AACzF,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,MAAuC,GAClD,MAAM,CAOR;AAED,qFAAqF;AACrF,eAAO,MAAM,eAAe,EAAE,OAkC7B,CAAC;AAEF,sCAAsC;AACtC,eAAO,MAAM,eAAe,EAAE,OAAyB,CAAC;AAExD,MAAM,WAAW,2BAA2B;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,2EAA2E;AAC3E,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,QAAQ,EACf,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,2BAAgC,GACrC,OAAO,CAAC,iBAAiB,CAAC,CA4B5B;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,QAAQ,EACf,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GAClD,OAAO,CAAC,iBAAiB,CAAC,CAE5B;AAED,eAAO,MAAM,iCAAiC,IAAI,CAAC;AAEnD,+EAA+E;AAC/E,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,MAAuC,GAClD,MAAM,CAIR;AAED,MAAM,MAAM,oBAAoB,GAC5B,SAAS,GACT,UAAU,GACV,aAAa,GACb,iBAAiB,GACjB,cAAc,CAAC;AAEnB,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,oBAAoB,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,+BAA+B;IAC9C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,QAAQ,EACf,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,IAAI,GAAE,+BAAoC,GACzC,OAAO,CAAC,oBAAoB,CAAC,CAqB/B"}
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DEFAULT_PROJECT_SUMMARY_THRESHOLD = exports.detachedSpawner = exports.spawnSummarizer = exports.DEFAULT_SUMMARIZER_TIMEOUT_SEC = void 0;
37
+ exports.summarizerLogPath = summarizerLogPath;
38
+ exports.buildSummarizerCommand = buildSummarizerCommand;
39
+ exports.maybeSpawnSummarizer = maybeSpawnSummarizer;
40
+ exports.onSessionStop = onSessionStop;
41
+ exports.buildProjectSummaryCommand = buildProjectSummaryCommand;
42
+ exports.maybeSpawnProjectSummary = maybeSpawnProjectSummary;
43
+ const child_process_1 = require("child_process");
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const marker_js_1 = require("./marker.js");
47
+ const constants_js_1 = require("./constants.js");
48
+ var constants_js_2 = require("./constants.js");
49
+ Object.defineProperty(exports, "DEFAULT_SUMMARIZER_TIMEOUT_SEC", { enumerable: true, get: function () { return constants_js_2.DEFAULT_SUMMARIZER_TIMEOUT_SEC; } });
50
+ function summarizerLogPath(cwd) {
51
+ return path.join(cwd, '.tim', 'summarizer.log');
52
+ }
53
+ /** Shell snippet: trap lock release, timeout, run tim-summarizer CLI with log append. */
54
+ function buildSummarizerCommand(sessionId, lockPath, logPath, timeoutSec = constants_js_1.DEFAULT_SUMMARIZER_TIMEOUT_SEC) {
55
+ const q = (s) => JSON.stringify(s);
56
+ const cmd = 'node ' + JSON.stringify(path.resolve(__dirname, '..', '..', 'tim-summarizer', 'dist', 'summarize.js'));
57
+ return (`{ trap ${q(`rm -f ${lockPath}`)} EXIT; ` +
58
+ `timeout ${timeoutSec} env TIM_SESSION_ID=${q(sessionId)} ${cmd} >>${q(logPath)} 2>&1; }`);
59
+ }
60
+ /** Detached spawn with log dir creation and spawn-error capture (does not throw). */
61
+ const spawnSummarizer = (command, ctx) => {
62
+ const timDir = path.join(ctx.cwd, '.tim');
63
+ try {
64
+ fs.mkdirSync(timDir, { recursive: true });
65
+ }
66
+ catch {
67
+ /* ignore */
68
+ }
69
+ const logPath = summarizerLogPath(ctx.cwd);
70
+ try {
71
+ const child = (0, child_process_1.spawn)(command, {
72
+ shell: true,
73
+ cwd: ctx.cwd,
74
+ detached: true,
75
+ stdio: 'ignore',
76
+ env: { ...process.env, TIM_SESSION_ID: ctx.sessionId },
77
+ });
78
+ child.on('error', err => {
79
+ try {
80
+ fs.appendFileSync(logPath, `[${new Date().toISOString()}] spawn error: ${err.message}\n`);
81
+ }
82
+ catch {
83
+ /* ignore */
84
+ }
85
+ (0, marker_js_1.releaseLock)(ctx.cwd);
86
+ });
87
+ child.unref();
88
+ }
89
+ catch (err) {
90
+ const msg = err instanceof Error ? err.message : String(err);
91
+ try {
92
+ fs.appendFileSync(logPath, `[${new Date().toISOString()}] spawn failed: ${msg}\n`);
93
+ }
94
+ catch {
95
+ /* ignore */
96
+ }
97
+ (0, marker_js_1.releaseLock)(ctx.cwd);
98
+ }
99
+ };
100
+ exports.spawnSummarizer = spawnSummarizer;
101
+ /** @deprecated Use spawnSummarizer */
102
+ exports.detachedSpawner = exports.spawnSummarizer;
103
+ /** Shared spawn gate for session-stop hook and live batch-full trigger. */
104
+ async function maybeSpawnSummarizer(store, cwd, opts = {}) {
105
+ const spawn = opts.spawn ?? exports.spawnSummarizer;
106
+ const marker = (0, marker_js_1.detectProject)(cwd);
107
+ if (!marker)
108
+ return { spawned: false, reason: 'no-marker' };
109
+ const reconciled = await (0, marker_js_1.reconcileMarker)(store, cwd);
110
+ const pending = reconciled.exchanges - reconciled.batches_summarized * reconciled.batch_size;
111
+ if (!opts.batchFull && pending < reconciled.batch_size) {
112
+ return { spawned: false, reason: 'below-threshold', pending };
113
+ }
114
+ if (!(0, marker_js_1.acquireLock)(cwd))
115
+ return { spawned: false, reason: 'locked', pending };
116
+ const lockPath = path.join(cwd, marker_js_1.MARKER_LOCK);
117
+ const logPath = summarizerLogPath(cwd);
118
+ const timeoutSec = opts.timeoutSec ?? constants_js_1.DEFAULT_SUMMARIZER_TIMEOUT_SEC;
119
+ try {
120
+ spawn(buildSummarizerCommand(reconciled.session, lockPath, logPath, timeoutSec), {
121
+ sessionId: reconciled.session,
122
+ cwd,
123
+ });
124
+ return { spawned: true, reason: 'spawned', pending };
125
+ }
126
+ catch {
127
+ (0, marker_js_1.releaseLock)(cwd);
128
+ return { spawned: false, reason: 'spawn-failed', pending };
129
+ }
130
+ }
131
+ async function onSessionStop(store, cwd, opts = {}) {
132
+ return maybeSpawnSummarizer(store, cwd, opts);
133
+ }
134
+ exports.DEFAULT_PROJECT_SUMMARY_THRESHOLD = 5;
135
+ /** Shell snippet: run tim-summarizer in --project-summary mode for a label. */
136
+ function buildProjectSummaryCommand(label, logPath, timeoutSec = constants_js_1.DEFAULT_SUMMARIZER_TIMEOUT_SEC) {
137
+ const q = (s) => JSON.stringify(s);
138
+ const cmd = 'node ' + JSON.stringify(path.resolve(__dirname, '..', '..', 'tim-summarizer', 'dist', 'summarize.js'));
139
+ return `timeout ${timeoutSec} ${cmd} --project-summary ${q(label)} >>${q(logPath)} 2>&1`;
140
+ }
141
+ /**
142
+ * Gate + detached spawn for periodic project-summary generation.
143
+ * Fires only when sessions-so-far is a positive multiple of the threshold.
144
+ * Fire-and-forget — never throws.
145
+ */
146
+ async function maybeSpawnProjectSummary(store, cwd, label, opts = {}) {
147
+ if (!label)
148
+ return { spawned: false, reason: 'no-label' };
149
+ const count = await store.countSessionSummaries(label);
150
+ if (count <= 0)
151
+ return { spawned: false, reason: 'no-sessions', count };
152
+ const threshold = opts.threshold ?? exports.DEFAULT_PROJECT_SUMMARY_THRESHOLD;
153
+ if (threshold <= 0 || count % threshold !== 0) {
154
+ return { spawned: false, reason: 'below-threshold', count };
155
+ }
156
+ const spawn = opts.spawn ?? exports.spawnSummarizer;
157
+ const logPath = summarizerLogPath(cwd);
158
+ const timeoutSec = opts.timeoutSec ?? constants_js_1.DEFAULT_SUMMARIZER_TIMEOUT_SEC;
159
+ try {
160
+ spawn(buildProjectSummaryCommand(label, logPath, timeoutSec), { sessionId: label, cwd });
161
+ return { spawned: true, reason: 'spawned', count };
162
+ }
163
+ catch {
164
+ return { spawned: false, reason: 'spawn-failed', count };
165
+ }
166
+ }
167
+ //# sourceMappingURL=session-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-hooks.js","sourceRoot":"","sources":["../src/session-hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function getUpdateCheckLine(options?: {
2
+ fetchTimeoutMs?: number;
3
+ }): Promise<string | null>;
4
+ export declare function getUpdateCheckLineBriefing(): Promise<string | null>;
5
+ //# sourceMappingURL=update-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-check.d.ts","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"AA4CA,wBAAsB,kBAAkB,CAAC,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAW1G;AAED,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEzE"}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getUpdateCheckLine = getUpdateCheckLine;
4
+ exports.getUpdateCheckLineBriefing = getUpdateCheckLineBriefing;
5
+ const node_fs_1 = require("node:fs");
6
+ const node_path_1 = require("node:path");
7
+ const tim_core_1 = require("tim-core");
8
+ const ONE_DAY_MS = 86_400_000;
9
+ const PACKAGE_NAME = 'tim-mcp';
10
+ const DEFAULT_FETCH_TIMEOUT_MS = 3000;
11
+ const DEFAULT_BRIEFING_TIMEOUT_MS = 500;
12
+ function timMcpPackagePath() {
13
+ return (0, node_path_1.join)(__dirname, '..', '..', 'tim-mcp', 'package.json');
14
+ }
15
+ function installedVersion() {
16
+ try {
17
+ const pkg = JSON.parse((0, node_fs_1.readFileSync)(timMcpPackagePath(), 'utf8'));
18
+ return pkg.version ?? '0.0.0';
19
+ }
20
+ catch {
21
+ return '0.0.0';
22
+ }
23
+ }
24
+ async function fetchLatestVersion(timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
25
+ const controller = new AbortController();
26
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
27
+ try {
28
+ const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { signal: controller.signal });
29
+ if (!res.ok)
30
+ return null;
31
+ const data = await res.json();
32
+ return data.version ?? null;
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ finally {
38
+ clearTimeout(timer);
39
+ }
40
+ }
41
+ function raceWithTimeout(promise, timeoutMs) {
42
+ return new Promise((resolve) => {
43
+ const timer = setTimeout(() => resolve(null), timeoutMs);
44
+ promise.then((v) => { clearTimeout(timer); resolve(v); }, () => { clearTimeout(timer); resolve(null); });
45
+ });
46
+ }
47
+ async function getUpdateCheckLine(options = {}) {
48
+ const config = (0, tim_core_1.loadConfig)();
49
+ if (config.updateCheck === false)
50
+ return null;
51
+ const lastAt = config.updateCheckLastAt;
52
+ if (lastAt && Date.now() - new Date(lastAt).getTime() < ONE_DAY_MS)
53
+ return null;
54
+ const latest = await fetchLatestVersion(options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS);
55
+ (0, tim_core_1.saveConfig)({ ...(0, tim_core_1.loadConfig)(), updateCheckLastAt: new Date().toISOString() });
56
+ if (!latest)
57
+ return null;
58
+ const installed = installedVersion();
59
+ if (latest === installed)
60
+ return null;
61
+ return `TIM ${latest} available (installed: ${installed}) — npm i -g ${PACKAGE_NAME}`;
62
+ }
63
+ async function getUpdateCheckLineBriefing() {
64
+ return raceWithTimeout(getUpdateCheckLine(), DEFAULT_BRIEFING_TIMEOUT_MS);
65
+ }
66
+ //# sourceMappingURL=update-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-check.js","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "tim-hooks",
3
+ "version": "0.1.0-beta.0",
4
+ "description": "TIM hook scripts and checkpoint orchestration",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist/**/*",
9
+ "!dist/**/__tests__/**",
10
+ "scripts/**/*"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "pretest": "npm --prefix ../.. run build",
15
+ "test": "vitest run"
16
+ },
17
+ "dependencies": {
18
+ "tim-core": "0.1.0-beta.0",
19
+ "tim-store": "0.1.0-beta.0"
20
+ },
21
+ "optionalDependencies": {
22
+ "fastembed": "^1.0.0"
23
+ }
24
+ }
@@ -0,0 +1,47 @@
1
+ # Hermes status bar (TIM)
2
+
3
+ Hermes has **no** Claude-style `statusLine` in `settings.json`. Persistent project/batch info uses the **Hermes CLI TUI status bar**, which calls a hook script on refresh.
4
+
5
+ ## Mechanism
6
+
7
+ | Piece | Role |
8
+ |-------|------|
9
+ | `tim-hermes-session-cache.sh` | `pre_llm_call` — writes `~/.tim/.session-cache` (`session_id`, `cwd`). Returns `{}`. |
10
+ | `tim-hermes-statusline.sh` | Called by patched Hermes `cli.py` — prints JSON: `{device, project, o_node, counter}` |
11
+ | `hermes-cli-tim-statusline.patch` | Reference only — install uses programmatic `cli.py` patch (line drift safe) |
12
+ | `tim statusline --format hermes` | Core formatter (used by the shell script) |
13
+
14
+ `pre_llm_call` **`context`** injection is for turn prompts (see `tim-session-start.sh`), not the status bar.
15
+
16
+ ## Install (one command)
17
+
18
+ ```bash
19
+ cd ~/projects/tim && npx tsc -b # once, if developing from source
20
+ node packages/tim-cli/dist/cli.js setup-hermes-statusline
21
+ # or after npm link -g: tim setup-hermes-statusline
22
+ ```
23
+
24
+ Idempotent: safe to re-run. Options: `--dry-run`, `--skip-build`, env `HERMES_AGENT_DIR`.
25
+
26
+ Manual steps (what the command does):
27
+
28
+ 1. Symlink `tim-hermes-*.sh` → `~/.hermes/agent-hooks/`
29
+ 2. Insert `tim-hermes-session-cache.sh` into `~/.hermes/config.yaml` `pre_llm_call` (after `o9k-startup.sh`)
30
+ 3. Patch `~/.hermes/hermes-agent/cli.py` (`_get_tim_status` + status-bar fragments)
31
+ 4. `npx tsc -b` when run from TIM monorepo
32
+
33
+ ## Verify
34
+
35
+ ```bash
36
+ # JSON for status bar
37
+ bash packages/tim-hooks/scripts/tim-hermes-statusline.sh | jq .
38
+
39
+ # Text line (debug)
40
+ node packages/tim-cli/dist/cli.js statusline --cwd ~/projects/tim
41
+ ```
42
+
43
+ Restart Hermes after patching `cli.py`.
44
+
45
+ ## Claude Code
46
+
47
+ Do **not** use `statusLine` in `~/.claude/settings.json` for TIM — that is Claude-only. Use this Hermes path instead.
@@ -0,0 +1,75 @@
1
+ diff --git a/cli.py b/cli.py
2
+ --- a/cli.py
3
+ +++ b/cli.py
4
+ @@ -2821,6 +2821,22 @@ class HermesCLI:
5
+
6
+ return snapshot
7
+
8
+ + def _get_tim_status(self) -> Dict[str, str]:
9
+ + """Call tim-hermes-statusline.sh for TIM project / batch counter."""
10
+ + try:
11
+ + import subprocess, json
12
+ + script = os.path.expanduser("~/.hermes/agent-hooks/tim-hermes-statusline.sh")
13
+ + if not os.path.isfile(script):
14
+ + return {}
15
+ + result = subprocess.run(
16
+ + ["bash", script], capture_output=True, text=True, timeout=3
17
+ + )
18
+ + if result.returncode == 0 and result.stdout.strip():
19
+ + return json.loads(result.stdout)
20
+ + except Exception:
21
+ + pass
22
+ + return {}
23
+ +
24
+ @staticmethod
25
+ def _status_bar_display_width(text: str) -> int:
26
+ """Return terminal cell width for status-bar text.
27
+ @@ -3030,14 +3046,27 @@ class HermesCLI:
28
+ return []
29
+ try:
30
+ snapshot = self._get_status_bar_snapshot()
31
+ width = self._get_tui_terminal_width()
32
+ duration_label = snapshot["duration"]
33
+
34
+ + tim = self._get_tim_status()
35
+ + tim_prefix = ""
36
+ + if tim:
37
+ + parts = []
38
+ + if tim.get("device"):
39
+ + parts.append(tim["device"])
40
+ + proj = tim.get("project", "")
41
+ + o_node = tim.get("o_node", "")
42
+ + if o_node:
43
+ + proj = f"{proj} → {o_node}"
44
+ + if proj:
45
+ + parts.append(proj)
46
+ + if tim.get("counter"):
47
+ + parts.append(tim["counter"])
48
+ + if parts:
49
+ + tim_prefix = " │ ".join(parts)
50
+ +
51
+ if width < 52:
52
+ frags = [
53
+ ("class:status-bar", " ⚕ "),
54
+ @@ -3075,7 +3104,11 @@ class HermesCLI:
55
+
56
+ bar_style = self._status_bar_context_style(percent)
57
+ compressions = snapshot.get("compressions", 0)
58
+ - frags = [
59
+ + frags = []
60
+ + if tim_prefix:
61
+ + frags.append(("class:status-bar-strong", f" {tim_prefix}"))
62
+ + frags.append(("class:status-bar-dim", " │ "))
63
+ + frags.extend([
64
+ ("class:status-bar", " ⚕ "),
65
+ ("class:status-bar-strong", snapshot["model_short"]),
66
+ ("class:status-bar-dim", " │ "),
67
+ @@ -3084,7 +3117,7 @@ class HermesCLI:
68
+ (bar_style, self._build_context_bar(percent)),
69
+ ("class:status-bar-dim", " "),
70
+ (bar_style, percent_label),
71
+ - ]
72
+ + ])
73
+ if compressions:
74
+ frags.append(("class:status-bar-dim", " │ "))
75
+ frags.append((self._compression_count_style(compressions), f"🗜️ {compressions}"))
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Shared TIM CLI discovery for hook entrypoints.
3
+
4
+ TIM_HOOKS_SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+
6
+ resolve_tim_cli() {
7
+ if [[ -n "${TIM_CLI:-}" ]]; then
8
+ printf '%s\n' "$TIM_CLI"
9
+ return 0
10
+ fi
11
+
12
+ if command -v tim >/dev/null 2>&1; then
13
+ command -v tim
14
+ return 0
15
+ fi
16
+
17
+ local candidate="${TIM_HOOKS_SCRIPTS_DIR}/../../tim-cli/dist/cli.js"
18
+ [[ -f "$candidate" ]] || return 1
19
+ printf '%s\n' "$candidate"
20
+ }
21
+
22
+ run_tim_cli() {
23
+ local tim_cli
24
+ tim_cli="$(resolve_tim_cli)" || return 1
25
+ if [[ "$tim_cli" == *.js ]]; then
26
+ node "$tim_cli" "$@"
27
+ else
28
+ "$tim_cli" "$@"
29
+ fi
30
+ }
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # post-commit.sh — Global git post-commit: record commit to TIM Commits section.
3
+ #
4
+ # Walk-up .tim-project via `tim record-commit`. Silent skip when no marker.
5
+ # Must not fail the commit (errors swallowed).
6
+ #
7
+ # Install:
8
+ # mkdir -p ~/.hermes/git-hooks
9
+ # ln -sf ~/projects/tim/packages/tim-hooks/scripts/post-commit.sh ~/.hermes/git-hooks/post-commit
10
+ # git config --global core.hooksPath ~/.hermes/git-hooks
11
+ #
12
+ # Requires: git, node. Override CLI with TIM_CLI.
13
+ set -euo pipefail
14
+
15
+ SCRIPT_PATH="${BASH_SOURCE[0]}"
16
+ while [[ -L "$SCRIPT_PATH" ]]; do
17
+ SCRIPT_LINK_DIR="$(cd -P "$(dirname "$SCRIPT_PATH")" && pwd)"
18
+ SCRIPT_PATH="$(readlink "$SCRIPT_PATH")"
19
+ [[ "$SCRIPT_PATH" == /* ]] || SCRIPT_PATH="$SCRIPT_LINK_DIR/$SCRIPT_PATH"
20
+ done
21
+ SCRIPT_DIR="$(cd -P "$(dirname "$SCRIPT_PATH")" && pwd)"
22
+ unset SCRIPT_PATH SCRIPT_LINK_DIR
23
+ # shellcheck source=lib/resolve-tim-cli.sh
24
+ source "$SCRIPT_DIR/lib/resolve-tim-cli.sh"
25
+
26
+ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
27
+ [[ -z "$repo_root" ]] && exit 0
28
+
29
+ run_tim_cli record-commit --cwd "$repo_root" >/dev/null 2>&1 || true
30
+ exit 0