opencroc 1.8.1 → 1.8.3

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.
Files changed (37) hide show
  1. package/dist/cli/index.js +755 -8
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/index.d.ts +128 -1
  4. package/dist/index.js +548 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/web/dist/assets/main-Ccg3eDNK.js +1 -0
  7. package/dist/web/dist/assets/office-runtime-B3iNctxE.css +1 -0
  8. package/dist/web/dist/assets/office-runtime-BsCh82Pj.js +183 -0
  9. package/dist/web/dist/assets/pixel-page-3BYGm7dH.js +470 -0
  10. package/dist/web/dist/assets/react-vendor-C8RhVn0h.js +49 -0
  11. package/dist/web/dist/assets/studio-page-BInoyoV2.css +1 -0
  12. package/dist/web/dist/assets/studio-page-o3SCvE_v.js +351 -0
  13. package/dist/web/dist/assets/three-addons-BdrPp04O.js +470 -0
  14. package/dist/web/dist/assets/three-core-CsxM1PCY.js +4057 -0
  15. package/dist/web/dist/index.html +15 -0
  16. package/package.json +11 -2
  17. package/dist/web/index-studio.html +0 -1644
  18. package/dist/web/index-v2-pixel.html +0 -1571
  19. package/dist/web/index.html +0 -573
  20. package/dist/web/js/agents.js +0 -465
  21. package/dist/web/js/camera.js +0 -125
  22. package/dist/web/js/dataviz.js +0 -288
  23. package/dist/web/js/effects.js +0 -345
  24. package/dist/web/js/engine.js +0 -489
  25. package/dist/web/js/office.js +0 -816
  26. package/dist/web/js/state.js +0 -37
  27. package/dist/web/js/ui.js +0 -384
  28. /package/dist/web/{assets → dist}/botreview/char_0.png +0 -0
  29. /package/dist/web/{assets → dist}/botreview/char_1.png +0 -0
  30. /package/dist/web/{assets → dist}/botreview/char_2.png +0 -0
  31. /package/dist/web/{assets → dist}/botreview/coffee-machine.gif +0 -0
  32. /package/dist/web/{assets → dist}/botreview/server.gif +0 -0
  33. /package/dist/web/{assets → dist}/botreview/walls.png +0 -0
  34. /package/dist/web/{assets → dist}/star/desk-v3.webp +0 -0
  35. /package/dist/web/{assets → dist}/star/office_bg_small.webp +0 -0
  36. /package/dist/web/{assets → dist}/star/star-idle-v5.png +0 -0
  37. /package/dist/web/{assets → dist}/star/star-working-spritesheet-grid.webp +0 -0
package/dist/cli/index.js CHANGED
@@ -6511,12 +6511,430 @@ var init_studio_store = __esm({
6511
6511
  }
6512
6512
  });
6513
6513
 
6514
+ // src/agents/role-registry.ts
6515
+ var role_registry_exports = {};
6516
+ __export(role_registry_exports, {
6517
+ RoleRegistry: () => RoleRegistry,
6518
+ getRoleRegistry: () => getRoleRegistry
6519
+ });
6520
+ function getRoleRegistry() {
6521
+ if (!_registry) {
6522
+ _registry = new RoleRegistry();
6523
+ }
6524
+ return _registry;
6525
+ }
6526
+ var CORE_ROLES, LANGUAGE_ROLES, FRAMEWORK_ROLES, DOMAIN_ROLES, RoleRegistry, _registry;
6527
+ var init_role_registry = __esm({
6528
+ "src/agents/role-registry.ts"() {
6529
+ "use strict";
6530
+ init_esm_shims();
6531
+ CORE_ROLES = [
6532
+ {
6533
+ id: "parser-croc",
6534
+ name: "\u89E3\u6790\u9CC4",
6535
+ nameEn: "Parser Croc",
6536
+ category: "core",
6537
+ description: "\u89E3\u6790\u9879\u76EE\u7ED3\u6784\u3001\u63D0\u53D6\u5B9E\u4F53\u548C\u5173\u7CFB",
6538
+ sprite: "parser",
6539
+ color: "#34d399",
6540
+ priority: 0,
6541
+ triggers: { custom: () => true },
6542
+ // Always summoned
6543
+ systemPrompt: "You are an expert code parser. Analyze the project structure, extract all entities (classes, functions, APIs, models) and their relationships.",
6544
+ outputType: "analysis",
6545
+ tags: ["core", "parser", "structure"]
6546
+ },
6547
+ {
6548
+ id: "analyzer-croc",
6549
+ name: "\u5206\u6790\u9CC4",
6550
+ nameEn: "Analyzer Croc",
6551
+ category: "core",
6552
+ description: "\u6784\u5EFA\u77E5\u8BC6\u56FE\u8C31\u3001\u5206\u6790\u4F9D\u8D56\u5173\u7CFB",
6553
+ sprite: "analyzer",
6554
+ color: "#60a5fa",
6555
+ priority: 1,
6556
+ triggers: { custom: () => true },
6557
+ systemPrompt: "You are an expert software architect. Build a knowledge graph of the project, analyze dependencies, coupling, and cohesion.",
6558
+ outputType: "diagram",
6559
+ tags: ["core", "graph", "architecture"]
6560
+ },
6561
+ {
6562
+ id: "tester-croc",
6563
+ name: "\u6D4B\u8BD5\u9CC4",
6564
+ nameEn: "Tester Croc",
6565
+ category: "core",
6566
+ description: "\u751F\u6210\u548C\u6267\u884C E2E \u6D4B\u8BD5",
6567
+ sprite: "tester",
6568
+ color: "#a78bfa",
6569
+ priority: 2,
6570
+ triggers: { custom: () => true },
6571
+ systemPrompt: "You are an expert test engineer. Generate comprehensive E2E test cases covering all critical paths, edge cases, and error scenarios.",
6572
+ outputType: "analysis",
6573
+ tags: ["core", "testing", "e2e"]
6574
+ },
6575
+ {
6576
+ id: "healer-croc",
6577
+ name: "\u4FEE\u590D\u9CC4",
6578
+ nameEn: "Healer Croc",
6579
+ category: "core",
6580
+ description: "\u81EA\u52A8\u4FEE\u590D\u6D4B\u8BD5\u5931\u8D25\u548C\u4EE3\u7801\u95EE\u9898",
6581
+ sprite: "healer",
6582
+ color: "#f87171",
6583
+ priority: 3,
6584
+ triggers: { custom: () => true },
6585
+ systemPrompt: "You are an expert debugger and code fixer. Analyze test failures, diagnose root causes, and propose minimal targeted fixes.",
6586
+ outputType: "fix",
6587
+ tags: ["core", "healing", "debug"]
6588
+ },
6589
+ {
6590
+ id: "planner-croc",
6591
+ name: "\u89C4\u5212\u9CC4",
6592
+ nameEn: "Planner Croc",
6593
+ category: "core",
6594
+ description: "\u5236\u5B9A\u6D4B\u8BD5\u7B56\u7565\u548C\u6267\u884C\u8BA1\u5212",
6595
+ sprite: "planner",
6596
+ color: "#fbbf24",
6597
+ priority: 4,
6598
+ triggers: { custom: () => true },
6599
+ systemPrompt: "You are an expert project planner. Create test strategies, prioritize test execution order, and optimize the testing pipeline.",
6600
+ outputType: "analysis",
6601
+ tags: ["core", "planning", "strategy"]
6602
+ },
6603
+ {
6604
+ id: "reporter-croc",
6605
+ name: "\u6C47\u62A5\u9CC4",
6606
+ nameEn: "Reporter Croc",
6607
+ category: "core",
6608
+ description: "\u751F\u6210\u591A\u89C6\u89D2\u5206\u6790\u62A5\u544A",
6609
+ sprite: "reporter",
6610
+ color: "#22d3ee",
6611
+ priority: 5,
6612
+ triggers: { custom: () => true },
6613
+ systemPrompt: "You are an expert technical writer. Generate clear, actionable reports from multiple perspectives (developer, architect, tester, product, executive).",
6614
+ outputType: "report",
6615
+ tags: ["core", "reporting", "documentation"]
6616
+ }
6617
+ ];
6618
+ LANGUAGE_ROLES = [
6619
+ {
6620
+ id: "python-croc",
6621
+ name: "Python\u4E13\u5BB6\u9CC4",
6622
+ nameEn: "Python Expert Croc",
6623
+ category: "language",
6624
+ description: "Python \u751F\u6001\u4E13\u5BB6\uFF1ADjango/Flask/FastAPI/SQLAlchemy",
6625
+ sprite: "language",
6626
+ color: "#3776ab",
6627
+ priority: 10,
6628
+ triggers: { languages: ["python"] },
6629
+ systemPrompt: "You are a Python ecosystem expert. Analyze Python code for best practices, type safety, async patterns, Django/Flask/FastAPI conventions, SQLAlchemy usage, and Python-specific security issues.",
6630
+ outputType: "review",
6631
+ tags: ["language", "python", "django", "flask", "fastapi"]
6632
+ },
6633
+ {
6634
+ id: "go-croc",
6635
+ name: "Go\u4E13\u5BB6\u9CC4",
6636
+ nameEn: "Go Expert Croc",
6637
+ category: "language",
6638
+ description: "Go \u751F\u6001\u4E13\u5BB6\uFF1Agoroutine/channel/\u63A5\u53E3\u8BBE\u8BA1",
6639
+ sprite: "language",
6640
+ color: "#00add8",
6641
+ priority: 10,
6642
+ triggers: { languages: ["go"] },
6643
+ systemPrompt: "You are a Go ecosystem expert. Analyze Go code for goroutine safety, channel patterns, interface design, error handling, and Go-specific performance concerns.",
6644
+ outputType: "review",
6645
+ tags: ["language", "go", "golang", "concurrency"]
6646
+ },
6647
+ {
6648
+ id: "java-croc",
6649
+ name: "Java\u4E13\u5BB6\u9CC4",
6650
+ nameEn: "Java Expert Croc",
6651
+ category: "language",
6652
+ description: "Java/Kotlin \u751F\u6001\u4E13\u5BB6\uFF1ASpring Boot/JPA/\u5FAE\u670D\u52A1",
6653
+ sprite: "language",
6654
+ color: "#ed8b00",
6655
+ priority: 10,
6656
+ triggers: { languages: ["java", "kotlin"] },
6657
+ systemPrompt: "You are a Java/Kotlin ecosystem expert. Analyze Spring Boot applications for bean lifecycle issues, JPA N+1 queries, transaction management, and microservice patterns.",
6658
+ outputType: "review",
6659
+ tags: ["language", "java", "kotlin", "spring"]
6660
+ },
6661
+ {
6662
+ id: "rust-croc",
6663
+ name: "Rust\u4E13\u5BB6\u9CC4",
6664
+ nameEn: "Rust Expert Croc",
6665
+ category: "language",
6666
+ description: "Rust \u751F\u6001\u4E13\u5BB6\uFF1A\u6240\u6709\u6743/\u751F\u547D\u5468\u671F/unsafe",
6667
+ sprite: "language",
6668
+ color: "#dea584",
6669
+ priority: 10,
6670
+ triggers: { languages: ["rust"] },
6671
+ systemPrompt: "You are a Rust ecosystem expert. Analyze Rust code for ownership patterns, lifetime issues, unsafe code safety, and performance optimization.",
6672
+ outputType: "review",
6673
+ tags: ["language", "rust", "ownership", "safety"]
6674
+ }
6675
+ ];
6676
+ FRAMEWORK_ROLES = [
6677
+ {
6678
+ id: "react-croc",
6679
+ name: "React\u4E13\u5BB6\u9CC4",
6680
+ nameEn: "React Expert Croc",
6681
+ category: "framework",
6682
+ description: "React/Next.js \u524D\u7AEF\u6027\u80FD\u548C\u67B6\u6784\u4E13\u5BB6",
6683
+ sprite: "framework",
6684
+ color: "#61dafb",
6685
+ priority: 15,
6686
+ triggers: { frameworks: ["React", "Next.js"] },
6687
+ systemPrompt: "You are a React/Next.js expert. Analyze component architecture, render performance, state management, SSR/SSG patterns, and React-specific anti-patterns.",
6688
+ outputType: "review",
6689
+ tags: ["framework", "react", "nextjs", "frontend"]
6690
+ },
6691
+ {
6692
+ id: "vue-croc",
6693
+ name: "Vue\u4E13\u5BB6\u9CC4",
6694
+ nameEn: "Vue Expert Croc",
6695
+ category: "framework",
6696
+ description: "Vue/Nuxt \u4E13\u5BB6\uFF1A\u7EC4\u5408\u5F0FAPI/\u54CD\u5E94\u5F0F/SSR",
6697
+ sprite: "framework",
6698
+ color: "#42b883",
6699
+ priority: 15,
6700
+ triggers: { frameworks: ["Vue", "Nuxt"] },
6701
+ systemPrompt: "You are a Vue/Nuxt expert. Analyze Composition API usage, reactivity patterns, Pinia stores, SSR hydration, and Vue-specific best practices.",
6702
+ outputType: "review",
6703
+ tags: ["framework", "vue", "nuxt", "frontend"]
6704
+ },
6705
+ {
6706
+ id: "express-croc",
6707
+ name: "Express\u4E13\u5BB6\u9CC4",
6708
+ nameEn: "Express Expert Croc",
6709
+ category: "framework",
6710
+ description: "Express/Koa/Fastify \u8DEF\u7531\u548C\u4E2D\u95F4\u4EF6\u4E13\u5BB6",
6711
+ sprite: "framework",
6712
+ color: "#68a063",
6713
+ priority: 15,
6714
+ triggers: { frameworks: ["Express", "Koa", "Fastify"] },
6715
+ systemPrompt: "You are a Node.js backend expert. Analyze Express/Koa/Fastify middleware chains, route organization, error handling, authentication patterns, and performance.",
6716
+ outputType: "review",
6717
+ tags: ["framework", "express", "koa", "fastify", "nodejs"]
6718
+ },
6719
+ {
6720
+ id: "django-croc",
6721
+ name: "Django\u4E13\u5BB6\u9CC4",
6722
+ nameEn: "Django Expert Croc",
6723
+ category: "framework",
6724
+ description: "Django/DRF \u4E13\u5BB6\uFF1AORM/\u5E8F\u5217\u5316/\u6743\u9650",
6725
+ sprite: "framework",
6726
+ color: "#092e20",
6727
+ priority: 15,
6728
+ triggers: { frameworks: ["Django", "DRF"] },
6729
+ systemPrompt: "You are a Django/DRF expert. Analyze ORM query efficiency, serializer design, view permissions, middleware, and Django-specific security practices.",
6730
+ outputType: "review",
6731
+ tags: ["framework", "django", "drf", "python"]
6732
+ },
6733
+ {
6734
+ id: "spring-croc",
6735
+ name: "SpringBoot\u4E13\u5BB6\u9CC4",
6736
+ nameEn: "Spring Boot Expert Croc",
6737
+ category: "framework",
6738
+ description: "Spring Boot/Cloud \u5FAE\u670D\u52A1\u67B6\u6784\u4E13\u5BB6",
6739
+ sprite: "framework",
6740
+ color: "#6db33f",
6741
+ priority: 15,
6742
+ triggers: { frameworks: ["Spring Boot", "Spring Cloud"] },
6743
+ systemPrompt: "You are a Spring Boot/Cloud expert. Analyze bean configurations, transaction management, service discovery, circuit breakers, and microservice communication patterns.",
6744
+ outputType: "review",
6745
+ tags: ["framework", "spring", "springboot", "microservice"]
6746
+ }
6747
+ ];
6748
+ DOMAIN_ROLES = [
6749
+ {
6750
+ id: "security-croc",
6751
+ name: "\u5B89\u5168\u5BA1\u8BA1\u9CC4",
6752
+ nameEn: "Security Auditor Croc",
6753
+ category: "domain",
6754
+ description: "\u5B89\u5168\u6F0F\u6D1E\u68C0\u6D4B\uFF1A\u6CE8\u5165/XSS/CSRF/\u8BA4\u8BC1/\u6388\u6743",
6755
+ sprite: "security",
6756
+ color: "#ef4444",
6757
+ priority: 8,
6758
+ triggers: {
6759
+ riskCategories: ["security"],
6760
+ custom: (ctx) => ctx.hasAPIs
6761
+ },
6762
+ systemPrompt: "You are a security auditor. Scan for OWASP Top 10 vulnerabilities: SQL injection, XSS, CSRF, broken authentication, sensitive data exposure, insecure deserialization, and missing access controls.",
6763
+ outputType: "report",
6764
+ tags: ["domain", "security", "owasp", "audit"]
6765
+ },
6766
+ {
6767
+ id: "performance-croc",
6768
+ name: "\u6027\u80FD\u5206\u6790\u9CC4",
6769
+ nameEn: "Performance Analyst Croc",
6770
+ category: "domain",
6771
+ description: "\u6027\u80FD\u74F6\u9888\u68C0\u6D4B\uFF1AN+1\u67E5\u8BE2/\u5185\u5B58\u6CC4\u6F0F/\u6162\u63A5\u53E3",
6772
+ sprite: "performance",
6773
+ color: "#f59e0b",
6774
+ priority: 9,
6775
+ triggers: {
6776
+ minEntities: 50,
6777
+ custom: (ctx) => ctx.hasModels && ctx.hasAPIs
6778
+ },
6779
+ systemPrompt: "You are a performance analyst. Detect N+1 queries, memory leaks, slow API endpoints, missing indexes, unnecessary data loading, and recommend caching strategies.",
6780
+ outputType: "report",
6781
+ tags: ["domain", "performance", "optimization", "n+1"]
6782
+ },
6783
+ {
6784
+ id: "architecture-croc",
6785
+ name: "\u67B6\u6784\u8BC4\u5BA1\u9CC4",
6786
+ nameEn: "Architecture Reviewer Croc",
6787
+ category: "domain",
6788
+ description: "\u67B6\u6784\u8D28\u91CF\u8BC4\u5BA1\uFF1A\u8026\u5408\u5EA6/\u5185\u805A\u6027/\u5206\u5C42/DDD",
6789
+ sprite: "architecture",
6790
+ color: "#8b5cf6",
6791
+ priority: 7,
6792
+ triggers: {
6793
+ minEntities: 30
6794
+ },
6795
+ systemPrompt: "You are a software architect. Evaluate coupling, cohesion, layering, dependency injection, SOLID principles, DDD patterns, and recommend architectural improvements.",
6796
+ outputType: "review",
6797
+ tags: ["domain", "architecture", "solid", "ddd"]
6798
+ },
6799
+ {
6800
+ id: "data-modeling-croc",
6801
+ name: "\u6570\u636E\u5EFA\u6A21\u9CC4",
6802
+ nameEn: "Data Modeling Croc",
6803
+ category: "domain",
6804
+ description: "\u6570\u636E\u6A21\u578B\u8BC4\u5BA1\uFF1A\u8303\u5F0F/\u7D22\u5F15/\u5173\u8054/\u8FC1\u79FB",
6805
+ sprite: "database",
6806
+ color: "#06b6d4",
6807
+ priority: 9,
6808
+ triggers: {
6809
+ custom: (ctx) => ctx.hasModels,
6810
+ frameworks: ["Sequelize", "Prisma", "TypeORM", "Drizzle", "SQLAlchemy", "Django"]
6811
+ },
6812
+ systemPrompt: "You are a database expert. Review data models for normalization, index design, relationship integrity, migration safety, cascade delete risks, and query optimization.",
6813
+ outputType: "review",
6814
+ tags: ["domain", "database", "modeling", "orm"]
6815
+ },
6816
+ {
6817
+ id: "devops-croc",
6818
+ name: "\u8FD0\u7EF4\u90E8\u7F72\u9CC4",
6819
+ nameEn: "DevOps Croc",
6820
+ category: "domain",
6821
+ description: "CI/CD\u3001Docker\u3001K8s \u90E8\u7F72\u548C\u8FD0\u7EF4\u4E13\u5BB6",
6822
+ sprite: "devops",
6823
+ color: "#2563eb",
6824
+ priority: 12,
6825
+ triggers: {
6826
+ custom: (ctx) => ctx.hasDocker || ctx.hasCI,
6827
+ filePatterns: ["Dockerfile", "docker-compose*", ".github/workflows/*", ".gitlab-ci*", "Jenkinsfile"]
6828
+ },
6829
+ systemPrompt: "You are a DevOps expert. Analyze Dockerfile efficiency, compose orchestration, CI/CD pipeline design, secret management, health checks, and deployment strategies.",
6830
+ outputType: "review",
6831
+ tags: ["domain", "devops", "docker", "ci/cd", "kubernetes"]
6832
+ },
6833
+ {
6834
+ id: "api-design-croc",
6835
+ name: "API\u8BBE\u8BA1\u9CC4",
6836
+ nameEn: "API Design Croc",
6837
+ category: "domain",
6838
+ description: "RESTful API \u8BBE\u8BA1\u8BC4\u5BA1\uFF1A\u547D\u540D/\u7248\u672C/\u5206\u9875/\u9519\u8BEF\u5904\u7406",
6839
+ sprite: "api",
6840
+ color: "#10b981",
6841
+ priority: 11,
6842
+ triggers: {
6843
+ custom: (ctx) => ctx.hasAPIs,
6844
+ minEntities: 10
6845
+ },
6846
+ systemPrompt: "You are an API design expert. Review REST API naming conventions, versioning strategy, pagination, filtering, error response format, rate limiting, and documentation completeness.",
6847
+ outputType: "review",
6848
+ tags: ["domain", "api", "rest", "design"]
6849
+ },
6850
+ {
6851
+ id: "refactor-croc",
6852
+ name: "\u91CD\u6784\u5EFA\u8BAE\u9CC4",
6853
+ nameEn: "Refactoring Croc",
6854
+ category: "domain",
6855
+ description: "\u4EE3\u7801\u8D28\u91CF\u8BC4\u4F30\uFF1A\u6280\u672F\u503A/\u590D\u6742\u5EA6/\u91CD\u590D\u4EE3\u7801",
6856
+ sprite: "refactor",
6857
+ color: "#ec4899",
6858
+ priority: 13,
6859
+ triggers: {
6860
+ riskCategories: ["maintainability"],
6861
+ minEntities: 40
6862
+ },
6863
+ systemPrompt: "You are a refactoring expert. Identify code smells, duplicated logic, high cyclomatic complexity, god classes, feature envy, and suggest targeted refactoring strategies with minimal risk.",
6864
+ outputType: "review",
6865
+ tags: ["domain", "refactoring", "quality", "tech-debt"]
6866
+ },
6867
+ {
6868
+ id: "microservice-croc",
6869
+ name: "\u670D\u52A1\u6CBB\u7406\u9CC4",
6870
+ nameEn: "Microservice Governance Croc",
6871
+ category: "domain",
6872
+ description: "\u5FAE\u670D\u52A1\u67B6\u6784\u6CBB\u7406\uFF1A\u670D\u52A1\u8FB9\u754C/\u7194\u65AD/\u94FE\u8DEF\u8FFD\u8E2A",
6873
+ sprite: "microservice",
6874
+ color: "#7c3aed",
6875
+ priority: 14,
6876
+ triggers: {
6877
+ projectTypes: ["microservice", "monorepo"],
6878
+ custom: (ctx) => ctx.entityCount > 100
6879
+ },
6880
+ systemPrompt: "You are a microservice governance expert. Analyze service boundaries, inter-service communication, circuit breakers, distributed tracing, service mesh, and API gateway patterns.",
6881
+ outputType: "review",
6882
+ tags: ["domain", "microservice", "governance", "distributed"]
6883
+ }
6884
+ ];
6885
+ RoleRegistry = class {
6886
+ roles = /* @__PURE__ */ new Map();
6887
+ constructor() {
6888
+ for (const role of [...CORE_ROLES, ...LANGUAGE_ROLES, ...FRAMEWORK_ROLES, ...DOMAIN_ROLES]) {
6889
+ this.roles.set(role.id, role);
6890
+ }
6891
+ }
6892
+ /** Register a new role (community or custom) */
6893
+ register(role) {
6894
+ if (this.roles.has(role.id)) {
6895
+ throw new Error(`Role "${role.id}" is already registered`);
6896
+ }
6897
+ this.roles.set(role.id, role);
6898
+ }
6899
+ /** Unregister a role */
6900
+ unregister(id) {
6901
+ return this.roles.delete(id);
6902
+ }
6903
+ /** Get a role by ID */
6904
+ get(id) {
6905
+ return this.roles.get(id);
6906
+ }
6907
+ /** List all registered roles */
6908
+ list() {
6909
+ return Array.from(this.roles.values());
6910
+ }
6911
+ /** List roles by category */
6912
+ listByCategory(category) {
6913
+ return this.list().filter((r) => r.category === category);
6914
+ }
6915
+ /** Search roles by tags */
6916
+ search(query) {
6917
+ const q = query.toLowerCase();
6918
+ return this.list().filter(
6919
+ (r) => r.name.toLowerCase().includes(q) || r.nameEn.toLowerCase().includes(q) || r.description.toLowerCase().includes(q) || r.tags.some((t) => t.includes(q))
6920
+ );
6921
+ }
6922
+ /** Total number of registered roles */
6923
+ get size() {
6924
+ return this.roles.size;
6925
+ }
6926
+ };
6927
+ _registry = null;
6928
+ }
6929
+ });
6930
+
6514
6931
  // src/server/routes/studio.ts
6515
6932
  function restoreStore(snapshotStore) {
6516
6933
  return snapshotStore?.load() ?? { ...EMPTY_STUDIO_STORE };
6517
6934
  }
6518
6935
  function registerStudioRoutes(app, office, snapshotStore) {
6519
6936
  const store = restoreStore(snapshotStore);
6937
+ let lastScanResult = null;
6520
6938
  if (store.graph) {
6521
6939
  office.log(`\u267B\uFE0F Restored Studio snapshot: ${store.graph.nodes.length} nodes, ${store.risks.length} risks`, "info");
6522
6940
  }
@@ -6578,6 +6996,7 @@ function registerStudioRoutes(app, office, snapshotStore) {
6578
6996
  store.risks = risks;
6579
6997
  store.scanTime = Date.now();
6580
6998
  store.source = target;
6999
+ lastScanResult = scanResult;
6581
7000
  persistStore();
6582
7001
  broadcastGraph();
6583
7002
  return {
@@ -6826,6 +7245,90 @@ function registerStudioRoutes(app, office, snapshotStore) {
6826
7245
  snapshots: snapshotStore.list()
6827
7246
  };
6828
7247
  });
7248
+ app.get("/api/studio/roles", async (req) => {
7249
+ const { getRoleRegistry: getRoleRegistry2 } = await Promise.resolve().then(() => (init_role_registry(), role_registry_exports));
7250
+ const registry = getRoleRegistry2();
7251
+ const category = req.query.category;
7252
+ const search = req.query.search;
7253
+ let roles = registry.list();
7254
+ if (category) roles = roles.filter((r) => r.category === category);
7255
+ if (search) roles = registry.search(search);
7256
+ return {
7257
+ total: roles.length,
7258
+ categories: {
7259
+ core: roles.filter((r) => r.category === "core").length,
7260
+ language: roles.filter((r) => r.category === "language").length,
7261
+ framework: roles.filter((r) => r.category === "framework").length,
7262
+ domain: roles.filter((r) => r.category === "domain").length,
7263
+ community: roles.filter((r) => r.category === "community").length
7264
+ },
7265
+ roles: roles.map((r) => ({
7266
+ id: r.id,
7267
+ name: r.name,
7268
+ nameEn: r.nameEn,
7269
+ category: r.category,
7270
+ description: r.description,
7271
+ color: r.color,
7272
+ sprite: r.sprite,
7273
+ priority: r.priority,
7274
+ tags: r.tags,
7275
+ outputType: r.outputType
7276
+ }))
7277
+ };
7278
+ });
7279
+ app.post("/api/studio/summon", async (_req, reply) => {
7280
+ if (!lastScanResult) {
7281
+ reply.code(400).send({ error: "No project scanned. Run /api/studio/scan first." });
7282
+ return;
7283
+ }
7284
+ const riskCategories = [...new Set(store.risks.map((r) => r.category))];
7285
+ const plan = await office.summonForProject(lastScanResult, riskCategories);
7286
+ return {
7287
+ ok: true,
7288
+ totalAgents: office.getAgents().length,
7289
+ coreAgents: 6,
7290
+ dynamicAgents: office.getAgents().length - 6,
7291
+ reasoning: plan.reasoning,
7292
+ agents: office.getAgents().map((a) => ({
7293
+ id: a.id,
7294
+ name: a.name,
7295
+ role: a.role,
7296
+ sprite: a.sprite,
7297
+ status: a.status,
7298
+ category: a.category,
7299
+ color: a.color,
7300
+ description: a.description
7301
+ })),
7302
+ context: {
7303
+ languages: plan.context.languages,
7304
+ frameworks: plan.context.frameworks,
7305
+ projectType: plan.context.projectType,
7306
+ entityCount: plan.context.entityCount,
7307
+ hasAPIs: plan.context.hasAPIs,
7308
+ hasModels: plan.context.hasModels,
7309
+ hasFrontend: plan.context.hasFrontend,
7310
+ hasDocker: plan.context.hasDocker,
7311
+ hasCI: plan.context.hasCI
7312
+ }
7313
+ };
7314
+ });
7315
+ app.get("/api/studio/agents", async () => {
7316
+ const info = office.getSummonPlan();
7317
+ return {
7318
+ ...info,
7319
+ agents: info.agents.map((a) => ({
7320
+ id: a.id,
7321
+ name: a.name,
7322
+ role: a.role,
7323
+ sprite: a.sprite,
7324
+ status: a.status,
7325
+ currentTask: a.currentTask,
7326
+ category: a.category,
7327
+ color: a.color,
7328
+ description: a.description
7329
+ }))
7330
+ };
7331
+ });
6829
7332
  }
6830
7333
  var init_studio = __esm({
6831
7334
  "src/server/routes/studio.ts"() {
@@ -6838,6 +7341,155 @@ var init_studio = __esm({
6838
7341
  }
6839
7342
  });
6840
7343
 
7344
+ // src/agents/task-router.ts
7345
+ var task_router_exports = {};
7346
+ __export(task_router_exports, {
7347
+ buildMatchContext: () => buildMatchContext,
7348
+ planSummon: () => planSummon
7349
+ });
7350
+ function buildMatchContext(scan2) {
7351
+ const languages = scan2.languages;
7352
+ const frameworks = scan2.frameworks.map((f) => f.name);
7353
+ const frontendFrameworks = ["React", "Vue", "Angular", "Svelte", "Next.js", "Nuxt"];
7354
+ const backendFrameworks = ["Express", "Fastify", "NestJS", "Django", "Flask", "Spring Boot", "Gin", "Actix"];
7355
+ const hasFE = frameworks.some((f) => frontendFrameworks.includes(f));
7356
+ const hasBE = frameworks.some((f) => backendFrameworks.includes(f));
7357
+ const projectType = hasFE && hasBE ? "fullstack" : hasFE ? "frontend" : hasBE ? "backend" : "unknown";
7358
+ const entityTypes = new Set(scan2.entities.map((e) => e.type));
7359
+ const hasModels = entityTypes.has("model") || entityTypes.has("class");
7360
+ const hasAPIs = entityTypes.has("api") || entityTypes.has("route");
7361
+ const hasFrontend = hasFE || (languages["html"] ?? 0) > 10 || (languages["css"] ?? 0) > 5;
7362
+ const allPaths = scan2.files.map((f) => f.path);
7363
+ const hasDocker = allPaths.some(
7364
+ (p) => p.includes("Dockerfile") || p.includes("docker-compose")
7365
+ );
7366
+ const hasCI = allPaths.some(
7367
+ (p) => p.includes(".github/workflows") || p.includes(".gitlab-ci") || p.includes("Jenkinsfile")
7368
+ );
7369
+ const riskCategories = [];
7370
+ return {
7371
+ languages,
7372
+ frameworks,
7373
+ projectType,
7374
+ fileCount: scan2.files.length,
7375
+ entityCount: scan2.entities.length,
7376
+ riskCategories,
7377
+ hasModels,
7378
+ hasAPIs,
7379
+ hasFrontend,
7380
+ hasDocker,
7381
+ hasCI
7382
+ };
7383
+ }
7384
+ function matchesTriggers(trigger, ctx) {
7385
+ const reasons = [];
7386
+ let score = 0;
7387
+ let checks = 0;
7388
+ if (trigger.languages && trigger.languages.length > 0) {
7389
+ checks++;
7390
+ const matched = trigger.languages.filter((l) => (ctx.languages[l] ?? 0) > 0);
7391
+ if (matched.length > 0) {
7392
+ score++;
7393
+ reasons.push(`\u8BED\u8A00\u5339\u914D: ${matched.join(", ")}`);
7394
+ }
7395
+ }
7396
+ if (trigger.frameworks && trigger.frameworks.length > 0) {
7397
+ checks++;
7398
+ const matched = trigger.frameworks.filter(
7399
+ (f) => ctx.frameworks.some((cf) => cf.toLowerCase() === f.toLowerCase())
7400
+ );
7401
+ if (matched.length > 0) {
7402
+ score++;
7403
+ reasons.push(`\u6846\u67B6\u5339\u914D: ${matched.join(", ")}`);
7404
+ }
7405
+ }
7406
+ if (trigger.projectTypes && trigger.projectTypes.length > 0) {
7407
+ checks++;
7408
+ if (trigger.projectTypes.includes(ctx.projectType)) {
7409
+ score++;
7410
+ reasons.push(`\u9879\u76EE\u7C7B\u578B\u5339\u914D: ${ctx.projectType}`);
7411
+ }
7412
+ }
7413
+ if (trigger.minEntities !== void 0) {
7414
+ checks++;
7415
+ if (ctx.entityCount >= trigger.minEntities) {
7416
+ score++;
7417
+ reasons.push(`\u5B9E\u4F53\u6570\u91CF\u6EE1\u8DB3: ${ctx.entityCount} \u2265 ${trigger.minEntities}`);
7418
+ }
7419
+ }
7420
+ if (trigger.riskCategories && trigger.riskCategories.length > 0) {
7421
+ checks++;
7422
+ const matched = trigger.riskCategories.filter((r) => ctx.riskCategories.includes(r));
7423
+ if (matched.length > 0) {
7424
+ score++;
7425
+ reasons.push(`\u98CE\u9669\u7C7B\u522B\u5339\u914D: ${matched.join(", ")}`);
7426
+ }
7427
+ }
7428
+ if (trigger.custom) {
7429
+ checks++;
7430
+ try {
7431
+ if (trigger.custom(ctx)) {
7432
+ score++;
7433
+ reasons.push("\u81EA\u5B9A\u4E49\u6761\u4EF6\u6EE1\u8DB3");
7434
+ }
7435
+ } catch {
7436
+ }
7437
+ }
7438
+ const matches = checks === 0 ? false : score > 0;
7439
+ const confidence = checks > 0 ? score / checks : 0;
7440
+ return {
7441
+ matches,
7442
+ reason: reasons.join("; ") || "\u65E0\u5339\u914D",
7443
+ confidence
7444
+ };
7445
+ }
7446
+ function planSummon(scan2, maxRoles = 8, riskCategories = []) {
7447
+ const registry = getRoleRegistry();
7448
+ const ctx = buildMatchContext(scan2);
7449
+ ctx.riskCategories = riskCategories;
7450
+ const summoned = [];
7451
+ const reasoning = [];
7452
+ const allRoles = registry.list();
7453
+ const coreRoles = allRoles.filter((r) => r.category === "core");
7454
+ for (const role of coreRoles) {
7455
+ summoned.push({ role, reason: "\u6838\u5FC3\u89D2\u8272(\u59CB\u7EC8\u53EC\u5524)", confidence: 1 });
7456
+ reasoning.push(`\u2705 ${role.name} \u2014 \u6838\u5FC3\u89D2\u8272`);
7457
+ }
7458
+ const nonCore = allRoles.filter((r) => r.category !== "core");
7459
+ const candidates = [];
7460
+ for (const role of nonCore) {
7461
+ const result = matchesTriggers(role.triggers, ctx);
7462
+ if (result.matches) {
7463
+ candidates.push({
7464
+ role,
7465
+ reason: result.reason,
7466
+ confidence: result.confidence
7467
+ });
7468
+ }
7469
+ }
7470
+ candidates.sort((a, b) => {
7471
+ if (a.role.priority !== b.role.priority) return a.role.priority - b.role.priority;
7472
+ return b.confidence - a.confidence;
7473
+ });
7474
+ const selected = candidates.slice(0, maxRoles);
7475
+ for (const s of selected) {
7476
+ summoned.push(s);
7477
+ reasoning.push(`\u{1F40A} ${s.role.name} \u2014 ${s.reason} (\u7F6E\u4FE1\u5EA6: ${(s.confidence * 100).toFixed(0)}%)`);
7478
+ }
7479
+ const skipped = candidates.slice(maxRoles);
7480
+ for (const s of skipped) {
7481
+ reasoning.push(`\u23ED\uFE0F ${s.role.name} \u2014 \u5339\u914D\u4F46\u8D85\u51FA\u9650\u5236 (\u7F6E\u4FE1\u5EA6: ${(s.confidence * 100).toFixed(0)}%)`);
7482
+ }
7483
+ return { roles: summoned, reasoning, context: ctx };
7484
+ }
7485
+ var init_task_router = __esm({
7486
+ "src/agents/task-router.ts"() {
7487
+ "use strict";
7488
+ init_esm_shims();
7489
+ init_role_registry();
7490
+ }
7491
+ });
7492
+
6841
7493
  // src/execution/coordinator.ts
6842
7494
  var coordinator_exports = {};
6843
7495
  __export(coordinator_exports, {
@@ -7216,7 +7868,7 @@ var init_croc_office = __esm({
7216
7868
  { id: "planner-croc", name: "\u89C4\u5212\u9CC4", role: "planner", sprite: "planner", status: "idle", tokensUsed: 0 },
7217
7869
  { id: "reporter-croc", name: "\u6C47\u62A5\u9CC4", role: "reporter", sprite: "reporter", status: "idle", tokensUsed: 0 }
7218
7870
  ];
7219
- CrocOffice = class {
7871
+ CrocOffice = class _CrocOffice {
7220
7872
  config;
7221
7873
  cwd;
7222
7874
  clients = /* @__PURE__ */ new Set();
@@ -7228,6 +7880,10 @@ var init_croc_office = __esm({
7228
7880
  lastExecutionMetrics = null;
7229
7881
  lastExecutionQuality = null;
7230
7882
  lastReports = [];
7883
+ static ACTIVE_AGENT_STATUSES = /* @__PURE__ */ new Set([
7884
+ "working",
7885
+ "thinking"
7886
+ ]);
7231
7887
  constructor(config, cwd) {
7232
7888
  this.config = config;
7233
7889
  this.cwd = cwd;
@@ -7262,10 +7918,34 @@ var init_croc_office = __esm({
7262
7918
  updateAgent(id, update) {
7263
7919
  const agent = this.agents.find((a) => a.id === id);
7264
7920
  if (agent) {
7921
+ const wasActive = this.isAgentActive(agent.status);
7265
7922
  Object.assign(agent, update);
7923
+ const isActive = this.isAgentActive(agent.status);
7266
7924
  this.broadcast("agent:update", this.agents);
7925
+ if (!wasActive && isActive) {
7926
+ this.broadcast("agent:assigned", {
7927
+ id: agent.id,
7928
+ name: agent.name,
7929
+ role: agent.role,
7930
+ status: agent.status,
7931
+ currentTask: agent.currentTask ?? null,
7932
+ at: Date.now()
7933
+ });
7934
+ } else if (wasActive && !isActive) {
7935
+ this.broadcast("agent:released", {
7936
+ id: agent.id,
7937
+ name: agent.name,
7938
+ role: agent.role,
7939
+ status: agent.status,
7940
+ currentTask: agent.currentTask ?? null,
7941
+ at: Date.now()
7942
+ });
7943
+ }
7267
7944
  }
7268
7945
  }
7946
+ isAgentActive(status) {
7947
+ return _CrocOffice.ACTIVE_AGENT_STATUSES.has(status);
7948
+ }
7269
7949
  isRunning() {
7270
7950
  return this.running;
7271
7951
  }
@@ -7395,12 +8075,75 @@ var init_croc_office = __esm({
7395
8075
  /** Reset all agents to idle */
7396
8076
  resetAgents() {
7397
8077
  for (const agent of this.agents) {
8078
+ const wasActive = this.isAgentActive(agent.status);
7398
8079
  agent.status = "idle";
7399
8080
  agent.currentTask = void 0;
7400
8081
  agent.progress = void 0;
8082
+ if (wasActive) {
8083
+ this.broadcast("agent:released", {
8084
+ id: agent.id,
8085
+ name: agent.name,
8086
+ role: agent.role,
8087
+ status: agent.status,
8088
+ currentTask: null,
8089
+ at: Date.now()
8090
+ });
8091
+ }
7401
8092
  }
7402
8093
  this.broadcast("agent:update", this.agents);
7403
8094
  }
8095
+ /**
8096
+ * Dynamically summon crocs based on a project scan result.
8097
+ * Core 6 are always present; additional expert crocs are added based on project characteristics.
8098
+ */
8099
+ async summonForProject(scan2, riskCategories = []) {
8100
+ const { planSummon: planSummon2 } = await Promise.resolve().then(() => (init_task_router(), task_router_exports));
8101
+ const plan = planSummon2(scan2, 8, riskCategories);
8102
+ const coreIds = new Set(DEFAULT_AGENTS.map((a) => a.id));
8103
+ this.agents = DEFAULT_AGENTS.map((a) => ({ ...a }));
8104
+ for (const summoned of plan.roles) {
8105
+ if (coreIds.has(summoned.role.id)) continue;
8106
+ const agent = {
8107
+ id: summoned.role.id,
8108
+ name: summoned.role.name,
8109
+ role: summoned.role.id.replace(/-croc$/, ""),
8110
+ sprite: summoned.role.sprite,
8111
+ status: "idle",
8112
+ tokensUsed: 0,
8113
+ category: summoned.role.category,
8114
+ color: summoned.role.color,
8115
+ description: summoned.role.description
8116
+ };
8117
+ this.agents.push(agent);
8118
+ }
8119
+ this.broadcast("agent:update", this.agents);
8120
+ for (const line of plan.reasoning) {
8121
+ this.log(line);
8122
+ }
8123
+ this.log(`\u{1F40A} \u5171\u53EC\u5524 ${this.agents.length} \u4E2A\u9CC4\u9C7C\u4E13\u5BB6 (${plan.roles.length - DEFAULT_AGENTS.length} \u4E2A\u52A8\u6001\u89D2\u8272)`);
8124
+ const dynamicAgents = this.agents.filter((a) => !coreIds.has(a.id));
8125
+ for (let i = 0; i < dynamicAgents.length; i++) {
8126
+ const agent = dynamicAgents[i];
8127
+ setTimeout(() => {
8128
+ this.updateAgent(agent.id, { status: "working", currentTask: "\u5206\u6790\u9879\u76EE\u4E2D\u2026" });
8129
+ setTimeout(() => {
8130
+ this.updateAgent(agent.id, { status: "idle", currentTask: void 0 });
8131
+ }, 2e3 + Math.random() * 1e3);
8132
+ }, 300 * i);
8133
+ }
8134
+ return plan;
8135
+ }
8136
+ /** Get the current summon plan context */
8137
+ getSummonPlan() {
8138
+ const coreIds = new Set(DEFAULT_AGENTS.map((a) => a.id));
8139
+ const coreCount = this.agents.filter((a) => coreIds.has(a.id)).length;
8140
+ return {
8141
+ agentCount: this.agents.length,
8142
+ coreCount,
8143
+ dynamicCount: this.agents.length - coreCount,
8144
+ agents: this.agents
8145
+ };
8146
+ }
7404
8147
  /** Get last pipeline result */
7405
8148
  getLastPipelineResult() {
7406
8149
  return this.lastPipelineResult;
@@ -7827,7 +8570,8 @@ async function startServer(opts) {
7827
8570
  await app.register(fastifyStatic, {
7828
8571
  root: webDir,
7829
8572
  prefix: "/",
7830
- decorateReply: false
8573
+ decorateReply: false,
8574
+ index: false
7831
8575
  });
7832
8576
  }
7833
8577
  const office = new CrocOffice(opts.config, opts.cwd);
@@ -7841,17 +8585,20 @@ async function startServer(opts) {
7841
8585
  socket.on("close", () => office.removeClient(socket));
7842
8586
  });
7843
8587
  });
8588
+ app.get("/index-studio.html", (_req, reply) => {
8589
+ reply.redirect("/studio");
8590
+ });
8591
+ app.get("/index-v2-pixel.html", (_req, reply) => {
8592
+ reply.redirect("/pixel");
8593
+ });
7844
8594
  app.setNotFoundHandler((req, reply) => {
7845
8595
  if (req.url.startsWith("/api/")) {
7846
8596
  reply.code(404).send({ error: "Not found" });
7847
8597
  return;
7848
8598
  }
7849
- const studioPath = join17(webDir, "index-studio.html");
7850
- const indexPath = join17(webDir, "index.html");
7851
- if (existsSync20(studioPath)) {
7852
- reply.sendFile("index-studio.html");
7853
- } else if (existsSync20(indexPath)) {
7854
- reply.sendFile("index.html");
8599
+ const builtIndexPath = join17(webDir, "dist", "index.html");
8600
+ if (existsSync20(builtIndexPath)) {
8601
+ reply.sendFile("dist/index.html");
7855
8602
  } else {
7856
8603
  reply.code(200).header("content-type", "text/html").send(getEmbeddedHtml());
7857
8604
  }