chore: update LOGIC_INCONSISTENCIES_BY_PACKAGE.md and implement refreshSkillMetadata method

- Updated the status and impact of the `organize_tabs` tool in LOGIC_INCONSISTENCIES_BY_PACKAGE.md, noting its removal from the default bundle.
- Resolved naming inconsistencies for the `ungroup_tabs` tool in the tab-groups module.
- Implemented the `refreshSkillMetadata` method in SkillManager to update skill metadata from SKILL.md, including validation and error handling.
This commit is contained in:
ropzislaw
2026-02-09 23:02:35 +08:00
parent 9cbfd88455
commit 50705fb27b
4 changed files with 138 additions and 20 deletions

View File

@@ -140,33 +140,41 @@
### 2.3 `organize_tabs` Is a Stub (AI Grouping Disabled)
**Status**: ⚠️ Mitigated | **Tool removed from default bundle**
| Legacy | New |
| ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `aipex/src/mcp-servers/tab-groups.ts``groupTabsByAI()` with full LLM prompt | `new-aipex/packages/browser-runtime/src/tools/tab.ts` → returns `{ success: false, message: "...requires additional implementation..." }` |
**Impact**: Core feature (smart tab grouping) non-functional.
**Impact**: ~~Core feature (smart tab grouping) non-functional.~~ Tool is no longer exposed to users.
**Priority**: P0
**Priority**: N/A — Mitigated
**Migration target**: `packages/browser-runtime`
**Migration target**: N/A (tool removed from `allBrowserTools`)
**Resolution**: The `organize_tabs` tool has been removed from `allBrowserTools` in `packages/browser-runtime/src/tools/index.ts`. The implementation code is retained for future completion of AI-powered tab grouping. The tool is listed in the "Disabled tools" comment block.
---
### 2.4 Tab-Group Tool Naming Inconsistency
**Status**: ✅ Resolved
| Tool | Legacy name | New (tab.ts) | New (tab-groups/index.ts) |
| ----------- | -------------- | -------------- | ------------------------- |
| Ungroup all | `ungroup_tabs` | `ungroup_tabs` | `ungroup_all_tabs` |
| Ungroup all | `ungroup_tabs` | `ungroup_tabs` | `ungroup_tabs` |
**Impact**: Skill scripts / prompts referencing old names may break.
**Impact**: ~~Skill scripts / prompts referencing old names may break.~~ Resolved.
**Priority**: P1
**Priority**: N/A — Resolved
**Migration target**: Consolidate naming in `packages/browser-runtime`
**Migration target**: N/A
**Resolution**: The `ungroupAllTabsTool` in `packages/browser-runtime/src/tools/tools/tab-groups/index.ts` has been renamed from `ungroup_all_tabs` to `ungroup_tabs` for consistency with the legacy naming convention. A comment has been added warning against registering both tools simultaneously to avoid duplicate name conflicts.
---
@@ -222,17 +230,21 @@
### 2.8 Skill System: `refreshSkillMetadata()` Missing
**Status**: ✅ Resolved
| Legacy | New |
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `aipex/src/skill/lib/services/skill-manager.ts` has `refreshSkillMetadata()` | Not present in `new-aipex/packages/browser-runtime/src/skill/lib/services/skill-manager.ts` |
| `aipex/src/skill/lib/services/skill-manager.ts` has `refreshSkillMetadata()` | `new-aipex/packages/browser-runtime/src/skill/lib/services/skill-manager.ts` now has `refreshSkillMetadata()` |
**Impact**: Skill metadata may become stale after updates.
**Impact**: ~~Skill metadata may become stale after updates.~~ Resolved.
**Priority**: P2
**Priority**: N/A — Resolved
**Migration target**: `packages/browser-runtime`
**Migration target**: N/A
**Resolution**: The `refreshSkillMetadata(skillId: string)` method has been ported to the new `SkillManager`. It reads `SKILL.md` from ZenFS, parses frontmatter, updates IndexedDB metadata via `skillStorage.updateSkill()`, refreshes the registry cache via `skillRegistry.updateSkill()`, and emits a `skill_loaded` event with type `skill_metadata_refreshed`. Path traversal is guarded by rejecting skill IDs containing `/`, `\\`, or `..`.
---
@@ -321,10 +333,10 @@
| Priority | Items |
| -------- | ---------------------------------------- |
| P0 | 2.1, 2.2, 2.3, 6 |
| P1 | ~~1.1~~, ~~1.2~~ (completed), 2.4, 2.5, 2.7, 3.1, 4.2 (auth) |
| P2 | ~~1.3~~ (superseded), 2.6, 2.8, 4.2 (non-auth) |
| Closed | 1.1 (acceptable difference), 1.2 (resolved), 1.3 (superseded) |
| P0 | 2.1, 2.2, 6 |
| P1 | ~~1.1~~, ~~1.2~~ (completed), ~~2.4~~ (resolved), 2.5, 2.7, 3.1, 4.2 (auth) |
| P2 | ~~1.3~~ (superseded), 2.6, ~~2.8~~ (resolved), 4.2 (non-auth) |
| Closed | 1.1 (acceptable difference), 1.2 (resolved), 1.3 (superseded), 2.3 (mitigated), 2.4 (resolved), 2.8 (resolved) |
---

View File

@@ -450,6 +450,106 @@ export class SkillManager {
}
}
/**
* Refresh skill metadata from SKILL.md file.
* This is called when SKILL.md is edited and saved via the file manager.
* It re-parses the frontmatter and updates both IndexedDB and the registry.
*/
async refreshSkillMetadata(skillId: string): Promise<void> {
if (!this.initialized) {
throw new Error("SkillManager not initialized");
}
// Validate skillId to prevent path traversal
if (
!skillId ||
skillId.includes("/") ||
skillId.includes("\\") ||
skillId.includes("..")
) {
throw new Error(`Invalid skill ID: ${skillId}`);
}
try {
// Get current metadata
const currentMetadata = await skillStorage.getSkillMetadata(skillId);
if (!currentMetadata) {
throw new Error(`Skill not found: ${skillId}`);
}
// Read the SKILL.md content from ZenFS
const skillPath = zenfs.getSkillPath(skillId);
const skillMdPath = `${skillPath}/SKILL.md`;
const skillMdExists = await zenfs.exists(skillMdPath);
if (!skillMdExists) {
throw new Error(`SKILL.md not found for skill: ${skillId}`);
}
const skillMdContent = (await zenfs.readFile(
skillMdPath,
"utf8",
)) as string;
// Parse the frontmatter to extract description and version
const parsedMetadata = skillRegistry.parseSkillMetadata(skillMdContent);
// Check that name hasn't changed (we don't support rename)
if (parsedMetadata.name && parsedMetadata.name !== skillId) {
throw new Error(
`Skill name mismatch: expected "${skillId}" but found "${parsedMetadata.name}" in SKILL.md. Skill renaming is not supported.`,
);
}
// Build updates object (only update fields that are present in frontmatter)
const updates: Partial<SkillMetadata> = {};
if (parsedMetadata.description !== undefined) {
updates.description = parsedMetadata.description;
}
if (parsedMetadata.version !== undefined) {
updates.version = parsedMetadata.version;
}
// Update in IndexedDB if there are changes
if (Object.keys(updates).length > 0) {
await skillStorage.updateSkill(skillId, updates);
}
// Get the updated metadata
const updatedMetadata = await skillStorage.getSkillMetadata(skillId);
if (!updatedMetadata) {
throw new Error(
`Failed to retrieve updated metadata for skill: ${skillId}`,
);
}
// Update the registry with updated metadata and refreshed content
const existingSkill = skillRegistry.getSkill(currentMetadata.name);
if (existingSkill) {
skillRegistry.updateSkill(currentMetadata.name, {
metadata: updatedMetadata,
skillMdContent: skillMdContent,
});
}
console.log(`✅ Skill metadata refreshed: ${skillId}`);
// Emit an event so UI components can react
this._emit("skill_loaded", {
type: "skill_metadata_refreshed",
skillId,
skillName: currentMetadata.name,
skillMetadata: updatedMetadata,
});
} catch (error) {
console.error(
`❌ Failed to refresh skill metadata for ${skillId}:`,
error,
);
throw error;
}
}
getRegisteredTools(): any[] {
return skillExecutor.getRegisteredTools();
}

View File

@@ -24,14 +24,13 @@ import {
getAllTabsTool,
getCurrentTabTool,
getTabInfoTool,
organizeTabsTool,
ungroupTabsTool,
} from "./tab";
import { downloadChatImagesTool, downloadImageTool } from "./tools/downloads";
/**
* All browser tools registered for AI use
* Total: 32 tools (28 core + 4 intervention tools)
* Total: 31 tools (27 core + 4 intervention tools)
*
* Disabled tools (per aipex):
* - switch_to_tab (causes context switching issues)
@@ -40,6 +39,7 @@ import { downloadChatImagesTool, downloadImageTool } from "./tools/downloads";
* - capture_screenshot_to_clipboard (not enabled in aipex)
* - download_text_as_markdown (not enabled in aipex)
* - download_current_chat_images (architecture issue, not enabled in aipex)
* - organize_tabs (stub implementation, temporarily disabled until AI grouping is complete)
*/
type BrowserFunctionTool = FunctionTool<
unknown,
@@ -48,13 +48,13 @@ type BrowserFunctionTool = FunctionTool<
>;
const browserFunctionTools: BrowserFunctionTool[] = [
// Browser/Tab Management (7 tools)
// Browser/Tab Management (6 tools)
// Note: organize_tabs temporarily disabled (stub/not shipped)
getAllTabsTool,
getCurrentTabTool,
createNewTabTool,
getTabInfoTool,
closeTabTool,
organizeTabsTool,
ungroupTabsTool,
// UI Operations (7 tools) - computer tool replaces visual XY tools

View File

@@ -135,8 +135,14 @@ export async function deleteTabGroup(groupId: number): Promise<{
}
}
/**
* Tool to remove all tab groups in the current window.
* Note: This tool uses the name "ungroup_tabs" for consistency with legacy naming.
* Do not register this alongside the default ungroupTabsTool from ./tab.ts to avoid
* duplicate tool name registration.
*/
export const ungroupAllTabsTool = tool({
name: "ungroup_all_tabs",
name: "ungroup_tabs",
description: "Remove all tab groups in the current window",
parameters: z.object({}),
execute: async () => {