myshell-tools 2.0.0-alpha.0 → 2.1.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/CHANGELOG.md +13 -1
- package/README.md +6 -3
- package/dist/cli.js +85 -30
- package/dist/cli.js.map +1 -1
- package/dist/commands/doctor.d.ts +3 -2
- package/dist/commands/doctor.js +9 -5
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/login.d.ts +20 -0
- package/dist/commands/login.js +60 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/core/orchestrate.js +80 -28
- package/dist/core/orchestrate.js.map +1 -1
- package/dist/core/policy.d.ts +10 -0
- package/dist/core/policy.js +40 -0
- package/dist/core/policy.js.map +1 -1
- package/dist/core/review.d.ts +5 -0
- package/dist/core/review.js +2 -2
- package/dist/core/review.js.map +1 -1
- package/dist/infra/atomic.d.ts +0 -3
- package/dist/infra/atomic.js +1 -1
- package/dist/infra/atomic.js.map +1 -1
- package/dist/infra/config.d.ts +23 -0
- package/dist/infra/config.js +64 -0
- package/dist/infra/config.js.map +1 -0
- package/dist/infra/conversation-store.d.ts +42 -0
- package/dist/infra/conversation-store.js +14 -0
- package/dist/infra/conversation-store.js.map +1 -0
- package/dist/infra/conversations.d.ts +18 -0
- package/dist/infra/conversations.js +296 -0
- package/dist/infra/conversations.js.map +1 -0
- package/dist/infra/insights.d.ts +66 -0
- package/dist/infra/insights.js +105 -0
- package/dist/infra/insights.js.map +1 -0
- package/dist/infra/ledger.d.ts +4 -6
- package/dist/infra/ledger.js.map +1 -1
- package/dist/interface/menu.d.ts +112 -0
- package/dist/interface/menu.js +622 -0
- package/dist/interface/menu.js.map +1 -0
- package/dist/providers/claude.d.ts +4 -13
- package/dist/providers/claude.js +5 -4
- package/dist/providers/claude.js.map +1 -1
- package/dist/providers/codex.d.ts +6 -12
- package/dist/providers/codex.js +6 -4
- package/dist/providers/codex.js.map +1 -1
- package/dist/providers/detect.d.ts +63 -14
- package/dist/providers/detect.js +123 -27
- package/dist/providers/detect.js.map +1 -1
- package/dist/ui/tui.d.ts +127 -0
- package/dist/ui/tui.js +316 -0
- package/dist/ui/tui.js.map +1 -0
- package/package.json +4 -1
- package/dist/core/index.d.ts +0 -13
- package/dist/core/index.js +0 -12
- package/dist/core/index.js.map +0 -1
- package/dist/infra/index.d.ts +0 -9
- package/dist/infra/index.js +0 -7
- package/dist/infra/index.js.map +0 -1
- package/dist/providers/index.d.ts +0 -9
- package/dist/providers/index.js +0 -7
- package/dist/providers/index.js.map +0 -1
package/dist/core/policy.js
CHANGED
|
@@ -24,4 +24,44 @@ export const DEFAULT_POLICY = {
|
|
|
24
24
|
manager: ['claude', 'codex'],
|
|
25
25
|
},
|
|
26
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Named policy presets selectable from the Settings screen.
|
|
29
|
+
*
|
|
30
|
+
* - cost-saver : raises escalation thresholds so it rarely escalates; stays
|
|
31
|
+
* on cheaper worker/IC tiers as long as possible.
|
|
32
|
+
* - balanced : identical to DEFAULT_POLICY; good for most work.
|
|
33
|
+
* - quality-first: lowers escalation thresholds so it escalates sooner and
|
|
34
|
+
* reviews more; prioritises quality over token cost.
|
|
35
|
+
*/
|
|
36
|
+
export const POLICY_PRESETS = {
|
|
37
|
+
'cost-saver': {
|
|
38
|
+
maxAttempts: 4,
|
|
39
|
+
escalateBelowConfidence: {
|
|
40
|
+
low: 0.2,
|
|
41
|
+
medium: 0.3,
|
|
42
|
+
high: 0.5,
|
|
43
|
+
critical: 0.65,
|
|
44
|
+
},
|
|
45
|
+
providerOrderByTier: {
|
|
46
|
+
worker: ['claude', 'codex'],
|
|
47
|
+
ic: ['claude', 'codex'],
|
|
48
|
+
manager: ['claude', 'codex'],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
'balanced': DEFAULT_POLICY,
|
|
52
|
+
'quality-first': {
|
|
53
|
+
maxAttempts: 3,
|
|
54
|
+
escalateBelowConfidence: {
|
|
55
|
+
low: 0.6,
|
|
56
|
+
medium: 0.7,
|
|
57
|
+
high: 0.85,
|
|
58
|
+
critical: 0.92,
|
|
59
|
+
},
|
|
60
|
+
providerOrderByTier: {
|
|
61
|
+
worker: ['claude', 'codex'],
|
|
62
|
+
ic: ['claude', 'codex'],
|
|
63
|
+
manager: ['claude', 'codex'],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
27
67
|
//# sourceMappingURL=policy.js.map
|
package/dist/core/policy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/core/policy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,CAAC,MAAM,cAAc,GAAW;IACpC,WAAW,EAAE,CAAC;IAEd,4EAA4E;IAC5E,6EAA6E;IAC7E,uBAAuB,EAAE;QACvB,GAAG,EAAE,GAAG;QACR,MAAM,EAAE,GAAG;QACX,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,GAAG;KACd;IAED,8EAA8E;IAC9E,2EAA2E;IAC3E,2EAA2E;IAC3E,mBAAmB,EAAE;QACnB,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;QAC3B,EAAE,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;QACvB,OAAO,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;KAC7B;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/core/policy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,CAAC,MAAM,cAAc,GAAW;IACpC,WAAW,EAAE,CAAC;IAEd,4EAA4E;IAC5E,6EAA6E;IAC7E,uBAAuB,EAAE;QACvB,GAAG,EAAE,GAAG;QACR,MAAM,EAAE,GAAG;QACX,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,GAAG;KACd;IAED,8EAA8E;IAC9E,2EAA2E;IAC3E,2EAA2E;IAC3E,mBAAmB,EAAE;QACnB,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;QAC3B,EAAE,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;QACvB,OAAO,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;KAC7B;CACF,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,cAAc,GAAgE;IACzF,YAAY,EAAE;QACZ,WAAW,EAAE,CAAC;QACd,uBAAuB,EAAE;YACvB,GAAG,EAAE,GAAG;YACR,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,IAAI;SACf;QACD,mBAAmB,EAAE;YACnB,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;YAC3B,EAAE,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;YACvB,OAAO,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;SAC7B;KACF;IAED,UAAU,EAAE,cAAc;IAE1B,eAAe,EAAE;QACf,WAAW,EAAE,CAAC;QACd,uBAAuB,EAAE;YACvB,GAAG,EAAE,GAAG;YACR,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,IAAI;YACV,QAAQ,EAAE,IAAI;SACf;QACD,mBAAmB,EAAE;YACnB,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;YAC3B,EAAE,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;YACvB,OAAO,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;SAC7B;KACF;CACF,CAAC"}
|
package/dist/core/review.d.ts
CHANGED
|
@@ -16,11 +16,16 @@
|
|
|
16
16
|
* - `approve` : IC output is acceptable; proceed to final.
|
|
17
17
|
* - `revise` : IC output needs changes; retry IC with the reviewer's notes.
|
|
18
18
|
* - `escalate` : The issue is beyond IC scope; escalate to manager tier.
|
|
19
|
+
* - `parsed` : True when a real verdict envelope was found and validated;
|
|
20
|
+
* false when the fail-open default was used. Callers should
|
|
21
|
+
* treat `parsed === false` on high/critical risk as
|
|
22
|
+
* inconclusive rather than approved.
|
|
19
23
|
*/
|
|
20
24
|
export interface ReviewVerdict {
|
|
21
25
|
readonly verdict: 'approve' | 'revise' | 'escalate';
|
|
22
26
|
readonly notes: string;
|
|
23
27
|
readonly confidence: number | null;
|
|
28
|
+
readonly parsed: boolean;
|
|
24
29
|
}
|
|
25
30
|
/**
|
|
26
31
|
* Build a manager-style prompt asking the reviewer to critically assess the
|
package/dist/core/review.js
CHANGED
|
@@ -61,7 +61,7 @@ confidence: your honest estimate that your review is complete and correct (1.0 =
|
|
|
61
61
|
}
|
|
62
62
|
const VALID_VERDICTS = new Set(['approve', 'revise', 'escalate']);
|
|
63
63
|
/** Fail-open default used whenever the envelope is absent or malformed. */
|
|
64
|
-
const FAIL_OPEN = { verdict: 'approve', notes: '', confidence: null };
|
|
64
|
+
const FAIL_OPEN = { verdict: 'approve', notes: '', confidence: null, parsed: false };
|
|
65
65
|
/**
|
|
66
66
|
* Attempt to extract the last JSON object from `text` that contains a `verdict` key.
|
|
67
67
|
* Returns null if no valid envelope is found.
|
|
@@ -143,6 +143,6 @@ export function parseReviewVerdict(output) {
|
|
|
143
143
|
if (typeof envelope.confidence === 'number' && isFinite(envelope.confidence)) {
|
|
144
144
|
confidence = Math.min(1, Math.max(0, envelope.confidence));
|
|
145
145
|
}
|
|
146
|
-
return { verdict, notes, confidence };
|
|
146
|
+
return { verdict, notes, confidence, parsed: true };
|
|
147
147
|
}
|
|
148
148
|
//# sourceMappingURL=review.js.map
|
package/dist/core/review.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/core/review.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/core/review.ts"],"names":[],"mappings":"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"}
|
package/dist/infra/atomic.d.ts
CHANGED
|
@@ -15,9 +15,6 @@ export interface LockOptions {
|
|
|
15
15
|
export declare class LockTimeoutError extends Error {
|
|
16
16
|
constructor(lockPath: string, timeoutMs: number);
|
|
17
17
|
}
|
|
18
|
-
export declare class AtomicWriteError extends Error {
|
|
19
|
-
constructor(filePath: string, cause: unknown);
|
|
20
|
-
}
|
|
21
18
|
/**
|
|
22
19
|
* Acquire a `.lock` file using `O_EXCL` for atomic creation.
|
|
23
20
|
* Retries with exponential backoff until `timeoutMs` is reached.
|
package/dist/infra/atomic.js
CHANGED
|
@@ -15,7 +15,7 @@ export class LockTimeoutError extends Error {
|
|
|
15
15
|
this.name = 'LockTimeoutError';
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
class AtomicWriteError extends Error {
|
|
19
19
|
constructor(filePath, cause) {
|
|
20
20
|
super(`Atomic write failed for: ${filePath} — ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
21
21
|
this.name = 'AtomicWriteError';
|
package/dist/infra/atomic.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atomic.js","sourceRoot":"","sources":["../../src/infra/atomic.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAa1C,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YAAY,QAAgB,EAAE,SAAiB;QAC7C,KAAK,CAAC,oCAAoC,SAAS,WAAW,QAAQ,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,MAAM,
|
|
1
|
+
{"version":3,"file":"atomic.js","sourceRoot":"","sources":["../../src/infra/atomic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/infra/config.ts — Global app configuration persisted at
|
|
3
|
+
* <homeDir>/.myshell-tools/config.json.
|
|
4
|
+
*
|
|
5
|
+
* Reads merge over defaults so that new keys added in future versions are
|
|
6
|
+
* always present even when the on-disk file pre-dates them.
|
|
7
|
+
*/
|
|
8
|
+
export interface AppConfig {
|
|
9
|
+
onboarded: boolean;
|
|
10
|
+
setAsDefault: boolean;
|
|
11
|
+
/** Active routing mode. Absent → use DEFAULT_POLICY (same as 'balanced'). */
|
|
12
|
+
mode?: 'cost-saver' | 'balanced' | 'quality-first';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Load the global app config. Returns defaults merged with any on-disk
|
|
16
|
+
* values so unknown/corrupt files never throw and new keys are always present.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadConfig(homeDir?: string): Promise<AppConfig>;
|
|
19
|
+
/**
|
|
20
|
+
* Persist the app config atomically. Creates the `.myshell-tools` directory
|
|
21
|
+
* if it does not exist.
|
|
22
|
+
*/
|
|
23
|
+
export declare function saveConfig(config: AppConfig, homeDir?: string): Promise<void>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/infra/config.ts — Global app configuration persisted at
|
|
3
|
+
* <homeDir>/.myshell-tools/config.json.
|
|
4
|
+
*
|
|
5
|
+
* Reads merge over defaults so that new keys added in future versions are
|
|
6
|
+
* always present even when the on-disk file pre-dates them.
|
|
7
|
+
*/
|
|
8
|
+
import { mkdir, readFile } from 'node:fs/promises';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { atomicWrite } from './atomic.js';
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Defaults
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
const DEFAULTS = {
|
|
16
|
+
onboarded: false,
|
|
17
|
+
setAsDefault: false,
|
|
18
|
+
};
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Path helpers
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
function getConfigDir(homeDir) {
|
|
23
|
+
return join(homeDir, '.myshell-tools');
|
|
24
|
+
}
|
|
25
|
+
function getConfigPath(homeDir) {
|
|
26
|
+
return join(getConfigDir(homeDir), 'config.json');
|
|
27
|
+
}
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Public API
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Load the global app config. Returns defaults merged with any on-disk
|
|
33
|
+
* values so unknown/corrupt files never throw and new keys are always present.
|
|
34
|
+
*/
|
|
35
|
+
export async function loadConfig(homeDir) {
|
|
36
|
+
const home = homeDir ?? homedir();
|
|
37
|
+
let raw;
|
|
38
|
+
try {
|
|
39
|
+
raw = await readFile(getConfigPath(home), 'utf8');
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Missing file — return defaults
|
|
43
|
+
return { ...DEFAULTS };
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(raw);
|
|
47
|
+
// Merge: defaults first, then on-disk values (new keys default safely)
|
|
48
|
+
return { ...DEFAULTS, ...parsed };
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Corrupt JSON — return defaults
|
|
52
|
+
return { ...DEFAULTS };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Persist the app config atomically. Creates the `.myshell-tools` directory
|
|
57
|
+
* if it does not exist.
|
|
58
|
+
*/
|
|
59
|
+
export async function saveConfig(config, homeDir) {
|
|
60
|
+
const home = homeDir ?? homedir();
|
|
61
|
+
await mkdir(getConfigDir(home), { recursive: true });
|
|
62
|
+
await atomicWrite(getConfigPath(home), JSON.stringify(config, null, 2));
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/infra/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAa1C,8EAA8E;AAC9E,WAAW;AACX,8EAA8E;AAE9E,MAAM,QAAQ,GAAc;IAC1B,SAAS,EAAE,KAAK;IAChB,YAAY,EAAE,KAAK;CACpB,CAAC;AAEF,8EAA8E;AAC9E,eAAe;AACf,8EAA8E;AAE9E,SAAS,YAAY,CAAC,OAAe;IACnC,OAAO,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,aAAa,CAAC,OAAe;IACpC,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC,CAAC;AACpD,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAgB;IAC/C,MAAM,IAAI,GAAG,OAAO,IAAI,OAAO,EAAE,CAAC;IAClC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;QACjC,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IACzB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC;QACrD,uEAAuE;QACvE,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;QACjC,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAiB,EAAE,OAAgB;IAClE,MAAM,IAAI,GAAG,OAAO,IAAI,OAAO,EAAE,CAAC;IAClC,MAAM,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,MAAM,WAAW,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC1E,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/infra/conversation-store.ts — the persistent conversation contract.
|
|
3
|
+
*
|
|
4
|
+
* The menu UX manages multiple named, persistent conversations. This is the
|
|
5
|
+
* port both the file-backed implementation (infra) and the menu (interface)
|
|
6
|
+
* build against. Conversations live in a GLOBAL store (the user's home dir) so
|
|
7
|
+
* they follow the user across projects; each one is an append-only message log
|
|
8
|
+
* plus lightweight metadata.
|
|
9
|
+
*
|
|
10
|
+
* A conversation's `SessionWriter` is what gets injected into orchestrate() so a
|
|
11
|
+
* run's messages persist into that conversation.
|
|
12
|
+
*/
|
|
13
|
+
import type { SessionEntry, SessionWriter } from '../core/types.js';
|
|
14
|
+
export interface ConversationMeta {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly title: string;
|
|
17
|
+
readonly createdAt: string;
|
|
18
|
+
readonly updatedAt: string;
|
|
19
|
+
readonly messageCount: number;
|
|
20
|
+
/** Whether this conversation is pinned (sorted to the top of the list). */
|
|
21
|
+
readonly pinned: boolean;
|
|
22
|
+
/** Optional short category tag (e.g. "ui", "refactor"); null when unset. */
|
|
23
|
+
readonly category: string | null;
|
|
24
|
+
}
|
|
25
|
+
export interface ConversationStore {
|
|
26
|
+
/** All conversations, pinned first then most-recently-updated first. */
|
|
27
|
+
list(): Promise<ConversationMeta[]>;
|
|
28
|
+
/** Create a new conversation; returns its metadata (with a fresh id). */
|
|
29
|
+
create(title: string): Promise<ConversationMeta>;
|
|
30
|
+
/** Read a conversation's full message history (oldest first); [] if missing. */
|
|
31
|
+
load(id: string): Promise<SessionEntry[]>;
|
|
32
|
+
/** Rename a conversation. No-op if the id does not exist. */
|
|
33
|
+
rename(id: string, title: string): Promise<void>;
|
|
34
|
+
/** Delete a conversation and its messages. No-op if missing. */
|
|
35
|
+
remove(id: string): Promise<void>;
|
|
36
|
+
/** A SessionWriter bound to `id` — appends entries and bumps updatedAt/count. */
|
|
37
|
+
writer(id: string): SessionWriter;
|
|
38
|
+
/** Pin or unpin a conversation. No-op if the id does not exist. */
|
|
39
|
+
setPinned(id: string, pinned: boolean): Promise<void>;
|
|
40
|
+
/** Set or clear the category tag for a conversation. No-op if id missing. */
|
|
41
|
+
setCategory(id: string, category: string | null): Promise<void>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/infra/conversation-store.ts — the persistent conversation contract.
|
|
3
|
+
*
|
|
4
|
+
* The menu UX manages multiple named, persistent conversations. This is the
|
|
5
|
+
* port both the file-backed implementation (infra) and the menu (interface)
|
|
6
|
+
* build against. Conversations live in a GLOBAL store (the user's home dir) so
|
|
7
|
+
* they follow the user across projects; each one is an append-only message log
|
|
8
|
+
* plus lightweight metadata.
|
|
9
|
+
*
|
|
10
|
+
* A conversation's `SessionWriter` is what gets injected into orchestrate() so a
|
|
11
|
+
* run's messages persist into that conversation.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=conversation-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-store.js","sourceRoot":"","sources":["../../src/infra/conversation-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/infra/conversations.ts — File-backed ConversationStore implementation.
|
|
3
|
+
*
|
|
4
|
+
* Storage layout under <homeDir>/.myshell-tools/conversations/:
|
|
5
|
+
* index.json — JSON array of ConversationMeta, newest first
|
|
6
|
+
* index.json.lock — advisory lock for concurrent index mutations
|
|
7
|
+
* <id>.jsonl — one SessionEntry per line (append-only message log)
|
|
8
|
+
*/
|
|
9
|
+
import type { Clock } from '../core/types.js';
|
|
10
|
+
import type { ConversationStore } from './conversation-store.js';
|
|
11
|
+
/**
|
|
12
|
+
* Create a file-backed ConversationStore that persists conversations under
|
|
13
|
+
* `<homeDir ?? os.homedir()>/.myshell-tools/conversations/`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createFileConversationStore(opts: {
|
|
16
|
+
homeDir?: string;
|
|
17
|
+
clock: Clock;
|
|
18
|
+
}): ConversationStore;
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/infra/conversations.ts — File-backed ConversationStore implementation.
|
|
3
|
+
*
|
|
4
|
+
* Storage layout under <homeDir>/.myshell-tools/conversations/:
|
|
5
|
+
* index.json — JSON array of ConversationMeta, newest first
|
|
6
|
+
* index.json.lock — advisory lock for concurrent index mutations
|
|
7
|
+
* <id>.jsonl — one SessionEntry per line (append-only message log)
|
|
8
|
+
*/
|
|
9
|
+
import { mkdir, readFile, unlink } from 'node:fs/promises';
|
|
10
|
+
import { homedir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { atomicAppendJSONL, atomicWrite, withLock } from './atomic.js';
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Path helpers (local — conversations dir lives in homeDir, not cwd)
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
function getConversationsDir(homeDir) {
|
|
17
|
+
return join(homeDir, '.myshell-tools', 'conversations');
|
|
18
|
+
}
|
|
19
|
+
function getIndexPath(homeDir) {
|
|
20
|
+
return join(getConversationsDir(homeDir), 'index.json');
|
|
21
|
+
}
|
|
22
|
+
function getIndexLockPath(homeDir) {
|
|
23
|
+
return join(getConversationsDir(homeDir), 'index.json.lock');
|
|
24
|
+
}
|
|
25
|
+
function getMessagePath(homeDir, id) {
|
|
26
|
+
return join(getConversationsDir(homeDir), `${id}.jsonl`);
|
|
27
|
+
}
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Internal index helpers
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
async function ensureDir(homeDir) {
|
|
32
|
+
await mkdir(getConversationsDir(homeDir), { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Normalise a raw index entry that may be missing fields added in later
|
|
36
|
+
* versions (pinned, category). Old on-disk entries that predate these fields
|
|
37
|
+
* will be migrated transparently on read so existing stores keep working.
|
|
38
|
+
*/
|
|
39
|
+
function normaliseMeta(raw) {
|
|
40
|
+
const r = raw;
|
|
41
|
+
return {
|
|
42
|
+
id: String(r['id'] ?? ''),
|
|
43
|
+
title: String(r['title'] ?? ''),
|
|
44
|
+
createdAt: String(r['createdAt'] ?? ''),
|
|
45
|
+
updatedAt: String(r['updatedAt'] ?? ''),
|
|
46
|
+
messageCount: typeof r['messageCount'] === 'number' ? r['messageCount'] : 0,
|
|
47
|
+
pinned: typeof r['pinned'] === 'boolean' ? r['pinned'] : false,
|
|
48
|
+
category: typeof r['category'] === 'string' ? r['category'] : null,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function readIndex(homeDir) {
|
|
52
|
+
try {
|
|
53
|
+
const raw = await readFile(getIndexPath(homeDir), 'utf8');
|
|
54
|
+
const parsed = JSON.parse(raw);
|
|
55
|
+
if (!Array.isArray(parsed))
|
|
56
|
+
return [];
|
|
57
|
+
return parsed.map(normaliseMeta);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function writeIndex(homeDir, index) {
|
|
64
|
+
await atomicWrite(getIndexPath(homeDir), JSON.stringify(index, null, 2));
|
|
65
|
+
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Title extraction: trim + truncate to 80 chars
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
const MAX_TITLE_LEN = 80;
|
|
70
|
+
function deriveTitle(content) {
|
|
71
|
+
const trimmed = content.trim();
|
|
72
|
+
return trimmed.length <= MAX_TITLE_LEN ? trimmed : trimmed.slice(0, MAX_TITLE_LEN);
|
|
73
|
+
}
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Factory
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
/**
|
|
78
|
+
* Create a file-backed ConversationStore that persists conversations under
|
|
79
|
+
* `<homeDir ?? os.homedir()>/.myshell-tools/conversations/`.
|
|
80
|
+
*/
|
|
81
|
+
export function createFileConversationStore(opts) {
|
|
82
|
+
const { clock } = opts;
|
|
83
|
+
const home = opts.homeDir ?? homedir();
|
|
84
|
+
return {
|
|
85
|
+
// -----------------------------------------------------------------------
|
|
86
|
+
// list
|
|
87
|
+
// -----------------------------------------------------------------------
|
|
88
|
+
async list() {
|
|
89
|
+
const index = await readIndex(home);
|
|
90
|
+
return [...index].sort((a, b) => {
|
|
91
|
+
// Pinned items always come before unpinned
|
|
92
|
+
if (a.pinned !== b.pinned)
|
|
93
|
+
return a.pinned ? -1 : 1;
|
|
94
|
+
// Within the same pin group, most-recently-updated first
|
|
95
|
+
return a.updatedAt < b.updatedAt ? 1 : -1;
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
// -----------------------------------------------------------------------
|
|
99
|
+
// create
|
|
100
|
+
// -----------------------------------------------------------------------
|
|
101
|
+
async create(title) {
|
|
102
|
+
await ensureDir(home);
|
|
103
|
+
const id = clock.uuid();
|
|
104
|
+
const now = clock.isoNow();
|
|
105
|
+
const meta = {
|
|
106
|
+
id,
|
|
107
|
+
title,
|
|
108
|
+
createdAt: now,
|
|
109
|
+
updatedAt: now,
|
|
110
|
+
messageCount: 0,
|
|
111
|
+
pinned: false,
|
|
112
|
+
category: null,
|
|
113
|
+
};
|
|
114
|
+
await withLock(getIndexLockPath(home), async () => {
|
|
115
|
+
const index = await readIndex(home);
|
|
116
|
+
await writeIndex(home, [meta, ...index]);
|
|
117
|
+
});
|
|
118
|
+
return meta;
|
|
119
|
+
},
|
|
120
|
+
// -----------------------------------------------------------------------
|
|
121
|
+
// load
|
|
122
|
+
// -----------------------------------------------------------------------
|
|
123
|
+
async load(id) {
|
|
124
|
+
let raw;
|
|
125
|
+
try {
|
|
126
|
+
raw = await readFile(getMessagePath(home, id), 'utf8');
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
const nodeErr = err;
|
|
130
|
+
if (nodeErr.code === 'ENOENT')
|
|
131
|
+
return [];
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
134
|
+
const entries = [];
|
|
135
|
+
for (const line of raw.split('\n')) {
|
|
136
|
+
const trimmed = line.trim();
|
|
137
|
+
if (trimmed.length === 0)
|
|
138
|
+
continue;
|
|
139
|
+
try {
|
|
140
|
+
entries.push(JSON.parse(trimmed));
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Skip malformed lines
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return entries;
|
|
147
|
+
},
|
|
148
|
+
// -----------------------------------------------------------------------
|
|
149
|
+
// writer
|
|
150
|
+
// -----------------------------------------------------------------------
|
|
151
|
+
writer(id) {
|
|
152
|
+
return {
|
|
153
|
+
id,
|
|
154
|
+
async append(entry) {
|
|
155
|
+
await ensureDir(home);
|
|
156
|
+
// Append the entry to the conversation's JSONL file
|
|
157
|
+
await atomicAppendJSONL(getMessagePath(home, id), entry);
|
|
158
|
+
// Update index under lock
|
|
159
|
+
await withLock(getIndexLockPath(home), async () => {
|
|
160
|
+
const index = await readIndex(home);
|
|
161
|
+
const idx = index.findIndex((m) => m.id === id);
|
|
162
|
+
if (idx === -1)
|
|
163
|
+
return;
|
|
164
|
+
const existing = index[idx];
|
|
165
|
+
if (existing === undefined)
|
|
166
|
+
return;
|
|
167
|
+
const updatedAt = clock.isoNow();
|
|
168
|
+
const messageCount = existing.messageCount + 1;
|
|
169
|
+
// If this is a user message and the title is still the placeholder,
|
|
170
|
+
// use the message content as the title (first user message wins).
|
|
171
|
+
let title = existing.title;
|
|
172
|
+
if (entry.role === 'user' &&
|
|
173
|
+
entry.content &&
|
|
174
|
+
(title.trim().length === 0 || title === existing.title) &&
|
|
175
|
+
existing.messageCount === 0) {
|
|
176
|
+
title = deriveTitle(entry.content);
|
|
177
|
+
}
|
|
178
|
+
const updated = {
|
|
179
|
+
id: existing.id,
|
|
180
|
+
title,
|
|
181
|
+
createdAt: existing.createdAt,
|
|
182
|
+
updatedAt,
|
|
183
|
+
messageCount,
|
|
184
|
+
pinned: existing.pinned,
|
|
185
|
+
category: existing.category,
|
|
186
|
+
};
|
|
187
|
+
const newIndex = [...index];
|
|
188
|
+
newIndex[idx] = updated;
|
|
189
|
+
await writeIndex(home, newIndex);
|
|
190
|
+
});
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
},
|
|
194
|
+
// -----------------------------------------------------------------------
|
|
195
|
+
// rename
|
|
196
|
+
// -----------------------------------------------------------------------
|
|
197
|
+
async rename(id, title) {
|
|
198
|
+
await withLock(getIndexLockPath(home), async () => {
|
|
199
|
+
const index = await readIndex(home);
|
|
200
|
+
const idx = index.findIndex((m) => m.id === id);
|
|
201
|
+
if (idx === -1)
|
|
202
|
+
return;
|
|
203
|
+
const existing = index[idx];
|
|
204
|
+
if (existing === undefined)
|
|
205
|
+
return;
|
|
206
|
+
const updated = {
|
|
207
|
+
id: existing.id,
|
|
208
|
+
title,
|
|
209
|
+
createdAt: existing.createdAt,
|
|
210
|
+
updatedAt: existing.updatedAt,
|
|
211
|
+
messageCount: existing.messageCount,
|
|
212
|
+
pinned: existing.pinned,
|
|
213
|
+
category: existing.category,
|
|
214
|
+
};
|
|
215
|
+
const newIndex = [...index];
|
|
216
|
+
newIndex[idx] = updated;
|
|
217
|
+
await writeIndex(home, newIndex);
|
|
218
|
+
});
|
|
219
|
+
},
|
|
220
|
+
// -----------------------------------------------------------------------
|
|
221
|
+
// remove
|
|
222
|
+
// -----------------------------------------------------------------------
|
|
223
|
+
async remove(id) {
|
|
224
|
+
// Best-effort delete of message file
|
|
225
|
+
try {
|
|
226
|
+
await unlink(getMessagePath(home, id));
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
// Missing or already gone — ignore
|
|
230
|
+
}
|
|
231
|
+
// Remove from index under lock
|
|
232
|
+
await withLock(getIndexLockPath(home), async () => {
|
|
233
|
+
const index = await readIndex(home);
|
|
234
|
+
const filtered = index.filter((m) => m.id !== id);
|
|
235
|
+
if (filtered.length === index.length)
|
|
236
|
+
return; // not found, no-op
|
|
237
|
+
await writeIndex(home, filtered);
|
|
238
|
+
});
|
|
239
|
+
},
|
|
240
|
+
// -----------------------------------------------------------------------
|
|
241
|
+
// setPinned
|
|
242
|
+
// -----------------------------------------------------------------------
|
|
243
|
+
async setPinned(id, pinned) {
|
|
244
|
+
await ensureDir(home);
|
|
245
|
+
await withLock(getIndexLockPath(home), async () => {
|
|
246
|
+
const index = await readIndex(home);
|
|
247
|
+
const idx = index.findIndex((m) => m.id === id);
|
|
248
|
+
if (idx === -1)
|
|
249
|
+
return; // no-op if missing
|
|
250
|
+
const existing = index[idx];
|
|
251
|
+
if (existing === undefined)
|
|
252
|
+
return;
|
|
253
|
+
const updated = {
|
|
254
|
+
id: existing.id,
|
|
255
|
+
title: existing.title,
|
|
256
|
+
createdAt: existing.createdAt,
|
|
257
|
+
updatedAt: existing.updatedAt,
|
|
258
|
+
messageCount: existing.messageCount,
|
|
259
|
+
pinned,
|
|
260
|
+
category: existing.category,
|
|
261
|
+
};
|
|
262
|
+
const newIndex = [...index];
|
|
263
|
+
newIndex[idx] = updated;
|
|
264
|
+
await writeIndex(home, newIndex);
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
// -----------------------------------------------------------------------
|
|
268
|
+
// setCategory
|
|
269
|
+
// -----------------------------------------------------------------------
|
|
270
|
+
async setCategory(id, category) {
|
|
271
|
+
await ensureDir(home);
|
|
272
|
+
await withLock(getIndexLockPath(home), async () => {
|
|
273
|
+
const index = await readIndex(home);
|
|
274
|
+
const idx = index.findIndex((m) => m.id === id);
|
|
275
|
+
if (idx === -1)
|
|
276
|
+
return; // no-op if missing
|
|
277
|
+
const existing = index[idx];
|
|
278
|
+
if (existing === undefined)
|
|
279
|
+
return;
|
|
280
|
+
const updated = {
|
|
281
|
+
id: existing.id,
|
|
282
|
+
title: existing.title,
|
|
283
|
+
createdAt: existing.createdAt,
|
|
284
|
+
updatedAt: existing.updatedAt,
|
|
285
|
+
messageCount: existing.messageCount,
|
|
286
|
+
pinned: existing.pinned,
|
|
287
|
+
category,
|
|
288
|
+
};
|
|
289
|
+
const newIndex = [...index];
|
|
290
|
+
newIndex[idx] = updated;
|
|
291
|
+
await writeIndex(home, newIndex);
|
|
292
|
+
});
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=conversations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversations.js","sourceRoot":"","sources":["../../src/infra/conversations.ts"],"names":[],"mappings":"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"}
|