@abelxiaoxing/cadence 1.0.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.
@@ -0,0 +1,42 @@
1
+ ---
2
+ description: Initialize OpenSpec and AGENTS indexes for an Abel workflow project
3
+ argument-hint: "[project-path]"
4
+ ---
5
+
6
+ Load the bundled `abel-workflow` Skill before inspecting or modifying OpenSpec or AGENTS files.
7
+ If that core Skill is unavailable, stop before any write and report an executable package-resource restore or reinstall command.
8
+ Read the complete value inside `<abel-request>` without a second whitespace-tokenization pass.
9
+
10
+ <abel-request>
11
+ $ARGUMENTS
12
+ </abel-request>
13
+
14
+ The request is an optional project path; when absent, target the current working directory.
15
+ Resolve that root before action and preserve unrelated files, all human-authored AGENTS content, baseline dirty state, and nested repository boundaries.
16
+ Never overwrite or cross a nested repository boundary merely because it is below the selected root.
17
+
18
+ ## Prerequisites
19
+
20
+ Select Bun when it is usable; otherwise select npm.
21
+ Use that single JavaScript toolchain for the whole run.
22
+ If neither is usable, stop with the original probe errors and executable remediation.
23
+
24
+ Discover the bundled `context7-auto-research` and `grok-search` Skills.
25
+ Record each resolved discovery path when present.
26
+ When either is missing, report an actionable package-resource restore or reinstall command, continue OpenSpec initialization and AGENTS repair, and mark the final environment as not fully ready.
27
+ A missing research Skill does not prevent OpenSpec initialization or AGENTS repair.
28
+ The absence of `git-commit`, external `dev-browser`, or a dedicated `time` Skill does not affect Init readiness; do not check them as Init prerequisites.
29
+
30
+ Probe OpenSpec and all CLI capabilities required by the shared workflow.
31
+ If OpenSpec is absent, install `@fission-ai/openspec@latest` globally with the selected toolchain (`bun add --global @fission-ai/openspec@latest` or `npm install --global @fission-ai/openspec@latest`), then recheck every required capability.
32
+ If installation or the recheck fails, stop, preserve the original error, and report the exact executable remediation command for the selected toolchain.
33
+
34
+ ## Initialization
35
+
36
+ Initialize or safely update OpenSpec without `--force` or another destructive force option.
37
+ Resolve and validate the selected schema and its templates before reporting success.
38
+ Create or repair only the verified managed blocks of appropriate root and nested AGENTS indexes, preserving all human-authored text and unrelated content.
39
+ You must not edit `openspec/AGENTS.md`.
40
+
41
+ Report the resolved project root, selected toolchain, OpenSpec capability results, schema/template validation, AGENTS repairs, research-Skill discovery paths or remediation, and final readiness.
42
+ A missing research Skill makes the environment not fully ready even when OpenSpec and AGENTS repair completed.
@@ -0,0 +1,89 @@
1
+ const RETRYABLE_STATUS = new Set([408, 429, 500, 502, 503, 504]);
2
+ const DEFAULT_RETRY_DELAYS_MS = [1000, 2000];
3
+ const MAX_RETRY_DELAY_MS = 10_000;
4
+ const DEFAULT_TIMEOUT_MS = 30_000;
5
+ const MAX_ATTEMPTS = 3;
6
+
7
+ const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8
+
9
+ function retryAfterMs(value, now = Date.now()) {
10
+ if (!value) return null;
11
+ if (/^\d+(?:\.\d+)?$/.test(value.trim())) {
12
+ return Math.min(Math.max(Number(value) * 1000, 0), MAX_RETRY_DELAY_MS);
13
+ }
14
+ const date = Date.parse(value);
15
+ if (Number.isNaN(date)) return null;
16
+ return Math.min(Math.max(date - now, 0), MAX_RETRY_DELAY_MS);
17
+ }
18
+
19
+ function sanitizedError({ operation, url, status, cause }) {
20
+ const endpoint = (() => {
21
+ try {
22
+ const parsed = new URL(url);
23
+ return `${parsed.origin}${parsed.pathname}`;
24
+ } catch {
25
+ return "invalid endpoint";
26
+ }
27
+ })();
28
+ const statusText = status ? ` (HTTP ${status})` : "";
29
+ const causeText = cause instanceof SyntaxError ? ": malformed JSON" : "";
30
+ return new Error(
31
+ `${operation} failed at ${endpoint}${statusText}${causeText}`,
32
+ );
33
+ }
34
+
35
+ export async function requestJson({
36
+ operation,
37
+ url,
38
+ method = "GET",
39
+ headers = {},
40
+ body,
41
+ fetchImpl = fetch,
42
+ sleep = defaultSleep,
43
+ timeoutMs = DEFAULT_TIMEOUT_MS,
44
+ }) {
45
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {
46
+ const controller = new AbortController();
47
+ const timeout = setTimeout(
48
+ () => controller.abort(new Error("request timeout")),
49
+ timeoutMs,
50
+ );
51
+ let response;
52
+ try {
53
+ response = await fetchImpl(url, {
54
+ method,
55
+ headers,
56
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
57
+ signal: controller.signal,
58
+ });
59
+ } catch (cause) {
60
+ clearTimeout(timeout);
61
+ if (attempt + 1 >= MAX_ATTEMPTS) {
62
+ throw sanitizedError({ operation, url, cause });
63
+ }
64
+ await sleep(DEFAULT_RETRY_DELAYS_MS[attempt]);
65
+ continue;
66
+ }
67
+ clearTimeout(timeout);
68
+
69
+ if (!response.ok) {
70
+ if (
71
+ !RETRYABLE_STATUS.has(response.status) ||
72
+ attempt + 1 >= MAX_ATTEMPTS
73
+ ) {
74
+ throw sanitizedError({ operation, url, status: response.status });
75
+ }
76
+ const retryAfter = retryAfterMs(response.headers.get("retry-after"));
77
+ await sleep(retryAfter ?? DEFAULT_RETRY_DELAYS_MS[attempt]);
78
+ continue;
79
+ }
80
+
81
+ try {
82
+ return await response.json();
83
+ } catch (cause) {
84
+ throw sanitizedError({ operation, url, status: response.status, cause });
85
+ }
86
+ }
87
+
88
+ throw sanitizedError({ operation, url });
89
+ }
@@ -0,0 +1,64 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+
4
+ export function parseEnvFile(content) {
5
+ const values = {};
6
+ const lines = content
7
+ .replaceAll("\r\n", "\n")
8
+ .replaceAll("\r", "\n")
9
+ .split("\n");
10
+
11
+ for (let index = 0; index < lines.length; index += 1) {
12
+ const line = lines[index].trim();
13
+ if (!line || line.startsWith("#")) continue;
14
+
15
+ const separator = line.indexOf("=");
16
+ if (separator <= 0)
17
+ throw new Error(`Invalid configuration syntax at line ${index + 1}`);
18
+
19
+ const name = line.slice(0, separator).trim();
20
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
21
+ throw new Error(`Invalid configuration name at line ${index + 1}`);
22
+ }
23
+
24
+ let value = line.slice(separator + 1).trim();
25
+ const quote = value[0];
26
+ if (quote === '"' || quote === "'") {
27
+ if (value.at(-1) !== quote) {
28
+ throw new Error(
29
+ `Unterminated quoted configuration value at line ${index + 1}`,
30
+ );
31
+ }
32
+ value = value.slice(1, -1);
33
+ }
34
+ values[name] = value;
35
+ }
36
+
37
+ return values;
38
+ }
39
+
40
+ export function loadConfig({ cwd, home, required = [] }) {
41
+ const projectPath = path.join(cwd, ".pi", "cadence", ".env");
42
+ const userPath = path.join(home, ".pi", "agent", "cadence", ".env");
43
+ const selectedPath = existsSync(projectPath)
44
+ ? projectPath
45
+ : existsSync(userPath)
46
+ ? userPath
47
+ : null;
48
+
49
+ if (!selectedPath) {
50
+ throw new Error(
51
+ `No cadence configuration file found; create ${projectPath} or ${userPath}`,
52
+ );
53
+ }
54
+
55
+ const values = parseEnvFile(readFileSync(selectedPath, "utf8"));
56
+ const missing = required.filter((name) => !values[name]);
57
+ if (missing.length > 0) {
58
+ throw new Error(
59
+ `Missing required configuration names in ${selectedPath}: ${missing.join(", ")}`,
60
+ );
61
+ }
62
+
63
+ return { path: selectedPath, values };
64
+ }
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: abel-workflow
3
+ description: Shared contract for the Abel Init, Design, Implement, and Diagnose workflow stages
4
+ ---
5
+
6
+ # Abel workflow
7
+
8
+ This Skill is the authoritative shared contract for `/abel-init`, `/abel-design`, `/abel-implement`, and `/abel-diagnose`.
9
+ Load it before executing any stage; the package remains usable when no user-global `AGENTS.md` contains Abel-specific instructions.
10
+ Stage prompts own their input contract and stage-specific procedure, while rules shared across stages live here.
11
+
12
+ ## Gates and trusted delivery
13
+
14
+ Gate A approves the complete product behavior contract.
15
+ Gate B separately approves the complete technical implementation contract.
16
+ Neither Gate authorizes a tool permission; they do not grant permission authorization, identity signing, or approval to bypass repository controls.
17
+
18
+ Design and Implement may run in different contexts.
19
+ A handoff is trusted only when versioned Gate receipts bind the same change and schema, every covered artifact's normalized relative path and SHA-256 hash is valid, the Gate A receipt hash is valid, OpenSpec strict validation passes, and the artifact graph is complete.
20
+ Normalize only the tracked task file's completed Markdown checkboxes before hashing; reject absolute paths, `..`, path escapes, and symbolic-link escapes.
21
+ A missing receipt, invalid hash, or inconsistent artifact returns the change to Design rather than recreating a decision in Implement.
22
+
23
+ Every approved behavior must retain a stable trace from Requirement to Scenario to Verification to Task.
24
+ Every task must state exactly one executable verification type, its Red command and expected target failure, Green behavior, affected suite, target files, and AGENTS impact.
25
+
26
+ ## Verification discipline
27
+
28
+ Before writes, record target, affected-suite, and full-suite baselines with commands, exit codes, and normalized failure identities.
29
+ A pre-existing baseline failure is separate evidence and never satisfies a target Red.
30
+
31
+ Use Red-Green-Refactor for code, tests, and executable static contracts:
32
+
33
+ 1. **Red:** add only the approved failing verification and run the exact command; it must fail for the specified target defect.
34
+ 2. **Green:** implement the minimum approved change and run the target verification after every code or test edit.
35
+ 3. **Refactor:** improve only in-scope structure or readability while target verification stays green; then run the affected suite.
36
+
37
+ If Red passes, uses an invalid command, or fails for another reason, stop and return to Design rather than substituting an improvised verification.
38
+
39
+ ### Implement affected-suite repair boundary
40
+
41
+ This boundary applies only to Implement.
42
+ For each task, its exact affected-suite commands define a dynamic cross-module repair boundary.
43
+ Keep each command, exit code, normalized failure identity, reproducibility result, attribution, and root-cause evidence in the implementation context and final report, not in a state file.
44
+ Every reproducible pre-existing failure enters the current task separately from the task Red and requires a verified root cause plus minimum repair, including a failure outside the original module or target-file list.
45
+ Only an attributed failure inside this boundary authorizes an additional repair path.
46
+ Classify a failure newly visible on a later affected run as `pre-existing`, `introduced`, or `unresolved`; include and minimally repair a pre-existing failure, repair or revert an introduced failure, and block completion when attribution is unresolved.
47
+ An environmental, transient, external-service-dependent, or non-reproducible failure blocks completion with an executable recovery condition and never authorizes a speculative product edit or repair.
48
+ If an included repair requires new behavior, policy, dependency, architecture, irreversibility, or another substantive decision, return the expanded requirement to Design.
49
+ A full-suite-only baseline failure outside the affected commands remains outside automatic scope.
50
+ Finish only when all target and affected verifications are green and the full suite has no new failure relative to baseline.
51
+ Do not archive, publish, or commit implicitly.
52
+
53
+ ## AGENTS indexes
54
+
55
+ Treat AGENTS indexes as verified routers, not architecture documents or session ledgers.
56
+ Preserve all human-authored text and edit only the managed region delimited by `<!-- ABEL:AGENTS-INDEX:START -->` and `<!-- ABEL:AGENTS-INDEX:END -->`.
57
+ Update an index only at a stable task or diagnosis checkpoint when module routes, entrypoints, public/configuration surfaces, commands, ownership, data-flow boundaries, or indexed invariants changed.
58
+ Otherwise record `none` with evidence.
59
+ Never persist runtime user or session state, dirty-state ledgers, timestamps, or approval status in an index.
60
+
61
+ Before an index write, inspect the complete task diff and compare actual impact with the approved contract.
62
+ Stop and return to Design if the diff exposes unapproved behavior or architecture.
63
+ Validate paths, commands, removed references, managed-marker uniqueness, and root-to-nested routes after every index update.
64
+
65
+ ## Parent and subagent authority
66
+
67
+ The parent agent owns Gate handling, patches, repository writes, AGENTS index updates, and task completion state.
68
+ A subagent receives bounded relevant index context and may perform only the delegated read-only exploration or review.
69
+ A subagent must not approve a Gate, apply a patch, edit an index, or advance a task checkbox.
70
+
71
+ ## External browser E2E
72
+
73
+ `dev-browser` is external and is required only when an approved task verification contract explicitly names browser E2E.
74
+ Its absence does not block another task or workflow stage whose approved contract has no browser E2E step.
75
+ When an approved browser-E2E task requires `dev-browser` and it is missing or unavailable, stop that task, report the missing capability and an executable remediation, and do not report the verification as passing.
76
+
77
+ ## Stage responsibilities
78
+
79
+ - **Init:** initialize or safely repair OpenSpec and verified AGENTS routes without destructive overwrite.
80
+ - **Design:** resolve blocking decisions, obtain Gate A and Gate B, write only OpenSpec change artifacts after Gate A, and deliver a strictly validated traceable change.
81
+ - **Implement:** validate the trusted delivery in a fresh context, execute task contracts in tracked order through Red-Green-Refactor, and maintain indexes at stable checkpoints.
82
+ - **Diagnose:** reproduce existing bugs, falsify candidate causes, establish a failing regression, and make the minimum repair; new behavior or substantive architecture returns to Design.
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: context7-auto-research
3
+ description: Fetch current official library documentation from Context7 when library or framework APIs are needed
4
+ ---
5
+
6
+ # Context7 auto research
7
+
8
+ Use this Skill proactively for current library/framework documentation, version-specific APIs, setup, migration, or code examples.
9
+ Run its CLI from this Skill directory.
10
+
11
+ ## Commands
12
+
13
+ ```bash
14
+ node context7.mjs search <library-name> <query|->
15
+ node context7.mjs context <library-id> <query|->
16
+ ```
17
+
18
+ Use a lone `-` to pass the complete UTF-8 query through stdin without shell tokenization:
19
+
20
+ ```bash
21
+ node context7.mjs search react - <<'QUERY'
22
+ <complete user question>
23
+ QUERY
24
+ ```
25
+
26
+ Choose an exact official library match first, then version fit and trust score.
27
+ Fetch focused context for the selected library ID and answer from the returned current documentation.
28
+ Treat a non-zero exit, invalid shape, or empty useful result as a failed lookup; never present it as successful research.
29
+
30
+ The CLI selects the project `.pi/cadence/.env` as a whole file when present, otherwise `~/.pi/agent/cadence/.env`.
31
+ It does not merge files, read ambient API configuration, or read `.env` beside the installed package.
32
+ `CONTEXT7_API_KEY` may be empty; `CONTEXT7_API_URL` defaults to `https://context7.com/api/v2`.
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { homedir } from "node:os";
4
+ import path from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+ import { requestJson } from "../_shared/http-client.mjs";
7
+ import { loadConfig } from "../_shared/load-config.mjs";
8
+
9
+ const DEFAULT_API_URL = "https://context7.com/api/v2";
10
+
11
+ export function loadContext7Config({ cwd, home }) {
12
+ const selected = loadConfig({ cwd, home });
13
+ return {
14
+ path: selected.path,
15
+ apiUrl: selected.values.CONTEXT7_API_URL || DEFAULT_API_URL,
16
+ apiKey: selected.values.CONTEXT7_API_KEY ?? "",
17
+ };
18
+ }
19
+
20
+ async function resolveQuery(queryArgs, readStdin) {
21
+ if (queryArgs.length === 1 && queryArgs[0] === "-") {
22
+ const query = (await readStdin()).trim();
23
+ if (!query) throw new Error("Stdin query input was empty");
24
+ return query;
25
+ }
26
+ if (queryArgs.includes("-")) {
27
+ throw new Error("When using -, it must be the only query argument");
28
+ }
29
+ const query = queryArgs.join(" ").trim();
30
+ if (!query) throw new Error("A non-empty query is required");
31
+ return query;
32
+ }
33
+
34
+ function buildHeaders(apiKey) {
35
+ return {
36
+ Accept: "application/json",
37
+ "User-Agent": "cadence-context7/0.0.0",
38
+ ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
39
+ };
40
+ }
41
+
42
+ function validateResult(command, value) {
43
+ if (command === "search" && value && Array.isArray(value.results))
44
+ return value;
45
+ if (command === "context") {
46
+ if (Array.isArray(value)) return value;
47
+ if (
48
+ value &&
49
+ typeof value === "object" &&
50
+ (Array.isArray(value.codeSnippets) || Array.isArray(value.infoSnippets))
51
+ ) {
52
+ return value;
53
+ }
54
+ }
55
+ throw new Error(`Invalid Context7 ${command} response shape`);
56
+ }
57
+
58
+ export async function runContext7({
59
+ argv,
60
+ cwd,
61
+ home,
62
+ fetchImpl = fetch,
63
+ sleep,
64
+ readStdin,
65
+ }) {
66
+ const [command, operand, ...queryArgs] = argv;
67
+ if (command !== "search" && command !== "context") {
68
+ throw new Error("Unknown command; expected search or context");
69
+ }
70
+ if (!operand) {
71
+ throw new Error(
72
+ `${command} requires a library ${command === "search" ? "name" : "id"}`,
73
+ );
74
+ }
75
+
76
+ const query = await resolveQuery(queryArgs, readStdin);
77
+ const config = loadContext7Config({ cwd, home });
78
+ const base = config.apiUrl.replace(/\/+$/, "");
79
+ const endpoint = new URL(
80
+ `${base}/${command === "search" ? "libs/search" : "context"}`,
81
+ );
82
+ endpoint.searchParams.set(
83
+ command === "search" ? "libraryName" : "libraryId",
84
+ operand,
85
+ );
86
+ endpoint.searchParams.set("query", query);
87
+ if (command === "context") endpoint.searchParams.set("type", "json");
88
+
89
+ const result = await requestJson({
90
+ operation: `Context7 ${command}`,
91
+ url: endpoint,
92
+ headers: buildHeaders(config.apiKey),
93
+ fetchImpl,
94
+ sleep,
95
+ });
96
+ return validateResult(command, result);
97
+ }
98
+
99
+ async function readAllStdin() {
100
+ process.stdin.setEncoding("utf8");
101
+ let content = "";
102
+ for await (const chunk of process.stdin) content += chunk;
103
+ return content;
104
+ }
105
+
106
+ function printUsage() {
107
+ process.stderr.write(
108
+ "Usage:\n node context7.mjs search <library-name> <query|->\n node context7.mjs context <library-id> <query|->\n",
109
+ );
110
+ }
111
+
112
+ const isMain =
113
+ process.argv[1] &&
114
+ import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href;
115
+
116
+ if (isMain) {
117
+ try {
118
+ const result = await runContext7({
119
+ argv: process.argv.slice(2),
120
+ cwd: process.cwd(),
121
+ home: homedir(),
122
+ readStdin: readAllStdin,
123
+ });
124
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
125
+ } catch (error) {
126
+ printUsage();
127
+ process.stderr.write(
128
+ `${error instanceof Error ? error.message : "Context7 failed"}\n`,
129
+ );
130
+ process.exitCode = 1;
131
+ }
132
+ }
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: git-commit
3
+ description: Create one repository-compliant commit only after an explicit user commit request and deterministic safety audit
4
+ ---
5
+
6
+ # Git commit
7
+
8
+ Activate this Skill only when the user explicitly asks to commit or invokes this Skill.
9
+ Workflow completion does not authorize a commit, Gate approval never authorizes a commit, and an installed Skill does not authorize or execute a commit by itself.
10
+
11
+ ## Safety audit
12
+
13
+ After an explicit request:
14
+
15
+ 1. Read the repository root and all relevant nested `AGENTS.md` files.
16
+ 2. Inspect `git status --short`, `git diff`, and `git diff --staged` so staged and unstaged changes are both visible.
17
+ 3. Exclude every `.env` file, credential, private key, user or session state, workflow state, and obviously unrelated path.
18
+ 4. Review deletions and renames explicitly; never infer that an ambiguous deletion is safe.
19
+ 5. Decide whether the remaining safe relevant paths form exactly one logical commit.
20
+
21
+ If changes contain multiple logical groups, unrelated dirty files, ambiguous deletions, unsafe files, or no uniquely safe group, leave the index and history unchanged and ask the user which logical group to commit.
22
+
23
+ ## One safe logical commit
24
+
25
+ When the audit identifies one unambiguous logical commit:
26
+
27
+ 1. Stage only the explicit path list with `git add -- <paths>`.
28
+ 2. Reinspect `git diff --staged` and confirm it contains only that path list and no prohibited content.
29
+ 3. Derive a repository-compliant Conventional Commit message from the actual staged diff.
30
+ 4. Execute one normal commit and report its received output.
31
+
32
+ ## Prohibited operations
33
+
34
+ Never use `git add .`, `git add -A`, `git add -p`, `--no-verify`, `--amend`, reset, force push, or `git config` mutation.
35
+ Never bypass hooks, rewrite history, discard work, or broaden the explicit path list.
36
+
37
+ If a commit hook fails, report the failure as received, preserve the staged state for inspection, and do not bypass the hook.
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: grok-search
3
+ description: Search and retrieve current web information through a Grok-compatible gateway and optional Tavily operations
4
+ ---
5
+
6
+ # Grok search
7
+
8
+ Use this Skill for current web search, page retrieval, site maps, and source-backed fact checking.
9
+ Run the dependency-free Node.js CLI from this Skill directory.
10
+
11
+ ## Commands
12
+
13
+ ```bash
14
+ node grok-search.mjs web_search --query <text> \
15
+ [--platform <name>] [--min-results <n>] [--max-results <n>] \
16
+ [--model <id>] [--extra-sources <n>]
17
+
18
+ node grok-search.mjs web_fetch --url <url> [--out <path>]
19
+
20
+ node grok-search.mjs web_map --url <url> \
21
+ [--instructions <text>] [--max-depth <n>] \
22
+ [--max-breadth <n>] [--limit <n>]
23
+
24
+ node grok-search.mjs get_config_info
25
+ ```
26
+
27
+ Start with `web_search`, use `web_fetch` for a selected page, and use `web_map` for site structure.
28
+ Cite source URLs and relevant dates for time-sensitive answers.
29
+ Treat non-zero exits and invalid result shapes as failed research.
30
+
31
+ `web_search` returns normalized `{title,url,description}` objects.
32
+ `--extra-sources` explicitly requires enabled Tavily and fails rather than returning partial provider results.
33
+ `web_fetch` uses Tavily Markdown extraction when enabled, emits a sanitized warning on extract failure, then falls back to Grok.
34
+ `web_map` requires enabled Tavily.
35
+ `get_config_info` prints only the selected path, provider URLs, model, and enabled/configured booleans; it does not test the network or print keys.
36
+
37
+ The CLI selects the project `.pi/cadence/.env` as a whole file when present, otherwise `~/.pi/agent/cadence/.env`.
38
+ `GROK_API_URL` and `GROK_API_KEY` are required.
39
+ `GROK_MODEL` defaults to `grok-4.20-non-reasoning`, `TAVILY_API_URL` defaults to `https://api.tavily.com`, and Tavily is enabled by default once a non-empty `TAVILY_API_KEY` is configured; set `TAVILY_ENABLED=false` to opt out.