aios-core 4.2.12 → 4.2.13
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/.aios-core/core/code-intel/registry-syncer.js +331 -0
- package/.aios-core/core/ids/registry-loader.js +29 -0
- package/.aios-core/core/synapse/runtime/hook-runtime.js +2 -2
- package/.aios-core/data/entity-registry.yaml +46 -9
- package/.aios-core/data/registry-update-log.jsonl +12 -0
- package/.aios-core/development/agents/aios-master.md +6 -0
- package/.aios-core/development/tasks/sync-registry-intel.md +79 -0
- package/.aios-core/install-manifest.yaml +19 -11
- package/package.json +1 -1
- package/packages/installer/src/wizard/pro-setup.js +28 -1
- package/pro/license/license-api.js +34 -6
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const yaml = require('js-yaml');
|
|
6
|
+
const { getClient, isCodeIntelAvailable } = require('../code-intel');
|
|
7
|
+
const { RegistryLoader, DEFAULT_REGISTRY_PATH } = require('../ids/registry-loader');
|
|
8
|
+
|
|
9
|
+
// Role inference from entity path (order matters: more specific patterns first)
|
|
10
|
+
const ROLE_MAP = [
|
|
11
|
+
['tasks/', 'task'],
|
|
12
|
+
['templates/', 'template'],
|
|
13
|
+
['agents/', 'agent'],
|
|
14
|
+
['workflows/', 'workflow'],
|
|
15
|
+
['scripts/', 'script'],
|
|
16
|
+
['/data/', 'config'],
|
|
17
|
+
['/core/', 'module'],
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Infer entity role from its file path.
|
|
22
|
+
* @param {string} entityPath - Entity source path
|
|
23
|
+
* @returns {string} Inferred role
|
|
24
|
+
*/
|
|
25
|
+
function inferRole(entityPath) {
|
|
26
|
+
if (!entityPath) return 'unknown';
|
|
27
|
+
const normalized = entityPath.replace(/\\/g, '/');
|
|
28
|
+
for (const [pattern, role] of ROLE_MAP) {
|
|
29
|
+
if (normalized.includes(pattern)) return role;
|
|
30
|
+
}
|
|
31
|
+
return 'unknown';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* RegistrySyncer — Enriches entity registry with code intelligence data.
|
|
36
|
+
*
|
|
37
|
+
* Features:
|
|
38
|
+
* - Batch enrichment of usedBy, dependencies, keywords via code intelligence
|
|
39
|
+
* - Incremental sync (mtime-based) and full resync (--full flag)
|
|
40
|
+
* - Atomic write (temp file + rename) to prevent registry corruption
|
|
41
|
+
* - Graceful fallback when no provider is available
|
|
42
|
+
*/
|
|
43
|
+
class RegistrySyncer {
|
|
44
|
+
/**
|
|
45
|
+
* @param {Object} [options]
|
|
46
|
+
* @param {string} [options.registryPath] - Path to entity-registry.yaml
|
|
47
|
+
* @param {string} [options.repoRoot] - Repository root for resolving entity paths
|
|
48
|
+
* @param {Object} [options.client] - Code intel client (for testing injection)
|
|
49
|
+
* @param {Function} [options.logger] - Logger function (defaults to console.log)
|
|
50
|
+
*/
|
|
51
|
+
constructor(options = {}) {
|
|
52
|
+
this._registryPath = options.registryPath || DEFAULT_REGISTRY_PATH;
|
|
53
|
+
this._repoRoot = options.repoRoot || path.resolve(__dirname, '../../../');
|
|
54
|
+
this._client = options.client || null;
|
|
55
|
+
this._logger = options.logger || console.log;
|
|
56
|
+
this._stats = { processed: 0, skipped: 0, errors: 0, total: 0 };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get code intel client (lazy, allows injection for testing).
|
|
61
|
+
* @returns {Object|null}
|
|
62
|
+
*/
|
|
63
|
+
_getClient() {
|
|
64
|
+
if (this._client) return this._client;
|
|
65
|
+
return getClient();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Sync the entity registry with code intelligence data.
|
|
70
|
+
* @param {Object} [options]
|
|
71
|
+
* @param {boolean} [options.full=false] - Force full resync (ignore lastSynced)
|
|
72
|
+
* @returns {Promise<Object>} Sync stats
|
|
73
|
+
*/
|
|
74
|
+
async sync(options = {}) {
|
|
75
|
+
const isFull = options.full === true;
|
|
76
|
+
|
|
77
|
+
// AC5: Fallback — check provider availability first
|
|
78
|
+
if (!this._isProviderAvailable()) {
|
|
79
|
+
this._logger('[registry-syncer] No code intelligence provider available, skipping enrichment');
|
|
80
|
+
return { processed: 0, skipped: 0, errors: 0, total: 0, aborted: true };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Load registry
|
|
84
|
+
const loader = new RegistryLoader(this._registryPath);
|
|
85
|
+
const registry = loader.load();
|
|
86
|
+
const entities = registry.entities || {};
|
|
87
|
+
|
|
88
|
+
// Flatten all entities for iteration
|
|
89
|
+
const allEntities = [];
|
|
90
|
+
for (const [category, categoryEntities] of Object.entries(entities)) {
|
|
91
|
+
if (!categoryEntities || typeof categoryEntities !== 'object') continue;
|
|
92
|
+
for (const [entityId, entityData] of Object.entries(categoryEntities)) {
|
|
93
|
+
allEntities.push({ id: entityId, category, data: entityData });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this._stats = { processed: 0, skipped: 0, errors: 0, total: allEntities.length };
|
|
98
|
+
this._logger(`[registry-syncer] Starting ${isFull ? 'full' : 'incremental'} sync of ${allEntities.length} entities`);
|
|
99
|
+
|
|
100
|
+
// Iterate and enrich
|
|
101
|
+
for (const entity of allEntities) {
|
|
102
|
+
try {
|
|
103
|
+
const wasProcessed = await this.syncEntity(entity, entities, isFull);
|
|
104
|
+
if (wasProcessed) {
|
|
105
|
+
this._stats.processed++;
|
|
106
|
+
} else {
|
|
107
|
+
this._stats.skipped++;
|
|
108
|
+
}
|
|
109
|
+
} catch (error) {
|
|
110
|
+
this._stats.errors++;
|
|
111
|
+
this._logger(`[registry-syncer] Error enriching ${entity.id}: ${error.message}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Update metadata
|
|
116
|
+
registry.metadata = registry.metadata || {};
|
|
117
|
+
registry.metadata.lastUpdated = new Date().toISOString();
|
|
118
|
+
registry.metadata.entityCount = allEntities.length;
|
|
119
|
+
|
|
120
|
+
// Atomic write
|
|
121
|
+
this._atomicWrite(this._registryPath, registry);
|
|
122
|
+
|
|
123
|
+
this._logger(`[registry-syncer] Sync complete: ${this._stats.processed} processed, ${this._stats.skipped} skipped, ${this._stats.errors} errors`);
|
|
124
|
+
return { ...this._stats };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Enrich a single entity with code intelligence data.
|
|
129
|
+
* @param {Object} entity - { id, category, data }
|
|
130
|
+
* @param {Object} entities - Full entities map (for cross-reference)
|
|
131
|
+
* @param {boolean} isFull - Force full resync
|
|
132
|
+
* @returns {Promise<boolean>} true if entity was processed, false if skipped
|
|
133
|
+
*/
|
|
134
|
+
async syncEntity(entity, entities, isFull) {
|
|
135
|
+
const { id, data } = entity;
|
|
136
|
+
const sourcePath = data.path;
|
|
137
|
+
|
|
138
|
+
// Skip entities without source path
|
|
139
|
+
if (!sourcePath) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// AC6: Incremental sync — check mtime vs lastSynced
|
|
144
|
+
if (!isFull) {
|
|
145
|
+
const shouldSkip = this._shouldSkipIncremental(data);
|
|
146
|
+
if (shouldSkip) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const client = this._getClient();
|
|
152
|
+
const now = new Date().toISOString();
|
|
153
|
+
|
|
154
|
+
// AC2: Populate usedBy via findReferences
|
|
155
|
+
const usedByIds = await this._findUsedBy(client, id, entities);
|
|
156
|
+
if (usedByIds !== null) {
|
|
157
|
+
data.usedBy = usedByIds;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// AC3: Populate dependencies via analyzeDependencies (JS/TS files only)
|
|
161
|
+
const deps = await this._findDependencies(client, sourcePath);
|
|
162
|
+
if (deps !== null) {
|
|
163
|
+
data.dependencies = deps;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// AC4: Populate codeIntelMetadata
|
|
167
|
+
const callerCount = Array.isArray(data.usedBy) ? data.usedBy.length : 0;
|
|
168
|
+
data.codeIntelMetadata = {
|
|
169
|
+
callerCount,
|
|
170
|
+
role: inferRole(sourcePath),
|
|
171
|
+
lastSynced: now,
|
|
172
|
+
provider: client._activeProvider ? client._activeProvider.name : 'unknown',
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Check if provider is available.
|
|
180
|
+
* @returns {boolean}
|
|
181
|
+
* @private
|
|
182
|
+
*/
|
|
183
|
+
_isProviderAvailable() {
|
|
184
|
+
if (this._client) {
|
|
185
|
+
return typeof this._client.findReferences === 'function';
|
|
186
|
+
}
|
|
187
|
+
return isCodeIntelAvailable();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Check if entity should be skipped in incremental mode.
|
|
192
|
+
* @param {Object} entityData
|
|
193
|
+
* @returns {boolean} true if should skip
|
|
194
|
+
* @private
|
|
195
|
+
*/
|
|
196
|
+
_shouldSkipIncremental(entityData) {
|
|
197
|
+
const metadata = entityData.codeIntelMetadata;
|
|
198
|
+
|
|
199
|
+
// Entities without lastSynced are always processed (AC6)
|
|
200
|
+
if (!metadata || !metadata.lastSynced) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Get file mtime
|
|
205
|
+
const sourcePath = entityData.path;
|
|
206
|
+
if (!sourcePath) return true;
|
|
207
|
+
|
|
208
|
+
const fullPath = path.resolve(this._repoRoot, sourcePath);
|
|
209
|
+
try {
|
|
210
|
+
const stat = fs.statSync(fullPath);
|
|
211
|
+
const lastSyncedMs = new Date(metadata.lastSynced).getTime();
|
|
212
|
+
// Skip if file hasn't changed since last sync
|
|
213
|
+
return stat.mtimeMs <= lastSyncedMs;
|
|
214
|
+
} catch (_error) {
|
|
215
|
+
// File doesn't exist — clear metadata and skip
|
|
216
|
+
this._logger(`[registry-syncer] Warning: source file not found for ${sourcePath}, skipping`);
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Find entities that reference this entity (usedBy).
|
|
223
|
+
* @param {Object} client - Code intel client
|
|
224
|
+
* @param {string} entityId - Entity ID to search for
|
|
225
|
+
* @param {Object} entities - Full entities map for cross-referencing
|
|
226
|
+
* @returns {Promise<string[]|null>} Array of entity IDs or null on failure
|
|
227
|
+
* @private
|
|
228
|
+
*/
|
|
229
|
+
async _findUsedBy(client, entityId, entities) {
|
|
230
|
+
try {
|
|
231
|
+
const references = await client.findReferences(entityId);
|
|
232
|
+
if (!references || !Array.isArray(references)) return null;
|
|
233
|
+
|
|
234
|
+
// Cross-reference with registry to get entity IDs
|
|
235
|
+
const usedByIds = [];
|
|
236
|
+
for (const ref of references) {
|
|
237
|
+
const refPath = ref.file || ref.path || ref;
|
|
238
|
+
if (typeof refPath !== 'string') continue;
|
|
239
|
+
|
|
240
|
+
const matchedId = this._findEntityByPath(refPath, entities);
|
|
241
|
+
if (matchedId && matchedId !== entityId) {
|
|
242
|
+
usedByIds.push(matchedId);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return [...new Set(usedByIds)]; // Deduplicate
|
|
247
|
+
} catch (_error) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Find dependencies of a source file.
|
|
254
|
+
* @param {Object} client - Code intel client
|
|
255
|
+
* @param {string} sourcePath - Source file path
|
|
256
|
+
* @returns {Promise<string[]|null>} Array of dependency names or null on failure
|
|
257
|
+
* @private
|
|
258
|
+
*/
|
|
259
|
+
async _findDependencies(client, sourcePath) {
|
|
260
|
+
// Only analyze JS/TS files for import dependencies
|
|
261
|
+
if (!sourcePath.match(/\.(js|ts|mjs|cjs)$/)) return null;
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
const fullPath = path.resolve(this._repoRoot, sourcePath);
|
|
265
|
+
const result = await client.analyzeDependencies(fullPath);
|
|
266
|
+
if (!result) return null;
|
|
267
|
+
|
|
268
|
+
// Filter to internal project dependencies only
|
|
269
|
+
const deps = [];
|
|
270
|
+
const items = result.dependencies || result.imports || result;
|
|
271
|
+
if (!Array.isArray(items)) return null;
|
|
272
|
+
|
|
273
|
+
for (const dep of items) {
|
|
274
|
+
const depPath = dep.path || dep.source || dep;
|
|
275
|
+
if (typeof depPath !== 'string') continue;
|
|
276
|
+
// Internal deps: relative paths or project paths (not node_modules)
|
|
277
|
+
if (depPath.startsWith('.') || depPath.startsWith('/') || depPath.includes('.aios-core')) {
|
|
278
|
+
deps.push(depPath);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return deps;
|
|
283
|
+
} catch (_error) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Find entity ID by file path (cross-reference lookup).
|
|
290
|
+
* @param {string} filePath - File path from reference
|
|
291
|
+
* @param {Object} entities - Full entities map
|
|
292
|
+
* @returns {string|null} Entity ID or null
|
|
293
|
+
* @private
|
|
294
|
+
*/
|
|
295
|
+
_findEntityByPath(filePath, entities) {
|
|
296
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
297
|
+
for (const [_category, categoryEntities] of Object.entries(entities)) {
|
|
298
|
+
if (!categoryEntities || typeof categoryEntities !== 'object') continue;
|
|
299
|
+
for (const [entityId, entityData] of Object.entries(categoryEntities)) {
|
|
300
|
+
if (entityData.path && normalized.includes(entityData.path.replace(/\\/g, '/'))) {
|
|
301
|
+
return entityId;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Atomic write: write to temp file then rename.
|
|
310
|
+
* Prevents partial corruption if process crashes mid-write.
|
|
311
|
+
* @param {string} registryPath - Target path
|
|
312
|
+
* @param {Object} registry - Registry data to write
|
|
313
|
+
* @private
|
|
314
|
+
*/
|
|
315
|
+
_atomicWrite(registryPath, registry) {
|
|
316
|
+
const tmpPath = registryPath + '.tmp';
|
|
317
|
+
const content = yaml.dump(registry, { lineWidth: 120, noRefs: true });
|
|
318
|
+
fs.writeFileSync(tmpPath, content, 'utf8');
|
|
319
|
+
fs.renameSync(tmpPath, registryPath);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Get sync statistics from last run.
|
|
324
|
+
* @returns {Object}
|
|
325
|
+
*/
|
|
326
|
+
getStats() {
|
|
327
|
+
return { ...this._stats };
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
module.exports = { RegistrySyncer, inferRole, ROLE_MAP };
|
|
@@ -232,6 +232,35 @@ class RegistryLoader {
|
|
|
232
232
|
return entity;
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Get entity with code intelligence metadata (Story NOG-2).
|
|
237
|
+
* @param {string} entityId - Entity ID
|
|
238
|
+
* @returns {Object|null} Entity with codeIntelMetadata or null
|
|
239
|
+
*/
|
|
240
|
+
getEntityWithIntel(entityId) {
|
|
241
|
+
const entity = this._findById(entityId);
|
|
242
|
+
if (!entity) return null;
|
|
243
|
+
return {
|
|
244
|
+
...entity,
|
|
245
|
+
codeIntelMetadata: entity.codeIntelMetadata || null,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Query entities by keywords with optional role filter (Story NOG-2).
|
|
251
|
+
* @param {string[]} keywords - Keywords to search
|
|
252
|
+
* @param {Object} [options]
|
|
253
|
+
* @param {string} [options.role] - Filter by codeIntelMetadata.role
|
|
254
|
+
* @returns {Object[]} Matching entities
|
|
255
|
+
*/
|
|
256
|
+
findByKeyword(keywords, options = {}) {
|
|
257
|
+
const results = this.queryByKeywords(keywords);
|
|
258
|
+
if (!options.role) return results;
|
|
259
|
+
return results.filter(
|
|
260
|
+
(e) => e.codeIntelMetadata && e.codeIntelMetadata.role === options.role,
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
235
264
|
/**
|
|
236
265
|
* Get registry metadata.
|
|
237
266
|
*/
|
|
@@ -6,7 +6,7 @@ const fs = require('fs');
|
|
|
6
6
|
/**
|
|
7
7
|
* Resolve runtime dependencies for Synapse hook execution.
|
|
8
8
|
*
|
|
9
|
-
* @param {{cwd?: string, sessionId?: string}} input
|
|
9
|
+
* @param {{cwd?: string, session_id?: string, sessionId?: string}} input
|
|
10
10
|
* @returns {{
|
|
11
11
|
* engine: import('../engine').SynapseEngine,
|
|
12
12
|
* session: Object
|
|
@@ -14,7 +14,7 @@ const fs = require('fs');
|
|
|
14
14
|
*/
|
|
15
15
|
function resolveHookRuntime(input) {
|
|
16
16
|
const cwd = input && input.cwd;
|
|
17
|
-
const sessionId = input && input.sessionId;
|
|
17
|
+
const sessionId = input && (input.session_id || input.sessionId);
|
|
18
18
|
if (!cwd || typeof cwd !== 'string') return null;
|
|
19
19
|
|
|
20
20
|
const synapsePath = path.join(cwd, '.synapse');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
metadata:
|
|
2
2
|
version: 1.0.0
|
|
3
|
-
lastUpdated: '2026-02-
|
|
4
|
-
entityCount:
|
|
3
|
+
lastUpdated: '2026-02-16T20:22:35.905Z'
|
|
4
|
+
entityCount: 508
|
|
5
5
|
checksumAlgorithm: sha256
|
|
6
6
|
entities:
|
|
7
7
|
tasks:
|
|
@@ -3909,6 +3909,24 @@ entities:
|
|
|
3909
3909
|
extensionPoints: []
|
|
3910
3910
|
checksum: sha256:f7a0bb8fed5663c88ad691b8871fdf7a861b6a7c02599f0c2db3eb9393d353c8
|
|
3911
3911
|
lastVerified: '2026-02-16T01:21:26.585Z'
|
|
3912
|
+
sync-registry-intel:
|
|
3913
|
+
path: .aios-core/development/tasks/sync-registry-intel.md
|
|
3914
|
+
type: task
|
|
3915
|
+
purpose: 'Task: Sync Registry Intel'
|
|
3916
|
+
keywords:
|
|
3917
|
+
- sync
|
|
3918
|
+
- registry
|
|
3919
|
+
- intel
|
|
3920
|
+
- 'task:'
|
|
3921
|
+
usedBy: []
|
|
3922
|
+
dependencies:
|
|
3923
|
+
- registry-syncer
|
|
3924
|
+
adaptability:
|
|
3925
|
+
score: 0.8
|
|
3926
|
+
constraints: []
|
|
3927
|
+
extensionPoints: []
|
|
3928
|
+
checksum: sha256:0e69435307db814563823896e7ba9b29a4a9c10d90f6dedec5cb7a6d6f7ba936
|
|
3929
|
+
lastVerified: '2026-02-16T20:19:27.659Z'
|
|
3912
3930
|
templates:
|
|
3913
3931
|
activation-instructions-inline-greeting:
|
|
3914
3932
|
path: .aios-core/product/templates/activation-instructions-inline-greeting.yaml
|
|
@@ -5979,6 +5997,24 @@ entities:
|
|
|
5979
5997
|
checksum: sha256:310884d94b81be976a346987822306a16a73ba812c08c3b805f4a03216ffef38
|
|
5980
5998
|
lastVerified: '2026-02-15T19:28:17.743Z'
|
|
5981
5999
|
modules:
|
|
6000
|
+
registry-syncer:
|
|
6001
|
+
path: .aios-core/core/code-intel/registry-syncer.js
|
|
6002
|
+
type: module
|
|
6003
|
+
purpose: Entity at .aios-core\core\code-intel\registry-syncer.js
|
|
6004
|
+
keywords:
|
|
6005
|
+
- registry
|
|
6006
|
+
- syncer
|
|
6007
|
+
usedBy:
|
|
6008
|
+
- sync-registry-intel
|
|
6009
|
+
dependencies:
|
|
6010
|
+
- code-intel
|
|
6011
|
+
- registry-loader
|
|
6012
|
+
adaptability:
|
|
6013
|
+
score: 0.4
|
|
6014
|
+
constraints: []
|
|
6015
|
+
extensionPoints: []
|
|
6016
|
+
checksum: sha256:011318e2ba5c250daae2e565a8e8fb1570c99b7569e631276a2bf46e887fc514
|
|
6017
|
+
lastVerified: '2026-02-16T20:19:27.658Z'
|
|
5982
6018
|
index.esm:
|
|
5983
6019
|
path: .aios-core/core/index.esm.js
|
|
5984
6020
|
type: module
|
|
@@ -6562,6 +6598,7 @@ entities:
|
|
|
6562
6598
|
- registry
|
|
6563
6599
|
- loader
|
|
6564
6600
|
usedBy:
|
|
6601
|
+
- registry-syncer
|
|
6565
6602
|
- index
|
|
6566
6603
|
- framework-governor
|
|
6567
6604
|
dependencies: []
|
|
@@ -6569,8 +6606,8 @@ entities:
|
|
|
6569
6606
|
score: 0.4
|
|
6570
6607
|
constraints: []
|
|
6571
6608
|
extensionPoints: []
|
|
6572
|
-
checksum: sha256:
|
|
6573
|
-
lastVerified: '2026-02-
|
|
6609
|
+
checksum: sha256:88c67bace0a5ab6a14cb1d096e3f9cab9f5edc0dd8377788e27b692ccefbd487
|
|
6610
|
+
lastVerified: '2026-02-16T20:19:27.659Z'
|
|
6574
6611
|
manifest-generator:
|
|
6575
6612
|
path: .aios-core/core/manifest/manifest-generator.js
|
|
6576
6613
|
type: module
|
|
@@ -8536,7 +8573,7 @@ entities:
|
|
|
8536
8573
|
hook-runtime:
|
|
8537
8574
|
path: .aios-core/core/synapse/runtime/hook-runtime.js
|
|
8538
8575
|
type: module
|
|
8539
|
-
purpose: Entity at .aios-core
|
|
8576
|
+
purpose: Entity at .aios-core\core\synapse\runtime\hook-runtime.js
|
|
8540
8577
|
keywords:
|
|
8541
8578
|
- hook
|
|
8542
8579
|
- runtime
|
|
@@ -8546,8 +8583,8 @@ entities:
|
|
|
8546
8583
|
score: 0.4
|
|
8547
8584
|
constraints: []
|
|
8548
8585
|
extensionPoints: []
|
|
8549
|
-
checksum: sha256:
|
|
8550
|
-
lastVerified: '2026-02-
|
|
8586
|
+
checksum: sha256:2fdd54f36a1bbb4ba05d6577c607cf2d6711dcf9a06284fc935676189a461fa2
|
|
8587
|
+
lastVerified: '2026-02-16T20:22:35.904Z'
|
|
8551
8588
|
migration-config:
|
|
8552
8589
|
path: .aios-core/core/migration/migration-config.yaml
|
|
8553
8590
|
type: module
|
|
@@ -8819,8 +8856,8 @@ entities:
|
|
|
8819
8856
|
score: 0.3
|
|
8820
8857
|
constraints: []
|
|
8821
8858
|
extensionPoints: []
|
|
8822
|
-
checksum: sha256:
|
|
8823
|
-
lastVerified: '2026-02-
|
|
8859
|
+
checksum: sha256:092161d318ab523b8cd5c3dc8a2bd19accc23ab7fa731d5b4fa11c5afb8b5a08
|
|
8860
|
+
lastVerified: '2026-02-16T19:52:18.846Z'
|
|
8824
8861
|
analyst:
|
|
8825
8862
|
path: .aios-core/development/agents/analyst.md
|
|
8826
8863
|
type: agent
|
|
@@ -177,3 +177,15 @@
|
|
|
177
177
|
{"timestamp":"2026-02-16T01:46:56.302Z","action":"change","path":".aios-core/core/code-intel/code-intel-enricher.js","trigger":"watcher"}
|
|
178
178
|
{"timestamp":"2026-02-16T01:46:56.302Z","action":"change","path":".aios-core/core/code-intel/index.js","trigger":"watcher"}
|
|
179
179
|
{"timestamp":"2026-02-16T01:46:56.303Z","action":"change","path":".aios-core/core/code-intel/providers/code-graph-provider.js","trigger":"watcher"}
|
|
180
|
+
{"timestamp":"2026-02-16T19:52:18.842Z","action":"add","path":".aios-core/core/code-intel/registry-syncer.js","trigger":"watcher"}
|
|
181
|
+
{"timestamp":"2026-02-16T19:52:18.845Z","action":"change","path":".aios-core/core/ids/registry-loader.js","trigger":"watcher"}
|
|
182
|
+
{"timestamp":"2026-02-16T19:52:18.846Z","action":"change","path":".aios-core/development/agents/aios-master.md","trigger":"watcher"}
|
|
183
|
+
{"timestamp":"2026-02-16T19:52:18.847Z","action":"add","path":".aios-core/development/tasks/sync-registry-intel.md","trigger":"watcher"}
|
|
184
|
+
{"timestamp":"2026-02-16T20:19:21.429Z","action":"change","path":".aios-core/core/code-intel/registry-syncer.js","trigger":"watcher"}
|
|
185
|
+
{"timestamp":"2026-02-16T20:19:21.430Z","action":"change","path":".aios-core/core/ids/registry-loader.js","trigger":"watcher"}
|
|
186
|
+
{"timestamp":"2026-02-16T20:19:21.431Z","action":"change","path":".aios-core/development/tasks/sync-registry-intel.md","trigger":"watcher"}
|
|
187
|
+
{"timestamp":"2026-02-16T20:19:27.658Z","action":"change","path":".aios-core/core/code-intel/registry-syncer.js","trigger":"watcher"}
|
|
188
|
+
{"timestamp":"2026-02-16T20:19:27.659Z","action":"change","path":".aios-core/core/ids/registry-loader.js","trigger":"watcher"}
|
|
189
|
+
{"timestamp":"2026-02-16T20:19:27.659Z","action":"change","path":".aios-core/development/tasks/sync-registry-intel.md","trigger":"watcher"}
|
|
190
|
+
{"timestamp":"2026-02-16T20:22:31.161Z","action":"change","path":".aios-core/core/synapse/runtime/hook-runtime.js","trigger":"watcher"}
|
|
191
|
+
{"timestamp":"2026-02-16T20:22:35.904Z","action":"change","path":".aios-core/core/synapse/runtime/hook-runtime.js","trigger":"watcher"}
|
|
@@ -217,6 +217,11 @@ commands:
|
|
|
217
217
|
- name: ids stats
|
|
218
218
|
description: 'Registry statistics (entity count by type, categories, health score)'
|
|
219
219
|
|
|
220
|
+
# Code Intelligence — Registry Enrichment (Story NOG-2)
|
|
221
|
+
- name: sync-registry-intel
|
|
222
|
+
args: '[--full]'
|
|
223
|
+
description: 'Enrich entity registry with code intelligence data (usedBy, dependencies, codeIntelMetadata). Use --full to force full resync.'
|
|
224
|
+
|
|
220
225
|
# IDS Pre-Action Hooks (Story IDS-7)
|
|
221
226
|
# These hooks run BEFORE *create and *modify commands as advisory (non-blocking) steps.
|
|
222
227
|
ids_hooks:
|
|
@@ -282,6 +287,7 @@ dependencies:
|
|
|
282
287
|
- run-workflow.md
|
|
283
288
|
- run-workflow-engine.md
|
|
284
289
|
- ids-governor.md
|
|
290
|
+
- sync-registry-intel.md
|
|
285
291
|
# Delegated tasks (Story 6.1.2.3):
|
|
286
292
|
# brownfield-create-epic.md → @pm
|
|
287
293
|
# brownfield-create-story.md → @pm
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Task: Sync Registry Intel
|
|
2
|
+
|
|
3
|
+
## Metadata
|
|
4
|
+
- **Task ID:** sync-registry-intel
|
|
5
|
+
- **Agent:** @aios-master
|
|
6
|
+
- **Story:** NOG-2
|
|
7
|
+
- **Type:** Command Task
|
|
8
|
+
- **Elicit:** false
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Description
|
|
13
|
+
|
|
14
|
+
Enrich the entity registry with code intelligence data (usedBy, dependencies, codeIntelMetadata) using the configured code intelligence provider.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Prerequisites
|
|
19
|
+
|
|
20
|
+
- Code intelligence provider available (NOG-1 complete)
|
|
21
|
+
- Entity registry exists at `.aios-core/data/entity-registry.yaml`
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Execution Steps
|
|
26
|
+
|
|
27
|
+
### Step 1: Parse Arguments
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
Arguments:
|
|
31
|
+
--full Force full resync (reprocess all entities regardless of lastSynced)
|
|
32
|
+
|
|
33
|
+
Default: Incremental sync (only entities whose source file mtime > lastSynced)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Step 2: Execute Sync
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
const { RegistrySyncer } = require('.aios-core/core/code-intel/registry-syncer');
|
|
40
|
+
|
|
41
|
+
const syncer = new RegistrySyncer();
|
|
42
|
+
const stats = await syncer.sync({ full: hasFullFlag });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Step 3: Report Results
|
|
46
|
+
|
|
47
|
+
Display sync statistics:
|
|
48
|
+
- Total entities in registry
|
|
49
|
+
- Entities processed (enriched)
|
|
50
|
+
- Entities skipped (unchanged)
|
|
51
|
+
- Errors encountered
|
|
52
|
+
|
|
53
|
+
### Step 4: Handle Fallback
|
|
54
|
+
|
|
55
|
+
If no code intelligence provider is available:
|
|
56
|
+
- Display: "No code intelligence provider available, skipping enrichment"
|
|
57
|
+
- Exit gracefully with zero modifications
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Output
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
success: true
|
|
65
|
+
stats:
|
|
66
|
+
total: 506
|
|
67
|
+
processed: 42
|
|
68
|
+
skipped: 464
|
|
69
|
+
errors: 0
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Error Handling
|
|
75
|
+
|
|
76
|
+
- **No provider:** Graceful exit, zero modifications
|
|
77
|
+
- **Registry not found:** Error message, exit
|
|
78
|
+
- **Partial failure:** Continue batch, log errors, report count
|
|
79
|
+
- **Write failure:** Atomic write prevents corruption (temp + rename)
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
# - SHA256 hashes for change detection
|
|
8
8
|
# - File types for categorization
|
|
9
9
|
#
|
|
10
|
-
version: 4.2.
|
|
11
|
-
generated_at: "2026-02-
|
|
10
|
+
version: 4.2.13
|
|
11
|
+
generated_at: "2026-02-16T21:09:06.284Z"
|
|
12
12
|
generator: scripts/generate-install-manifest.js
|
|
13
|
-
file_count:
|
|
13
|
+
file_count: 1006
|
|
14
14
|
files:
|
|
15
15
|
- path: cli/commands/config/index.js
|
|
16
16
|
hash: sha256:ebcad2ce3807eda29dcddff76d7a95ddc9b7fa160df21fd608f94b802237e862
|
|
@@ -200,6 +200,10 @@ files:
|
|
|
200
200
|
hash: sha256:7d16aa715155e9c077720a6bffc7e9e5411b65f821b6b4e5e909f226796e7acb
|
|
201
201
|
type: core
|
|
202
202
|
size: 3079
|
|
203
|
+
- path: core/code-intel/registry-syncer.js
|
|
204
|
+
hash: sha256:011318e2ba5c250daae2e565a8e8fb1570c99b7569e631276a2bf46e887fc514
|
|
205
|
+
type: core
|
|
206
|
+
size: 10891
|
|
203
207
|
- path: core/config/config-cache.js
|
|
204
208
|
hash: sha256:527a788cbe650aa6b13d1101ebc16419489bfef20b2ee93042f6eb6a51e898e9
|
|
205
209
|
type: core
|
|
@@ -565,9 +569,9 @@ files:
|
|
|
565
569
|
type: core
|
|
566
570
|
size: 26179
|
|
567
571
|
- path: core/ids/registry-loader.js
|
|
568
|
-
hash: sha256:
|
|
572
|
+
hash: sha256:88c67bace0a5ab6a14cb1d096e3f9cab9f5edc0dd8377788e27b692ccefbd487
|
|
569
573
|
type: core
|
|
570
|
-
size:
|
|
574
|
+
size: 8096
|
|
571
575
|
- path: core/ids/registry-updater.js
|
|
572
576
|
hash: sha256:6d87ec21d32acff1ba9b9d13025118c106ce6db59c1339c3a6ef4b2a02fd7f52
|
|
573
577
|
type: core
|
|
@@ -985,9 +989,9 @@ files:
|
|
|
985
989
|
type: core
|
|
986
990
|
size: 16418
|
|
987
991
|
- path: core/synapse/runtime/hook-runtime.js
|
|
988
|
-
hash: sha256:
|
|
992
|
+
hash: sha256:2fdd54f36a1bbb4ba05d6577c607cf2d6711dcf9a06284fc935676189a461fa2
|
|
989
993
|
type: core
|
|
990
|
-
size:
|
|
994
|
+
size: 1572
|
|
991
995
|
- path: core/synapse/scripts/generate-constitution.js
|
|
992
996
|
hash: sha256:65405d3e4ee080d19a25fb8967e159360a289e773c15253a351ee163b469e877
|
|
993
997
|
type: script
|
|
@@ -1037,9 +1041,9 @@ files:
|
|
|
1037
1041
|
type: data
|
|
1038
1042
|
size: 34251
|
|
1039
1043
|
- path: data/entity-registry.yaml
|
|
1040
|
-
hash: sha256:
|
|
1044
|
+
hash: sha256:fefd5d2eba3de2c49a3dbf6219cda466062b05a05b894dc2f89886b1f937bd21
|
|
1041
1045
|
type: data
|
|
1042
|
-
size:
|
|
1046
|
+
size: 291072
|
|
1043
1047
|
- path: data/learned-patterns.yaml
|
|
1044
1048
|
hash: sha256:24ac0b160615583a0ff783d3da8af80b7f94191575d6db2054ec8e10a3f945dc
|
|
1045
1049
|
type: data
|
|
@@ -1085,9 +1089,9 @@ files:
|
|
|
1085
1089
|
type: development
|
|
1086
1090
|
size: 5012
|
|
1087
1091
|
- path: development/agents/aios-master.md
|
|
1088
|
-
hash: sha256:
|
|
1092
|
+
hash: sha256:092161d318ab523b8cd5c3dc8a2bd19accc23ab7fa731d5b4fa11c5afb8b5a08
|
|
1089
1093
|
type: agent
|
|
1090
|
-
size:
|
|
1094
|
+
size: 17821
|
|
1091
1095
|
- path: development/agents/analyst.md
|
|
1092
1096
|
hash: sha256:470384d9ee05d1373fe7519602f135179a88a35895252277823b35339dafd2a3
|
|
1093
1097
|
type: agent
|
|
@@ -2180,6 +2184,10 @@ files:
|
|
|
2180
2184
|
hash: sha256:caa2077e7a5bbbba9269b04e878b7772a71422ed6fd138447fe5cfb7345f96fb
|
|
2181
2185
|
type: task
|
|
2182
2186
|
size: 23362
|
|
2187
|
+
- path: development/tasks/sync-registry-intel.md
|
|
2188
|
+
hash: sha256:0e69435307db814563823896e7ba9b29a4a9c10d90f6dedec5cb7a6d6f7ba936
|
|
2189
|
+
type: task
|
|
2190
|
+
size: 1664
|
|
2183
2191
|
- path: development/tasks/tailwind-upgrade.md
|
|
2184
2192
|
hash: sha256:c369df0a28d8be7f0092405ecaed669a40075841427337990e2346b8c1d43c3a
|
|
2185
2193
|
type: task
|
package/package.json
CHANGED
|
@@ -395,7 +395,34 @@ async function loginWithRetry(client, email) {
|
|
|
395
395
|
// Activate Pro
|
|
396
396
|
return activateProByAuth(client, loginResult.sessionToken);
|
|
397
397
|
} catch (loginError) {
|
|
398
|
-
if (loginError.code === '
|
|
398
|
+
if (loginError.code === 'EMAIL_NOT_VERIFIED') {
|
|
399
|
+
// Email not verified — poll by retrying login until verified
|
|
400
|
+
spinner.info('Email not verified yet. Please check your inbox and click the verification link.');
|
|
401
|
+
console.log(colors.dim(' (Checking every 5 seconds... timeout in 10 minutes)'));
|
|
402
|
+
|
|
403
|
+
const startTime = Date.now();
|
|
404
|
+
while (Date.now() - startTime < VERIFY_POLL_TIMEOUT_MS) {
|
|
405
|
+
await new Promise((resolve) => setTimeout(resolve, VERIFY_POLL_INTERVAL_MS));
|
|
406
|
+
try {
|
|
407
|
+
const retryLogin = await client.login(email, password);
|
|
408
|
+
showSuccess('Email verified!');
|
|
409
|
+
if (!retryLogin.emailVerified) {
|
|
410
|
+
const verifyResult = await waitForEmailVerification(client, retryLogin.sessionToken, email);
|
|
411
|
+
if (!verifyResult.success) return verifyResult;
|
|
412
|
+
}
|
|
413
|
+
return activateProByAuth(client, retryLogin.sessionToken);
|
|
414
|
+
} catch (retryError) {
|
|
415
|
+
if (retryError.code !== 'EMAIL_NOT_VERIFIED') {
|
|
416
|
+
return { success: false, error: retryError.message };
|
|
417
|
+
}
|
|
418
|
+
// Still not verified, continue polling
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
showError('Email verification timed out after 10 minutes.');
|
|
423
|
+
showInfo('Run the installer again to retry.');
|
|
424
|
+
return { success: false, error: 'Email verification timed out.' };
|
|
425
|
+
} else if (loginError.code === 'INVALID_CREDENTIALS') {
|
|
399
426
|
const remaining = MAX_RETRIES - attempt;
|
|
400
427
|
if (remaining > 0) {
|
|
401
428
|
spinner.fail(`Incorrect password. ${remaining} attempt${remaining > 1 ? 's' : ''} remaining.`);
|
|
@@ -151,7 +151,24 @@ class LicenseApiClient {
|
|
|
151
151
|
break;
|
|
152
152
|
|
|
153
153
|
case 401:
|
|
154
|
-
|
|
154
|
+
// Preserve server error code (e.g., INVALID_CREDENTIALS, EMAIL_NOT_VERIFIED)
|
|
155
|
+
reject(
|
|
156
|
+
new LicenseActivationError(
|
|
157
|
+
message || 'Unauthorized',
|
|
158
|
+
code || 'INVALID_KEY',
|
|
159
|
+
details,
|
|
160
|
+
),
|
|
161
|
+
);
|
|
162
|
+
break;
|
|
163
|
+
|
|
164
|
+
case 409:
|
|
165
|
+
reject(
|
|
166
|
+
new LicenseActivationError(
|
|
167
|
+
message || 'Conflict',
|
|
168
|
+
code || 'CONFLICT',
|
|
169
|
+
details,
|
|
170
|
+
),
|
|
171
|
+
);
|
|
155
172
|
break;
|
|
156
173
|
|
|
157
174
|
case 403:
|
|
@@ -198,11 +215,12 @@ class LicenseApiClient {
|
|
|
198
215
|
break;
|
|
199
216
|
|
|
200
217
|
default:
|
|
218
|
+
// Preserve server error code/message when available
|
|
201
219
|
reject(
|
|
202
220
|
new LicenseActivationError(
|
|
203
|
-
`Unexpected response: ${statusCode}`,
|
|
204
|
-
'UNEXPECTED_STATUS',
|
|
205
|
-
{ statusCode, response },
|
|
221
|
+
message || `Unexpected response: ${statusCode}`,
|
|
222
|
+
code || 'UNEXPECTED_STATUS',
|
|
223
|
+
details || { statusCode, response },
|
|
206
224
|
),
|
|
207
225
|
);
|
|
208
226
|
}
|
|
@@ -402,7 +420,10 @@ class LicenseApiClient {
|
|
|
402
420
|
message: response.message || 'Verification email sent. Please check your inbox.',
|
|
403
421
|
};
|
|
404
422
|
} catch (error) {
|
|
405
|
-
if (
|
|
423
|
+
if (
|
|
424
|
+
error.code === 'EMAIL_ALREADY_REGISTERED' ||
|
|
425
|
+
(error.code === 'BAD_REQUEST' && error.message.includes('already'))
|
|
426
|
+
) {
|
|
406
427
|
throw AuthError.emailAlreadyRegistered();
|
|
407
428
|
}
|
|
408
429
|
if (error.code === 'RATE_LIMITED') {
|
|
@@ -437,7 +458,14 @@ class LicenseApiClient {
|
|
|
437
458
|
emailVerified: response.emailVerified !== false,
|
|
438
459
|
};
|
|
439
460
|
} catch (error) {
|
|
440
|
-
if (error.code === '
|
|
461
|
+
if (error.code === 'EMAIL_NOT_VERIFIED') {
|
|
462
|
+
throw AuthError.emailNotVerified();
|
|
463
|
+
}
|
|
464
|
+
if (
|
|
465
|
+
error.code === 'INVALID_KEY' ||
|
|
466
|
+
error.code === 'INVALID_CREDENTIALS' ||
|
|
467
|
+
error.code === 'BAD_REQUEST'
|
|
468
|
+
) {
|
|
441
469
|
throw AuthError.invalidCredentials();
|
|
442
470
|
}
|
|
443
471
|
if (error.code === 'RATE_LIMITED') {
|