Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
fcc114e
docs(plans): add inline type extraction plan for 28 findings across 1…
genesiscz Mar 18, 2026
e9a60cc
refactor(ask): extract ValidationResult and OutputFormatResult interf…
genesiscz Mar 18, 2026
cab9ce1
refactor(ask): extract EngineWithRestore interface
genesiscz Mar 18, 2026
4b412da
refactor(ask): extract WebSearchParams interface
genesiscz Mar 18, 2026
ca5206e
refactor(ask): extract ModelMetadata interface
genesiscz Mar 18, 2026
20cfe29
refactor(mcp-manager): extract ParsedCommand and KeyValuePair interfaces
genesiscz Mar 18, 2026
afe33b2
refactor(mcp-tsc): extract LspDiagnosticsNotification and QueueStats …
genesiscz Mar 18, 2026
288250e
refactor(github-release-notes): extract RepoIdentity interface
genesiscz Mar 18, 2026
9727494
refactor(telegram): extract StopHandle interface
genesiscz Mar 18, 2026
bd96a40
refactor(macos-resources): extract React component prop interfaces
genesiscz Mar 18, 2026
d8ac489
refactor(claude-history-dashboard): extract ProviderProps interface
genesiscz Mar 18, 2026
68f64f7
refactor(markdown-cli): extract MarkdownCLIOptions interface
genesiscz Mar 18, 2026
b525013
refactor(azure-devops): extract timelog prepare-import option interfaces
genesiscz Mar 18, 2026
24513b1
refactor(automate): extract StepHandlerResult to shared types
genesiscz Mar 18, 2026
ceb1418
refactor(utils): extract ParsedTableTokens interface
genesiscz Mar 18, 2026
750687b
refactor(mcp-web-reader): extract TokenLimitResult interface
genesiscz Mar 18, 2026
54b8433
refactor(internal): extract FileTransportOptions interface
genesiscz Mar 18, 2026
333f2eb
chore: remove completed inline type extraction plan
genesiscz Mar 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/Internal/LoggerLib/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,13 @@ export const consoleTransport: ITransport = {
},
};

export const fileTransport = (options: { filePath: string; append?: boolean; encoding?: string }): ITransport => ({
interface FileTransportOptions {
filePath: string;
append?: boolean;
encoding?: string;
}

export const fileTransport = (options: FileTransportOptions): ITransport => ({
log(props) {
// Here you would implement file writing logic
// Using appropriate React Native or Node.js APIs
Expand Down
7 changes: 6 additions & 1 deletion src/ask/AIChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import { providerManager } from "@ask/providers/ProviderManager";
import type { ChatConfig } from "@ask/types";
import { getLanguageModel } from "@ask/types";

interface EngineWithRestore {
engine: ChatEngine;
restore: () => void;
}

const DEFAULT_SESSION_DIR = resolve(homedir(), ".genesis-tools/ai-chat/sessions");

export class AIChat {
Expand Down Expand Up @@ -241,7 +246,7 @@ export class AIChat {
/** Get a ChatEngine instance with optional per-call overrides.
* Returns a restore function that resets the engine to its previous state.
*/
private _getEngine(override?: SendOptions["override"]): { engine: ChatEngine; restore: () => void } {
private _getEngine(override?: SendOptions["override"]): EngineWithRestore {
if (!this._engine) {
throw new Error("AIChat not initialized");
}
Expand Down
7 changes: 6 additions & 1 deletion src/ask/providers/ProviderManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import { getLanguageModel } from "@ask/types";
import type { AskConfig } from "@ask/types/config";
import { generateText } from "ai";

interface ModelMetadata {
id: string;
description?: string;
}

export class ProviderManager {
private detectedProviders: Map<string, DetectedProvider> = new Map();
private initialized = false;
Expand Down Expand Up @@ -598,7 +603,7 @@ export class ProviderManager {
};
}

private parseCapabilities(model: { id: string; description?: string }): string[] {
private parseCapabilities(model: ModelMetadata): string[] {
const capabilities: string[] = ["chat"];

if (model.description?.toLowerCase().includes("vision") || model.id.toLowerCase().includes("vision")) {
Expand Down
14 changes: 12 additions & 2 deletions src/ask/utils/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import { SafeJSON } from "@app/utils/json";
import type { Args, CLIOptions, OutputFormat } from "@ask/types";
import { Command } from "commander";

interface ValidationResult {
valid: boolean;
errors: string[];
}

interface OutputFormatResult {
type: OutputFormat;
filename?: string;
}

export function parseCLIArguments(): Args {
const program = new Command()
.name("ask")
Expand Down Expand Up @@ -162,7 +172,7 @@ export function showVersion(): void {
console.log("Multi-provider LLM chat application for GenesisTools");
}

export function validateOptions(options: CLIOptions): { valid: boolean; errors: string[] } {
export function validateOptions(options: CLIOptions): ValidationResult {
const errors: string[] = [];

// Validate temperature
Expand Down Expand Up @@ -253,7 +263,7 @@ export function shouldShowVersion(options: CLIOptions): boolean {
* -f json → json to stdout
* -f markdown -o out.md → markdown written to file
*/
export function getOutputFormat(options: CLIOptions): { type: OutputFormat; filename?: string } | undefined {
export function getOutputFormat(options: CLIOptions): OutputFormatResult | undefined {
// -o implies file output
if (options.output) {
return { type: "file", filename: options.output };
Expand Down
8 changes: 7 additions & 1 deletion src/ask/utils/websearch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import logger from "@app/logger";
import type { SearchResult, WebSearchOptions } from "@ask/types";

interface WebSearchParams {
query: string;
numResults?: number;
safeSearch?: string;
}

export class WebSearchTool {
private apiKey?: string;
private baseURL = "https://api.search.brave.com/res/v1";
Expand Down Expand Up @@ -175,7 +181,7 @@ export class WebSearchTool {
optional: true,
},
},
execute: async (params: { query: string; numResults?: number; safeSearch?: string }) => {
execute: async (params: WebSearchParams) => {
try {
const results = await this.searchWeb(params.query, {
numResults: params.numResults,
Expand Down
14 changes: 7 additions & 7 deletions src/automate/lib/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { SafeJSON } from "@app/utils/json";
import * as p from "@clack/prompts";
import { resolveExpression, resolveParams } from "./expressions.ts";
import type { ExecutionContext, PresetStep, StepResult } from "./types.ts";
import type { ExecutionContext, PresetStep, StepHandlerResult, StepResult } from "./types.ts";

/** The set of built-in action names that are handled directly (not via Bun.spawn) */
export const BUILTIN_ACTIONS = new Set(["if", "log", "prompt", "shell", "set"]);
Expand All @@ -20,7 +20,7 @@ export function isBuiltinAction(action: string): boolean {
export async function executeBuiltin(
step: PresetStep,
ctx: ExecutionContext
): Promise<{ result: StepResult; jumpTo?: string }> {
): Promise<StepHandlerResult> {
const start = Date.now();

switch (step.action) {
Expand All @@ -40,7 +40,7 @@ export async function executeBuiltin(
}

/** if -- evaluate condition expression, return jumpTo target step ID */
function handleIf(step: PresetStep, ctx: ExecutionContext, start: number): { result: StepResult; jumpTo?: string } {
function handleIf(step: PresetStep, ctx: ExecutionContext, start: number): StepHandlerResult {
if (!step.condition) {
throw new Error(`Step "${step.id}": "if" action requires a "condition" field`);
}
Expand All @@ -60,7 +60,7 @@ function handleIf(step: PresetStep, ctx: ExecutionContext, start: number): { res
}

/** log -- print a resolved message to console via @clack/prompts */
function handleLog(step: PresetStep, ctx: ExecutionContext, start: number): { result: StepResult } {
function handleLog(step: PresetStep, ctx: ExecutionContext, start: number): StepHandlerResult {
const params = step.params ? resolveParams(step.params as Record<string, unknown>, ctx) : {};
const message = String(params.message ?? "");

Expand All @@ -76,7 +76,7 @@ function handleLog(step: PresetStep, ctx: ExecutionContext, start: number): { re
}

/** prompt -- ask user a question interactively, store answer as output */
async function handlePrompt(step: PresetStep, ctx: ExecutionContext, start: number): Promise<{ result: StepResult }> {
async function handlePrompt(step: PresetStep, ctx: ExecutionContext, start: number): Promise<StepHandlerResult> {
const params = step.params ? resolveParams(step.params as Record<string, unknown>, ctx) : {};
const message = String(params.message ?? "Enter value:");
const defaultValue = params.default != null ? String(params.default) : undefined;
Expand Down Expand Up @@ -108,7 +108,7 @@ async function handlePrompt(step: PresetStep, ctx: ExecutionContext, start: numb
}

/** shell -- run a raw shell command via bash, capture stdout/stderr */
async function handleShell(step: PresetStep, ctx: ExecutionContext, start: number): Promise<{ result: StepResult }> {
async function handleShell(step: PresetStep, ctx: ExecutionContext, start: number): Promise<StepHandlerResult> {
const params = step.params ? resolveParams(step.params as Record<string, unknown>, ctx) : {};
const command = String(params.command ?? params.cmd ?? "");

Expand Down Expand Up @@ -162,7 +162,7 @@ async function handleShell(step: PresetStep, ctx: ExecutionContext, start: numbe
}

/** set -- set key-value pairs into ctx.vars */
function handleSet(step: PresetStep, ctx: ExecutionContext, start: number): { result: StepResult } {
function handleSet(step: PresetStep, ctx: ExecutionContext, start: number): StepHandlerResult {
const params = step.params ? resolveParams(step.params as Record<string, unknown>, ctx) : {};

for (const [key, value] of Object.entries(params)) {
Expand Down
4 changes: 2 additions & 2 deletions src/automate/lib/step-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { executeBuiltin, isBuiltinAction } from "./builtins.ts";
import { resolveExpression, resolveParams } from "./expressions.ts";
import type { StepContext } from "./registry.ts";
import { resolveStepHandler } from "./registry.ts";
import type { ExecutionContext, PresetStep, StepResult } from "./types.ts";
import type { ExecutionContext, PresetStep, StepHandlerResult, StepResult } from "./types.ts";

/**
* Execute a single step.
Expand All @@ -18,7 +18,7 @@ export async function executeStep(
step: PresetStep,
ctx: ExecutionContext,
options: { dryRun?: boolean; verbose?: boolean }
): Promise<{ result: StepResult; jumpTo?: string }> {
): Promise<StepHandlerResult> {
// Dispatch built-in actions
if (isBuiltinAction(step.action)) {
if (options.dryRun) {
Expand Down
6 changes: 6 additions & 0 deletions src/automate/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ export interface StepResult {
error?: string;
}

/** Result from a step handler, with optional jump target for branching */
export interface StepHandlerResult {
result: StepResult;
jumpTo?: string;
}

/** Run options from CLI flags */
export interface RunOptions {
dryRun?: boolean;
Expand Down
29 changes: 25 additions & 4 deletions src/azure-devops/commands/timelog/prepare-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@ interface PrepareImportFile {
entries: StoredEntry[];
}

interface TimelogAddOptions {
from?: string;
to?: string;
name?: string;
entry: string;
}

interface TimelogRemoveOptions {
name: string;
id: string;
}

interface TimelogListOptions {
name: string;
format?: string;
}

interface TimelogClearOptions {
name: string;
}

// ============= Helpers =============

const storage = new Storage("azure-devops");
Expand Down Expand Up @@ -83,7 +104,7 @@ function printEntry(entry: StoredEntry): void {

// ============= Subcommand Actions =============

async function handleAdd(options: { from?: string; to?: string; name?: string; entry: string }): Promise<void> {
async function handleAdd(options: TimelogAddOptions): Promise<void> {
const fileName = resolveFileName(options);

// Parse and validate entry JSON
Expand Down Expand Up @@ -187,7 +208,7 @@ async function handleAdd(options: { from?: string; to?: string; name?: string; e
printEntry(storedEntry);
}

async function handleRemove(options: { name: string; id: string }): Promise<void> {
async function handleRemove(options: TimelogRemoveOptions): Promise<void> {
const key = cacheKey(options.name);

const updated = await storage.atomicUpdate<PrepareImportFile>(key, (current) => {
Expand All @@ -207,7 +228,7 @@ async function handleRemove(options: { name: string; id: string }): Promise<void
console.log(`Entry ${options.id} removed. ${updated.entries.length} entries remaining.`);
}

async function handleList(options: { name: string; format?: string }): Promise<void> {
async function handleList(options: TimelogListOptions): Promise<void> {
const key = cacheKey(options.name);
const data = await storage.getCacheFile<PrepareImportFile>(key, "30 days");

Expand Down Expand Up @@ -276,7 +297,7 @@ async function handleList(options: { name: string; format?: string }): Promise<v
console.log(`\nGrand total: ${formatMinutes(grandTotal)}`);
}

async function handleClear(options: { name: string }): Promise<void> {
async function handleClear(options: TimelogClearOptions): Promise<void> {
const key = cacheKey(options.name);
await storage.deleteCacheFile(key);
console.log(`Prepare-import file "${options.name}" cleared.`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

interface ProviderProps {
children: React.ReactNode;
queryClient: QueryClient;
}

export function getContext() {
const queryClient = new QueryClient();
return {
queryClient,
};
}

export function Provider({ children, queryClient }: { children: React.ReactNode; queryClient: QueryClient }) {
export function Provider({ children, queryClient }: ProviderProps) {
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
}
7 changes: 6 additions & 1 deletion src/github-release-notes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ interface GitHubRelease {
html_url: string;
}

interface RepoIdentity {
owner: string;
repo: string;
}

interface ScriptOptions {
owner: string;
repo: string;
Expand Down Expand Up @@ -124,7 +129,7 @@ function generateMarkdown(releases: GitHubRelease[], owner: string, repo: string
return headerContent + releasesContent;
}

function parseRepoArg(repoArg: string): { owner: string; repo: string } | null {
function parseRepoArg(repoArg: string): RepoIdentity | null {
// Accepts owner/repo or full github.com URL
if (!repoArg) {
return null;
Expand Down
26 changes: 22 additions & 4 deletions src/macos-resources/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,24 @@ interface CommandPerformance {
timestamp: Date;
}

interface MemoizedHeaderProps {
children: React.ReactNode;
sortBy: "cpu" | "pid" | "files";
}

interface MemoizedCellProps {
children: React.ReactNode;
column: number;
}

interface MemoizedNotificationsPanelProps {
notifications: Notification[];
}

interface MemoizedCommandPanelProps {
commandHistory: CommandPerformance[];
}

// Custom Table component using Ink's Box and Text
// const Table: React.FC<{
// data: Array<Record<string, string>>;
Expand Down Expand Up @@ -147,7 +165,7 @@ const generateTableData = (processes: ProcessInfo[], selectedIndex: number) => {

// Memoized header component
const MemoizedHeader = React.memo(
({ children, sortBy }: { children: React.ReactNode; sortBy: "cpu" | "pid" | "files" }) => {
({ children, sortBy }: MemoizedHeaderProps) => {
const headerText = String(children);
let sortIndicator = "";
let color = "blue";
Expand All @@ -173,7 +191,7 @@ const MemoizedHeader = React.memo(
);

// Memoized cell component
const MemoizedCell = React.memo(({ children, column }: { children: React.ReactNode; column: number }) => {
const MemoizedCell = React.memo(({ children, column }: MemoizedCellProps) => {
return (
<Text color={column === 0 ? "green" : column === 2 ? "yellow" : column === 4 ? "cyan" : undefined}>
{children}
Expand All @@ -182,7 +200,7 @@ const MemoizedCell = React.memo(({ children, column }: { children: React.ReactNo
});

// Memoized notifications panel
const MemoizedNotificationsPanel = React.memo(({ notifications }: { notifications: Notification[] }) => {
const MemoizedNotificationsPanel = React.memo(({ notifications }: MemoizedNotificationsPanelProps) => {
return (
<Box flexDirection="column" width="25%" height="50" marginLeft={1} borderStyle="single">
<Box marginBottom={1}>
Expand All @@ -204,7 +222,7 @@ const MemoizedNotificationsPanel = React.memo(({ notifications }: { notification
});

// Memoized command performance panel
const MemoizedCommandPanel = React.memo(({ commandHistory }: { commandHistory: CommandPerformance[] }) => {
const MemoizedCommandPanel = React.memo(({ commandHistory }: MemoizedCommandPanelProps) => {
return (
<Box flexDirection="column" width="25%" height="50%" marginLeft={1} borderStyle="single" overflow="hidden">
<Box marginBottom={1}>
Expand Down
9 changes: 8 additions & 1 deletion src/markdown-cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import { type MarkdownRenderOptions, renderMarkdownToCli } from "@app/utils/mark
import chokidar from "chokidar";
import { Command, Option } from "commander";

interface MarkdownCLIOptions {
watch?: boolean;
width?: number;
theme?: string;
color?: boolean;
}

const program = new Command();

program
Expand All @@ -19,7 +26,7 @@ program
.default("dark")
)
.option("--no-color", "Strip ANSI color codes from output")
.action((file?: string, opts?: { watch?: boolean; width?: number; theme?: string; color?: boolean }) => {
.action((file?: string, opts?: MarkdownCLIOptions) => {
const renderOpts: MarkdownRenderOptions = {
width: opts?.width && !Number.isNaN(opts.width) ? opts.width : undefined,
theme: (opts?.theme as MarkdownRenderOptions["theme"]) || "dark",
Expand Down
Loading
Loading