@helmiq/crew 0.1.0
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/defaults/personas/architect.persona.yaml +72 -0
- package/defaults/personas/engineer.persona.yaml +137 -0
- package/defaults/personas/persona-spec.schema.yaml +149 -0
- package/defaults/personas/reviewer.persona.yaml +47 -0
- package/defaults/rubrics/adr.rubric.yaml +48 -0
- package/defaults/rubrics/code-review.rubric.yaml +39 -0
- package/defaults/rubrics/pull-request.rubric.yaml +40 -0
- package/dist/actions/actions.test.d.ts +2 -0
- package/dist/actions/actions.test.d.ts.map +1 -0
- package/dist/actions/actions.test.js +158 -0
- package/dist/actions/direct-dispatcher.d.ts +10 -0
- package/dist/actions/direct-dispatcher.d.ts.map +1 -0
- package/dist/actions/direct-dispatcher.js +27 -0
- package/dist/actions/dispatcher.d.ts +11 -0
- package/dist/actions/dispatcher.d.ts.map +1 -0
- package/dist/actions/dispatcher.js +1 -0
- package/dist/actions/index.d.ts +7 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +3 -0
- package/dist/actions/registry.d.ts +13 -0
- package/dist/actions/registry.d.ts.map +1 -0
- package/dist/actions/registry.js +40 -0
- package/dist/actions/resolver.d.ts +47 -0
- package/dist/actions/resolver.d.ts.map +1 -0
- package/dist/actions/resolver.js +43 -0
- package/dist/cli/cli.test.d.ts +2 -0
- package/dist/cli/cli.test.d.ts.map +1 -0
- package/dist/cli/cli.test.js +392 -0
- package/dist/cli/run.d.ts +45 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +236 -0
- package/dist/common/errors.d.ts +76 -0
- package/dist/common/errors.d.ts.map +1 -0
- package/dist/common/errors.js +74 -0
- package/dist/config/config.test.d.ts +2 -0
- package/dist/config/config.test.d.ts.map +1 -0
- package/dist/config/config.test.js +691 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/loader.d.ts +16 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +56 -0
- package/dist/config/model-resolver.d.ts +24 -0
- package/dist/config/model-resolver.d.ts.map +1 -0
- package/dist/config/model-resolver.js +39 -0
- package/dist/config/resolver.d.ts +22 -0
- package/dist/config/resolver.d.ts.map +1 -0
- package/dist/config/resolver.js +115 -0
- package/dist/config/schemas.d.ts +266 -0
- package/dist/config/schemas.d.ts.map +1 -0
- package/dist/config/schemas.js +115 -0
- package/dist/context/artifact-reader.d.ts +12 -0
- package/dist/context/artifact-reader.d.ts.map +1 -0
- package/dist/context/artifact-reader.js +92 -0
- package/dist/context/assembler.d.ts +22 -0
- package/dist/context/assembler.d.ts.map +1 -0
- package/dist/context/assembler.js +126 -0
- package/dist/context/code-reader.d.ts +14 -0
- package/dist/context/code-reader.d.ts.map +1 -0
- package/dist/context/code-reader.js +56 -0
- package/dist/context/context.test.d.ts +2 -0
- package/dist/context/context.test.d.ts.map +1 -0
- package/dist/context/context.test.js +260 -0
- package/dist/context/index.d.ts +9 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +5 -0
- package/dist/context/section-extractor.d.ts +9 -0
- package/dist/context/section-extractor.d.ts.map +1 -0
- package/dist/context/section-extractor.js +32 -0
- package/dist/context/token-budget.d.ts +11 -0
- package/dist/context/token-budget.d.ts.map +1 -0
- package/dist/context/token-budget.js +22 -0
- package/dist/control/control.test.d.ts +2 -0
- package/dist/control/control.test.d.ts.map +1 -0
- package/dist/control/control.test.js +137 -0
- package/dist/control/id-generator.d.ts +12 -0
- package/dist/control/id-generator.d.ts.map +1 -0
- package/dist/control/id-generator.js +20 -0
- package/dist/control/index.d.ts +5 -0
- package/dist/control/index.d.ts.map +1 -0
- package/dist/control/index.js +3 -0
- package/dist/control/lock-manager.d.ts +13 -0
- package/dist/control/lock-manager.d.ts.map +1 -0
- package/dist/control/lock-manager.js +72 -0
- package/dist/control/run-state.d.ts +16 -0
- package/dist/control/run-state.d.ts.map +1 -0
- package/dist/control/run-state.js +55 -0
- package/dist/engine/composite.d.ts +34 -0
- package/dist/engine/composite.d.ts.map +1 -0
- package/dist/engine/composite.js +192 -0
- package/dist/engine/composite.test.d.ts +2 -0
- package/dist/engine/composite.test.d.ts.map +1 -0
- package/dist/engine/composite.test.js +1947 -0
- package/dist/engine/engine.test.d.ts +2 -0
- package/dist/engine/engine.test.d.ts.map +1 -0
- package/dist/engine/engine.test.js +334 -0
- package/dist/engine/index.d.ts +10 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +5 -0
- package/dist/engine/llm-client.d.ts +27 -0
- package/dist/engine/llm-client.d.ts.map +1 -0
- package/dist/engine/llm-client.js +46 -0
- package/dist/engine/simple.d.ts +21 -0
- package/dist/engine/simple.d.ts.map +1 -0
- package/dist/engine/simple.js +59 -0
- package/dist/engine/tool-dispatch.d.ts +37 -0
- package/dist/engine/tool-dispatch.d.ts.map +1 -0
- package/dist/engine/tool-dispatch.js +146 -0
- package/dist/engine/tool-dispatch.test.d.ts +2 -0
- package/dist/engine/tool-dispatch.test.d.ts.map +1 -0
- package/dist/engine/tool-dispatch.test.js +348 -0
- package/dist/engine/tool-filter.d.ts +13 -0
- package/dist/engine/tool-filter.d.ts.map +1 -0
- package/dist/engine/tool-filter.js +25 -0
- package/dist/evaluation/evaluation.test.d.ts +2 -0
- package/dist/evaluation/evaluation.test.d.ts.map +1 -0
- package/dist/evaluation/evaluation.test.js +490 -0
- package/dist/evaluation/evaluator.d.ts +19 -0
- package/dist/evaluation/evaluator.d.ts.map +1 -0
- package/dist/evaluation/evaluator.js +78 -0
- package/dist/evaluation/index.d.ts +4 -0
- package/dist/evaluation/index.d.ts.map +1 -0
- package/dist/evaluation/index.js +2 -0
- package/dist/evaluation/scorer.d.ts +38 -0
- package/dist/evaluation/scorer.d.ts.map +1 -0
- package/dist/evaluation/scorer.js +94 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/provider-factory.d.ts +11 -0
- package/dist/providers/provider-factory.d.ts.map +1 -0
- package/dist/providers/provider-factory.js +30 -0
- package/dist/publication/frontmatter.d.ts +21 -0
- package/dist/publication/frontmatter.d.ts.map +1 -0
- package/dist/publication/frontmatter.js +15 -0
- package/dist/publication/git-ops.d.ts +18 -0
- package/dist/publication/git-ops.d.ts.map +1 -0
- package/dist/publication/git-ops.js +74 -0
- package/dist/publication/index.d.ts +9 -0
- package/dist/publication/index.d.ts.map +1 -0
- package/dist/publication/index.js +5 -0
- package/dist/publication/provenance-writer.d.ts +27 -0
- package/dist/publication/provenance-writer.d.ts.map +1 -0
- package/dist/publication/provenance-writer.js +21 -0
- package/dist/publication/publication.test.d.ts +2 -0
- package/dist/publication/publication.test.d.ts.map +1 -0
- package/dist/publication/publication.test.js +235 -0
- package/dist/publication/publisher.d.ts +32 -0
- package/dist/publication/publisher.d.ts.map +1 -0
- package/dist/publication/publisher.js +113 -0
- package/dist/publication/secret-scanner.d.ts +6 -0
- package/dist/publication/secret-scanner.d.ts.map +1 -0
- package/dist/publication/secret-scanner.js +19 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +2 -0
- package/dist/tools/registry.d.ts +15 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +288 -0
- package/dist/tools/registry.test.d.ts +2 -0
- package/dist/tools/registry.test.d.ts.map +1 -0
- package/dist/tools/registry.test.js +131 -0
- package/dist/tools/tool-groups.d.ts +20 -0
- package/dist/tools/tool-groups.d.ts.map +1 -0
- package/dist/tools/tool-groups.js +48 -0
- package/dist/tools/tool-groups.test.d.ts +2 -0
- package/dist/tools/tool-groups.test.d.ts.map +1 -0
- package/dist/tools/tool-groups.test.js +127 -0
- package/dist/types/artifact-store.d.ts +33 -0
- package/dist/types/artifact-store.d.ts.map +1 -0
- package/dist/types/artifact-store.js +9 -0
- package/dist/types/evaluation-rubric.d.ts +18 -0
- package/dist/types/evaluation-rubric.d.ts.map +1 -0
- package/dist/types/evaluation-rubric.js +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/llm-provider.d.ts +47 -0
- package/dist/types/llm-provider.d.ts.map +1 -0
- package/dist/types/llm-provider.js +8 -0
- package/dist/types/persona-spec.d.ts +79 -0
- package/dist/types/persona-spec.d.ts.map +1 -0
- package/dist/types/persona-spec.js +1 -0
- package/dist/types/project-config.d.ts +28 -0
- package/dist/types/project-config.d.ts.map +1 -0
- package/dist/types/project-config.js +1 -0
- package/dist/types/provenance.d.ts +67 -0
- package/dist/types/provenance.d.ts.map +1 -0
- package/dist/types/provenance.js +1 -0
- package/dist/types/run-state.d.ts +11 -0
- package/dist/types/run-state.d.ts.map +1 -0
- package/dist/types/run-state.js +1 -0
- package/dist/types/tool-runtime.d.ts +43 -0
- package/dist/types/tool-runtime.d.ts.map +1 -0
- package/dist/types/tool-runtime.js +30 -0
- package/dist/workspace/detect.d.ts +11 -0
- package/dist/workspace/detect.d.ts.map +1 -0
- package/dist/workspace/detect.js +28 -0
- package/dist/workspace/detect.test.d.ts +2 -0
- package/dist/workspace/detect.test.d.ts.map +1 -0
- package/dist/workspace/detect.test.js +53 -0
- package/dist/workspace/index.d.ts +2 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const ProviderSchema = z.object({
|
|
3
|
+
api_key_env: z.string().min(1),
|
|
4
|
+
models: z.record(z.string(), z.string()),
|
|
5
|
+
});
|
|
6
|
+
export const LlmConfigSchema = z
|
|
7
|
+
.object({
|
|
8
|
+
default_model: z.string().min(1),
|
|
9
|
+
allowed_models: z.array(z.string()).optional(),
|
|
10
|
+
providers: z.record(z.string(), ProviderSchema),
|
|
11
|
+
})
|
|
12
|
+
.refine((data) => {
|
|
13
|
+
const allAliases = Object.values(data.providers).flatMap((p) => Object.keys(p.models));
|
|
14
|
+
return allAliases.includes(data.default_model);
|
|
15
|
+
}, { message: 'default_model must be an alias present in a provider models map' });
|
|
16
|
+
export const SourceConfigSchema = z.object({
|
|
17
|
+
repo: z.string().min(1),
|
|
18
|
+
path: z.string().min(1),
|
|
19
|
+
});
|
|
20
|
+
export const WorkspaceConfigSchema = z.object({
|
|
21
|
+
path: z.string().min(1),
|
|
22
|
+
work: z.string().min(1),
|
|
23
|
+
runs: z.string().min(1),
|
|
24
|
+
});
|
|
25
|
+
export const ProjectConfigSchema = z.object({
|
|
26
|
+
project: z.object({
|
|
27
|
+
name: z.string().min(1),
|
|
28
|
+
key: z.string().min(1),
|
|
29
|
+
}),
|
|
30
|
+
workspace: WorkspaceConfigSchema,
|
|
31
|
+
source: SourceConfigSchema,
|
|
32
|
+
llm: LlmConfigSchema,
|
|
33
|
+
});
|
|
34
|
+
export const ContextRefSchema = z.object({
|
|
35
|
+
artifact: z.string().min(1),
|
|
36
|
+
scope: z.record(z.string(), z.string()).optional(),
|
|
37
|
+
sections: z.array(z.string()).optional(),
|
|
38
|
+
when: z.string().optional(),
|
|
39
|
+
});
|
|
40
|
+
export const SubAgentSchema = z.object({
|
|
41
|
+
name: z.string().min(1),
|
|
42
|
+
skill: z.string().min(1),
|
|
43
|
+
reads: z.array(z.string()),
|
|
44
|
+
produces: z.string().min(1),
|
|
45
|
+
tools: z.array(z.string()).optional(),
|
|
46
|
+
max_iterations: z.number().int().positive().optional(),
|
|
47
|
+
gate: z.string().optional(),
|
|
48
|
+
on_fail: z.string().optional(),
|
|
49
|
+
max_loops: z.number().int().positive().optional(),
|
|
50
|
+
});
|
|
51
|
+
const TriggerTypeSchema = z.enum(['manual', 'scheduled', 'event']);
|
|
52
|
+
const QualityGateTypeSchema = z.enum(['self-eval', 'human-review', 'multi-model']);
|
|
53
|
+
export const SimpleTaskSchema = z.object({
|
|
54
|
+
mode: z.literal('simple'),
|
|
55
|
+
trigger: z.array(TriggerTypeSchema),
|
|
56
|
+
skill: z.string().min(1),
|
|
57
|
+
reads: z.array(z.string()).optional(),
|
|
58
|
+
produces: z.string().min(1),
|
|
59
|
+
tools: z.array(z.string()).optional(),
|
|
60
|
+
published_artifact: z.string().optional(),
|
|
61
|
+
quality_gate: QualityGateTypeSchema.optional(),
|
|
62
|
+
});
|
|
63
|
+
export const CompositeTaskSchema = z.object({
|
|
64
|
+
mode: z.literal('composite'),
|
|
65
|
+
trigger: z.array(TriggerTypeSchema),
|
|
66
|
+
sub_agents: z.array(SubAgentSchema).min(1),
|
|
67
|
+
published_artifact: z.string().min(1),
|
|
68
|
+
quality_gate: QualityGateTypeSchema.optional(),
|
|
69
|
+
});
|
|
70
|
+
export const TaskSchema = z.discriminatedUnion('mode', [SimpleTaskSchema, CompositeTaskSchema]);
|
|
71
|
+
export const PersonaSpecSchema = z.object({
|
|
72
|
+
persona: z.object({
|
|
73
|
+
name: z.string().min(1),
|
|
74
|
+
identity: z.object({
|
|
75
|
+
role: z.string().min(1),
|
|
76
|
+
}),
|
|
77
|
+
skills: z.array(z.string().min(1)).min(1),
|
|
78
|
+
perception: z.object({
|
|
79
|
+
always_read: z.array(ContextRefSchema).optional(),
|
|
80
|
+
per_task: z.record(z.string(), z.array(ContextRefSchema)),
|
|
81
|
+
}),
|
|
82
|
+
tasks: z.record(z.string(), TaskSchema),
|
|
83
|
+
tools: z.object({
|
|
84
|
+
permitted: z.array(z.string()),
|
|
85
|
+
denied: z.array(z.string()),
|
|
86
|
+
writable_paths: z.array(z.string()).optional(),
|
|
87
|
+
}),
|
|
88
|
+
cadence: z.object({
|
|
89
|
+
scheduled: z.array(z.object({ cron: z.string().min(1), task: z.string().min(1) })).optional(),
|
|
90
|
+
event: z.array(z.object({ on: z.string().min(1), task: z.string().min(1) })).optional(),
|
|
91
|
+
}),
|
|
92
|
+
evaluation: z.object({
|
|
93
|
+
self_eval_model: z.string().optional(),
|
|
94
|
+
rubric: z.string().min(1),
|
|
95
|
+
multi_model: z.boolean().optional(),
|
|
96
|
+
}),
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
export const CriterionWeightSchema = z.enum(['blocking', 'important', 'advisory']);
|
|
100
|
+
export const CriterionSchema = z.object({
|
|
101
|
+
name: z.string().min(1),
|
|
102
|
+
weight: CriterionWeightSchema,
|
|
103
|
+
description: z.string().optional(),
|
|
104
|
+
});
|
|
105
|
+
export const EvaluationRubricSchema = z.object({
|
|
106
|
+
rubric: z.object({
|
|
107
|
+
artifact_type: z.string().min(1),
|
|
108
|
+
scoring_scale: z.number().positive().optional(),
|
|
109
|
+
pass_threshold: z.number(),
|
|
110
|
+
aggregate_threshold: z.number().optional(),
|
|
111
|
+
divergence_threshold: z.number().optional(),
|
|
112
|
+
models: z.array(z.string()).optional(),
|
|
113
|
+
criteria: z.array(CriterionSchema).min(1),
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ContextRef, ProjectConfig } from '../types/index.js';
|
|
2
|
+
export interface ArtifactReadResult {
|
|
3
|
+
content: string | null;
|
|
4
|
+
path: string;
|
|
5
|
+
found: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Read a single artifact from the filesystem based on its ContextRef and project config.
|
|
9
|
+
* Returns null content (with found=false) if the file does not exist.
|
|
10
|
+
*/
|
|
11
|
+
export declare function readArtifact(ref: ContextRef, config: ProjectConfig, workspacePath: string): Promise<ArtifactReadResult>;
|
|
12
|
+
//# sourceMappingURL=artifact-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-reader.d.ts","sourceRoot":"","sources":["../../src/context/artifact-reader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAsFnE,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,aAAa,EACrB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,kBAAkB,CAAC,CAY7B"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Map an artifact type + scope to a filesystem path within the workspace repo.
|
|
5
|
+
*
|
|
6
|
+
* Uses the project config's artifact paths to resolve the location.
|
|
7
|
+
* Template variables in the path pattern (e.g., `{EPIC_ID}`) are interpolated
|
|
8
|
+
* from the scope record on the ContextRef.
|
|
9
|
+
*/
|
|
10
|
+
function resolveArtifactPath(ref, config, workspacePath) {
|
|
11
|
+
const artifactType = ref.artifact;
|
|
12
|
+
const scope = ref.scope ?? {};
|
|
13
|
+
let basePath;
|
|
14
|
+
let appendFilename = false;
|
|
15
|
+
switch (artifactType) {
|
|
16
|
+
case 'requirements':
|
|
17
|
+
case 'design':
|
|
18
|
+
case 'review':
|
|
19
|
+
basePath = config.workspace.work;
|
|
20
|
+
appendFilename = true;
|
|
21
|
+
break;
|
|
22
|
+
case 'standards':
|
|
23
|
+
basePath = 'architecture/';
|
|
24
|
+
break;
|
|
25
|
+
case 'architecture':
|
|
26
|
+
basePath = 'architecture/';
|
|
27
|
+
break;
|
|
28
|
+
case 'adr-register':
|
|
29
|
+
return join(workspacePath, 'architecture', 'decisions', 'register.md');
|
|
30
|
+
case 'adr':
|
|
31
|
+
case 'decision':
|
|
32
|
+
basePath = 'architecture/decisions/';
|
|
33
|
+
break;
|
|
34
|
+
case 'product':
|
|
35
|
+
case 'backlog':
|
|
36
|
+
basePath = 'product/';
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
basePath = config.workspace.work;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
let resolvedPath = interpolatePath(basePath, scope);
|
|
43
|
+
if (appendFilename) {
|
|
44
|
+
resolvedPath = join(resolvedPath, `${artifactType}.md`);
|
|
45
|
+
}
|
|
46
|
+
// For directory-style artifact types (standards, product), look for the
|
|
47
|
+
// scope "type" value as a filename hint, or fall back to a conventional name.
|
|
48
|
+
if (!appendFilename && ref.scope?.['type']) {
|
|
49
|
+
resolvedPath = join(resolvedPath, `${ref.scope['type']}.md`);
|
|
50
|
+
}
|
|
51
|
+
return join(workspacePath, resolvedPath);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Replace `{PLACEHOLDER}` patterns in a path template using scope values.
|
|
55
|
+
*
|
|
56
|
+
* Matching strategy for each `{VAR}` placeholder:
|
|
57
|
+
* 1. Exact match against a scope key (case-insensitive)
|
|
58
|
+
* 2. Match by stripping common suffixes (_ID, _PATH) from the placeholder
|
|
59
|
+
*/
|
|
60
|
+
function interpolatePath(template, scope) {
|
|
61
|
+
return template.replace(/\{([^}]+)\}/g, (_match, varName) => {
|
|
62
|
+
const upper = varName.toUpperCase();
|
|
63
|
+
for (const [key, value] of Object.entries(scope)) {
|
|
64
|
+
if (key.toUpperCase() === upper)
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
const stripped = upper.replace(/_(ID|PATH)$/, '');
|
|
68
|
+
for (const [key, value] of Object.entries(scope)) {
|
|
69
|
+
if (key.toUpperCase() === stripped)
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
return `{${varName}}`;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Read a single artifact from the filesystem based on its ContextRef and project config.
|
|
77
|
+
* Returns null content (with found=false) if the file does not exist.
|
|
78
|
+
*/
|
|
79
|
+
export async function readArtifact(ref, config, workspacePath) {
|
|
80
|
+
const filePath = resolveArtifactPath(ref, config, workspacePath);
|
|
81
|
+
try {
|
|
82
|
+
const content = await readFile(filePath, 'utf-8');
|
|
83
|
+
return { content, path: filePath, found: true };
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
const code = err.code;
|
|
87
|
+
if (code === 'ENOENT' || code === 'EISDIR') {
|
|
88
|
+
return { content: null, path: filePath, found: false };
|
|
89
|
+
}
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Perception, ProjectConfig, ArtifactReference } from '../types/index.js';
|
|
2
|
+
export interface ContextBlock {
|
|
3
|
+
source: ArtifactReference;
|
|
4
|
+
content: string;
|
|
5
|
+
tokens: number;
|
|
6
|
+
priority: 'required' | 'conditional';
|
|
7
|
+
}
|
|
8
|
+
export interface AssembledContext {
|
|
9
|
+
blocks: ContextBlock[];
|
|
10
|
+
totalTokens: number;
|
|
11
|
+
budgetLimit: number;
|
|
12
|
+
gaps: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Assemble context for a persona task run.
|
|
16
|
+
*
|
|
17
|
+
* Gathers artifacts from the persona's perception (always_read + per_task),
|
|
18
|
+
* interpolates template variables, extracts named sections, enforces a token
|
|
19
|
+
* budget, and returns a structured result ready for the LLM call.
|
|
20
|
+
*/
|
|
21
|
+
export declare function assembleContext(perception: Perception, taskName: string, scopeVars: Record<string, string>, config: ProjectConfig, workspacePath: string, budgetTokens?: number): Promise<AssembledContext>;
|
|
22
|
+
//# sourceMappingURL=assembler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assembler.d.ts","sourceRoot":"","sources":["../../src/context/assembler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAc,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAMlG,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,UAAU,GAAG,aAAa,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAoBD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACjC,MAAM,EAAE,aAAa,EACrB,aAAa,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,CAAC,CA0F3B"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { readArtifact } from './artifact-reader.js';
|
|
2
|
+
import { readCodeFiles } from './code-reader.js';
|
|
3
|
+
import { extractSections } from './section-extractor.js';
|
|
4
|
+
import { countTokens, defaultTokenBudget } from './token-budget.js';
|
|
5
|
+
/**
|
|
6
|
+
* Interpolate `{{ .VAR }}` template variables in a ContextRef's scope values.
|
|
7
|
+
*/
|
|
8
|
+
function interpolateRef(ref, scopeVars) {
|
|
9
|
+
if (!ref.scope)
|
|
10
|
+
return ref;
|
|
11
|
+
const interpolated = {};
|
|
12
|
+
for (const [key, value] of Object.entries(ref.scope)) {
|
|
13
|
+
let resolved = value;
|
|
14
|
+
for (const [varName, varValue] of Object.entries(scopeVars)) {
|
|
15
|
+
resolved = resolved.replace(`{{ .${varName} }}`, varValue);
|
|
16
|
+
}
|
|
17
|
+
interpolated[key] = resolved;
|
|
18
|
+
}
|
|
19
|
+
return { ...ref, scope: interpolated };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Assemble context for a persona task run.
|
|
23
|
+
*
|
|
24
|
+
* Gathers artifacts from the persona's perception (always_read + per_task),
|
|
25
|
+
* interpolates template variables, extracts named sections, enforces a token
|
|
26
|
+
* budget, and returns a structured result ready for the LLM call.
|
|
27
|
+
*/
|
|
28
|
+
export async function assembleContext(perception, taskName, scopeVars, config, workspacePath, budgetTokens) {
|
|
29
|
+
const budget = budgetTokens ?? defaultTokenBudget();
|
|
30
|
+
const taskRefs = perception.per_task[taskName] ?? [];
|
|
31
|
+
const allRefs = [...(perception.always_read ?? []), ...taskRefs];
|
|
32
|
+
const interpolatedRefs = allRefs.map((ref) => interpolateRef(ref, scopeVars));
|
|
33
|
+
const requiredRefs = interpolatedRefs.filter((r) => !r.when);
|
|
34
|
+
const conditionalRefs = interpolatedRefs.filter((r) => !!r.when);
|
|
35
|
+
const blocks = [];
|
|
36
|
+
const gaps = [];
|
|
37
|
+
let usedTokens = 0;
|
|
38
|
+
// --- Process required refs ---
|
|
39
|
+
for (const ref of requiredRefs) {
|
|
40
|
+
if (ref.artifact === 'codebase') {
|
|
41
|
+
const codeBlocks = await readCodeContext(ref, config, workspacePath);
|
|
42
|
+
for (const block of codeBlocks) {
|
|
43
|
+
usedTokens += block.tokens;
|
|
44
|
+
blocks.push(block);
|
|
45
|
+
}
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const result = await readArtifact(ref, config, workspacePath);
|
|
49
|
+
if (!result.found || result.content === null) {
|
|
50
|
+
gaps.push(`${ref.artifact}${ref.scope ? ` (scope: ${JSON.stringify(ref.scope)})` : ''}`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
let content = result.content;
|
|
54
|
+
if (ref.sections && ref.sections.length > 0) {
|
|
55
|
+
content = extractSections(content, ref.sections);
|
|
56
|
+
}
|
|
57
|
+
const tokens = countTokens(content);
|
|
58
|
+
usedTokens += tokens;
|
|
59
|
+
blocks.push({
|
|
60
|
+
source: {
|
|
61
|
+
type: ref.artifact,
|
|
62
|
+
path: result.path,
|
|
63
|
+
sections: ref.sections,
|
|
64
|
+
},
|
|
65
|
+
content,
|
|
66
|
+
tokens,
|
|
67
|
+
priority: 'required',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// --- Process conditional refs (budget-aware) ---
|
|
71
|
+
for (const ref of conditionalRefs) {
|
|
72
|
+
if (usedTokens >= budget)
|
|
73
|
+
break;
|
|
74
|
+
const result = await readArtifact(ref, config, workspacePath);
|
|
75
|
+
if (!result.found || result.content === null) {
|
|
76
|
+
gaps.push(`${ref.artifact}${ref.scope ? ` (scope: ${JSON.stringify(ref.scope)})` : ''} [conditional]`);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
let content = result.content;
|
|
80
|
+
if (ref.sections && ref.sections.length > 0) {
|
|
81
|
+
content = extractSections(content, ref.sections);
|
|
82
|
+
}
|
|
83
|
+
const tokens = countTokens(content);
|
|
84
|
+
if (usedTokens + tokens > budget)
|
|
85
|
+
break;
|
|
86
|
+
usedTokens += tokens;
|
|
87
|
+
blocks.push({
|
|
88
|
+
source: {
|
|
89
|
+
type: ref.artifact,
|
|
90
|
+
path: result.path,
|
|
91
|
+
sections: ref.sections,
|
|
92
|
+
},
|
|
93
|
+
content,
|
|
94
|
+
tokens,
|
|
95
|
+
priority: 'conditional',
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
blocks,
|
|
100
|
+
totalTokens: usedTokens,
|
|
101
|
+
budgetLimit: budget,
|
|
102
|
+
gaps,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async function readCodeContext(ref, config, workspacePath) {
|
|
106
|
+
const targetPath = config.source.path;
|
|
107
|
+
const pathPatterns = ref.scope?.['paths']
|
|
108
|
+
? ref.scope['paths'].split(',').map((p) => p.trim())
|
|
109
|
+
: [];
|
|
110
|
+
if (pathPatterns.length === 0)
|
|
111
|
+
return [];
|
|
112
|
+
const codeFiles = await readCodeFiles(targetPath, pathPatterns);
|
|
113
|
+
return codeFiles.map((file) => {
|
|
114
|
+
const content = `// File: ${file.relativePath}\n${file.content}`;
|
|
115
|
+
return {
|
|
116
|
+
source: {
|
|
117
|
+
type: 'codebase',
|
|
118
|
+
path: file.relativePath,
|
|
119
|
+
repo: config.source.repo,
|
|
120
|
+
},
|
|
121
|
+
content,
|
|
122
|
+
tokens: countTokens(content),
|
|
123
|
+
priority: 'required',
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface CodeFileResult {
|
|
2
|
+
relativePath: string;
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Read code files from the target repo matching the given path patterns.
|
|
7
|
+
*
|
|
8
|
+
* Patterns are relative paths or simple glob-like prefixes within the target repo.
|
|
9
|
+
* For Phase 1, supports:
|
|
10
|
+
* - Exact file paths (e.g., "src/index.ts")
|
|
11
|
+
* - Directory paths (e.g., "src/config/") -- reads all files in the directory recursively
|
|
12
|
+
*/
|
|
13
|
+
export declare function readCodeFiles(targetRepoPath: string, pathPatterns: string[]): Promise<CodeFileResult[]>;
|
|
14
|
+
//# sourceMappingURL=code-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-reader.d.ts","sourceRoot":"","sources":["../../src/context/code-reader.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,cAAc,EAAE,CAAC,CA4B3B"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { readFile, readdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Read code files from the target repo matching the given path patterns.
|
|
5
|
+
*
|
|
6
|
+
* Patterns are relative paths or simple glob-like prefixes within the target repo.
|
|
7
|
+
* For Phase 1, supports:
|
|
8
|
+
* - Exact file paths (e.g., "src/index.ts")
|
|
9
|
+
* - Directory paths (e.g., "src/config/") -- reads all files in the directory recursively
|
|
10
|
+
*/
|
|
11
|
+
export async function readCodeFiles(targetRepoPath, pathPatterns) {
|
|
12
|
+
const results = [];
|
|
13
|
+
for (const pattern of pathPatterns) {
|
|
14
|
+
const fullPath = join(targetRepoPath, pattern);
|
|
15
|
+
try {
|
|
16
|
+
const fileStat = await stat(fullPath);
|
|
17
|
+
if (fileStat.isFile()) {
|
|
18
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
19
|
+
results.push({ relativePath: pattern, content });
|
|
20
|
+
}
|
|
21
|
+
else if (fileStat.isDirectory()) {
|
|
22
|
+
const files = await collectFiles(fullPath);
|
|
23
|
+
for (const absPath of files) {
|
|
24
|
+
const content = await readFile(absPath, 'utf-8');
|
|
25
|
+
results.push({
|
|
26
|
+
relativePath: relative(targetRepoPath, absPath),
|
|
27
|
+
content,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
const code = err.code;
|
|
34
|
+
if (code === 'ENOENT')
|
|
35
|
+
continue;
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return results;
|
|
40
|
+
}
|
|
41
|
+
async function collectFiles(dirPath) {
|
|
42
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
43
|
+
const files = [];
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
const fullPath = join(dirPath, entry.name);
|
|
46
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules')
|
|
47
|
+
continue;
|
|
48
|
+
if (entry.isFile()) {
|
|
49
|
+
files.push(fullPath);
|
|
50
|
+
}
|
|
51
|
+
else if (entry.isDirectory()) {
|
|
52
|
+
files.push(...(await collectFiles(fullPath)));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return files;
|
|
56
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.test.d.ts","sourceRoot":"","sources":["../../src/context/context.test.ts"],"names":[],"mappings":""}
|