@happyvertical/smrt-projects

Provider-agnostic project management -- GitHub-style issues, PRs, projects, and repositories with Living Spec AI synthesis.

v0.20.44Project ManagementGitHub-style

Overview

smrt-projects manages repositories, issues, pull requests, and project boards with sync support for external providers (GitHub primary, GitLab/Bitbucket/Azure types defined). Features AI-powered Living Spec pattern where comments are synthesized into issue bodies, and PullRequest extends Issue via STI.

Installation

bash
npm install @happyvertical/smrt-projects

Quick Start

typescript
import {
  Repository, RepositoryCollection,
  Issue, IssueCollection,
  PullRequest, PullRequestCollection,
  Project, ProjectCollection
} from '@happyvertical/smrt-projects';

// Initialize
const repos = new RepositoryCollection(db);
const issues = new IssueCollection(db);

// Connect repository
const repo = await repos.create({
  owner: 'happyvertical',
  name: 'smrt',
  providerType: 'github',
  tokenConfigKey: 'GITHUB_TOKEN'  // From env, not stored in DB
});
await repo.save();

// Sync repository metadata
await repo.sync();

// Discover issues
const discoveredIssues = await repo.getIssues({ state: 'open' });

// Get or create tracked issue
const issue = await issues.create({
  repositoryId: repo.id,
  number: 123,
  title: 'Add dark mode',
  body: 'We should support dark mode',
  state: 'open'
});
await issue.save();

// Living Spec: Synthesize comments into body
const result = await issue.incorporateFeedback({
  applyUpdate: true,  // Update issue on GitHub
  model: 'sonnet'
});
console.log(result.synthesized); // Updated body with comments

Core Models

Repository

typescript
class Repository extends SmrtObject {
  owner: string
  name: string
  providerType: 'github' | 'gitlab' | 'bitbucket' | 'azure'
  tokenConfigKey: string   // Env var name (not the token!) -- resolved at runtime
  lastSyncedAt?: Date

  async sync(options?): Promise<void>
  async getIssues(filters?): Promise<Issue[]>
  async getPullRequests(filters?): Promise<PullRequest[]>
  async createIssue(data): Promise<Issue>
  async createPullRequest(data): Promise<PullRequest>
  async summarizeActivity(): Promise<string>  // AI
}

Issue (Living Spec -- STI Base)

PullRequest extends Issue via single-table inheritance. Both share the same table, discriminated by _meta_type.

typescript
class Issue extends SmrtObject {
  repositoryId: string     // FK
  number: number
  title: string
  body: string
  state: 'open' | 'closed'
  labels: string[]
  originalBody?: string    // Before synthesis
  synthesisCount: number   // Incremented on each incorporateFeedback apply

  // Living Spec Pattern
  async incorporateFeedback(options): Promise<IncorporateFeedbackResult>
  async rollback(): Promise<void>

  // AI-Powered
  async suggestLabels(): Promise<string[]>

  // Operations
  async sync(options?): Promise<void>
  async close(): Promise<void>
  async addLabels(labels: string[]): Promise<void>
  async addComment(body: string): Promise<Comment>
}

class PullRequest extends Issue {
  headRef: string
  baseRef: string
  merged: boolean
  draft: boolean
  additions: number
  deletions: number
  changedFiles: number

  async summarize(): Promise<string>
  async merge(): Promise<void>
}

Project (GitHub Projects V2)

typescript
class Project extends SmrtObject {
  projectId: string        // Provider-specific ID
  title: string
  providerType: 'github' | 'jira' | 'linear' | 'zenhub'
  statuses: string[]       // Available columns/statuses
  statusFieldId?: string

  async sync(): Promise<void>
  async addItem(issue | pr): Promise<void>
  async moveItem(issue, status): Promise<void>
  async listItems(filters?): Promise<ProjectItem[]>
  async updateItemStatus(itemId, status): Promise<void>
  async analyzeHealth(): Promise<string>  // AI
}

Living Spec Pattern

The Living Spec pattern automatically incorporates comment feedback into issue bodies using AI synthesis.

typescript
// 1. Create issue on GitHub
const issue = await issues.create({
  repositoryId: repo.id,
  number: 42,
  title: 'Add user authentication',
  body: 'We need to add login functionality'
});
await issue.save();

// 2. Users comment on the issue with feedback:
// - "Should support OAuth2"
// - "Need password reset flow"
// - "2FA required for admins"

// 3. Incorporate feedback using AI
const result = await issue.incorporateFeedback({
  applyUpdate: true,       // Update issue on GitHub
  model: 'sonnet',         // Claude Sonnet
  synthesisStrategy: 'append'  // or 'replace', 'merge'
});

console.log(result.synthesized);
// "We need to add login functionality with OAuth2 support,
//  password reset flow, and 2FA for admins."

// 4. Original body is preserved
console.log(issue.originalBody);  // "We need to add login functionality"
console.log(issue.synthesisCount); // 1

// 5. Rollback if needed
await issue.rollback();
// Restores originalBody to body field

Time Tracking Components (NEW v0.19.0)

TimeEntryCard

svelte
<script>
  import { TimeEntryCard } from '@happyvertical/smrt-projects/svelte';

  const entry = {
    id: 'entry-1',
    date: new Date('2025-01-15'),
    hours: 8.5,
    description: 'Developed new feature',
    status: 'submitted',
    amount: 1062.50,
    workerName: 'John Doe',
    hourlyRate: 125
  };
</script>

<TimeEntryCard {entry} href="/entries/entry-1" />

TimeSummary

svelte
<TimeSummary
  totalHours={160}
  totalValue={20000}
  pendingHours={40}
  approvedHours={120}
  entryCount={20}
  layout="grid"
/>

ApprovalActions

svelte
<ApprovalActions
  status="submitted"
  onapprove={() => approve()}
  onreject={() => showRejectDialog()}
  onedit={() => edit()}
  ondelete={() => confirmDelete()}
/>

BulkActions

svelte
<BulkActions
  selectedCount={5}
  onclear={() => clearSelection()}
  onapproveall={() => bulkApprove()}
  onrejectall={() => bulkReject()}
  onexport={() => exportSelected()}
/>

UI Components

Seven specialized Svelte components for time tracking and project management workflows. Each component is fully typed, accessible, and follows the SMRT design system.

svelte
import {
  TimeEntryCard,
  TimeEntryList,
  TimeSummary,
  DurationDisplay,
  ApprovalActions,
  BulkActions,
  RejectDialog
} from '@happyvertical/smrt-projects/svelte';

// Example: Time entry with approval workflow
<TimeEntryCard {entry} href="/entries/{entry.id}">
  <svelte:fragment slot="actions">
    <ApprovalActions
      status={entry.status}
      onapprove={() => approve(entry.id)}
      onreject={() => openRejectDialog(entry)}
    />
  </svelte:fragment>
</TimeEntryCard>

View all project components →

AI-Powered Features

typescript
// Classify issues
const isBug = await issue.isBugReport();
const isFeature = await issue.isFeatureRequest();
const needsAttention = await issue.needsReview();

// Suggest labels
const suggestedLabels = await issue.suggestLabels();
// ['bug', 'priority:high', 'needs-design']

// Analyze comment sentiment
const sentiment = await comment.getSentiment();
// 'positive' | 'negative' | 'neutral'

// Extract action items from comments
const actionItems = await comment.extractActionItems();
// ['Add OAuth2 support', 'Implement password reset']

// Suggest PR reviewers
const reviewers = await pr.suggestReviewers();

// Check merge readiness
const isReady = await pr.isReadyToMerge();

// Analyze project health
const health = await project.analyzeHealth();
// "3 blocked issues, 2 PRs need review, on track for Q1"

Best Practices

Related Modules