omegon 0.6.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/.gitattributes +3 -0
- package/AGENTS.md +16 -0
- package/LICENSE +15 -0
- package/README.md +289 -0
- package/bin/pi.mjs +30 -0
- package/extensions/00-secrets/index.ts +1126 -0
- package/extensions/01-auth/auth.ts +401 -0
- package/extensions/01-auth/index.ts +289 -0
- package/extensions/auto-compact.ts +42 -0
- package/extensions/bootstrap/deps.ts +291 -0
- package/extensions/bootstrap/index.ts +811 -0
- package/extensions/chronos/chronos.sh +487 -0
- package/extensions/chronos/index.ts +148 -0
- package/extensions/cleave/assessment.ts +754 -0
- package/extensions/cleave/bridge.ts +31 -0
- package/extensions/cleave/conflicts.ts +250 -0
- package/extensions/cleave/dispatcher.ts +808 -0
- package/extensions/cleave/guardrails.ts +426 -0
- package/extensions/cleave/index.ts +3121 -0
- package/extensions/cleave/lifecycle-emitter.ts +20 -0
- package/extensions/cleave/openspec.ts +811 -0
- package/extensions/cleave/planner.ts +260 -0
- package/extensions/cleave/review.ts +579 -0
- package/extensions/cleave/skills.ts +355 -0
- package/extensions/cleave/types.ts +261 -0
- package/extensions/cleave/workspace.ts +861 -0
- package/extensions/cleave/worktree.ts +243 -0
- package/extensions/core-renderers.ts +253 -0
- package/extensions/dashboard/context-gauge.ts +58 -0
- package/extensions/dashboard/file-watch.ts +14 -0
- package/extensions/dashboard/footer.ts +1145 -0
- package/extensions/dashboard/git.ts +185 -0
- package/extensions/dashboard/index.ts +478 -0
- package/extensions/dashboard/memory-audit.ts +34 -0
- package/extensions/dashboard/overlay-data.ts +705 -0
- package/extensions/dashboard/overlay.ts +365 -0
- package/extensions/dashboard/render-utils.ts +54 -0
- package/extensions/dashboard/types.ts +191 -0
- package/extensions/dashboard/uri-helper.ts +45 -0
- package/extensions/debug.ts +69 -0
- package/extensions/defaults.ts +282 -0
- package/extensions/design-tree/dashboard-state.ts +161 -0
- package/extensions/design-tree/design-card.ts +362 -0
- package/extensions/design-tree/index.ts +2130 -0
- package/extensions/design-tree/lifecycle-emitter.ts +41 -0
- package/extensions/design-tree/tree.ts +1607 -0
- package/extensions/design-tree/types.ts +163 -0
- package/extensions/distill.ts +127 -0
- package/extensions/effort/index.ts +395 -0
- package/extensions/effort/tiers.ts +146 -0
- package/extensions/effort/types.ts +105 -0
- package/extensions/lib/git-state.ts +227 -0
- package/extensions/lib/local-models.ts +157 -0
- package/extensions/lib/model-preferences.ts +51 -0
- package/extensions/lib/model-routing.ts +720 -0
- package/extensions/lib/operator-fallback.ts +205 -0
- package/extensions/lib/operator-profile.ts +360 -0
- package/extensions/lib/slash-command-bridge.ts +253 -0
- package/extensions/lib/typebox-helpers.ts +16 -0
- package/extensions/local-inference/index.ts +727 -0
- package/extensions/mcp-bridge/README.md +220 -0
- package/extensions/mcp-bridge/index.ts +951 -0
- package/extensions/mcp-bridge/lib.ts +365 -0
- package/extensions/mcp-bridge/mcp.json +3 -0
- package/extensions/mcp-bridge/package.json +11 -0
- package/extensions/model-budget.ts +752 -0
- package/extensions/offline-driver.ts +403 -0
- package/extensions/openspec/archive-gate.ts +164 -0
- package/extensions/openspec/branch-cleanup.ts +64 -0
- package/extensions/openspec/dashboard-state.ts +50 -0
- package/extensions/openspec/index.ts +1917 -0
- package/extensions/openspec/lifecycle-emitter.ts +65 -0
- package/extensions/openspec/lifecycle-files.ts +70 -0
- package/extensions/openspec/lifecycle.ts +50 -0
- package/extensions/openspec/reconcile.ts +187 -0
- package/extensions/openspec/spec.ts +1385 -0
- package/extensions/openspec/types.ts +98 -0
- package/extensions/project-memory/DESIGN-global-mind.md +198 -0
- package/extensions/project-memory/README.md +202 -0
- package/extensions/project-memory/api-types.ts +382 -0
- package/extensions/project-memory/compaction-policy.ts +29 -0
- package/extensions/project-memory/core.ts +164 -0
- package/extensions/project-memory/embeddings.ts +230 -0
- package/extensions/project-memory/extraction-v2.ts +861 -0
- package/extensions/project-memory/factstore.ts +2177 -0
- package/extensions/project-memory/index.ts +3459 -0
- package/extensions/project-memory/injection-metrics.ts +91 -0
- package/extensions/project-memory/jsonl-io.ts +12 -0
- package/extensions/project-memory/lifecycle.ts +331 -0
- package/extensions/project-memory/migration.ts +293 -0
- package/extensions/project-memory/package.json +9 -0
- package/extensions/project-memory/sci-renderers.ts +7 -0
- package/extensions/project-memory/template.ts +103 -0
- package/extensions/project-memory/triggers.ts +52 -0
- package/extensions/project-memory/types.ts +102 -0
- package/extensions/render/composition/fonts/Inter-Bold.ttf +0 -0
- package/extensions/render/composition/fonts/Inter-Regular.ttf +0 -0
- package/extensions/render/composition/fonts/Tomorrow-Bold.ttf +0 -0
- package/extensions/render/composition/fonts/Tomorrow-Regular.ttf +0 -0
- package/extensions/render/composition/package-lock.json +534 -0
- package/extensions/render/composition/package.json +22 -0
- package/extensions/render/composition/render.mjs +246 -0
- package/extensions/render/composition/test-comp.tsx +87 -0
- package/extensions/render/composition/types.ts +24 -0
- package/extensions/render/excalidraw/UPSTREAM.md +81 -0
- package/extensions/render/excalidraw/elements.ts +764 -0
- package/extensions/render/excalidraw/index.ts +66 -0
- package/extensions/render/excalidraw/types.ts +223 -0
- package/extensions/render/excalidraw-renderer/pyproject.toml +8 -0
- package/extensions/render/excalidraw-renderer/render_excalidraw.py +182 -0
- package/extensions/render/excalidraw-renderer/render_template.html +59 -0
- package/extensions/render/index.ts +830 -0
- package/extensions/render/native-diagrams/index.ts +57 -0
- package/extensions/render/native-diagrams/motifs.ts +542 -0
- package/extensions/render/native-diagrams/raster.ts +8 -0
- package/extensions/render/native-diagrams/scene.ts +75 -0
- package/extensions/render/native-diagrams/spec.ts +204 -0
- package/extensions/render/native-diagrams/svg.ts +116 -0
- package/extensions/sci-ui.ts +304 -0
- package/extensions/session-log.ts +174 -0
- package/extensions/shared-state.ts +146 -0
- package/extensions/spinner-verbs.ts +91 -0
- package/extensions/style.ts +281 -0
- package/extensions/terminal-title.ts +191 -0
- package/extensions/tool-profile/index.ts +291 -0
- package/extensions/tool-profile/profiles.ts +290 -0
- package/extensions/types.d.ts +9 -0
- package/extensions/vault/index.ts +185 -0
- package/extensions/version-check.ts +90 -0
- package/extensions/view/index.ts +859 -0
- package/extensions/view/uri-resolver.ts +148 -0
- package/extensions/web-search/index.ts +182 -0
- package/extensions/web-search/providers.ts +121 -0
- package/extensions/web-ui/index.ts +110 -0
- package/extensions/web-ui/server.ts +265 -0
- package/extensions/web-ui/state.ts +462 -0
- package/extensions/web-ui/static/index.html +145 -0
- package/extensions/web-ui/types.ts +284 -0
- package/package.json +76 -0
- package/prompts/init.md +75 -0
- package/prompts/new-repo.md +54 -0
- package/prompts/oci-login.md +56 -0
- package/prompts/status.md +50 -0
- package/settings.json +4 -0
- package/skills/cleave/SKILL.md +218 -0
- package/skills/git/SKILL.md +209 -0
- package/skills/git/_reference/ci-validation.md +204 -0
- package/skills/oci/SKILL.md +338 -0
- package/skills/openspec/SKILL.md +346 -0
- package/skills/pi-extensions/SKILL.md +191 -0
- package/skills/pi-tui/SKILL.md +517 -0
- package/skills/python/SKILL.md +189 -0
- package/skills/rust/SKILL.md +268 -0
- package/skills/security/SKILL.md +206 -0
- package/skills/style/SKILL.md +264 -0
- package/skills/typescript/SKILL.md +225 -0
- package/skills/vault/SKILL.md +102 -0
- package/themes/alpharius-legacy.json +85 -0
- package/themes/alpharius.conf +59 -0
- package/themes/alpharius.json +88 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cleave/skills — Skill matching and resolution for child tasks.
|
|
3
|
+
*
|
|
4
|
+
* Maps child task scope (file patterns) and explicit annotations to
|
|
5
|
+
* skill directives. Skills are specialized instruction sets (SKILL.md)
|
|
6
|
+
* that guide child agents on language-specific conventions, tooling,
|
|
7
|
+
* and best practices.
|
|
8
|
+
*
|
|
9
|
+
* Two matching strategies:
|
|
10
|
+
* 1. **Annotation**: `<!-- skills: python, k8s-operations -->` in tasks.md
|
|
11
|
+
* overrides auto-matching for that group
|
|
12
|
+
* 2. **Auto-match**: File scope patterns (*.py→python, *.rs→rust, etc.)
|
|
13
|
+
* matched against DEFAULT_MAPPINGS
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { existsSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import type { ChildPlan } from "./types.ts";
|
|
19
|
+
|
|
20
|
+
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Maps file patterns to a skill name and optional model tier hint.
|
|
24
|
+
*
|
|
25
|
+
* The glob patterns follow minimatch-style syntax but are matched
|
|
26
|
+
* with a simplified engine (see `globMatches`).
|
|
27
|
+
*/
|
|
28
|
+
export interface SkillMapping {
|
|
29
|
+
/** Glob patterns that trigger this skill (e.g., "*.py", "Containerfile") */
|
|
30
|
+
patterns: string[];
|
|
31
|
+
/** Skill name (matches the directory name under skills/) */
|
|
32
|
+
skill: string;
|
|
33
|
+
/** Optional preferred model tier for this skill's complexity */
|
|
34
|
+
preferredTier?: "retribution" | "victory" | "gloriana";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ─── Default Mappings ───────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Default file-pattern-to-skill mappings.
|
|
41
|
+
*
|
|
42
|
+
* Ordered by specificity — more specific patterns first.
|
|
43
|
+
* Projects can extend this via future config.
|
|
44
|
+
*/
|
|
45
|
+
export const DEFAULT_MAPPINGS: SkillMapping[] = [
|
|
46
|
+
// Python
|
|
47
|
+
{
|
|
48
|
+
patterns: ["*.py", "pyproject.toml", "setup.py", "setup.cfg", "requirements*.txt", "Pipfile"],
|
|
49
|
+
skill: "python",
|
|
50
|
+
preferredTier: "victory",
|
|
51
|
+
},
|
|
52
|
+
// Rust
|
|
53
|
+
{
|
|
54
|
+
patterns: ["*.rs", "Cargo.toml", "Cargo.lock"],
|
|
55
|
+
skill: "rust",
|
|
56
|
+
preferredTier: "victory",
|
|
57
|
+
},
|
|
58
|
+
// OCI / Containers
|
|
59
|
+
{
|
|
60
|
+
patterns: ["Containerfile", "Dockerfile", "*.containerfile", "*.dockerfile", "docker-compose*.yml", "docker-compose*.yaml"],
|
|
61
|
+
skill: "oci",
|
|
62
|
+
preferredTier: "victory",
|
|
63
|
+
},
|
|
64
|
+
// Kubernetes / Helm
|
|
65
|
+
{
|
|
66
|
+
patterns: ["k8s/**", "kubernetes/**", "helm/**", "charts/**", "Chart.yaml", "values*.yaml", "**/templates/*.yaml"],
|
|
67
|
+
skill: "k8s-operations",
|
|
68
|
+
preferredTier: "victory",
|
|
69
|
+
},
|
|
70
|
+
// TypeScript
|
|
71
|
+
{
|
|
72
|
+
patterns: ["*.ts", "*.tsx", "tsconfig.json", "tsconfig.*.json"],
|
|
73
|
+
skill: "typescript",
|
|
74
|
+
preferredTier: "victory",
|
|
75
|
+
},
|
|
76
|
+
// Pi extensions (more specific than generic TypeScript — matched first by specificity)
|
|
77
|
+
{
|
|
78
|
+
patterns: ["extensions/**/*.ts", "extensions/*/index.ts"],
|
|
79
|
+
skill: "pi-extensions",
|
|
80
|
+
preferredTier: "victory",
|
|
81
|
+
},
|
|
82
|
+
// Git (rarely auto-matched, usually annotated)
|
|
83
|
+
{
|
|
84
|
+
patterns: [".gitignore", ".gitattributes", ".gitmodules"],
|
|
85
|
+
skill: "git",
|
|
86
|
+
},
|
|
87
|
+
// OpenSpec
|
|
88
|
+
{
|
|
89
|
+
patterns: ["openspec/**", "**/spec.md", "**/proposal.md"],
|
|
90
|
+
skill: "openspec",
|
|
91
|
+
},
|
|
92
|
+
// Style / visual
|
|
93
|
+
{
|
|
94
|
+
patterns: ["*.excalidraw", "*.d2"],
|
|
95
|
+
skill: "style",
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
// ─── Matching ───────────────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Match skills to a child plan based on its scope and annotations.
|
|
103
|
+
*
|
|
104
|
+
* If the child has explicit `skills` from a `<!-- skills: ... -->` annotation,
|
|
105
|
+
* those are used as-is (annotation overrides auto-match, per design D1).
|
|
106
|
+
*
|
|
107
|
+
* Otherwise, file scope patterns are matched against DEFAULT_MAPPINGS
|
|
108
|
+
* (or a custom mappings array) to auto-detect relevant skills.
|
|
109
|
+
*
|
|
110
|
+
* Returns the list of skill names (deduplicated, stable order).
|
|
111
|
+
*/
|
|
112
|
+
export function matchSkillsToChild(
|
|
113
|
+
child: ChildPlan,
|
|
114
|
+
mappings: SkillMapping[] = DEFAULT_MAPPINGS,
|
|
115
|
+
): string[] {
|
|
116
|
+
// Annotation override: if child already has skills from <!-- skills: ... -->,
|
|
117
|
+
// use those directly and skip auto-matching
|
|
118
|
+
if (child.skills && child.skills.length > 0) {
|
|
119
|
+
return child.skills;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Auto-match from scope patterns
|
|
123
|
+
const matched = new Set<string>();
|
|
124
|
+
|
|
125
|
+
for (const scopePattern of child.scope) {
|
|
126
|
+
for (const mapping of mappings) {
|
|
127
|
+
if (mapping.patterns.some((p) => globMatches(scopePattern, p))) {
|
|
128
|
+
matched.add(mapping.skill);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Also check description for file references (backtick-quoted paths and known filenames)
|
|
134
|
+
const fileRefs = child.description.match(/`([a-zA-Z0-9_./-]+)`/g) ?? [];
|
|
135
|
+
for (const ref of fileRefs) {
|
|
136
|
+
const cleanRef = ref.replace(/`/g, "");
|
|
137
|
+
for (const mapping of mappings) {
|
|
138
|
+
if (mapping.patterns.some((p) => globMatches(cleanRef, p))) {
|
|
139
|
+
matched.add(mapping.skill);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return [...matched];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Match skills to all children in a plan, mutating their `skills` arrays.
|
|
149
|
+
*
|
|
150
|
+
* This is the top-level entry point called before task file generation.
|
|
151
|
+
*/
|
|
152
|
+
export function matchSkillsToAllChildren(
|
|
153
|
+
children: ChildPlan[],
|
|
154
|
+
mappings: SkillMapping[] = DEFAULT_MAPPINGS,
|
|
155
|
+
): void {
|
|
156
|
+
for (const child of children) {
|
|
157
|
+
const skills = matchSkillsToChild(child, mappings);
|
|
158
|
+
child.skills = skills;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ─── Resolution ─────────────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Well-known skill search paths, in priority order.
|
|
166
|
+
*
|
|
167
|
+
* 1. Package-local skills (relative to Omegon root)
|
|
168
|
+
* 2. User-installed skills under ~/.pi/agent/
|
|
169
|
+
*/
|
|
170
|
+
function getSkillSearchPaths(piKitRoot?: string): string[] {
|
|
171
|
+
const paths: string[] = [];
|
|
172
|
+
|
|
173
|
+
// Package-local skills
|
|
174
|
+
if (piKitRoot) {
|
|
175
|
+
paths.push(join(piKitRoot, "skills"));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// User skill symlinks (loaded via ~/.pi/agent/git/)
|
|
179
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
180
|
+
if (home) {
|
|
181
|
+
paths.push(join(home, ".pi", "agent", "skills"));
|
|
182
|
+
// Also check external skills loaded via git symlinks
|
|
183
|
+
const gitSkillsBase = join(home, ".pi", "agent", "git");
|
|
184
|
+
if (existsSync(gitSkillsBase)) {
|
|
185
|
+
paths.push(gitSkillsBase);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return paths;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Resolve a skill name to its absolute SKILL.md path.
|
|
194
|
+
*
|
|
195
|
+
* Searches well-known skill directories for `{skillName}/SKILL.md`.
|
|
196
|
+
* Returns null if the skill is not found.
|
|
197
|
+
*/
|
|
198
|
+
export function resolveSkillPath(
|
|
199
|
+
skillName: string,
|
|
200
|
+
piKitRoot?: string,
|
|
201
|
+
): string | null {
|
|
202
|
+
const searchPaths = getSkillSearchPaths(piKitRoot);
|
|
203
|
+
|
|
204
|
+
for (const base of searchPaths) {
|
|
205
|
+
const candidate = join(base, skillName, "SKILL.md");
|
|
206
|
+
if (existsSync(candidate)) {
|
|
207
|
+
return candidate;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Resolve multiple skill names to their SKILL.md paths.
|
|
216
|
+
*
|
|
217
|
+
* Returns an array of { skill, path } for each skill that was found.
|
|
218
|
+
* Skills that cannot be resolved are omitted with a warning in the
|
|
219
|
+
* returned `notFound` array.
|
|
220
|
+
*/
|
|
221
|
+
export function resolveSkillPaths(
|
|
222
|
+
skillNames: string[],
|
|
223
|
+
piKitRoot?: string,
|
|
224
|
+
): { resolved: Array<{ skill: string; path: string }>; notFound: string[] } {
|
|
225
|
+
const resolved: Array<{ skill: string; path: string }> = [];
|
|
226
|
+
const notFound: string[] = [];
|
|
227
|
+
|
|
228
|
+
for (const name of skillNames) {
|
|
229
|
+
const path = resolveSkillPath(name, piKitRoot);
|
|
230
|
+
if (path) {
|
|
231
|
+
resolved.push({ skill: name, path });
|
|
232
|
+
} else {
|
|
233
|
+
notFound.push(name);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return { resolved, notFound };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Get the preferred model tier for a set of skills.
|
|
242
|
+
*
|
|
243
|
+
* Returns the highest tier among all matched skills:
|
|
244
|
+
* gloriana > victory > retribution > undefined (default)
|
|
245
|
+
*
|
|
246
|
+
* This allows skills to hint at the model complexity needed
|
|
247
|
+
* for good results (e.g., Rust/Python → victory, complex architecture → gloriana).
|
|
248
|
+
*/
|
|
249
|
+
export function getPreferredTier(
|
|
250
|
+
skillNames: string[],
|
|
251
|
+
mappings: SkillMapping[] = DEFAULT_MAPPINGS,
|
|
252
|
+
): "retribution" | "victory" | "gloriana" | undefined {
|
|
253
|
+
const tierRank = { retribution: 1, victory: 2, gloriana: 3 };
|
|
254
|
+
let maxRank = 0;
|
|
255
|
+
let maxTier: "retribution" | "victory" | "gloriana" | undefined;
|
|
256
|
+
|
|
257
|
+
for (const name of skillNames) {
|
|
258
|
+
const mapping = mappings.find((m) => m.skill === name);
|
|
259
|
+
if (mapping?.preferredTier) {
|
|
260
|
+
const rank = tierRank[mapping.preferredTier] ?? 0;
|
|
261
|
+
if (rank > maxRank) {
|
|
262
|
+
maxRank = rank;
|
|
263
|
+
maxTier = mapping.preferredTier;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return maxTier;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ─── Glob Matching ──────────────────────────────────────────────────────────
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Simplified glob matching for file patterns.
|
|
275
|
+
*
|
|
276
|
+
* Supports:
|
|
277
|
+
* - `*` matches any characters within a path segment (no slashes)
|
|
278
|
+
* - `**` matches any number of path segments
|
|
279
|
+
* - Exact matches (e.g., "Containerfile")
|
|
280
|
+
*
|
|
281
|
+
* The `filePath` is the scope entry (which may itself contain globs),
|
|
282
|
+
* and `pattern` is the mapping pattern.
|
|
283
|
+
*
|
|
284
|
+
* Matching is bidirectional: the scope pattern may be a glob ("src/models/*.py")
|
|
285
|
+
* and the mapping pattern may also be a glob ("*.py"). We check if they could
|
|
286
|
+
* match the same files.
|
|
287
|
+
*/
|
|
288
|
+
export function globMatches(filePath: string, pattern: string): boolean {
|
|
289
|
+
const fpLower = filePath.toLowerCase();
|
|
290
|
+
const patLower = pattern.toLowerCase();
|
|
291
|
+
|
|
292
|
+
// Exact match
|
|
293
|
+
if (fpLower === patLower) return true;
|
|
294
|
+
|
|
295
|
+
// If pattern has no wildcards, check if filePath ends with it or contains it as a segment
|
|
296
|
+
if (!patLower.includes("*")) {
|
|
297
|
+
const fpParts = fpLower.split("/");
|
|
298
|
+
return fpParts.some((part) => part === patLower) || fpLower.endsWith("/" + patLower);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Pattern is a simple extension glob: *.ext
|
|
302
|
+
const extMatch = patLower.match(/^\*\.(\w+)$/);
|
|
303
|
+
if (extMatch) {
|
|
304
|
+
const ext = "." + extMatch[1];
|
|
305
|
+
// Check if filePath ends with this extension (ignoring glob chars in filePath)
|
|
306
|
+
const fpClean = fpLower.replace(/\*/g, "x"); // neutralize globs in filePath for extension check
|
|
307
|
+
if (fpClean.endsWith(ext)) return true;
|
|
308
|
+
// Also match if filePath is a glob that would produce files with this extension
|
|
309
|
+
// e.g., "src/models/*.py" should match "*.py"
|
|
310
|
+
if (fpLower.endsWith("*" + ext)) return true;
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Pattern uses **: directory-recursive match
|
|
315
|
+
if (patLower.includes("**")) {
|
|
316
|
+
// Convert ** pattern to a check: does filePath start with the prefix?
|
|
317
|
+
const parts = patLower.split("**");
|
|
318
|
+
if (parts.length === 2) {
|
|
319
|
+
const prefix = parts[0].replace(/\/$/, "");
|
|
320
|
+
const suffix = parts[1].replace(/^\//, "");
|
|
321
|
+
// Check if filePath starts with the prefix directory
|
|
322
|
+
if (prefix && !fpLower.startsWith(prefix) && !fpLower.startsWith(prefix + "/")) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
// If there's a suffix, check if filePath (or its glob expansion) could match
|
|
326
|
+
if (suffix) {
|
|
327
|
+
// Suffix may contain path segments + globs, e.g., "templates/*.yaml"
|
|
328
|
+
// Convert the suffix into a regex for matching against the relevant part of filePath
|
|
329
|
+
const suffixRegex = suffix
|
|
330
|
+
.replace(/[.+?^${}()|[\]\\]/g, "\\$&")
|
|
331
|
+
.replace(/\*/g, "[^/]*");
|
|
332
|
+
const re = new RegExp("(?:^|/)" + suffixRegex + "$");
|
|
333
|
+
if (re.test(fpLower)) return true;
|
|
334
|
+
// Also check extension-only: "*.yaml" suffix against file ending
|
|
335
|
+
const suffixExtMatch = suffix.match(/^\*\.(\w+)$/);
|
|
336
|
+
if (suffixExtMatch) {
|
|
337
|
+
const ext = "." + suffixExtMatch[1];
|
|
338
|
+
return fpLower.endsWith(ext) || fpLower.endsWith("*" + ext);
|
|
339
|
+
}
|
|
340
|
+
// Check for path-based suffix like "templates/*.yaml"
|
|
341
|
+
return fpLower.includes(suffix.replace(/\*/g, ""));
|
|
342
|
+
}
|
|
343
|
+
// No suffix — prefix/** matches anything under that directory
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Pattern with single *: segment match (e.g., "docker-compose*.yml")
|
|
349
|
+
// Convert to a regex
|
|
350
|
+
const escaped = patLower
|
|
351
|
+
.replace(/[.+?^${}()|[\]\\]/g, "\\$&")
|
|
352
|
+
.replace(/\*/g, "[^/]*");
|
|
353
|
+
const re = new RegExp("(?:^|/)" + escaped + "$");
|
|
354
|
+
return re.test(fpLower);
|
|
355
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cleave/types — Shared type definitions for the cleave extension.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { BUILTIN_VOLATILE_ALLOWLIST } from "../lib/git-state.ts";
|
|
6
|
+
|
|
7
|
+
// ─── Assessment ──────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export interface PatternDefinition {
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
keywords: string[];
|
|
13
|
+
requiredAny: string[];
|
|
14
|
+
expectedComponents: Record<string, string[]>;
|
|
15
|
+
systemsBase: number;
|
|
16
|
+
modifiersDefault: string[];
|
|
17
|
+
splitStrategy: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface PatternMatch {
|
|
21
|
+
patternId: string;
|
|
22
|
+
name: string;
|
|
23
|
+
confidence: number;
|
|
24
|
+
keywordsMatched: string[];
|
|
25
|
+
systems: number;
|
|
26
|
+
modifiers: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AssessmentResult {
|
|
30
|
+
complexity: number;
|
|
31
|
+
systems: number;
|
|
32
|
+
modifiers: string[];
|
|
33
|
+
method: "fast-path" | "heuristic";
|
|
34
|
+
pattern: string | null;
|
|
35
|
+
confidence: number;
|
|
36
|
+
decision: "execute" | "cleave" | "needs_assessment";
|
|
37
|
+
reasoning: string;
|
|
38
|
+
skipInterrogation: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AssessmentFlags {
|
|
42
|
+
robust: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ─── Planning ────────────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
/** Model tier for child execution — maps to pi's --model flag */
|
|
48
|
+
export type ModelTier = "local" | "retribution" | "victory" | "gloriana";
|
|
49
|
+
|
|
50
|
+
export interface ChildPlan {
|
|
51
|
+
label: string;
|
|
52
|
+
description: string;
|
|
53
|
+
scope: string[];
|
|
54
|
+
dependsOn: string[];
|
|
55
|
+
/** Spec domains this child owns (from <!-- specs: ... --> annotation). Empty if none. */
|
|
56
|
+
specDomains: string[];
|
|
57
|
+
/** Skill names matched to this child (from <!-- skills: ... --> annotation or auto-matched from scope). Empty if none. */
|
|
58
|
+
skills: string[];
|
|
59
|
+
/** Resolved execution model tier. Set by model resolution before dispatch. */
|
|
60
|
+
executeModel?: ModelTier;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface SplitPlan {
|
|
64
|
+
children: ChildPlan[];
|
|
65
|
+
rationale: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── Preflight ───────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
export const DEFAULT_VOLATILE_ALLOWLIST: readonly string[] = BUILTIN_VOLATILE_ALLOWLIST;
|
|
71
|
+
|
|
72
|
+
export type PreflightFileClass = "related" | "unrelated" | "unknown" | "volatile";
|
|
73
|
+
|
|
74
|
+
export type ClassificationConfidence = "high" | "medium" | "low";
|
|
75
|
+
|
|
76
|
+
export type PreflightAction =
|
|
77
|
+
| "checkpoint"
|
|
78
|
+
| "stash_unrelated"
|
|
79
|
+
| "stash_volatile"
|
|
80
|
+
| "continue_without_cleave"
|
|
81
|
+
| "cancel";
|
|
82
|
+
|
|
83
|
+
export type PreflightDisposition = "proceed" | "continue_without_cleave" | "cancel";
|
|
84
|
+
|
|
85
|
+
export interface PreflightClassifiedFile {
|
|
86
|
+
path: string;
|
|
87
|
+
class: PreflightFileClass;
|
|
88
|
+
confidence: ClassificationConfidence;
|
|
89
|
+
reason: string;
|
|
90
|
+
tracked: boolean;
|
|
91
|
+
untracked: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface PreparedPreflightCheckpoint {
|
|
95
|
+
message: string;
|
|
96
|
+
paths: string[];
|
|
97
|
+
requiresApproval: true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface PreparedPreflightStash {
|
|
101
|
+
label: string;
|
|
102
|
+
paths: string[];
|
|
103
|
+
includeUntracked: boolean;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface PreflightPlan {
|
|
107
|
+
checkpoint: PreparedPreflightCheckpoint | null;
|
|
108
|
+
stashUnrelated: PreparedPreflightStash | null;
|
|
109
|
+
stashVolatile: PreparedPreflightStash | null;
|
|
110
|
+
safeToProceedAfterVolatileOnly: boolean;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface CleavePreflightSummary {
|
|
114
|
+
hasOpenSpecContext: boolean;
|
|
115
|
+
isDirty: boolean;
|
|
116
|
+
files: PreflightClassifiedFile[];
|
|
117
|
+
related: PreflightClassifiedFile[];
|
|
118
|
+
unrelated: PreflightClassifiedFile[];
|
|
119
|
+
unknown: PreflightClassifiedFile[];
|
|
120
|
+
volatile: PreflightClassifiedFile[];
|
|
121
|
+
availableActions: PreflightAction[];
|
|
122
|
+
plan: PreflightPlan;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface CleavePreflightOutcome {
|
|
126
|
+
action: PreflightAction;
|
|
127
|
+
disposition: PreflightDisposition;
|
|
128
|
+
checkpointApproved: boolean;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ─── Execution ───────────────────────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
export type ChildStatus =
|
|
134
|
+
| "pending"
|
|
135
|
+
| "running"
|
|
136
|
+
| "completed"
|
|
137
|
+
| "failed"
|
|
138
|
+
| "needs_decomposition";
|
|
139
|
+
|
|
140
|
+
export interface ChildState {
|
|
141
|
+
childId: number;
|
|
142
|
+
label: string;
|
|
143
|
+
dependsOn: string[];
|
|
144
|
+
status: ChildStatus;
|
|
145
|
+
branch: string;
|
|
146
|
+
worktreePath?: string;
|
|
147
|
+
startedAt?: string;
|
|
148
|
+
completedAt?: string;
|
|
149
|
+
error?: string;
|
|
150
|
+
/** Duration in seconds */
|
|
151
|
+
durationSec?: number;
|
|
152
|
+
/** "local" | "cloud" — which execution backend was used */
|
|
153
|
+
backend?: "local" | "cloud";
|
|
154
|
+
/** Resolved execution model tier for this child */
|
|
155
|
+
executeModel?: string;
|
|
156
|
+
/** Number of review iterations completed (0 = no review, 1+ = reviewed) */
|
|
157
|
+
reviewIterations?: number;
|
|
158
|
+
/** Review history: verdict + issues per round */
|
|
159
|
+
reviewHistory?: Array<{
|
|
160
|
+
round: number;
|
|
161
|
+
status: string;
|
|
162
|
+
issueCount: number;
|
|
163
|
+
reappeared: string[];
|
|
164
|
+
}>;
|
|
165
|
+
/** Final review decision */
|
|
166
|
+
reviewDecision?: "accepted" | "escalated" | "no_review";
|
|
167
|
+
/** Escalation reason if review loop failed */
|
|
168
|
+
reviewEscalationReason?: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export type CleavePhase =
|
|
172
|
+
| "assess"
|
|
173
|
+
| "plan"
|
|
174
|
+
| "confirm"
|
|
175
|
+
| "dispatch"
|
|
176
|
+
| "harvest"
|
|
177
|
+
| "reunify"
|
|
178
|
+
| "report"
|
|
179
|
+
| "complete"
|
|
180
|
+
| "failed";
|
|
181
|
+
|
|
182
|
+
export interface CleaveState {
|
|
183
|
+
runId: string;
|
|
184
|
+
phase: CleavePhase;
|
|
185
|
+
directive: string;
|
|
186
|
+
repoPath: string;
|
|
187
|
+
baseBranch: string;
|
|
188
|
+
assessment: AssessmentResult | null;
|
|
189
|
+
plan: SplitPlan | null;
|
|
190
|
+
children: ChildState[];
|
|
191
|
+
workspacePath: string;
|
|
192
|
+
/** Total wall-clock duration in seconds */
|
|
193
|
+
totalDurationSec: number;
|
|
194
|
+
createdAt: string;
|
|
195
|
+
completedAt?: string;
|
|
196
|
+
error?: string;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ─── Conflicts ───────────────────────────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
export type ConflictType =
|
|
202
|
+
| "file_overlap"
|
|
203
|
+
| "decision_contradiction"
|
|
204
|
+
| "interface_mismatch"
|
|
205
|
+
| "assumption_violation";
|
|
206
|
+
|
|
207
|
+
export type ConflictResolution =
|
|
208
|
+
| "3way_merge"
|
|
209
|
+
| "escalate_to_parent"
|
|
210
|
+
| "adapter_required"
|
|
211
|
+
| "verify_with_parent";
|
|
212
|
+
|
|
213
|
+
export interface Conflict {
|
|
214
|
+
type: ConflictType;
|
|
215
|
+
description: string;
|
|
216
|
+
involved: number[];
|
|
217
|
+
resolution: ConflictResolution;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export interface TaskResult {
|
|
221
|
+
path: string;
|
|
222
|
+
status: "SUCCESS" | "PARTIAL" | "FAILED" | "PENDING" | "NOT_FOUND";
|
|
223
|
+
summary: string | null;
|
|
224
|
+
fileClaims: string[];
|
|
225
|
+
interfacesPublished: string[];
|
|
226
|
+
decisions: string[];
|
|
227
|
+
assumptions: string[];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface ReunificationResult {
|
|
231
|
+
tasksFound: number;
|
|
232
|
+
rollupStatus: "SUCCESS" | "PARTIAL" | "FAILED" | "PENDING";
|
|
233
|
+
conflicts: Conflict[];
|
|
234
|
+
files: string[];
|
|
235
|
+
interfaces: string[];
|
|
236
|
+
decisions: string[];
|
|
237
|
+
readyToClose: boolean;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ─── Config ──────────────────────────────────────────────────────────────────
|
|
241
|
+
|
|
242
|
+
export interface CleaveConfig {
|
|
243
|
+
/** Complexity threshold — above this, the directive gets cleaved */
|
|
244
|
+
threshold: number;
|
|
245
|
+
/** Maximum recursion depth */
|
|
246
|
+
maxDepth: number;
|
|
247
|
+
/** Maximum parallel children */
|
|
248
|
+
maxParallel: number;
|
|
249
|
+
/** Use local model for leaf tasks when possible */
|
|
250
|
+
preferLocal: boolean;
|
|
251
|
+
/** Success criteria for the directive */
|
|
252
|
+
successCriteria: string[];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export const DEFAULT_CONFIG: CleaveConfig = {
|
|
256
|
+
threshold: 2.0,
|
|
257
|
+
maxDepth: 3,
|
|
258
|
+
maxParallel: 4,
|
|
259
|
+
preferLocal: true,
|
|
260
|
+
successCriteria: [],
|
|
261
|
+
};
|