@julioventura/opensquad 0.1.17
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/README.md +433 -0
- package/_opensquad/config/playwright.config.json +11 -0
- package/_opensquad/core/architect.agent.yaml +112 -0
- package/_opensquad/core/best-practices/_catalog.yaml +126 -0
- package/_opensquad/core/best-practices/blog-post.md +132 -0
- package/_opensquad/core/best-practices/blog-seo.md +127 -0
- package/_opensquad/core/best-practices/brand-resolution-checklist.md +172 -0
- package/_opensquad/core/best-practices/copywriting.md +441 -0
- package/_opensquad/core/best-practices/data-analysis.md +401 -0
- package/_opensquad/core/best-practices/email-newsletter.md +118 -0
- package/_opensquad/core/best-practices/email-sales.md +110 -0
- package/_opensquad/core/best-practices/image-design.md +348 -0
- package/_opensquad/core/best-practices/instagram-feed.md +235 -0
- package/_opensquad/core/best-practices/instagram-reels.md +112 -0
- package/_opensquad/core/best-practices/instagram-stories.md +107 -0
- package/_opensquad/core/best-practices/linkedin-article.md +116 -0
- package/_opensquad/core/best-practices/linkedin-post.md +121 -0
- package/_opensquad/core/best-practices/researching.md +349 -0
- package/_opensquad/core/best-practices/review.md +269 -0
- package/_opensquad/core/best-practices/run-recovery.md +61 -0
- package/_opensquad/core/best-practices/social-networks-publishing.md +327 -0
- package/_opensquad/core/best-practices/squad-creation-checklist.md +32 -0
- package/_opensquad/core/best-practices/strategist.md +344 -0
- package/_opensquad/core/best-practices/technical-writing.md +365 -0
- package/_opensquad/core/best-practices/twitter-post.md +105 -0
- package/_opensquad/core/best-practices/twitter-thread.md +122 -0
- package/_opensquad/core/best-practices/whatsapp-broadcast.md +107 -0
- package/_opensquad/core/best-practices/youtube-script.md +122 -0
- package/_opensquad/core/best-practices/youtube-shorts.md +112 -0
- package/_opensquad/core/defaults/youtube-video-assembly.json +84 -0
- package/_opensquad/core/prompts/build.prompt.md +613 -0
- package/_opensquad/core/prompts/design.prompt.md +606 -0
- package/_opensquad/core/prompts/discovery.prompt.md +377 -0
- package/_opensquad/core/prompts/sherlock-instagram.md +123 -0
- package/_opensquad/core/prompts/sherlock-linkedin.md +73 -0
- package/_opensquad/core/prompts/sherlock-shared.md +684 -0
- package/_opensquad/core/prompts/sherlock-twitter.md +78 -0
- package/_opensquad/core/prompts/sherlock-youtube.md +85 -0
- package/_opensquad/core/runner.pipeline.md +743 -0
- package/_opensquad/core/skills.engine.md +384 -0
- package/bin/opensquad.js +108 -0
- package/dashboard/index.html +15 -0
- package/dashboard/package-lock.json +1964 -0
- package/dashboard/package.json +28 -0
- package/dashboard/public/assets/avatars/Female1_1wave.png +0 -0
- package/dashboard/public/assets/avatars/Female1_2wave.png +0 -0
- package/dashboard/public/assets/avatars/Female1_blink.png +0 -0
- package/dashboard/public/assets/avatars/Female1_talk.png +0 -0
- package/dashboard/public/assets/avatars/Female2_1wave.png +0 -0
- package/dashboard/public/assets/avatars/Female2_2wave.png +0 -0
- package/dashboard/public/assets/avatars/Female2_blink.png +0 -0
- package/dashboard/public/assets/avatars/Female2_talk.png +0 -0
- package/dashboard/public/assets/avatars/Female3_blink.png +0 -0
- package/dashboard/public/assets/avatars/Female3_talk.png +0 -0
- package/dashboard/public/assets/avatars/Female3_wave.png +0 -0
- package/dashboard/public/assets/avatars/Female4_blink.png +0 -0
- package/dashboard/public/assets/avatars/Female4_talk.png +0 -0
- package/dashboard/public/assets/avatars/Female4_wave.png +0 -0
- package/dashboard/public/assets/avatars/Female5_blink.png +0 -0
- package/dashboard/public/assets/avatars/Female5_talk.png +0 -0
- package/dashboard/public/assets/avatars/Female5_wave.png +0 -0
- package/dashboard/public/assets/avatars/Female6_blink.png +0 -0
- package/dashboard/public/assets/avatars/Female6_talk.png +0 -0
- package/dashboard/public/assets/avatars/Female6_wave.png +0 -0
- package/dashboard/public/assets/avatars/Male1_1wave.png +0 -0
- package/dashboard/public/assets/avatars/Male1_2wave.png +0 -0
- package/dashboard/public/assets/avatars/Male1_blink.png +0 -0
- package/dashboard/public/assets/avatars/Male1_talk.png +0 -0
- package/dashboard/public/assets/avatars/Male2_1wave.png +0 -0
- package/dashboard/public/assets/avatars/Male2_2wave.png +0 -0
- package/dashboard/public/assets/avatars/Male2_blink.png +0 -0
- package/dashboard/public/assets/avatars/Male2_talk.png +0 -0
- package/dashboard/public/assets/avatars/Male3_blink.png +0 -0
- package/dashboard/public/assets/avatars/Male3_talk.png +0 -0
- package/dashboard/public/assets/avatars/Male3_wave.png +0 -0
- package/dashboard/public/assets/avatars/Male4_blink.png +0 -0
- package/dashboard/public/assets/avatars/Male4_talk.png +0 -0
- package/dashboard/public/assets/avatars/Male4_wave.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_black_down.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_black_down_coding-1.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_black_down_coding.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_black_up.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_white_down.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_white_down_coding-1.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_white_down_coding.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_white_up.png +0 -0
- package/dashboard/public/assets/furniture/armchair_tan.png +0 -0
- package/dashboard/public/assets/furniture/armchair_tan_down.png +0 -0
- package/dashboard/public/assets/furniture/backpack_blue.png +0 -0
- package/dashboard/public/assets/furniture/backpack_red.png +0 -0
- package/dashboard/public/assets/furniture/blinds.png +0 -0
- package/dashboard/public/assets/furniture/blinds_large_closed_white.png +0 -0
- package/dashboard/public/assets/furniture/bookshelf.png +0 -0
- package/dashboard/public/assets/furniture/bookshelf_purple_tall.png +0 -0
- package/dashboard/public/assets/furniture/bulletin_board.png +0 -0
- package/dashboard/public/assets/furniture/clock.png +0 -0
- package/dashboard/public/assets/furniture/coffee_mug.png +0 -0
- package/dashboard/public/assets/furniture/coffee_mug_blue.png +0 -0
- package/dashboard/public/assets/furniture/coffee_table.png +0 -0
- package/dashboard/public/assets/furniture/coffeepot_right.png +0 -0
- package/dashboard/public/assets/furniture/coffeetable_black_horizontal.png +0 -0
- package/dashboard/public/assets/furniture/couch.png +0 -0
- package/dashboard/public/assets/furniture/couch_tan_down.png +0 -0
- package/dashboard/public/assets/furniture/cushion_blue.png +0 -0
- package/dashboard/public/assets/furniture/cushion_tan.png +0 -0
- package/dashboard/public/assets/furniture/desk_wood.png +0 -0
- package/dashboard/public/assets/furniture/fancy_rug.png +0 -0
- package/dashboard/public/assets/furniture/fancy_rug_wide.png +0 -0
- package/dashboard/public/assets/furniture/flowers1.png +0 -0
- package/dashboard/public/assets/furniture/flowers2.png +0 -0
- package/dashboard/public/assets/furniture/lamp_tan.png +0 -0
- package/dashboard/public/assets/furniture/lantern.png +0 -0
- package/dashboard/public/assets/furniture/monstera.png +0 -0
- package/dashboard/public/assets/furniture/monstera_small.png +0 -0
- package/dashboard/public/assets/furniture/picture_frame.png +0 -0
- package/dashboard/public/assets/furniture/plant1.png +0 -0
- package/dashboard/public/assets/furniture/plant2.png +0 -0
- package/dashboard/public/assets/furniture/plant3.png +0 -0
- package/dashboard/public/assets/furniture/plant_poof.png +0 -0
- package/dashboard/public/assets/furniture/plant_spindly.png +0 -0
- package/dashboard/public/assets/furniture/poster_blue.png +0 -0
- package/dashboard/public/assets/furniture/rug.png +0 -0
- package/dashboard/public/assets/furniture/succulent_blue.png +0 -0
- package/dashboard/public/assets/furniture/succulent_green.png +0 -0
- package/dashboard/public/assets/furniture/treasurechest_closed_gold.png +0 -0
- package/dashboard/public/assets/furniture/water_cooler_better.png +0 -0
- package/dashboard/public/assets/furniture/whiteboard.png +0 -0
- package/dashboard/public/assets/furniture/whiteboard_stand_graph.png +0 -0
- package/dashboard/public/assets/furniture/window_blinds_open.png +0 -0
- package/dashboard/src/App.tsx +46 -0
- package/dashboard/src/components/RunDashboardButton.tsx +92 -0
- package/dashboard/src/components/SquadCard.tsx +49 -0
- package/dashboard/src/components/SquadSelector.tsx +67 -0
- package/dashboard/src/components/StatusBadge.tsx +32 -0
- package/dashboard/src/components/StatusBar.tsx +116 -0
- package/dashboard/src/hooks/useSquadSocket.ts +135 -0
- package/dashboard/src/lib/formatTime.ts +16 -0
- package/dashboard/src/lib/normalizeState.ts +25 -0
- package/dashboard/src/main.tsx +10 -0
- package/dashboard/src/office/AgentSprite.ts +241 -0
- package/dashboard/src/office/OfficeScene.ts +153 -0
- package/dashboard/src/office/PhaserGame.tsx +80 -0
- package/dashboard/src/office/RoomBuilder.ts +190 -0
- package/dashboard/src/office/assetKeys.ts +150 -0
- package/dashboard/src/office/palette.ts +32 -0
- package/dashboard/src/plugin/squadWatcher.ts +397 -0
- package/dashboard/src/store/useSquadStore.ts +56 -0
- package/dashboard/src/styles/globals.css +36 -0
- package/dashboard/src/types/state.ts +63 -0
- package/dashboard/src/vite-env.d.ts +1 -0
- package/dashboard/tsconfig.json +24 -0
- package/dashboard/vite.config.ts +13 -0
- package/package.json +59 -0
- package/public/sfx/slide-transition-sfx.mp3 +0 -0
- package/skills/README.md +84 -0
- package/skills/apify/SKILL.md +55 -0
- package/skills/blotato/SKILL.md +63 -0
- package/skills/canva/SKILL.md +60 -0
- package/skills/higgsfield/SKILL.md +147 -0
- package/skills/image-ai-generator/SKILL.md +124 -0
- package/skills/image-ai-generator/scripts/generate.py +175 -0
- package/skills/image-creator/SKILL.md +166 -0
- package/skills/image-creator/editorial-slide-template.js +645 -0
- package/skills/image-fetcher/SKILL.md +91 -0
- package/skills/imgbb-uploader/SKILL.md +73 -0
- package/skills/imgbb-uploader/scripts/upload.js +125 -0
- package/skills/instagram-publisher/README.md +36 -0
- package/skills/instagram-publisher/SKILL.md +231 -0
- package/skills/instagram-publisher/scripts/publish-playwright.js +418 -0
- package/skills/instagram-publisher/scripts/publish.js +521 -0
- package/skills/opensquad-agent-creator/SKILL.md +192 -0
- package/skills/opensquad-skill-creator/SKILL.md +420 -0
- package/skills/opensquad-skill-creator/agents/analyzer.md +274 -0
- package/skills/opensquad-skill-creator/agents/comparator.md +202 -0
- package/skills/opensquad-skill-creator/agents/grader.md +223 -0
- package/skills/opensquad-skill-creator/assets/eval_review.html +146 -0
- package/skills/opensquad-skill-creator/eval-viewer/generate_review.py +471 -0
- package/skills/opensquad-skill-creator/eval-viewer/viewer.html +1325 -0
- package/skills/opensquad-skill-creator/references/schemas.md +430 -0
- package/skills/opensquad-skill-creator/references/skill-format.md +235 -0
- package/skills/opensquad-skill-creator/scripts/__init__.py +0 -0
- package/skills/opensquad-skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/skills/opensquad-skill-creator/scripts/quick_validate.py +103 -0
- package/skills/opensquad-skill-creator/scripts/run_eval.py +310 -0
- package/skills/opensquad-skill-creator/scripts/utils.py +47 -0
- package/skills/pdf-extractor/SKILL.md +57 -0
- package/skills/pdf-extractor/scripts/extract.py +82 -0
- package/skills/resend/SKILL.md +80 -0
- package/skills/run-dashboard/README.md +93 -0
- package/skills/run-dashboard/SKILL.md +173 -0
- package/skills/run-dashboard/scripts/finalize-state.js +273 -0
- package/skills/run-dashboard/scripts/generate.js +1296 -0
- package/skills/run-dashboard/scripts/serve.js +135 -0
- package/skills/run-dashboard/templates/run-dashboard-simple.template.html +191 -0
- package/skills/run-dashboard/templates/run-dashboard.template.html +1164 -0
- package/skills/smtp-sender/SKILL.md +88 -0
- package/skills/smtp-sender/scripts/send.js +478 -0
- package/skills/template-designer/SKILL.md +201 -0
- package/skills/template-designer/base-templates/model-a.html +27 -0
- package/skills/template-designer/base-templates/model-b.html +31 -0
- package/skills/template-designer/base-templates/model-c.html +42 -0
- package/skills/youtube-publisher/SKILL.md +232 -0
- package/skills/youtube-publisher/scripts/publish.js +2078 -0
- package/src/agents-cli.js +158 -0
- package/src/agents.js +134 -0
- package/src/i18n.js +48 -0
- package/src/init.js +442 -0
- package/src/locales/en.json +79 -0
- package/src/locales/es.json +78 -0
- package/src/locales/pt-BR.json +78 -0
- package/src/logger.js +38 -0
- package/src/prompt.js +46 -0
- package/src/readme/README.md +146 -0
- package/src/runs.js +318 -0
- package/src/skills-cli.js +157 -0
- package/src/skills.js +146 -0
- package/src/supabase-cli.js +584 -0
- package/src/update.js +169 -0
- package/templates/_opensquad/.opensquad-version +1 -0
- package/templates/_opensquad/_investigations/.gitkeep +0 -0
- package/templates/ide-templates/antigravity/.agent/rules/opensquad.md +68 -0
- package/templates/ide-templates/antigravity/.agent/workflows/opensquad.md +102 -0
- package/templates/ide-templates/claude-code/.claude/skills/opensquad/SKILL.md +182 -0
- package/templates/ide-templates/claude-code/.mcp.json +8 -0
- package/templates/ide-templates/claude-code/CLAUDE.md +57 -0
- package/templates/ide-templates/codex/.agents/skills/opensquad/SKILL.md +6 -0
- package/templates/ide-templates/codex/AGENTS.md +120 -0
- package/templates/ide-templates/cursor/.cursor/commands/opensquad.md +9 -0
- package/templates/ide-templates/cursor/.cursor/mcp.json +8 -0
- package/templates/ide-templates/cursor/.cursor/rules/opensquad.mdc +62 -0
- package/templates/ide-templates/cursor/.cursorignore +3 -0
- package/templates/ide-templates/gemini-cli/.gemini/settings.json +8 -0
- package/templates/ide-templates/gemini-cli/.gemini/skills/opensquad/SKILL.md +186 -0
- package/templates/ide-templates/gemini-cli/GEMINI.md +57 -0
- package/templates/ide-templates/opencode/.opencode/commands/opensquad.md +9 -0
- package/templates/ide-templates/opencode/AGENTS.md +120 -0
- package/templates/ide-templates/qwen-code/.qwen/settings.json +8 -0
- package/templates/ide-templates/qwen-code/.qwen/skills/opensquad/SKILL.md +182 -0
- package/templates/ide-templates/qwen-code/QWEN.md +57 -0
- package/templates/ide-templates/trae/.trae/mcp.json +8 -0
- package/templates/ide-templates/trae/.trae/rules/opensquad.md +64 -0
- package/templates/ide-templates/vscode-copilot/.github/copilot-instructions.md +59 -0
- package/templates/ide-templates/vscode-copilot/.github/prompts/opensquad.prompt.md +209 -0
- package/templates/ide-templates/vscode-copilot/.vscode/mcp.json +8 -0
- package/templates/ide-templates/vscode-copilot/.vscode/settings.json +3 -0
- package/templates/package.json +8 -0
- package/templates/squads/.gitkeep +0 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import type { Plugin, ViteDevServer } from "vite";
|
|
2
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
3
|
+
import type { Server, IncomingMessage } from "node:http";
|
|
4
|
+
import type { Duplex } from "node:stream";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import fsp from "node:fs/promises";
|
|
7
|
+
import { watch as chokidarWatch } from "chokidar";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { pathToFileURL } from "node:url";
|
|
10
|
+
import { parse as parseYaml } from "yaml";
|
|
11
|
+
import type { SquadInfo, SquadState, WsMessage } from "../types/state";
|
|
12
|
+
|
|
13
|
+
type WriteDashboardFilesFn = (options: {
|
|
14
|
+
runDir: string;
|
|
15
|
+
workspaceRoot: string;
|
|
16
|
+
outputHtml: string;
|
|
17
|
+
outputJson: string;
|
|
18
|
+
templatePath: string;
|
|
19
|
+
refreshMetrics: boolean;
|
|
20
|
+
}) => Promise<{ payload: unknown }>;
|
|
21
|
+
|
|
22
|
+
let cachedWriteDashboardFiles: WriteDashboardFilesFn | null = null;
|
|
23
|
+
|
|
24
|
+
function resolveSquadsDir(): string {
|
|
25
|
+
const candidates = [
|
|
26
|
+
path.resolve(process.cwd(), "../squads"), // started from dashboard/
|
|
27
|
+
path.resolve(process.cwd(), "squads"), // started from project root
|
|
28
|
+
];
|
|
29
|
+
for (const c of candidates) {
|
|
30
|
+
if (fs.existsSync(c)) return c;
|
|
31
|
+
}
|
|
32
|
+
return path.resolve(process.cwd(), "../squads"); // default (will be created on demand)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function discoverSquads(squadsDir: string): Promise<SquadInfo[]> {
|
|
36
|
+
let entries;
|
|
37
|
+
try {
|
|
38
|
+
entries = await fsp.readdir(squadsDir, { withFileTypes: true });
|
|
39
|
+
} catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const squads: SquadInfo[] = [];
|
|
44
|
+
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
if (!entry.isDirectory()) continue;
|
|
47
|
+
if (entry.name.startsWith(".") || entry.name.startsWith("_")) continue;
|
|
48
|
+
|
|
49
|
+
const yamlPath = path.join(squadsDir, entry.name, "squad.yaml");
|
|
50
|
+
try {
|
|
51
|
+
const raw = await fsp.readFile(yamlPath, "utf-8");
|
|
52
|
+
const parsed = parseYaml(raw);
|
|
53
|
+
const s = parsed?.squad;
|
|
54
|
+
if (s) {
|
|
55
|
+
squads.push({
|
|
56
|
+
code: typeof s.code === "string" ? s.code : entry.name,
|
|
57
|
+
name: typeof s.name === "string" ? s.name : entry.name,
|
|
58
|
+
description: typeof s.description === "string" ? s.description : "",
|
|
59
|
+
icon: typeof s.icon === "string" ? s.icon : "\u{1F4CB}",
|
|
60
|
+
agents: Array.isArray(s.agents) ? (s.agents as unknown[]).filter((a): a is string => typeof a === "string") : [],
|
|
61
|
+
});
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
// No squad.yaml or invalid YAML — fall through to default
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
squads.push({
|
|
69
|
+
code: entry.name,
|
|
70
|
+
name: entry.name,
|
|
71
|
+
description: "",
|
|
72
|
+
icon: "\u{1F4CB}",
|
|
73
|
+
agents: [],
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return squads;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function isValidState(data: unknown): data is SquadState {
|
|
81
|
+
if (!data || typeof data !== "object") return false;
|
|
82
|
+
const d = data as Record<string, unknown>;
|
|
83
|
+
return (
|
|
84
|
+
typeof d.status === "string" &&
|
|
85
|
+
d.step != null && typeof d.step === "object" &&
|
|
86
|
+
Array.isArray(d.agents)
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function readActiveStates(squadsDir: string): Promise<Record<string, SquadState>> {
|
|
91
|
+
const states: Record<string, SquadState> = {};
|
|
92
|
+
|
|
93
|
+
let entries;
|
|
94
|
+
try {
|
|
95
|
+
entries = await fsp.readdir(squadsDir, { withFileTypes: true });
|
|
96
|
+
} catch {
|
|
97
|
+
return states;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
if (!entry.isDirectory()) continue;
|
|
102
|
+
const statePath = path.join(squadsDir, entry.name, "state.json");
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const raw = await fsp.readFile(statePath, "utf-8");
|
|
106
|
+
const parsed = JSON.parse(raw);
|
|
107
|
+
if (isValidState(parsed)) {
|
|
108
|
+
states[entry.name] = parsed;
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
// Skip missing or invalid JSON
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return states;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function buildSnapshot(squadsDir: string): Promise<WsMessage> {
|
|
119
|
+
return {
|
|
120
|
+
type: "SNAPSHOT",
|
|
121
|
+
squads: await discoverSquads(squadsDir),
|
|
122
|
+
activeStates: await readActiveStates(squadsDir),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function broadcast(wss: WebSocketServer, msg: WsMessage) {
|
|
127
|
+
const data = JSON.stringify(msg);
|
|
128
|
+
for (const client of wss.clients) {
|
|
129
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
130
|
+
try {
|
|
131
|
+
client.send(data);
|
|
132
|
+
} catch {
|
|
133
|
+
// Client connection dying — ws library will clean it up
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function resolveDashboardRunDir(squadsDir: string, squadCode: string): Promise<string | null> {
|
|
140
|
+
const squadDir = path.join(squadsDir, squadCode);
|
|
141
|
+
const outputDir = path.join(squadDir, "output");
|
|
142
|
+
|
|
143
|
+
if (!fs.existsSync(outputDir)) return null;
|
|
144
|
+
|
|
145
|
+
const entries = await fsp.readdir(outputDir, { withFileTypes: true });
|
|
146
|
+
const candidateDirs = entries
|
|
147
|
+
.filter((entry) => entry.isDirectory() && entry.name !== "archive" && !entry.name.startsWith("."))
|
|
148
|
+
.map((entry) => path.join(outputDir, entry.name))
|
|
149
|
+
.sort((left, right) => path.basename(right).localeCompare(path.basename(left)));
|
|
150
|
+
|
|
151
|
+
for (const candidate of candidateDirs) {
|
|
152
|
+
if (await directoryHasRunDashboardArtifacts(candidate)) return candidate;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (await directoryHasRunDashboardArtifacts(outputDir)) return outputDir;
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function directoryHasRunDashboardArtifacts(targetDir: string): Promise<boolean> {
|
|
160
|
+
const candidateFiles = [
|
|
161
|
+
"publish-result.md",
|
|
162
|
+
"content-package.md",
|
|
163
|
+
"run-dashboard.html",
|
|
164
|
+
"run-dashboard.data.json",
|
|
165
|
+
"state.json",
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
for (const fileName of candidateFiles) {
|
|
169
|
+
try {
|
|
170
|
+
const stats = await fsp.stat(path.join(targetDir, fileName));
|
|
171
|
+
if (stats.isFile()) return true;
|
|
172
|
+
} catch {
|
|
173
|
+
// Keep scanning
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function detectContentType(filePath: string) {
|
|
181
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
182
|
+
if (extension === ".html") return "text/html; charset=utf-8";
|
|
183
|
+
if (extension === ".json") return "application/json; charset=utf-8";
|
|
184
|
+
if (extension === ".md") return "text/markdown; charset=utf-8";
|
|
185
|
+
if (extension === ".jpg" || extension === ".jpeg") return "image/jpeg";
|
|
186
|
+
if (extension === ".png") return "image/png";
|
|
187
|
+
if (extension === ".webp") return "image/webp";
|
|
188
|
+
return "application/octet-stream";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function sendJson(res: { setHeader: (name: string, value: string) => void; end: (body: string) => void; writeHead: (code: number) => void }, statusCode: number, payload: unknown) {
|
|
192
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
193
|
+
res.setHeader("Cache-Control", "no-store");
|
|
194
|
+
res.writeHead(statusCode);
|
|
195
|
+
res.end(JSON.stringify(payload));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function loadWriteDashboardFiles(workspaceRoot: string): Promise<WriteDashboardFilesFn> {
|
|
199
|
+
if (cachedWriteDashboardFiles) return cachedWriteDashboardFiles;
|
|
200
|
+
|
|
201
|
+
const moduleUrl = pathToFileURL(
|
|
202
|
+
path.join(workspaceRoot, "skills", "run-dashboard", "scripts", "generate.js"),
|
|
203
|
+
).href;
|
|
204
|
+
const mod = await import(/* @vite-ignore */ moduleUrl) as { writeDashboardFiles: WriteDashboardFilesFn };
|
|
205
|
+
cachedWriteDashboardFiles = mod.writeDashboardFiles;
|
|
206
|
+
return cachedWriteDashboardFiles;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function squadWatcherPlugin(): Plugin {
|
|
210
|
+
return {
|
|
211
|
+
name: "squad-watcher",
|
|
212
|
+
configureServer(server: ViteDevServer) {
|
|
213
|
+
if (!server.httpServer) {
|
|
214
|
+
server.config.logger.warn("[squad-watcher] no httpServer — skipping");
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const squadsDir = resolveSquadsDir();
|
|
219
|
+
server.config.logger.info(`[squad-watcher] squads dir: ${squadsDir}`);
|
|
220
|
+
|
|
221
|
+
// Create WebSocket server with noServer to avoid intercepting Vite's HMR
|
|
222
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
223
|
+
(server.httpServer as Server).on("upgrade", (req: IncomingMessage, socket: Duplex, head: Buffer) => {
|
|
224
|
+
if (req.url === "/__squads_ws") {
|
|
225
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
226
|
+
wss.emit("connection", ws, req);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// Let Vite handle all other upgrade requests (HMR)
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Send snapshot on new connection
|
|
233
|
+
wss.on("connection", async (ws) => {
|
|
234
|
+
try {
|
|
235
|
+
const snap = await buildSnapshot(squadsDir);
|
|
236
|
+
ws.send(JSON.stringify(snap));
|
|
237
|
+
} catch {
|
|
238
|
+
// Connection may have closed before snapshot was ready
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Ensure squads directory exists
|
|
243
|
+
fsp.mkdir(squadsDir, { recursive: true }).catch((err) => {
|
|
244
|
+
server.config.logger.error(`[squad-watcher] failed to create squads dir: ${err.message}`);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// REST API fallback — serves snapshot over HTTP for polling clients
|
|
248
|
+
server.middlewares.use(async (req, res, next) => {
|
|
249
|
+
const requestUrl = new URL(req.url || "/", "http://dashboard.local");
|
|
250
|
+
|
|
251
|
+
if (requestUrl.pathname === "/api/run-dashboard") {
|
|
252
|
+
const squadCode = requestUrl.searchParams.get("squad");
|
|
253
|
+
if (!squadCode) {
|
|
254
|
+
return sendJson(res, 400, { error: "Missing squad parameter" });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const runDir = await resolveDashboardRunDir(squadsDir, squadCode);
|
|
258
|
+
if (!runDir) {
|
|
259
|
+
return sendJson(res, 404, { available: false, url: null, runId: null, isLegacyOutputRoot: false });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return sendJson(res, 200, {
|
|
263
|
+
available: true,
|
|
264
|
+
url: `/__run_dashboard/${encodeURIComponent(squadCode)}/run-dashboard.html`,
|
|
265
|
+
runId: path.basename(runDir) === "output" ? "legacy-output-root" : path.basename(runDir),
|
|
266
|
+
isLegacyOutputRoot: path.basename(runDir) === "output",
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (requestUrl.pathname.startsWith("/__run_dashboard/")) {
|
|
271
|
+
const [, , encodedSquad, ...rest] = requestUrl.pathname.split("/");
|
|
272
|
+
const squadCode = decodeURIComponent(encodedSquad || "");
|
|
273
|
+
const runDir = await resolveDashboardRunDir(squadsDir, squadCode);
|
|
274
|
+
if (!runDir) {
|
|
275
|
+
res.writeHead(404);
|
|
276
|
+
res.end("Run dashboard not found");
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const workspaceRoot = path.dirname(squadsDir);
|
|
281
|
+
const templatePath = path.join(workspaceRoot, "skills", "run-dashboard", "templates", "run-dashboard.template.html");
|
|
282
|
+
const outputHtml = path.join(runDir, "run-dashboard.html");
|
|
283
|
+
const outputJson = path.join(runDir, "run-dashboard.data.json");
|
|
284
|
+
const relativeTarget = rest.join("/") || "run-dashboard.html";
|
|
285
|
+
const writeDashboardFiles = await loadWriteDashboardFiles(workspaceRoot);
|
|
286
|
+
|
|
287
|
+
if (req.method === "POST" && relativeTarget === "__run_dashboard_refresh__") {
|
|
288
|
+
const { payload } = await writeDashboardFiles({
|
|
289
|
+
runDir,
|
|
290
|
+
workspaceRoot,
|
|
291
|
+
outputHtml,
|
|
292
|
+
outputJson,
|
|
293
|
+
templatePath,
|
|
294
|
+
refreshMetrics: true,
|
|
295
|
+
});
|
|
296
|
+
return sendJson(res, 200, payload);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (relativeTarget === "run-dashboard.html" || relativeTarget === "run-dashboard.data.json") {
|
|
300
|
+
await writeDashboardFiles({
|
|
301
|
+
runDir,
|
|
302
|
+
workspaceRoot,
|
|
303
|
+
outputHtml,
|
|
304
|
+
outputJson,
|
|
305
|
+
templatePath,
|
|
306
|
+
refreshMetrics: false,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const requestedPath = path.resolve(path.join(runDir, relativeTarget));
|
|
311
|
+
const normalizedRunDir = runDir.toLowerCase();
|
|
312
|
+
const normalizedRequestedPath = requestedPath.toLowerCase();
|
|
313
|
+
if (!(normalizedRequestedPath === normalizedRunDir || normalizedRequestedPath.startsWith(`${normalizedRunDir}${path.sep}`))) {
|
|
314
|
+
res.writeHead(403);
|
|
315
|
+
res.end("Forbidden");
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
const content = await fsp.readFile(requestedPath);
|
|
321
|
+
res.setHeader("Content-Type", detectContentType(requestedPath));
|
|
322
|
+
res.setHeader("Cache-Control", "no-store");
|
|
323
|
+
res.end(content);
|
|
324
|
+
} catch {
|
|
325
|
+
res.writeHead(404);
|
|
326
|
+
res.end("Not found");
|
|
327
|
+
}
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (requestUrl.pathname !== "/api/snapshot") return next();
|
|
332
|
+
try {
|
|
333
|
+
const snapshot = await buildSnapshot(squadsDir);
|
|
334
|
+
res.setHeader("Content-Type", "application/json");
|
|
335
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
336
|
+
res.end(JSON.stringify(snapshot));
|
|
337
|
+
} catch {
|
|
338
|
+
res.writeHead(500);
|
|
339
|
+
res.end("Internal Server Error");
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// File watcher using chokidar — reliable cross-platform, handles partial writes
|
|
344
|
+
const watcher = chokidarWatch(squadsDir, {
|
|
345
|
+
ignoreInitial: true,
|
|
346
|
+
awaitWriteFinish: { stabilityThreshold: 300, pollInterval: 50 },
|
|
347
|
+
ignored: [/(^|[/\\])\./, /node_modules/, /output[/\\]/],
|
|
348
|
+
depth: 2,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
function handleFileChange(filePath: string) {
|
|
352
|
+
const relative = path.relative(squadsDir, filePath).replace(/\\/g, "/");
|
|
353
|
+
const parts = relative.split("/");
|
|
354
|
+
if (parts.length < 2) return;
|
|
355
|
+
|
|
356
|
+
const squadName = parts[0];
|
|
357
|
+
const fileName = parts[1];
|
|
358
|
+
|
|
359
|
+
if (fileName === "state.json") {
|
|
360
|
+
fsp.readFile(filePath, "utf-8").then((raw) => {
|
|
361
|
+
const parsed = JSON.parse(raw);
|
|
362
|
+
if (!isValidState(parsed)) return;
|
|
363
|
+
broadcast(wss, { type: "SQUAD_UPDATE", squad: squadName, state: parsed });
|
|
364
|
+
}).catch(() => {
|
|
365
|
+
// Invalid JSON — next change event will retry
|
|
366
|
+
});
|
|
367
|
+
} else if (fileName === "squad.yaml") {
|
|
368
|
+
buildSnapshot(squadsDir).then((snap) => broadcast(wss, snap));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function handleFileRemoval(filePath: string) {
|
|
373
|
+
const relative = path.relative(squadsDir, filePath).replace(/\\/g, "/");
|
|
374
|
+
const parts = relative.split("/");
|
|
375
|
+
if (parts.length < 2) return;
|
|
376
|
+
|
|
377
|
+
const squadName = parts[0];
|
|
378
|
+
const fileName = parts[1];
|
|
379
|
+
|
|
380
|
+
if (fileName === "state.json") {
|
|
381
|
+
broadcast(wss, { type: "SQUAD_INACTIVE", squad: squadName });
|
|
382
|
+
} else if (fileName === "squad.yaml") {
|
|
383
|
+
buildSnapshot(squadsDir).then((snap) => broadcast(wss, snap));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
watcher.on("add", handleFileChange);
|
|
388
|
+
watcher.on("change", handleFileChange);
|
|
389
|
+
watcher.on("unlink", handleFileRemoval);
|
|
390
|
+
|
|
391
|
+
server.httpServer.on("close", () => {
|
|
392
|
+
watcher.close();
|
|
393
|
+
});
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { create } from "zustand";
|
|
2
|
+
import type { SquadInfo, SquadState } from "@/types/state";
|
|
3
|
+
|
|
4
|
+
interface SquadStore {
|
|
5
|
+
// State
|
|
6
|
+
squads: Map<string, SquadInfo>;
|
|
7
|
+
activeStates: Map<string, SquadState>;
|
|
8
|
+
selectedSquad: string | null;
|
|
9
|
+
isConnected: boolean;
|
|
10
|
+
|
|
11
|
+
// Actions
|
|
12
|
+
selectSquad: (name: string | null) => void;
|
|
13
|
+
setConnected: (connected: boolean) => void;
|
|
14
|
+
setSnapshot: (squads: SquadInfo[], activeStates: Record<string, SquadState>) => void;
|
|
15
|
+
setSquadActive: (squad: string, state: SquadState) => void;
|
|
16
|
+
updateSquadState: (squad: string, state: SquadState) => void;
|
|
17
|
+
setSquadInactive: (squad: string) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const useSquadStore = create<SquadStore>((set) => ({
|
|
21
|
+
squads: new Map(),
|
|
22
|
+
activeStates: new Map(),
|
|
23
|
+
selectedSquad: null,
|
|
24
|
+
isConnected: false,
|
|
25
|
+
|
|
26
|
+
selectSquad: (name) => set({ selectedSquad: name }),
|
|
27
|
+
|
|
28
|
+
setConnected: (connected) => set({ isConnected: connected }),
|
|
29
|
+
|
|
30
|
+
setSnapshot: (squads, activeStates) =>
|
|
31
|
+
set({
|
|
32
|
+
squads: new Map(squads.map((s) => [s.code, s])),
|
|
33
|
+
activeStates: new Map(Object.entries(activeStates)),
|
|
34
|
+
}),
|
|
35
|
+
|
|
36
|
+
setSquadActive: (squad, state) =>
|
|
37
|
+
set((prev) => ({
|
|
38
|
+
activeStates: new Map(prev.activeStates).set(squad, state),
|
|
39
|
+
})),
|
|
40
|
+
|
|
41
|
+
updateSquadState: (squad, state) =>
|
|
42
|
+
set((prev) => ({
|
|
43
|
+
activeStates: new Map(prev.activeStates).set(squad, state),
|
|
44
|
+
})),
|
|
45
|
+
|
|
46
|
+
setSquadInactive: (squad) =>
|
|
47
|
+
set((prev) => {
|
|
48
|
+
const next = new Map(prev.activeStates);
|
|
49
|
+
next.delete(squad);
|
|
50
|
+
return {
|
|
51
|
+
activeStates: next,
|
|
52
|
+
// Reset selection if the inactive squad was selected
|
|
53
|
+
selectedSquad: prev.selectedSquad === squad ? null : prev.selectedSquad,
|
|
54
|
+
};
|
|
55
|
+
}),
|
|
56
|
+
}));
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
*,
|
|
2
|
+
*::before,
|
|
3
|
+
*::after {
|
|
4
|
+
margin: 0;
|
|
5
|
+
padding: 0;
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
:root {
|
|
10
|
+
--bg-primary: #101018;
|
|
11
|
+
--bg-secondary: #1a1a2e;
|
|
12
|
+
--bg-sidebar: #141422;
|
|
13
|
+
--border: #2a2a3e;
|
|
14
|
+
--text-primary: #e0e0e0;
|
|
15
|
+
--text-secondary: #8888a0;
|
|
16
|
+
--accent-cyan: #00d4ff;
|
|
17
|
+
--accent-green: #00e676;
|
|
18
|
+
--accent-amber: #ffab00;
|
|
19
|
+
--accent-red: #ff5252;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
html,
|
|
23
|
+
body,
|
|
24
|
+
#root {
|
|
25
|
+
height: 100%;
|
|
26
|
+
width: 100%;
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
font-family: "JetBrains Mono", "Fira Code", monospace;
|
|
29
|
+
background: var(--bg-primary);
|
|
30
|
+
color: var(--text-primary);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@keyframes pulse {
|
|
34
|
+
0%, 100% { opacity: 1; }
|
|
35
|
+
50% { opacity: 0.4; }
|
|
36
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// state.json structure — matches Pipeline Runner output
|
|
2
|
+
export interface AgentDesk {
|
|
3
|
+
col: number;
|
|
4
|
+
row: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type AgentStatus =
|
|
8
|
+
| "idle"
|
|
9
|
+
| "working"
|
|
10
|
+
| "delivering"
|
|
11
|
+
| "done"
|
|
12
|
+
| "checkpoint";
|
|
13
|
+
|
|
14
|
+
export interface Agent {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
icon: string;
|
|
18
|
+
status: AgentStatus;
|
|
19
|
+
gender?: "male" | "female";
|
|
20
|
+
desk: AgentDesk;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface Handoff {
|
|
24
|
+
from: string;
|
|
25
|
+
to: string;
|
|
26
|
+
message: string;
|
|
27
|
+
completedAt: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type SquadStatus =
|
|
31
|
+
| "idle"
|
|
32
|
+
| "running"
|
|
33
|
+
| "completed"
|
|
34
|
+
| "checkpoint";
|
|
35
|
+
|
|
36
|
+
export interface SquadState {
|
|
37
|
+
squad: string;
|
|
38
|
+
status: SquadStatus;
|
|
39
|
+
step: {
|
|
40
|
+
current: number;
|
|
41
|
+
total: number;
|
|
42
|
+
label: string;
|
|
43
|
+
};
|
|
44
|
+
agents: Agent[];
|
|
45
|
+
handoff: Handoff | null;
|
|
46
|
+
startedAt: string | null;
|
|
47
|
+
updatedAt: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Squad metadata from squad.yaml
|
|
51
|
+
export interface SquadInfo {
|
|
52
|
+
code: string;
|
|
53
|
+
name: string;
|
|
54
|
+
description: string;
|
|
55
|
+
icon: string;
|
|
56
|
+
agents: string[]; // agent file paths
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// WebSocket messages
|
|
60
|
+
export type WsMessage =
|
|
61
|
+
| { type: "SNAPSHOT"; squads: SquadInfo[]; activeStates: Record<string, SquadState> }
|
|
62
|
+
| { type: "SQUAD_UPDATE"; squad: string; state: SquadState }
|
|
63
|
+
| { type: "SQUAD_INACTIVE"; squad: string };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"allowImportingTsExtensions": true,
|
|
10
|
+
"isolatedModules": true,
|
|
11
|
+
"moduleDetection": "force",
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"jsx": "react-jsx",
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noUnusedLocals": true,
|
|
16
|
+
"noUnusedParameters": true,
|
|
17
|
+
"noFallthroughCasesInSwitch": true,
|
|
18
|
+
"noUncheckedSideEffectImports": true,
|
|
19
|
+
"paths": {
|
|
20
|
+
"@/*": ["./src/*"]
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"include": ["src"]
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import react from "@vitejs/plugin-react";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { squadWatcherPlugin } from "./src/plugin/squadWatcher";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [react(), squadWatcherPlugin()],
|
|
8
|
+
resolve: {
|
|
9
|
+
alias: {
|
|
10
|
+
"@": path.resolve(__dirname, "./src"),
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@julioventura/opensquad",
|
|
3
|
+
"version": "0.1.17",
|
|
4
|
+
"description": "Multi-agent orchestration framework — create AI squads that work together",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"opensquad": "bin/opensquad.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node --test tests/*.test.js",
|
|
11
|
+
"lint": "eslint src/ bin/ tests/",
|
|
12
|
+
"version": "node -e \"require('fs').writeFileSync('templates/_opensquad/.opensquad-version', require('./package.json').version + '\\n')\" && git add templates/_opensquad/.opensquad-version"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"claude-code",
|
|
16
|
+
"ai-agents",
|
|
17
|
+
"multi-agent",
|
|
18
|
+
"orchestration",
|
|
19
|
+
"squads"
|
|
20
|
+
],
|
|
21
|
+
"author": "Renato Asse (https://github.com/renatoasse)",
|
|
22
|
+
"homepage": "https://github.com/julioventura/opensquad#readme",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/julioventura/opensquad.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/julioventura/opensquad/issues"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20.0.0"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"bin/",
|
|
36
|
+
"src/",
|
|
37
|
+
"agents/",
|
|
38
|
+
"skills/",
|
|
39
|
+
"templates/",
|
|
40
|
+
"_opensquad/config/",
|
|
41
|
+
"_opensquad/core/",
|
|
42
|
+
"dashboard/",
|
|
43
|
+
"public/sfx/"
|
|
44
|
+
],
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@inquirer/checkbox": "^5.1.0",
|
|
47
|
+
"@inquirer/input": "^5.0.0",
|
|
48
|
+
"@inquirer/select": "^5.1.0",
|
|
49
|
+
"@supabase/supabase-js": "^2.107.0",
|
|
50
|
+
"basic-ftp": "^5.3.1"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@eslint/js": "^10.0.1",
|
|
54
|
+
"eslint": "^10.0.3",
|
|
55
|
+
"globals": "^17.4.0",
|
|
56
|
+
"playwright": "^1.60.0",
|
|
57
|
+
"qrcode": "^1.5.4"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
Binary file
|