• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

apowers313 / aiforge / 20962792399

13 Jan 2026 03:39PM UTC coverage: 84.707%. First build
20962792399

push

github

apowers313
feat: initial commit

787 of 905 branches covered (86.96%)

Branch coverage included in aggregate %.

4248 of 5039 new or added lines in 70 files covered. (84.3%)

4248 of 5039 relevant lines covered (84.3%)

13.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.5
/src/server/services/project/ProjectService.ts
1
/**
2
 * ProjectService - Project business logic
3
 */
4
import { randomUUID } from 'node:crypto';
1✔
5
import { basename } from 'node:path';
1✔
6
import { stat } from 'node:fs/promises';
1✔
7
import type { Project } from '@shared/types/index.js';
8
import type { ProjectStore } from '../../storage/stores/ProjectStore.js';
9
import type { ShellStore } from '../../storage/stores/ShellStore.js';
10

11
export interface ProjectServiceOptions {
12
  projectStore: ProjectStore;
13
  shellStore: ShellStore;
14
}
15

16
export class ProjectService {
1✔
17
  private readonly projectStore: ProjectStore;
8✔
18
  private readonly shellStore: ShellStore;
8✔
19

20
  constructor(options: ProjectServiceOptions) {
8✔
21
    this.projectStore = options.projectStore;
8✔
22
    this.shellStore = options.shellStore;
8✔
23
  }
8✔
24

25
  /**
26
   * Get all projects
27
   */
28
  async getAll(): Promise<Project[]> {
8✔
29
    return this.projectStore.getAll();
2✔
30
  }
2✔
31

32
  /**
33
   * Get a project by ID
34
   */
35
  async getById(id: string): Promise<Project | null> {
8✔
NEW
36
    return this.projectStore.getById(id);
×
NEW
37
  }
×
38

39
  /**
40
   * Create a new project from a directory path
41
   * Validates the path exists and is a directory
42
   */
43
  async create(path: string): Promise<Project> {
8✔
44
    // Check if path exists and is a directory
45
    try {
8✔
46
      const stats = await stat(path);
8✔
47
      if (!stats.isDirectory()) {
8!
NEW
48
        throw new Error('Path is not a directory');
×
NEW
49
      }
×
50
    } catch (err) {
8✔
51
      if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
1✔
52
        throw new Error('Path does not exist');
1✔
53
      }
1!
NEW
54
      throw err;
×
NEW
55
    }
✔
56

57
    // Check if project already exists for this path
58
    const existing = await this.projectStore.getByPath(path);
7✔
59
    if (existing) {
8✔
60
      throw new Error('Project already exists for this path');
1✔
61
    }
1✔
62

63
    const now = new Date().toISOString();
6✔
64
    const project: Project = {
6✔
65
      id: randomUUID(),
6✔
66
      name: basename(path),
6✔
67
      path,
6✔
68
      createdAt: now,
6✔
69
      updatedAt: now,
6✔
70
    };
6✔
71

72
    await this.projectStore.create(project);
6✔
73
    return project;
6✔
74
  }
8✔
75

76
  /**
77
   * Update a project's name
78
   */
79
  async update(id: string, updates: { name?: string }): Promise<Project | null> {
8✔
80
    return this.projectStore.update(id, updates);
2✔
81
  }
2✔
82

83
  /**
84
   * Delete a project and all its shells
85
   */
86
  async delete(id: string): Promise<boolean> {
8✔
87
    // Delete all shells for this project first
88
    await this.shellStore.deleteByProjectId(id);
2✔
89

90
    return this.projectStore.delete(id);
2✔
91
  }
2✔
92
}
8✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc