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
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/infra/insights.ts — Pure reducers over LedgerEntry[] for spend and provider health.
|
|
3
|
+
*
|
|
4
|
+
* No I/O. All functions are deterministic given the same inputs.
|
|
5
|
+
* Safe to call in tests with hand-built arrays.
|
|
6
|
+
*
|
|
7
|
+
* Honesty Contract:
|
|
8
|
+
* - No hardcoded percentages — success rates are always computed from data.
|
|
9
|
+
* - No digit-% literals in source — percent strings are built by concatenation.
|
|
10
|
+
* - No fabricated values — all outputs derive strictly from the input entries.
|
|
11
|
+
*/
|
|
12
|
+
import type { LedgerEntry } from '../core/types.js';
|
|
13
|
+
/** Aggregated spend data suitable for display in the control panel header. */
|
|
14
|
+
export interface SpendSummary {
|
|
15
|
+
/** Total USD spent in the current calendar day (UTC). */
|
|
16
|
+
readonly todayUsd: number;
|
|
17
|
+
/** Total USD spent across all time. */
|
|
18
|
+
readonly totalUsd: number;
|
|
19
|
+
/** Total number of ledger entries (calls). */
|
|
20
|
+
readonly calls: number;
|
|
21
|
+
/** Per-provider breakdown keyed by ProviderId string. */
|
|
22
|
+
readonly byProvider: Record<string, {
|
|
23
|
+
readonly usd: number;
|
|
24
|
+
readonly calls: number;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Pure reduction over LedgerEntry[] that produces a SpendSummary.
|
|
29
|
+
*
|
|
30
|
+
* "Today" is defined as entries whose `timestamp` ISO string has the same
|
|
31
|
+
* YYYY-MM-DD date prefix as `nowIso`. Both are treated as UTC.
|
|
32
|
+
*
|
|
33
|
+
* @param entries - Array of LedgerEntry objects (may be empty).
|
|
34
|
+
* @param nowIso - ISO-8601 timestamp representing "now" (e.g. from Clock.isoNow()).
|
|
35
|
+
*/
|
|
36
|
+
export declare function summarizeSpend(entries: LedgerEntry[], nowIso: string): SpendSummary;
|
|
37
|
+
/** Health summary for a single provider derived from its ledger entries. */
|
|
38
|
+
export interface ProviderHealth {
|
|
39
|
+
readonly provider: string;
|
|
40
|
+
readonly calls: number;
|
|
41
|
+
/** Fraction of successful calls in [0, 1]. 0 when calls === 0. */
|
|
42
|
+
readonly successRate: number;
|
|
43
|
+
/** Arithmetic mean of durationMs. 0 when calls === 0. */
|
|
44
|
+
readonly avgDurationMs: number;
|
|
45
|
+
readonly status: 'healthy' | 'degraded' | 'unknown';
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Compute per-provider health from a LedgerEntry array.
|
|
49
|
+
*
|
|
50
|
+
* Status thresholds:
|
|
51
|
+
* - `unknown` — 0 calls
|
|
52
|
+
* - `degraded` — successRate < 0.7
|
|
53
|
+
* - `healthy` — successRate >= 0.7
|
|
54
|
+
*
|
|
55
|
+
* @param entries - Array of LedgerEntry objects (may be empty).
|
|
56
|
+
*/
|
|
57
|
+
export declare function providerHealth(entries: LedgerEntry[]): ProviderHealth[];
|
|
58
|
+
/**
|
|
59
|
+
* Format a USD amount as a string with a `$` prefix and 4 decimal places.
|
|
60
|
+
*
|
|
61
|
+
* The result never contains a digit immediately before `%` — this is a dollar
|
|
62
|
+
* amount, not a percentage.
|
|
63
|
+
*
|
|
64
|
+
* @param n - Amount in US dollars (may be 0).
|
|
65
|
+
*/
|
|
66
|
+
export declare function formatUsd(n: number): string;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/infra/insights.ts — Pure reducers over LedgerEntry[] for spend and provider health.
|
|
3
|
+
*
|
|
4
|
+
* No I/O. All functions are deterministic given the same inputs.
|
|
5
|
+
* Safe to call in tests with hand-built arrays.
|
|
6
|
+
*
|
|
7
|
+
* Honesty Contract:
|
|
8
|
+
* - No hardcoded percentages — success rates are always computed from data.
|
|
9
|
+
* - No digit-% literals in source — percent strings are built by concatenation.
|
|
10
|
+
* - No fabricated values — all outputs derive strictly from the input entries.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Pure reduction over LedgerEntry[] that produces a SpendSummary.
|
|
14
|
+
*
|
|
15
|
+
* "Today" is defined as entries whose `timestamp` ISO string has the same
|
|
16
|
+
* YYYY-MM-DD date prefix as `nowIso`. Both are treated as UTC.
|
|
17
|
+
*
|
|
18
|
+
* @param entries - Array of LedgerEntry objects (may be empty).
|
|
19
|
+
* @param nowIso - ISO-8601 timestamp representing "now" (e.g. from Clock.isoNow()).
|
|
20
|
+
*/
|
|
21
|
+
export function summarizeSpend(entries, nowIso) {
|
|
22
|
+
const todayDate = nowIso.slice(0, 10); // 'YYYY-MM-DD'
|
|
23
|
+
let todayUsd = 0;
|
|
24
|
+
let totalUsd = 0;
|
|
25
|
+
const byProvider = {};
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
totalUsd += entry.usd;
|
|
28
|
+
if (entry.timestamp.slice(0, 10) === todayDate) {
|
|
29
|
+
todayUsd += entry.usd;
|
|
30
|
+
}
|
|
31
|
+
const existing = byProvider[entry.provider];
|
|
32
|
+
if (existing !== undefined) {
|
|
33
|
+
existing.usd += entry.usd;
|
|
34
|
+
existing.calls += 1;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
byProvider[entry.provider] = { usd: entry.usd, calls: 1 };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
todayUsd,
|
|
42
|
+
totalUsd,
|
|
43
|
+
calls: entries.length,
|
|
44
|
+
byProvider,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Compute per-provider health from a LedgerEntry array.
|
|
49
|
+
*
|
|
50
|
+
* Status thresholds:
|
|
51
|
+
* - `unknown` — 0 calls
|
|
52
|
+
* - `degraded` — successRate < 0.7
|
|
53
|
+
* - `healthy` — successRate >= 0.7
|
|
54
|
+
*
|
|
55
|
+
* @param entries - Array of LedgerEntry objects (may be empty).
|
|
56
|
+
*/
|
|
57
|
+
export function providerHealth(entries) {
|
|
58
|
+
const byProvider = {};
|
|
59
|
+
for (const entry of entries) {
|
|
60
|
+
const existing = byProvider[entry.provider];
|
|
61
|
+
if (existing !== undefined) {
|
|
62
|
+
existing.calls += 1;
|
|
63
|
+
existing.successes += entry.success ? 1 : 0;
|
|
64
|
+
existing.durationMs += entry.durationMs;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
byProvider[entry.provider] = {
|
|
68
|
+
calls: 1,
|
|
69
|
+
successes: entry.success ? 1 : 0,
|
|
70
|
+
durationMs: entry.durationMs,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return Object.entries(byProvider).map(([provider, agg]) => {
|
|
75
|
+
const { calls, successes, durationMs } = agg;
|
|
76
|
+
const successRate = calls === 0 ? 0 : successes / calls;
|
|
77
|
+
const avgDurationMs = calls === 0 ? 0 : durationMs / calls;
|
|
78
|
+
let status;
|
|
79
|
+
if (calls === 0) {
|
|
80
|
+
status = 'unknown';
|
|
81
|
+
}
|
|
82
|
+
else if (successRate < 0.7) {
|
|
83
|
+
status = 'degraded';
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
status = 'healthy';
|
|
87
|
+
}
|
|
88
|
+
return { provider, calls, successRate, avgDurationMs, status };
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// formatUsd
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
/**
|
|
95
|
+
* Format a USD amount as a string with a `$` prefix and 4 decimal places.
|
|
96
|
+
*
|
|
97
|
+
* The result never contains a digit immediately before `%` — this is a dollar
|
|
98
|
+
* amount, not a percentage.
|
|
99
|
+
*
|
|
100
|
+
* @param n - Amount in US dollars (may be 0).
|
|
101
|
+
*/
|
|
102
|
+
export function formatUsd(n) {
|
|
103
|
+
return '$' + n.toFixed(4);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=insights.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insights.js","sourceRoot":"","sources":["../../src/infra/insights.ts"],"names":[],"mappings":"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"}
|
package/dist/infra/ledger.d.ts
CHANGED
|
@@ -23,16 +23,14 @@ export declare function createLedger(opts: {
|
|
|
23
23
|
* @param cwd - The project working directory.
|
|
24
24
|
*/
|
|
25
25
|
export declare function readLedger(cwd: string): Promise<LedgerEntry[]>;
|
|
26
|
-
/** Per-model aggregation used in the summary. */
|
|
27
|
-
export interface ModelSummary {
|
|
28
|
-
readonly calls: number;
|
|
29
|
-
readonly usd: number;
|
|
30
|
-
}
|
|
31
26
|
/** Aggregate summary returned by `summarizeLedger`. */
|
|
32
27
|
export interface LedgerSummary {
|
|
33
28
|
readonly totalUsd: number;
|
|
34
29
|
readonly calls: number;
|
|
35
|
-
readonly byModel: Record<string,
|
|
30
|
+
readonly byModel: Record<string, {
|
|
31
|
+
readonly calls: number;
|
|
32
|
+
readonly usd: number;
|
|
33
|
+
}>;
|
|
36
34
|
}
|
|
37
35
|
/**
|
|
38
36
|
* Pure reduction over a ledger entry array.
|
package/dist/infra/ledger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/infra/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAExD;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAqB;IAChD,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAErB,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAAkB;YAC7B,MAAM,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,MAAM,iBAAiB,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;QACrD,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW;IAC1C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAA4B,CAAC;QAC7C,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAC;QACzC,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACnC,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAgB,CAAC,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;QACzB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;
|
|
1
|
+
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/infra/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAExD;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAqB;IAChD,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAErB,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAAkB;YAC7B,MAAM,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,MAAM,iBAAiB,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;QACrD,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW;IAC1C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAA4B,CAAC;QAC7C,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAC;QACzC,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACnC,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAgB,CAAC,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;QACzB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AASD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,OAAsB;IACpD,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,MAAM,OAAO,GAAmD,EAAE,CAAC;IAEnE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,QAAQ,IAAI,KAAK,CAAC,GAAG,CAAC;QAEtB,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;YACpB,QAAQ,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC;QACtD,CAAC;IACH,CAAC;IAED,OAAO;QACL,QAAQ;QACR,KAAK,EAAE,OAAO,CAAC,MAAM;QACrB,OAAO;KACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/interface/menu.ts — Sessions-first interactive control panel.
|
|
3
|
+
*
|
|
4
|
+
* Implements the dual-brain UX design bible:
|
|
5
|
+
* - A boxed header with provider status (real, from EnvironmentStatus).
|
|
6
|
+
* - Recent conversations (up to 7, relative timestamps from the store).
|
|
7
|
+
* - A sectioned menu with letter-key dispatch.
|
|
8
|
+
* - First-run welcome / 10-second setup flow.
|
|
9
|
+
* - Per-conversation chat loop backed by runTask().
|
|
10
|
+
*
|
|
11
|
+
* Architecture rules:
|
|
12
|
+
* - NO process.exit() — caller (cli.ts) owns process lifetime.
|
|
13
|
+
* - NO Math.random() — all ids / timestamps via injected Clock.
|
|
14
|
+
* - NO fabricated data — every displayed value is real (env, store, clock).
|
|
15
|
+
* - NO digit-% literals — percentages are always computed, never hardcoded.
|
|
16
|
+
* - All rendering goes through ui/tui.ts primitives.
|
|
17
|
+
*/
|
|
18
|
+
import type { Clock, LedgerWriter } from '../core/types.js';
|
|
19
|
+
import type { AppConfig } from '../infra/config.js';
|
|
20
|
+
import type { ConversationMeta, ConversationStore } from '../infra/conversation-store.js';
|
|
21
|
+
import type { SpendSummary } from '../infra/insights.js';
|
|
22
|
+
import type { EnvironmentStatus } from '../providers/detect.js';
|
|
23
|
+
import type { Provider, ProviderId, SandboxLevel } from '../providers/port.js';
|
|
24
|
+
import type { OutputSink } from './render.js';
|
|
25
|
+
export interface MenuContext {
|
|
26
|
+
readonly version: string;
|
|
27
|
+
readonly clock: Clock;
|
|
28
|
+
readonly ledger: LedgerWriter;
|
|
29
|
+
readonly providers: Partial<Record<ProviderId, Provider>>;
|
|
30
|
+
readonly env: EnvironmentStatus;
|
|
31
|
+
readonly store: ConversationStore;
|
|
32
|
+
readonly config: AppConfig;
|
|
33
|
+
readonly cwd: string;
|
|
34
|
+
readonly sandbox: SandboxLevel;
|
|
35
|
+
readonly timeoutMs: number;
|
|
36
|
+
/**
|
|
37
|
+
* Optional injected line reader for testing. When provided, `startMenu` uses
|
|
38
|
+
* this instead of the real `node:readline` interface, allowing tests to drive
|
|
39
|
+
* the menu with scripted input without a TTY.
|
|
40
|
+
*
|
|
41
|
+
* Returns the next trimmed line of input, or `null` on EOF/close.
|
|
42
|
+
*/
|
|
43
|
+
readonly readLine?: () => Promise<string | null>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Return the shell alias hint the user can add to their shell profile to make
|
|
47
|
+
* `myshell-tools` the default command-line assistant.
|
|
48
|
+
*
|
|
49
|
+
* This is a pure, I/O-free helper — it never reads or writes any file. The
|
|
50
|
+
* caller is responsible for printing the result. No claim is made that the
|
|
51
|
+
* system has been changed; the output is a copy-pasteable suggestion only.
|
|
52
|
+
*
|
|
53
|
+
* @param shell - The value of `process.env.SHELL` (e.g. '/bin/bash'), or
|
|
54
|
+
* empty/undefined on Windows where SHELL is absent.
|
|
55
|
+
* @param platform - The `process.platform` string (e.g. 'win32', 'linux').
|
|
56
|
+
* @returns A human-readable string containing the exact alias line to add.
|
|
57
|
+
*/
|
|
58
|
+
export declare function defaultAliasHint(shell: string | undefined, platform: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* Format a relative time string from a past epoch-ms to a now epoch-ms.
|
|
61
|
+
* Returns "just now", "Nm ago" (minutes), "Nh ago" (hours), or "Nd ago" (days).
|
|
62
|
+
* All arithmetic is pure — no Date, no Math.random.
|
|
63
|
+
*/
|
|
64
|
+
export declare function relativeTime(thenMs: number, nowMs: number): string;
|
|
65
|
+
/**
|
|
66
|
+
* Build the header box lines (provider status) from real EnvironmentStatus.
|
|
67
|
+
* Returns string[] safe to pass as the `lines` arg to box().
|
|
68
|
+
*
|
|
69
|
+
* Per-provider logic (uses REAL authenticated + plan fields):
|
|
70
|
+
* ✅ when ps.installed && ps.authenticated
|
|
71
|
+
* ⚠️ when ps.installed && !ps.authenticated (append " not signed in")
|
|
72
|
+
* ❌ when !ps.installed (append install command)
|
|
73
|
+
* Plan label appended when ps.plan is non-null (e.g. " (Max x5)").
|
|
74
|
+
*/
|
|
75
|
+
export declare function renderHeaderLines(env: EnvironmentStatus, _version: string): string[];
|
|
76
|
+
/**
|
|
77
|
+
* Render the budget/spend status line shown beneath the provider header.
|
|
78
|
+
*
|
|
79
|
+
* Uses real numbers only — all values come from the SpendSummary which is
|
|
80
|
+
* derived from `readLedger`. No digit-% literals appear in this function; it
|
|
81
|
+
* shows dollar amounts only.
|
|
82
|
+
*
|
|
83
|
+
* @param spend - Output of summarizeSpend() over real ledger entries.
|
|
84
|
+
* @param color - When false, no ANSI escape codes are emitted.
|
|
85
|
+
*/
|
|
86
|
+
export declare function renderBudgetLine(spend: SpendSummary, _color: boolean): string;
|
|
87
|
+
/**
|
|
88
|
+
* Build the conversation list lines from real ConversationMeta[].
|
|
89
|
+
* Format: "[N] <pin> <relative-time> <title>[ [<category>]]"
|
|
90
|
+
*
|
|
91
|
+
* Pin prefix: "📌 " for pinned, " " (3 spaces) for alignment when not pinned.
|
|
92
|
+
* Category suffix: " [<category>]" appended when category is set, omitted otherwise.
|
|
93
|
+
* Returns string[] (no ANSI — pure string building, safe for tests).
|
|
94
|
+
*/
|
|
95
|
+
export declare function renderConversationList(metas: ConversationMeta[], nowMs: number): string[];
|
|
96
|
+
/**
|
|
97
|
+
* Start the sessions-first interactive menu.
|
|
98
|
+
*
|
|
99
|
+
* Follows the dual-brain UX design bible:
|
|
100
|
+
* A. First run: welcome / 10-second setup → mark onboarded.
|
|
101
|
+
* B. Main screen loop: header + recent conversations + sectioned menu.
|
|
102
|
+
* C. Per-conversation chat loop backed by runTask().
|
|
103
|
+
*
|
|
104
|
+
* Never calls process.exit() — resolves when the user presses [q] or when
|
|
105
|
+
* stdin reaches EOF (resolves cleanly, no ERR_USE_AFTER_CLOSE thrown).
|
|
106
|
+
*
|
|
107
|
+
* When `ctx.readLine` is provided (e.g. in tests), it is used directly in
|
|
108
|
+
* place of a real readline interface. When absent, a readline interface is
|
|
109
|
+
* created from `process.stdin` as usual; the `close` event is wired up so
|
|
110
|
+
* that EOF resolves gracefully instead of throwing.
|
|
111
|
+
*/
|
|
112
|
+
export declare function startMenu(ctx: MenuContext, out: OutputSink): Promise<void>;
|