openpets 1.0.11 ā 1.0.12
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/data/api.json +3758 -7222
- package/dist/src/core/build-pet.d.ts.map +1 -1
- package/dist/src/core/build-pet.js +7 -0
- package/dist/src/core/build-pet.js.map +1 -1
- package/dist/src/core/cli.js +456 -130
- package/dist/src/core/cli.js.map +1 -1
- package/dist/src/core/ensure-npmignore.d.ts +30 -0
- package/dist/src/core/ensure-npmignore.d.ts.map +1 -0
- package/dist/src/core/ensure-npmignore.js +121 -0
- package/dist/src/core/ensure-npmignore.js.map +1 -0
- package/dist/src/core/index.d.ts +6 -3
- package/dist/src/core/index.d.ts.map +1 -1
- package/dist/src/core/index.js +9 -3
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/core/mcp-generator.d.ts +56 -0
- package/dist/src/core/mcp-generator.d.ts.map +1 -0
- package/dist/src/core/mcp-generator.js +1438 -0
- package/dist/src/core/mcp-generator.js.map +1 -0
- package/dist/src/core/mcp-server.js +0 -0
- package/dist/src/core/openapi-generator.d.ts +59 -0
- package/dist/src/core/openapi-generator.d.ts.map +1 -0
- package/dist/src/core/openapi-generator.js +800 -0
- package/dist/src/core/openapi-generator.js.map +1 -0
- package/dist/src/core/pet-config.d.ts +107 -49
- package/dist/src/core/pet-config.d.ts.map +1 -1
- package/dist/src/core/pet-config.js +6 -4
- package/dist/src/core/pet-config.js.map +1 -1
- package/dist/src/core/pet-downloader.d.ts +16 -0
- package/dist/src/core/pet-downloader.d.ts.map +1 -1
- package/dist/src/core/pet-downloader.js +145 -3
- package/dist/src/core/pet-downloader.js.map +1 -1
- package/dist/src/core/publish-pet.d.ts +29 -0
- package/dist/src/core/publish-pet.d.ts.map +1 -0
- package/dist/src/core/publish-pet.js +372 -0
- package/dist/src/core/publish-pet.js.map +1 -0
- package/dist/src/core/sdk-generator.d.ts +92 -0
- package/dist/src/core/sdk-generator.d.ts.map +1 -0
- package/dist/src/core/sdk-generator.js +567 -0
- package/dist/src/core/sdk-generator.js.map +1 -0
- package/dist/src/core/search-pets.d.ts +5 -0
- package/dist/src/core/search-pets.d.ts.map +1 -1
- package/dist/src/core/search-pets.js +43 -0
- package/dist/src/core/search-pets.js.map +1 -1
- package/dist/src/core/security-scanner.d.ts +49 -0
- package/dist/src/core/security-scanner.d.ts.map +1 -0
- package/dist/src/core/security-scanner.js +255 -0
- package/dist/src/core/security-scanner.js.map +1 -0
- package/dist/src/core/tool-lister.d.ts +61 -0
- package/dist/src/core/tool-lister.d.ts.map +1 -0
- package/dist/src/core/tool-lister.js +333 -0
- package/dist/src/core/tool-lister.js.map +1 -0
- package/dist/src/core/validate-pet.d.ts +2 -0
- package/dist/src/core/validate-pet.d.ts.map +1 -1
- package/dist/src/core/validate-pet.js +93 -1
- package/dist/src/core/validate-pet.js.map +1 -1
- package/dist/src/sdk/plugin-factory.d.ts +86 -0
- package/dist/src/sdk/plugin-factory.d.ts.map +1 -1
- package/dist/src/sdk/plugin-factory.js +450 -53
- package/dist/src/sdk/plugin-factory.js.map +1 -1
- package/dist/src/sdk/prompts-manager.d.ts +6 -0
- package/dist/src/sdk/prompts-manager.d.ts.map +1 -0
- package/dist/src/sdk/prompts-manager.js +162 -0
- package/dist/src/sdk/prompts-manager.js.map +1 -0
- package/package.json +1 -1
- package/dist/src/core/local-cache.d.ts +0 -69
- package/dist/src/core/local-cache.d.ts.map +0 -1
- package/dist/src/core/local-cache.js +0 -212
- package/dist/src/core/local-cache.js.map +0 -1
- package/dist/src/core/plugin-factory.d.ts +0 -58
- package/dist/src/core/plugin-factory.d.ts.map +0 -1
- package/dist/src/core/plugin-factory.js +0 -212
- package/dist/src/core/plugin-factory.js.map +0 -1
package/dist/src/core/cli.js
CHANGED
|
@@ -1,82 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { buildPet } from './build-pet.js';
|
|
3
3
|
import { deployPet } from './deploy-pet.js';
|
|
4
|
+
import { publishPet } from './publish-pet.js';
|
|
5
|
+
import { generateOpenAPITools } from './openapi-generator.js';
|
|
4
6
|
import { addFolderToHistory } from './config-manager.js';
|
|
5
7
|
import { getRegistryClient } from './registry-client.js';
|
|
6
8
|
import { loadPetConfig, validatePetConfig, generateOpenpetsYamlTemplate } from './pet-config.js';
|
|
7
9
|
import { getPackageDiscovery, getRegistryAggregator } from './discovery.js';
|
|
8
10
|
import { getPetDownloader } from './pet-downloader.js';
|
|
9
|
-
import { getAllPets } from './search-pets.js';
|
|
11
|
+
import { getAllPets, findSimilarPets } from './search-pets.js';
|
|
10
12
|
import { syncEnvToConfig } from '../sdk/plugin-factory.js';
|
|
13
|
+
import { fetchFromRegistry, readLocalCommands, getLiveTools, formatTools, listProjectTools, printProjectToolsSummary, getOpencodeCacheDir } from './tool-lister.js';
|
|
11
14
|
import { spawn } from 'child_process';
|
|
12
15
|
import { resolve, join } from 'path';
|
|
13
|
-
import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'fs';
|
|
16
|
+
import { readFileSync, existsSync, writeFileSync, mkdirSync, rmSync } from 'fs';
|
|
14
17
|
import * as readline from 'readline';
|
|
15
|
-
async function publishPet(petName, options = {}) {
|
|
16
|
-
const { preview = false, channel = 'latest' } = options;
|
|
17
|
-
// Find the script path - try multiple locations
|
|
18
|
-
const possiblePaths = [
|
|
19
|
-
resolve(__dirname, '../../scripts/publish-pet.ts'),
|
|
20
|
-
resolve(__dirname, '../../../scripts/publish-pet.ts'),
|
|
21
|
-
resolve(process.cwd(), 'scripts/publish-pet.ts'),
|
|
22
|
-
resolve(process.cwd(), '../../scripts/publish-pet.ts')
|
|
23
|
-
];
|
|
24
|
-
let scriptPath = possiblePaths.find(p => existsSync(p));
|
|
25
|
-
if (!scriptPath) {
|
|
26
|
-
console.error(`ā Publish script not found`);
|
|
27
|
-
console.error(` Tried:`);
|
|
28
|
-
possiblePaths.forEach(p => console.error(` - ${p}`));
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
// If no petName provided, try to infer from current directory
|
|
32
|
-
let finalPetName = petName;
|
|
33
|
-
if (!finalPetName) {
|
|
34
|
-
const cwd = process.cwd();
|
|
35
|
-
const pkgPath = resolve(cwd, 'package.json');
|
|
36
|
-
if (existsSync(pkgPath)) {
|
|
37
|
-
try {
|
|
38
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
39
|
-
// Extract pet name from @openpets/maps -> maps
|
|
40
|
-
if (pkg.name && pkg.name.startsWith('@openpets/')) {
|
|
41
|
-
finalPetName = pkg.name.replace('@openpets/', '');
|
|
42
|
-
console.log(`š¦ Detected pet: ${finalPetName}`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
catch (e) {
|
|
46
|
-
// Ignore parse errors
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
if (!finalPetName) {
|
|
50
|
-
console.error('ā Could not determine pet name. Run from pet directory or specify pet name.');
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const bunArgs = [scriptPath, finalPetName];
|
|
55
|
-
if (preview)
|
|
56
|
-
bunArgs.push('--preview');
|
|
57
|
-
if (channel && channel !== 'latest')
|
|
58
|
-
bunArgs.push('--channel', channel);
|
|
59
|
-
return new Promise((resolve, reject) => {
|
|
60
|
-
const child = spawn('bun', bunArgs, {
|
|
61
|
-
stdio: 'inherit',
|
|
62
|
-
shell: true,
|
|
63
|
-
env: {
|
|
64
|
-
...process.env,
|
|
65
|
-
OPENPETS_PREVIEW: preview ? 'true' : 'false',
|
|
66
|
-
OPENPETS_CHANNEL: channel
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
child.on('error', reject);
|
|
70
|
-
child.on('exit', (code) => {
|
|
71
|
-
if (code !== 0) {
|
|
72
|
-
reject(new Error(`Publish exited with code ${code}`));
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
resolve();
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
18
|
const args = process.argv.slice(2);
|
|
81
19
|
const command = args[0];
|
|
82
20
|
const DEBUG = process.env.PETS_DEBUG === 'true';
|
|
@@ -220,8 +158,17 @@ else if (command === 'publish') {
|
|
|
220
158
|
const preview = args.includes('--preview');
|
|
221
159
|
const channelIndex = args.indexOf('--channel');
|
|
222
160
|
const channel = channelIndex >= 0 ? args[channelIndex + 1] : 'latest';
|
|
223
|
-
|
|
224
|
-
|
|
161
|
+
const scopeIndex = args.indexOf('--scope');
|
|
162
|
+
const scope = scopeIndex >= 0 ? args[scopeIndex + 1] : '@openpets';
|
|
163
|
+
const otpIndex = args.indexOf('--otp');
|
|
164
|
+
const otp = otpIndex >= 0 ? args[otpIndex + 1] : undefined;
|
|
165
|
+
log('Publishing pet:', petName || '(auto-detect)', { preview, channel, scope, otp: otp ? '***' : undefined });
|
|
166
|
+
publishPet(petName, { preview, channel, scope, otp }).then(result => {
|
|
167
|
+
if (!result.success) {
|
|
168
|
+
console.error('Error publishing pet:', result.message);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
}).catch(error => {
|
|
225
172
|
console.error('Error publishing pet:', error);
|
|
226
173
|
log('Publish error details:', error);
|
|
227
174
|
process.exit(1);
|
|
@@ -266,9 +213,21 @@ else if (command === 'registry') {
|
|
|
266
213
|
else if (command === 'prune') {
|
|
267
214
|
handlePrune(args.slice(1));
|
|
268
215
|
}
|
|
216
|
+
else if (command === 'clear') {
|
|
217
|
+
handleClear(args.slice(1));
|
|
218
|
+
}
|
|
219
|
+
else if (command === 'config') {
|
|
220
|
+
handleConfig(args.slice(1));
|
|
221
|
+
}
|
|
269
222
|
else if (command === 'reload') {
|
|
270
223
|
handleReload(args.slice(1));
|
|
271
224
|
}
|
|
225
|
+
else if (command === 'tools') {
|
|
226
|
+
handleTools(args.slice(1));
|
|
227
|
+
}
|
|
228
|
+
else if (command === 'generate-openapi') {
|
|
229
|
+
handleGenerateOpenAPI(args.slice(1));
|
|
230
|
+
}
|
|
272
231
|
else if (command === 'help' || command === '--help' || command === '-h') {
|
|
273
232
|
showHelp();
|
|
274
233
|
}
|
|
@@ -842,26 +801,55 @@ async function handleAdd(args) {
|
|
|
842
801
|
const packageName = args.find(a => !a.startsWith('--'));
|
|
843
802
|
if (!packageName) {
|
|
844
803
|
console.error('ā Package name required');
|
|
845
|
-
console.error('Usage: pets add <package-name> [--
|
|
804
|
+
console.error('Usage: pets add <package-name> [--version <version>]');
|
|
846
805
|
process.exit(1);
|
|
847
806
|
}
|
|
848
|
-
const skipConfig = args.includes('--skip-config');
|
|
849
807
|
const versionIndex = args.indexOf('--version');
|
|
850
808
|
const version = versionIndex >= 0 ? args[versionIndex + 1] : undefined;
|
|
851
|
-
const configIndex = args.indexOf('--config');
|
|
852
|
-
const configStr = configIndex >= 0 ? args[configIndex + 1] : undefined;
|
|
853
|
-
const presetEnv = configStr ? JSON.parse(configStr) : undefined;
|
|
854
809
|
ensureOpencodeJson();
|
|
855
810
|
console.log(`\nš¦ Adding ${packageName}...`);
|
|
856
811
|
const downloader = getPetDownloader();
|
|
857
812
|
const result = await downloader.add(packageName, {
|
|
858
813
|
targetDir: process.cwd(),
|
|
859
|
-
version
|
|
860
|
-
skipConfig,
|
|
861
|
-
envValues: presetEnv
|
|
814
|
+
version
|
|
862
815
|
});
|
|
863
816
|
if (!result.success) {
|
|
864
817
|
console.error(`\nā ${result.message}`);
|
|
818
|
+
// If package not found, suggest similar pets
|
|
819
|
+
if (result.message.includes('Package not found')) {
|
|
820
|
+
const shortName = packageName
|
|
821
|
+
.replace(/^@openpets\//, '')
|
|
822
|
+
.replace(/^openpets\//, '');
|
|
823
|
+
try {
|
|
824
|
+
const similar = await findSimilarPets(shortName, 5);
|
|
825
|
+
if (similar.length > 0) {
|
|
826
|
+
console.log('\nš” Did you mean one of these?\n');
|
|
827
|
+
for (const pet of similar) {
|
|
828
|
+
// Strip all prefixes to get clean pet ID
|
|
829
|
+
const petId = pet.id
|
|
830
|
+
.replace(/^@openpets\//, '')
|
|
831
|
+
.replace(/^openpets\//, '')
|
|
832
|
+
.replace(/^@/, '');
|
|
833
|
+
console.log(` š¦ ${petId}`);
|
|
834
|
+
if (pet.description) {
|
|
835
|
+
console.log(` ${pet.description}`);
|
|
836
|
+
}
|
|
837
|
+
console.log(` Run: pets add ${petId}`);
|
|
838
|
+
console.log('');
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
else {
|
|
842
|
+
console.log('\nš” Try searching for available pets:');
|
|
843
|
+
console.log(` pets search ${shortName}`);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
catch (e) {
|
|
847
|
+
// Silently fail if we can't fetch suggestions
|
|
848
|
+
log('Failed to fetch similar pets:', e);
|
|
849
|
+
console.log('\nš” Try searching for available pets:');
|
|
850
|
+
console.log(` pets search ${shortName}`);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
865
853
|
process.exit(1);
|
|
866
854
|
}
|
|
867
855
|
console.log(`\nā
${result.message}`);
|
|
@@ -874,23 +862,100 @@ async function handleAdd(args) {
|
|
|
874
862
|
console.log(` ${result.config.description}`);
|
|
875
863
|
}
|
|
876
864
|
}
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
865
|
+
// Display prompts/agents copied info
|
|
866
|
+
if (result.promptsCopied && result.promptsCopied.copied > 0) {
|
|
867
|
+
console.log(` š¤ Installed ${result.promptsCopied.copied} agent(s) to .opencode/agent/${packageName.replace('@openpets/', '')}/`);
|
|
868
|
+
for (const file of result.promptsCopied.files) {
|
|
869
|
+
console.log(` ⢠${file.replace('.md', '')}`);
|
|
870
|
+
}
|
|
880
871
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
872
|
+
// Show required env vars if any are missing
|
|
873
|
+
if (result.envVariables && result.envVariables.length > 0) {
|
|
874
|
+
const missing = result.envVariables.filter(e => e.required && !e.currentValue);
|
|
875
|
+
const optional = result.envVariables.filter(e => !e.required && !e.currentValue);
|
|
876
|
+
if (missing.length > 0) {
|
|
877
|
+
console.log('\nā ļø Required environment variables:');
|
|
878
|
+
for (const env of missing) {
|
|
879
|
+
console.log(` ā ${env.name}: ${env.description}`);
|
|
880
|
+
}
|
|
881
|
+
console.log(`\n Run "pets config ${packageName.replace('@openpets/', '')}" to configure`);
|
|
882
|
+
}
|
|
883
|
+
if (optional.length > 0 && missing.length === 0) {
|
|
884
|
+
console.log('\nš” Optional environment variables available:');
|
|
885
|
+
console.log(` Run "pets config ${packageName.replace('@openpets/', '')}" to configure`);
|
|
886
886
|
}
|
|
887
887
|
}
|
|
888
|
-
console.log('\n
|
|
888
|
+
console.log('\nā
Plugin added to opencode.json');
|
|
889
889
|
console.log(' To apply changes:');
|
|
890
890
|
console.log(' ⢠New session: restart opencode');
|
|
891
891
|
console.log(' ⢠Keep session: run "opencode --continue" or "pets reload"\n');
|
|
892
892
|
}
|
|
893
|
-
async function
|
|
893
|
+
async function handleConfig(args) {
|
|
894
|
+
const petName = args.find(a => !a.startsWith('--'));
|
|
895
|
+
if (!petName) {
|
|
896
|
+
console.error('ā Pet name required');
|
|
897
|
+
console.error('Usage: pets config <pet-name>');
|
|
898
|
+
console.error('\nExamples:');
|
|
899
|
+
console.error(' pets config maps');
|
|
900
|
+
console.error(' pets config github');
|
|
901
|
+
process.exit(1);
|
|
902
|
+
}
|
|
903
|
+
const shortName = petName
|
|
904
|
+
.replace(/^@openpets\//, '')
|
|
905
|
+
.replace(/^openpets\//, '');
|
|
906
|
+
console.log(`\nš§ Configuring ${shortName}...\n`);
|
|
907
|
+
// Try to load pet config to get env variable definitions
|
|
908
|
+
const downloader = getPetDownloader();
|
|
909
|
+
const petPath = downloader.getInstalledPetPath(shortName);
|
|
910
|
+
if (!petPath) {
|
|
911
|
+
console.error(`ā Pet "${shortName}" is not installed`);
|
|
912
|
+
console.error(` Run "pets add ${shortName}" first`);
|
|
913
|
+
process.exit(1);
|
|
914
|
+
}
|
|
915
|
+
const { loadPetConfig } = await import('./pet-config.js');
|
|
916
|
+
const petConfig = loadPetConfig(petPath);
|
|
917
|
+
if (!petConfig) {
|
|
918
|
+
console.error(`ā Could not load configuration for ${shortName}`);
|
|
919
|
+
process.exit(1);
|
|
920
|
+
}
|
|
921
|
+
// Extract env variables from pet config
|
|
922
|
+
const envVariables = [];
|
|
923
|
+
if (petConfig.envVariables) {
|
|
924
|
+
const required = petConfig.envVariables.required || [];
|
|
925
|
+
const optional = petConfig.envVariables.optional || [];
|
|
926
|
+
for (const v of required) {
|
|
927
|
+
const varName = v.name || '';
|
|
928
|
+
const isSecret = v.secret || varName.includes('TOKEN') || varName.includes('KEY') || varName.includes('SECRET');
|
|
929
|
+
envVariables.push({
|
|
930
|
+
name: varName,
|
|
931
|
+
description: v.description || '',
|
|
932
|
+
required: true,
|
|
933
|
+
secret: isSecret,
|
|
934
|
+
currentValue: process.env[varName]
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
for (const v of optional) {
|
|
938
|
+
const varName = v.name || '';
|
|
939
|
+
const isSecret = v.secret || varName.includes('TOKEN') || varName.includes('KEY') || varName.includes('SECRET');
|
|
940
|
+
envVariables.push({
|
|
941
|
+
name: varName,
|
|
942
|
+
description: v.description || '',
|
|
943
|
+
required: false,
|
|
944
|
+
secret: isSecret,
|
|
945
|
+
currentValue: process.env[varName]
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
if (envVariables.length === 0) {
|
|
950
|
+
console.log(`ā
${shortName} has no configurable environment variables`);
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
await promptForConfiguration(envVariables, petPath, shortName);
|
|
954
|
+
console.log('\nš” To apply changes:');
|
|
955
|
+
console.log(' ⢠New session: restart opencode');
|
|
956
|
+
console.log(' ⢠Keep session: run "opencode --continue" or "pets reload"\n');
|
|
957
|
+
}
|
|
958
|
+
async function promptForConfiguration(envVariables, packagePath, petId) {
|
|
894
959
|
const rl = readline.createInterface({
|
|
895
960
|
input: process.stdin,
|
|
896
961
|
output: process.stdout
|
|
@@ -938,8 +1003,8 @@ async function promptForConfiguration(envVariables, packagePath) {
|
|
|
938
1003
|
}
|
|
939
1004
|
rl.close();
|
|
940
1005
|
if (Object.keys(envValues).length > 0) {
|
|
941
|
-
//
|
|
942
|
-
const
|
|
1006
|
+
// Use provided petId or extract from package path
|
|
1007
|
+
const resolvedPetId = petId || packagePath.split('/').pop() || 'unknown';
|
|
943
1008
|
// Save to .pets/config.json (proper location for pet environment variables)
|
|
944
1009
|
const petsConfigDir = resolve(process.cwd(), '.pets');
|
|
945
1010
|
const petsConfigPath = resolve(petsConfigDir, 'config.json');
|
|
@@ -973,11 +1038,11 @@ async function promptForConfiguration(envVariables, packagePath) {
|
|
|
973
1038
|
petsConfig.envConfig = {};
|
|
974
1039
|
}
|
|
975
1040
|
// Create or update pet-specific env config
|
|
976
|
-
if (!petsConfig.envConfig[
|
|
977
|
-
petsConfig.envConfig[
|
|
1041
|
+
if (!petsConfig.envConfig[resolvedPetId]) {
|
|
1042
|
+
petsConfig.envConfig[resolvedPetId] = {};
|
|
978
1043
|
}
|
|
979
1044
|
// Set the environment variables
|
|
980
|
-
Object.assign(petsConfig.envConfig[
|
|
1045
|
+
Object.assign(petsConfig.envConfig[resolvedPetId], envValues);
|
|
981
1046
|
// Update timestamp
|
|
982
1047
|
petsConfig.last_updated = new Date().toISOString();
|
|
983
1048
|
// Write config
|
|
@@ -993,36 +1058,6 @@ async function promptForConfiguration(envVariables, packagePath) {
|
|
|
993
1058
|
}
|
|
994
1059
|
}
|
|
995
1060
|
}
|
|
996
|
-
async function launchConfigModal(packageName, envVariables) {
|
|
997
|
-
const uiDir = resolve(__dirname, '../../apps/desktop');
|
|
998
|
-
if (!existsSync(uiDir)) {
|
|
999
|
-
console.log('Desktop UI not available, using CLI configuration...');
|
|
1000
|
-
await promptForConfiguration(envVariables, '');
|
|
1001
|
-
return;
|
|
1002
|
-
}
|
|
1003
|
-
console.log('š„ļø Launching configuration modal...');
|
|
1004
|
-
const configData = {
|
|
1005
|
-
packageName,
|
|
1006
|
-
envVariables,
|
|
1007
|
-
projectDir: process.cwd()
|
|
1008
|
-
};
|
|
1009
|
-
const child = spawn('bun', ['run', 'tauri:dev'], {
|
|
1010
|
-
cwd: uiDir,
|
|
1011
|
-
stdio: 'inherit',
|
|
1012
|
-
shell: true,
|
|
1013
|
-
env: {
|
|
1014
|
-
...process.env,
|
|
1015
|
-
OPENPETS_CONFIG_MODE: 'true',
|
|
1016
|
-
OPENPETS_CONFIG_DATA: JSON.stringify(configData),
|
|
1017
|
-
OPENPETS_PROJECT_DIR: process.cwd()
|
|
1018
|
-
}
|
|
1019
|
-
});
|
|
1020
|
-
child.on('error', (error) => {
|
|
1021
|
-
console.error('Failed to launch configuration modal:', error);
|
|
1022
|
-
console.log('Falling back to CLI configuration...');
|
|
1023
|
-
promptForConfiguration(envVariables, '');
|
|
1024
|
-
});
|
|
1025
|
-
}
|
|
1026
1061
|
async function handlePrune(args) {
|
|
1027
1062
|
const keepPets = args.filter(a => !a.startsWith('--'));
|
|
1028
1063
|
const dryRun = args.includes('--dry-run');
|
|
@@ -1167,11 +1202,240 @@ async function handlePrune(args) {
|
|
|
1167
1202
|
}
|
|
1168
1203
|
writeFileSync(petsConfigPath, JSON.stringify(petsConfig, null, 2));
|
|
1169
1204
|
console.log('ā
Updated .pets/config.json');
|
|
1205
|
+
// Remove agent folders for disabled plugins
|
|
1206
|
+
const agentRemovals = [];
|
|
1207
|
+
for (const plugin of toDisable) {
|
|
1208
|
+
const normalized = normalizeId(plugin);
|
|
1209
|
+
const agentDir = resolve(process.cwd(), '.opencode', 'agent', normalized);
|
|
1210
|
+
if (existsSync(agentDir)) {
|
|
1211
|
+
try {
|
|
1212
|
+
rmSync(agentDir, { recursive: true, force: true });
|
|
1213
|
+
agentRemovals.push(normalized);
|
|
1214
|
+
log(`Removed agent folder: ${agentDir}`);
|
|
1215
|
+
}
|
|
1216
|
+
catch (e) {
|
|
1217
|
+
log(`Failed to remove agent folder ${agentDir}:`, e);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
if (agentRemovals.length > 0) {
|
|
1222
|
+
console.log(`šļø Removed ${agentRemovals.length} agent folder(s) from .opencode/agent/`);
|
|
1223
|
+
for (const agent of agentRemovals) {
|
|
1224
|
+
console.log(` ⢠${agent}`);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1170
1227
|
console.log(`\nš Pruned ${toDisable.length} plugin(s), kept ${toKeep.length} plugin(s)`);
|
|
1171
1228
|
console.log('š” To apply changes:');
|
|
1172
1229
|
console.log(' ⢠New session: restart opencode');
|
|
1173
1230
|
console.log(' ⢠Keep session: run "opencode --continue" or "pets reload"');
|
|
1174
1231
|
}
|
|
1232
|
+
async function handleTools(args) {
|
|
1233
|
+
const petName = args.find(a => !a.startsWith('--'));
|
|
1234
|
+
const showSchema = !args.includes('--no-schema'); // Default: show schema
|
|
1235
|
+
const jsonOutput = args.includes('--json');
|
|
1236
|
+
const liveMode = !args.includes('--static'); // Default: live mode
|
|
1237
|
+
const expanded = args.includes('--expanded'); // Show detailed expanded view
|
|
1238
|
+
const options = { showSchema, jsonOutput, liveMode, expanded };
|
|
1239
|
+
let commandsData = null;
|
|
1240
|
+
// Determine the plugin directory
|
|
1241
|
+
let pluginDir = null;
|
|
1242
|
+
if (petName) {
|
|
1243
|
+
// Check local pets/ directory first
|
|
1244
|
+
const localPetDir = resolve(process.cwd(), 'pets', petName);
|
|
1245
|
+
if (existsSync(localPetDir)) {
|
|
1246
|
+
pluginDir = localPetDir;
|
|
1247
|
+
}
|
|
1248
|
+
else {
|
|
1249
|
+
// Check opencode cache
|
|
1250
|
+
const cachePath = join(getOpencodeCacheDir(), '@openpets', petName);
|
|
1251
|
+
if (existsSync(cachePath)) {
|
|
1252
|
+
pluginDir = cachePath;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
else {
|
|
1257
|
+
// Current directory - only if it's a pet directory
|
|
1258
|
+
if (existsSync(resolve(process.cwd(), 'index.ts'))) {
|
|
1259
|
+
pluginDir = process.cwd();
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
// If we have a plugin directory, try to get tools
|
|
1263
|
+
if (pluginDir) {
|
|
1264
|
+
if (liveMode) {
|
|
1265
|
+
commandsData = await getLiveTools(pluginDir, options);
|
|
1266
|
+
if (!commandsData) {
|
|
1267
|
+
console.log('ā ļø Could not load live tools, falling back to static commands.json...');
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
if (!commandsData) {
|
|
1271
|
+
commandsData = readLocalCommands(pluginDir);
|
|
1272
|
+
}
|
|
1273
|
+
if (commandsData) {
|
|
1274
|
+
if (jsonOutput) {
|
|
1275
|
+
console.log(JSON.stringify(commandsData, null, 2));
|
|
1276
|
+
}
|
|
1277
|
+
else {
|
|
1278
|
+
formatTools(commandsData, showSchema, expanded);
|
|
1279
|
+
}
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
// If pet name specified but not found locally, fetch from registry
|
|
1284
|
+
if (petName && !commandsData) {
|
|
1285
|
+
console.log(`š Fetching tools for ${petName} from registry...`);
|
|
1286
|
+
commandsData = await fetchFromRegistry(petName);
|
|
1287
|
+
if (commandsData) {
|
|
1288
|
+
if (jsonOutput) {
|
|
1289
|
+
console.log(JSON.stringify(commandsData, null, 2));
|
|
1290
|
+
}
|
|
1291
|
+
else {
|
|
1292
|
+
formatTools(commandsData, showSchema, expanded);
|
|
1293
|
+
}
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
console.error(`ā Could not find tools for '${petName}'`);
|
|
1297
|
+
console.error(' Make sure the pet exists and has been built with "pets build"');
|
|
1298
|
+
console.error(' Or check if it\'s published to npm under @openpets/ scope');
|
|
1299
|
+
process.exit(1);
|
|
1300
|
+
}
|
|
1301
|
+
// No pet name - check if this is a project directory with installed plugins
|
|
1302
|
+
const opencodeJsonPath = resolve(process.cwd(), 'opencode.json');
|
|
1303
|
+
if (existsSync(opencodeJsonPath)) {
|
|
1304
|
+
const { plugins, totalTools } = await listProjectTools(process.cwd(), options);
|
|
1305
|
+
if (plugins.length > 0) {
|
|
1306
|
+
if (jsonOutput) {
|
|
1307
|
+
console.log(JSON.stringify({ plugins, totalTools }, null, 2));
|
|
1308
|
+
}
|
|
1309
|
+
else {
|
|
1310
|
+
printProjectToolsSummary(plugins, totalTools, showSchema, expanded);
|
|
1311
|
+
}
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
// Nothing found
|
|
1316
|
+
console.error('ā No tools found');
|
|
1317
|
+
console.error(' - In a pet directory: run "pets build" to generate commands.json');
|
|
1318
|
+
console.error(' - In a project: ensure opencode.json has plugins configured');
|
|
1319
|
+
console.error(' - Or specify a pet name: pets tools <pet-name>');
|
|
1320
|
+
process.exit(1);
|
|
1321
|
+
}
|
|
1322
|
+
async function handleClear(args) {
|
|
1323
|
+
const dryRun = args.includes('--dry-run');
|
|
1324
|
+
const keepAgents = args.includes('--keep-agents');
|
|
1325
|
+
const opencodeJsonPath = resolve(process.cwd(), 'opencode.json');
|
|
1326
|
+
const opencodeJsoncPath = resolve(process.cwd(), 'opencode.jsonc');
|
|
1327
|
+
const petsConfigDir = resolve(process.cwd(), '.pets');
|
|
1328
|
+
const petsConfigPath = resolve(petsConfigDir, 'config.json');
|
|
1329
|
+
const agentBaseDir = resolve(process.cwd(), '.opencode', 'agent');
|
|
1330
|
+
const configPath = existsSync(opencodeJsonPath) ? opencodeJsonPath :
|
|
1331
|
+
existsSync(opencodeJsoncPath) ? opencodeJsoncPath : null;
|
|
1332
|
+
if (!configPath) {
|
|
1333
|
+
console.error('ā No opencode.json found in current directory');
|
|
1334
|
+
process.exit(1);
|
|
1335
|
+
}
|
|
1336
|
+
console.log('\nš§¹ Clearing all pets plugins...');
|
|
1337
|
+
if (dryRun) {
|
|
1338
|
+
console.log(' (dry-run mode - no changes will be made)\n');
|
|
1339
|
+
}
|
|
1340
|
+
else {
|
|
1341
|
+
console.log('');
|
|
1342
|
+
}
|
|
1343
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
1344
|
+
const currentPlugins = Array.isArray(config.plugin)
|
|
1345
|
+
? config.plugin
|
|
1346
|
+
: (config.plugin ? [config.plugin] : []);
|
|
1347
|
+
const normalizeId = (id) => {
|
|
1348
|
+
return id
|
|
1349
|
+
.replace(/^@openpets\//, '')
|
|
1350
|
+
.replace(/^openpets\//, '')
|
|
1351
|
+
.replace(/^pets\//, '')
|
|
1352
|
+
.replace(/^\.\/pets\//, '')
|
|
1353
|
+
.replace(/\/index\.ts$/, '')
|
|
1354
|
+
.replace(/\/index\.js$/, '');
|
|
1355
|
+
};
|
|
1356
|
+
if (currentPlugins.length === 0) {
|
|
1357
|
+
console.log('š¦ No plugins currently installed');
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
console.log('š¦ Plugins to REMOVE:');
|
|
1361
|
+
for (const plugin of currentPlugins) {
|
|
1362
|
+
console.log(` ā ${plugin}`);
|
|
1363
|
+
}
|
|
1364
|
+
// Find agent folders to remove
|
|
1365
|
+
const agentFoldersToRemove = [];
|
|
1366
|
+
if (!keepAgents && existsSync(agentBaseDir)) {
|
|
1367
|
+
for (const plugin of currentPlugins) {
|
|
1368
|
+
const normalized = normalizeId(plugin);
|
|
1369
|
+
const agentDir = resolve(agentBaseDir, normalized);
|
|
1370
|
+
if (existsSync(agentDir)) {
|
|
1371
|
+
agentFoldersToRemove.push(normalized);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
if (agentFoldersToRemove.length > 0) {
|
|
1375
|
+
console.log('\nš¤ Agent folders to REMOVE:');
|
|
1376
|
+
for (const agent of agentFoldersToRemove) {
|
|
1377
|
+
console.log(` ā .opencode/agent/${agent}/`);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
if (dryRun) {
|
|
1382
|
+
console.log('\nš” Run without --dry-run to apply these changes');
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
// Clear plugins from opencode.json
|
|
1386
|
+
config.plugin = [];
|
|
1387
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
1388
|
+
console.log('\nā
Cleared all plugins from opencode.json');
|
|
1389
|
+
// Update .pets/config.json
|
|
1390
|
+
let petsConfig = {
|
|
1391
|
+
enabled: [],
|
|
1392
|
+
disabled: [],
|
|
1393
|
+
};
|
|
1394
|
+
if (existsSync(petsConfigPath)) {
|
|
1395
|
+
try {
|
|
1396
|
+
petsConfig = JSON.parse(readFileSync(petsConfigPath, 'utf-8'));
|
|
1397
|
+
}
|
|
1398
|
+
catch (e) {
|
|
1399
|
+
log('Could not parse existing .pets/config.json, creating new one');
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
if (!Array.isArray(petsConfig.enabled))
|
|
1403
|
+
petsConfig.enabled = [];
|
|
1404
|
+
if (!Array.isArray(petsConfig.disabled))
|
|
1405
|
+
petsConfig.disabled = [];
|
|
1406
|
+
// Move all enabled to disabled
|
|
1407
|
+
for (const plugin of currentPlugins) {
|
|
1408
|
+
const normalized = normalizeId(plugin);
|
|
1409
|
+
petsConfig.enabled = petsConfig.enabled.filter(p => normalizeId(p) !== normalized);
|
|
1410
|
+
if (!petsConfig.disabled.some(p => normalizeId(p) === normalized)) {
|
|
1411
|
+
petsConfig.disabled.push(normalized);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
petsConfig.last_updated = new Date().toISOString();
|
|
1415
|
+
if (!existsSync(petsConfigDir)) {
|
|
1416
|
+
mkdirSync(petsConfigDir, { recursive: true });
|
|
1417
|
+
}
|
|
1418
|
+
writeFileSync(petsConfigPath, JSON.stringify(petsConfig, null, 2));
|
|
1419
|
+
console.log('ā
Updated .pets/config.json');
|
|
1420
|
+
// Remove agent folders
|
|
1421
|
+
if (!keepAgents && agentFoldersToRemove.length > 0) {
|
|
1422
|
+
for (const agent of agentFoldersToRemove) {
|
|
1423
|
+
const agentDir = resolve(agentBaseDir, agent);
|
|
1424
|
+
try {
|
|
1425
|
+
rmSync(agentDir, { recursive: true, force: true });
|
|
1426
|
+
log(`Removed agent folder: ${agentDir}`);
|
|
1427
|
+
}
|
|
1428
|
+
catch (e) {
|
|
1429
|
+
log(`Failed to remove agent folder ${agentDir}:`, e);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
console.log(`šļø Removed ${agentFoldersToRemove.length} agent folder(s)`);
|
|
1433
|
+
}
|
|
1434
|
+
console.log(`\nš Cleared ${currentPlugins.length} plugin(s)`);
|
|
1435
|
+
console.log('š” To apply changes:');
|
|
1436
|
+
console.log(' ⢠New session: restart opencode');
|
|
1437
|
+
console.log(' ⢠Keep session: run "opencode --continue" or "pets reload"');
|
|
1438
|
+
}
|
|
1175
1439
|
async function handleReload(args) {
|
|
1176
1440
|
const sessionIndex = args.indexOf('--session');
|
|
1177
1441
|
const sessionId = sessionIndex >= 0 ? args[sessionIndex + 1] : undefined;
|
|
@@ -1238,10 +1502,37 @@ async function handleReload(args) {
|
|
|
1238
1502
|
});
|
|
1239
1503
|
}
|
|
1240
1504
|
}
|
|
1505
|
+
async function handleGenerateOpenAPI(args) {
|
|
1506
|
+
const urlIndex = args.indexOf('--url');
|
|
1507
|
+
const fileIndex = args.indexOf('--file');
|
|
1508
|
+
const baseUrlIndex = args.indexOf('--base-url');
|
|
1509
|
+
const outputIndex = args.indexOf('--output');
|
|
1510
|
+
const options = {
|
|
1511
|
+
url: urlIndex >= 0 ? args[urlIndex + 1] : undefined,
|
|
1512
|
+
file: fileIndex >= 0 ? args[fileIndex + 1] : undefined,
|
|
1513
|
+
baseUrl: baseUrlIndex >= 0 ? args[baseUrlIndex + 1] : undefined,
|
|
1514
|
+
outputFile: outputIndex >= 0 ? args[outputIndex + 1] : undefined,
|
|
1515
|
+
dryRun: args.includes('--dry-run') || args.includes('-n'),
|
|
1516
|
+
verbose: args.includes('--verbose') || args.includes('-v'),
|
|
1517
|
+
dumpSpec: args.includes('--dump-spec'),
|
|
1518
|
+
};
|
|
1519
|
+
console.log('š§ Generating OpenAPI tools...');
|
|
1520
|
+
const result = await generateOpenAPITools(options);
|
|
1521
|
+
if (result.success) {
|
|
1522
|
+
console.log(`ā
${result.message}`);
|
|
1523
|
+
if (result.outputPath && !options.dryRun) {
|
|
1524
|
+
console.log(`š Output: ${result.outputPath}`);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
else {
|
|
1528
|
+
console.error(`ā ${result.message}`);
|
|
1529
|
+
process.exit(1);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1241
1532
|
function showUnknownCommandError(command) {
|
|
1242
1533
|
const validCommands = [
|
|
1243
|
-
'add', 'install', 'uninstall', 'remove', 'delete', 'inspect', 'search', 'list', 'run', 'prune', 'reload',
|
|
1244
|
-
'init', 'build', 'deploy', 'validate', 'publish', 'playground',
|
|
1534
|
+
'add', 'install', 'uninstall', 'remove', 'delete', 'inspect', 'search', 'list', 'run', 'prune', 'clear', 'config', 'reload',
|
|
1535
|
+
'init', 'build', 'deploy', 'validate', 'publish', 'playground', 'generate-openapi',
|
|
1245
1536
|
'discover', 'registry', 'help'
|
|
1246
1537
|
];
|
|
1247
1538
|
const similar = validCommands.find(cmd => cmd.startsWith(command.slice(0, 2)) ||
|
|
@@ -1261,13 +1552,14 @@ USAGE:
|
|
|
1261
1552
|
|
|
1262
1553
|
REGISTRY COMMANDS:
|
|
1263
1554
|
add <name> Download pet and add to local opencode.json
|
|
1555
|
+
Also copies prompts/ to .opencode/agent/<name>/
|
|
1264
1556
|
--version <version> Install specific version
|
|
1265
|
-
|
|
1266
|
-
|
|
1557
|
+
|
|
1558
|
+
config <name> Configure environment variables for a pet
|
|
1559
|
+
Interactive prompt for required and optional vars
|
|
1267
1560
|
|
|
1268
1561
|
install <name> Install a pet from the registry (npm install)
|
|
1269
1562
|
--client <client> Target client (claude, opencode, cursor, vscode)
|
|
1270
|
-
--config '{"k":"v"}' Pre-configure environment variables
|
|
1271
1563
|
--global, -g Install globally
|
|
1272
1564
|
--version <version> Install specific version
|
|
1273
1565
|
|
|
@@ -1290,7 +1582,13 @@ REGISTRY COMMANDS:
|
|
|
1290
1582
|
--transport <type> Transport type (stdio, http)
|
|
1291
1583
|
|
|
1292
1584
|
prune <pet> [pets...] Keep only specified pets, disable all others
|
|
1585
|
+
Also removes agent folders for disabled pets
|
|
1586
|
+
--dry-run Preview changes without applying
|
|
1587
|
+
|
|
1588
|
+
clear Remove ALL pets plugins from opencode.json
|
|
1589
|
+
Also removes all pet agent folders
|
|
1293
1590
|
--dry-run Preview changes without applying
|
|
1591
|
+
--keep-agents Keep agent folders (only clear opencode.json)
|
|
1294
1592
|
|
|
1295
1593
|
reload [message] Reload OpenCode with updated plugins (continues session)
|
|
1296
1594
|
--session <id> Continue a specific session instead of the last one
|
|
@@ -1304,10 +1602,25 @@ DEVELOPMENT COMMANDS:
|
|
|
1304
1602
|
build <pet-name> Build and validate a pet package
|
|
1305
1603
|
deploy <pet-name> Build and deploy with metadata
|
|
1306
1604
|
validate [dir] Validate pet configuration
|
|
1605
|
+
generate-openapi Generate tools from OpenAPI/Swagger spec
|
|
1606
|
+
--url <url> URL to fetch OpenAPI spec from
|
|
1607
|
+
--file <path> Local file path to OpenAPI spec
|
|
1608
|
+
--base-url <url> Base URL for API calls (overrides spec servers)
|
|
1609
|
+
--output <file> Output file name (default: openapi-client.ts)
|
|
1610
|
+
-n, --dry-run Preview without writing files
|
|
1611
|
+
-v, --verbose Show detailed output
|
|
1612
|
+
--dump-spec Dump raw spec to JSON for debugging
|
|
1613
|
+
tools [pet-name] List tools provided by a pet (single line by default)
|
|
1614
|
+
--expanded Show detailed view with parameters
|
|
1615
|
+
--static Use static commands.json instead of running plugin
|
|
1616
|
+
--no-schema Hide parameter details (in expanded mode)
|
|
1617
|
+
--json Output as JSON
|
|
1307
1618
|
|
|
1308
1619
|
publish [pet-name] Publish a pet to npm
|
|
1309
1620
|
--preview Dry-run mode
|
|
1310
1621
|
--channel <name> npm tag (default: latest)
|
|
1622
|
+
--scope <scope> npm scope (default: @openpets, use "" for unscoped)
|
|
1623
|
+
--otp <code> One-time password for 2FA-enabled accounts
|
|
1311
1624
|
|
|
1312
1625
|
playground Open the web playground
|
|
1313
1626
|
|
|
@@ -1327,9 +1640,9 @@ OTHER:
|
|
|
1327
1640
|
help, --help, -h Show this help message
|
|
1328
1641
|
|
|
1329
1642
|
EXAMPLES:
|
|
1330
|
-
pets add maps # Download @openpets/maps
|
|
1331
|
-
pets add
|
|
1332
|
-
pets
|
|
1643
|
+
pets add maps # Download @openpets/maps
|
|
1644
|
+
pets add maps --version 1.0.0 # Install specific version
|
|
1645
|
+
pets config maps # Configure environment variables
|
|
1333
1646
|
pets install maps --client claude # Install via npm for specific client
|
|
1334
1647
|
pets search github
|
|
1335
1648
|
pets inspect linear
|
|
@@ -1338,6 +1651,10 @@ EXAMPLES:
|
|
|
1338
1651
|
pets run hackernews
|
|
1339
1652
|
pets init my-awesome-pet
|
|
1340
1653
|
pets validate ./pets/my-pet
|
|
1654
|
+
pets tools # List tools in current dir (single line)
|
|
1655
|
+
pets tools coder # List tools for coder pet
|
|
1656
|
+
pets tools coder --expanded # List tools with detailed parameters
|
|
1657
|
+
pets tools asana --static # List tools from static commands.json
|
|
1341
1658
|
pets publish --preview
|
|
1342
1659
|
pets discover --github
|
|
1343
1660
|
pets registry update
|
|
@@ -1350,6 +1667,15 @@ ENVIRONMENT:
|
|
|
1350
1667
|
PETS_DEBUG=true Enable detailed debug logging
|
|
1351
1668
|
GITHUB_TOKEN=<token> GitHub token for repository discovery
|
|
1352
1669
|
|
|
1670
|
+
AGENT PROMPTS:
|
|
1671
|
+
Pets can include agent prompts in their prompts/ directory.
|
|
1672
|
+
When you run "pets add <name>", these are automatically copied to:
|
|
1673
|
+
.opencode/agent/<pet-name>/
|
|
1674
|
+
|
|
1675
|
+
OpenCode discovers agents from .opencode/agent/ directories and makes
|
|
1676
|
+
them available for @mentions in your conversations. Agent files are
|
|
1677
|
+
markdown with YAML frontmatter defining behavior (model, tools, etc.)
|
|
1678
|
+
|
|
1353
1679
|
HOW DISCOVERY WORKS:
|
|
1354
1680
|
The discovery system finds OpenPets-compatible packages via:
|
|
1355
1681
|
|