@clue-ai/cli 0.0.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/README.md +30 -0
- package/bin/clue-tool.mjs +83 -0
- package/commands/claude-code/clue-init.md +27 -0
- package/commands/codex/clue-init.md +27 -0
- package/package.json +20 -0
- package/src/command-spec.mjs +73 -0
- package/src/contracts.mjs +124 -0
- package/src/fastapi-analyzer.mjs +340 -0
- package/src/init-tool.mjs +86 -0
- package/src/lifecycle-init.mjs +206 -0
- package/src/path-policy.mjs +75 -0
- package/src/public-schema.cjs +784 -0
- package/src/semantic-ci.mjs +1830 -0
- package/src/setup-tool.mjs +141 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import readline from "node:readline/promises";
|
|
4
|
+
|
|
5
|
+
const SKILL_NAMES = [
|
|
6
|
+
"clue-route-semantic-snapshot",
|
|
7
|
+
"clue-semantic-ci",
|
|
8
|
+
"clue-sdk-instrumentation",
|
|
9
|
+
"clue-setup-audit",
|
|
10
|
+
"clue-local-verification",
|
|
11
|
+
"clue-setup-report",
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const TARGETS = new Set(["codex", "claude_code"]);
|
|
15
|
+
|
|
16
|
+
const TARGET_SKILL_ROOTS = {
|
|
17
|
+
codex: [".agents", "skills"],
|
|
18
|
+
claude_code: [".claude", "skills"],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const normalizeTarget = (target) => {
|
|
22
|
+
const normalized = String(target ?? "").trim().toLowerCase().replace(/[\s-]+/g, "_");
|
|
23
|
+
if (!TARGETS.has(normalized)) {
|
|
24
|
+
throw new Error("target must be codex or claude_code");
|
|
25
|
+
}
|
|
26
|
+
return normalized;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const skillBody = (name) => {
|
|
30
|
+
const descriptions = {
|
|
31
|
+
"clue-route-semantic-snapshot":
|
|
32
|
+
"Use when analyzing backend routes, handlers, controllers, or endpoints to create privacy-safe Clue semantic snapshots.",
|
|
33
|
+
"clue-semantic-ci":
|
|
34
|
+
"Use when adding or updating Clue semantic snapshot CI for this repository.",
|
|
35
|
+
"clue-sdk-instrumentation":
|
|
36
|
+
"Use when adding ClueInit, ClueIdentify, ClueSetAccount, and ClueLogout lifecycle calls to a customer repository.",
|
|
37
|
+
"clue-setup-audit":
|
|
38
|
+
"Use when reviewing Clue setup changes for missing lifecycle calls, unsafe instrumentation, leaked secrets, or bad insertion points.",
|
|
39
|
+
"clue-local-verification":
|
|
40
|
+
"Use when verifying local Clue setup artifacts before checking event delivery in the Clue setup screen.",
|
|
41
|
+
"clue-setup-report":
|
|
42
|
+
"Use when producing the final Clue setup report with changed files, blockers, env names, and next steps.",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const steps = {
|
|
46
|
+
"clue-route-semantic-snapshot": [
|
|
47
|
+
"Inspect only allowed source paths.",
|
|
48
|
+
"Identify route, handler, controller, and endpoint behavior from privacy-safe evidence.",
|
|
49
|
+
"Create semantic snapshot inputs without raw source, secrets, prompts, or completions.",
|
|
50
|
+
"Report unresolved routes as blockers instead of guessing.",
|
|
51
|
+
],
|
|
52
|
+
"clue-semantic-ci": [
|
|
53
|
+
"Create or update `.github/workflows/clue-semantic-snapshot.yml`.",
|
|
54
|
+
"Use `npx @clue-ai/cli semantic-ci --request .clue/semantic-request.runtime.json --repo .`.",
|
|
55
|
+
"Reference GitHub Secrets and Variables by name only.",
|
|
56
|
+
"Do not print or commit secret values.",
|
|
57
|
+
],
|
|
58
|
+
"clue-sdk-instrumentation": [
|
|
59
|
+
"Find the app/bootstrap entrypoint and add ClueInit only when the location is clear.",
|
|
60
|
+
"Find login success handling and add ClueIdentify only when the user identity is available.",
|
|
61
|
+
"Find account, workspace, organization, or tenant resolution and add ClueSetAccount only when the subject is available.",
|
|
62
|
+
"Find logout/sign-out completion and add ClueLogout only when the session reset point is clear.",
|
|
63
|
+
"Skip unclear lifecycle points and report blockers.",
|
|
64
|
+
],
|
|
65
|
+
"clue-setup-audit": [
|
|
66
|
+
"Review changed files line by line.",
|
|
67
|
+
"Reject broad ClueTrack instrumentation and DOM clue tags.",
|
|
68
|
+
"Confirm no project key, API key, secret, or env value appears in diff or report.",
|
|
69
|
+
"Confirm lifecycle insertions are minimal and reviewable.",
|
|
70
|
+
],
|
|
71
|
+
"clue-local-verification": [
|
|
72
|
+
"Confirm generated skill files exist.",
|
|
73
|
+
"Confirm workflow files and SDK lifecycle imports/calls exist when those phases have run.",
|
|
74
|
+
"Confirm only env names are reported.",
|
|
75
|
+
"Leave event delivery verification to the Clue setup screen.",
|
|
76
|
+
],
|
|
77
|
+
"clue-setup-report": [
|
|
78
|
+
"Summarize changed files.",
|
|
79
|
+
"List completed setup phases.",
|
|
80
|
+
"List blockers with exact file or environment names when available.",
|
|
81
|
+
"List required env names without values.",
|
|
82
|
+
"State that commit and push were not performed.",
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return [
|
|
87
|
+
"---",
|
|
88
|
+
`name: ${name}`,
|
|
89
|
+
`description: ${descriptions[name]}`,
|
|
90
|
+
"---",
|
|
91
|
+
"",
|
|
92
|
+
"# Rules",
|
|
93
|
+
"",
|
|
94
|
+
"- Do not expose project keys, API keys, secrets, tokens, or environment variable values.",
|
|
95
|
+
"- Do not commit or push changes.",
|
|
96
|
+
"- Prefer existing repository patterns and minimal diffs.",
|
|
97
|
+
"- If evidence is unclear, report a blocker instead of guessing.",
|
|
98
|
+
"",
|
|
99
|
+
"# Workflow",
|
|
100
|
+
"",
|
|
101
|
+
...steps[name].map((step, index) => `${index + 1}. ${step}`),
|
|
102
|
+
"",
|
|
103
|
+
].join("\n");
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const askTarget = async ({ input = process.stdin, output = process.stdout } = {}) => {
|
|
107
|
+
const rl = readline.createInterface({ input, output });
|
|
108
|
+
try {
|
|
109
|
+
const answer = await rl.question("Use Clue setup skills for which AI tool? [codex/claude_code] ");
|
|
110
|
+
return normalizeTarget(answer);
|
|
111
|
+
} finally {
|
|
112
|
+
rl.close();
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const installSetupSkills = async ({
|
|
117
|
+
repoRoot,
|
|
118
|
+
target,
|
|
119
|
+
input,
|
|
120
|
+
output,
|
|
121
|
+
} = {}) => {
|
|
122
|
+
const resolvedTarget = target ? normalizeTarget(target) : await askTarget({ input, output });
|
|
123
|
+
const resolvedRepoRoot = resolve(repoRoot ?? ".");
|
|
124
|
+
const skillRoot = join(resolvedRepoRoot, ...TARGET_SKILL_ROOTS[resolvedTarget]);
|
|
125
|
+
const installed = [];
|
|
126
|
+
|
|
127
|
+
for (const skillName of SKILL_NAMES) {
|
|
128
|
+
const skillDir = join(skillRoot, skillName);
|
|
129
|
+
const skillPath = join(skillDir, "SKILL.md");
|
|
130
|
+
await mkdir(skillDir, { recursive: true });
|
|
131
|
+
await writeFile(skillPath, skillBody(skillName), "utf8");
|
|
132
|
+
installed.push(skillPath);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
target: resolvedTarget,
|
|
137
|
+
skill_root: join(...TARGET_SKILL_ROOTS[resolvedTarget]),
|
|
138
|
+
skills: SKILL_NAMES,
|
|
139
|
+
installed_files: installed.map((path) => path.replace(`${resolvedRepoRoot}/`, "")),
|
|
140
|
+
};
|
|
141
|
+
};
|