@uluops/setup 0.4.0 → 0.6.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.
- package/LICENSE +21 -0
- package/README.md +75 -60
- package/assets/auto-tracker-save.mjs +142 -0
- package/assets/{agents → claude-code/agents}/api-contract-validator-agent.md +9 -228
- package/assets/{agents → claude-code/agents}/aristotle-analyst-agent.md +51 -4
- package/assets/{agents → claude-code/agents}/aristotle-explorer-agent.md +6 -2
- package/assets/{agents → claude-code/agents}/aristotle-forecaster-agent.md +15 -230
- package/assets/{agents → claude-code/agents}/aristotle-validator-agent.md +12 -252
- package/assets/{agents → claude-code/agents}/assumption-excavator-agent.md +21 -247
- package/assets/{agents → claude-code/agents}/code-auditor-agent.md +12 -255
- package/assets/{agents → claude-code/agents}/code-optimizer-agent.md +15 -236
- package/assets/{agents → claude-code/agents}/code-validator-agent.md +31 -300
- package/assets/claude-code/agents/docs-validator-agent.md +472 -0
- package/assets/{agents → claude-code/agents}/frontend-validator-agent.md +15 -258
- package/assets/{agents → claude-code/agents}/mcp-validator-agent.md +8 -252
- package/assets/{agents → claude-code/agents}/pre-implementation-architect-agent.md +8 -224
- package/assets/{agents → claude-code/agents}/prompt-engineer-agent.md +57 -290
- package/assets/{agents → claude-code/agents}/prompt-pattern-analyzer-agent.md +10 -225
- package/assets/{agents → claude-code/agents}/prompt-quality-validator-agent.md +11 -249
- package/assets/{agents → claude-code/agents}/public-interface-validator-agent.md +15 -268
- package/assets/claude-code/agents/release-readiness-agent.md +495 -0
- package/assets/{agents → claude-code/agents}/security-analyst-agent.md +236 -480
- package/assets/{agents → claude-code/agents}/test-architect-agent.md +16 -259
- package/assets/{agents → claude-code/agents}/type-safety-validator-agent.md +23 -266
- package/assets/{agents → claude-code/agents}/workflow-synthesis-agent.md +23 -226
- package/assets/{commands → claude-code/commands}/agents/anxiety-reader.md +12 -15
- package/assets/{commands → claude-code/commands}/agents/api-contract.md +156 -136
- package/assets/{commands → claude-code/commands}/agents/architect.md +156 -136
- package/assets/claude-code/commands/agents/aristotle-analyst.md +157 -0
- package/assets/claude-code/commands/agents/aristotle-explorer.md +157 -0
- package/assets/claude-code/commands/agents/aristotle-forecaster.md +157 -0
- package/assets/claude-code/commands/agents/aristotle-validator.md +157 -0
- package/assets/{commands → claude-code/commands}/agents/assumption-excavator.md +49 -7
- package/assets/{commands → claude-code/commands}/agents/audit.md +156 -137
- package/assets/{commands → claude-code/commands}/agents/docs-validate.md +156 -134
- package/assets/{commands → claude-code/commands}/agents/frontend.md +156 -136
- package/assets/{commands → claude-code/commands}/agents/mcp-validate.md +156 -137
- package/assets/{commands → claude-code/commands}/agents/optimize.md +156 -134
- package/assets/{commands → claude-code/commands}/agents/pattern-analyzer.md +150 -127
- package/assets/{commands → claude-code/commands}/agents/prompt-quality.md +155 -135
- package/assets/claude-code/commands/agents/prompt-validate.md +155 -0
- package/assets/{commands → claude-code/commands}/agents/public-interface.md +156 -135
- package/assets/{commands → claude-code/commands}/agents/release.md +156 -136
- package/assets/{commands → claude-code/commands}/agents/security.md +156 -138
- package/assets/{commands → claude-code/commands}/agents/test-review.md +156 -137
- package/assets/{commands → claude-code/commands}/agents/type-safety.md +156 -136
- package/assets/{commands/agents/code-validate.md → claude-code/commands/agents/validate.md} +156 -135
- package/assets/claude-code/commands/agents/workflow-synthesis.md +157 -0
- package/assets/{commands → claude-code/commands}/pipelines/aristotle.md +8 -8
- package/assets/{commands → claude-code/commands}/pipelines/ship.md +8 -8
- package/assets/claude-code/commands/workflows/post-implementation.md +60 -0
- package/assets/claude-code/commands/workflows/pre-implementation.md +46 -0
- package/assets/{commands → claude-code/commands}/workflows/prompt-audit.md +2 -2
- package/assets/codex/agents/anxiety-reader-agent.toml +462 -0
- package/assets/codex/agents/api-contract-validator-agent.toml +738 -0
- package/assets/codex/agents/aristotle-analyst-agent.toml +750 -0
- package/assets/codex/agents/aristotle-explorer-agent.toml +155 -0
- package/assets/codex/agents/aristotle-forecaster-agent.toml +449 -0
- package/assets/codex/agents/aristotle-validator-agent.toml +424 -0
- package/assets/codex/agents/assumption-excavator-agent.toml +1126 -0
- package/assets/codex/agents/code-auditor-agent.toml +815 -0
- package/assets/codex/agents/code-optimizer-agent.toml +652 -0
- package/assets/codex/agents/code-validator-agent.toml +573 -0
- package/assets/codex/agents/docs-validator-agent.toml +468 -0
- package/assets/codex/agents/frontend-validator-agent.toml +598 -0
- package/assets/codex/agents/mcp-validator-agent.toml +580 -0
- package/assets/codex/agents/pre-implementation-architect-agent.toml +817 -0
- package/assets/codex/agents/prompt-engineer-agent.toml +922 -0
- package/assets/codex/agents/prompt-pattern-analyzer-agent.toml +689 -0
- package/assets/codex/agents/prompt-quality-validator-agent.toml +777 -0
- package/assets/codex/agents/public-interface-validator-agent.toml +695 -0
- package/assets/codex/agents/release-readiness-agent.toml +491 -0
- package/assets/codex/agents/security-analyst-agent.toml +847 -0
- package/assets/codex/agents/test-architect-agent.toml +615 -0
- package/assets/codex/agents/type-safety-validator-agent.toml +686 -0
- package/assets/codex/agents/workflow-synthesis-agent.toml +631 -0
- package/assets/gemini-cli/agents/anxiety-reader-agent.md +470 -0
- package/assets/gemini-cli/agents/api-contract-validator-agent.md +747 -0
- package/assets/gemini-cli/agents/aristotle-analyst-agent.md +758 -0
- package/assets/gemini-cli/agents/aristotle-explorer-agent.md +163 -0
- package/assets/gemini-cli/agents/aristotle-forecaster-agent.md +457 -0
- package/assets/gemini-cli/agents/aristotle-validator-agent.md +432 -0
- package/assets/gemini-cli/agents/assumption-excavator-agent.md +1134 -0
- package/assets/gemini-cli/agents/code-auditor-agent.md +827 -0
- package/assets/gemini-cli/agents/code-optimizer-agent.md +661 -0
- package/assets/gemini-cli/agents/code-validator-agent.md +582 -0
- package/assets/gemini-cli/agents/docs-validator-agent.md +477 -0
- package/assets/gemini-cli/agents/frontend-validator-agent.md +610 -0
- package/assets/gemini-cli/agents/mcp-validator-agent.md +589 -0
- package/assets/gemini-cli/agents/pre-implementation-architect-agent.md +826 -0
- package/assets/gemini-cli/agents/prompt-engineer-agent.md +931 -0
- package/assets/gemini-cli/agents/prompt-pattern-analyzer-agent.md +698 -0
- package/assets/gemini-cli/agents/prompt-quality-validator-agent.md +786 -0
- package/assets/gemini-cli/agents/public-interface-validator-agent.md +707 -0
- package/assets/gemini-cli/agents/release-readiness-agent.md +500 -0
- package/assets/gemini-cli/agents/security-analyst-agent.md +859 -0
- package/assets/gemini-cli/agents/test-architect-agent.md +624 -0
- package/assets/gemini-cli/agents/type-safety-validator-agent.md +695 -0
- package/assets/gemini-cli/agents/workflow-synthesis-agent.md +639 -0
- package/assets/gemini-cli/commands/agents/anxiety-reader.toml +155 -0
- package/assets/gemini-cli/commands/agents/api-contract.toml +154 -0
- package/assets/gemini-cli/commands/agents/architect.toml +154 -0
- package/assets/gemini-cli/commands/agents/aristotle-analyst.toml +155 -0
- package/assets/gemini-cli/commands/agents/aristotle-explorer.toml +155 -0
- package/assets/gemini-cli/commands/agents/aristotle-forecaster.toml +155 -0
- package/assets/gemini-cli/commands/agents/aristotle-validator.toml +155 -0
- package/assets/gemini-cli/commands/agents/assumption-excavator.toml +155 -0
- package/assets/gemini-cli/commands/agents/audit.toml +154 -0
- package/assets/gemini-cli/commands/agents/docs-validate.toml +154 -0
- package/assets/gemini-cli/commands/agents/frontend.toml +154 -0
- package/assets/gemini-cli/commands/agents/mcp-validate.toml +154 -0
- package/assets/gemini-cli/commands/agents/optimize.toml +154 -0
- package/assets/gemini-cli/commands/agents/pattern-analyzer.toml +148 -0
- package/assets/gemini-cli/commands/agents/prompt-quality.toml +153 -0
- package/assets/gemini-cli/commands/agents/prompt-validate.toml +153 -0
- package/assets/gemini-cli/commands/agents/public-interface.toml +154 -0
- package/assets/gemini-cli/commands/agents/release.toml +154 -0
- package/assets/gemini-cli/commands/agents/security.toml +154 -0
- package/assets/gemini-cli/commands/agents/test-review.toml +154 -0
- package/assets/gemini-cli/commands/agents/type-safety.toml +154 -0
- package/assets/gemini-cli/commands/agents/validate.toml +154 -0
- package/assets/gemini-cli/commands/agents/workflow-synthesis.toml +155 -0
- package/assets/gemini-cli/commands/pipelines/aristotle.toml +139 -0
- package/assets/gemini-cli/commands/pipelines/ship.toml +184 -0
- package/assets/gemini-cli/commands/workflows/post-implementation.toml +56 -0
- package/assets/gemini-cli/commands/workflows/pre-implementation.toml +42 -0
- package/assets/gemini-cli/commands/workflows/prompt-audit.toml +40 -0
- package/assets/opencode/agents/anxiety-reader-agent.md +472 -0
- package/assets/opencode/agents/api-contract-validator-agent.md +749 -0
- package/assets/opencode/agents/aristotle-analyst-agent.md +760 -0
- package/assets/opencode/agents/aristotle-explorer-agent.md +164 -0
- package/assets/opencode/agents/aristotle-forecaster-agent.md +459 -0
- package/assets/opencode/agents/aristotle-validator-agent.md +434 -0
- package/assets/opencode/agents/assumption-excavator-agent.md +1136 -0
- package/assets/opencode/agents/code-auditor-agent.md +826 -0
- package/assets/opencode/agents/code-optimizer-agent.md +663 -0
- package/assets/opencode/agents/code-validator-agent.md +584 -0
- package/assets/opencode/agents/docs-validator-agent.md +479 -0
- package/assets/opencode/agents/frontend-validator-agent.md +609 -0
- package/assets/opencode/agents/mcp-validator-agent.md +591 -0
- package/assets/opencode/agents/pre-implementation-architect-agent.md +828 -0
- package/assets/opencode/agents/prompt-engineer-agent.md +933 -0
- package/assets/opencode/agents/prompt-pattern-analyzer-agent.md +700 -0
- package/assets/opencode/agents/prompt-quality-validator-agent.md +788 -0
- package/assets/opencode/agents/public-interface-validator-agent.md +706 -0
- package/assets/opencode/agents/release-readiness-agent.md +502 -0
- package/assets/opencode/agents/security-analyst-agent.md +858 -0
- package/assets/opencode/agents/test-architect-agent.md +626 -0
- package/assets/opencode/agents/type-safety-validator-agent.md +697 -0
- package/assets/opencode/agents/workflow-synthesis-agent.md +641 -0
- package/dist/cli.js +49 -416
- package/dist/commands/helpers.d.ts +73 -0
- package/dist/commands/helpers.js +311 -0
- package/dist/commands/setup.d.ts +13 -0
- package/dist/commands/setup.js +93 -0
- package/dist/commands/uninstall.d.ts +3 -0
- package/dist/commands/uninstall.js +126 -0
- package/dist/commands/verify.d.ts +1 -0
- package/dist/commands/verify.js +28 -0
- package/dist/harnesses/claude-code.d.ts +1 -1
- package/dist/harnesses/claude-code.js +3 -1
- package/dist/harnesses/codex.js +6 -5
- package/dist/harnesses/gemini-cli.d.ts +4 -8
- package/dist/harnesses/gemini-cli.js +47 -21
- package/dist/harnesses/index.d.ts +10 -1
- package/dist/harnesses/index.js +11 -2
- package/dist/harnesses/opencode.d.ts +1 -1
- package/dist/harnesses/opencode.js +17 -8
- package/dist/harnesses/types.d.ts +19 -0
- package/dist/harnesses/types.js +2 -0
- package/dist/lib/asset-catalog.js +2 -2
- package/dist/lib/config-merger.d.ts +2 -1
- package/dist/lib/config-merger.js +15 -7
- package/dist/lib/file-ops.d.ts +5 -0
- package/dist/lib/file-ops.js +18 -3
- package/dist/lib/hash.d.ts +1 -1
- package/dist/lib/hash.js +2 -2
- package/dist/lib/manifest.d.ts +30 -1
- package/dist/lib/manifest.js +5 -7
- package/dist/lib/paths.d.ts +16 -1
- package/dist/lib/paths.js +31 -3
- package/dist/lib/settings-merger.d.ts +24 -9
- package/dist/lib/settings-merger.js +57 -22
- package/dist/lib/version.d.ts +2 -0
- package/dist/lib/version.js +10 -0
- package/dist/steps/agents.d.ts +1 -2
- package/dist/steps/agents.js +7 -18
- package/dist/steps/auth.d.ts +6 -0
- package/dist/steps/auth.js +19 -2
- package/dist/steps/cli.d.ts +53 -0
- package/dist/steps/cli.js +90 -0
- package/dist/steps/commands.d.ts +1 -1
- package/dist/steps/commands.js +20 -71
- package/dist/steps/detect.js +4 -0
- package/dist/steps/mcp.js +7 -15
- package/dist/steps/metrics.d.ts +12 -0
- package/dist/steps/metrics.js +52 -22
- package/dist/steps/shell.js +11 -1
- package/dist/steps/signup.d.ts +2 -2
- package/dist/steps/signup.js +9 -12
- package/dist/steps/verify.js +47 -8
- package/package.json +12 -11
- package/assets/agents/docs-validator-agent.md +0 -490
- package/assets/agents/release-readiness-agent.md +0 -482
- package/assets/commands/agents/aristotle-analyst.md +0 -116
- package/assets/commands/agents/aristotle-explorer.md +0 -93
- package/assets/commands/agents/aristotle-forecaster.md +0 -115
- package/assets/commands/agents/aristotle-validator.md +0 -115
- package/assets/commands/agents/prompt-validate.md +0 -136
- package/assets/commands/agents/workflow-synthesis.md +0 -102
- package/assets/commands/workflows/post-implementation.md +0 -577
- package/assets/commands/workflows/pre-implementation.md +0 -670
- /package/assets/{agents → claude-code/agents}/anxiety-reader-agent.md +0 -0
package/dist/steps/metrics.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Installs agent-metrics tool files and configures post-agent hooks.
|
|
5
5
|
* Only active for harnesses that support hooks (currently Claude Code only).
|
|
6
6
|
*/
|
|
7
|
-
import { mkdir, readdir, copyFile, rm, access } from "node:fs/promises";
|
|
7
|
+
import { mkdir, readdir, copyFile, rm, access, readFile } from "node:fs/promises";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
/** Where agent-metrics dist files are installed (derived from profile) */
|
|
10
10
|
function getMetricsToolDir(profile) {
|
|
@@ -18,30 +18,52 @@ export function getHookCommand(profile) {
|
|
|
18
18
|
const toolDir = getMetricsToolDir(profile);
|
|
19
19
|
if (!toolDir)
|
|
20
20
|
throw new Error("No tool dir for this harness");
|
|
21
|
-
|
|
21
|
+
const nodePath = process.execPath;
|
|
22
|
+
const hookPath = join(toolDir, "dist", "hook.js");
|
|
23
|
+
if (nodePath.includes('"') || hookPath.includes('"')) {
|
|
24
|
+
throw new Error("Hook command paths must not contain double-quote characters");
|
|
25
|
+
}
|
|
26
|
+
return `"${nodePath}" "${hookPath}"`;
|
|
22
27
|
}
|
|
23
28
|
/**
|
|
24
29
|
* Find the agent-metrics package source directory.
|
|
25
30
|
* Looks for it as a sibling package in the monorepo or as an npm dependency.
|
|
31
|
+
*
|
|
32
|
+
* Also reads the source package.json's version so the manifest can record
|
|
33
|
+
* which agent-metrics version was actually copied — the shared version
|
|
34
|
+
* ledger across the setup↔agent-metrics seam.
|
|
26
35
|
*/
|
|
27
36
|
async function findMetricsSource() {
|
|
28
|
-
// Try to find the package via Node.js module resolution
|
|
29
37
|
try {
|
|
30
38
|
const resolved = import.meta.resolve("@uluops/agent-metrics");
|
|
31
39
|
// resolved is like file:///path/to/dist/index.js — get the package root
|
|
32
40
|
const distDir = new URL(".", resolved).pathname;
|
|
33
41
|
const pkgRoot = join(distDir, "..");
|
|
34
|
-
|
|
42
|
+
const version = await readSourceVersion(pkgRoot);
|
|
43
|
+
return { pkgRoot, version };
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function readSourceVersion(pkgRoot) {
|
|
50
|
+
try {
|
|
51
|
+
const raw = await readFile(join(pkgRoot, "package.json"), "utf-8");
|
|
52
|
+
const parsed = JSON.parse(raw);
|
|
53
|
+
return typeof parsed.version === "string" ? parsed.version : null;
|
|
35
54
|
}
|
|
36
55
|
catch {
|
|
37
|
-
|
|
56
|
+
return null;
|
|
38
57
|
}
|
|
39
|
-
return null;
|
|
40
58
|
}
|
|
41
59
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
60
|
+
* Read the installed agent-metrics version from the harness tree.
|
|
61
|
+
* Returns null when the file is missing or unparseable.
|
|
62
|
+
* Exported so verify.ts can detect drift between installed and source.
|
|
44
63
|
*/
|
|
64
|
+
export async function readInstalledMetricsVersion(toolDir) {
|
|
65
|
+
return readSourceVersion(toolDir);
|
|
66
|
+
}
|
|
45
67
|
/** Copy .js files from a source dir to a dest dir, skipping test files. */
|
|
46
68
|
async function copyJsDir(srcDir, destDir, dryRun) {
|
|
47
69
|
let count = 0;
|
|
@@ -64,7 +86,13 @@ async function copyToolFiles(srcRoot, destRoot, dryRun) {
|
|
|
64
86
|
const srcDist = join(srcRoot, "dist");
|
|
65
87
|
const destDist = join(destRoot, "dist");
|
|
66
88
|
const subDirs = ["commands", "display"];
|
|
89
|
+
// Replace, don't merge. The previous behavior copied new files over old
|
|
90
|
+
// ones without removing stale entries — if agent-metrics renames or
|
|
91
|
+
// removes a file in a future version, the stale file would persist on
|
|
92
|
+
// disk and shadow the new one. Wipe dist/ before repopulating so the
|
|
93
|
+
// installed tree matches the source tree exactly.
|
|
67
94
|
if (!dryRun) {
|
|
95
|
+
await rm(destDist, { recursive: true, force: true });
|
|
68
96
|
await mkdir(destDist, { recursive: true });
|
|
69
97
|
for (const sub of subDirs) {
|
|
70
98
|
await mkdir(join(destDist, sub), { recursive: true });
|
|
@@ -74,7 +102,7 @@ async function copyToolFiles(srcRoot, destRoot, dryRun) {
|
|
|
74
102
|
for (const sub of subDirs) {
|
|
75
103
|
filesCopied += await copyJsDir(join(srcDist, sub), join(destDist, sub), dryRun);
|
|
76
104
|
}
|
|
77
|
-
// Copy package.json (needed for CLI bin resolution)
|
|
105
|
+
// Copy package.json (needed for CLI bin resolution and version detection)
|
|
78
106
|
try {
|
|
79
107
|
if (!dryRun) {
|
|
80
108
|
await copyFile(join(srcRoot, "package.json"), join(destRoot, "package.json"));
|
|
@@ -92,25 +120,22 @@ async function copyToolFiles(srcRoot, destRoot, dryRun) {
|
|
|
92
120
|
*/
|
|
93
121
|
export async function installMetrics(profile, dryRun) {
|
|
94
122
|
if (!profile.hooks || !profile.paths.toolsDir || !profile.paths.settingsPath) {
|
|
95
|
-
return {
|
|
123
|
+
return {
|
|
124
|
+
toolFilesCopied: 0,
|
|
125
|
+
hookConfigured: false,
|
|
126
|
+
hooksInstalledVersion: null,
|
|
127
|
+
skippedReason: "no-hook-support",
|
|
128
|
+
};
|
|
96
129
|
}
|
|
97
130
|
const toolDir = profile.paths.toolsDir;
|
|
98
131
|
const settingsPath = profile.paths.settingsPath;
|
|
99
|
-
const
|
|
132
|
+
const source = await findMetricsSource();
|
|
100
133
|
let toolFilesCopied = 0;
|
|
101
|
-
if (
|
|
134
|
+
if (source) {
|
|
102
135
|
if (!dryRun) {
|
|
103
136
|
await mkdir(toolDir, { recursive: true });
|
|
104
137
|
}
|
|
105
|
-
toolFilesCopied = await copyToolFiles(
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
try {
|
|
109
|
-
await access(join(toolDir, "dist", "hook.js"));
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
// Not found anywhere
|
|
113
|
-
}
|
|
138
|
+
toolFilesCopied = await copyToolFiles(source.pkgRoot, toolDir, dryRun);
|
|
114
139
|
}
|
|
115
140
|
let hookConfigured = false;
|
|
116
141
|
const hookJsPath = join(toolDir, "dist", "hook.js");
|
|
@@ -123,7 +148,12 @@ export async function installMetrics(profile, dryRun) {
|
|
|
123
148
|
else if (hookJsExists && dryRun) {
|
|
124
149
|
hookConfigured = true;
|
|
125
150
|
}
|
|
126
|
-
|
|
151
|
+
// Prefer the source version (from import.meta.resolve); fall back to reading
|
|
152
|
+
// the just-copied package.json from the harness tree so the manifest still
|
|
153
|
+
// records something useful when the source resolution returned null but a
|
|
154
|
+
// prior install left files behind.
|
|
155
|
+
const hooksInstalledVersion = source?.version ?? (hookJsExists ? await readInstalledMetricsVersion(toolDir) : null);
|
|
156
|
+
return { toolFilesCopied, hookConfigured, hooksInstalledVersion };
|
|
127
157
|
}
|
|
128
158
|
/**
|
|
129
159
|
* Uninstall agent-metrics: remove hook and tool files.
|
package/dist/steps/shell.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
2
3
|
import { atomicWrite } from "../lib/atomic-write.js";
|
|
4
|
+
import { backupFile } from "../lib/file-ops.js";
|
|
5
|
+
import { getUluopsDir } from "../lib/paths.js";
|
|
3
6
|
const FENCE_START = "# --- UluOps (managed by @uluops/setup) ---";
|
|
4
7
|
const FENCE_END = "# --- /UluOps ---";
|
|
5
8
|
/** Characters safe for shell variable values (no metacharacters). */
|
|
@@ -16,12 +19,15 @@ export async function writeShellExport(profilePath, apiKey, dryRun) {
|
|
|
16
19
|
}
|
|
17
20
|
catch {
|
|
18
21
|
if (!dryRun) {
|
|
19
|
-
await atomicWrite(profilePath, block + "\n");
|
|
22
|
+
await atomicWrite(profilePath, block + "\n", { mode: 0o600 });
|
|
20
23
|
}
|
|
21
24
|
return;
|
|
22
25
|
}
|
|
23
26
|
const startIdx = content.indexOf(FENCE_START);
|
|
24
27
|
const endIdx = content.indexOf(FENCE_END);
|
|
28
|
+
if (!dryRun) {
|
|
29
|
+
await backupProfile(profilePath);
|
|
30
|
+
}
|
|
25
31
|
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
26
32
|
// Replace existing fenced block (use last FENCE_END after FENCE_START to handle duplicates)
|
|
27
33
|
const before = content.slice(0, startIdx);
|
|
@@ -48,8 +54,12 @@ export async function removeShellExport(profilePath) {
|
|
|
48
54
|
const startIdx = content.indexOf(FENCE_START);
|
|
49
55
|
const endIdx = content.indexOf(FENCE_END);
|
|
50
56
|
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
57
|
+
await backupProfile(profilePath);
|
|
51
58
|
const before = content.slice(0, startIdx);
|
|
52
59
|
const after = content.slice(endIdx + FENCE_END.length);
|
|
53
60
|
await atomicWrite(profilePath, (before + after).replace(/\n{3,}/g, "\n\n"));
|
|
54
61
|
}
|
|
55
62
|
}
|
|
63
|
+
async function backupProfile(profilePath) {
|
|
64
|
+
await backupFile(profilePath, join(getUluopsDir(), "backups", "shell"));
|
|
65
|
+
}
|
package/dist/steps/signup.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { AuthResult } from "./auth.js";
|
|
|
4
4
|
* but all are non-blocking — server validation is the authority.
|
|
5
5
|
* @internal Exported for testing only — not part of the public API.
|
|
6
6
|
*/
|
|
7
|
-
declare function
|
|
7
|
+
declare function hintPassword(password: string): true;
|
|
8
8
|
/** @internal Exported for testing only — not part of the public API. */
|
|
9
9
|
declare function validateEmail(email: string): string | true;
|
|
10
10
|
/**
|
|
@@ -13,4 +13,4 @@ declare function validateEmail(email: string): string | true;
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function signup(): Promise<AuthResult>;
|
|
15
15
|
/** @internal Exported for testing only — not part of the public API. */
|
|
16
|
-
export {
|
|
16
|
+
export { hintPassword, validateEmail };
|
package/dist/steps/signup.js
CHANGED
|
@@ -4,7 +4,7 @@ const API_BASE = "https://api.uluops.ai/api/v1";
|
|
|
4
4
|
* but all are non-blocking — server validation is the authority.
|
|
5
5
|
* @internal Exported for testing only — not part of the public API.
|
|
6
6
|
*/
|
|
7
|
-
function
|
|
7
|
+
function hintPassword(password) {
|
|
8
8
|
if (password.length < 8)
|
|
9
9
|
console.warn(" ⚠ Hint: server may require at least 8 characters");
|
|
10
10
|
else if (password.length > 128)
|
|
@@ -38,25 +38,19 @@ export async function signup() {
|
|
|
38
38
|
const pwd = await password({
|
|
39
39
|
message: "Password",
|
|
40
40
|
mask: "*",
|
|
41
|
-
validate:
|
|
41
|
+
validate: hintPassword,
|
|
42
42
|
});
|
|
43
43
|
// Register
|
|
44
|
-
const registerRes = await callApi(`${API_BASE}/auth/register`, "POST", { email, password: pwd });
|
|
45
|
-
if (!registerRes.data?.sessionToken) {
|
|
46
|
-
throw new Error("Registration succeeded but response missing session token");
|
|
47
|
-
}
|
|
44
|
+
const registerRes = await callApi(`${API_BASE}/auth/register`, "POST", { email, password: pwd }, undefined, (res) => !!res.data?.sessionToken && typeof res.data.user?.email === "string");
|
|
48
45
|
const sessionToken = registerRes.data.sessionToken;
|
|
49
46
|
// Create API key using the session
|
|
50
|
-
const keyRes = await callApi(`${API_BASE}/auth/keys`, "POST", { name: "Setup CLI" }, sessionToken);
|
|
51
|
-
if (!keyRes.data?.key) {
|
|
52
|
-
throw new Error("API key creation succeeded but response missing key");
|
|
53
|
-
}
|
|
47
|
+
const keyRes = await callApi(`${API_BASE}/auth/keys`, "POST", { name: "Setup CLI" }, sessionToken, (res) => !!res.data?.key);
|
|
54
48
|
return {
|
|
55
49
|
apiKey: keyRes.data.key,
|
|
56
50
|
email: registerRes.data.user?.email ?? email,
|
|
57
51
|
};
|
|
58
52
|
}
|
|
59
|
-
async function callApi(url, method, body, bearerToken) {
|
|
53
|
+
async function callApi(url, method, body, bearerToken, validate) {
|
|
60
54
|
const headers = {
|
|
61
55
|
"Content-Type": "application/json",
|
|
62
56
|
};
|
|
@@ -83,6 +77,9 @@ async function callApi(url, method, body, bearerToken) {
|
|
|
83
77
|
if (typeof body !== "object" || body === null) {
|
|
84
78
|
throw new Error("Unexpected API response shape");
|
|
85
79
|
}
|
|
80
|
+
if (validate && !validate(body)) {
|
|
81
|
+
throw new Error("API response failed structural validation");
|
|
82
|
+
}
|
|
86
83
|
return body;
|
|
87
84
|
}
|
|
88
85
|
// Handle known error codes
|
|
@@ -101,4 +98,4 @@ async function callApi(url, method, body, bearerToken) {
|
|
|
101
98
|
throw new Error(`Signup failed (${res.status}): ${message}`);
|
|
102
99
|
}
|
|
103
100
|
/** @internal Exported for testing only — not part of the public API. */
|
|
104
|
-
export {
|
|
101
|
+
export { hintPassword, validateEmail };
|
package/dist/steps/verify.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { readdir, access } from "node:fs/promises";
|
|
1
|
+
import { readdir, access, stat } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { loadManifest } from "../lib/manifest.js";
|
|
4
4
|
import { getHealthTimeout } from "../lib/health.js";
|
|
5
5
|
import { getProfile } from "../harnesses/index.js";
|
|
6
|
+
import { readInstalledMetricsVersion } from "./metrics.js";
|
|
6
7
|
/** Verify a single harness entry, appending results to checks. Returns false if any check fails. */
|
|
7
8
|
async function verifyHarness(harnessName, hm, checks) {
|
|
8
9
|
let allOk = true;
|
|
@@ -14,7 +15,24 @@ async function verifyHarness(harnessName, hm, checks) {
|
|
|
14
15
|
checks.push({ label: `Harness: ${harnessName}`, passed: false, detail: "Unknown harness in manifest" });
|
|
15
16
|
return false;
|
|
16
17
|
}
|
|
17
|
-
//
|
|
18
|
+
// 1. Readiness Check (Harness Restart)
|
|
19
|
+
try {
|
|
20
|
+
const configStat = await stat(hm.mcpConfigPath);
|
|
21
|
+
const installedTime = new Date(hm.installedAt).getTime();
|
|
22
|
+
const configTime = configStat.mtimeMs;
|
|
23
|
+
// If config was modified AFTER install, it's a proxy for the harness having read it.
|
|
24
|
+
// We add a 100ms buffer to handle near-simultaneous writes.
|
|
25
|
+
const isReady = configTime > (installedTime + 100);
|
|
26
|
+
checks.push({
|
|
27
|
+
label: `[${profile.displayName}] Readiness`,
|
|
28
|
+
passed: true,
|
|
29
|
+
detail: isReady ? "Active (loaded)" : "Waiting for restart",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// If we can't stat it, we'll catch the error in the MCP config check below.
|
|
34
|
+
}
|
|
35
|
+
// 2. MCP config
|
|
18
36
|
try {
|
|
19
37
|
const config = await profile.mcpConfig.read(hm.mcpConfigPath);
|
|
20
38
|
if (profile.mcpConfig.check(config)) {
|
|
@@ -49,14 +67,16 @@ async function verifyHarness(harnessName, hm, checks) {
|
|
|
49
67
|
// Command files
|
|
50
68
|
if (hm.commands.length > 0) {
|
|
51
69
|
const commandsDir = join(hm.defsPath, "commands");
|
|
52
|
-
|
|
53
|
-
for (const cmd of hm.commands) {
|
|
70
|
+
const cmdResults = await Promise.all(hm.commands.map(async (cmd) => {
|
|
54
71
|
try {
|
|
55
72
|
await access(join(commandsDir, cmd));
|
|
56
|
-
|
|
73
|
+
return true;
|
|
57
74
|
}
|
|
58
|
-
catch {
|
|
59
|
-
|
|
75
|
+
catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}));
|
|
79
|
+
const found = cmdResults.filter(Boolean).length;
|
|
60
80
|
checks.push({
|
|
61
81
|
label: `[${profile.displayName}] ${found}/${hm.commands.length} commands`,
|
|
62
82
|
passed: found === hm.commands.length,
|
|
@@ -77,7 +97,26 @@ async function verifyHarness(harnessName, hm, checks) {
|
|
|
77
97
|
catch { /* Missing */ }
|
|
78
98
|
}
|
|
79
99
|
if (hookPresent && hookFilePresent) {
|
|
80
|
-
|
|
100
|
+
// Existence is necessary but not sufficient — check version drift against the
|
|
101
|
+
// currently-resolvable agent-metrics. Without this, verify cannot detect that
|
|
102
|
+
// a fresh setup release is shipping while the installed hook is stale.
|
|
103
|
+
const installedVersion = profile.paths.toolsDir
|
|
104
|
+
? await readInstalledMetricsVersion(profile.paths.toolsDir)
|
|
105
|
+
: null;
|
|
106
|
+
const recordedVersion = hm.hooksInstalledVersion ?? null;
|
|
107
|
+
const detail = installedVersion
|
|
108
|
+
? `v${installedVersion}${recordedVersion && recordedVersion !== installedVersion ? ` (manifest records v${recordedVersion} — out of sync)` : ""}`
|
|
109
|
+
: "version unknown";
|
|
110
|
+
checks.push({
|
|
111
|
+
label: `[${profile.displayName}] Agent metrics hook configured`,
|
|
112
|
+
passed: true,
|
|
113
|
+
detail,
|
|
114
|
+
});
|
|
115
|
+
if (recordedVersion && installedVersion && recordedVersion !== installedVersion) {
|
|
116
|
+
// Drift between manifest record and on-disk hook copy — not a hard failure
|
|
117
|
+
// because verify is read-only, but surface it so the user re-runs setup.
|
|
118
|
+
allOk = false;
|
|
119
|
+
}
|
|
81
120
|
}
|
|
82
121
|
else {
|
|
83
122
|
const missing = [
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uluops/setup",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "Zero-friction installer for UluOps agentic harnesses",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/Uluops/-uluops-setup.git"
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"dist/lib/**",
|
|
33
33
|
"dist/steps/**",
|
|
34
34
|
"dist/harnesses/**",
|
|
35
|
+
"dist/commands/**",
|
|
35
36
|
"assets"
|
|
36
37
|
],
|
|
37
38
|
"engines": {
|
|
@@ -46,16 +47,16 @@
|
|
|
46
47
|
"prepublishOnly": "npm run build"
|
|
47
48
|
},
|
|
48
49
|
"dependencies": {
|
|
49
|
-
"@inquirer/prompts": "
|
|
50
|
-
"@uluops/agent-metrics": "
|
|
51
|
-
"chalk": "
|
|
52
|
-
"commander": "
|
|
53
|
-
"jsonc-parser": "
|
|
50
|
+
"@inquirer/prompts": "7.10.1",
|
|
51
|
+
"@uluops/agent-metrics": "0.4.0",
|
|
52
|
+
"chalk": "5.6.2",
|
|
53
|
+
"commander": "12.1.0",
|
|
54
|
+
"jsonc-parser": "3.3.1"
|
|
54
55
|
},
|
|
55
56
|
"devDependencies": {
|
|
56
|
-
"@types/node": "
|
|
57
|
-
"tsx": "
|
|
58
|
-
"typescript": "
|
|
59
|
-
"vitest": "
|
|
57
|
+
"@types/node": "22.19.15",
|
|
58
|
+
"tsx": "4.21.0",
|
|
59
|
+
"typescript": "5.9.3",
|
|
60
|
+
"vitest": "3.2.4"
|
|
60
61
|
}
|
|
61
62
|
}
|