memorix 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/dist/cli/index.js +197 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/static/app.js +93 -10
- package/dist/dashboard/static/style.css +150 -0
- package/dist/index.js +182 -21
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -42,6 +42,15 @@ const i18n = {
|
|
|
42
42
|
noObsTitle: 'No Observations',
|
|
43
43
|
noObsDesc: 'Use memorix_store to create observations',
|
|
44
44
|
untitled: 'Untitled',
|
|
45
|
+
exportData: 'Export',
|
|
46
|
+
deleteObs: 'Delete',
|
|
47
|
+
deleteConfirm: 'Delete observation #%id%?',
|
|
48
|
+
deleted: 'Deleted',
|
|
49
|
+
narrative: 'Narrative',
|
|
50
|
+
facts: 'Facts',
|
|
51
|
+
concepts: 'Concepts',
|
|
52
|
+
files: 'Files Modified',
|
|
53
|
+
clickToExpand: 'Click to expand',
|
|
45
54
|
|
|
46
55
|
// Retention
|
|
47
56
|
memoryRetention: 'Memory Retention',
|
|
@@ -102,6 +111,15 @@ const i18n = {
|
|
|
102
111
|
noObsTitle: '暂无观察记录',
|
|
103
112
|
noObsDesc: '使用 memorix_store 创建观察记录',
|
|
104
113
|
untitled: '无标题',
|
|
114
|
+
exportData: '导出',
|
|
115
|
+
deleteObs: '删除',
|
|
116
|
+
deleteConfirm: '确认删除观察 #%id%?',
|
|
117
|
+
deleted: '已删除',
|
|
118
|
+
narrative: '叙述',
|
|
119
|
+
facts: '事实',
|
|
120
|
+
concepts: '概念',
|
|
121
|
+
files: '相关文件',
|
|
122
|
+
clickToExpand: '点击展开',
|
|
105
123
|
|
|
106
124
|
// Retention
|
|
107
125
|
memoryRetention: '记忆衰减',
|
|
@@ -760,9 +778,15 @@ async function loadObservations() {
|
|
|
760
778
|
const types = [...new Set(allObservations.map(o => o.type).filter(Boolean))];
|
|
761
779
|
|
|
762
780
|
container.innerHTML = `
|
|
763
|
-
<div class="page-header">
|
|
764
|
-
<
|
|
765
|
-
|
|
781
|
+
<div class="page-header" style="display:flex;align-items:center;justify-content:space-between;">
|
|
782
|
+
<div>
|
|
783
|
+
<h1 class="page-title">${t('observations')}</h1>
|
|
784
|
+
<p class="page-subtitle">${allObservations.length} ${t('observationsStored')}</p>
|
|
785
|
+
</div>
|
|
786
|
+
<button class="export-btn" id="btn-export" title="${t('exportData')}">
|
|
787
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M8 2v8M4 7l4 4 4-4M2 12v2h12v-2"/></svg>
|
|
788
|
+
${t('exportData')}
|
|
789
|
+
</button>
|
|
766
790
|
</div>
|
|
767
791
|
|
|
768
792
|
<div class="search-bar">
|
|
@@ -774,6 +798,12 @@ async function loadObservations() {
|
|
|
774
798
|
<div class="obs-grid" id="obs-list"></div>
|
|
775
799
|
`;
|
|
776
800
|
|
|
801
|
+
// Export handler
|
|
802
|
+
document.getElementById('btn-export').addEventListener('click', () => {
|
|
803
|
+
const sep = selectedProject ? `?project=${encodeURIComponent(selectedProject)}` : '';
|
|
804
|
+
window.open(`/api/export${sep}`, '_blank');
|
|
805
|
+
});
|
|
806
|
+
|
|
777
807
|
document.getElementById('obs-search').addEventListener('input', (e) => {
|
|
778
808
|
obsFilter = e.target.value.toLowerCase();
|
|
779
809
|
renderObsList();
|
|
@@ -822,25 +852,32 @@ function renderObsList() {
|
|
|
822
852
|
}
|
|
823
853
|
|
|
824
854
|
list.innerHTML = filtered.map(obs => `
|
|
825
|
-
<div class="obs-card">
|
|
826
|
-
<div class="obs-card-header">
|
|
855
|
+
<div class="obs-card" data-obs-id="${obs.id}">
|
|
856
|
+
<div class="obs-card-header" onclick="toggleObsDetail(${obs.id})">
|
|
827
857
|
<span class="obs-card-id">#${obs.id}</span>
|
|
828
858
|
<span class="type-badge" data-type="${obs.type || 'unknown'}">
|
|
829
859
|
${typeIcons[obs.type] || '❓'} ${obs.type || 'unknown'}
|
|
830
860
|
</span>
|
|
831
861
|
<span class="obs-card-title">${escapeHtml(obs.title || t('untitled'))}</span>
|
|
862
|
+
<span class="obs-expand-icon">▼</span>
|
|
832
863
|
</div>
|
|
833
864
|
<div class="obs-card-meta">
|
|
834
865
|
<span>📁 ${escapeHtml(obs.entityName || 'unknown')}</span>
|
|
835
866
|
${obs.createdAt ? `<span>🕐 ${formatTime(obs.createdAt)}</span>` : ''}
|
|
836
867
|
${obs.accessCount ? `<span>👁 ${obs.accessCount}</span>` : ''}
|
|
837
868
|
</div>
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
869
|
+
<div class="obs-detail" id="obs-detail-${obs.id}" style="display:none;">
|
|
870
|
+
${obs.narrative ? `<div class="obs-detail-section"><label>${t('narrative')}</label><div class="obs-card-narrative">${escapeHtml(obs.narrative)}</div></div>` : ''}
|
|
871
|
+
${obs.facts && obs.facts.length > 0 ? `<div class="obs-detail-section"><label>${t('facts')}</label><div class="obs-card-facts">${obs.facts.map(f => `<span class="fact-tag">${escapeHtml(f)}</span>`).join('')}</div></div>` : ''}
|
|
872
|
+
${obs.concepts && obs.concepts.length > 0 ? `<div class="obs-detail-section"><label>${t('concepts')}</label><div class="obs-card-facts">${obs.concepts.map(c => `<span class="fact-tag concept-tag">${escapeHtml(c)}</span>`).join('')}</div></div>` : ''}
|
|
873
|
+
${obs.filesModified && obs.filesModified.length > 0 ? `<div class="obs-detail-section"><label>${t('files')}</label><div class="obs-card-facts">${obs.filesModified.map(f => `<span class="fact-tag file-tag">${escapeHtml(f)}</span>`).join('')}</div></div>` : ''}
|
|
874
|
+
<div class="obs-detail-actions">
|
|
875
|
+
<button class="delete-btn" onclick="deleteObs(${obs.id}, event)">
|
|
876
|
+
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 4h12M5 4V3a1 1 0 011-1h4a1 1 0 011 1v1M6 7v5M10 7v5M3 4l1 9a1 1 0 001 1h6a1 1 0 001-1l1-9"/></svg>
|
|
877
|
+
${t('deleteObs')}
|
|
878
|
+
</button>
|
|
842
879
|
</div>
|
|
843
|
-
|
|
880
|
+
</div>
|
|
844
881
|
</div>
|
|
845
882
|
`).join('');
|
|
846
883
|
}
|
|
@@ -931,6 +968,52 @@ async function loadRetention() {
|
|
|
931
968
|
`;
|
|
932
969
|
}
|
|
933
970
|
|
|
971
|
+
// ============================================================
|
|
972
|
+
// Observation Interactions
|
|
973
|
+
// ============================================================
|
|
974
|
+
|
|
975
|
+
function toggleObsDetail(id) {
|
|
976
|
+
const detail = document.getElementById(`obs-detail-${id}`);
|
|
977
|
+
const card = detail?.closest('.obs-card');
|
|
978
|
+
if (!detail || !card) return;
|
|
979
|
+
|
|
980
|
+
const isOpen = detail.style.display !== 'none';
|
|
981
|
+
detail.style.display = isOpen ? 'none' : 'block';
|
|
982
|
+
card.classList.toggle('expanded', !isOpen);
|
|
983
|
+
|
|
984
|
+
// Rotate expand icon
|
|
985
|
+
const icon = card.querySelector('.obs-expand-icon');
|
|
986
|
+
if (icon) icon.style.transform = isOpen ? '' : 'rotate(180deg)';
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
async function deleteObs(id, event) {
|
|
990
|
+
event?.stopPropagation();
|
|
991
|
+
const msg = t('deleteConfirm').replace('%id%', id);
|
|
992
|
+
if (!confirm(msg)) return;
|
|
993
|
+
|
|
994
|
+
try {
|
|
995
|
+
const sep = selectedProject ? `?project=${encodeURIComponent(selectedProject)}` : '';
|
|
996
|
+
const res = await fetch(`/api/observations/${id}${sep}`, { method: 'DELETE' });
|
|
997
|
+
const data = await res.json();
|
|
998
|
+
if (data.ok) {
|
|
999
|
+
// Remove from local array and re-render
|
|
1000
|
+
allObservations = allObservations.filter(o => o.id !== id);
|
|
1001
|
+
renderObsList();
|
|
1002
|
+
// Update counter in header
|
|
1003
|
+
const subtitle = document.querySelector('#page-observations .page-subtitle');
|
|
1004
|
+
if (subtitle) subtitle.textContent = `${allObservations.length} ${t('observationsStored')}`;
|
|
1005
|
+
} else {
|
|
1006
|
+
alert(data.error || 'Delete failed');
|
|
1007
|
+
}
|
|
1008
|
+
} catch (err) {
|
|
1009
|
+
alert('Delete failed: ' + err.message);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// Make functions globally accessible for onclick handlers
|
|
1014
|
+
window.toggleObsDetail = toggleObsDetail;
|
|
1015
|
+
window.deleteObs = deleteObs;
|
|
1016
|
+
|
|
934
1017
|
// ============================================================
|
|
935
1018
|
// Utilities
|
|
936
1019
|
// ============================================================
|
|
@@ -1092,4 +1092,154 @@ body {
|
|
|
1092
1092
|
color: var(--text-primary);
|
|
1093
1093
|
font-size: 12px;
|
|
1094
1094
|
padding: 4px 8px;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/* ============================================================
|
|
1098
|
+
* Export Button
|
|
1099
|
+
* ============================================================ */
|
|
1100
|
+
|
|
1101
|
+
.export-btn {
|
|
1102
|
+
display: flex;
|
|
1103
|
+
align-items: center;
|
|
1104
|
+
gap: 6px;
|
|
1105
|
+
padding: 8px 16px;
|
|
1106
|
+
background: var(--bg-card);
|
|
1107
|
+
border: 1px solid var(--border-subtle);
|
|
1108
|
+
border-radius: 8px;
|
|
1109
|
+
color: var(--text-secondary);
|
|
1110
|
+
font-size: 13px;
|
|
1111
|
+
font-family: var(--font-sans);
|
|
1112
|
+
cursor: pointer;
|
|
1113
|
+
transition: all 0.2s;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
.export-btn:hover {
|
|
1117
|
+
background: var(--bg-card-hover);
|
|
1118
|
+
border-color: var(--accent-cyan);
|
|
1119
|
+
color: var(--accent-cyan);
|
|
1120
|
+
box-shadow: var(--glow-cyan);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/* ============================================================
|
|
1124
|
+
* Observation Card Enhancements
|
|
1125
|
+
* ============================================================ */
|
|
1126
|
+
|
|
1127
|
+
.obs-card {
|
|
1128
|
+
cursor: pointer;
|
|
1129
|
+
transition: all 0.25s ease;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
.obs-card:hover {
|
|
1133
|
+
border-color: var(--border-medium);
|
|
1134
|
+
transform: translateY(-1px);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
.obs-card.expanded {
|
|
1138
|
+
border-color: var(--accent-cyan);
|
|
1139
|
+
box-shadow: var(--glow-cyan);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
.obs-card-header {
|
|
1143
|
+
display: flex;
|
|
1144
|
+
align-items: center;
|
|
1145
|
+
gap: 8px;
|
|
1146
|
+
position: relative;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
.obs-expand-icon {
|
|
1150
|
+
margin-left: auto;
|
|
1151
|
+
font-size: 10px;
|
|
1152
|
+
color: var(--text-muted);
|
|
1153
|
+
transition: transform 0.25s ease;
|
|
1154
|
+
flex-shrink: 0;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
.obs-card:hover .obs-expand-icon {
|
|
1158
|
+
color: var(--text-secondary);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/* Observation Detail Section */
|
|
1162
|
+
.obs-detail {
|
|
1163
|
+
margin-top: 12px;
|
|
1164
|
+
padding-top: 12px;
|
|
1165
|
+
border-top: 1px solid var(--border-subtle);
|
|
1166
|
+
animation: slideDown 0.2s ease;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
@keyframes slideDown {
|
|
1170
|
+
from {
|
|
1171
|
+
opacity: 0;
|
|
1172
|
+
transform: translateY(-8px);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
to {
|
|
1176
|
+
opacity: 1;
|
|
1177
|
+
transform: translateY(0);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
.obs-detail-section {
|
|
1182
|
+
margin-bottom: 12px;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
.obs-detail-section label {
|
|
1186
|
+
display: block;
|
|
1187
|
+
font-size: 11px;
|
|
1188
|
+
font-weight: 600;
|
|
1189
|
+
color: var(--text-muted);
|
|
1190
|
+
text-transform: uppercase;
|
|
1191
|
+
letter-spacing: 0.5px;
|
|
1192
|
+
margin-bottom: 6px;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
.obs-detail-section .obs-card-narrative {
|
|
1196
|
+
-webkit-line-clamp: unset;
|
|
1197
|
+
white-space: pre-wrap;
|
|
1198
|
+
max-height: none;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/* Tag variants */
|
|
1202
|
+
.concept-tag {
|
|
1203
|
+
background: rgba(168, 85, 247, 0.1) !important;
|
|
1204
|
+
border-color: rgba(168, 85, 247, 0.2) !important;
|
|
1205
|
+
color: var(--accent-purple) !important;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
.file-tag {
|
|
1209
|
+
background: rgba(59, 130, 246, 0.1) !important;
|
|
1210
|
+
border-color: rgba(59, 130, 246, 0.2) !important;
|
|
1211
|
+
color: var(--accent-blue) !important;
|
|
1212
|
+
font-family: var(--font-mono);
|
|
1213
|
+
font-size: 11px !important;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
/* Delete Button */
|
|
1217
|
+
.obs-detail-actions {
|
|
1218
|
+
display: flex;
|
|
1219
|
+
justify-content: flex-end;
|
|
1220
|
+
margin-top: 12px;
|
|
1221
|
+
padding-top: 8px;
|
|
1222
|
+
border-top: 1px solid var(--border-subtle);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
.delete-btn {
|
|
1226
|
+
display: flex;
|
|
1227
|
+
align-items: center;
|
|
1228
|
+
gap: 5px;
|
|
1229
|
+
padding: 6px 12px;
|
|
1230
|
+
background: transparent;
|
|
1231
|
+
border: 1px solid rgba(239, 68, 68, 0.2);
|
|
1232
|
+
border-radius: 6px;
|
|
1233
|
+
color: var(--accent-red);
|
|
1234
|
+
font-size: 12px;
|
|
1235
|
+
font-family: var(--font-sans);
|
|
1236
|
+
cursor: pointer;
|
|
1237
|
+
transition: all 0.2s;
|
|
1238
|
+
opacity: 0.7;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
.delete-btn:hover {
|
|
1242
|
+
background: rgba(239, 68, 68, 0.1);
|
|
1243
|
+
border-color: var(--accent-red);
|
|
1244
|
+
opacity: 1;
|
|
1095
1245
|
}
|
package/dist/index.js
CHANGED
|
@@ -1261,8 +1261,42 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir) {
|
|
|
1261
1261
|
});
|
|
1262
1262
|
break;
|
|
1263
1263
|
}
|
|
1264
|
-
default:
|
|
1264
|
+
default: {
|
|
1265
|
+
const deleteMatch = apiPath.match(/^\/observations\/(\d+)$/);
|
|
1266
|
+
if (deleteMatch && req.method === "DELETE") {
|
|
1267
|
+
const obsId = parseInt(deleteMatch[1], 10);
|
|
1268
|
+
const allObs = await loadObservationsJson(effectiveDataDir);
|
|
1269
|
+
const idx = allObs.findIndex((o) => o.id === obsId);
|
|
1270
|
+
if (idx === -1) {
|
|
1271
|
+
sendError(res, "Observation not found", 404);
|
|
1272
|
+
} else {
|
|
1273
|
+
allObs.splice(idx, 1);
|
|
1274
|
+
await saveObservationsJson(effectiveDataDir, allObs);
|
|
1275
|
+
sendJson(res, { ok: true, deleted: obsId });
|
|
1276
|
+
}
|
|
1277
|
+
break;
|
|
1278
|
+
}
|
|
1279
|
+
if (apiPath === "/export") {
|
|
1280
|
+
const graph = await loadGraphJsonl(effectiveDataDir);
|
|
1281
|
+
const allObs = await loadObservationsJson(effectiveDataDir);
|
|
1282
|
+
const observations2 = filterByProject(allObs, effectiveProjectId);
|
|
1283
|
+
const nextId2 = await loadIdCounter(effectiveDataDir);
|
|
1284
|
+
const exportData = {
|
|
1285
|
+
project: { id: effectiveProjectId, name: effectiveProjectName },
|
|
1286
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1287
|
+
graph,
|
|
1288
|
+
observations: observations2,
|
|
1289
|
+
nextId: nextId2
|
|
1290
|
+
};
|
|
1291
|
+
res.writeHead(200, {
|
|
1292
|
+
"Content-Type": "application/json",
|
|
1293
|
+
"Content-Disposition": `attachment; filename="memorix-${effectiveProjectId.replace(/\//g, "-")}-export.json"`
|
|
1294
|
+
});
|
|
1295
|
+
res.end(JSON.stringify(exportData, null, 2));
|
|
1296
|
+
break;
|
|
1297
|
+
}
|
|
1265
1298
|
sendError(res, "Not found", 404);
|
|
1299
|
+
}
|
|
1266
1300
|
}
|
|
1267
1301
|
} catch (err) {
|
|
1268
1302
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -2464,6 +2498,82 @@ var CopilotAdapter = class {
|
|
|
2464
2498
|
}
|
|
2465
2499
|
};
|
|
2466
2500
|
|
|
2501
|
+
// src/rules/adapters/kiro.ts
|
|
2502
|
+
init_esm_shims();
|
|
2503
|
+
import matter7 from "gray-matter";
|
|
2504
|
+
var KiroAdapter = class {
|
|
2505
|
+
source = "kiro";
|
|
2506
|
+
filePatterns = [
|
|
2507
|
+
".kiro/steering/*.md",
|
|
2508
|
+
"AGENTS.md"
|
|
2509
|
+
];
|
|
2510
|
+
parse(filePath, content) {
|
|
2511
|
+
if (filePath.includes(".kiro/steering/")) {
|
|
2512
|
+
return this.parseSteeringRule(filePath, content);
|
|
2513
|
+
}
|
|
2514
|
+
if (filePath.endsWith("AGENTS.md")) {
|
|
2515
|
+
return this.parseAgentsMd(filePath, content);
|
|
2516
|
+
}
|
|
2517
|
+
return [];
|
|
2518
|
+
}
|
|
2519
|
+
generate(rules) {
|
|
2520
|
+
return rules.map((rule, i) => {
|
|
2521
|
+
const fm = {};
|
|
2522
|
+
if (rule.description) fm.description = rule.description;
|
|
2523
|
+
if (rule.paths && rule.paths.length > 0) {
|
|
2524
|
+
fm.inclusion = "fileMatch";
|
|
2525
|
+
fm.fileMatchPattern = rule.paths.length === 1 ? rule.paths[0] : rule.paths;
|
|
2526
|
+
} else if (rule.alwaysApply) {
|
|
2527
|
+
fm.inclusion = "always";
|
|
2528
|
+
}
|
|
2529
|
+
const fileName = rule.id.replace(/^kiro:/, "").replace(/[^a-zA-Z0-9-_]/g, "-") || `rule-${i}`;
|
|
2530
|
+
const body = Object.keys(fm).length > 0 ? matter7.stringify(rule.content, fm) : rule.content;
|
|
2531
|
+
return {
|
|
2532
|
+
filePath: `.kiro/steering/${fileName}.md`,
|
|
2533
|
+
content: body
|
|
2534
|
+
};
|
|
2535
|
+
});
|
|
2536
|
+
}
|
|
2537
|
+
parseSteeringRule(filePath, content) {
|
|
2538
|
+
const { data, content: body } = matter7(content);
|
|
2539
|
+
const trimmed = body.trim();
|
|
2540
|
+
if (!trimmed) return [];
|
|
2541
|
+
const inclusion = data.inclusion ?? "always";
|
|
2542
|
+
const alwaysApply = inclusion === "always" || inclusion === "auto";
|
|
2543
|
+
let paths;
|
|
2544
|
+
if (inclusion === "fileMatch" && data.fileMatchPattern) {
|
|
2545
|
+
paths = Array.isArray(data.fileMatchPattern) ? data.fileMatchPattern : [data.fileMatchPattern];
|
|
2546
|
+
}
|
|
2547
|
+
let scope = "project";
|
|
2548
|
+
if (alwaysApply) scope = "global";
|
|
2549
|
+
else if (paths && paths.length > 0) scope = "path-specific";
|
|
2550
|
+
return [{
|
|
2551
|
+
id: generateRuleId("kiro", filePath),
|
|
2552
|
+
content: trimmed,
|
|
2553
|
+
description: data.description,
|
|
2554
|
+
source: "kiro",
|
|
2555
|
+
scope,
|
|
2556
|
+
paths,
|
|
2557
|
+
alwaysApply,
|
|
2558
|
+
priority: alwaysApply ? 10 : 5,
|
|
2559
|
+
hash: hashContent(trimmed)
|
|
2560
|
+
}];
|
|
2561
|
+
}
|
|
2562
|
+
parseAgentsMd(filePath, content) {
|
|
2563
|
+
const trimmed = content.trim();
|
|
2564
|
+
if (!trimmed) return [];
|
|
2565
|
+
return [{
|
|
2566
|
+
id: generateRuleId("kiro", filePath),
|
|
2567
|
+
content: trimmed,
|
|
2568
|
+
source: "kiro",
|
|
2569
|
+
scope: "project",
|
|
2570
|
+
alwaysApply: true,
|
|
2571
|
+
priority: 10,
|
|
2572
|
+
hash: hashContent(trimmed)
|
|
2573
|
+
}];
|
|
2574
|
+
}
|
|
2575
|
+
};
|
|
2576
|
+
|
|
2467
2577
|
// src/rules/syncer.ts
|
|
2468
2578
|
var RulesSyncer = class {
|
|
2469
2579
|
projectRoot;
|
|
@@ -2477,7 +2587,8 @@ var RulesSyncer = class {
|
|
|
2477
2587
|
new CodexAdapter(),
|
|
2478
2588
|
new WindsurfAdapter(),
|
|
2479
2589
|
new AntigravityAdapter(),
|
|
2480
|
-
new CopilotAdapter()
|
|
2590
|
+
new CopilotAdapter(),
|
|
2591
|
+
new KiroAdapter()
|
|
2481
2592
|
];
|
|
2482
2593
|
for (const a of all) {
|
|
2483
2594
|
this.adapters.set(a.source, a);
|
|
@@ -2619,8 +2730,8 @@ var RulesSyncer = class {
|
|
|
2619
2730
|
// src/workspace/engine.ts
|
|
2620
2731
|
init_esm_shims();
|
|
2621
2732
|
import { readFileSync as readFileSync2, readdirSync, existsSync as existsSync4, cpSync, mkdirSync as mkdirSync2 } from "fs";
|
|
2622
|
-
import { join as
|
|
2623
|
-
import { homedir as
|
|
2733
|
+
import { join as join9 } from "path";
|
|
2734
|
+
import { homedir as homedir8 } from "os";
|
|
2624
2735
|
|
|
2625
2736
|
// src/workspace/mcp-adapters/windsurf.ts
|
|
2626
2737
|
init_esm_shims();
|
|
@@ -3054,9 +3165,55 @@ var AntigravityMCPAdapter = class {
|
|
|
3054
3165
|
}
|
|
3055
3166
|
};
|
|
3056
3167
|
|
|
3168
|
+
// src/workspace/mcp-adapters/kiro.ts
|
|
3169
|
+
init_esm_shims();
|
|
3170
|
+
import { homedir as homedir7 } from "os";
|
|
3171
|
+
import { join as join7 } from "path";
|
|
3172
|
+
var KiroMCPAdapter = class {
|
|
3173
|
+
source = "kiro";
|
|
3174
|
+
parse(content) {
|
|
3175
|
+
try {
|
|
3176
|
+
const config = JSON.parse(content);
|
|
3177
|
+
const servers = config.mcpServers ?? {};
|
|
3178
|
+
return Object.entries(servers).map(([name, entry]) => ({
|
|
3179
|
+
name,
|
|
3180
|
+
command: entry.command ?? "",
|
|
3181
|
+
args: entry.args ?? [],
|
|
3182
|
+
...entry.env && Object.keys(entry.env).length > 0 ? { env: entry.env } : {},
|
|
3183
|
+
...entry.url ? { url: entry.url } : {}
|
|
3184
|
+
}));
|
|
3185
|
+
} catch {
|
|
3186
|
+
return [];
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
generate(servers) {
|
|
3190
|
+
const mcpServers = {};
|
|
3191
|
+
for (const s of servers) {
|
|
3192
|
+
const entry = {};
|
|
3193
|
+
if (s.url) {
|
|
3194
|
+
entry.url = s.url;
|
|
3195
|
+
} else {
|
|
3196
|
+
entry.command = s.command;
|
|
3197
|
+
entry.args = s.args;
|
|
3198
|
+
}
|
|
3199
|
+
if (s.env && Object.keys(s.env).length > 0) {
|
|
3200
|
+
entry.env = s.env;
|
|
3201
|
+
}
|
|
3202
|
+
mcpServers[s.name] = entry;
|
|
3203
|
+
}
|
|
3204
|
+
return JSON.stringify({ mcpServers }, null, 2);
|
|
3205
|
+
}
|
|
3206
|
+
getConfigPath(projectRoot) {
|
|
3207
|
+
if (projectRoot) {
|
|
3208
|
+
return join7(projectRoot, ".kiro", "settings", "mcp.json");
|
|
3209
|
+
}
|
|
3210
|
+
return join7(homedir7(), ".kiro", "settings", "mcp.json");
|
|
3211
|
+
}
|
|
3212
|
+
};
|
|
3213
|
+
|
|
3057
3214
|
// src/workspace/workflow-sync.ts
|
|
3058
3215
|
init_esm_shims();
|
|
3059
|
-
import
|
|
3216
|
+
import matter8 from "gray-matter";
|
|
3060
3217
|
var WorkflowSyncer = class {
|
|
3061
3218
|
/**
|
|
3062
3219
|
* Parse a Windsurf workflow markdown file into a WorkflowEntry.
|
|
@@ -3066,7 +3223,7 @@ var WorkflowSyncer = class {
|
|
|
3066
3223
|
let description = "";
|
|
3067
3224
|
let content = raw;
|
|
3068
3225
|
try {
|
|
3069
|
-
const parsed =
|
|
3226
|
+
const parsed = matter8(raw);
|
|
3070
3227
|
description = parsed.data?.description ?? "";
|
|
3071
3228
|
content = parsed.content.trim();
|
|
3072
3229
|
} catch {
|
|
@@ -3088,7 +3245,7 @@ var WorkflowSyncer = class {
|
|
|
3088
3245
|
if (wf.description) {
|
|
3089
3246
|
fm.description = wf.description;
|
|
3090
3247
|
}
|
|
3091
|
-
const content =
|
|
3248
|
+
const content = matter8.stringify(wf.content, fm);
|
|
3092
3249
|
return {
|
|
3093
3250
|
filePath: `.agents/skills/${safeName}/SKILL.md`,
|
|
3094
3251
|
content
|
|
@@ -3105,7 +3262,7 @@ var WorkflowSyncer = class {
|
|
|
3105
3262
|
}
|
|
3106
3263
|
fm.globs = "";
|
|
3107
3264
|
fm.alwaysApply = "false";
|
|
3108
|
-
const content =
|
|
3265
|
+
const content = matter8.stringify(wf.content, fm);
|
|
3109
3266
|
return {
|
|
3110
3267
|
filePath: `.cursor/rules/${safeName}.mdc`,
|
|
3111
3268
|
content
|
|
@@ -3287,7 +3444,8 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
|
|
|
3287
3444
|
["codex", new CodexMCPAdapter()],
|
|
3288
3445
|
["claude-code", new ClaudeCodeMCPAdapter()],
|
|
3289
3446
|
["copilot", new CopilotMCPAdapter()],
|
|
3290
|
-
["antigravity", new AntigravityMCPAdapter()]
|
|
3447
|
+
["antigravity", new AntigravityMCPAdapter()],
|
|
3448
|
+
["kiro", new KiroMCPAdapter()]
|
|
3291
3449
|
]);
|
|
3292
3450
|
this.workflowSyncer = new WorkflowSyncer();
|
|
3293
3451
|
this.rulesSyncer = new RulesSyncer(projectRoot);
|
|
@@ -3305,7 +3463,8 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
|
|
|
3305
3463
|
codex: [],
|
|
3306
3464
|
"claude-code": [],
|
|
3307
3465
|
copilot: [],
|
|
3308
|
-
antigravity: []
|
|
3466
|
+
antigravity: [],
|
|
3467
|
+
kiro: []
|
|
3309
3468
|
};
|
|
3310
3469
|
for (const [target, adapter] of this.adapters) {
|
|
3311
3470
|
const configPath = adapter.getConfigPath(this.projectRoot);
|
|
@@ -3397,13 +3556,14 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
|
|
|
3397
3556
|
windsurf: [".windsurf/skills"],
|
|
3398
3557
|
"claude-code": [".claude/skills"],
|
|
3399
3558
|
copilot: [".github/skills", ".copilot/skills"],
|
|
3400
|
-
antigravity: [".agent/skills", ".gemini/skills", ".gemini/antigravity/skills"]
|
|
3559
|
+
antigravity: [".agent/skills", ".gemini/skills", ".gemini/antigravity/skills"],
|
|
3560
|
+
kiro: [".kiro/skills"]
|
|
3401
3561
|
};
|
|
3402
3562
|
/** Get the target skills directory for an agent (null if agent has no skills support) */
|
|
3403
3563
|
getTargetSkillsDir(target) {
|
|
3404
3564
|
const dirs = _WorkspaceSyncEngine.SKILLS_DIRS[target];
|
|
3405
3565
|
if (!dirs || dirs.length === 0) return null;
|
|
3406
|
-
return
|
|
3566
|
+
return join9(this.projectRoot, dirs[0]);
|
|
3407
3567
|
}
|
|
3408
3568
|
/**
|
|
3409
3569
|
* Scan all agent skills directories and collect unique skills.
|
|
@@ -3412,12 +3572,12 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
|
|
|
3412
3572
|
const skills = [];
|
|
3413
3573
|
const conflicts = [];
|
|
3414
3574
|
const seen = /* @__PURE__ */ new Map();
|
|
3415
|
-
const home =
|
|
3575
|
+
const home = homedir8();
|
|
3416
3576
|
for (const [agent, dirs] of Object.entries(_WorkspaceSyncEngine.SKILLS_DIRS)) {
|
|
3417
3577
|
for (const dir of dirs) {
|
|
3418
3578
|
const paths = [
|
|
3419
|
-
|
|
3420
|
-
|
|
3579
|
+
join9(this.projectRoot, dir),
|
|
3580
|
+
join9(home, dir)
|
|
3421
3581
|
];
|
|
3422
3582
|
for (const skillsRoot of paths) {
|
|
3423
3583
|
if (!existsSync4(skillsRoot)) continue;
|
|
@@ -3425,7 +3585,7 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
|
|
|
3425
3585
|
const entries = readdirSync(skillsRoot, { withFileTypes: true });
|
|
3426
3586
|
for (const entry of entries) {
|
|
3427
3587
|
if (!entry.isDirectory()) continue;
|
|
3428
|
-
const skillMd =
|
|
3588
|
+
const skillMd = join9(skillsRoot, entry.name, "SKILL.md");
|
|
3429
3589
|
if (!existsSync4(skillMd)) continue;
|
|
3430
3590
|
let description = "";
|
|
3431
3591
|
try {
|
|
@@ -3437,7 +3597,7 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
|
|
|
3437
3597
|
const newEntry = {
|
|
3438
3598
|
name: entry.name,
|
|
3439
3599
|
description,
|
|
3440
|
-
sourcePath:
|
|
3600
|
+
sourcePath: join9(skillsRoot, entry.name),
|
|
3441
3601
|
sourceAgent: agent
|
|
3442
3602
|
};
|
|
3443
3603
|
const existing = seen.get(entry.name);
|
|
@@ -3474,7 +3634,7 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
|
|
|
3474
3634
|
}
|
|
3475
3635
|
for (const skill of skills) {
|
|
3476
3636
|
if (skill.sourceAgent === target) continue;
|
|
3477
|
-
const dest =
|
|
3637
|
+
const dest = join9(targetDir, skill.name);
|
|
3478
3638
|
if (existsSync4(dest)) {
|
|
3479
3639
|
skipped.push(`${skill.name} (already exists in ${target})`);
|
|
3480
3640
|
continue;
|
|
@@ -3490,13 +3650,13 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
|
|
|
3490
3650
|
}
|
|
3491
3651
|
scanWorkflows() {
|
|
3492
3652
|
const workflows = [];
|
|
3493
|
-
const wfDir =
|
|
3653
|
+
const wfDir = join9(this.projectRoot, ".windsurf", "workflows");
|
|
3494
3654
|
if (!existsSync4(wfDir)) return workflows;
|
|
3495
3655
|
try {
|
|
3496
3656
|
const files = readdirSync(wfDir).filter((f) => f.endsWith(".md"));
|
|
3497
3657
|
for (const file of files) {
|
|
3498
3658
|
try {
|
|
3499
|
-
const content = readFileSync2(
|
|
3659
|
+
const content = readFileSync2(join9(wfDir, file), "utf-8");
|
|
3500
3660
|
workflows.push(this.workflowSyncer.parseWindsurfWorkflow(file, content));
|
|
3501
3661
|
} catch {
|
|
3502
3662
|
}
|
|
@@ -3584,7 +3744,8 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
|
|
|
3584
3744
|
codex: "codex",
|
|
3585
3745
|
windsurf: "windsurf",
|
|
3586
3746
|
copilot: "copilot",
|
|
3587
|
-
antigravity: "antigravity"
|
|
3747
|
+
antigravity: "antigravity",
|
|
3748
|
+
kiro: "kiro"
|
|
3588
3749
|
};
|
|
3589
3750
|
return map[target] ?? null;
|
|
3590
3751
|
}
|