pi-monofold 0.7.0 → 0.8.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/focus-preset.ts +5 -1
- package/focus-route-override.ts +44 -0
- package/index.ts +22 -7
- package/package.json +2 -1
package/focus-preset.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type MonofoldRouteType, parseDefaultRouteOverride } from "./focus-route-override.js";
|
|
1
2
|
import { parseFocusSkills, resetFocusSkillsWarningState } from "./focus-skills.js";
|
|
2
3
|
import { assertKnownKeys, asStringArray, isRecord, uniqueStrings } from "./validation.js";
|
|
3
4
|
|
|
@@ -10,13 +11,14 @@ export type FocusPreset = {
|
|
|
10
11
|
label: string;
|
|
11
12
|
targets: FocusPresetTarget[];
|
|
12
13
|
focusSkills?: string[];
|
|
14
|
+
defaultRouteOverride?: MonofoldRouteType;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
export type FocusMatchableWorkspace = {
|
|
16
18
|
tags: string[];
|
|
17
19
|
};
|
|
18
20
|
|
|
19
|
-
const FOCUS_PRESET_KEYS = new Set(["id", "label", "targets", "focusSkills"]);
|
|
21
|
+
const FOCUS_PRESET_KEYS = new Set(["id", "label", "targets", "focusSkills", "defaultRouteOverride"]);
|
|
20
22
|
const FOCUS_PRESET_TARGET_KEYS = new Set(["targetTags"]);
|
|
21
23
|
|
|
22
24
|
/** Parses and validates focus preset configuration from YAML/JSON input. */
|
|
@@ -52,6 +54,7 @@ export function parseFocusPresets(value: unknown, label = "focusPresets"): Focus
|
|
|
52
54
|
targets.push({ targetTags });
|
|
53
55
|
}
|
|
54
56
|
const focusSkills = parseFocusSkills(itemLabel, item.focusSkills);
|
|
57
|
+
const defaultRouteOverride = parseDefaultRouteOverride(itemLabel, item.defaultRouteOverride);
|
|
55
58
|
if (seenIds.has(item.id)) throw new Error(`${label} has duplicate preset id: ${item.id}`);
|
|
56
59
|
seenIds.add(item.id);
|
|
57
60
|
presets.push({
|
|
@@ -59,6 +62,7 @@ export function parseFocusPresets(value: unknown, label = "focusPresets"): Focus
|
|
|
59
62
|
label: item.label,
|
|
60
63
|
targets,
|
|
61
64
|
...(focusSkills !== undefined ? { focusSkills } : {}),
|
|
65
|
+
...(defaultRouteOverride !== undefined ? { defaultRouteOverride } : {}),
|
|
62
66
|
});
|
|
63
67
|
}
|
|
64
68
|
return presets;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Monofold route types available for routed Markdown writes. */
|
|
2
|
+
export const MONOFOLD_ROUTE_TYPES = [
|
|
3
|
+
"default",
|
|
4
|
+
"prd",
|
|
5
|
+
"design",
|
|
6
|
+
"progress",
|
|
7
|
+
"issue",
|
|
8
|
+
"research",
|
|
9
|
+
"decision",
|
|
10
|
+
] as const;
|
|
11
|
+
|
|
12
|
+
export type MonofoldRouteType = (typeof MONOFOLD_ROUTE_TYPES)[number];
|
|
13
|
+
|
|
14
|
+
const ROUTE_TYPE_SET = new Set<string>(MONOFOLD_ROUTE_TYPES);
|
|
15
|
+
|
|
16
|
+
/** Validates a route type string and returns the typed value. */
|
|
17
|
+
export function assertMonofoldRouteType(label: string, value: string): MonofoldRouteType {
|
|
18
|
+
if (!ROUTE_TYPE_SET.has(value)) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`${label} must be one of: ${MONOFOLD_ROUTE_TYPES.join(", ")} (got: ${value})`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return value as MonofoldRouteType;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Parses an optional defaultRouteOverride from preset config. */
|
|
27
|
+
export function parseDefaultRouteOverride(itemLabel: string, value: unknown): MonofoldRouteType | undefined {
|
|
28
|
+
if (value === undefined) return undefined;
|
|
29
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
30
|
+
throw new Error(`${itemLabel}.defaultRouteOverride must be a non-empty string`);
|
|
31
|
+
}
|
|
32
|
+
return assertMonofoldRouteType(`${itemLabel}.defaultRouteOverride`, value.trim());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Resolves the effective write route: explicit selection wins over Focus default, then "default". */
|
|
36
|
+
export function resolveWriteRouteType(
|
|
37
|
+
explicitRoute: string | undefined,
|
|
38
|
+
defaultRouteOverride: MonofoldRouteType | undefined,
|
|
39
|
+
): MonofoldRouteType {
|
|
40
|
+
if (explicitRoute !== undefined && explicitRoute !== "") {
|
|
41
|
+
return assertMonofoldRouteType("routeType", explicitRoute);
|
|
42
|
+
}
|
|
43
|
+
return defaultRouteOverride ?? "default";
|
|
44
|
+
}
|
package/index.ts
CHANGED
|
@@ -20,6 +20,10 @@ import {
|
|
|
20
20
|
setActiveFocusPresetId,
|
|
21
21
|
warnZeroTargetMatchesForPreset,
|
|
22
22
|
} from "./focus-preset.js";
|
|
23
|
+
import {
|
|
24
|
+
resolveWriteRouteType,
|
|
25
|
+
type MonofoldRouteType,
|
|
26
|
+
} from "./focus-route-override.js";
|
|
23
27
|
import {
|
|
24
28
|
applyFocusSkillsToSystemPrompt,
|
|
25
29
|
warnMissingFocusSkills,
|
|
@@ -41,7 +45,7 @@ import { assertKnownKeys, asStringArray, isRecord, uniqueStrings } from "./valid
|
|
|
41
45
|
type CapabilityTag = "read" | "writeDocs" | "editCode" | "runCommands" | "git";
|
|
42
46
|
type LegacyCapabilityTag = CapabilityTag | "gitCommit" | "gitPush";
|
|
43
47
|
type IntentCategory = "Explore" | "Write" | "Config" | "Git";
|
|
44
|
-
type RouteType =
|
|
48
|
+
type RouteType = MonofoldRouteType;
|
|
45
49
|
|
|
46
50
|
type RouteConfig = {
|
|
47
51
|
path: string;
|
|
@@ -892,7 +896,11 @@ function sendCommandError(pi: ExtensionAPI, command: string, error: unknown, usa
|
|
|
892
896
|
}
|
|
893
897
|
|
|
894
898
|
function formatFocusStatus(position: ActiveFocusPresetPosition): string {
|
|
895
|
-
|
|
899
|
+
const base = `focus: ${position.preset.label} (${position.index + 1}/${position.total}) ${FOCUS_CYCLE_SHORTCUT}`;
|
|
900
|
+
if (position.preset.defaultRouteOverride) {
|
|
901
|
+
return `${base} route:${position.preset.defaultRouteOverride}`;
|
|
902
|
+
}
|
|
903
|
+
return base;
|
|
896
904
|
}
|
|
897
905
|
|
|
898
906
|
function updateFocusStatus(ctx: ExtensionContext | ExtensionCommandContext, loaded: LoadedConfig): void {
|
|
@@ -1026,6 +1034,9 @@ async function buildManifest(loaded: LoadedConfig): Promise<string> {
|
|
|
1026
1034
|
const activeTargetIds = new Set(activeWorkspaces.map(({ workspace }) => workspace.targetId));
|
|
1027
1035
|
if (activeWorkspaces.length > 0) {
|
|
1028
1036
|
lines.push(`Active Focus: ${activePreset.label} (${activePreset.id})`);
|
|
1037
|
+
if (activePreset.defaultRouteOverride) {
|
|
1038
|
+
lines.push(`Default write route override: ${activePreset.defaultRouteOverride} (explicit routeType/--route still wins)`);
|
|
1039
|
+
}
|
|
1029
1040
|
for (const { workspace } of activeWorkspaces) {
|
|
1030
1041
|
lines.push(await buildFullWorkspaceManifestEntry(workspace, " (active)"));
|
|
1031
1042
|
}
|
|
@@ -1046,6 +1057,9 @@ async function buildManifest(loaded: LoadedConfig): Promise<string> {
|
|
|
1046
1057
|
async function buildFocusContextInjection(loaded: LoadedConfig, preset: FocusPreset): Promise<FocusContextInjection> {
|
|
1047
1058
|
const activeWorkspaces = getActiveFocusWorkspaces(loaded, preset);
|
|
1048
1059
|
const lines = ["## Focus Context Injection", "", `Active Focus: ${preset.label} (${preset.id})`];
|
|
1060
|
+
if (preset.defaultRouteOverride) {
|
|
1061
|
+
lines.push(`Default write route override: ${preset.defaultRouteOverride} (explicit routeType/--route still wins)`);
|
|
1062
|
+
}
|
|
1049
1063
|
const notices: string[] = [];
|
|
1050
1064
|
const seenFiles = new Set<string>();
|
|
1051
1065
|
let injectedFileCount = 0;
|
|
@@ -1386,7 +1400,7 @@ ${focusInjection.text}` : ""}
|
|
|
1386
1400
|
label: "Workspace Write",
|
|
1387
1401
|
description: "Write a Markdown document to a routed Workspace destination using routeType, title, body, filename, and metadata.",
|
|
1388
1402
|
parameters: Type.Object({
|
|
1389
|
-
routeType: Type.String({ description: "default, prd, design, progress, issue, research, or decision" }),
|
|
1403
|
+
routeType: Type.Optional(Type.String({ description: "default, prd, design, progress, issue, research, or decision; omitted uses active Focus defaultRouteOverride or default" })),
|
|
1390
1404
|
title: Type.String(),
|
|
1391
1405
|
body: Type.String(),
|
|
1392
1406
|
filename: Type.Optional(Type.String()),
|
|
@@ -1397,9 +1411,9 @@ ${focusInjection.text}` : ""}
|
|
|
1397
1411
|
workspaceName: Type.Optional(Type.String()),
|
|
1398
1412
|
}),
|
|
1399
1413
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1400
|
-
const routeType = params.routeType as RouteType;
|
|
1401
|
-
if (!ROUTE_TYPES.includes(routeType)) throw new Error(`Unknown routeType: ${params.routeType}`);
|
|
1402
1414
|
const loaded = await loadConfig(ctx.cwd);
|
|
1415
|
+
const activePreset = getActiveFocusPreset(loaded);
|
|
1416
|
+
const routeType = resolveWriteRouteType(params.routeType, activePreset?.defaultRouteOverride);
|
|
1403
1417
|
const workspace = await resolveWorkspace(ctx, loaded, {
|
|
1404
1418
|
targetTags: params.targetTags,
|
|
1405
1419
|
targetName: params.targetName,
|
|
@@ -1613,14 +1627,15 @@ ${focusInjection.text}` : ""}
|
|
|
1613
1627
|
const writeCommand = async (args: string, ctx: ExtensionCommandContext) => {
|
|
1614
1628
|
try {
|
|
1615
1629
|
const parsed = parseCommandArgs(args);
|
|
1616
|
-
const routeType = (stringFlag(parsed.flags, "route", "r") ?? parsed.positional[0] ?? "default") as RouteType;
|
|
1617
|
-
if (!ROUTE_TYPES.includes(routeType)) throw new Error(`Unknown routeType: ${routeType}`);
|
|
1618
1630
|
const title = stringFlag(parsed.flags, "title", "t");
|
|
1619
1631
|
const body = stringFlag(parsed.flags, "body", "b");
|
|
1620
1632
|
if (!title) throw new Error("--title is required");
|
|
1621
1633
|
if (!body) throw new Error("--body is required");
|
|
1622
1634
|
|
|
1623
1635
|
const loaded = await loadConfig(ctx.cwd);
|
|
1636
|
+
const activePreset = getActiveFocusPreset(loaded);
|
|
1637
|
+
const explicitRoute = stringFlag(parsed.flags, "route", "r") ?? parsed.positional[0];
|
|
1638
|
+
const routeType = resolveWriteRouteType(explicitRoute, activePreset?.defaultRouteOverride);
|
|
1624
1639
|
const workspace = await resolveWorkspace(ctx, loaded, commandTarget(parsed.flags, ["writeDocs"]));
|
|
1625
1640
|
const route = workspace.normalizedRoutes[routeType] ?? workspace.normalizedRoutes.default;
|
|
1626
1641
|
if (!route) throw new Error(`Workspace has no route for ${routeType} and no default route`);
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"description": "Pi extension that folds multiple repositories and folders into a guarded virtual monorepo for AI agents.",
|
|
13
13
|
"type": "module",
|
|
14
|
-
"version": "0.
|
|
14
|
+
"version": "0.8.0",
|
|
15
15
|
"pi": {
|
|
16
16
|
"extensions": [
|
|
17
17
|
"./index.ts"
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"unknown-path-allows.ts",
|
|
26
26
|
"file-read-preview.ts",
|
|
27
27
|
"focus-preset.ts",
|
|
28
|
+
"focus-route-override.ts",
|
|
28
29
|
"focus-skills.ts",
|
|
29
30
|
"monofold-read-ops.ts",
|
|
30
31
|
"read-caps.ts",
|