indusagi-coding-agent 0.1.60 → 0.1.62

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.
@@ -120,6 +120,7 @@ type StateAction = {
120
120
  } | {
121
121
  readonly type: "usage";
122
122
  readonly usage: Usage;
123
+ readonly contextTokens: number;
123
124
  } | {
124
125
  readonly type: "model";
125
126
  readonly modelId: string;
@@ -269,6 +269,13 @@ export interface ConductorState {
269
269
  readonly head: SessionHead;
270
270
  /** Cumulative token/cost spend across the session so far. */
271
271
  readonly usage: Usage;
272
+ /**
273
+ * Tokens occupying the model's context window as of the most recent turn —
274
+ * the last assistant turn's reported usage, NOT the cumulative session spend.
275
+ * This is what the footer's `ctx:%` divides by the context window;
276
+ * {@link usage}.totalTokens grows unbounded across turns and would inflate it.
277
+ */
278
+ readonly contextTokens: number;
272
279
  /** Canonical id of the model currently bound to the session. */
273
280
  readonly modelId: string;
274
281
  /** The fault from the most recent turn, when {@link phase} is `"faulted"`. */
@@ -33,6 +33,26 @@ export interface BannerProps {
33
33
  * single compact header line. Wired from the verbose / quiet-startup flag.
34
34
  */
35
35
  readonly quiet?: boolean;
36
+ /**
37
+ * When set, the masthead auto-condenses to a single emblem + brand + model
38
+ * line (the repeat-launch presentation): the user has already seen this
39
+ * version's full masthead, so the big wordmark is skipped. Distinct from
40
+ * {@link quiet}, which is the manual suppression toggle; either collapses the
41
+ * banner, but `compact` keeps the small emblem and the welcome line.
42
+ */
43
+ readonly compact?: boolean;
44
+ /**
45
+ * The signed-in account label, used for the personalized "Welcome back,
46
+ * {name}!" line. Absent → the plain "Welcome back!" fallback is shown.
47
+ */
48
+ readonly name?: string;
49
+ /**
50
+ * Opt-in static colour-sweep flourish: when set, the wordmark and emblem fill
51
+ * are tinted along a frozen primary→secondary gradient instead of the flat
52
+ * accent. The caller is responsible for suppressing it under reduced-motion /
53
+ * non-TTY; this prop is simply the resolved on/off decision.
54
+ */
55
+ readonly sweep?: boolean;
36
56
  /** The gathered session resources rendered as the Startup Map panel. */
37
57
  readonly startup?: StartupMap;
38
58
  /** Out-of-band lines drawn above the wordmark (errors, warnings, info). */
@@ -43,12 +63,14 @@ export interface BannerProps {
43
63
  /**
44
64
  * Render the console masthead.
45
65
  *
46
- * In the default (loud) mode this is the block-letter wordmark, the brand /
47
- * version line, the optional notices region, the bordered Startup Map, and the
48
- * changelog block. The {@link BannerProps.quiet} flag collapses all of that to a
49
- * single compact header line (brand + version + model), still carrying the
50
- * notices and a condensed changelog so nothing important is silently dropped.
66
+ * In the default (loud) mode this is the two-tone emblem beside the block-letter
67
+ * wordmark, the brand / version line, the personalized welcome line, the
68
+ * optional notices region, the bordered Startup Map, and the changelog block.
69
+ * The {@link BannerProps.quiet} or {@link BannerProps.compact} flags collapse
70
+ * all of that to a single compact header line (emblem glyph + brand + version +
71
+ * model), still carrying the welcome line, the notices, and a condensed
72
+ * changelog so nothing important is silently dropped.
51
73
  *
52
74
  * @param props the wordmark context, version, session facts, and startup chrome
53
75
  */
54
- export declare function Banner({ theme, modelId, workspace, version, verbose, quiet, startup, notices, changelog, }: BannerProps): JSX.Element;
76
+ export declare function Banner({ theme, modelId, workspace, version, verbose, quiet, compact, name, sweep, startup, notices, changelog, }: BannerProps): JSX.Element;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Emblem — the two-tone block monogram that sits to the left of the wordmark
3
+ * (roadmap #2).
4
+ *
5
+ * Before this, the masthead was lettering alone — the "INDUS CODE" wordmark
6
+ * *was* the whole identity, painted in one flat accent. The emblem gives the
7
+ * brand a *mark*: a small fixed-width block glyph built from the box quadrant
8
+ * family (`▛ ▜ ▙ ▟ ▝ ▘ ▗ ▖ █ ▓`), split per row into a bright "fill" span and a
9
+ * dim "shadow" span so it reads as a single solid shape lit from one corner.
10
+ * The fill is the primary accent; the shadow is the secondary accent — the two
11
+ * distinct palette hues the scheme already carries — so the mark is genuinely
12
+ * two-tone rather than a single colour with a darker edge.
13
+ *
14
+ * It is purely presentational: a fixed glyph grid tinted through the framework
15
+ * {@link InkThemeAdapter} at render time, no state, no effects, no props beyond
16
+ * the theme and an optional row-colour override the banner uses to fold the
17
+ * emblem's fill into a frozen colour-sweep.
18
+ *
19
+ * Layout is a `flexDirection="column"` of one `<Box>` per row; within a row the
20
+ * fill and shadow are adjacent `<Text>` spans so each row's two tones share a
21
+ * line. The whole emblem is the same display width on every row, so it stacks
22
+ * into a clean column the wordmark can sit beside in a parent row Box.
23
+ */
24
+ import type { InkThemeAdapter } from "indusagi/react-ink";
25
+ /** How many terminal rows the emblem occupies (its glyph-grid height). */
26
+ export declare const EMBLEM_HEIGHT: number;
27
+ /** What the {@link Emblem} renders. */
28
+ export interface EmblemProps {
29
+ /** The framework adapter that turns token roles into terminal colours. */
30
+ readonly theme: InkThemeAdapter;
31
+ /**
32
+ * Optional per-row fill colour (a `#rrggbb` hex), indexed by row. When supplied
33
+ * (the banner's frozen colour-sweep), row `i`'s fill is painted with
34
+ * `rowColors[i]` via chalk's hex path instead of the flat accent role; the
35
+ * shadow keeps the secondary accent so the mark stays two-tone. Absent rows
36
+ * fall back to the accent role.
37
+ */
38
+ readonly rowColors?: readonly string[];
39
+ }
40
+ /**
41
+ * Render the two-tone block emblem.
42
+ *
43
+ * Each row paints its fill span in the accent role (or the supplied sweep colour
44
+ * for that row) and its shadow span in the secondary `customMessage` role, so the
45
+ * monogram reads as one solid mark with a lit and a shadowed face.
46
+ *
47
+ * @param props the theme adapter and an optional per-row fill-colour override
48
+ */
49
+ export declare function Emblem({ theme, rowColors }: EmblemProps): JSX.Element;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Banner colour-sweep helpers — the pure, render-agnostic maths behind the
3
+ * optional startup flourish (roadmap #14) and the personalized welcome line
4
+ * (#7).
5
+ *
6
+ * Two concerns, no React or Ink, no I/O, no state:
7
+ *
8
+ * - {@link rowGradient} computes a *frozen* (single-pass, no animation timer)
9
+ * colour for one wordmark row by lerping between two palette hexes across
10
+ * the block's rows. The banner paints row `i` of `n` with this colour when
11
+ * the opt-in sweep is on; off, the wordmark stays a flat accent. Because it
12
+ * takes no clock and never schedules a frame, it is reduced-motion safe by
13
+ * construction — the suppression decision lives at the call site, which
14
+ * simply does not ask for a gradient when motion is reduced.
15
+ *
16
+ * - {@link welcomeLine} formats the "Welcome back{, name}!" greeting from the
17
+ * signed-in account label, with a length guard and a name-less fallback, so
18
+ * the banner and its tests share one formatter rather than re-deriving the
19
+ * string.
20
+ *
21
+ * Everything here is referentially transparent: same inputs → same output.
22
+ */
23
+ /**
24
+ * Format the personalized welcome greeting.
25
+ *
26
+ * Returns `"Welcome back, {name}!"` for a usable name, and the plain
27
+ * `"Welcome back!"` fallback when the name is absent, blank, or too long to read
28
+ * as a name. The name is trimmed of surrounding whitespace before the checks so
29
+ * a padded label does not slip past the guards.
30
+ *
31
+ * @param name the signed-in account label, if one is known
32
+ * @returns the greeting string (never the tag markup — plain text)
33
+ */
34
+ export declare function welcomeLine(name?: string): string;
35
+ /**
36
+ * The frozen sweep colour for one wordmark row.
37
+ *
38
+ * Lerps from `primaryHex` (the first row) to `secondaryHex` (the last row)
39
+ * across the `rowCount` rows of the block, returning the colour for row
40
+ * `rowIndex` as a `#rrggbb` hex string. A single-row block pins to the primary;
41
+ * the endpoints are exact (row 0 = primary, row n-1 = secondary). If either hex
42
+ * cannot be parsed, the corresponding raw input hex is returned so the banner
43
+ * still gets a usable colour string rather than nothing.
44
+ *
45
+ * This computes a *static* gradient — there is no timer, no frame loop, no
46
+ * clock. The whole sweep is laid down in the one render pass, which is exactly
47
+ * why it is safe under reduced-motion: the flourish is colour, not movement.
48
+ *
49
+ * @param rowIndex the zero-based row being painted
50
+ * @param rowCount the total number of rows in the wordmark block
51
+ * @param primaryHex the colour of the first row (e.g. the accent)
52
+ * @param secondaryHex the colour of the last row (e.g. the secondary accent)
53
+ * @returns a `#rrggbb` hex string (or a raw input hex when one is unparseable)
54
+ */
55
+ export declare function rowGradient(rowIndex: number, rowCount: number, primaryHex: string, secondaryHex: string): string;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Banner colour-sweep helpers — pure behavioural tests.
3
+ *
4
+ * These exercise the render-agnostic maths behind the masthead flourish without
5
+ * mounting Ink: the welcome-line formatter (#7) and the frozen colour-sweep
6
+ * gradient (#14). Both are referentially transparent, so the assertions are
7
+ * exact.
8
+ */
9
+ export {};
@@ -56,13 +56,17 @@ export type { SessionSnapshot, ToolExecutionState, StatusMessage, UiDisplayBlock
56
56
  /** Re-exported conductor vocabulary the console renders and drives. */
57
57
  export type { SessionConductor, SessionSignal, ConductorState };
58
58
  /**
59
- * The two built-in colour schemes the console ships with.
60
- *
61
- * Named for the time-of-day they evoke rather than a bare light/dark axis:
62
- * - `midnight` a low-luminance scheme for dark terminals.
63
- * - `daylight` — a high-luminance scheme for light terminals.
59
+ * The built-in colour schemes the console ships with.
60
+ *
61
+ * Named for the time-of-day they evoke rather than a bare light/dark axis, with
62
+ * a daltonized (color-blind-friendly) variant of each:
63
+ * - `midnight` — a low-luminance scheme for dark terminals.
64
+ * - `daylight` — a high-luminance scheme for light terminals.
65
+ * - `midnight-cb` — the dark scheme re-derived so success vs failure separates
66
+ * off the red-green axis (success → blue), deuteran/protan-safe.
67
+ * - `daylight-cb` — the light scheme's color-blind-safe counterpart.
64
68
  */
65
- export type ThemeScheme = "midnight" | "daylight";
69
+ export type ThemeScheme = "midnight" | "daylight" | "midnight-cb" | "daylight-cb";
66
70
  /** The default scheme applied before any user preference is loaded. */
67
71
  export declare const DEFAULT_SCHEME: ThemeScheme;
68
72
  /**
@@ -96,6 +100,23 @@ export declare function isThemeScheme(value: string): value is ThemeScheme;
96
100
  * - `caution` — warning status tone.
97
101
  * - `alarm` — error/fault status tone.
98
102
  * - `pending` — busy/in-flight status tone.
103
+ *
104
+ * Rich-render roles (markdown / diff / syntax highlighting). These feed the
105
+ * framework adapter's markdown/diff/highlight accessors so the styled
106
+ * transcript, colored diffs, and fenced-code highlighting recolor for free with
107
+ * each scheme:
108
+ * - `codeInline` — inline `` `code` `` foreground.
109
+ * - `heading` — markdown heading foreground.
110
+ * - `blockquoteBar` — the dim bar drawn beside a blockquote.
111
+ * - `diffAddedBg` — background tint for an added (`+`) diff line.
112
+ * - `diffRemovedBg` — background tint for a removed (`-`) diff line.
113
+ * - `diffAddedText` — foreground for added-line content / `+` marker.
114
+ * - `diffRemovedText`— foreground for removed-line content / `-` marker.
115
+ * - `synKeyword` — syntax scope: keywords.
116
+ * - `synString` — syntax scope: string literals.
117
+ * - `synNumber` — syntax scope: numeric literals.
118
+ * - `synComment` — syntax scope: comments.
119
+ * - `synType` — syntax scope: types / classes / built-ins.
99
120
  */
100
121
  export interface ThemeTokens {
101
122
  readonly signal: string;
@@ -111,6 +132,18 @@ export interface ThemeTokens {
111
132
  readonly caution: string;
112
133
  readonly alarm: string;
113
134
  readonly pending: string;
135
+ readonly codeInline: string;
136
+ readonly heading: string;
137
+ readonly blockquoteBar: string;
138
+ readonly diffAddedBg: string;
139
+ readonly diffRemovedBg: string;
140
+ readonly diffAddedText: string;
141
+ readonly diffRemovedText: string;
142
+ readonly synKeyword: string;
143
+ readonly synString: string;
144
+ readonly synNumber: string;
145
+ readonly synComment: string;
146
+ readonly synType: string;
114
147
  }
115
148
  /** The closed set of token role names, for iteration and validation. */
116
149
  export type ThemeToken = keyof ThemeTokens;
@@ -42,6 +42,25 @@ import type { InkThemeAdapter, ThemeTokens } from "../contract";
42
42
  * info ← notice (informational status tone)
43
43
  * highlight ← inkText (high-contrast on-accent text)
44
44
  *
45
+ * Plus the markdown / diff / syntax-highlight role keys the framework's rich
46
+ * render path (`theme.role(...)` / `theme.roleBackground(...)`) resolves. These
47
+ * key names match the framework adapter's default role → key map exactly, so the
48
+ * styled transcript, colored diffs, and fenced-code highlighting resolve their
49
+ * colours straight from the console's derived tokens:
50
+ *
51
+ * codeInline ← codeInline (inline `code` foreground)
52
+ * heading ← heading (markdown heading foreground)
53
+ * blockquoteBar ← blockquoteBar (dim quote bar)
54
+ * diffAddedBg ← diffAddedBg (added-line background tint)
55
+ * diffRemovedBg ← diffRemovedBg (removed-line background tint)
56
+ * diffAddedText ← diffAddedText (added foreground / `+`)
57
+ * diffRemovedText ← diffRemovedText (removed foreground / `-`)
58
+ * synKeyword ← synKeyword (syntax: keywords)
59
+ * synString ← synString (syntax: strings)
60
+ * synNumber ← synNumber (syntax: numbers)
61
+ * synComment ← synComment (syntax: comments)
62
+ * synType ← synType (syntax: types / classes)
63
+ *
45
64
  * @param tokens the derived semantic token map for a scheme
46
65
  * @returns a flat colour Record keyed by the framework's vocabulary
47
66
  */
@@ -12,7 +12,7 @@
12
12
  * Consumers import from `src/console/theme` and never reach into the individual
13
13
  * palette/tokens/adapter/resolve modules.
14
14
  */
15
- export { MIDNIGHT_PALETTE, DAYLIGHT_PALETTE, PALETTES } from "./palette";
15
+ export { MIDNIGHT_PALETTE, DAYLIGHT_PALETTE, MIDNIGHT_CB_PALETTE, DAYLIGHT_CB_PALETTE, PALETTES, } from "./palette";
16
16
  export { deriveTokens } from "./tokens";
17
17
  export { themeAdapter, frameworkColors } from "./adapter";
18
18
  export { THEMES, THEME_SCHEMES, resolveTheme } from "./resolve";
@@ -33,6 +33,31 @@ export declare const MIDNIGHT_PALETTE: ThemePalette;
33
33
  * to a dark-on-light slate.
34
34
  */
35
35
  export declare const DAYLIGHT_PALETTE: ThemePalette;
36
+ /**
37
+ * The dark-terminal color-blind-safe ramp.
38
+ *
39
+ * A clone of {@link MIDNIGHT_PALETTE} that re-derives the three *status* hues so
40
+ * a red-green color-blind user (deuteran/protan, ~8% of men) can tell success
41
+ * from failure without relying on the red-green axis. Success moves off green
42
+ * onto a vivid blue (`affirm → #4aa3ff`); failure stays red but is deepened to a
43
+ * darker tone (`alarm → #c83232`) so it sits well below the bright blue in
44
+ * lightness, and the amber warning is nudged brighter — so the three status
45
+ * tones separate by *lightness*, not hue alone. The three accent hues and the
46
+ * neutral gradient are carried over unchanged from the base midnight ramp — only
47
+ * the status stops move, so the overall look stays the midnight scheme.
48
+ */
49
+ export declare const MIDNIGHT_CB_PALETTE: ThemePalette;
50
+ /**
51
+ * The light-terminal color-blind-safe ramp.
52
+ *
53
+ * The daylight counterpart of {@link MIDNIGHT_CB_PALETTE}: clones
54
+ * {@link DAYLIGHT_PALETTE} and remaps success to a deeper blue tuned for a
55
+ * bright background (`affirm → #1f6fd6`), deepens the red alarm
56
+ * (`alarm → #b02622`) for lightness separation from that blue, and darkens the
57
+ * amber warning so the three status tones stay separable by lightness on a light
58
+ * terminal too.
59
+ */
60
+ export declare const DAYLIGHT_CB_PALETTE: ThemePalette;
36
61
  /**
37
62
  * The raw ramp for each scheme, keyed by {@link ThemeScheme}.
38
63
  *
@@ -25,6 +25,28 @@
25
25
  * - `caution` ← `caution` (warning tone)
26
26
  * - `alarm` ← `alarm` (error/fault tone)
27
27
  * - `pending` ← `primary` (busy/in-flight tone)
28
+ *
29
+ * Rich-render roles (markdown / diff / syntax highlighting) are derived from the
30
+ * same nine stops so the rebuild's new styled-transcript / colored-diff /
31
+ * fenced-code surfaces recolour with the scheme and need no extra palette stop:
32
+ *
33
+ * - `codeInline` ← `primary` (inline code accent)
34
+ * - `heading` ← `primary` (heading accent)
35
+ * - `blockquoteBar` ← `muted` (dim quote bar)
36
+ * - `diffAddedBg` ← `affirm` darkened toward ink (added-line tint)
37
+ * - `diffRemovedBg` ← `alarm` darkened toward ink (removed-line tint)
38
+ * - `diffAddedText` ← `affirm` (added foreground / `+`)
39
+ * - `diffRemovedText` ← `alarm` (removed foreground / `-`)
40
+ * - `synKeyword` ← `primary` (keywords)
41
+ * - `synString` ← `affirm` (strings)
42
+ * - `synNumber` ← `caution` (numbers)
43
+ * - `synComment` ← `muted` (comments)
44
+ * - `synType` ← `tertiary` (types / classes)
45
+ *
46
+ * Deriving the two diff backgrounds (rather than adding raw stops) keeps the
47
+ * {@link ThemePalette} a flat nine-stop ramp while guaranteeing every scheme —
48
+ * including the color-blind variants — gets a legible `+`/`-` tint that tracks
49
+ * its own success/alarm hue.
28
50
  */
29
51
  import type { ThemePalette, ThemeTokens } from "../contract";
30
52
  /**
@@ -35,6 +57,6 @@ import type { ThemePalette, ThemeTokens } from "../contract";
35
57
  * assignments are shared by both schemes — only the ramp handed in differs.
36
58
  *
37
59
  * @param palette the raw nine-stop ramp for a scheme
38
- * @returns the thirteen-role semantic token map
60
+ * @returns the full semantic token map (status + rich-render roles)
39
61
  */
40
62
  export declare function deriveTokens(palette: ThemePalette): ThemeTokens;
@@ -21,4 +21,4 @@ export * as insight from "./insight";
21
21
  export * as kit from "./kit";
22
22
  export * as settings from "./settings";
23
23
  export * as sessions from "./sessions";
24
- export declare const VERSION = "0.1.60";
24
+ export { VERSION } from "./workspace";
@@ -93,6 +93,18 @@ export interface Preferences {
93
93
  defaultThinkingLevel?: ThinkingLevel;
94
94
  /** Suppress the banner / tips shown on a normal interactive launch. */
95
95
  quietStartup?: boolean;
96
+ /**
97
+ * The last product version whose full masthead the user has already seen. The
98
+ * banner auto-condenses to a one-line header when this equals the running
99
+ * version (and nothing else demands the full block); it is rewritten to the
100
+ * running version after the full masthead is shown, so a launch only shows the
101
+ * big wordmark on the first sight of a version or a version bump.
102
+ */
103
+ lastSeenVersion?: string;
104
+ /** Opt-in static colour-sweep flourish tinting the startup wordmark / emblem. */
105
+ logoSweep?: boolean;
106
+ /** When set, motion-flavoured flourishes (e.g. the logo sweep) are suppressed. */
107
+ reducedMotion?: boolean;
96
108
  /** Whether the window-budget manager compacts the transcript automatically. */
97
109
  autoCompact?: boolean;
98
110
  /** What a double-escape does in the console. */
@@ -18,3 +18,9 @@ import type { Brand } from "../boot/contract";
18
18
  * rather than a runtime surprise downstream.
19
19
  */
20
20
  export declare const BRAND: Brand;
21
+ /**
22
+ * The product version — printed by `--version` and shown on the startup banner.
23
+ * Single source of truth: bump this one line per release. Co-located with the
24
+ * brand so `boot` reads it without importing the index barrel.
25
+ */
26
+ export declare const VERSION = "0.1.62";
@@ -5,7 +5,7 @@
5
5
  * record, and the runtime/packaging detection. Boot stages depend on this
6
6
  * module rather than reaching into the individual files.
7
7
  */
8
- export { BRAND } from "./brand";
8
+ export { BRAND, VERSION } from "./brand";
9
9
  export { createWorkspace, ensureDirs, type WorkspaceOverrides, } from "./locator";
10
10
  export { isPackagedBinary, resolvePackageRoot, detectPackagingFromModuleUrl, type Packaging, } from "./runtime-detect";
11
11
  export type { Brand, Workspace } from "../boot/contract";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "indusagi-coding-agent",
3
- "version": "0.1.60",
3
+ "version": "0.1.62",
4
4
  "description": "Indusagi coding agent — a terminal-first AI coding agent, built from scratch on the indusagi framework.",
5
5
  "author": "Varun Israni",
6
6
  "license": "MIT",
@@ -46,7 +46,7 @@
46
46
  "@sinclair/typebox": "^0.34.49",
47
47
  "chalk": "^5.6.2",
48
48
  "highlight.js": "^11.11.1",
49
- "indusagi": "^0.12.33",
49
+ "indusagi": "^0.12.34",
50
50
  "ink": "^5.2.1",
51
51
  "jiti": "^2.7.0",
52
52
  "marked": "^18.0.4",