Add comprehensive development roadmap via GitHub Issues

Created 10 detailed GitHub issues covering:
- Project activation and management UI (#1-2)
- Worker node coordination and visualization (#3-4)
- Automated GitHub repository scanning (#5)
- Intelligent model-to-issue matching (#6)
- Multi-model task execution system (#7)
- N8N workflow integration (#8)
- Hive-Bzzz P2P bridge (#9)
- Peer assistance protocol (#10)

Each issue includes detailed specifications, acceptance criteria,
technical implementation notes, and dependency mapping.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
anthonyrawlins
2025-07-12 19:41:01 +10:00
parent 9a6a06da89
commit e89f2f4b7b
4980 changed files with 1501266 additions and 57 deletions

View File

@@ -0,0 +1,19 @@
import type FileState from '../FileState.js';
import type { ComponentNodePath, Resolver, ResolverClass } from './index.js';
declare enum ChainingLogic {
ALL = 0,
FIRST_FOUND = 1
}
interface ChainResolverOptions {
chainingLogic?: ChainingLogic;
}
export default class ChainResolver implements ResolverClass {
resolvers: Resolver[];
options: ChainResolverOptions;
static Logic: typeof ChainingLogic;
constructor(resolvers: Resolver[], options: ChainResolverOptions);
private resolveFirstOnly;
private resolveAll;
resolve(file: FileState): ComponentNodePath[];
}
export {};

View File

@@ -0,0 +1,39 @@
import runResolver from './utils/runResolver.js';
var ChainingLogic;
(function (ChainingLogic) {
ChainingLogic[ChainingLogic["ALL"] = 0] = "ALL";
ChainingLogic[ChainingLogic["FIRST_FOUND"] = 1] = "FIRST_FOUND";
})(ChainingLogic || (ChainingLogic = {}));
class ChainResolver {
constructor(resolvers, options) {
this.resolvers = resolvers;
this.options = options;
}
resolveFirstOnly(file) {
for (const resolver of this.resolvers) {
const components = runResolver(resolver, file);
if (components.length > 0) {
return components;
}
}
return [];
}
resolveAll(file) {
const allComponents = new Set();
for (const resolver of this.resolvers) {
const components = runResolver(resolver, file);
components.forEach((component) => {
allComponents.add(component);
});
}
return Array.from(allComponents);
}
resolve(file) {
if (this.options.chainingLogic === ChainingLogic.FIRST_FOUND) {
return this.resolveFirstOnly(file);
}
return this.resolveAll(file);
}
}
ChainResolver.Logic = ChainingLogic;
export default ChainResolver;

View File

@@ -0,0 +1,9 @@
import type FileState from '../FileState.js';
import type { ComponentNodePath, ResolverClass } from './index.js';
/**
* Given an AST, this function tries to find all object expressions that are
* passed to `React.createClass` calls, by resolving all references properly.
*/
export default class FindAllDefinitionsResolver implements ResolverClass {
resolve(file: FileState): ComponentNodePath[];
}

View File

@@ -0,0 +1,66 @@
import isReactComponentClass from '../utils/isReactComponentClass.js';
import isReactCreateClassCall from '../utils/isReactCreateClassCall.js';
import isReactForwardRefCall from '../utils/isReactForwardRefCall.js';
import isStatelessComponent from '../utils/isStatelessComponent.js';
import normalizeClassDefinition from '../utils/normalizeClassDefinition.js';
import resolveToValue from '../utils/resolveToValue.js';
import { visitors } from '@babel/traverse';
function classVisitor(path, state) {
if (isReactComponentClass(path)) {
normalizeClassDefinition(path);
state.foundDefinitions.add(path);
}
path.skip();
}
function statelessVisitor(path, state) {
if (isStatelessComponent(path)) {
state.foundDefinitions.add(path);
}
path.skip();
}
const explodedVisitors = visitors.explode({
FunctionDeclaration: { enter: statelessVisitor },
FunctionExpression: { enter: statelessVisitor },
ObjectMethod: { enter: statelessVisitor },
ArrowFunctionExpression: { enter: statelessVisitor },
ClassExpression: { enter: classVisitor },
ClassDeclaration: { enter: classVisitor },
CallExpression: {
enter: function (path, state) {
const argument = path.get('arguments')[0];
if (!argument) {
return;
}
if (isReactForwardRefCall(path)) {
// If the the inner function was previously identified as a component
// replace it with the parent node
const inner = resolveToValue(argument);
state.foundDefinitions.delete(inner);
state.foundDefinitions.add(path);
// Do not traverse into arguments
return path.skip();
}
else if (isReactCreateClassCall(path)) {
const resolvedPath = resolveToValue(argument);
if (resolvedPath.isObjectExpression()) {
state.foundDefinitions.add(resolvedPath);
}
// Do not traverse into arguments
return path.skip();
}
},
},
});
/**
* Given an AST, this function tries to find all object expressions that are
* passed to `React.createClass` calls, by resolving all references properly.
*/
export default class FindAllDefinitionsResolver {
resolve(file) {
const state = {
foundDefinitions: new Set(),
};
file.traverse(explodedVisitors, state);
return Array.from(state.foundDefinitions);
}
}

View File

@@ -0,0 +1,15 @@
import type FileState from '../FileState.js';
import type { ComponentNodePath, ResolverClass } from './index.js';
interface FindAnnotatedDefinitionsResolverOptions {
annotation?: string;
}
/**
* Given an AST, this function tries to find all react components which
* are annotated with an annotation
*/
export default class FindAnnotatedDefinitionsResolver implements ResolverClass {
annotation: string;
constructor({ annotation, }?: FindAnnotatedDefinitionsResolverOptions);
resolve(file: FileState): ComponentNodePath[];
}
export {};

View File

@@ -0,0 +1,62 @@
import normalizeClassDefinition from '../utils/normalizeClassDefinition.js';
import { visitors } from '@babel/traverse';
function isAnnotated(path, annotation) {
let inspectPath = path;
do {
const leadingComments = inspectPath.node.leadingComments;
// If an export doesn't have leading comments, we can simply continue
if (leadingComments && leadingComments.length > 0) {
// Search for the annotation in any comment.
const hasAnnotation = leadingComments.some(({ value }) => value.includes(annotation));
// if we found an annotation return true
if (hasAnnotation) {
return true;
}
}
// return false if the container of the current path is an array
// as we do not want to traverse up through this kind of nodes, like ArrayExpressions for example
// The only exception is variable declarations
if (Array.isArray(inspectPath.container) &&
!inspectPath.isVariableDeclarator() &&
!inspectPath.parentPath?.isCallExpression()) {
return false;
}
} while ((inspectPath = inspectPath.parentPath));
return false;
}
function classVisitor(path, state) {
if (isAnnotated(path, state.annotation)) {
normalizeClassDefinition(path);
state.foundDefinitions.add(path);
}
}
function statelessVisitor(path, state) {
if (isAnnotated(path, state.annotation)) {
state.foundDefinitions.add(path);
}
}
const explodedVisitors = visitors.explode({
ArrowFunctionExpression: { enter: statelessVisitor },
FunctionDeclaration: { enter: statelessVisitor },
FunctionExpression: { enter: statelessVisitor },
ObjectMethod: { enter: statelessVisitor },
ClassDeclaration: { enter: classVisitor },
ClassExpression: { enter: classVisitor },
});
/**
* Given an AST, this function tries to find all react components which
* are annotated with an annotation
*/
export default class FindAnnotatedDefinitionsResolver {
constructor({ annotation = '@component', } = {}) {
this.annotation = annotation;
}
resolve(file) {
const state = {
foundDefinitions: new Set(),
annotation: this.annotation,
};
file.traverse(explodedVisitors, state);
return Array.from(state.foundDefinitions);
}
}

View File

@@ -0,0 +1,28 @@
import type FileState from '../FileState.js';
import type { ComponentNodePath, ResolverClass } from './index.js';
interface FindExportedDefinitionsResolverOptions {
limit?: number;
}
/**
* Given an AST, this function tries to find the exported component definitions.
*
* The component definitions are either the ObjectExpression passed to
* `React.createClass` or a `class` definition extending `React.Component` or
* having a `render()` method.
*
* If a definition is part of the following statements, it is considered to be
* exported:
*
* modules.exports = Definition;
* exports.foo = Definition;
* export default Definition;
* export var Definition = ...;
*
* limit can be used to limit the components to be found. When the limit is reached an error will be thrown
*/
export default class FindExportedDefinitionsResolver implements ResolverClass {
limit: number;
constructor({ limit }?: FindExportedDefinitionsResolverOptions);
resolve(file: FileState): ComponentNodePath[];
}
export {};

View File

@@ -0,0 +1,75 @@
import isExportsOrModuleAssignment from '../utils/isExportsOrModuleAssignment.js';
import resolveExportDeclaration from '../utils/resolveExportDeclaration.js';
import resolveToValue from '../utils/resolveToValue.js';
import { visitors } from '@babel/traverse';
import { shallowIgnoreVisitors } from '../utils/traverse.js';
import findComponentDefinition from '../utils/findComponentDefinition.js';
import { ERROR_CODES, ReactDocgenError } from '../error.js';
function exportDeclaration(path, state) {
resolveExportDeclaration(path).forEach((exportedPath) => {
const definition = findComponentDefinition(exportedPath);
if (definition) {
if (state.limit > 0 && state.foundDefinitions.size > 0) {
// If a file exports multiple components, ... complain!
throw new ReactDocgenError(ERROR_CODES.MULTIPLE_DEFINITIONS);
}
state.foundDefinitions.add(definition);
}
});
return path.skip();
}
function assignmentExpressionVisitor(path, state) {
// Ignore anything that is not `exports.X = ...;` or
// `module.exports = ...;`
if (!isExportsOrModuleAssignment(path)) {
return path.skip();
}
// Resolve the value of the right hand side. It should resolve to a call
// expression, something like React.createClass
const resolvedPath = resolveToValue(path.get('right'));
const definition = findComponentDefinition(resolvedPath);
if (definition) {
if (state.limit > 0 && state.foundDefinitions.size > 0) {
// If a file exports multiple components, ... complain!
throw new ReactDocgenError(ERROR_CODES.MULTIPLE_DEFINITIONS);
}
state.foundDefinitions.add(definition);
}
return path.skip();
}
const explodedVisitors = visitors.explode({
...shallowIgnoreVisitors,
ExportNamedDeclaration: { enter: exportDeclaration },
ExportDefaultDeclaration: { enter: exportDeclaration },
AssignmentExpression: { enter: assignmentExpressionVisitor },
});
/**
* Given an AST, this function tries to find the exported component definitions.
*
* The component definitions are either the ObjectExpression passed to
* `React.createClass` or a `class` definition extending `React.Component` or
* having a `render()` method.
*
* If a definition is part of the following statements, it is considered to be
* exported:
*
* modules.exports = Definition;
* exports.foo = Definition;
* export default Definition;
* export var Definition = ...;
*
* limit can be used to limit the components to be found. When the limit is reached an error will be thrown
*/
export default class FindExportedDefinitionsResolver {
constructor({ limit = 0 } = {}) {
this.limit = limit;
}
resolve(file) {
const state = {
foundDefinitions: new Set(),
limit: this.limit,
};
file.traverse(explodedVisitors, state);
return Array.from(state.foundDefinitions);
}
}

View File

@@ -0,0 +1,16 @@
import ChainResolver from './ChainResolver.js';
import FindAllDefinitionsResolver from './FindAllDefinitionsResolver.js';
import FindAnnotatedDefinitionsResolver from './FindAnnotatedDefinitionsResolver.js';
import FindExportedDefinitionsResolver from './FindExportedDefinitionsResolver.js';
import type { NodePath } from '@babel/traverse';
import type FileState from '../FileState.js';
import type { ArrowFunctionExpression, CallExpression, ClassDeclaration, ClassExpression, FunctionDeclaration, FunctionExpression, ObjectExpression, ObjectMethod } from '@babel/types';
export type StatelessComponentNode = ArrowFunctionExpression | FunctionDeclaration | FunctionExpression | ObjectMethod;
export type ComponentNode = CallExpression | ClassDeclaration | ClassExpression | ObjectExpression | StatelessComponentNode;
export type ComponentNodePath = NodePath<ComponentNode>;
export type ResolverFunction = (file: FileState) => ComponentNodePath[];
export interface ResolverClass {
resolve: ResolverFunction;
}
export type Resolver = ResolverClass | ResolverFunction;
export { FindAllDefinitionsResolver, FindAnnotatedDefinitionsResolver, FindExportedDefinitionsResolver, ChainResolver, };

View File

@@ -0,0 +1,5 @@
import ChainResolver from './ChainResolver.js';
import FindAllDefinitionsResolver from './FindAllDefinitionsResolver.js';
import FindAnnotatedDefinitionsResolver from './FindAnnotatedDefinitionsResolver.js';
import FindExportedDefinitionsResolver from './FindExportedDefinitionsResolver.js';
export { FindAllDefinitionsResolver, FindAnnotatedDefinitionsResolver, FindExportedDefinitionsResolver, ChainResolver, };

View File

@@ -0,0 +1,3 @@
import type FileState from '../../FileState.js';
import type { Resolver, ResolverFunction } from '../index.js';
export default function runResolver(resolver: Resolver, file: FileState): ReturnType<ResolverFunction>;

View File

@@ -0,0 +1,6 @@
function isResolverClass(resolver) {
return typeof resolver === 'object';
}
export default function runResolver(resolver, file) {
return isResolverClass(resolver) ? resolver.resolve(file) : resolver(file);
}