pi-monofold 0.6.0 → 0.6.2
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 +1 -1
- package/focus-preset.ts +28 -0
- package/index.ts +14 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ Example command flows: [docs/examples.md](./docs/examples.md).
|
|
|
106
106
|
|
|
107
107
|
Default focus shortcut: `ctrl+shift+m` cycles Active Focus forward through `focusPresets` YAML order. No backward focus shortcut ships in the MVP.
|
|
108
108
|
|
|
109
|
-
When Active Focus is set, Pi Monofold injects the active preset's `contextFiles` into each agent turn under **Focus Context Injection** and recomposes the manifest so active Workspace Targets are shown first while non-active targets are collapsed to one-line summaries. The MVP uses provisional context-injection caps that are intentionally temporary and exposed as constants for future tuning:
|
|
109
|
+
When Active Focus is set, Pi Monofold injects the active preset's `contextFiles` into each agent turn under **Focus Context Injection** and recomposes the manifest so active Workspace Targets are shown first while non-active targets are collapsed to one-line summaries. Tag-based target inference in `monofold_read`, `monofold_write`, and `monofold_git` also prefers Workspace Targets that belong to the active preset when a tag query would otherwise match multiple candidates; explicit `targetId` / workspace name selectors and uniquely matching targets are unchanged. If multiple in-focus targets still tie, the existing workspace selection flow applies. The MVP uses provisional context-injection caps that are intentionally temporary and exposed as constants for future tuning:
|
|
110
110
|
|
|
111
111
|
- Max **6** context files per active preset.
|
|
112
112
|
- Max **6,000** characters per file, with `… [truncated]` appended when a file is cut.
|
package/focus-preset.ts
CHANGED
|
@@ -184,3 +184,31 @@ export function resetActiveFocusSessionState(): void {
|
|
|
184
184
|
activeFocusPresetId = null;
|
|
185
185
|
activeFocusInitialized = false;
|
|
186
186
|
}
|
|
187
|
+
|
|
188
|
+
export type TagBasedTargetInput = {
|
|
189
|
+
targetTags?: string[];
|
|
190
|
+
targetId?: string;
|
|
191
|
+
targetName?: string;
|
|
192
|
+
workspaceName?: string;
|
|
193
|
+
workspaceIndex?: number;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/** Returns true when workspace resolution relies on tag query without an explicit selector. */
|
|
197
|
+
export function isTagBasedTargetInference(target: TagBasedTargetInput): boolean {
|
|
198
|
+
if (!target.targetTags?.length) return false;
|
|
199
|
+
if (target.targetId) return false;
|
|
200
|
+
const targetName = target.targetName ?? target.workspaceName;
|
|
201
|
+
if (targetName) return false;
|
|
202
|
+
if (target.workspaceIndex !== undefined) return false;
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Narrows ambiguous tag matches to active-focus targets when at least one in-focus candidate exists. */
|
|
207
|
+
export function biasMatchesTowardActiveFocus<T extends { targetId: string }>(
|
|
208
|
+
matches: T[],
|
|
209
|
+
activeTargetIds: ReadonlySet<string>,
|
|
210
|
+
): T[] {
|
|
211
|
+
if (matches.length <= 1 || activeTargetIds.size === 0) return matches;
|
|
212
|
+
const inFocus = matches.filter((match) => activeTargetIds.has(match.targetId));
|
|
213
|
+
return inFocus.length > 0 ? inFocus : matches;
|
|
214
|
+
}
|
package/index.ts
CHANGED
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
findFocusPresetById,
|
|
13
13
|
getActiveFocusPresetId,
|
|
14
14
|
getActiveFocusPresetPosition,
|
|
15
|
+
biasMatchesTowardActiveFocus,
|
|
16
|
+
isTagBasedTargetInference,
|
|
15
17
|
matchesFocusTarget,
|
|
16
18
|
parseFocusPresets,
|
|
17
19
|
setActiveFocusPresetByLabel,
|
|
@@ -917,9 +919,20 @@ function notifyNoFocusPresets(ctx: ExtensionContext | ExtensionCommandContext, p
|
|
|
917
919
|
if (pi) sendCommandOutput(pi, "monofold:focus", message, { focusPresets: 0 });
|
|
918
920
|
}
|
|
919
921
|
|
|
922
|
+
function biasWorkspaceMatchesWithActiveFocus(loaded: LoadedConfig, matches: ResolvedWorkspace[]): ResolvedWorkspace[] {
|
|
923
|
+
const activePreset = getActiveFocusPreset(loaded);
|
|
924
|
+
if (!activePreset) return matches;
|
|
925
|
+
const activeTargetIds = new Set(getActiveFocusWorkspaces(loaded, activePreset).map(({ workspace }) => workspace.targetId));
|
|
926
|
+
return biasMatchesTowardActiveFocus(matches, activeTargetIds);
|
|
927
|
+
}
|
|
928
|
+
|
|
920
929
|
async function resolveWorkspace(ctx: ExtensionContext | ExtensionCommandContext, loaded: LoadedConfig, target: TargetInput): Promise<ResolvedWorkspace> {
|
|
921
|
-
|
|
930
|
+
ensureActiveFocusInitialized(loaded.raw.focusPresets);
|
|
931
|
+
let matches = loaded.workspaces.filter((workspace) => matchesTarget(workspace, target));
|
|
922
932
|
if (matches.length === 0) throw new Error(`No workspace matches target: ${JSON.stringify(target)}`);
|
|
933
|
+
if (matches.length > 1 && isTagBasedTargetInference(target)) {
|
|
934
|
+
matches = biasWorkspaceMatchesWithActiveFocus(loaded, matches);
|
|
935
|
+
}
|
|
923
936
|
if (matches.length === 1) return matches[0];
|
|
924
937
|
if (!ctx.hasUI) {
|
|
925
938
|
throw new Error(`Multiple workspaces match target in non-interactive mode: ${matches.map(formatWorkspaceLabel).join(", ")}`);
|
package/package.json
CHANGED