Enhance SkillsGrid and Manifest Handling

- Added `ignoreInProduction` property to `SkillListEntry` and updated processing logic to filter skills based on the `IS_DEV` flag.
- Updated Rust structures in the Tauri backend to include `ignoreInProduction` in the skill manifest.
- Refactored desktop deep link listener to remove unnecessary `IS_DEV` checks.
- Improved JSON serialization handling in QuickJS skill instance.
This commit is contained in:
Steven Enamakel
2026-02-09 09:07:51 +05:30
parent 63625ae08e
commit af15c7c8e1
5 changed files with 42 additions and 31 deletions
+1
View File
@@ -64,6 +64,7 @@ mod desktop {
"entry": m.entry,
"autoStart": m.auto_start,
"version": m.version,
"ignoreInProduction": m.ignoreInProduction,
"description": m.description,
"setup": m.setup.as_ref().map(|s| {
let mut obj = serde_json::json!({
+3
View File
@@ -37,6 +37,9 @@ pub struct SkillManifest {
pub auto_start: bool,
/// Version string (informational).
pub version: Option<String>,
/// Whether to ignore in production.
#[serde(default)]
pub ignoreInProduction: bool,
/// Description (informational).
pub description: Option<String>,
/// Setup configuration (optional).
+1 -1
View File
@@ -672,7 +672,7 @@ async fn handle_js_call(
if (typeof fn === 'function') {{
var args = {args_json};
var result = fn.call(skill, args);
return JSON.stringify(result);
return JSON.stringify(result === undefined ? null : result);
}}
return "null";
}})()"#
+34 -27
View File
@@ -9,6 +9,7 @@ import { useSkillConnectionStatus } from '../lib/skills/hooks';
import { skillManager } from '../lib/skills/manager';
import type { SkillConnectionStatus, SkillHostConnectionState } from '../lib/skills/types';
import { useAppSelector } from '../store/hooks';
import { IS_DEV } from '../utils/config';
import SkillSetupModal from './skills/SkillSetupModal';
// Map skill IDs to icons
@@ -109,6 +110,7 @@ interface SkillListEntry {
id: string;
name: string;
description: string;
ignoreInProduction?: boolean;
icon?: React.ReactElement;
hasSetup: boolean;
}
@@ -322,18 +324,21 @@ export default function SkillsGrid() {
return true;
});
const processed: SkillListEntry[] = validManifests.map(m => {
const setup = m.setup as Record<string, unknown> | undefined;
return {
id: m.id as string,
name:
(m.name as string) ||
(m.id as string).charAt(0).toUpperCase() + (m.id as string).slice(1),
description: (m.description as string) || '',
icon: SKILL_ICONS[m.id as string],
hasSetup: !!(setup && setup.required),
};
});
const processed: SkillListEntry[] = validManifests
.map(m => {
const setup = m.setup as Record<string, unknown> | undefined;
return {
id: m.id as string,
name:
(m.name as string) ||
(m.id as string).charAt(0).toUpperCase() + (m.id as string).slice(1),
description: (m.description as string) || '',
icon: SKILL_ICONS[m.id as string],
ignoreInProduction: (m.ignoreInProduction as boolean) ?? false,
hasSetup: !!(setup && setup.required),
};
})
.filter(s => IS_DEV || !s.ignoreInProduction);
setSkillsList(processed);
setLoading(false);
@@ -348,25 +353,27 @@ export default function SkillsGrid() {
// Sort skills by connection status (connected first)
const sortedSkillsList = useMemo(() => {
return [...skillsList].sort((a, b) => {
const skillA = skillsState[a.id];
const skillB = skillsState[b.id];
const stateA = skillStates[a.id];
const stateB = skillStates[b.id];
return [...skillsList]
.sort((a, b) => {
const skillA = skillsState[a.id];
const skillB = skillsState[b.id];
const stateA = skillStates[a.id];
const stateB = skillStates[b.id];
const statusA = deriveConnectionStatus(skillA?.status, skillA?.setupComplete, stateA);
const statusB = deriveConnectionStatus(skillB?.status, skillB?.setupComplete, stateB);
const statusA = deriveConnectionStatus(skillA?.status, skillA?.setupComplete, stateA);
const statusB = deriveConnectionStatus(skillB?.status, skillB?.setupComplete, stateB);
const priorityA = STATUS_PRIORITY[statusA] ?? 999;
const priorityB = STATUS_PRIORITY[statusB] ?? 999;
const priorityA = STATUS_PRIORITY[statusA] ?? 999;
const priorityB = STATUS_PRIORITY[statusB] ?? 999;
// If same priority, sort alphabetically by name
if (priorityA === priorityB) {
return a.name.localeCompare(b.name);
}
// If same priority, sort alphabetically by name
if (priorityA === priorityB) {
return a.name.localeCompare(b.name);
}
return priorityA - priorityB;
});
return priorityA - priorityB;
})
.filter(s => IS_DEV || !s.ignoreInProduction);
}, [skillsList, skillsState, skillStates]);
// Show mobile-only message on mobile platforms
+3 -3
View File
@@ -5,7 +5,6 @@ import { skillManager } from '../lib/skills/manager';
import { consumeLoginToken } from '../services/api/authApi';
import { store } from '../store';
import { setToken } from '../store/authSlice';
import { IS_DEV } from './config';
/**
* Handle an `alphahuman://auth?token=...` deep link for login.
@@ -126,8 +125,9 @@ export const setupDesktopDeepLinkListener = async () => {
void handleDeepLinkUrls(urls);
});
if (IS_DEV && typeof window !== 'undefined') {
// window.__simulateDeepLink('alphahuman//auth?token=1234567890')
if (typeof window !== 'undefined') {
// window.__simulateDeepLink('alphahuman://auth?token=1234567890')
// window.__simulateDeepLink('alphahuman://oauth/success?integrationId=6989178fad6dbfe9b137f577&skillId=notion')
(
window as Window & { __simulateDeepLink?: (url: string) => Promise<void> }
).__simulateDeepLink = (url: string) => handleDeepLinkUrls([url]);