feat(skills): add admin operations, airbase omni, knowledge, grafana, zulip, and scheme editor skills

This commit is contained in:
2026-10-30 07:45:13 +03:00
parent ed8cbc582b
commit c75bb03b24
26 changed files with 393 additions and 65 deletions
@@ -131,9 +131,15 @@
return Boolean(action && ((Array.isArray(action.targets) && action.targets.length) || action.target));
}
function matchesCondition(action) {
if (!object(action && action.when)) return true;
if (!state.selected || typeof action.when.path !== 'string') return false;
return readPath(state.selected, action.when.path) === action.when.equals;
}
function actionList() {
return Array.isArray(state.widget.actions) ? state.widget.actions.filter(function (action) {
return action && action.tool;
return action && action.tool && matchesCondition(action);
}) : [];
}
@@ -245,20 +251,22 @@
function renderForm() {
ensurePanel();
var actions = actionList();
if (state.action && !actions.some(function (action) { return action.id === state.action.id; })) state.action = null;
var selectedActionId = state.action ? state.action.id : '';
elements.select.replaceChildren();
var placeholder = document.createElement('option');
placeholder.value = '';
placeholder.textContent = t('chooseAction');
elements.select.append(placeholder);
actionList().forEach(function (action) {
actions.forEach(function (action) {
var option = document.createElement('option');
option.value = action.id;
option.textContent = actionLabel(action);
elements.select.append(option);
});
elements.select.value = selectedActionId;
elements.select.hidden = actionList().length === 0;
elements.select.hidden = actions.length === 0;
elements.form.replaceChildren();
elements.status.textContent = '';
elements.submit.disabled = state.busy;
@@ -267,7 +275,7 @@
elements.selection.textContent = state.selected ? text(state.selected.title || state.selected.name || state.selected.id || '') : '';
elements.submit.hidden = true;
elements.cancel.hidden = true;
if (actionList().length && !state.selected && actionList().every(hasTargets)) {
if (actions.length && !state.selected && actions.every(hasTargets)) {
elements.form.append(make('div', t('selectRecord'), 'shd-widget-actions-hint'));
}
return;
@@ -402,13 +410,17 @@
var records = recordsFrom(payload);
if (records.length || Array.isArray(payload.data) || Array.isArray(payload.items) || Array.isArray(payload.projects)) {
state.records = records;
if (state.selected && !state.records.some(function (item) {
if (state.selected) {
var selectedId = text(state.selected.id || state.selected.code || state.selected.key || state.selected.number).toLowerCase();
var itemId = text(item.id || item.code || item.key || item.number).toLowerCase();
return selectedId && itemId && selectedId === itemId;
})) {
state.selected = null;
state.action = null;
var selectedRecord = selectedId ? state.records.find(function (item) {
var itemId = text(item.id || item.code || item.key || item.number).toLowerCase();
return itemId && selectedId === itemId;
}) : null;
if (selectedRecord) state.selected = selectedRecord;
else {
state.selected = null;
state.action = null;
}
}
}
ensurePanel();
@@ -1390,12 +1390,16 @@ progress::-moz-progress-bar {
<main class="app" aria-live="polite" data-shd-widget-engine="shd-universal-v1" data-widget-module="active-projects">
<header class="header">
<div>
<h1 data-i18n="title">Активные проекты</h1>
<h1 id="title" data-i18n="title">Активные проекты</h1>
<p class="subtitle" data-i18n="subtitle">Сроки завершения и текущие статусы</p>
</div>
<button class="button" id="refresh" type="button" data-i18n="refresh">Обновить</button>
</header>
<div class="toolbar">
<select id="project-filter" aria-label="Список проектов">
<option value="active" data-i18n="activeFilter">Активные</option>
<option value="archived" data-i18n="archivedFilter">Архивные</option>
</select>
<input class="search" id="search" type="search" autocomplete="off" data-i18n-placeholder="search" placeholder="Поиск по проекту или коду">
</div>
<div class="summary" id="summary"></div>
@@ -1428,8 +1432,12 @@ progress::-moz-progress-bar {
var messages = {
ru: {
title: 'Активные проекты',
archivedTitle: 'Архивные проекты',
subtitle: 'Сроки завершения и текущие статусы',
refresh: 'Обновить',
activeFilter: 'Активные',
archivedFilter: 'Архивные',
projectFilter: 'Список проектов',
search: 'Поиск по проекту или коду',
project: 'Проект',
deadline: 'Дата завершения',
@@ -1452,8 +1460,12 @@ progress::-moz-progress-bar {
},
en: {
title: 'Active projects',
archivedTitle: 'Archived projects',
subtitle: 'Completion dates and current statuses',
refresh: 'Refresh',
activeFilter: 'Active',
archivedFilter: 'Archived',
projectFilter: 'Project list',
search: 'Search by project or code',
project: 'Project',
deadline: 'Completion date',
@@ -1490,9 +1502,12 @@ progress::-moz-progress-bar {
var nextRequestId = 1;
var mcpAppsProtocolVersion = '2026-01-26';
var busy = false;
var archivedFilter = false;
var elements = {
title: document.getElementById('title'),
refresh: document.getElementById('refresh'),
projectFilter: document.getElementById('project-filter'),
search: document.getElementById('search'),
summary: document.getElementById('summary'),
rows: document.getElementById('rows'),
@@ -1516,6 +1531,7 @@ progress::-moz-progress-bar {
if (t[key]) node.setAttribute('placeholder', t[key]);
});
document.querySelector('.table').setAttribute('aria-label', t.project);
elements.projectFilter.setAttribute('aria-label', t.projectFilter);
}
function request(method, params) {
@@ -1669,7 +1685,7 @@ progress::-moz-progress-bar {
projects.forEach(function (project) {
var row = makeElement('button', '', 'project-row');
row.type = 'button';
row.dataset.shdRecordId = getProjectCode(project);
row.dataset.shdRecordId = String(project.id || project.code || project.key || project.number || getProjectCode(project));
row.dataset.shdRecordTitle = getProjectName(project);
row.setAttribute('aria-label', getProjectName(project));
@@ -1701,6 +1717,7 @@ progress::-moz-progress-bar {
function setBusy(nextBusy, label) {
busy = nextBusy;
elements.refresh.disabled = nextBusy;
elements.projectFilter.disabled = nextBusy;
if (nextBusy) elements.refresh.textContent = label || t.loading;
else elements.refresh.textContent = t.refresh;
}
@@ -1722,7 +1739,7 @@ progress::-moz-progress-bar {
setBusy(true);
elements.error.hidden = true;
try {
var result = await request('tools/call', { name: 'shd_list_projects', arguments: { archived: false, limit: 100 } });
var result = await request('tools/call', { name: 'shd_list_projects', arguments: { archived: archivedFilter, limit: 100 } });
render(unwrapResult(result));
} catch (error) {
setError(error && error.message ? error.message : t.refreshError);
@@ -1789,6 +1806,13 @@ progress::-moz-progress-bar {
elements.search.addEventListener('input', renderRows);
elements.refresh.addEventListener('click', refresh);
elements.projectFilter.addEventListener('change', function () {
archivedFilter = elements.projectFilter.value === 'archived';
elements.title.textContent = archivedFilter ? t.archivedTitle : t.title;
elements.search.value = '';
elements.details.hidden = true;
void refresh();
});
elements.closeDetails.addEventListener('click', function () { elements.details.hidden = true; });
applyTranslations();
void initializeHost();