jobarbiter 0.3.0 → 0.3.2
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/dist/index.js +172 -43
- package/dist/lib/config.js +1 -1
- package/dist/lib/detect-tools.d.ts +46 -0
- package/dist/lib/detect-tools.js +473 -0
- package/dist/lib/observe.d.ts +52 -0
- package/dist/lib/observe.js +672 -0
- package/dist/lib/onboard.d.ts +13 -0
- package/dist/lib/onboard.js +580 -0
- package/package.json +15 -5
- package/src/index.ts +194 -48
- package/src/lib/config.ts +1 -1
- package/src/lib/detect-tools.ts +526 -0
- package/src/lib/observe.ts +753 -0
- package/src/lib/onboard.ts +694 -0
- package/test/smoke.test.ts +205 -0
- package/vitest.config.ts +8 -0
package/src/index.ts
CHANGED
|
@@ -4,6 +4,9 @@ import { Command } from "commander";
|
|
|
4
4
|
import { loadConfig, saveConfig, requireConfig, getConfigPath, type Config } from "./lib/config.js";
|
|
5
5
|
import { api, apiUnauthenticated, ApiError } from "./lib/api.js";
|
|
6
6
|
import { output, outputList, success, error, setJsonMode } from "./lib/output.js";
|
|
7
|
+
import { runOnboardWizard } from "./lib/onboard.js";
|
|
8
|
+
import { detectAgents, installObservers, removeObservers, getObservationStatus } from "./lib/observe.js";
|
|
9
|
+
import { getObservableTools, formatToolDisplay } from "./lib/detect-tools.js";
|
|
7
10
|
|
|
8
11
|
const program = new Command();
|
|
9
12
|
|
|
@@ -18,57 +21,19 @@ program
|
|
|
18
21
|
});
|
|
19
22
|
|
|
20
23
|
// ============================================================
|
|
21
|
-
//
|
|
24
|
+
// onboard — Interactive setup wizard (primary entry point)
|
|
22
25
|
// ============================================================
|
|
23
26
|
|
|
24
27
|
program
|
|
25
|
-
.command("
|
|
26
|
-
.description("
|
|
27
|
-
.
|
|
28
|
-
.requiredOption("--type <type>", "Account type: worker or employer")
|
|
28
|
+
.command("onboard")
|
|
29
|
+
.description("Interactive setup wizard — the only command you need to get started")
|
|
30
|
+
.option("--force", "Start fresh even if already configured")
|
|
29
31
|
.option("--base-url <url>", "API base URL", "https://jobarbiter-api-production.up.railway.app")
|
|
30
32
|
.action(async (opts) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Step 1: Register and request verification code
|
|
39
|
-
const registerData = await apiUnauthenticated(opts.baseUrl, "POST", "/v1/auth/register", {
|
|
40
|
-
email: opts.email,
|
|
41
|
-
userType,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
success(`Verification code sent to ${opts.email} (expires in 15 minutes)`);
|
|
45
|
-
|
|
46
|
-
// Step 2: Prompt for verification code
|
|
47
|
-
const code = await promptForCode();
|
|
48
|
-
if (!code) {
|
|
49
|
-
error("Verification cancelled.");
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Step 3: Verify the code
|
|
54
|
-
const verifyData = await apiUnauthenticated(opts.baseUrl, "POST", "/v1/auth/verify", {
|
|
55
|
-
email: opts.email,
|
|
56
|
-
code: code.trim(),
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// Step 4: Save config with API key
|
|
60
|
-
saveConfig({
|
|
61
|
-
apiKey: verifyData.apiKey as string,
|
|
62
|
-
baseUrl: opts.baseUrl,
|
|
63
|
-
userType: userType as "worker" | "employer",
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
success(`Email verified! API key saved to ${getConfigPath()}`);
|
|
67
|
-
console.log(` Key: ${(verifyData.apiKey as string).slice(0, 20)}... (save this — shown only once)`);
|
|
68
|
-
output({ id: verifyData.id, email: opts.email, userType });
|
|
69
|
-
} catch (e) {
|
|
70
|
-
handleError(e);
|
|
71
|
-
}
|
|
33
|
+
await runOnboardWizard({
|
|
34
|
+
force: opts.force,
|
|
35
|
+
baseUrl: opts.baseUrl,
|
|
36
|
+
});
|
|
72
37
|
});
|
|
73
38
|
|
|
74
39
|
// ============================================================
|
|
@@ -76,7 +41,7 @@ program
|
|
|
76
41
|
// ============================================================
|
|
77
42
|
|
|
78
43
|
program
|
|
79
|
-
.command("verify")
|
|
44
|
+
.command("verify-email")
|
|
80
45
|
.description("Verify email with code (if registration was interrupted)")
|
|
81
46
|
.requiredOption("--email <email>", "Email address")
|
|
82
47
|
.option("--code <code>", "6-digit verification code")
|
|
@@ -746,7 +711,7 @@ program
|
|
|
746
711
|
// verify
|
|
747
712
|
// ============================================================
|
|
748
713
|
|
|
749
|
-
const verify = program.command("
|
|
714
|
+
const verify = program.command("identity").description("Identity verification (GitHub, LinkedIn, domain)");
|
|
750
715
|
|
|
751
716
|
verify
|
|
752
717
|
.command("linkedin <url>")
|
|
@@ -815,6 +780,187 @@ program
|
|
|
815
780
|
}
|
|
816
781
|
});
|
|
817
782
|
|
|
783
|
+
// ============================================================
|
|
784
|
+
// observe (manage coding agent observers)
|
|
785
|
+
// ============================================================
|
|
786
|
+
|
|
787
|
+
const observe = program.command("observe").description("Manage coding agent proficiency observers");
|
|
788
|
+
|
|
789
|
+
observe
|
|
790
|
+
.command("status")
|
|
791
|
+
.description("Show observer status and accumulated data")
|
|
792
|
+
.action(async () => {
|
|
793
|
+
try {
|
|
794
|
+
const agents = detectAgents();
|
|
795
|
+
const status = getObservationStatus();
|
|
796
|
+
|
|
797
|
+
const detected = agents.filter((a) => a.installed);
|
|
798
|
+
|
|
799
|
+
console.log("\n🔍 Coding Agent Observers\n");
|
|
800
|
+
console.log(" Agents:");
|
|
801
|
+
for (const agent of agents) {
|
|
802
|
+
if (!agent.installed) {
|
|
803
|
+
console.log(` ⬚ ${agent.name} (not installed)`);
|
|
804
|
+
} else if (agent.hookInstalled) {
|
|
805
|
+
console.log(` ✅ ${agent.name} (observer active)`);
|
|
806
|
+
} else {
|
|
807
|
+
console.log(` ⚠️ ${agent.name} (detected, no observer)`);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
console.log("\n Accumulated Data:");
|
|
812
|
+
console.log(` Sessions observed: ${status.totalSessions}`);
|
|
813
|
+
console.log(` Total tokens: ${status.totalTokens.toLocaleString()}`);
|
|
814
|
+
console.log(` Agents seen: ${status.agents.join(", ") || "none yet"}`);
|
|
815
|
+
console.log(` Last submitted: ${status.lastSubmitted || "never"}`);
|
|
816
|
+
|
|
817
|
+
if (status.topTools.length > 0) {
|
|
818
|
+
console.log("\n Top Tools:");
|
|
819
|
+
for (const { tool, count } of status.topTools.slice(0, 5)) {
|
|
820
|
+
console.log(` ${tool}: ${count} uses`);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
console.log(`\n Data file: ~/.config/jobarbiter/observer/observations.json\n`);
|
|
825
|
+
|
|
826
|
+
output({
|
|
827
|
+
detectedAgents: agents.map((a) => ({
|
|
828
|
+
id: a.id,
|
|
829
|
+
name: a.name,
|
|
830
|
+
installed: a.installed,
|
|
831
|
+
observerActive: a.hookInstalled,
|
|
832
|
+
})),
|
|
833
|
+
...status,
|
|
834
|
+
});
|
|
835
|
+
} catch (e) {
|
|
836
|
+
handleError(e);
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
observe
|
|
841
|
+
.command("install")
|
|
842
|
+
.description("Install observers for detected coding agents")
|
|
843
|
+
.option("--agent <id>", "Install for specific agent (claude-code, cursor, opencode, codex, gemini)")
|
|
844
|
+
.option("--all", "Install for all detected agents")
|
|
845
|
+
.action(async (opts) => {
|
|
846
|
+
try {
|
|
847
|
+
const agents = detectAgents();
|
|
848
|
+
const detected = agents.filter((a) => a.installed);
|
|
849
|
+
|
|
850
|
+
if (detected.length === 0) {
|
|
851
|
+
error("No coding agents detected on this system.");
|
|
852
|
+
console.log(" Supported: Claude Code, Cursor, OpenCode, Codex CLI, Gemini CLI");
|
|
853
|
+
process.exit(1);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
let toInstall: string[];
|
|
857
|
+
|
|
858
|
+
if (opts.agent) {
|
|
859
|
+
const agent = agents.find((a) => a.id === opts.agent);
|
|
860
|
+
if (!agent) {
|
|
861
|
+
error(`Unknown agent: ${opts.agent}. Available: ${agents.map((a) => a.id).join(", ")}`);
|
|
862
|
+
process.exit(1);
|
|
863
|
+
}
|
|
864
|
+
if (!agent.installed) {
|
|
865
|
+
error(`${agent.name} is not installed on this system.`);
|
|
866
|
+
process.exit(1);
|
|
867
|
+
}
|
|
868
|
+
toInstall = [opts.agent];
|
|
869
|
+
} else {
|
|
870
|
+
toInstall = detected.map((a) => a.id);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
const result = installObservers(toInstall);
|
|
874
|
+
|
|
875
|
+
for (const name of result.installed) {
|
|
876
|
+
success(`Installed observer for ${name}`);
|
|
877
|
+
}
|
|
878
|
+
for (const name of result.skipped) {
|
|
879
|
+
console.log(` — ${name} (already installed)`);
|
|
880
|
+
}
|
|
881
|
+
for (const { agent: a, error: errMsg } of result.errors) {
|
|
882
|
+
error(`${a}: ${errMsg}`);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
output(result);
|
|
886
|
+
} catch (e) {
|
|
887
|
+
handleError(e);
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
observe
|
|
892
|
+
.command("remove")
|
|
893
|
+
.description("Remove observers from coding agents")
|
|
894
|
+
.option("--agent <id>", "Remove from specific agent")
|
|
895
|
+
.option("--all", "Remove from all agents")
|
|
896
|
+
.action(async (opts) => {
|
|
897
|
+
try {
|
|
898
|
+
const agents = detectAgents();
|
|
899
|
+
const withHooks = agents.filter((a) => a.hookInstalled);
|
|
900
|
+
|
|
901
|
+
if (withHooks.length === 0) {
|
|
902
|
+
console.log("No observers are currently installed.");
|
|
903
|
+
process.exit(0);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
let toRemove: string[];
|
|
907
|
+
|
|
908
|
+
if (opts.agent) {
|
|
909
|
+
toRemove = [opts.agent];
|
|
910
|
+
} else {
|
|
911
|
+
toRemove = withHooks.map((a) => a.id);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
const result = removeObservers(toRemove);
|
|
915
|
+
|
|
916
|
+
for (const name of result.removed) {
|
|
917
|
+
success(`Removed observer from ${name}`);
|
|
918
|
+
}
|
|
919
|
+
for (const name of result.notFound) {
|
|
920
|
+
console.log(` — ${name} (no observer found)`);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
output(result);
|
|
924
|
+
} catch (e) {
|
|
925
|
+
handleError(e);
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
observe
|
|
930
|
+
.command("review")
|
|
931
|
+
.description("Review accumulated observation data before submission")
|
|
932
|
+
.action(async () => {
|
|
933
|
+
try {
|
|
934
|
+
const status = getObservationStatus();
|
|
935
|
+
|
|
936
|
+
if (!status.hasData) {
|
|
937
|
+
console.log("\nNo observation data collected yet.");
|
|
938
|
+
console.log("Use your coding agents normally — data accumulates automatically.\n");
|
|
939
|
+
process.exit(0);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
console.log("\n📊 Observation Data Review\n");
|
|
943
|
+
console.log(` Sessions: ${status.totalSessions}`);
|
|
944
|
+
console.log(` Tokens: ${status.totalTokens.toLocaleString()}`);
|
|
945
|
+
console.log(` Agents: ${status.agents.join(", ")}`);
|
|
946
|
+
|
|
947
|
+
if (status.topTools.length > 0) {
|
|
948
|
+
console.log("\n Tool Usage:");
|
|
949
|
+
for (const { tool, count } of status.topTools) {
|
|
950
|
+
const bar = "█".repeat(Math.min(count, 30));
|
|
951
|
+
console.log(` ${tool.padEnd(25)} ${bar} ${count}`);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
console.log(`\n This data will be submitted as an attestation when you run:`);
|
|
956
|
+
console.log(` jobarbiter attest --agent observer --capabilities <auto-generated>\n`);
|
|
957
|
+
|
|
958
|
+
output(status);
|
|
959
|
+
} catch (e) {
|
|
960
|
+
handleError(e);
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
|
|
818
964
|
// ============================================================
|
|
819
965
|
// Helpers
|
|
820
966
|
// ============================================================
|
package/src/lib/config.ts
CHANGED
|
@@ -47,7 +47,7 @@ export function saveConfig(config: Config): void {
|
|
|
47
47
|
export function requireConfig(): Config {
|
|
48
48
|
const config = loadConfig();
|
|
49
49
|
if (!config) {
|
|
50
|
-
console.error("Not configured. Run: jobarbiter
|
|
50
|
+
console.error("Not configured. Run: jobarbiter onboard");
|
|
51
51
|
console.error("Or set JOBARBITER_API_KEY environment variable.");
|
|
52
52
|
process.exit(1);
|
|
53
53
|
}
|