Skip to content

Latest commit

 

History

History
229 lines (169 loc) · 5 KB

File metadata and controls

229 lines (169 loc) · 5 KB

Code Standards

TypeScript patterns and conventions for IntellyWeave CLI.

TypeScript Guidelines

Configuration

  • Target: ES2022
  • Module: ESNext (bundler resolution)
  • Strict mode: Enabled (all strict flags on)

Module System

Use ESM with file extensions:

// CORRECT: Include .js extension
import { runMutation } from "./lib/execution.js";
import { getCliOptions } from "@/lib/options.js";

// INCORRECT: Missing extension
import { runMutation } from "./lib/execution";

Type Annotations

Prefer explicit types over any:

// GOOD
function processData(items: CollectionItem[]): ProcessResult {
  // ...
}

// ACCEPTABLE in error handling
catch (error: any) {
  console.error(error.message || String(error));
}

Naming Conventions

Type Convention Example
Files kebab-case weaviate-client.ts
Classes PascalCase WeaviateManager
Functions camelCase runMutation()
Constants UPPER_SNAKE_CASE DEFAULT_MODEL
Types/Interfaces PascalCase CliOptions
Private fields underscore prefix _client

Mutation Safety Pattern

CRITICAL: All data-modifying operations MUST use runMutation():

import { runMutation } from "../lib/execution.js";

// CORRECT: Wrapped in runMutation
const result = await runMutation(
  "import collection data",           // Human-readable description
  async () => await manager.import(), // The mutation function
  { collection: name }                // Optional context
);

if (result !== undefined) {
  // Mutation was executed (--live flag present)
  console.log("Success!");
}

// INCORRECT: Direct mutation call
await manager.import(); // Bypasses safety checks!

Rules

  • Never call mutations directly
  • Use runMutation() for create/update/delete/import
  • Read-only operations don't need wrapping
  • Do NOT add --dry-run flags (default IS dry-run)

Command Structure

All commands follow this pattern:

import { Command } from "commander";
import { runMutation } from "../lib/execution.js";
import { getCliOptions } from "../lib/options.js";

export const myCommand = new Command("mycommand")
  .description("Description of what this command does")
  .option("-f, --flag <value>", "Description of flag")
  .action(async (options) => {
    const opts = getCliOptions(); // Get global flags

    // Your command logic here

    // For mutations, use runMutation()
    const result = await runMutation(
      "perform action",
      async () => await doSomething(),
      { context: "data" }
    );
  });

Import Organization

Order imports as:

  1. External packages
  2. Internal modules (using @/ alias)
  3. Type imports (if separate)
// External packages
import { Command } from "commander";
import chalk from "chalk";

// Internal modules
import { runMutation } from "@/lib/execution.js";
import { getCliOptions } from "@/lib/options.js";

// Types (if separate)
import type { CliOptions } from "@/types/index.js";

Error Handling

try {
  const result = await operation();
  return result;
} catch (error) {
  if (error instanceof SpecificError) {
    throw new Error(`Failed to do X: ${error.message}`);
  }
  throw error; // Re-throw unknown errors
}

Linting

The project uses Biome (v2.2.6):

# Check for issues
npm run lint

# Auto-fix issues
npm run lint:fix

# Format code
npm run format

# Run all checks
npm run check

Biome Settings

  • Line width: 180 characters
  • Indent: 2 spaces
  • Quotes: Double quotes
  • Semicolons: Always
  • Trailing commas: ES5 style

Acceptable Warnings

  • noExplicitAny: OFF for error handling and global types
  • Prefer template literals (warning, not error)

File Organization

src/
├── cli.ts                 # Main entry point
├── commands/              # Command implementations
├── lib/                   # Shared utilities
├── shell/                 # Shell-specific features
└── types/                 # Type definitions

Best Practices

Keep Commands Thin

Commands should be thin wrappers that delegate to services:

// GOOD: Command delegates to service
.action(async () => {
  const manager = new WeaviateManager();
  const collections = await manager.listCollections();
  displayCollections(collections);
});

// AVOID: Command does everything
.action(async () => {
  const client = await weaviate.client();
  const schema = await client.schema.getter().do();
  // ... lots of logic
});

Use Path Aliases

// GOOD: Use @/ alias for src/
import { config } from "@/lib/config.js";

// AVOID: Deep relative paths
import { config } from "../../../lib/config.js";

Avoid Over-Engineering

  • Don't add features not requested
  • Keep solutions simple
  • No premature abstractions
  • Three similar lines > abstraction

See Also