@adaptic/maestro 1.1.8 → 1.5.0
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/.claude/commands/init-maestro.md +304 -8
- package/README.md +28 -0
- package/bin/maestro.mjs +258 -56
- package/docs/guides/agents-observe-setup.md +64 -0
- package/docs/guides/ccxray-diagnostics.md +65 -0
- package/docs/guides/claude-mem-setup.md +79 -0
- package/docs/guides/claude-pace-setup.md +56 -0
- package/docs/guides/claudraband-sessions.md +98 -0
- package/docs/guides/clawteam-swarm.md +116 -0
- package/docs/guides/code-review-graph-setup.md +86 -0
- package/docs/guides/self-optimization-pattern.md +82 -0
- package/docs/guides/slack-setup.md +4 -2
- package/docs/guides/twilio-subaccounts-setup.md +223 -0
- package/docs/guides/webhook-relay-setup.md +349 -0
- package/package.json +2 -1
- package/plugins/maestro-skills/plugin.json +16 -0
- package/plugins/maestro-skills/skills/agents-observe.md +110 -0
- package/plugins/maestro-skills/skills/ccxray-diagnostics.md +91 -0
- package/plugins/maestro-skills/skills/claude-pace.md +61 -0
- package/plugins/maestro-skills/skills/code-review-graph.md +99 -0
- package/scaffold/CLAUDE.md +64 -0
- package/scaffold/config/agent.ts.example +2 -1
- package/scaffold/config/known-agents.json +35 -0
- package/scripts/daemon/classifier.mjs +264 -50
- package/scripts/daemon/dispatcher.mjs +109 -5
- package/scripts/daemon/launchd-wrapper-generic.sh +96 -0
- package/scripts/daemon/launchd-wrapper-slack-events.sh +37 -0
- package/scripts/daemon/launchd-wrapper.sh +91 -0
- package/scripts/daemon/lib/session-router.mjs +274 -0
- package/scripts/daemon/lib/session-router.test.mjs +295 -0
- package/scripts/daemon/prompt-builder.mjs +51 -11
- package/scripts/daemon/responder.mjs +234 -19
- package/scripts/daemon/session-lock.mjs +194 -0
- package/scripts/daemon/sophie-daemon.mjs +16 -2
- package/scripts/email-signature.html +20 -4
- package/scripts/local-triggers/generate-plists.sh +62 -10
- package/scripts/poller/imap-client.mjs +4 -2
- package/scripts/poller/slack-poller.mjs +104 -52
- package/scripts/setup/init-agent.sh +91 -1
- package/scripts/setup/install-dev-tools.sh +150 -0
- package/scripts/spawn-session.sh +21 -6
- package/workflows/continuous/backlog-executor.yaml +141 -0
- package/workflows/daily/evening-wrap.yaml +41 -1
- package/workflows/daily/morning-brief.yaml +17 -0
- package/workflows/event-driven/agent-failure-investigation.yaml +137 -0
- package/workflows/event-driven/pr-review.yaml +104 -0
- package/workflows/weekly/engineering-health.yaml +154 -0
package/bin/maestro.mjs
CHANGED
|
@@ -8,16 +8,21 @@
|
|
|
8
8
|
* npx @adaptic/maestro doctor # Verify installation and configuration
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { resolve, join, dirname } from "node:path";
|
|
11
|
+
import { resolve, join, dirname, relative, sep } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import {
|
|
14
14
|
mkdirSync,
|
|
15
15
|
cpSync,
|
|
16
|
+
copyFileSync,
|
|
16
17
|
existsSync,
|
|
17
18
|
readFileSync,
|
|
18
19
|
writeFileSync,
|
|
20
|
+
readdirSync,
|
|
21
|
+
statSync,
|
|
22
|
+
lstatSync,
|
|
19
23
|
} from "node:fs";
|
|
20
24
|
import { execFileSync } from "node:child_process";
|
|
25
|
+
import { createHash } from "node:crypto";
|
|
21
26
|
|
|
22
27
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
28
|
const MAESTRO_ROOT = resolve(__dirname, "..");
|
|
@@ -81,6 +86,7 @@ function create(targetName) {
|
|
|
81
86
|
"desktop-control",
|
|
82
87
|
"ingest",
|
|
83
88
|
"mcp",
|
|
89
|
+
"services",
|
|
84
90
|
];
|
|
85
91
|
|
|
86
92
|
for (const dir of frameworkDirs) {
|
|
@@ -595,7 +601,6 @@ rubrics: []
|
|
|
595
601
|
upgrade: "npx @adaptic/maestro upgrade",
|
|
596
602
|
},
|
|
597
603
|
dependencies: {
|
|
598
|
-
"@anthropic-ai/sdk": "^0.82.0",
|
|
599
604
|
"@google/genai": "^1.42.0",
|
|
600
605
|
dotenv: "^16.4.5",
|
|
601
606
|
execa: "^9.6.1",
|
|
@@ -664,68 +669,242 @@ rubrics: []
|
|
|
664
669
|
}
|
|
665
670
|
|
|
666
671
|
// ---------------------------------------------------------------------------
|
|
667
|
-
// UPGRADE — update framework files in current agent repo
|
|
672
|
+
// UPGRADE — update framework files in current agent repo (smart merge)
|
|
668
673
|
// ---------------------------------------------------------------------------
|
|
674
|
+
//
|
|
675
|
+
// Upgrade philosophy: by default, never silently overwrite a file the user has
|
|
676
|
+
// modified locally. Detection uses git as the source of truth:
|
|
677
|
+
//
|
|
678
|
+
// • untracked or has-uncommitted-changes in agent repo ⇒ "locally modified"
|
|
679
|
+
// • tracked and clean against HEAD ⇒ "vendored, safe to overwrite"
|
|
680
|
+
//
|
|
681
|
+
// For every framework file we'd otherwise overwrite, we classify into:
|
|
682
|
+
//
|
|
683
|
+
// added — file doesn't exist in agent repo → copy
|
|
684
|
+
// updated — file exists, agent's copy matches HEAD (no local edits) → overwrite
|
|
685
|
+
// same — file exists, content already byte-identical to upstream → skip
|
|
686
|
+
// preserved — file exists with local edits → keep agent's copy, write the
|
|
687
|
+
// upstream version to .maestro/incoming/<path> for manual review
|
|
688
|
+
// (unless --force-overwrite is passed)
|
|
689
|
+
//
|
|
690
|
+
// Flags:
|
|
691
|
+
// --dry-run preview only; no files written
|
|
692
|
+
// --force-overwrite overwrite even locally-modified files (with backup)
|
|
693
|
+
// --no-incoming skip writing .maestro/incoming/ shadows
|
|
694
|
+
// --verbose list every file's classification
|
|
695
|
+
|
|
696
|
+
// Paths that get upgraded. Each entry is { path, mode } where mode is:
|
|
697
|
+
// "smart" — per-file classification described above
|
|
698
|
+
// "merge" — add new files only; never overwrite existing (used for agents/)
|
|
699
|
+
const UPGRADE_PATHS = [
|
|
700
|
+
{ path: "scripts", mode: "smart" },
|
|
701
|
+
{ path: "policies", mode: "smart" },
|
|
702
|
+
{ path: "docs", mode: "smart" },
|
|
703
|
+
{ path: "public/assets", mode: "smart" },
|
|
704
|
+
{ path: "workflows", mode: "smart" },
|
|
705
|
+
{ path: "schedules", mode: "smart" },
|
|
706
|
+
{ path: "desktop-control", mode: "smart" },
|
|
707
|
+
{ path: "ingest", mode: "smart" },
|
|
708
|
+
{ path: "mcp", mode: "smart" },
|
|
709
|
+
{ path: ".claude/commands", mode: "smart" },
|
|
710
|
+
{ path: "plugins/maestro-skills", mode: "smart" },
|
|
711
|
+
{ path: "teams", mode: "smart" },
|
|
712
|
+
{ path: "agents", mode: "merge" },
|
|
713
|
+
];
|
|
714
|
+
|
|
715
|
+
function sha256File(p) {
|
|
716
|
+
return createHash("sha256").update(readFileSync(p)).digest("hex");
|
|
717
|
+
}
|
|
669
718
|
|
|
670
|
-
function
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
719
|
+
function walkFiles(root) {
|
|
720
|
+
const out = [];
|
|
721
|
+
if (!existsSync(root)) return out;
|
|
722
|
+
const stack = [root];
|
|
723
|
+
while (stack.length) {
|
|
724
|
+
const dir = stack.pop();
|
|
725
|
+
for (const name of readdirSync(dir)) {
|
|
726
|
+
const full = join(dir, name);
|
|
727
|
+
// Use lstat first so we can detect (and skip) symlinks — particularly
|
|
728
|
+
// broken ones, which are common in shared scaffolds.
|
|
729
|
+
let lst;
|
|
730
|
+
try { lst = lstatSync(full); }
|
|
731
|
+
catch { continue; }
|
|
732
|
+
if (lst.isSymbolicLink()) {
|
|
733
|
+
// Skip symlinks entirely — agents shouldn't inherit links into paths
|
|
734
|
+
// that may not exist on the target machine.
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
if (lst.isDirectory()) stack.push(full);
|
|
738
|
+
else if (lst.isFile()) out.push(full);
|
|
739
|
+
}
|
|
676
740
|
}
|
|
741
|
+
return out;
|
|
742
|
+
}
|
|
677
743
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
];
|
|
744
|
+
function isGitRepo(cwd) {
|
|
745
|
+
try {
|
|
746
|
+
execFileSync("git", ["rev-parse", "--is-inside-work-tree"], { cwd, stdio: "pipe" });
|
|
747
|
+
return true;
|
|
748
|
+
} catch { return false; }
|
|
749
|
+
}
|
|
685
750
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
751
|
+
// Build a Set of repo-relative paths that are dirty (modified, added, untracked).
|
|
752
|
+
// Single git invocation is much faster than per-file checks.
|
|
753
|
+
function dirtyPathSet(cwd) {
|
|
754
|
+
const set = new Set();
|
|
755
|
+
try {
|
|
756
|
+
const out = execFileSync(
|
|
757
|
+
"git",
|
|
758
|
+
["status", "--porcelain=v1", "-z", "--untracked-files=all"],
|
|
759
|
+
{ cwd, encoding: "utf-8" }
|
|
760
|
+
);
|
|
761
|
+
// -z separates records with NUL; each record is "XY path" (no quoting).
|
|
762
|
+
// For renames (R/C) the format is "R newpath\0oldpath\0" — we only care
|
|
763
|
+
// about destination, but for safety we treat both as dirty.
|
|
764
|
+
for (const rec of out.split("\0")) {
|
|
765
|
+
if (!rec) continue;
|
|
766
|
+
const status = rec.slice(0, 2);
|
|
767
|
+
const path = rec.slice(3);
|
|
768
|
+
// Anything in porcelain output is non-clean by definition.
|
|
769
|
+
if (status !== " ") set.add(path);
|
|
693
770
|
}
|
|
771
|
+
} catch {
|
|
772
|
+
// Not a git repo or git failed — caller will fall back to caution mode.
|
|
694
773
|
}
|
|
774
|
+
return set;
|
|
775
|
+
}
|
|
695
776
|
|
|
696
|
-
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
777
|
+
function parseUpgradeFlags(args) {
|
|
778
|
+
const flags = {
|
|
779
|
+
dryRun: false,
|
|
780
|
+
forceOverwrite: false,
|
|
781
|
+
noIncoming: false,
|
|
782
|
+
verbose: false,
|
|
783
|
+
};
|
|
784
|
+
for (const a of args) {
|
|
785
|
+
if (a === "--dry-run" || a === "-n") flags.dryRun = true;
|
|
786
|
+
else if (a === "--force-overwrite" || a === "--force") flags.forceOverwrite = true;
|
|
787
|
+
else if (a === "--no-incoming") flags.noIncoming = true;
|
|
788
|
+
else if (a === "--verbose" || a === "-v") flags.verbose = true;
|
|
789
|
+
else if (a === "--help" || a === "-h") return null;
|
|
790
|
+
else { fail(`Unknown flag: ${a}`); process.exit(1); }
|
|
702
791
|
}
|
|
792
|
+
return flags;
|
|
793
|
+
}
|
|
703
794
|
|
|
704
|
-
|
|
705
|
-
const
|
|
706
|
-
if (
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
795
|
+
function upgrade(args = []) {
|
|
796
|
+
const flags = parseUpgradeFlags(args);
|
|
797
|
+
if (flags === null) {
|
|
798
|
+
console.log(`
|
|
799
|
+
Usage: maestro upgrade [flags]
|
|
800
|
+
|
|
801
|
+
Flags:
|
|
802
|
+
--dry-run, -n Preview changes without writing
|
|
803
|
+
--force-overwrite Overwrite even locally-modified files (backs them up)
|
|
804
|
+
--no-incoming Don't write .maestro/incoming/ shadows for preserved files
|
|
805
|
+
--verbose, -v Print classification for every file
|
|
806
|
+
--help, -h Show this help
|
|
807
|
+
`);
|
|
808
|
+
return;
|
|
710
809
|
}
|
|
711
810
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
if (existsSync(
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
811
|
+
const cwd = process.cwd();
|
|
812
|
+
|
|
813
|
+
if (!existsSync(join(cwd, "config/agent.ts")) && !existsSync(join(cwd, "CLAUDE.md"))) {
|
|
814
|
+
fail("Not a Maestro agent directory (config/agent.ts not found)");
|
|
815
|
+
process.exit(1);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
const inGit = isGitRepo(cwd);
|
|
819
|
+
if (!inGit) {
|
|
820
|
+
warn("Not in a git repo — cannot detect local modifications.");
|
|
821
|
+
warn("All existing files will be treated as locally modified and preserved.");
|
|
822
|
+
warn("Use --force-overwrite to override.");
|
|
718
823
|
}
|
|
719
824
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
825
|
+
const dirty = inGit ? dirtyPathSet(cwd) : null;
|
|
826
|
+
|
|
827
|
+
const banner = flags.dryRun ? "DRY RUN — " : "";
|
|
828
|
+
log(`${banner}Upgrading framework files from @adaptic/maestro...`);
|
|
829
|
+
|
|
830
|
+
const counts = { added: 0, updated: 0, same: 0, preserved: 0, mergeKept: 0, forced: 0 };
|
|
831
|
+
const preservedFiles = [];
|
|
832
|
+
|
|
833
|
+
for (const { path: relRoot, mode } of UPGRADE_PATHS) {
|
|
834
|
+
const srcRoot = join(MAESTRO_ROOT, relRoot);
|
|
835
|
+
const dstRoot = join(cwd, relRoot);
|
|
836
|
+
if (!existsSync(srcRoot)) continue;
|
|
837
|
+
|
|
838
|
+
const srcFiles = walkFiles(srcRoot);
|
|
839
|
+
for (const srcFile of srcFiles) {
|
|
840
|
+
const relFromRoot = relative(srcRoot, srcFile);
|
|
841
|
+
const dstFile = join(dstRoot, relFromRoot);
|
|
842
|
+
const repoRel = relative(cwd, dstFile).split(sep).join("/");
|
|
843
|
+
|
|
844
|
+
// Case 1: new file (doesn't exist in agent) — always copy.
|
|
845
|
+
if (!existsSync(dstFile)) {
|
|
846
|
+
if (!flags.dryRun) {
|
|
847
|
+
mkdirSync(dirname(dstFile), { recursive: true });
|
|
848
|
+
copyFileSync(srcFile, dstFile);
|
|
849
|
+
}
|
|
850
|
+
counts.added++;
|
|
851
|
+
if (flags.verbose) ok(`+ ${repoRel}`);
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// Case 2: byte-identical — nothing to do.
|
|
856
|
+
const srcHash = sha256File(srcFile);
|
|
857
|
+
const dstHash = sha256File(dstFile);
|
|
858
|
+
if (srcHash === dstHash) {
|
|
859
|
+
counts.same++;
|
|
860
|
+
if (flags.verbose) console.log(` = ${repoRel}`);
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Case 3: merge-mode never overwrites existing files.
|
|
865
|
+
if (mode === "merge") {
|
|
866
|
+
counts.mergeKept++;
|
|
867
|
+
if (flags.verbose) console.log(` ~ ${repoRel} (merge-mode, kept)`);
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Case 4: locally modified? Determine via git dirty set.
|
|
872
|
+
const locallyModified = inGit ? dirty.has(repoRel) : true;
|
|
873
|
+
|
|
874
|
+
if (locallyModified && !flags.forceOverwrite) {
|
|
875
|
+
counts.preserved++;
|
|
876
|
+
preservedFiles.push(repoRel);
|
|
877
|
+
if (!flags.dryRun && !flags.noIncoming) {
|
|
878
|
+
const shadow = join(cwd, ".maestro", "incoming", repoRel);
|
|
879
|
+
mkdirSync(dirname(shadow), { recursive: true });
|
|
880
|
+
copyFileSync(srcFile, shadow);
|
|
881
|
+
}
|
|
882
|
+
if (flags.verbose) console.log(` ~ ${repoRel} (local edits, preserved)`);
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// Case 5: overwrite (clean tracked file, or --force).
|
|
887
|
+
if (locallyModified && flags.forceOverwrite) {
|
|
888
|
+
// Back up the about-to-be-clobbered file.
|
|
889
|
+
if (!flags.dryRun) {
|
|
890
|
+
const backup = join(cwd, ".maestro", "backup", repoRel);
|
|
891
|
+
mkdirSync(dirname(backup), { recursive: true });
|
|
892
|
+
copyFileSync(dstFile, backup);
|
|
893
|
+
}
|
|
894
|
+
counts.forced++;
|
|
895
|
+
if (flags.verbose) warn(`! ${repoRel} (forced; backup written)`);
|
|
896
|
+
} else {
|
|
897
|
+
counts.updated++;
|
|
898
|
+
if (flags.verbose) console.log(` > ${repoRel}`);
|
|
899
|
+
}
|
|
900
|
+
if (!flags.dryRun) {
|
|
901
|
+
mkdirSync(dirname(dstFile), { recursive: true });
|
|
902
|
+
copyFileSync(srcFile, dstFile);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
726
905
|
}
|
|
727
906
|
|
|
728
|
-
//
|
|
907
|
+
// Ensure standard runtime directories exist.
|
|
729
908
|
const ensureDirs = [
|
|
730
909
|
"state/handoffs", "state/huddle", "state/indexes", "state/rag",
|
|
731
910
|
"state/sessions", "state/slack-responded", "state/slack-thread-tracker",
|
|
@@ -739,15 +918,32 @@ function upgrade() {
|
|
|
739
918
|
for (const dir of ensureDirs) {
|
|
740
919
|
const dirPath = join(cwd, dir);
|
|
741
920
|
if (!existsSync(dirPath)) {
|
|
742
|
-
mkdirSync(dirPath, { recursive: true });
|
|
921
|
+
if (!flags.dryRun) mkdirSync(dirPath, { recursive: true });
|
|
743
922
|
newDirs++;
|
|
744
923
|
}
|
|
745
924
|
}
|
|
746
|
-
|
|
925
|
+
|
|
926
|
+
// Summary
|
|
927
|
+
console.log();
|
|
928
|
+
console.log(`${C.bold}Upgrade summary${C.reset}${flags.dryRun ? " (dry run, nothing written)" : ""}`);
|
|
929
|
+
if (counts.added) ok(`${counts.added} added (new files)`);
|
|
930
|
+
if (counts.updated) ok(`${counts.updated} updated (vendored, no local edits)`);
|
|
931
|
+
if (counts.same) console.log(` = ${counts.same} already up-to-date`);
|
|
932
|
+
if (counts.mergeKept) console.log(` ~ ${counts.mergeKept} merge-mode kept (agents/ custom files preserved)`);
|
|
933
|
+
if (counts.preserved) warn(`${counts.preserved} preserved (local edits — kept your version)`);
|
|
934
|
+
if (counts.forced) warn(`${counts.forced} force-overwritten (backups in .maestro/backup/)`);
|
|
935
|
+
if (newDirs) ok(`${newDirs} new directories created`);
|
|
936
|
+
|
|
937
|
+
if (preservedFiles.length && !flags.noIncoming && !flags.dryRun) {
|
|
938
|
+
console.log();
|
|
939
|
+
log("Upstream versions of your locally-modified files saved to .maestro/incoming/");
|
|
940
|
+
log("Review with: diff <path> .maestro/incoming/<path>");
|
|
941
|
+
for (const p of preservedFiles.slice(0, 5)) console.log(` ${p}`);
|
|
942
|
+
if (preservedFiles.length > 5) console.log(` … and ${preservedFiles.length - 5} more`);
|
|
943
|
+
}
|
|
747
944
|
|
|
748
945
|
console.log();
|
|
749
|
-
|
|
750
|
-
log("Agent-specific files (config/, CLAUDE.md, knowledge/, memory/) were NOT modified.");
|
|
946
|
+
log("Agent-specific paths (config/, CLAUDE.md, knowledge/, memory/, state/, outputs/, logs/, .env) were NOT touched.");
|
|
751
947
|
}
|
|
752
948
|
|
|
753
949
|
// ---------------------------------------------------------------------------
|
|
@@ -820,16 +1016,22 @@ const [, , command, ...args] = process.argv;
|
|
|
820
1016
|
|
|
821
1017
|
switch (command) {
|
|
822
1018
|
case "create": create(args[0]); break;
|
|
823
|
-
case "upgrade": upgrade(); break;
|
|
1019
|
+
case "upgrade": upgrade(args); break;
|
|
824
1020
|
case "doctor": doctor(); break;
|
|
825
1021
|
case "--help": case "-h": case undefined:
|
|
826
1022
|
console.log(`
|
|
827
1023
|
Maestro — Autonomous AI Agent Operating System
|
|
828
1024
|
|
|
829
1025
|
Usage:
|
|
830
|
-
npx @adaptic/maestro create <dirname>
|
|
831
|
-
npx @adaptic/maestro upgrade
|
|
832
|
-
npx @adaptic/maestro doctor
|
|
1026
|
+
npx @adaptic/maestro create <dirname> Create a new agent repo
|
|
1027
|
+
npx @adaptic/maestro upgrade [--dry-run] Update framework files
|
|
1028
|
+
npx @adaptic/maestro doctor Verify installation
|
|
1029
|
+
|
|
1030
|
+
Upgrade flags:
|
|
1031
|
+
--dry-run, -n Preview changes without writing
|
|
1032
|
+
--force-overwrite Overwrite even locally-modified files (backs them up)
|
|
1033
|
+
--no-incoming Don't write .maestro/incoming/ shadows
|
|
1034
|
+
--verbose, -v List classification for every file
|
|
833
1035
|
|
|
834
1036
|
Workflow:
|
|
835
1037
|
1. npx @adaptic/maestro create my-agent
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Agents Observe — Multi-Agent Observability Dashboard
|
|
2
|
+
|
|
3
|
+
Real-time dashboard for monitoring Claude Code agent teams. Captures tool calls, agent hierarchy, and session state via background hooks with SQLite storage.
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Maestro agents run parallel backlog execution with multiple subagents. Currently debugging relies on post-hoc JSONL log scanning. Agents Observe provides real-time visibility into:
|
|
8
|
+
|
|
9
|
+
- **Live tool calls** across all active agents
|
|
10
|
+
- **Agent hierarchy trees** showing parent/child relationships
|
|
11
|
+
- **Search and filter** across sessions and tool invocations
|
|
12
|
+
- **WebSocket-streamed UI** with 3-5ms latency
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### Via install-dev-tools (recommended)
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
./scripts/setup/install-dev-tools.sh --tool agents-observe
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Manual
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# As Claude Code plugin
|
|
26
|
+
claude plugin install agents-observe
|
|
27
|
+
|
|
28
|
+
# Or global npm
|
|
29
|
+
npm install -g agents-observe
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### Start the dashboard
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
agents-observe serve
|
|
38
|
+
# Opens dashboard at http://localhost:3847
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### View active agent sessions
|
|
42
|
+
|
|
43
|
+
Navigate to the dashboard URL. Active sessions appear automatically when Claude Code agents run with the plugin enabled.
|
|
44
|
+
|
|
45
|
+
### Query session history
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
agents-observe query --session <id>
|
|
49
|
+
agents-observe query --tool Write --last 1h
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Integration with Maestro
|
|
53
|
+
|
|
54
|
+
Best used during:
|
|
55
|
+
- **Backlog executor cycles** — monitor parallel agent performance
|
|
56
|
+
- **Debugging agent failures** — trace tool call sequences leading to errors
|
|
57
|
+
- **Performance profiling** — identify slow or redundant tool calls
|
|
58
|
+
|
|
59
|
+
Not recommended as always-on in production (SQLite write overhead). Enable on-demand for debugging and profiling sessions.
|
|
60
|
+
|
|
61
|
+
## Repository
|
|
62
|
+
|
|
63
|
+
- GitHub: https://github.com/simple10/agents-observe
|
|
64
|
+
- License: MIT
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# ccxray Diagnostics Guide
|
|
2
|
+
|
|
3
|
+
ccxray provides X-ray vision into Claude Code sessions via a transparent HTTP proxy and live dashboard. It intercepts all API calls between Claude Code and Anthropic, giving you detailed token/cost analysis, timing breakdowns, and system prompt visibility.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
- Debugging expensive sessions (high token burn, unexpectedly long runs)
|
|
8
|
+
- Understanding which tool calls consumed the most tokens
|
|
9
|
+
- Comparing system prompts across main agent and sub-agents
|
|
10
|
+
- Investigating context window utilisation and heatmaps
|
|
11
|
+
- Post-incident analysis of failed or timed-out sessions
|
|
12
|
+
|
|
13
|
+
## What It Shows
|
|
14
|
+
|
|
15
|
+
- Real-time timeline of agent turns with thinking durations
|
|
16
|
+
- Per-turn token and cost breakdown with burn rate tracking
|
|
17
|
+
- Context window heatmaps showing what's consuming space
|
|
18
|
+
- System prompt diffs across main agent and sub-agents
|
|
19
|
+
- Multi-project hub: multiple terminals share one dashboard
|
|
20
|
+
- Full JSON logging of every request/response to `~/.ccxray/logs/`
|
|
21
|
+
|
|
22
|
+
## Technical Details
|
|
23
|
+
|
|
24
|
+
- Zero-config transparent HTTP proxy
|
|
25
|
+
- Launches Claude Code through the proxy automatically
|
|
26
|
+
- Web dashboard for live monitoring
|
|
27
|
+
- JSON log files for post-hoc analysis
|
|
28
|
+
- npx-based — no permanent installation required
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
### Quick start
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Launch Claude Code through the ccxray proxy
|
|
36
|
+
npx ccxray claude
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This starts the proxy, opens the dashboard, and launches Claude Code. All API traffic flows through ccxray for inspection.
|
|
40
|
+
|
|
41
|
+
### With an existing session
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Start the proxy on a specific port
|
|
45
|
+
npx ccxray --port 8080
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Viewing logs
|
|
49
|
+
|
|
50
|
+
Logs are stored at `~/.ccxray/logs/` as JSON files, one per session. These can be analysed post-hoc for cost attribution.
|
|
51
|
+
|
|
52
|
+
## Repository
|
|
53
|
+
|
|
54
|
+
- GitHub: https://github.com/lis186/ccxray
|
|
55
|
+
- License: MIT
|
|
56
|
+
|
|
57
|
+
## Integration with Maestro
|
|
58
|
+
|
|
59
|
+
ccxray is a diagnostic tool, not a runtime dependency. It is not installed by default via `init-agent.sh` but is available on-demand via npx.
|
|
60
|
+
|
|
61
|
+
Use cases for Maestro operators:
|
|
62
|
+
- **Token budget audit**: Run a backlog cycle through ccxray to see per-task token costs
|
|
63
|
+
- **Sub-agent analysis**: Compare token usage across spawned background agents
|
|
64
|
+
- **Prompt debugging**: Inspect what system prompts sub-agents actually receive
|
|
65
|
+
- **Cost optimisation**: Identify which tool calls are disproportionately expensive
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Claude-Mem Setup Guide
|
|
2
|
+
|
|
3
|
+
Claude-Mem is a persistent session memory plugin for Claude Code. It automatically captures tool usage, generates semantic summaries, and injects relevant context into future sessions — giving your agent continuity of knowledge across session boundaries.
|
|
4
|
+
|
|
5
|
+
## What Claude-Mem Does
|
|
6
|
+
|
|
7
|
+
- **Automatic capture**: 6 lifecycle hooks (SessionStart, UserPromptSubmit, PostToolUse, Stop, SessionEnd, PreCompact) record agent activity without manual intervention
|
|
8
|
+
- **AI-powered compression**: Captured actions are summarised via Claude's agent-sdk into semantic memory
|
|
9
|
+
- **Vector search**: Relevant past context is retrieved and injected at session start using Chroma vector search
|
|
10
|
+
- **Local storage**: All data stays on-machine in `~/.claude-mem/` (SQLite + Chroma)
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
### Via init-agent (recommended)
|
|
15
|
+
|
|
16
|
+
If you ran `scripts/setup/init-agent.sh`, Claude-Mem was installed automatically. Verify:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx claude-mem status
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Manual installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx claude-mem install
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This registers the plugin hooks and starts the worker service (port 37777).
|
|
29
|
+
|
|
30
|
+
### Via Claude Code plugin commands
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
claude plugin marketplace add thedotmack/claude-mem
|
|
34
|
+
claude plugin install claude-mem
|
|
35
|
+
# Restart Claude Code
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
Settings live at `~/.claude-mem/settings.json` (auto-created with defaults on first run):
|
|
41
|
+
|
|
42
|
+
- **AI model**: Which model compresses captured data
|
|
43
|
+
- **Worker port**: Default 37777
|
|
44
|
+
- **Data directory**: Where SQLite and vector indices are stored
|
|
45
|
+
- **Context injection**: How much past context to inject per session
|
|
46
|
+
- **Log level**: Verbosity of worker logs
|
|
47
|
+
|
|
48
|
+
## Verification
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Check worker is running
|
|
52
|
+
npx claude-mem status
|
|
53
|
+
|
|
54
|
+
# View captured sessions
|
|
55
|
+
npx claude-mem sessions list
|
|
56
|
+
|
|
57
|
+
# View memory stats
|
|
58
|
+
npx claude-mem stats
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## How It Complements Maestro's Memory
|
|
62
|
+
|
|
63
|
+
Maestro already has:
|
|
64
|
+
- **Interaction memory** (`memory/interactions/`) — conversation transcripts by channel/date
|
|
65
|
+
- **User profiles** (`memory/profiles/`) — per-person preferences and standing instructions
|
|
66
|
+
- **Knowledge base** (`knowledge/`) — entities, decisions, syntheses
|
|
67
|
+
|
|
68
|
+
Claude-Mem adds:
|
|
69
|
+
- **Session-level recall** — what tools were used, what worked, what failed
|
|
70
|
+
- **Semantic search across sessions** — find past sessions where similar tasks were done
|
|
71
|
+
- **Automatic context injection** — no manual "read the last session" needed
|
|
72
|
+
|
|
73
|
+
Together they provide comprehensive memory: Maestro handles *what was communicated*, Claude-Mem handles *what was done*.
|
|
74
|
+
|
|
75
|
+
## Troubleshooting
|
|
76
|
+
|
|
77
|
+
- **Worker not starting**: Check `~/.claude-mem/logs/` for errors. Ensure port 37777 is free.
|
|
78
|
+
- **No context injected**: Verify hooks are registered: check `~/.claude/settings.json` for claude-mem entries
|
|
79
|
+
- **High memory usage**: Claude-Mem's Chroma index grows over time. Run `npx claude-mem compact` to optimise.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Claude-Pace Setup Guide
|
|
2
|
+
|
|
3
|
+
Claude-Pace is a real-time rate limit tracker for Claude Code. It displays a status line showing your 5-hour and 7-day quota usage, reset countdowns, and a pace delta indicator (green = headroom, red = burning too fast).
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Maestro agents run continuously on 10-minute backlog cycles. Without rate limit visibility, sessions hit quota walls mid-task — the backlog executor stalls, scheduled workflows fail silently, and recovery requires manual intervention. Claude-Pace makes quota state visible so agents (and operators) can pace work intelligently.
|
|
8
|
+
|
|
9
|
+
## What It Shows
|
|
10
|
+
|
|
11
|
+
- 5-hour and 7-day quota usage percentages
|
|
12
|
+
- Reset countdown timers
|
|
13
|
+
- Pace delta: whether current burn rate will exhaust quota before reset
|
|
14
|
+
- Current model, effort level, git branch, diff stats
|
|
15
|
+
|
|
16
|
+
## Technical Details
|
|
17
|
+
|
|
18
|
+
- Pure Bash + jq — no npm, no Node, no network calls
|
|
19
|
+
- ~10ms runtime per status line refresh
|
|
20
|
+
- Single file: `claude-pace.sh`
|
|
21
|
+
- Reads Claude Code's local quota cache
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
### Via init-agent (recommended)
|
|
26
|
+
|
|
27
|
+
If you ran `scripts/setup/init-agent.sh`, Claude-Pace was installed automatically. Verify:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Check if the plugin is installed
|
|
31
|
+
claude plugin list | grep claude-pace
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Via Claude Code plugin system
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
claude plugin marketplace add Astro-Han/claude-pace
|
|
38
|
+
claude plugin install claude-pace
|
|
39
|
+
# Restart Claude Code or run /reload-plugins
|
|
40
|
+
claude-pace:setup
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Manual installation
|
|
44
|
+
|
|
45
|
+
Download `claude-pace.sh` from the repository and add to `~/.claude/settings.json` under `statusLine`.
|
|
46
|
+
|
|
47
|
+
## Repository
|
|
48
|
+
|
|
49
|
+
- GitHub: https://github.com/Astro-Han/claude-pace
|
|
50
|
+
- License: MIT
|
|
51
|
+
|
|
52
|
+
## Integration with Maestro
|
|
53
|
+
|
|
54
|
+
Claude-Pace complements Maestro's existing rate-limit detection (see `project_rate_limit_detection` memory). While Maestro detects rate limits reactively via workflow log analysis ("started" without "completed"), Claude-Pace provides proactive visibility — operators see quota state before it becomes a problem.
|
|
55
|
+
|
|
56
|
+
For agents running heavy backlog cycles, the pace delta indicator is the key signal: if it's red, defer non-urgent queue items to the next cycle rather than risk a mid-task stall.
|