@joshski/dust 0.1.109 → 0.1.111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -47,3 +47,7 @@ Details live in the [.dust/facts](./.dust/facts) directory:
47
47
  - [Directory Structure](./.dust/facts/dust-directory-structure.md) — how `.dust/` is organized
48
48
  - [Configuration](./.dust/facts/configuration-system.md) — settings and quality checks
49
49
  - [CLI Commands](./.dust/facts/unified-cli.md) — full command reference
50
+
51
+ ## Dust Bucket Worker
52
+
53
+ The `dust bucket worker` command runs a background worker that syncs agent sessions to [dustbucket.com](https://dustbucket.com). This requires a dustbucket.com account (currently in private alpha, invite only).
@@ -1,9 +1,6 @@
1
1
  import type { FileReader } from '../filesystem/types';
2
- export interface Fact {
3
- slug: string;
4
- title: string;
5
- content: string;
6
- }
2
+ import type { Fact } from './types';
3
+ export type { Fact };
7
4
  /**
8
5
  * Parses a fact markdown file into a structured Fact object.
9
6
  */
@@ -1,19 +1,6 @@
1
1
  import type { FileReader } from '../filesystem/types';
2
- export interface IdeaOption {
3
- name: string;
4
- description: string;
5
- }
6
- export interface IdeaOpenQuestion {
7
- question: string;
8
- options: IdeaOption[];
9
- }
10
- export interface Idea {
11
- slug: string;
12
- title: string;
13
- openingSentence: string | null;
14
- content: string;
15
- openQuestions: IdeaOpenQuestion[];
16
- }
2
+ import type { Idea, IdeaOpenQuestion, IdeaOption } from './types';
3
+ export type { Idea, IdeaOpenQuestion, IdeaOption };
17
4
  export interface ParsedIdeaContent {
18
5
  title: string | null;
19
6
  body: string;
@@ -6,21 +6,11 @@ import { type Principle } from './principles';
6
6
  import { type Task } from './tasks';
7
7
  import { type ParsedArtifact, type ParsedMarkdownLink, type ParsedSection, parseArtifact } from './parsed-artifact';
8
8
  import { type AllWorkflowTasks, CAPTURE_IDEA_PREFIX, type CreateIdeaTransitionTaskResult, type DecomposeIdeaOptions, findAllWorkflowTasks, type IdeaInProgress, type OpenQuestionResponse, type ParsedCaptureIdeaTask, parseResolvedQuestions, type TaskType, type WorkflowTaskMatch } from './workflow-tasks';
9
+ import type { ArtifactType, ReadOnlyArtifactsRepository } from './types';
10
+ export type { ArtifactType, ReadOnlyArtifactsRepository, RepositoryPrincipleNode, TaskGraph, TaskGraphNode, } from './types';
9
11
  export type { AllWorkflowTasks, CreateIdeaTransitionTaskResult, DecomposeIdeaOptions, Fact, Idea, IdeaOpenQuestion, IdeaOption, OpenQuestionResponse, ParsedArtifact, ParsedCaptureIdeaTask, ParsedIdeaContent, ParsedMarkdownLink, ParsedSection, Principle, Task, TaskType, WorkflowTaskMatch, };
10
- export interface TaskGraphNode {
11
- task: Task;
12
- workflowType: TaskType | null;
13
- }
14
- export interface TaskGraph {
15
- nodes: TaskGraphNode[];
16
- edges: Array<{
17
- from: string;
18
- to: string;
19
- }>;
20
- }
21
12
  export { CAPTURE_IDEA_PREFIX, extractTitle, findAllWorkflowTasks, ideaContentToMarkdown, parseArtifact, parseIdeaContent, parseOpenQuestions, parseResolvedQuestions, };
22
13
  export type { IdeaInProgress };
23
- export type ArtifactType = 'ideas' | 'tasks' | 'principles' | 'facts';
24
14
  export declare const ARTIFACT_TYPES: ArtifactType[];
25
15
  export declare const DUST_PATH_PREFIX = ".dust/";
26
16
  /**
@@ -32,32 +22,6 @@ export declare function parseArtifactPath(path: string): {
32
22
  type: ArtifactType;
33
23
  slug: string;
34
24
  } | null;
35
- export interface ReadOnlyArtifactsRepository {
36
- artifactPath(type: ArtifactType, slug: string): string;
37
- parseIdea(options: {
38
- slug: string;
39
- }): Promise<Idea>;
40
- listIdeas(): Promise<string[]>;
41
- parsePrinciple(options: {
42
- slug: string;
43
- }): Promise<Principle>;
44
- listPrinciples(): Promise<string[]>;
45
- parseFact(options: {
46
- slug: string;
47
- }): Promise<Fact>;
48
- listFacts(): Promise<string[]>;
49
- parseTask(options: {
50
- slug: string;
51
- }): Promise<Task>;
52
- listTasks(): Promise<string[]>;
53
- findWorkflowTaskForIdea(options: {
54
- ideaSlug: string;
55
- }): Promise<WorkflowTaskMatch | null>;
56
- parseCaptureIdeaTask(options: {
57
- taskSlug: string;
58
- }): Promise<ParsedCaptureIdeaTask | null>;
59
- buildTaskGraph(): Promise<TaskGraph>;
60
- }
61
25
  export interface ArtifactsRepository extends ReadOnlyArtifactsRepository {
62
26
  createRefineIdeaTask(options: {
63
27
  ideaSlug: string;
@@ -1,11 +1,6 @@
1
1
  import type { FileReader } from '../filesystem/types';
2
- export interface Principle {
3
- slug: string;
4
- title: string;
5
- content: string;
6
- parentPrinciple: string | null;
7
- subPrinciples: string[];
8
- }
2
+ import type { Principle } from './types';
3
+ export type { Principle };
9
4
  /**
10
5
  * Parses a principle markdown file into a structured Principle object.
11
6
  */
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Repository Principle Hierarchy API
3
+ *
4
+ * Provides pure functional access to build hierarchical trees of principles
5
+ * from a repository's .dust/principles/ directory.
6
+ */
7
+ import type { ReadOnlyArtifactsRepository, RepositoryPrincipleNode } from './types';
8
+ /**
9
+ * Builds a hierarchy tree of repository principles from .dust/principles/.
10
+ * Returns root nodes (principles with no parent or filtered parent).
11
+ * Children are sorted alphabetically by title (recursive).
12
+ *
13
+ * @param repository - The repository to read principles from
14
+ * @returns Array of root principle nodes, empty array if no principles exist
15
+ */
16
+ export declare function getRepositoryPrincipleHierarchy(repository: ReadOnlyArtifactsRepository): Promise<RepositoryPrincipleNode[]>;
@@ -1,12 +1,6 @@
1
1
  import type { FileReader } from '../filesystem/types';
2
- export interface Task {
3
- slug: string;
4
- title: string;
5
- content: string;
6
- principles: string[];
7
- blockedBy: string[];
8
- definitionOfDone: string[];
9
- }
2
+ import type { Task } from './types';
3
+ export type { Task };
10
4
  /**
11
5
  * Parses a task markdown file into a structured Task object.
12
6
  */
@@ -0,0 +1,98 @@
1
+ export type TaskType = 'implement' | 'capture' | 'refine' | 'decompose' | 'shelve';
2
+ export interface Fact {
3
+ slug: string;
4
+ title: string;
5
+ content: string;
6
+ }
7
+ export interface IdeaOption {
8
+ name: string;
9
+ description: string;
10
+ }
11
+ export interface IdeaOpenQuestion {
12
+ question: string;
13
+ options: IdeaOption[];
14
+ }
15
+ export interface Idea {
16
+ slug: string;
17
+ title: string;
18
+ openingSentence: string | null;
19
+ content: string;
20
+ openQuestions: IdeaOpenQuestion[];
21
+ }
22
+ export interface Principle {
23
+ slug: string;
24
+ title: string;
25
+ content: string;
26
+ parentPrinciple: string | null;
27
+ subPrinciples: string[];
28
+ }
29
+ export interface Task {
30
+ slug: string;
31
+ title: string;
32
+ content: string;
33
+ principles: string[];
34
+ blockedBy: string[];
35
+ definitionOfDone: string[];
36
+ }
37
+ export interface OpenQuestionResponse {
38
+ question: string;
39
+ chosenOption: string;
40
+ }
41
+ export interface WorkflowTaskMatch {
42
+ type: TaskType;
43
+ ideaSlug: string;
44
+ taskSlug: string;
45
+ resolvedQuestions: OpenQuestionResponse[];
46
+ }
47
+ export interface ParsedCaptureIdeaTask {
48
+ ideaTitle: string;
49
+ ideaDescription: string;
50
+ expedite: boolean;
51
+ }
52
+ export type ArtifactType = 'ideas' | 'tasks' | 'principles' | 'facts';
53
+ export interface TaskGraphNode {
54
+ task: Task;
55
+ workflowType: TaskType | null;
56
+ }
57
+ export interface TaskGraph {
58
+ nodes: TaskGraphNode[];
59
+ edges: Array<{
60
+ from: string;
61
+ to: string;
62
+ }>;
63
+ }
64
+ /**
65
+ * Node in the principle hierarchy tree
66
+ */
67
+ export interface RepositoryPrincipleNode {
68
+ slug: string;
69
+ title: string;
70
+ children: RepositoryPrincipleNode[];
71
+ }
72
+ export interface ReadOnlyArtifactsRepository {
73
+ artifactPath(type: ArtifactType, slug: string): string;
74
+ parseIdea(options: {
75
+ slug: string;
76
+ }): Promise<Idea>;
77
+ listIdeas(): Promise<string[]>;
78
+ parsePrinciple(options: {
79
+ slug: string;
80
+ }): Promise<Principle>;
81
+ listPrinciples(): Promise<string[]>;
82
+ parseFact(options: {
83
+ slug: string;
84
+ }): Promise<Fact>;
85
+ listFacts(): Promise<string[]>;
86
+ parseTask(options: {
87
+ slug: string;
88
+ }): Promise<Task>;
89
+ listTasks(): Promise<string[]>;
90
+ findWorkflowTaskForIdea(options: {
91
+ ideaSlug: string;
92
+ }): Promise<WorkflowTaskMatch | null>;
93
+ parseCaptureIdeaTask(options: {
94
+ taskSlug: string;
95
+ }): Promise<ParsedCaptureIdeaTask | null>;
96
+ buildTaskGraph(): Promise<TaskGraph>;
97
+ getRepositoryPrincipleHierarchy(): Promise<RepositoryPrincipleNode[]>;
98
+ }
@@ -1,4 +1,6 @@
1
1
  import type { FileSystem, ReadableFileSystem } from '../filesystem/types';
2
+ import type { OpenQuestionResponse, ParsedCaptureIdeaTask, TaskType, WorkflowTaskMatch } from './types';
3
+ export type { OpenQuestionResponse, ParsedCaptureIdeaTask, TaskType, WorkflowTaskMatch, };
2
4
  export declare const IDEA_TRANSITION_PREFIXES: string[];
3
5
  export declare const CAPTURE_IDEA_PREFIX = "Add Idea: ";
4
6
  export declare const EXPEDITE_IDEA_PREFIX = "Expedite Idea: ";
@@ -6,11 +8,6 @@ export interface IdeaInProgress {
6
8
  taskSlug: string;
7
9
  ideaTitle: string;
8
10
  }
9
- export interface ParsedCaptureIdeaTask {
10
- ideaTitle: string;
11
- ideaDescription: string;
12
- expedite: boolean;
13
- }
14
11
  /**
15
12
  * Converts a markdown title to the expected filename using deterministic rules:
16
13
  * 1. Convert to lowercase
@@ -22,18 +19,11 @@ export interface ParsedCaptureIdeaTask {
22
19
  */
23
20
  export declare function titleToFilename(title: string): string;
24
21
  export declare const VALID_TASK_TYPES: readonly ["implement", "capture", "refine", "decompose", "shelve"];
25
- export type TaskType = (typeof VALID_TASK_TYPES)[number];
26
22
  /**
27
23
  * Extracts and validates the task type from the ## Task Type section.
28
24
  * Returns the task type if found and valid, null otherwise.
29
25
  */
30
26
  export declare function parseTaskType(content: string): TaskType | null;
31
- export interface WorkflowTaskMatch {
32
- type: TaskType;
33
- ideaSlug: string;
34
- taskSlug: string;
35
- resolvedQuestions: OpenQuestionResponse[];
36
- }
37
27
  export interface AllWorkflowTasks {
38
28
  captureIdeaTasks: IdeaInProgress[];
39
29
  workflowTasksByIdeaSlug: Map<string, WorkflowTaskMatch>;
@@ -43,10 +33,6 @@ export declare function findWorkflowTaskForIdea(fileSystem: ReadableFileSystem,
43
33
  export interface CreateIdeaTransitionTaskResult {
44
34
  filePath: string;
45
35
  }
46
- export interface OpenQuestionResponse {
47
- question: string;
48
- chosenOption: string;
49
- }
50
36
  export interface DecomposeIdeaOptions {
51
37
  ideaSlug: string;
52
38
  description?: string;
package/dist/artifacts.js CHANGED
@@ -294,10 +294,7 @@ async function parsePrinciple(fileSystem, dustPath, slug) {
294
294
  throw new Error(`Principle not found: "${slug}" (expected file at ${principlePath})`);
295
295
  }
296
296
  const content = await fileSystem.readFile(principlePath);
297
- const title = extractTitle(content);
298
- if (!title) {
299
- throw new Error(`Principle file has no title: ${principlePath}`);
300
- }
297
+ const title = extractTitle(content) || slug;
301
298
  const parentPrinciple = extractSingleLinkFromSection(content, "Parent Principle");
302
299
  const subPrinciples = extractLinksFromSection(content, "Sub-Principles");
303
300
  return {
@@ -999,6 +996,42 @@ async function parseCaptureIdeaTask(fileSystem, dustPath, taskSlug) {
999
996
  return { ideaTitle, ideaDescription, expedite };
1000
997
  }
1001
998
 
999
+ // lib/artifacts/repository-principle-hierarchy.ts
1000
+ function sortNodes(nodes) {
1001
+ nodes.sort((a, b) => a.title.localeCompare(b.title));
1002
+ for (const node of nodes) {
1003
+ sortNodes(node.children);
1004
+ }
1005
+ }
1006
+ async function getRepositoryPrincipleHierarchy(repository) {
1007
+ const slugs = await repository.listPrinciples();
1008
+ if (slugs.length === 0) {
1009
+ return [];
1010
+ }
1011
+ const principles = await Promise.all(slugs.map((slug) => repository.parsePrinciple({ slug })));
1012
+ const principleSet = new Set(slugs);
1013
+ const nodeBySlug = new Map;
1014
+ for (const p of principles) {
1015
+ nodeBySlug.set(p.slug, {
1016
+ slug: p.slug,
1017
+ title: p.title,
1018
+ children: []
1019
+ });
1020
+ }
1021
+ const roots = [];
1022
+ for (const p of principles) {
1023
+ const node = nodeBySlug.get(p.slug);
1024
+ const parentSlug = p.parentPrinciple;
1025
+ if (!parentSlug || !principleSet.has(parentSlug)) {
1026
+ roots.push(node);
1027
+ } else {
1028
+ nodeBySlug.get(parentSlug).children.push(node);
1029
+ }
1030
+ }
1031
+ sortNodes(roots);
1032
+ return roots;
1033
+ }
1034
+
1002
1035
  // lib/artifacts/index.ts
1003
1036
  var ARTIFACT_TYPES = [
1004
1037
  "facts",
@@ -1099,6 +1132,9 @@ function buildReadOperations(fileSystem, dustPath) {
1099
1132
  }
1100
1133
  }
1101
1134
  return { nodes, edges };
1135
+ },
1136
+ async getRepositoryPrincipleHierarchy() {
1137
+ return getRepositoryPrincipleHierarchy(this);
1102
1138
  }
1103
1139
  };
1104
1140
  }
package/dist/audits.js CHANGED
@@ -1865,6 +1865,168 @@ function slowTests() {
1865
1865
  - No changes to files outside \`.dust/\`
1866
1866
  `;
1867
1867
  }
1868
+ function overAbstraction() {
1869
+ return dedent`
1870
+ # Over-Abstraction
1871
+
1872
+ Identify violations of the "reasonably-dry" principle where code has been over-engineered with excessive abstraction.
1873
+
1874
+ ${ideasHint}
1875
+
1876
+ ## Scope
1877
+
1878
+ Detect these over-abstraction patterns:
1879
+
1880
+ 1. **Single-use abstractions** - Interfaces, base classes, or utility functions used in only one place
1881
+ 2. **Deep inheritance hierarchies** - Classes extending more than 2 levels deep
1882
+ 3. **Premature generalization** - Parameters always used with the same value, unused options/flags
1883
+ 4. **Excessive indirection** - Multiple layers of wrappers adding no value
1884
+
1885
+ ## Analysis Steps
1886
+
1887
+ ### 1. Find Single-Use Abstractions
1888
+
1889
+ Search for abstractions that are only used once:
1890
+
1891
+ 1. **Interfaces with one implementation**
1892
+ - Search for \`interface\` declarations
1893
+ - Check if each interface has only one implementing class
1894
+ - Flag interfaces that exist solely for testing (can be replaced with the concrete type)
1895
+
1896
+ 2. **Base classes with one subclass**
1897
+ - Search for \`abstract class\` or classes used as base classes
1898
+ - Count implementations extending each base class
1899
+ - Flag base classes with only one subclass
1900
+
1901
+ 3. **Utility functions called once**
1902
+ - Search for exported utility functions
1903
+ - Check call sites - if only called from one location, it's over-abstraction
1904
+ - Consider inlining single-use utilities
1905
+
1906
+ 4. **Generic types with one concrete usage**
1907
+ - Find generic type parameters: \`<T>\`, \`<TData>\`, etc.
1908
+ - Check if T is always the same type at all call sites
1909
+ - Flag generics that could be concrete types
1910
+
1911
+ ### 2. Detect Deep Inheritance Hierarchies
1912
+
1913
+ Find inheritance chains longer than 2 levels:
1914
+
1915
+ 1. Search for \`extends\` keywords in class declarations
1916
+ 2. Build inheritance tree for each class
1917
+ 3. Flag chains deeper than 2 (A extends B extends C extends D...)
1918
+ 4. Respect framework conventions (don't flag React.Component, etc.)
1919
+
1920
+ ### 3. Identify Premature Generalization
1921
+
1922
+ Look for flexibility that's never used:
1923
+
1924
+ 1. **Always-same parameter values**
1925
+ - Find function parameters
1926
+ - Check all call sites - if always the same value, it's not needed
1927
+ - Flag parameters that could be constants or removed
1928
+
1929
+ 2. **Unused configuration options**
1930
+ - Search for configuration objects/interfaces
1931
+ - Check which options are actually used
1932
+ - Flag options that are never set or always default
1933
+
1934
+ 3. **Unused function parameters**
1935
+ - Find parameters that aren't referenced in function bodies
1936
+ - Flag as candidates for removal
1937
+
1938
+ ### 4. Find Excessive Indirection
1939
+
1940
+ Detect wrapper chains that add no value:
1941
+
1942
+ 1. **Delegation chains**
1943
+ - Search for functions that only call another function
1944
+ - Flag wrappers that don't add logic, just forward calls
1945
+ - Example: \`function foo(x) { return bar(x) }\`
1946
+
1947
+ 2. **Proxy patterns without behavior**
1948
+ - Find classes that wrap another class
1949
+ - Check if wrapper adds any logic beyond forwarding
1950
+ - Flag pure proxies
1951
+
1952
+ 3. **Middleware without transformation**
1953
+ - Look for middleware/interceptor patterns
1954
+ - Check if they modify data or just pass through
1955
+ - Flag pass-through middleware
1956
+
1957
+ ## Output Format
1958
+
1959
+ For each over-abstraction found, create an idea file in \`.dust/ideas/\` with:
1960
+
1961
+ \`\`\`markdown
1962
+ # Over-Abstraction: [Type] in [Location]
1963
+
1964
+ ## Type
1965
+
1966
+ [Single-use | Deep hierarchy | Premature generalization | Excessive indirection]
1967
+
1968
+ ## Location
1969
+
1970
+ \`\`\`
1971
+ [file path]:[line number]
1972
+ \`\`\`
1973
+
1974
+ ## Description
1975
+
1976
+ [What the abstraction is]
1977
+
1978
+ ## Problem
1979
+
1980
+ [Why this is over-abstraction - complexity without benefit]
1981
+
1982
+ ## Usage Analysis
1983
+
1984
+ - **Times used**: [count]
1985
+ - **Variation in usage**: [how different are the use cases]
1986
+ - **Complexity cost**: [lines of code, indirection levels, etc.]
1987
+
1988
+ ## Suggested Simplification
1989
+
1990
+ [How to remove or reduce this abstraction]
1991
+
1992
+ ## Impact
1993
+
1994
+ [Lines of code saved, reduced complexity, improved clarity]
1995
+ \`\`\`
1996
+
1997
+ ## Special Considerations
1998
+
1999
+ 1. **Framework conventions** - Don't flag patterns mandated by frameworks:
2000
+ - React: Component base classes, hooks patterns
2001
+ - Express: Middleware signatures
2002
+ - Testing: Test base classes, fixture patterns
2003
+
2004
+ 2. **Library boundaries** - Public API abstractions may be justified even if internal usage is simple
2005
+
2006
+ 3. **Test code** - Apply the same standards to test code as production code
2007
+
2008
+ 4. **Context depth thresholds**:
2009
+ - Deep hierarchies (>2 levels) make understanding difficult
2010
+ - Wrapper chains (>2 levels) obscure actual behavior
2011
+ - Generic parameters should have multiple concrete usages
2012
+
2013
+ ## Blocked By
2014
+
2015
+ (none)
2016
+
2017
+ ## Definition of Done
2018
+
2019
+ - Searched for single-use interfaces, base classes, and utility functions
2020
+ - Identified deep inheritance hierarchies (>2 levels)
2021
+ - Found parameters always used with the same value
2022
+ - Detected unused configuration options
2023
+ - Located excessive wrapper chains and delegation
2024
+ - Respected framework conventions (didn't flag framework-mandated patterns)
2025
+ - Created idea files for each over-abstraction found
2026
+ - Each idea includes usage analysis and simplification suggestions
2027
+ - No changes to files outside \`.dust/\`
2028
+ `;
2029
+ }
1868
2030
  function primitiveObsession() {
1869
2031
  return dedent`
1870
2032
  # Primitive Obsession
@@ -3240,6 +3402,7 @@ var stockAuditFunctions = {
3240
3402
  "idiomatic-style": idiomaticStyle,
3241
3403
  "incidental-test-details": incidentalTestDetails,
3242
3404
  "logging-and-traceability": loggingAndTraceability,
3405
+ "over-abstraction": overAbstraction,
3243
3406
  "primitive-obsession": primitiveObsession,
3244
3407
  "repository-context": repositoryContext,
3245
3408
  "security-review": securityReview,
@@ -12,7 +12,7 @@ import type { SendAgentEventFn } from '../loop/wire-events';
12
12
  import { type RunnerDependencies as CodexRunnerDependencies, run as codexRun } from '../codex/run';
13
13
  import type { SendEventFn } from './events';
14
14
  import { type LogBuffer } from './log-buffer';
15
- import type { RepositoryDependencies, RepositoryState } from './repository';
15
+ import type { RepositoryDependencies, RepositoryState } from './repository-types';
16
16
  /**
17
17
  * Create stdout/stderr callbacks that append to a log buffer.
18
18
  * Extracted for testability (v8 coverage limitation on inline callbacks).
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Shared types for repository management.
3
+ *
4
+ * Extracted to break the cyclic dependency between repository.ts and repository-loop.ts.
5
+ */
6
+ import { spawn as nodeSpawn } from 'node:child_process';
7
+ import type { run as claudeRun } from '../claude/run';
8
+ import type { FileSystem } from '../cli/types';
9
+ import type { DockerDependencies } from '../docker/docker-agent';
10
+ import type { AuthConfig, RuntimeConfig, SessionConfig } from '../env-config';
11
+ import type { ToolExecutionRequest, ToolExecutionResult } from './command-events-proxy';
12
+ import type { LogBuffer } from './log-buffer';
13
+ import type { RepositoryLifecycleState } from './repository-lifecycle';
14
+ import type { ToolDefinition } from './server-messages';
15
+ export interface Repository {
16
+ name: string;
17
+ gitUrl: string;
18
+ gitSshUrl: string;
19
+ url: string;
20
+ id: number;
21
+ agentProvider?: string;
22
+ branch?: string;
23
+ }
24
+ export interface RepositoryState {
25
+ repository: Repository;
26
+ path: string;
27
+ logBuffer: LogBuffer;
28
+ lifecycle: RepositoryLifecycleState;
29
+ agentStatus: 'idle' | 'busy';
30
+ wakeUp?: () => void;
31
+ taskAvailablePending?: boolean;
32
+ }
33
+ export interface RepositoryDependencies {
34
+ spawn: typeof nodeSpawn;
35
+ run: typeof claudeRun;
36
+ fileSystem: FileSystem;
37
+ sleep: (ms: number) => Promise<void>;
38
+ getReposDir: () => string;
39
+ session: SessionConfig;
40
+ runtime: RuntimeConfig;
41
+ auth: AuthConfig;
42
+ /** Optional overrides for Docker dependency functions (for testing) */
43
+ dockerDeps?: Partial<DockerDependencies>;
44
+ /** Function to get current tool definitions */
45
+ getTools?: () => ToolDefinition[];
46
+ /** Function to get revealed tool families (for progressive disclosure) */
47
+ getRevealedFamilies?: () => Set<string>;
48
+ /** Forward tool execution requests to the bucket server */
49
+ forwardToolExecution?: (request: ToolExecutionRequest) => Promise<ToolExecutionResult>;
50
+ /** Mark a tool family as revealed (for progressive disclosure) */
51
+ revealFamily?: (familyName: string) => void;
52
+ /** Shell runner for pre-flight commands (install, check) */
53
+ shellRunner?: import('../cli/process-runner').ShellRunner;
54
+ /** Force Docker mode using bundled default Dockerfile */
55
+ forceDocker?: boolean;
56
+ /** Force Apple Container mode using bundled default Dockerfile */
57
+ forceAppleContainer?: boolean;
58
+ }
@@ -4,37 +4,13 @@
4
4
  * Git operations live in repository-git.ts.
5
5
  * Loop orchestration lives in repository-loop.ts.
6
6
  */
7
- import { spawn as nodeSpawn } from 'node:child_process';
8
- import { run as claudeRun } from '../claude/run';
9
7
  import type { CommandDependencies, FileSystem } from '../cli/types';
10
- import type { DockerDependencies } from '../docker/docker-agent';
11
- import { type AuthConfig, type RuntimeConfig, type SessionConfig } from '../env-config';
12
- import type { ToolExecutionRequest, ToolExecutionResult } from './command-events-proxy';
13
8
  import { type BucketEmitFn, type SendEventFn } from './events';
14
9
  import { type LogBuffer } from './log-buffer';
15
- import { type RepositoryLifecycleState } from './repository-lifecycle';
16
- import type { ToolDefinition } from './server-messages';
10
+ import type { Repository, RepositoryDependencies, RepositoryState } from './repository-types';
17
11
  export { cloneRepository, getRepoPath, removeRepository, } from './repository-git';
18
12
  export { runRepositoryLoop } from './repository-loop';
19
- export type { RepositoryLifecycleState } from './repository-lifecycle';
20
- export interface Repository {
21
- name: string;
22
- gitUrl: string;
23
- gitSshUrl: string;
24
- url: string;
25
- id: number;
26
- agentProvider?: string;
27
- branch?: string;
28
- }
29
- export interface RepositoryState {
30
- repository: Repository;
31
- path: string;
32
- logBuffer: LogBuffer;
33
- lifecycle: RepositoryLifecycleState;
34
- agentStatus: 'idle' | 'busy';
35
- wakeUp?: () => void;
36
- taskAvailablePending?: boolean;
37
- }
13
+ export type { Repository, RepositoryDependencies, RepositoryState, } from './repository-types';
38
14
  /**
39
15
  * Interface for the subset of bucket state needed by repository management.
40
16
  * Avoids circular dependency between repository.ts and bucket.ts.
@@ -46,32 +22,6 @@ export interface RepositoryManager {
46
22
  sendEvent: SendEventFn;
47
23
  sessionId: string;
48
24
  }
49
- export interface RepositoryDependencies {
50
- spawn: typeof nodeSpawn;
51
- run: typeof claudeRun;
52
- fileSystem: FileSystem;
53
- sleep: (ms: number) => Promise<void>;
54
- getReposDir: () => string;
55
- session: SessionConfig;
56
- runtime: RuntimeConfig;
57
- auth: AuthConfig;
58
- /** Optional overrides for Docker dependency functions (for testing) */
59
- dockerDeps?: Partial<DockerDependencies>;
60
- /** Function to get current tool definitions */
61
- getTools?: () => ToolDefinition[];
62
- /** Function to get revealed tool families (for progressive disclosure) */
63
- getRevealedFamilies?: () => Set<string>;
64
- /** Forward tool execution requests to the bucket server */
65
- forwardToolExecution?: (request: ToolExecutionRequest) => Promise<ToolExecutionResult>;
66
- /** Mark a tool family as revealed (for progressive disclosure) */
67
- revealFamily?: (familyName: string) => void;
68
- /** Shell runner for pre-flight commands (install, check) */
69
- shellRunner?: import('../cli/process-runner').ShellRunner;
70
- /** Force Docker mode using bundled default Dockerfile */
71
- forceDocker?: boolean;
72
- /** Force Apple Container mode using bundled default Dockerfile */
73
- forceAppleContainer?: boolean;
74
- }
75
25
  /**
76
26
  * Handle loop completion: transition lifecycle and reset agent status.
77
27
  * Extracted as a named function for testability.