@uluops/setup 0.2.0 → 0.6.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/LICENSE +21 -0
- package/README.md +109 -89
- package/assets/auto-tracker-save.mjs +142 -0
- package/assets/claude-code/agents/anxiety-reader-agent.md +464 -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/claude-code/commands/agents/anxiety-reader.md +157 -0
- package/assets/{commands → claude-code/commands}/agents/api-contract.md +156 -135
- package/assets/{commands → claude-code/commands}/agents/architect.md +156 -135
- 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 -6
- package/assets/{commands → claude-code/commands}/agents/audit.md +156 -136
- package/assets/{commands → claude-code/commands}/agents/docs-validate.md +156 -133
- package/assets/{commands → claude-code/commands}/agents/frontend.md +156 -135
- package/assets/{commands → claude-code/commands}/agents/mcp-validate.md +156 -136
- package/assets/{commands → claude-code/commands}/agents/optimize.md +156 -133
- package/assets/{commands → claude-code/commands}/agents/pattern-analyzer.md +150 -126
- package/assets/{commands → claude-code/commands}/agents/prompt-quality.md +155 -134
- package/assets/claude-code/commands/agents/prompt-validate.md +155 -0
- package/assets/{commands → claude-code/commands}/agents/public-interface.md +156 -134
- package/assets/{commands → claude-code/commands}/agents/release.md +156 -135
- package/assets/{commands → claude-code/commands}/agents/security.md +156 -137
- package/assets/{commands → claude-code/commands}/agents/test-review.md +156 -136
- package/assets/{commands → claude-code/commands}/agents/type-safety.md +156 -135
- package/assets/{commands → claude-code/commands}/agents/validate.md +156 -134
- package/assets/claude-code/commands/agents/workflow-synthesis.md +157 -0
- package/assets/claude-code/commands/pipelines/aristotle.md +143 -0
- package/assets/claude-code/commands/pipelines/ship.md +188 -0
- package/assets/claude-code/commands/workflows/post-implementation.md +60 -0
- package/assets/claude-code/commands/workflows/pre-implementation.md +46 -0
- package/assets/claude-code/commands/workflows/prompt-audit.md +44 -0
- 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 +22 -380
- package/dist/commands/helpers.d.ts +73 -0
- package/dist/commands/helpers.js +274 -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 +8 -0
- package/dist/harnesses/claude-code.js +74 -0
- package/dist/harnesses/codex.d.ts +15 -0
- package/dist/harnesses/codex.js +54 -0
- package/dist/harnesses/gemini-cli.d.ts +12 -0
- package/dist/harnesses/gemini-cli.js +80 -0
- package/dist/harnesses/index.d.ts +27 -0
- package/dist/harnesses/index.js +54 -0
- package/dist/harnesses/opencode.d.ts +14 -0
- package/dist/harnesses/opencode.js +139 -0
- package/dist/harnesses/types.d.ts +106 -0
- package/dist/harnesses/types.js +26 -0
- package/dist/lib/agent-transform.d.ts +12 -0
- package/dist/lib/agent-transform.js +129 -0
- package/dist/lib/asset-catalog.d.ts +9 -0
- package/dist/lib/asset-catalog.js +56 -0
- package/dist/lib/atomic-write.d.ts +11 -0
- package/dist/lib/atomic-write.js +28 -0
- package/dist/lib/config-merger.d.ts +9 -2
- package/dist/lib/config-merger.js +44 -7
- package/dist/lib/display.d.ts +14 -0
- package/dist/lib/display.js +66 -0
- package/dist/lib/file-ops.d.ts +11 -0
- package/dist/lib/file-ops.js +40 -4
- package/dist/lib/hash.d.ts +1 -0
- package/dist/lib/hash.js +2 -1
- package/dist/lib/health.d.ts +2 -0
- package/dist/lib/health.js +10 -0
- package/dist/lib/manifest.d.ts +51 -5
- package/dist/lib/manifest.js +146 -13
- package/dist/lib/paths.d.ts +30 -3
- package/dist/lib/paths.js +98 -12
- package/dist/lib/settings-merger.d.ts +31 -8
- package/dist/lib/settings-merger.js +87 -24
- package/dist/lib/version.d.ts +2 -0
- package/dist/lib/version.js +10 -0
- package/dist/steps/agents.d.ts +4 -1
- package/dist/steps/agents.js +48 -9
- package/dist/steps/auth.js +26 -10
- package/dist/steps/cli.d.ts +53 -0
- package/dist/steps/cli.js +90 -0
- package/dist/steps/commands.d.ts +6 -1
- package/dist/steps/commands.js +36 -9
- package/dist/steps/detect.d.ts +3 -0
- package/dist/steps/detect.js +11 -0
- package/dist/steps/mcp.d.ts +6 -2
- package/dist/steps/mcp.js +39 -22
- package/dist/steps/metrics.d.ts +26 -10
- package/dist/steps/metrics.js +108 -108
- package/dist/steps/shell.d.ts +2 -0
- package/dist/steps/shell.js +26 -9
- package/dist/steps/signup.d.ts +7 -4
- package/dist/steps/signup.js +29 -20
- package/dist/steps/verify.d.ts +2 -2
- package/dist/steps/verify.js +118 -112
- package/package.json +40 -14
- 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 -115
- package/assets/commands/agents/aristotle-explorer.md +0 -92
- package/assets/commands/agents/aristotle-forecaster.md +0 -114
- package/assets/commands/agents/aristotle-validator.md +0 -114
- package/assets/commands/agents/prompt-validate.md +0 -135
- package/assets/commands/agents/workflow-synthesis.md +0 -101
- package/assets/commands/workflows/aristotle.md +0 -543
- package/assets/commands/workflows/post-implementation.md +0 -577
- package/assets/commands/workflows/pre-implementation.md +0 -670
- package/assets/commands/workflows/prompt-audit.md +0 -754
- package/assets/commands/workflows/ship.md +0 -721
- package/dist/test/auth.test.d.ts +0 -1
- package/dist/test/auth.test.js +0 -43
- package/dist/test/config-io.test.d.ts +0 -1
- package/dist/test/config-io.test.js +0 -56
- package/dist/test/config-merger.test.d.ts +0 -1
- package/dist/test/config-merger.test.js +0 -94
- package/dist/test/detect.test.d.ts +0 -1
- package/dist/test/detect.test.js +0 -25
- package/dist/test/file-ops.test.d.ts +0 -1
- package/dist/test/file-ops.test.js +0 -100
- package/dist/test/hash.test.d.ts +0 -1
- package/dist/test/hash.test.js +0 -14
- package/dist/test/manifest.test.d.ts +0 -1
- package/dist/test/manifest.test.js +0 -78
- package/dist/test/paths.test.d.ts +0 -1
- package/dist/test/paths.test.js +0 -30
- package/dist/test/settings-merger.test.d.ts +0 -1
- package/dist/test/settings-merger.test.js +0 -167
- package/dist/test/shell-profile.test.d.ts +0 -1
- package/dist/test/shell-profile.test.js +0 -40
- package/dist/test/shell.test.d.ts +0 -1
- package/dist/test/shell.test.js +0 -71
- package/dist/test/signup.test.d.ts +0 -1
- package/dist/test/signup.test.js +0 -83
package/dist/steps/metrics.js
CHANGED
|
@@ -1,106 +1,108 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Metrics Step
|
|
3
3
|
*
|
|
4
|
-
* Installs agent-metrics tool files
|
|
5
|
-
*
|
|
4
|
+
* Installs agent-metrics tool files and configures post-agent hooks.
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export function getMetricsToolDir() {
|
|
13
|
-
return join(getClaudeHome(), "tools", "agent-metrics");
|
|
9
|
+
/** Where agent-metrics dist files are installed (derived from profile) */
|
|
10
|
+
function getMetricsToolDir(profile) {
|
|
11
|
+
return profile.paths.toolsDir;
|
|
14
12
|
}
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The hook command that runs on SubagentStop.
|
|
15
|
+
* @internal Exported for testing only — not part of the public API.
|
|
16
|
+
*/
|
|
17
|
+
export function getHookCommand(profile) {
|
|
18
|
+
const toolDir = getMetricsToolDir(profile);
|
|
19
|
+
if (!toolDir)
|
|
20
|
+
throw new Error("No tool dir for this harness");
|
|
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}"`;
|
|
23
27
|
}
|
|
24
28
|
/**
|
|
25
29
|
* Find the agent-metrics package source directory.
|
|
26
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.
|
|
27
35
|
*/
|
|
28
36
|
async function findMetricsSource() {
|
|
29
|
-
// Try to find the package via Node.js module resolution
|
|
30
37
|
try {
|
|
31
38
|
const resolved = import.meta.resolve("@uluops/agent-metrics");
|
|
32
39
|
// resolved is like file:///path/to/dist/index.js — get the package root
|
|
33
40
|
const distDir = new URL(".", resolved).pathname;
|
|
34
41
|
const pkgRoot = join(distDir, "..");
|
|
35
|
-
|
|
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;
|
|
36
54
|
}
|
|
37
55
|
catch {
|
|
38
|
-
|
|
56
|
+
return null;
|
|
39
57
|
}
|
|
40
|
-
return null;
|
|
41
58
|
}
|
|
42
59
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
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.
|
|
45
63
|
*/
|
|
46
|
-
async function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
await mkdir(join(destDist, "display"), { recursive: true });
|
|
53
|
-
}
|
|
54
|
-
let filesCopied = 0;
|
|
55
|
-
// Copy top-level dist files
|
|
56
|
-
const topFiles = await readdir(srcDist);
|
|
57
|
-
for (const file of topFiles) {
|
|
58
|
-
if (!file.endsWith(".js"))
|
|
59
|
-
continue;
|
|
60
|
-
if (file.includes(".test."))
|
|
61
|
-
continue;
|
|
62
|
-
if (file === "test-utils.js")
|
|
63
|
-
continue;
|
|
64
|
-
if (!dryRun) {
|
|
65
|
-
await copyFile(join(srcDist, file), join(destDist, file));
|
|
66
|
-
}
|
|
67
|
-
filesCopied++;
|
|
68
|
-
}
|
|
69
|
-
// Copy commands/ subdirectory
|
|
64
|
+
export async function readInstalledMetricsVersion(toolDir) {
|
|
65
|
+
return readSourceVersion(toolDir);
|
|
66
|
+
}
|
|
67
|
+
/** Copy .js files from a source dir to a dest dir, skipping test files. */
|
|
68
|
+
async function copyJsDir(srcDir, destDir, dryRun) {
|
|
69
|
+
let count = 0;
|
|
70
70
|
try {
|
|
71
|
-
const
|
|
72
|
-
for (const file of
|
|
73
|
-
if (!file.endsWith(".js"))
|
|
71
|
+
const files = await readdir(srcDir);
|
|
72
|
+
for (const file of files) {
|
|
73
|
+
if (!file.endsWith(".js") || file.includes(".test.") || file === "test-utils.js")
|
|
74
74
|
continue;
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
await copyFile(join(srcDist, "commands", file), join(destDist, "commands", file));
|
|
79
|
-
}
|
|
80
|
-
filesCopied++;
|
|
75
|
+
if (!dryRun)
|
|
76
|
+
await copyFile(join(srcDir, file), join(destDir, file));
|
|
77
|
+
count++;
|
|
81
78
|
}
|
|
82
79
|
}
|
|
83
80
|
catch {
|
|
84
|
-
//
|
|
81
|
+
// Directory doesn't exist — not critical
|
|
85
82
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
83
|
+
return count;
|
|
84
|
+
}
|
|
85
|
+
async function copyToolFiles(srcRoot, destRoot, dryRun) {
|
|
86
|
+
const srcDist = join(srcRoot, "dist");
|
|
87
|
+
const destDist = join(destRoot, "dist");
|
|
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.
|
|
94
|
+
if (!dryRun) {
|
|
95
|
+
await rm(destDist, { recursive: true, force: true });
|
|
96
|
+
await mkdir(destDist, { recursive: true });
|
|
97
|
+
for (const sub of subDirs) {
|
|
98
|
+
await mkdir(join(destDist, sub), { recursive: true });
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
let filesCopied = await copyJsDir(srcDist, destDist, dryRun);
|
|
102
|
+
for (const sub of subDirs) {
|
|
103
|
+
filesCopied += await copyJsDir(join(srcDist, sub), join(destDist, sub), dryRun);
|
|
102
104
|
}
|
|
103
|
-
// Copy package.json (needed for CLI bin resolution)
|
|
105
|
+
// Copy package.json (needed for CLI bin resolution and version detection)
|
|
104
106
|
try {
|
|
105
107
|
if (!dryRun) {
|
|
106
108
|
await copyFile(join(srcRoot, "package.json"), join(destRoot, "package.json"));
|
|
@@ -113,61 +115,59 @@ async function copyToolFiles(srcRoot, destRoot, dryRun) {
|
|
|
113
115
|
return filesCopied;
|
|
114
116
|
}
|
|
115
117
|
/**
|
|
116
|
-
* Install agent-metrics: copy tool files and configure
|
|
118
|
+
* Install agent-metrics: copy tool files and configure hook.
|
|
119
|
+
* Skips entirely if the harness doesn't support hooks.
|
|
117
120
|
*/
|
|
118
|
-
export async function installMetrics(dryRun) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
export async function installMetrics(profile, dryRun) {
|
|
122
|
+
if (!profile.hooks || !profile.paths.toolsDir || !profile.paths.settingsPath) {
|
|
123
|
+
return {
|
|
124
|
+
toolFilesCopied: 0,
|
|
125
|
+
hookConfigured: false,
|
|
126
|
+
hooksInstalledVersion: null,
|
|
127
|
+
skippedReason: "no-hook-support",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const toolDir = profile.paths.toolsDir;
|
|
131
|
+
const settingsPath = profile.paths.settingsPath;
|
|
132
|
+
const source = await findMetricsSource();
|
|
123
133
|
let toolFilesCopied = 0;
|
|
124
|
-
if (
|
|
125
|
-
// Copy tool files
|
|
134
|
+
if (source) {
|
|
126
135
|
if (!dryRun) {
|
|
127
136
|
await mkdir(toolDir, { recursive: true });
|
|
128
137
|
}
|
|
129
|
-
toolFilesCopied = await copyToolFiles(
|
|
138
|
+
toolFilesCopied = await copyToolFiles(source.pkgRoot, toolDir, dryRun);
|
|
130
139
|
}
|
|
131
|
-
else {
|
|
132
|
-
// Check if already installed (from previous run or install.sh)
|
|
133
|
-
try {
|
|
134
|
-
await access(join(toolDir, "dist", "hook.js"));
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
// Not found anywhere — skip tool installation, just configure hook
|
|
138
|
-
// if files happen to exist
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// Configure hook in settings.json
|
|
142
140
|
let hookConfigured = false;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
await
|
|
141
|
+
const hookJsPath = join(toolDir, "dist", "hook.js");
|
|
142
|
+
const hookJsExists = await access(hookJsPath).then(() => true, () => false);
|
|
143
|
+
if (hookJsExists && !dryRun) {
|
|
144
|
+
const hookCommand = getHookCommand(profile);
|
|
145
|
+
await profile.hooks.install(settingsPath, hookCommand, false);
|
|
148
146
|
hookConfigured = true;
|
|
149
147
|
}
|
|
150
|
-
else {
|
|
148
|
+
else if (hookJsExists && dryRun) {
|
|
151
149
|
hookConfigured = true;
|
|
152
150
|
}
|
|
153
|
-
|
|
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 };
|
|
154
157
|
}
|
|
155
158
|
/**
|
|
156
|
-
* Uninstall agent-metrics: remove hook
|
|
159
|
+
* Uninstall agent-metrics: remove hook and tool files.
|
|
157
160
|
*/
|
|
158
|
-
export async function uninstallMetrics(dryRun) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
161
|
+
export async function uninstallMetrics(profile, dryRun) {
|
|
162
|
+
if (!profile.hooks || !profile.paths.toolsDir || !profile.paths.settingsPath) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
162
165
|
if (!dryRun) {
|
|
163
|
-
|
|
164
|
-
const cleaned = removeUluopsHook(settings);
|
|
165
|
-
await writeSettings(settingsPath, cleaned);
|
|
166
|
+
await profile.hooks.remove(profile.paths.settingsPath, false);
|
|
166
167
|
}
|
|
167
|
-
// Remove tool directory
|
|
168
168
|
if (!dryRun) {
|
|
169
169
|
try {
|
|
170
|
-
await rm(
|
|
170
|
+
await rm(profile.paths.toolsDir, { recursive: true, force: true });
|
|
171
171
|
}
|
|
172
172
|
catch {
|
|
173
173
|
// Already gone
|
package/dist/steps/shell.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
/** Write a fenced ULUOPS_API_KEY export block into the user's shell profile, replacing any existing UluOps block. */
|
|
1
2
|
export declare function writeShellExport(profilePath: string, apiKey: string, dryRun: boolean): Promise<void>;
|
|
3
|
+
/** Remove the fenced UluOps export block from the user's shell profile. */
|
|
2
4
|
export declare function removeShellExport(profilePath: string): Promise<void>;
|
package/dist/steps/shell.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import { readFile
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { atomicWrite } from "../lib/atomic-write.js";
|
|
4
|
+
import { backupFile } from "../lib/file-ops.js";
|
|
5
|
+
import { getUluopsDir } from "../lib/paths.js";
|
|
2
6
|
const FENCE_START = "# --- UluOps (managed by @uluops/setup) ---";
|
|
3
7
|
const FENCE_END = "# --- /UluOps ---";
|
|
8
|
+
/** Characters safe for shell variable values (no metacharacters). */
|
|
9
|
+
const SAFE_KEY_PATTERN = /^[a-zA-Z0-9_\-\.]+$/;
|
|
10
|
+
/** Write a fenced ULUOPS_API_KEY export block into the user's shell profile, replacing any existing UluOps block. */
|
|
4
11
|
export async function writeShellExport(profilePath, apiKey, dryRun) {
|
|
12
|
+
if (!SAFE_KEY_PATTERN.test(apiKey)) {
|
|
13
|
+
throw new Error("API key contains characters unsafe for shell export. Only alphanumeric, underscore, hyphen, and dot are allowed.");
|
|
14
|
+
}
|
|
5
15
|
const block = `${FENCE_START}\nexport ULUOPS_API_KEY="${apiKey}"\n${FENCE_END}`;
|
|
6
16
|
let content;
|
|
7
17
|
try {
|
|
@@ -9,27 +19,30 @@ export async function writeShellExport(profilePath, apiKey, dryRun) {
|
|
|
9
19
|
}
|
|
10
20
|
catch {
|
|
11
21
|
if (!dryRun) {
|
|
12
|
-
await
|
|
22
|
+
await atomicWrite(profilePath, block + "\n", { mode: 0o600 });
|
|
13
23
|
}
|
|
14
24
|
return;
|
|
15
25
|
}
|
|
16
26
|
const startIdx = content.indexOf(FENCE_START);
|
|
17
27
|
const endIdx = content.indexOf(FENCE_END);
|
|
18
|
-
if (
|
|
19
|
-
|
|
28
|
+
if (!dryRun) {
|
|
29
|
+
await backupProfile(profilePath);
|
|
30
|
+
}
|
|
31
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
32
|
+
// Replace existing fenced block (use last FENCE_END after FENCE_START to handle duplicates)
|
|
20
33
|
const before = content.slice(0, startIdx);
|
|
21
34
|
const after = content.slice(endIdx + FENCE_END.length);
|
|
22
35
|
if (!dryRun) {
|
|
23
|
-
await
|
|
36
|
+
await atomicWrite(profilePath, before + block + after);
|
|
24
37
|
}
|
|
25
38
|
}
|
|
26
39
|
else {
|
|
27
|
-
// Append
|
|
28
40
|
if (!dryRun) {
|
|
29
|
-
await
|
|
41
|
+
await atomicWrite(profilePath, content.trimEnd() + "\n\n" + block + "\n");
|
|
30
42
|
}
|
|
31
43
|
}
|
|
32
44
|
}
|
|
45
|
+
/** Remove the fenced UluOps export block from the user's shell profile. */
|
|
33
46
|
export async function removeShellExport(profilePath) {
|
|
34
47
|
let content;
|
|
35
48
|
try {
|
|
@@ -40,9 +53,13 @@ export async function removeShellExport(profilePath) {
|
|
|
40
53
|
}
|
|
41
54
|
const startIdx = content.indexOf(FENCE_START);
|
|
42
55
|
const endIdx = content.indexOf(FENCE_END);
|
|
43
|
-
if (startIdx !== -1 && endIdx !== -1) {
|
|
56
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
57
|
+
await backupProfile(profilePath);
|
|
44
58
|
const before = content.slice(0, startIdx);
|
|
45
59
|
const after = content.slice(endIdx + FENCE_END.length);
|
|
46
|
-
await
|
|
60
|
+
await atomicWrite(profilePath, (before + after).replace(/\n{3,}/g, "\n\n"));
|
|
47
61
|
}
|
|
48
62
|
}
|
|
63
|
+
async function backupProfile(profilePath) {
|
|
64
|
+
await backupFile(profilePath, join(getUluopsDir(), "backups", "shell"));
|
|
65
|
+
}
|
package/dist/steps/signup.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { AuthResult } from "./auth.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Advisory password hints. Returns warning strings for inquirer display,
|
|
4
|
+
* but all are non-blocking — server validation is the authority.
|
|
5
|
+
* @internal Exported for testing only — not part of the public API.
|
|
5
6
|
*/
|
|
6
|
-
declare function
|
|
7
|
+
declare function hintPassword(password: string): true;
|
|
8
|
+
/** @internal Exported for testing only — not part of the public API. */
|
|
7
9
|
declare function validateEmail(email: string): string | true;
|
|
8
10
|
/**
|
|
9
11
|
* Interactive signup flow: create account + generate API key.
|
|
10
12
|
* Returns the same AuthResult shape as resolveApiKey for seamless integration.
|
|
11
13
|
*/
|
|
12
14
|
export declare function signup(): Promise<AuthResult>;
|
|
13
|
-
|
|
15
|
+
/** @internal Exported for testing only — not part of the public API. */
|
|
16
|
+
export { hintPassword, validateEmail };
|
package/dist/steps/signup.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
const API_BASE = "https://api.uluops.ai/api/v1";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Advisory password hints. Returns warning strings for inquirer display,
|
|
4
|
+
* but all are non-blocking — server validation is the authority.
|
|
5
|
+
* @internal Exported for testing only — not part of the public API.
|
|
5
6
|
*/
|
|
6
|
-
function
|
|
7
|
+
function hintPassword(password) {
|
|
7
8
|
if (password.length < 8)
|
|
8
|
-
|
|
9
|
-
if (password.length > 128)
|
|
10
|
-
|
|
11
|
-
if (!/[a-z]/.test(password))
|
|
12
|
-
|
|
13
|
-
if (!/[A-Z]/.test(password))
|
|
14
|
-
|
|
15
|
-
if (!/[0-9]/.test(password))
|
|
16
|
-
|
|
9
|
+
console.warn(" ⚠ Hint: server may require at least 8 characters");
|
|
10
|
+
else if (password.length > 128)
|
|
11
|
+
console.warn(" ⚠ Hint: server may reject passwords over 128 characters");
|
|
12
|
+
else if (!/[a-z]/.test(password))
|
|
13
|
+
console.warn(" ⚠ Hint: server may require a lowercase letter");
|
|
14
|
+
else if (!/[A-Z]/.test(password))
|
|
15
|
+
console.warn(" ⚠ Hint: server may require an uppercase letter");
|
|
16
|
+
else if (!/[0-9]/.test(password))
|
|
17
|
+
console.warn(" ⚠ Hint: server may require a number");
|
|
17
18
|
return true;
|
|
18
19
|
}
|
|
20
|
+
/** @internal Exported for testing only — not part of the public API. */
|
|
19
21
|
function validateEmail(email) {
|
|
20
22
|
if (!email.trim())
|
|
21
23
|
return "Email is required";
|
|
@@ -36,19 +38,19 @@ export async function signup() {
|
|
|
36
38
|
const pwd = await password({
|
|
37
39
|
message: "Password",
|
|
38
40
|
mask: "*",
|
|
39
|
-
validate:
|
|
41
|
+
validate: hintPassword,
|
|
40
42
|
});
|
|
41
43
|
// Register
|
|
42
|
-
const registerRes = await callApi(`${API_BASE}/auth/register`, "POST", { email, password: pwd });
|
|
44
|
+
const registerRes = await callApi(`${API_BASE}/auth/register`, "POST", { email, password: pwd }, undefined, (res) => !!res.data?.sessionToken && typeof res.data.user?.email === "string");
|
|
43
45
|
const sessionToken = registerRes.data.sessionToken;
|
|
44
46
|
// Create API key using the session
|
|
45
|
-
const keyRes = await callApi(`${API_BASE}/auth/keys`, "POST", { name: "Setup CLI" }, sessionToken);
|
|
47
|
+
const keyRes = await callApi(`${API_BASE}/auth/keys`, "POST", { name: "Setup CLI" }, sessionToken, (res) => !!res.data?.key);
|
|
46
48
|
return {
|
|
47
49
|
apiKey: keyRes.data.key,
|
|
48
|
-
email: registerRes.data.user
|
|
50
|
+
email: registerRes.data.user?.email ?? email,
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
|
-
async function callApi(url, method, body, bearerToken) {
|
|
53
|
+
async function callApi(url, method, body, bearerToken, validate) {
|
|
52
54
|
const headers = {
|
|
53
55
|
"Content-Type": "application/json",
|
|
54
56
|
};
|
|
@@ -71,7 +73,14 @@ async function callApi(url, method, body, bearerToken) {
|
|
|
71
73
|
throw err;
|
|
72
74
|
}
|
|
73
75
|
if (res.ok) {
|
|
74
|
-
|
|
76
|
+
const body = await res.json();
|
|
77
|
+
if (typeof body !== "object" || body === null) {
|
|
78
|
+
throw new Error("Unexpected API response shape");
|
|
79
|
+
}
|
|
80
|
+
if (validate && !validate(body)) {
|
|
81
|
+
throw new Error("API response failed structural validation");
|
|
82
|
+
}
|
|
83
|
+
return body;
|
|
75
84
|
}
|
|
76
85
|
// Handle known error codes
|
|
77
86
|
const errorBody = await res.json().catch(() => null);
|
|
@@ -88,5 +97,5 @@ async function callApi(url, method, body, bearerToken) {
|
|
|
88
97
|
}
|
|
89
98
|
throw new Error(`Signup failed (${res.status}): ${message}`);
|
|
90
99
|
}
|
|
91
|
-
|
|
92
|
-
export {
|
|
100
|
+
/** @internal Exported for testing only — not part of the public API. */
|
|
101
|
+
export { hintPassword, validateEmail };
|
package/dist/steps/verify.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
interface VerifyResult {
|
|
1
|
+
export interface VerifyResult {
|
|
2
2
|
ok: boolean;
|
|
3
3
|
checks: {
|
|
4
4
|
label: string;
|
|
@@ -6,5 +6,5 @@ interface VerifyResult {
|
|
|
6
6
|
detail?: string;
|
|
7
7
|
}[];
|
|
8
8
|
}
|
|
9
|
+
/** Run all verification checks against the current installation and return structured results. */
|
|
9
10
|
export declare function verify(): Promise<VerifyResult>;
|
|
10
|
-
export {};
|