-
-
Notifications
You must be signed in to change notification settings - Fork 628
feat: Epic NOGIC Code Intelligence + Installation Health + Token Optimization #489
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
10ca803
8334b4c
dcd3e1f
ef40334
d216d22
f8a4662
10cb3b3
356e71a
82debb2
62295a3
cefdf97
0384ddf
a979d81
9af3d9e
db88179
858d1fd
1b73e84
d5c0cf3
fd396d9
c11791c
dece053
7e89ecd
0266bc5
f0a1f78
d76b4bb
5fdddb5
b2f7ebb
3581eaa
0f19f77
184850a
29f0e64
182dc85
73b58c3
b8d64d8
9b1d008
9c4a460
5ccf5da
692db85
724a964
23b18b1
ee678cb
bb24c53
d4aa0a5
540befd
6d1c16e
ac4b97d
b7e4d18
54397f7
3d7a8e2
f0b1662
372cbbd
6259058
d3e3d35
b1d5651
8b3daac
2dc19be
ee1f2f1
1f02fee
31cd46f
8ef32fe
962199e
337213d
1daf8d8
4a8d9f9
f41977c
8c92b01
bbf3b23
853b719
7c5d200
374788f
3c721d2
5513df9
1777ee8
d02df25
8af115a
12aa5c7
891b14f
40e54d5
1152f37
4eac54d
0ecbf5e
a220f87
f21dfe2
1176c89
3b61af1
f115d28
cff9be3
c4bece5
1c22492
19a6690
9533dec
ab10cae
1470b57
ac62c2b
b2eaa63
71ee0c6
780dbff
121df5d
704c513
ef94e75
39b9242
5a3c346
58db535
946f3a5
aa15bfe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| 'use strict'; | ||
|
|
||
| const path = require('path'); | ||
| const { RegistryProvider } = require('./providers/registry-provider'); | ||
|
|
||
| /** Cached provider instance (survives across hook invocations in same process). */ | ||
| let _provider = null; | ||
| let _providerRoot = null; | ||
|
|
||
| /** | ||
| * Get or create a RegistryProvider singleton. | ||
| * Resets if projectRoot changes between calls. | ||
| * @param {string} projectRoot - Project root directory | ||
| * @returns {RegistryProvider} | ||
| */ | ||
| function getProvider(projectRoot) { | ||
| if (!_provider || _providerRoot !== projectRoot) { | ||
| _provider = new RegistryProvider({ projectRoot }); | ||
| _providerRoot = projectRoot; | ||
| } | ||
| return _provider; | ||
| } | ||
|
|
||
| /** | ||
| * Resolve code intelligence context for a file being written/edited. | ||
| * | ||
| * Queries RegistryProvider for: | ||
| * - Entity definition (path, layer, purpose, type) | ||
| * - References (files that use this entity) | ||
| * - Dependencies (entities this file depends on) | ||
| * | ||
| * @param {string} filePath - Absolute or relative path to the target file | ||
| * @param {string} cwd - Project root / working directory | ||
| * @returns {{ entity: Object|null, references: Array|null, dependencies: Object|null }|null} | ||
| */ | ||
| async function resolveCodeIntel(filePath, cwd) { | ||
| if (!filePath || !cwd) return null; | ||
|
|
||
| try { | ||
| const provider = getProvider(cwd); | ||
| if (!provider.isAvailable()) return null; | ||
|
|
||
| // Normalize to relative path (registry uses relative paths) | ||
| let relativePath = filePath; | ||
| if (path.isAbsolute(filePath)) { | ||
| relativePath = path.relative(cwd, filePath).replace(/\\/g, '/'); | ||
| } else { | ||
| relativePath = filePath.replace(/\\/g, '/'); | ||
| } | ||
|
|
||
| // Run all three queries in parallel | ||
| const [definition, references, dependencies] = await Promise.all([ | ||
| provider.findDefinition(relativePath), | ||
| provider.findReferences(relativePath), | ||
| provider.analyzeDependencies(relativePath), | ||
| ]); | ||
|
|
||
| // Treat empty dependency graph as no data | ||
| const hasUsefulDeps = dependencies && dependencies.nodes && dependencies.nodes.length > 0; | ||
|
|
||
| // If nothing found at all, try searching by the file basename | ||
| if (!definition && !references && !hasUsefulDeps) { | ||
| const basename = path.basename(relativePath, path.extname(relativePath)); | ||
| const fallbackDef = await provider.findDefinition(basename); | ||
| if (!fallbackDef) return null; | ||
|
|
||
| const [fallbackRefs, fallbackDeps] = await Promise.all([ | ||
| provider.findReferences(basename), | ||
| provider.analyzeDependencies(basename), | ||
| ]); | ||
|
|
||
| return { | ||
| entity: fallbackDef, | ||
| references: fallbackRefs, | ||
| dependencies: fallbackDeps, | ||
| }; | ||
| } | ||
|
|
||
| return { | ||
| entity: definition, | ||
| references, | ||
| dependencies, | ||
| }; | ||
| } catch (_err) { | ||
| // Guard against provider exceptions to avoid unhandled rejections in hook runtime | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Format code intelligence data as XML for injection into Claude context. | ||
| * | ||
| * @param {Object|null} intel - Result from resolveCodeIntel() | ||
| * @param {string} filePath - Target file path (for display) | ||
| * @returns {string|null} XML string or null if no data | ||
| */ | ||
| function formatAsXml(intel, filePath) { | ||
| if (!intel) return null; | ||
|
|
||
| const { entity, references, dependencies } = intel; | ||
|
|
||
| // At least one piece of data must exist | ||
| if (!entity && !references && !dependencies) return null; | ||
|
|
||
| const lines = ['<code-intel-context>']; | ||
| lines.push(` <target-file>${escapeXml(filePath)}</target-file>`); | ||
|
|
||
| // Entity definition | ||
| if (entity) { | ||
| lines.push(' <existing-entity>'); | ||
| if (entity.file) lines.push(` <path>${escapeXml(entity.file)}</path>`); | ||
| if (entity.context) lines.push(` <purpose>${escapeXml(entity.context)}</purpose>`); | ||
| lines.push(' </existing-entity>'); | ||
| } | ||
|
|
||
| // References | ||
| if (references && references.length > 0) { | ||
| // Deduplicate by file path | ||
| const uniqueRefs = []; | ||
| const seen = new Set(); | ||
| for (const ref of references) { | ||
| if (ref.file && !seen.has(ref.file)) { | ||
| seen.add(ref.file); | ||
| uniqueRefs.push(ref); | ||
| } | ||
| } | ||
|
|
||
| lines.push(` <referenced-by count="${uniqueRefs.length}">`); | ||
| for (const ref of uniqueRefs.slice(0, 15)) { | ||
| const ctx = ref.context ? ` context="${escapeXml(ref.context)}"` : ''; | ||
| lines.push(` <ref file="${escapeXml(ref.file)}"${ctx} />`); | ||
| } | ||
| if (uniqueRefs.length > 15) { | ||
| lines.push(` <!-- ...and ${uniqueRefs.length - 15} more -->`); | ||
| } | ||
| lines.push(' </referenced-by>'); | ||
| } | ||
|
|
||
| // Dependencies | ||
| if (dependencies && dependencies.nodes && dependencies.nodes.length > 1) { | ||
| // First node is the target itself, rest are dependencies | ||
| const depNodes = dependencies.nodes.slice(1); | ||
| lines.push(` <dependencies count="${depNodes.length}">`); | ||
| for (const dep of depNodes.slice(0, 10)) { | ||
| const layer = dep.layer ? ` layer="${dep.layer}"` : ''; | ||
| lines.push(` <dep name="${escapeXml(dep.name)}"${layer} />`); | ||
| } | ||
| if (depNodes.length > 10) { | ||
| lines.push(` <!-- ...and ${depNodes.length - 10} more -->`); | ||
| } | ||
| lines.push(' </dependencies>'); | ||
| } | ||
|
|
||
| lines.push('</code-intel-context>'); | ||
| return lines.join('\n'); | ||
| } | ||
|
|
||
| /** | ||
| * Escape special XML characters. | ||
| * @param {string} str | ||
| * @returns {string} | ||
| */ | ||
| function escapeXml(str) { | ||
| if (!str) return ''; | ||
| return String(str) | ||
| .replace(/&/g, '&') | ||
| .replace(/</g, '<') | ||
| .replace(/>/g, '>') | ||
| .replace(/"/g, '"'); | ||
| } | ||
|
|
||
| /** | ||
| * Reset cached provider (for testing). | ||
| */ | ||
| function _resetForTesting() { | ||
| _provider = null; | ||
| _providerRoot = null; | ||
| } | ||
|
|
||
| module.exports = { | ||
| resolveCodeIntel, | ||
| formatAsXml, | ||
| escapeXml, | ||
| getProvider, | ||
| _resetForTesting, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,15 @@ class CodeIntelProvider { | |
| this.options = options; | ||
| } | ||
|
|
||
| /** | ||
| * Check if this provider is available and can serve requests. | ||
| * Subclasses MUST override this to indicate availability. | ||
| * @returns {boolean} | ||
| */ | ||
| isAvailable() { | ||
| return false; | ||
| } | ||
|
Comment on lines
+17
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find all CodeIntelProvider subclasses
echo "=== Finding all classes extending CodeIntelProvider ==="
rg -n "extends CodeIntelProvider" -g '*.js'
echo -e "\n=== Finding all isAvailable() implementations ==="
rg -n "isAvailable\s*\(" -g '*.js' .aios-core/core/code-intel/providers/
echo -e "\n=== Check provider-interface.js file to understand the base class ==="
head -40 .aios-core/core/code-intel/providers/provider-interface.jsRepository: SynkraAI/aios-core Length of output: 1737 🏁 Script executed: #!/bin/bash
# List all files in the providers directory
echo "=== All files in providers directory ==="
find .aios-core/core/code-intel/providers -type f -name '*.js' | sort
echo -e "\n=== Check each provider for class definition and isAvailable ==="
for file in $(find .aios-core/core/code-intel/providers -type f -name '*.js' | grep -v test); do
echo "--- $file ---"
grep -n "^class\|isAvailable" "$file" | head -5
done
echo -e "\n=== Search for all imports of CodeIntelProvider to find subclasses ==="
rg -B2 "extends CodeIntelProvider" -g '*.js' --type js
echo -e "\n=== Check how providers are exported and used ==="
cat .aios-core/core/code-intel/providers/index.js 2>/dev/null || echo "No index.js found"Repository: SynkraAI/aios-core Length of output: 1083 🏁 Script executed: #!/bin/bash
# Find where isAvailable() is being called/used
echo "=== Where isAvailable() is being called ==="
rg "isAvailable\(\)" -B2 -A2 -g '*.js' | grep -v ".test.js" | head -50
echo -e "\n=== Check the code-intel registry or selection logic ==="
fd -t f -name '*registry*' .aios-core/core/code-intel/ -o -name '*select*' .aios-core/core/code-intel/
echo -e "\n=== Look for provider selection/filtering logic ==="
rg "provider.*isAvailable|isAvailable.*provider" -B3 -A3 -g '*.js' | head -60Repository: SynkraAI/aios-core Length of output: 5339 🏁 Script executed: #!/bin/bash
# Search for any selection or orchestration logic
echo "=== Search for provider selection logic ==="
rg "provider" -g '*.js' .aios-core/core/code-intel/ -B2 -A2 | grep -i "select\|choose\|filter\|available" | head -30
echo -e "\n=== Look in code-intel index or orchestration files ==="
find .aios-core/core/code-intel -type f -name '*.js' | grep -v test | xargs wc -l | sort -rn | head -10
echo -e "\n=== Check the code-intel module structure ==="
ls -la .aios-core/core/code-intel/
echo -e "\n=== Look for any references to 'isAvailable' in all production code ==="
rg "\.isAvailable" -g '*.js' .aios-core/core/ | grep -v testRepository: SynkraAI/aios-core Length of output: 4266 🏁 Script executed: #!/bin/bash
# Get the full context of the selection flow in code-intel-client.js
echo "=== code-intel-client.js - getAvailableProvider method ==="
rg -B5 -A10 "First provider with isAvailable" .aios-core/core/code-intel/code-intel-client.js
echo -e "\n=== hook-runtime.js - where isAvailable is called ==="
rg -B5 -A5 "if (!provider.isAvailable())" .aios-core/core/code-intel/hook-runtime.js
echo -e "\n=== Check how providers are instantiated and registered ==="
head -100 .aios-core/core/code-intel/code-intel-client.js | tail -80Repository: SynkraAI/aios-core Length of output: 3234 🏁 Script executed: #!/bin/bash
# Check the full _detectProvider method and fallback behavior
echo "=== Full _detectProvider method ==="
rg -A20 "_detectProvider\(\)" .aios-core/core/code-intel/code-intel-client.js
echo -e "\n=== Check what happens when no provider available ==="
rg -B3 -A3 "No provider available" .aios-core/core/code-intel/code-intel-client.jsRepository: SynkraAI/aios-core Length of output: 1925 Confirm all providers override All existing providers (RegistryProvider, CodeGraphProvider) properly implement 🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
| * Locate the definition of a symbol. | ||
| * @param {string} symbol - Symbol name to find | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Singleton ignores
projectRootafter first initialization.getProvidercaches the firstRegistryProviderinstance and returns it for all subsequent calls regardless of theprojectRootargument. If this module is ever called with different project roots within the same process (e.g., a multi-workspace scenario), the second call silently uses the wrong project's registry.🔧 Suggested fix: key the cache by projectRoot
🤖 Prompt for AI Agents