alp-code 0.9.0
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/CHANGELOG.md +770 -0
- package/LICENSE +21 -0
- package/README.md +295 -0
- package/alp.config.yaml +5 -0
- package/dist/src/agents/agent-definition.js +28 -0
- package/dist/src/agents/capability-catalog.js +33 -0
- package/dist/src/agents/compaction.js +36 -0
- package/dist/src/agents/errors.js +12 -0
- package/dist/src/agents/librarian.js +38 -0
- package/dist/src/agents/main.js +37 -0
- package/dist/src/agents/memory-grant.js +29 -0
- package/dist/src/agents/model-context.js +70 -0
- package/dist/src/agents/modes.js +134 -0
- package/dist/src/agents/oracle.js +36 -0
- package/dist/src/agents/read-thread.js +38 -0
- package/dist/src/agents/registry.js +238 -0
- package/dist/src/agents/render-identity.js +38 -0
- package/dist/src/agents/review.js +37 -0
- package/dist/src/agents/search.js +37 -0
- package/dist/src/agents/shared/house-rules.js +33 -0
- package/dist/src/agents/shared/principal.js +18 -0
- package/dist/src/agents/shared/voice.js +29 -0
- package/dist/src/agents/titling.js +32 -0
- package/dist/src/agents/types.js +15 -0
- package/dist/src/backend/execution-backend.js +2 -0
- package/dist/src/backend/local-execution-store.js +144 -0
- package/dist/src/backend/local-process-backend.js +533 -0
- package/dist/src/backend/local-supervisor.js +104 -0
- package/dist/src/cli/alp.js +380 -0
- package/dist/src/cli/commands/context.js +203 -0
- package/dist/src/cli/commands/delegate.js +136 -0
- package/dist/src/cli/commands/identity-sync.js +31 -0
- package/dist/src/cli/commands/init.js +184 -0
- package/dist/src/cli/commands/mode.js +22 -0
- package/dist/src/cli/commands/principal.js +114 -0
- package/dist/src/cli/commands/run-main.js +90 -0
- package/dist/src/cli/commands/runtime.js +21 -0
- package/dist/src/cli/mode-preference-store.js +62 -0
- package/dist/src/cli/mode-selector.js +178 -0
- package/dist/src/cli/update-check.js +77 -0
- package/dist/src/context/checkpoint.js +134 -0
- package/dist/src/context/compact-journal.js +153 -0
- package/dist/src/context/compact-payload.js +121 -0
- package/dist/src/context/continuity.js +70 -0
- package/dist/src/context/types.js +2 -0
- package/dist/src/delegation/backend-registry.js +40 -0
- package/dist/src/delegation/delegation-service.js +300 -0
- package/dist/src/delegation/types.js +12 -0
- package/dist/src/execution/execution-policy.js +96 -0
- package/dist/src/execution/execution-service.js +115 -0
- package/dist/src/execution/execution-store.js +78 -0
- package/dist/src/execution/identity-capsule.js +65 -0
- package/dist/src/execution/types.js +12 -0
- package/dist/src/hooks/execution-bridge.js +84 -0
- package/dist/src/index.js +4 -0
- package/dist/src/memory/adapters/markdown-file-store.js +257 -0
- package/dist/src/memory/adapters/memory-api-client.js +2 -0
- package/dist/src/memory/adapters/memory-path-mapper.js +76 -0
- package/dist/src/memory/adapters/remote-api-store.js +25 -0
- package/dist/src/memory/context-ranker.js +21 -0
- package/dist/src/memory/errors.js +58 -0
- package/dist/src/memory/memory-service.js +149 -0
- package/dist/src/memory/memory-store.js +2 -0
- package/dist/src/memory/types.js +2 -0
- package/dist/src/policy/capability-policy.js +29 -0
- package/dist/src/policy/delegation-policy.js +25 -0
- package/dist/src/policy/errors.js +10 -0
- package/dist/src/policy/invariants.js +31 -0
- package/dist/src/policy/memory-policy.js +22 -0
- package/dist/src/policy/policy-engine.js +85 -0
- package/dist/src/policy/types.js +8 -0
- package/dist/src/policy/workspace-policy.js +77 -0
- package/dist/src/principal/principal-profile-store.js +89 -0
- package/dist/src/runtime/adapter-files.js +147 -0
- package/dist/src/runtime/claude-adapter.js +177 -0
- package/dist/src/runtime/codex-adapter.js +169 -0
- package/dist/src/runtime/permission-rules.js +156 -0
- package/dist/src/runtime/render-session-context.js +124 -0
- package/dist/src/runtime/render-task-input.js +33 -0
- package/dist/src/runtime/runtime-adapter.js +2 -0
- package/dist/src/runtime/runtime-preference-store.js +66 -0
- package/dist/src/runtime/runtime-selector.js +178 -0
- package/dist/src/runtime/types.js +2 -0
- package/dist/src/runtime/windows-shim.js +57 -0
- package/dist/src/state-paths.js +49 -0
- package/dist/src/workflow/output-validator.js +27 -0
- package/dist/src/workflow/repair-policy.js +8 -0
- package/dist/src/workflow/types.js +22 -0
- package/dist/src/workflow/workflow-runner.js +81 -0
- package/hooks/compact-record.cjs +109 -0
- package/hooks/session-boot.cjs +112 -0
- package/hooks/session-end.cjs +34 -0
- package/package.json +48 -0
- package/scaffold/memory/INDEX.md +27 -0
- package/scaffold/memory/README.md +76 -0
- package/scaffold/memory/projects/INDEX.md +22 -0
- package/scaffold/memory/projects/PROTOCOL.md +128 -0
- package/scaffold/memory/projects/_template/PROJECT.md +45 -0
- package/scripts/alp.cjs +126 -0
- package/scripts/alp.ps1 +4 -0
- package/scripts/alp.sh +3 -0
- package/scripts/bootstrap.cjs +144 -0
- package/scripts/checkout-release.cjs +30 -0
- package/scripts/delegate.cjs +19 -0
- package/scripts/doctor.cjs +158 -0
- package/scripts/doctor.sh +3 -0
- package/scripts/ensure-state.cjs +22 -0
- package/scripts/lib/cli-link.cjs +375 -0
- package/scripts/lib/codex-role.cjs +18 -0
- package/scripts/lib/delegation/command-runner.cjs +108 -0
- package/scripts/lib/delegation/config.cjs +81 -0
- package/scripts/lib/install-paths.cjs +154 -0
- package/scripts/lib/release-manifest.cjs +42 -0
- package/scripts/lib/semver-lite.cjs +20 -0
- package/scripts/lib/state.cjs +274 -0
- package/scripts/lib/uninstall.cjs +252 -0
- package/scripts/lib/update-check-worker.cjs +21 -0
- package/scripts/lib/update.cjs +395 -0
- package/scripts/run-role.cjs +42 -0
- package/scripts/run-role.ps1 +4 -0
- package/scripts/run-role.sh +3 -0
- package/scripts/sync-project-index.sh +167 -0
- package/skills/agent-memory/SKILL.md +109 -0
- package/skills/alp-debug/SKILL.md +90 -0
- package/skills/alp-debug/references/defense-in-depth.md +118 -0
- package/skills/alp-debug/references/investigation-methodology.md +106 -0
- package/skills/alp-debug/references/log-and-ci-analysis.md +96 -0
- package/skills/alp-debug/references/performance-diagnostics.md +112 -0
- package/skills/alp-debug/references/reporting-standards.md +120 -0
- package/skills/alp-debug/references/root-cause-tracing.md +134 -0
- package/skills/alp-debug/references/systematic-debugging.md +93 -0
- package/skills/alp-debug/references/verification.md +86 -0
- package/skills/alp-debug/scripts/find-polluter.sh +63 -0
- package/skills/alp-debug/scripts/find-polluter.test.md +102 -0
- package/skills/alp-plan/SKILL.md +128 -0
- package/skills/alp-plan/references/archive-workflow.md +77 -0
- package/skills/alp-plan/references/codebase-understanding.md +55 -0
- package/skills/alp-plan/references/output-standards.md +96 -0
- package/skills/alp-plan/references/plan-organization.md +129 -0
- package/skills/alp-plan/references/red-team-personas.md +76 -0
- package/skills/alp-plan/references/red-team-workflow.md +81 -0
- package/skills/alp-plan/references/research-phase.md +57 -0
- package/skills/alp-plan/references/scope-challenge.md +82 -0
- package/skills/alp-plan/references/solution-design.md +76 -0
- package/skills/alp-plan/references/validate-question-framework.md +89 -0
- package/skills/alp-plan/references/validate-workflow.md +83 -0
- package/skills/alp-predict/SKILL.md +98 -0
- package/skills/alp-scenario/SKILL.md +86 -0
- package/skills/code-review/SKILL.md +111 -0
- package/skills/code-review/references/code-review-reception.md +114 -0
- package/skills/code-review/references/edge-case-scouting.md +78 -0
- package/skills/code-review/references/verification-before-completion.md +117 -0
- package/skills/delegation/SKILL.md +46 -0
- package/skills/docs-seeker/.env.example +15 -0
- package/skills/docs-seeker/SKILL.md +87 -0
- package/skills/docs-seeker/package.json +25 -0
- package/skills/docs-seeker/references/advanced.md +82 -0
- package/skills/docs-seeker/references/context7-patterns.md +68 -0
- package/skills/docs-seeker/references/errors.md +72 -0
- package/skills/docs-seeker/scripts/analyze-llms-txt.js +211 -0
- package/skills/docs-seeker/scripts/detect-topic.js +172 -0
- package/skills/docs-seeker/scripts/fetch-docs.js +213 -0
- package/skills/docs-seeker/scripts/tests/run-tests.js +72 -0
- package/skills/docs-seeker/scripts/tests/test-analyze-llms.js +119 -0
- package/skills/docs-seeker/scripts/tests/test-detect-topic.js +112 -0
- package/skills/docs-seeker/scripts/tests/test-fetch-docs.js +84 -0
- package/skills/docs-seeker/scripts/utils/env-loader.js +94 -0
- package/skills/docs-seeker/workflows/library-search.md +73 -0
- package/skills/docs-seeker/workflows/repo-analysis.md +90 -0
- package/skills/docs-seeker/workflows/topic-search.md +69 -0
- package/skills/git/SKILL.md +121 -0
- package/skills/git/references/branch-management.md +90 -0
- package/skills/git/references/commit-standards.md +82 -0
- package/skills/git/references/gh-cli-guide.md +132 -0
- package/skills/git/references/safety-protocols.md +86 -0
- package/skills/git/references/workflow-commit.md +89 -0
- package/skills/git/references/workflow-merge.md +63 -0
- package/skills/git/references/workflow-pr.md +70 -0
- package/skills/git/references/workflow-push.md +62 -0
- package/skills/gkg/SKILL.md +87 -0
- package/skills/gkg/references/cli-commands.md +92 -0
- package/skills/gkg/references/http-api.md +99 -0
- package/skills/gkg/references/language-support.md +54 -0
- package/skills/problem-solving/SKILL.md +86 -0
- package/skills/problem-solving/references/attribution.md +48 -0
- package/skills/problem-solving/references/collision-zone-thinking.md +71 -0
- package/skills/problem-solving/references/inversion-exercise.md +88 -0
- package/skills/problem-solving/references/meta-pattern-recognition.md +80 -0
- package/skills/problem-solving/references/scale-game.md +82 -0
- package/skills/problem-solving/references/simplification-cascades.md +83 -0
- package/skills/problem-solving/references/when-stuck.md +76 -0
- package/skills/repomix/SKILL.md +94 -0
- package/skills/repomix/references/configuration.md +134 -0
- package/skills/repomix/references/usage-patterns.md +106 -0
- package/skills/repomix/scripts/.coverage +0 -0
- package/skills/repomix/scripts/README.md +179 -0
- package/skills/repomix/scripts/repomix_batch.py +455 -0
- package/skills/repomix/scripts/repos.example.json +15 -0
- package/skills/repomix/scripts/requirements.txt +15 -0
- package/skills/repomix/scripts/tests/test_repomix_batch.py +531 -0
- package/skills/research/SKILL.md +107 -0
- package/skills/security-scan/SKILL.md +101 -0
- package/skills/security-scan/references/secret-patterns.md +75 -0
- package/skills/security-scan/references/vulnerability-patterns.md +136 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Documentation Fetcher Script
|
|
5
|
+
* Fetches documentation from context7.com with topic support and fallback chain
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const https = require('https');
|
|
9
|
+
const { loadEnv } = require('./utils/env-loader');
|
|
10
|
+
const { detectTopic } = require('./detect-topic');
|
|
11
|
+
|
|
12
|
+
// Load environment
|
|
13
|
+
const env = loadEnv();
|
|
14
|
+
const DEBUG = env.DEBUG === 'true';
|
|
15
|
+
const API_KEY = env.CONTEXT7_API_KEY;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Make HTTPS GET request
|
|
19
|
+
* @param {string} url - URL to fetch
|
|
20
|
+
* @returns {Promise<string>} Response body
|
|
21
|
+
*/
|
|
22
|
+
function httpsGet(url) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const options = {
|
|
25
|
+
headers: API_KEY ? { 'Authorization': `Bearer ${API_KEY}` } : {},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
https.get(url, options, (res) => {
|
|
29
|
+
let data = '';
|
|
30
|
+
|
|
31
|
+
res.on('data', (chunk) => {
|
|
32
|
+
data += chunk;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
res.on('end', () => {
|
|
36
|
+
if (res.statusCode === 200) {
|
|
37
|
+
resolve(data);
|
|
38
|
+
} else if (res.statusCode === 404) {
|
|
39
|
+
resolve(null);
|
|
40
|
+
} else {
|
|
41
|
+
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}).on('error', reject);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Construct context7.com URL
|
|
50
|
+
* @param {string} library - Library name (e.g., "next.js", "shadcn/ui")
|
|
51
|
+
* @param {string} topic - Optional topic keyword
|
|
52
|
+
* @returns {string} context7.com URL
|
|
53
|
+
*/
|
|
54
|
+
function buildContext7Url(library, topic = null) {
|
|
55
|
+
// Determine if GitHub repo or website
|
|
56
|
+
let basePath;
|
|
57
|
+
|
|
58
|
+
if (library.includes('/')) {
|
|
59
|
+
// GitHub repo format: org/repo
|
|
60
|
+
const [org, repo] = library.split('/');
|
|
61
|
+
basePath = `${org}/${repo}`;
|
|
62
|
+
} else {
|
|
63
|
+
// Try common patterns
|
|
64
|
+
const normalized = library.toLowerCase().replace(/[^a-z0-9-]/g, '');
|
|
65
|
+
basePath = `websites/${normalized}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const baseUrl = `https://context7.com/${basePath}/llms.txt`;
|
|
69
|
+
|
|
70
|
+
if (topic) {
|
|
71
|
+
return `${baseUrl}?topic=${encodeURIComponent(topic)}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return baseUrl;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Try multiple URL variations for a library
|
|
79
|
+
* @param {string} library - Library name
|
|
80
|
+
* @param {string} topic - Optional topic
|
|
81
|
+
* @returns {Promise<Array>} Array of URLs to try
|
|
82
|
+
*/
|
|
83
|
+
async function getUrlVariations(library, topic = null) {
|
|
84
|
+
const urls = [];
|
|
85
|
+
|
|
86
|
+
// Known repo mappings
|
|
87
|
+
const knownRepos = {
|
|
88
|
+
'next.js': 'vercel/next.js',
|
|
89
|
+
'nextjs': 'vercel/next.js',
|
|
90
|
+
'remix': 'remix-run/remix',
|
|
91
|
+
'astro': 'withastro/astro',
|
|
92
|
+
'shadcn': 'shadcn-ui/ui',
|
|
93
|
+
'shadcn/ui': 'shadcn-ui/ui',
|
|
94
|
+
'better-auth': 'better-auth/better-auth',
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const normalized = library.toLowerCase();
|
|
98
|
+
const repo = knownRepos[normalized] || library;
|
|
99
|
+
|
|
100
|
+
// Primary: Try with topic if available
|
|
101
|
+
if (topic) {
|
|
102
|
+
urls.push(buildContext7Url(repo, topic));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Fallback: Try without topic
|
|
106
|
+
urls.push(buildContext7Url(repo));
|
|
107
|
+
|
|
108
|
+
return urls;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Fetch documentation from context7.com
|
|
113
|
+
* @param {string} query - User query
|
|
114
|
+
* @returns {Promise<Object>} Documentation result
|
|
115
|
+
*/
|
|
116
|
+
async function fetchDocs(query) {
|
|
117
|
+
const topicInfo = detectTopic(query);
|
|
118
|
+
|
|
119
|
+
if (DEBUG) {
|
|
120
|
+
console.error('[DEBUG] Topic detection result:', topicInfo);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let urls = [];
|
|
124
|
+
|
|
125
|
+
if (topicInfo && topicInfo.isTopicSpecific) {
|
|
126
|
+
// Topic-specific search
|
|
127
|
+
urls = await getUrlVariations(topicInfo.library, topicInfo.topic);
|
|
128
|
+
|
|
129
|
+
if (DEBUG) {
|
|
130
|
+
console.error('[DEBUG] Topic-specific URLs:', urls);
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
// Extract library from general query
|
|
134
|
+
const libraryMatch = query.match(/(?:documentation|docs|guide) (?:for )?(.+)/i);
|
|
135
|
+
if (libraryMatch) {
|
|
136
|
+
const library = libraryMatch[1].trim();
|
|
137
|
+
urls = await getUrlVariations(library);
|
|
138
|
+
|
|
139
|
+
if (DEBUG) {
|
|
140
|
+
console.error('[DEBUG] General library URLs:', urls);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Try each URL
|
|
146
|
+
for (const url of urls) {
|
|
147
|
+
if (DEBUG) {
|
|
148
|
+
console.error(`[DEBUG] Trying URL: ${url}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const content = await httpsGet(url);
|
|
153
|
+
|
|
154
|
+
if (content) {
|
|
155
|
+
return {
|
|
156
|
+
success: true,
|
|
157
|
+
source: 'context7.com',
|
|
158
|
+
url,
|
|
159
|
+
content,
|
|
160
|
+
topicSpecific: url.includes('?topic='),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (DEBUG) {
|
|
165
|
+
console.error(`[DEBUG] Failed to fetch ${url}:`, error.message);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// No URL worked
|
|
171
|
+
return {
|
|
172
|
+
success: false,
|
|
173
|
+
source: 'context7.com',
|
|
174
|
+
error: 'Documentation not found on context7.com',
|
|
175
|
+
urls,
|
|
176
|
+
suggestion: 'Try repository analysis or web search',
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* CLI entry point
|
|
182
|
+
*/
|
|
183
|
+
async function main() {
|
|
184
|
+
const args = process.argv.slice(2);
|
|
185
|
+
|
|
186
|
+
if (args.length === 0) {
|
|
187
|
+
console.error('Usage: node fetch-docs.js "<user query>"');
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const query = args.join(' ');
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const result = await fetchDocs(query);
|
|
195
|
+
console.log(JSON.stringify(result, null, 2));
|
|
196
|
+
process.exit(result.success ? 0 : 1);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.error('Error:', error.message);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Run if called directly
|
|
204
|
+
if (require.main === module) {
|
|
205
|
+
main();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
module.exports = {
|
|
209
|
+
fetchDocs,
|
|
210
|
+
buildContext7Url,
|
|
211
|
+
getUrlVariations,
|
|
212
|
+
httpsGet,
|
|
213
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test runner - runs all tests
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { spawn } = require('child_process');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const tests = [
|
|
11
|
+
'test-detect-topic.js',
|
|
12
|
+
'test-fetch-docs.js',
|
|
13
|
+
'test-analyze-llms.js',
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
let totalPassed = 0;
|
|
17
|
+
let totalFailed = 0;
|
|
18
|
+
|
|
19
|
+
function runTest(testFile) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
22
|
+
console.log(`Running: ${testFile}`);
|
|
23
|
+
console.log('='.repeat(60));
|
|
24
|
+
|
|
25
|
+
const testPath = path.join(__dirname, testFile);
|
|
26
|
+
const proc = spawn('node', [testPath], {
|
|
27
|
+
stdio: 'inherit',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
proc.on('close', (code) => {
|
|
31
|
+
if (code === 0) {
|
|
32
|
+
resolve();
|
|
33
|
+
} else {
|
|
34
|
+
reject(new Error(`Test failed: ${testFile}`));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
proc.on('error', reject);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function runAllTests() {
|
|
43
|
+
console.log('Running all docs-seeker tests...');
|
|
44
|
+
|
|
45
|
+
let failedTests = [];
|
|
46
|
+
|
|
47
|
+
for (const test of tests) {
|
|
48
|
+
try {
|
|
49
|
+
await runTest(test);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
failedTests.push(test);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
56
|
+
console.log('All Tests Summary');
|
|
57
|
+
console.log('='.repeat(60));
|
|
58
|
+
console.log(`Total test files: ${tests.length}`);
|
|
59
|
+
console.log(`Passed: ${tests.length - failedTests.length}`);
|
|
60
|
+
console.log(`Failed: ${failedTests.length}`);
|
|
61
|
+
|
|
62
|
+
if (failedTests.length > 0) {
|
|
63
|
+
console.log('\nFailed tests:');
|
|
64
|
+
failedTests.forEach((test) => console.log(` - ${test}`));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
} else {
|
|
67
|
+
console.log('\n✓ All tests passed!');
|
|
68
|
+
process.exit(0);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
runAllTests();
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tests for analyze-llms-txt.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
analyzeLlmsTxt,
|
|
9
|
+
parseUrls,
|
|
10
|
+
groupByPriority,
|
|
11
|
+
categorizeUrl,
|
|
12
|
+
suggestAgentDistribution,
|
|
13
|
+
} = require('../analyze-llms-txt');
|
|
14
|
+
|
|
15
|
+
// Test counter
|
|
16
|
+
let passed = 0;
|
|
17
|
+
let failed = 0;
|
|
18
|
+
|
|
19
|
+
function assert(condition, message) {
|
|
20
|
+
if (condition) {
|
|
21
|
+
console.log(`✓ ${message}`);
|
|
22
|
+
passed++;
|
|
23
|
+
} else {
|
|
24
|
+
console.error(`✗ ${message}`);
|
|
25
|
+
failed++;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function assertEqual(actual, expected, message) {
|
|
30
|
+
if (actual === expected) {
|
|
31
|
+
console.log(`✓ ${message}`);
|
|
32
|
+
passed++;
|
|
33
|
+
} else {
|
|
34
|
+
console.error(`✗ ${message}`);
|
|
35
|
+
console.error(` Expected: ${expected}`);
|
|
36
|
+
console.error(` Actual: ${actual}`);
|
|
37
|
+
failed++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log('Running analyze-llms-txt.js tests...\n');
|
|
42
|
+
|
|
43
|
+
// Test categorizeUrl
|
|
44
|
+
console.log('## Testing categorizeUrl()');
|
|
45
|
+
assertEqual(categorizeUrl('https://docs.example.com/getting-started'), 'critical', 'Categorize getting-started as critical');
|
|
46
|
+
assertEqual(categorizeUrl('https://docs.example.com/guide/routing'), 'important', 'Categorize routing guide as important');
|
|
47
|
+
assertEqual(categorizeUrl('https://docs.example.com/advanced/internals'), 'supplementary', 'Categorize internals as supplementary');
|
|
48
|
+
assertEqual(categorizeUrl('https://docs.example.com/api-reference'), 'important', 'Categorize API reference as important');
|
|
49
|
+
|
|
50
|
+
// Test parseUrls
|
|
51
|
+
console.log('\n## Testing parseUrls()');
|
|
52
|
+
|
|
53
|
+
const sampleContent = `# Documentation
|
|
54
|
+
https://docs.example.com/getting-started
|
|
55
|
+
https://docs.example.com/guide
|
|
56
|
+
# Comment line
|
|
57
|
+
https://docs.example.com/api-reference
|
|
58
|
+
|
|
59
|
+
https://docs.example.com/advanced
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
const urls = parseUrls(sampleContent);
|
|
63
|
+
assertEqual(urls.length, 4, 'Parse 4 URLs from content');
|
|
64
|
+
assert(urls[0].includes('getting-started'), 'First URL is getting-started');
|
|
65
|
+
|
|
66
|
+
const emptyContent = '';
|
|
67
|
+
const emptyUrls = parseUrls(emptyContent);
|
|
68
|
+
assertEqual(emptyUrls.length, 0, 'Empty content returns 0 URLs');
|
|
69
|
+
|
|
70
|
+
// Test groupByPriority
|
|
71
|
+
console.log('\n## Testing groupByPriority()');
|
|
72
|
+
|
|
73
|
+
const testUrls = [
|
|
74
|
+
'https://docs.example.com/getting-started',
|
|
75
|
+
'https://docs.example.com/guide/routing',
|
|
76
|
+
'https://docs.example.com/advanced/internals',
|
|
77
|
+
'https://docs.example.com/installation',
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
const grouped = groupByPriority(testUrls);
|
|
81
|
+
assert(grouped.critical.length >= 2, 'Has critical URLs');
|
|
82
|
+
assert(grouped.important.length >= 1, 'Has important URLs');
|
|
83
|
+
assert(grouped.supplementary.length >= 1, 'Has supplementary URLs');
|
|
84
|
+
|
|
85
|
+
// Test suggestAgentDistribution
|
|
86
|
+
console.log('\n## Testing suggestAgentDistribution()');
|
|
87
|
+
|
|
88
|
+
const dist1 = suggestAgentDistribution(2);
|
|
89
|
+
assertEqual(dist1.agentCount, 1, 'Suggest 1 agent for 2 URLs');
|
|
90
|
+
assertEqual(dist1.strategy, 'single', 'Strategy is single for few URLs');
|
|
91
|
+
|
|
92
|
+
const dist2 = suggestAgentDistribution(8);
|
|
93
|
+
assert(dist2.agentCount >= 3 && dist2.agentCount <= 5, 'Suggest 3-5 agents for 8 URLs');
|
|
94
|
+
assertEqual(dist2.strategy, 'parallel', 'Strategy is parallel for medium URLs');
|
|
95
|
+
|
|
96
|
+
const dist3 = suggestAgentDistribution(15);
|
|
97
|
+
assertEqual(dist3.agentCount, 7, 'Suggest 7 agents for 15 URLs');
|
|
98
|
+
|
|
99
|
+
const dist4 = suggestAgentDistribution(25);
|
|
100
|
+
assertEqual(dist4.agentCount, 7, 'Suggest 7 agents for 25 URLs');
|
|
101
|
+
assertEqual(dist4.strategy, 'phased', 'Strategy is phased for many URLs');
|
|
102
|
+
assertEqual(dist4.phases, 2, 'Use 2 phases for large sets');
|
|
103
|
+
|
|
104
|
+
// Test analyzeLlmsTxt
|
|
105
|
+
console.log('\n## Testing analyzeLlmsTxt()');
|
|
106
|
+
|
|
107
|
+
const analysis = analyzeLlmsTxt(sampleContent);
|
|
108
|
+
assertEqual(analysis.totalUrls, 4, 'Analysis counts 4 URLs');
|
|
109
|
+
assert(analysis.grouped, 'Analysis includes grouped URLs');
|
|
110
|
+
assert(analysis.distribution, 'Analysis includes distribution suggestion');
|
|
111
|
+
assert(analysis.summary, 'Analysis includes summary');
|
|
112
|
+
|
|
113
|
+
// Summary
|
|
114
|
+
console.log('\n## Test Summary');
|
|
115
|
+
console.log(`Passed: ${passed}`);
|
|
116
|
+
console.log(`Failed: ${failed}`);
|
|
117
|
+
console.log(`Total: ${passed + failed}`);
|
|
118
|
+
|
|
119
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tests for detect-topic.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { detectTopic, normalizeTopic, normalizeLibrary } = require('../detect-topic');
|
|
8
|
+
|
|
9
|
+
// Test counter
|
|
10
|
+
let passed = 0;
|
|
11
|
+
let failed = 0;
|
|
12
|
+
|
|
13
|
+
function assert(condition, message) {
|
|
14
|
+
if (condition) {
|
|
15
|
+
console.log(`✓ ${message}`);
|
|
16
|
+
passed++;
|
|
17
|
+
} else {
|
|
18
|
+
console.error(`✗ ${message}`);
|
|
19
|
+
failed++;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function assertEqual(actual, expected, message) {
|
|
24
|
+
if (actual === expected) {
|
|
25
|
+
console.log(`✓ ${message}`);
|
|
26
|
+
passed++;
|
|
27
|
+
} else {
|
|
28
|
+
console.error(`✗ ${message}`);
|
|
29
|
+
console.error(` Expected: ${expected}`);
|
|
30
|
+
console.error(` Actual: ${actual}`);
|
|
31
|
+
failed++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log('Running detect-topic.js tests...\n');
|
|
36
|
+
|
|
37
|
+
// Test normalizeTopic
|
|
38
|
+
console.log('## Testing normalizeTopic()');
|
|
39
|
+
assertEqual(normalizeTopic('date picker'), 'date', 'Normalize multi-word topic');
|
|
40
|
+
assertEqual(normalizeTopic('OAuth'), 'oauth', 'Normalize OAuth');
|
|
41
|
+
assertEqual(normalizeTopic('Server-Side'), 'server', 'Normalize Server-Side');
|
|
42
|
+
assertEqual(normalizeTopic('caching'), 'caching', 'Normalize caching');
|
|
43
|
+
|
|
44
|
+
// Test normalizeLibrary
|
|
45
|
+
console.log('\n## Testing normalizeLibrary()');
|
|
46
|
+
assertEqual(normalizeLibrary('Next.js'), 'next.js', 'Normalize Next.js');
|
|
47
|
+
assertEqual(normalizeLibrary('shadcn/ui'), 'shadcn/ui', 'Normalize shadcn/ui');
|
|
48
|
+
assertEqual(normalizeLibrary('Better Auth'), 'better-auth', 'Normalize Better Auth');
|
|
49
|
+
|
|
50
|
+
// Test topic-specific queries
|
|
51
|
+
console.log('\n## Testing topic-specific queries');
|
|
52
|
+
|
|
53
|
+
const topicQuery1 = detectTopic('How do I use date picker in shadcn/ui?');
|
|
54
|
+
assert(topicQuery1 !== null, 'Detect topic-specific query 1');
|
|
55
|
+
assert(topicQuery1.isTopicSpecific === true, 'Query 1 is topic-specific');
|
|
56
|
+
assertEqual(topicQuery1.topic, 'date', 'Query 1 topic is "date"');
|
|
57
|
+
assertEqual(topicQuery1.library, 'shadcn/ui', 'Query 1 library is "shadcn/ui"');
|
|
58
|
+
|
|
59
|
+
const topicQuery2 = detectTopic('Next.js caching strategies');
|
|
60
|
+
assert(topicQuery2 !== null, 'Detect topic-specific query 2');
|
|
61
|
+
assert(topicQuery2 && topicQuery2.isTopicSpecific === true, 'Query 2 is topic-specific');
|
|
62
|
+
if (topicQuery2) {
|
|
63
|
+
assertEqual(topicQuery2.topic, 'caching', 'Query 2 topic is "caching"');
|
|
64
|
+
assertEqual(topicQuery2.library, 'next.js', 'Query 2 library is "next.js"');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const topicQuery3 = detectTopic('Better Auth OAuth setup');
|
|
68
|
+
assert(topicQuery3 !== null, 'Detect topic-specific query 3');
|
|
69
|
+
assert(topicQuery3.isTopicSpecific === true, 'Query 3 is topic-specific');
|
|
70
|
+
|
|
71
|
+
const topicQuery4 = detectTopic('Using authentication with Better Auth');
|
|
72
|
+
assert(topicQuery4 !== null, 'Detect topic-specific query 4');
|
|
73
|
+
assert(topicQuery4.isTopicSpecific === true, 'Query 4 is topic-specific');
|
|
74
|
+
|
|
75
|
+
const topicQuery5 = detectTopic('Implement routing in Next.js');
|
|
76
|
+
assert(topicQuery5 !== null, 'Detect topic-specific query 5');
|
|
77
|
+
assert(topicQuery5.isTopicSpecific === true, 'Query 5 is topic-specific');
|
|
78
|
+
|
|
79
|
+
// Test general queries
|
|
80
|
+
console.log('\n## Testing general queries');
|
|
81
|
+
|
|
82
|
+
const generalQuery1 = detectTopic('Documentation for Next.js');
|
|
83
|
+
assert(generalQuery1 === null, 'Detect general query 1 (returns null)');
|
|
84
|
+
|
|
85
|
+
const generalQuery2 = detectTopic('Astro getting started');
|
|
86
|
+
assert(generalQuery2 === null, 'Detect general query 2 (returns null)');
|
|
87
|
+
|
|
88
|
+
const generalQuery3 = detectTopic('How to use Better Auth');
|
|
89
|
+
assert(generalQuery3 === null, 'Detect general query 3 (returns null)');
|
|
90
|
+
|
|
91
|
+
const generalQuery4 = detectTopic('Next.js API reference');
|
|
92
|
+
assert(generalQuery4 === null, 'Detect general query 4 (returns null)');
|
|
93
|
+
|
|
94
|
+
// Test edge cases
|
|
95
|
+
console.log('\n## Testing edge cases');
|
|
96
|
+
|
|
97
|
+
const edgeCase1 = detectTopic('');
|
|
98
|
+
assert(edgeCase1 === null, 'Empty string returns null');
|
|
99
|
+
|
|
100
|
+
const edgeCase2 = detectTopic(null);
|
|
101
|
+
assert(edgeCase2 === null, 'Null returns null');
|
|
102
|
+
|
|
103
|
+
const edgeCase3 = detectTopic('Random text without pattern');
|
|
104
|
+
assert(edgeCase3 === null, 'Non-matching query returns null');
|
|
105
|
+
|
|
106
|
+
// Summary
|
|
107
|
+
console.log('\n## Test Summary');
|
|
108
|
+
console.log(`Passed: ${passed}`);
|
|
109
|
+
console.log(`Failed: ${failed}`);
|
|
110
|
+
console.log(`Total: ${passed + failed}`);
|
|
111
|
+
|
|
112
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tests for fetch-docs.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { buildContext7Url, getUrlVariations } = require('../fetch-docs');
|
|
8
|
+
|
|
9
|
+
// Test counter
|
|
10
|
+
let passed = 0;
|
|
11
|
+
let failed = 0;
|
|
12
|
+
|
|
13
|
+
function assert(condition, message) {
|
|
14
|
+
if (condition) {
|
|
15
|
+
console.log(`✓ ${message}`);
|
|
16
|
+
passed++;
|
|
17
|
+
} else {
|
|
18
|
+
console.error(`✗ ${message}`);
|
|
19
|
+
failed++;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function assertEqual(actual, expected, message) {
|
|
24
|
+
if (actual === expected) {
|
|
25
|
+
console.log(`✓ ${message}`);
|
|
26
|
+
passed++;
|
|
27
|
+
} else {
|
|
28
|
+
console.error(`✗ ${message}`);
|
|
29
|
+
console.error(` Expected: ${expected}`);
|
|
30
|
+
console.error(` Actual: ${actual}`);
|
|
31
|
+
failed++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log('Running fetch-docs.js tests...\n');
|
|
36
|
+
|
|
37
|
+
// Test buildContext7Url
|
|
38
|
+
console.log('## Testing buildContext7Url()');
|
|
39
|
+
|
|
40
|
+
assertEqual(
|
|
41
|
+
buildContext7Url('vercel/next.js'),
|
|
42
|
+
'https://context7.com/vercel/next.js/llms.txt',
|
|
43
|
+
'Build URL for GitHub repo'
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
assertEqual(
|
|
47
|
+
buildContext7Url('vercel/next.js', 'cache'),
|
|
48
|
+
'https://context7.com/vercel/next.js/llms.txt?topic=cache',
|
|
49
|
+
'Build URL with topic parameter'
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
assertEqual(
|
|
53
|
+
buildContext7Url('shadcn-ui/ui', 'date'),
|
|
54
|
+
'https://context7.com/shadcn-ui/ui/llms.txt?topic=date',
|
|
55
|
+
'Build URL for shadcn with topic'
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Test getUrlVariations
|
|
59
|
+
console.log('\n## Testing getUrlVariations()');
|
|
60
|
+
|
|
61
|
+
async function testUrlVariations() {
|
|
62
|
+
const urls1 = await getUrlVariations('next.js', 'cache');
|
|
63
|
+
assert(urls1.length >= 2, 'Returns multiple URL variations with topic');
|
|
64
|
+
assert(urls1[0].includes('?topic=cache'), 'First URL has topic parameter');
|
|
65
|
+
assert(!urls1[1].includes('?topic='), 'Second URL has no topic parameter');
|
|
66
|
+
|
|
67
|
+
const urls2 = await getUrlVariations('shadcn/ui');
|
|
68
|
+
assert(urls2.length >= 1, 'Returns URL variations without topic');
|
|
69
|
+
assert(!urls2[0].includes('?topic='), 'URL has no topic parameter');
|
|
70
|
+
|
|
71
|
+
const urls3 = await getUrlVariations('astro', 'routing');
|
|
72
|
+
assert(urls3.length >= 2, 'Returns variations for known library');
|
|
73
|
+
assertEqual(urls3[0], 'https://context7.com/withastro/astro/llms.txt?topic=routing', 'Maps Astro correctly');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
testUrlVariations().then(() => {
|
|
77
|
+
// Summary
|
|
78
|
+
console.log('\n## Test Summary');
|
|
79
|
+
console.log(`Passed: ${passed}`);
|
|
80
|
+
console.log(`Failed: ${failed}`);
|
|
81
|
+
console.log(`Total: ${passed + failed}`);
|
|
82
|
+
|
|
83
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
84
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Environment variable loader for docs-seeker skill
|
|
5
|
+
* Respects order: process.env > skill/.env > skills/.env > .claude/.env
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parse .env file content into key-value pairs
|
|
13
|
+
* @param {string} content - .env file content
|
|
14
|
+
* @returns {Object} Parsed environment variables
|
|
15
|
+
*/
|
|
16
|
+
function parseEnvFile(content) {
|
|
17
|
+
const env = {};
|
|
18
|
+
const lines = content.split('\n');
|
|
19
|
+
|
|
20
|
+
for (const line of lines) {
|
|
21
|
+
// Skip comments and empty lines
|
|
22
|
+
if (!line || line.trim().startsWith('#')) continue;
|
|
23
|
+
|
|
24
|
+
const match = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
|
|
25
|
+
if (match) {
|
|
26
|
+
const key = match[1];
|
|
27
|
+
let value = match[2].trim();
|
|
28
|
+
|
|
29
|
+
// Remove quotes if present
|
|
30
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
31
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
32
|
+
value = value.slice(1, -1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
env[key] = value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return env;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Load environment variables from .env files in priority order
|
|
44
|
+
* Priority: process.env > skill/.env > skills/.env > .claude/.env
|
|
45
|
+
* @returns {Object} Merged environment variables
|
|
46
|
+
*/
|
|
47
|
+
function loadEnv() {
|
|
48
|
+
const skillDir = path.resolve(__dirname, '../..');
|
|
49
|
+
const skillsDir = path.resolve(skillDir, '..');
|
|
50
|
+
const claudeDir = path.resolve(skillsDir, '..');
|
|
51
|
+
|
|
52
|
+
const envPaths = [
|
|
53
|
+
path.join(claudeDir, '.env'), // Lowest priority
|
|
54
|
+
path.join(skillsDir, '.env'),
|
|
55
|
+
path.join(skillDir, '.env'), // Highest priority (file)
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
let mergedEnv = {};
|
|
59
|
+
|
|
60
|
+
// Load .env files in order (lowest to highest priority)
|
|
61
|
+
for (const envPath of envPaths) {
|
|
62
|
+
if (fs.existsSync(envPath)) {
|
|
63
|
+
try {
|
|
64
|
+
const content = fs.readFileSync(envPath, 'utf8');
|
|
65
|
+
const parsed = parseEnvFile(content);
|
|
66
|
+
mergedEnv = { ...mergedEnv, ...parsed };
|
|
67
|
+
} catch (error) {
|
|
68
|
+
// Silently skip unreadable files
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// process.env has highest priority
|
|
74
|
+
mergedEnv = { ...mergedEnv, ...process.env };
|
|
75
|
+
|
|
76
|
+
return mergedEnv;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get environment variable with fallback
|
|
81
|
+
* @param {string} key - Environment variable key
|
|
82
|
+
* @param {string} defaultValue - Default value if not found
|
|
83
|
+
* @returns {string} Environment variable value
|
|
84
|
+
*/
|
|
85
|
+
function getEnv(key, defaultValue = '') {
|
|
86
|
+
const env = loadEnv();
|
|
87
|
+
return env[key] || defaultValue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = {
|
|
91
|
+
loadEnv,
|
|
92
|
+
getEnv,
|
|
93
|
+
parseEnvFile,
|
|
94
|
+
};
|