This guide covers everything you need to know to contribute to ji.
- Prerequisites
- Development Setup
- Project Architecture
- Development Workflow
- Testing
- Code Style
- Publishing
- Troubleshooting
- Bun (v1.0.0 or higher) - JavaScript runtime and package manager
curl -fsSL https://bun.sh/install | bash - Git - Version control
- Jira Account - For testing against real Atlassian APIs
- AI Tool (optional) - Claude, Gemini, or Ollama for analyze command
# Clone the repository
git clone https://github.com/aaronshaf/ji.git
cd ji
# Install dependencies
bun install
# Link for local development
bun link# Set up Jira credentials
ji setup
# Or manually create ~/.ji/config.json
{
"jiraUrl": "https://your-domain.atlassian.net",
"username": "your-email@example.com",
"apiToken": "your-api-token"
}# Run in watch mode (auto-reload on changes)
bun run dev
# Run specific command for testing
bun run src/cli.ts mine
bun run src/cli.ts issue view PROJ-123
# Run with debug output
DEBUG=* bun run src/cli.ts mineji/
├── src/
│ ├── cli.ts # Main CLI entry point
│ ├── cli/
│ │ ├── index.ts # Command router
│ │ ├── commands/ # Individual command implementations
│ │ │ ├── analyze.ts # AI analysis command (Effect-based)
│ │ │ ├── auth.ts # Authentication setup
│ │ │ ├── board.ts # Board and sprint management
│ │ │ ├── comment.ts # Add comments (Effect-based)
│ │ │ ├── issue.ts # Issue viewing (Effect-based)
│ │ │ ├── mine.ts # Personal issues with filtering
│ │ │ ├── open.ts # Open issues in browser
│ │ │ ├── sprint.ts # Sprint overview
│ │ │ ├── take.ts # Assign issues to self
│ │ │ └── test.ts # Testing framework (Effect-based)
│ │ ├── formatters/ # Output formatting utilities
│ │ │ ├── issue.ts # Issue display formatting
│ │ │ ├── progress.ts # Progress bars
│ │ │ └── time.ts # Time formatting
│ │ └── utils/ # CLI utilities
│ │ └── time-parser.ts # Human time to JQL conversion
│ └── lib/
│ ├── config.ts # Configuration management
│ ├── jira-client.ts # Jira API client
│ ├── jira-client/ # Modular Jira client components
│ │ ├── jira-client-base.ts
│ │ ├── jira-client-boards.ts
│ │ ├── jira-client-comments.ts
│ │ ├── jira-client-issues.ts
│ │ ├── jira-client-sprints.ts
│ │ ├── jira-client-types.ts
│ │ └── jira-client-users.ts
│ ├── confluence-client.ts # Confluence API client
│ ├── confluence-converter.ts # Storage format converter
│ └── effects/ # Effect-based utilities
│ ├── errors.ts # Custom error types
│ ├── layers.ts # Effect layers
│ ├── jira/ # Jira-specific Effect code
│ └── test-layers.ts # Testing utilities
├── scripts/ # Build and maintenance scripts
├── docs/ # Documentation
└── test/ # Test files
We use Effect for type-safe, composable operations with proper error handling:
import { Effect, pipe } from 'effect';
import { Schema } from '@effect/schema';
// Define schemas
const IssueSchema = Schema.Struct({
key: Schema.String,
fields: Schema.Struct({
summary: Schema.String,
status: Schema.optional(Schema.Struct({
name: Schema.String
}))
})
});
// Create effects
const fetchIssue = (key: string) =>
Effect.tryPromise({
try: () => jiraClient.getIssue(key),
catch: (error) => new JiraApiError(`Failed to fetch ${key}: ${error}`)
});
// Compose with pipe
const processIssue = (key: string) =>
pipe(
fetchIssue(key),
Effect.flatMap(Schema.decodeUnknown(IssueSchema)),
Effect.map(formatIssue),
Effect.catchAll(handleError)
);Each command follows this pattern:
// commands/example.ts
export async function command(args: string[]): Promise<void> {
// 1. Parse arguments
const options = parseArgs(args);
// 2. Create Effect pipeline
const effect = pipe(
validateOptions(options),
Effect.flatMap(fetchData),
Effect.flatMap(processData),
Effect.flatMap(displayResults)
);
// 3. Run Effect
await Effect.runPromise(
effect.pipe(
Effect.catchAll(error =>
Console.error(`Error: ${error.message}`)
)
)
);
}class JiraClient {
constructor(private config: Config) {}
async request<T>(path: string, options?: RequestOptions): Promise<T> {
const url = `${this.config.jiraUrl}/rest/api/3/${path}`;
const response = await fetch(url, {
headers: {
'Authorization': `Basic ${this.getAuthHeader()}`,
'Content-Type': 'application/json',
...options?.headers
},
...options
});
if (!response.ok) {
throw new JiraApiError(response);
}
return response.json();
}
}# Always branch from main
git checkout main
git pull origin main
git checkout -b feature/your-feature-nameFollow these guidelines:
- Use Effect for new features requiring error handling
- Add types using Effect Schema instead of Zod
- Keep files under 500 lines (split large files)
- Write tests for new functionality
- Update documentation as needed
# Type checking
bun run typecheck
# Linting and formatting
bun run lint
bun run lint:fix
# Run tests
bun test
# Check file sizes
bun run check-file-sizes
# All checks (runs on pre-commit)
bun run pre-commitWe use conventional commits:
# Types: feat, fix, docs, style, refactor, test, chore
git commit -m "feat: add sprint filtering to mine command"
git commit -m "fix: handle missing assignee in issue view"
git commit -m "docs: update development setup instructions"# Push your branch
git push -u origin feature/your-feature-name
# Create PR using GitHub CLI
gh pr create --title "feat: your feature" --body "Description..."# Run all tests
bun test
# Run specific test file
bun test src/test/mine-command.test.ts
# Run with coverage
bun test --coverage
# Watch mode
bun test --watchBuilt-in testing for environment-specific commands:
# Configure tests for your environment
ji test --setup
# Run all configured tests
ji testFeatures:
- Environment-specific test cases (real issue keys, projects)
- Comprehensive coverage of all commands
- Pass/fail reporting with statistics
import { test, expect, mock } from 'bun:test';
import { Effect } from 'effect';
test('should fetch and format issue', async () => {
// Mock API response
const mockFetch = mock(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({
key: 'TEST-123',
fields: { summary: 'Test Issue' }
})
})
);
global.fetch = mockFetch;
// Run the effect
const result = await Effect.runPromise(
fetchAndFormatIssue('TEST-123')
);
// Assert
expect(result).toContain('TEST-123');
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/rest/api/3/issue/TEST-123')
);
});- Use
constby default,letwhen reassignment is needed - Prefer arrow functions for callbacks
- Use optional chaining (
?.) and nullish coalescing (??) - Avoid
anytype - useunknownor proper types
- Use Effect for operations that can fail
- Define custom error types with
_tagdiscriminator - Use
Effect.genfor sequential operations - Use
Effect.allfor parallel operations - Always handle errors explicitly
- Keep related functionality together
- Use barrel exports (index.ts) for clean imports
- Split large files (>500 lines) into smaller modules
- Group by feature, not by file type
The package is published as @aaronshaf/ji:
# Ensure you're on main with latest changes
git checkout main
git pull origin main
# Bump version
npm version patch # or minor/major
# Publish to npm (requires npm access)
npm publish
# Push tags
git push --tags origin mainKey files for npm publishing:
- package.json: Defines package metadata and entry points
- .npmignore: Excludes unnecessary files from package
- PUBLISHING.md: Detailed publishing instructions
- All tests pass (
bun test) - TypeScript compiles (
bun run typecheck) - Linting passes (
bun run lint) - README is up to date
- Version bumped appropriately
- CHANGELOG updated (if applicable)
# Check credentials
cat ~/.ji/config.json
# Re-run setup
ji setup# Clean and reinstall
rm -rf node_modules bun.lockb
bun install
# Check TypeScript version
bun run tsc --version# Run with verbose output
DEBUG=* bun test
# Check for environment issues
echo $NODE_ENV # Should not be 'production' for tests# Update Bun
bun upgrade
# Clear Bun cache
rm -rf ~/.bun/install/cache
# Check Bun version (should be 1.2.0+)
bun --version
# Reinstall dependencies
rm -rf node_modules bun.lockb
bun installEnable debug output for troubleshooting:
# Debug specific module
DEBUG=ji:* bun run src/cli.ts mine
# Debug everything
DEBUG=* bun run src/cli.ts mine
# Debug Effect operations
EFFECT_LOG_LEVEL=Debug bun run src/cli.ts mine
# Debug analyze command (shows full tool output)
DEBUG=1 ji analyze PROJ-123 --comment- Read this guide thoroughly
- Check existing issues and PRs
- Follow the code style and patterns
- Write tests for new features
- Update documentation as needed
- Submit PR with clear description
- Effect Documentation
- Bun Documentation
- Jira REST API
- Conventional Commits
- Agent Instructions
- Test Environment Setup
- Command Specifications
MIT - See LICENSE file for details