Files
Yonexus/plugin/tools/resources.ts
root 08a66d7659 refactor: restructure project layout and add install.mjs
- Move src/ → plugin/ with subdirectories:
  - plugin/core/ (business logic, models, store, permissions, utils, memory)
  - plugin/tools/ (query, resources)
  - plugin/commands/ (placeholder for slash commands)
  - plugin/hooks/ (placeholder for lifecycle hooks)
  - plugin/index.ts (wiring layer only, no business logic)
- Add install.mjs with --install, --uninstall, --openclaw-profile-path
- Add skills/ and docs/ root directories
- Move planning docs (PLAN.md, FEAT.md, AGENT_TASKS.md) to docs/
- Remove old scripts/install.sh
- Update tsconfig rootDir: src → plugin
- Update README.md and README.zh.md with new layout
- Bump version to 0.2.0
- All tests pass
2026-03-10 14:39:24 +00:00

112 lines
3.8 KiB
TypeScript

import fs from 'node:fs';
import path from 'node:path';
import { YonexusError } from '../core/models/errors';
import type { DocsScope, DocsTopic } from '../core/models/types';
import { slug } from '../core/utils/id';
const TOPICS: DocsTopic[] = ['docs', 'notes', 'knowledge', 'rules', 'lessons', 'workflows'];
function ensureDirs(base: string, dirs: string[]): void {
for (const d of dirs) fs.mkdirSync(path.join(base, d), { recursive: true });
}
export class ResourceLayout {
constructor(private readonly rootDir: string) {}
get organizationsRoot(): string {
return path.join(this.rootDir, 'organizations');
}
orgPath(orgName: string): string {
return path.join(this.organizationsRoot, slug(orgName));
}
teamPath(orgName: string, teamName: string): string {
return path.join(this.orgPath(orgName), 'teams', slug(teamName));
}
agentPath(orgName: string, teamName: string, agentId: string): string {
return path.join(this.teamPath(orgName, teamName), 'agents', slug(agentId));
}
ensureOrganization(orgName: string): void {
const root = this.orgPath(orgName);
ensureDirs(root, ['teams', ...TOPICS]);
}
ensureTeam(orgName: string, teamName: string): void {
const root = this.teamPath(orgName, teamName);
ensureDirs(root, ['agents', ...TOPICS]);
}
ensureAgent(orgName: string, teamName: string, agentId: string): void {
const root = this.agentPath(orgName, teamName, agentId);
ensureDirs(root, TOPICS);
}
private readTopicFiles(topicRoot: string, keyword: string): string[] {
if (!fs.existsSync(topicRoot)) return [];
let re: RegExp;
try {
re = new RegExp(keyword);
} catch {
throw new YonexusError('VALIDATION_ERROR', 'invalid_regex', { keyword });
}
const entries = fs.readdirSync(topicRoot, { withFileTypes: true });
return entries
.filter((e) => e.isFile() && re.test(e.name))
.map((e) => path.join(topicRoot, e.name));
}
getDocs(scope: DocsScope, topic: DocsTopic, keyword: string): string {
const groups: Record<'ORG' | 'DEPT' | 'TEAM' | 'AGENT', string[]> = {
ORG: [],
DEPT: [],
TEAM: [],
AGENT: []
};
const orgsRoot = this.organizationsRoot;
if (!fs.existsSync(orgsRoot)) {
return '----ORG\n----DEPT\n----TEAM\n----AGENT';
}
const orgs = fs.readdirSync(orgsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
for (const orgName of orgs) {
const orgPath = path.join(orgsRoot, orgName);
if (scope === 'organization') {
groups.ORG.push(...this.readTopicFiles(path.join(orgPath, topic), keyword));
}
const teamsRoot = path.join(orgPath, 'teams');
if (!fs.existsSync(teamsRoot)) continue;
const teams = fs.readdirSync(teamsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
for (const teamName of teams) {
const teamPath = path.join(teamsRoot, teamName);
if (scope === 'team') {
groups.TEAM.push(...this.readTopicFiles(path.join(teamPath, topic), keyword));
}
const agentsRoot = path.join(teamPath, 'agents');
if (!fs.existsSync(agentsRoot)) continue;
const agents = fs.readdirSync(agentsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
for (const agentId of agents) {
if (scope === 'agent') {
groups.AGENT.push(...this.readTopicFiles(path.join(agentsRoot, agentId, topic), keyword));
}
}
}
}
// department folders are not defined in this layout; reserved empty group for compatible output.
const printGroup = (k: 'ORG' | 'DEPT' | 'TEAM' | 'AGENT'): string =>
[`----${k}`, ...groups[k]].join('\n');
return [printGroup('ORG'), printGroup('DEPT'), printGroup('TEAM'), printGroup('AGENT')].join('\n');
}
}