@ze-norm/cli 0.13.0 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +16 -6
- package/dist/auth/device-flow.d.ts.map +1 -1
- package/dist/auth/device-flow.js +11 -0
- package/dist/auth/store.d.ts.map +1 -1
- package/dist/auth/store.js +7 -1
- package/dist/commands/interactive-agent.d.ts +1 -1
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +5 -3
- package/dist/commands/task.d.ts.map +1 -1
- package/dist/commands/task.js +3 -1
- package/dist/commands/work.d.ts +5 -4
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +24 -10
- package/dist/config/environments.d.ts +31 -0
- package/dist/config/environments.d.ts.map +1 -0
- package/dist/config/environments.js +65 -0
- package/dist/zenorm-skills/skills/zenorm/SKILL.md +8 -5
- package/package.json +1 -1
package/dist/api/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AA0BA,kFAAkF;AAClF,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAiCD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAS;IAIxB,OAAO,CAAC,aAAa,CAAgB;gBAEzB,IAAI,CAAC,EAAE,gBAAgB;IAKnC,wEAAwE;YAC1D,mBAAmB;IAMjC,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,cAAc;IAStB,2EAA2E;IAC3E,gBAAgB,IAAI,OAAO;IAI3B,uEAAuE;IACvE,qBAAqB,IAAI,OAAO;IAYhC,OAAO,CAAC,2BAA2B;YAIrB,QAAQ;YAqBR,OAAO;IAqEf,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIjD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIlD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAIzC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;CAG7C"}
|
package/dist/api/client.js
CHANGED
|
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { ApiError, AuthError, UpgradeRequiredError } from "../util/errors.js";
|
|
4
4
|
import { forceRefreshToken, hasRefreshableCredentials, resolveValidToken, } from "../auth/store.js";
|
|
5
5
|
import { PRODUCTION_API_URL } from "../config/defaults.js";
|
|
6
|
+
import { resolveEnvironment, ZENORM_CLI_ENV_VAR } from "../config/environments.js";
|
|
6
7
|
import { getCliVersion } from "../util/package.js";
|
|
7
8
|
import { log } from "../util/logger.js";
|
|
8
9
|
const UPGRADE_COMMAND = "npm install -g @ze-norm/cli@latest";
|
|
@@ -23,11 +24,17 @@ function resolveBaseUrl(explicit) {
|
|
|
23
24
|
// 1. Explicit option
|
|
24
25
|
if (explicit)
|
|
25
26
|
return explicit;
|
|
26
|
-
// 2.
|
|
27
|
+
// 2. Named environment via ZENORM_CLI. A selected environment is an explicit
|
|
28
|
+
// "talk to this stack" intent, so its API URL wins over a stray
|
|
29
|
+
// ZENORM_API_URL / .zenorm.json left over from another workflow.
|
|
30
|
+
if (process.env[ZENORM_CLI_ENV_VAR]) {
|
|
31
|
+
return resolveEnvironment().apiUrl;
|
|
32
|
+
}
|
|
33
|
+
// 3. Environment variable
|
|
27
34
|
const envUrl = process.env["ZENORM_API_URL"];
|
|
28
35
|
if (envUrl)
|
|
29
36
|
return envUrl;
|
|
30
|
-
//
|
|
37
|
+
// 4. .zenorm.json config file in cwd
|
|
31
38
|
const configPath = join(process.cwd(), ".zenorm.json");
|
|
32
39
|
if (existsSync(configPath)) {
|
|
33
40
|
const raw = readFileSync(configPath, "utf-8");
|
|
@@ -35,7 +42,7 @@ function resolveBaseUrl(explicit) {
|
|
|
35
42
|
if (config.apiUrl)
|
|
36
43
|
return config.apiUrl;
|
|
37
44
|
}
|
|
38
|
-
//
|
|
45
|
+
// 5. Public CLI default
|
|
39
46
|
return PRODUCTION_API_URL;
|
|
40
47
|
}
|
|
41
48
|
export class ZenormClient {
|
|
@@ -95,9 +102,12 @@ export class ZenormClient {
|
|
|
95
102
|
}
|
|
96
103
|
/** True when requests will intentionally ignore stored credentials. */
|
|
97
104
|
isUsingLocalDevBypass() {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
// Selecting the `local` environment implies dev-bypass — that is what the
|
|
106
|
+
// local stack is (no real Clerk sign-in, dev-bypass headers). An explicit
|
|
107
|
+
// ZENORM_LOCAL_DEV_BYPASS_AUTH still enables it for a raw localhost URL.
|
|
108
|
+
const bypassEnabled = process.env["ZENORM_LOCAL_DEV_BYPASS_AUTH"] === "true" ||
|
|
109
|
+
resolveEnvironment().name === "local";
|
|
110
|
+
return (this.isLocalBaseUrl() && bypassEnabled && !process.env["ZENORM_API_TOKEN"]);
|
|
101
111
|
}
|
|
102
112
|
shouldBypassAuthForLocalDev() {
|
|
103
113
|
return this.isUsingLocalDevBypass();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device-flow.d.ts","sourceRoot":"","sources":["../../src/auth/device-flow.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"device-flow.d.ts","sourceRoot":"","sources":["../../src/auth/device-flow.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA6UpD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAuBD;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAkDpE"}
|
package/dist/auth/device-flow.js
CHANGED
|
@@ -5,6 +5,7 @@ import { platform } from "node:os";
|
|
|
5
5
|
import { URL, URLSearchParams } from "node:url";
|
|
6
6
|
import { log } from "../util/logger.js";
|
|
7
7
|
import { CLI_CALLBACK_PORTS, PRODUCTION_CLERK_CLIENT_ID, PRODUCTION_CLERK_ISSUER, } from "../config/defaults.js";
|
|
8
|
+
import { resolveEnvironment, ZENORM_CLI_ENV_VAR } from "../config/environments.js";
|
|
8
9
|
/**
|
|
9
10
|
* Find the first pre-registered callback port that is free to bind. Clerk
|
|
10
11
|
* matches redirect_uris exactly, so the local listener must use one of the
|
|
@@ -26,6 +27,11 @@ async function pickFreeCallbackPort() {
|
|
|
26
27
|
`Free one of them and run \`zenorm login\` again.`);
|
|
27
28
|
}
|
|
28
29
|
function getClerkIssuer() {
|
|
30
|
+
// A named environment (ZENORM_CLI) selects the whole Clerk instance, so it
|
|
31
|
+
// wins over a stray ZENORM_CLERK_ISSUER from another workflow.
|
|
32
|
+
if (process.env[ZENORM_CLI_ENV_VAR]) {
|
|
33
|
+
return resolveEnvironment().clerkIssuer;
|
|
34
|
+
}
|
|
29
35
|
return process.env["ZENORM_CLERK_ISSUER"] ?? PRODUCTION_CLERK_ISSUER;
|
|
30
36
|
}
|
|
31
37
|
/** Escape user-influenced text before interpolating into the result page HTML. */
|
|
@@ -38,6 +44,11 @@ function escapeHtml(value) {
|
|
|
38
44
|
.replace(/'/g, "'");
|
|
39
45
|
}
|
|
40
46
|
function getClerkClientId() {
|
|
47
|
+
// A named environment (ZENORM_CLI) selects the whole Clerk instance, so it
|
|
48
|
+
// wins over stray ZENORM_CLERK_CLIENT_ID / VITE_CLERK_PUBLISHABLE_KEY vars.
|
|
49
|
+
if (process.env[ZENORM_CLI_ENV_VAR]) {
|
|
50
|
+
return resolveEnvironment().clerkClientId;
|
|
51
|
+
}
|
|
41
52
|
return (process.env["ZENORM_CLERK_CLIENT_ID"] ??
|
|
42
53
|
process.env["VITE_CLERK_PUBLISHABLE_KEY"] ??
|
|
43
54
|
PRODUCTION_CLERK_CLIENT_ID);
|
package/dist/auth/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/auth/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/auth/store.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAmBD,wBAAgB,eAAe,IAAI,iBAAiB,GAAG,IAAI,CAO1D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAM9D;AAED,wBAAgB,iBAAiB,IAAI,OAAO,CAO3C;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAU5C;AAWD;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwBhE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,OAAO,CAInD;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAezD"}
|
package/dist/auth/store.js
CHANGED
|
@@ -2,12 +2,18 @@ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { refreshAccessToken } from "./device-flow.js";
|
|
5
|
+
import { resolveEnvironment } from "../config/environments.js";
|
|
5
6
|
import { log } from "../util/logger.js";
|
|
6
7
|
// Refresh the access token this many ms before it actually expires, so an
|
|
7
8
|
// in-flight request never races the expiry boundary.
|
|
8
9
|
const EXPIRY_SKEW_MS = 60_000;
|
|
10
|
+
// Credentials are scoped per environment so a prod login and a dev login can
|
|
11
|
+
// coexist instead of overwriting each other. Prod keeps the historical
|
|
12
|
+
// `credentials.json` filename for back-compat; other stacks get a suffix.
|
|
9
13
|
function getCredentialsPath() {
|
|
10
|
-
|
|
14
|
+
const env = resolveEnvironment();
|
|
15
|
+
const filename = env.name === "prod" ? "credentials.json" : `credentials.${env.name}.json`;
|
|
16
|
+
return join(homedir(), ".zenorm", filename);
|
|
11
17
|
}
|
|
12
18
|
function ensureDir() {
|
|
13
19
|
const dir = join(homedir(), ".zenorm");
|
|
@@ -6,7 +6,7 @@ import type { ChildProcess, SpawnOptions } from "node:child_process";
|
|
|
6
6
|
* the daemon does NOT run the agent headlessly. Instead it launches the active
|
|
7
7
|
* coding agent — Claude Code by default, or Codex when the user runs with
|
|
8
8
|
* `--agent codex` — as a *live interactive session*, seeded with the
|
|
9
|
-
*
|
|
9
|
+
* harness-native ZeNorm skill handoff. The agent owns the terminal directly (inherited
|
|
10
10
|
* stdio), so the operator works with it interactively until they close it; when
|
|
11
11
|
* the agent exits, the daemon releases the worker and moves on to the next task.
|
|
12
12
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AAcA,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AAcA,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8F/D"}
|
package/dist/commands/pull.js
CHANGED
|
@@ -39,9 +39,11 @@ export async function pullCommand(argv) {
|
|
|
39
39
|
log.debug("Listing specs to resolve by key", { specRef });
|
|
40
40
|
const { specs } = await client.get("/v1/specs");
|
|
41
41
|
spec = specs.find((s) => s.key === specRef);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
if (!spec) {
|
|
43
|
+
const available = specs.map((s) => s.key).join(", ");
|
|
44
|
+
throw new CliError(`Spec "${specRef}" not found by key or id.` +
|
|
45
|
+
(available ? ` Available spec keys: ${available}` : " The workspace has no specs."));
|
|
46
|
+
}
|
|
45
47
|
}
|
|
46
48
|
// Fetch grilled blocks (goals, constraints, acceptance criteria, tasks).
|
|
47
49
|
let blocksMd = "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/commands/task.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/commands/task.ts"],"names":[],"mappings":"AA2OA,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB/D"}
|
package/dist/commands/task.js
CHANGED
|
@@ -33,7 +33,9 @@ async function resolveSpecId(client, specRef) {
|
|
|
33
33
|
const { specs } = await client.get("/v1/specs");
|
|
34
34
|
const spec = specs.find((s) => s.key === specRef);
|
|
35
35
|
if (!spec) {
|
|
36
|
-
|
|
36
|
+
const available = specs.map((s) => s.key).join(", ");
|
|
37
|
+
throw new CliError(`Spec "${specRef}" not found by key or id.` +
|
|
38
|
+
(available ? ` Available spec keys: ${available}` : " The workspace has no specs."));
|
|
37
39
|
}
|
|
38
40
|
return spec.id;
|
|
39
41
|
}
|
package/dist/commands/work.d.ts
CHANGED
|
@@ -23,8 +23,9 @@ export interface TaskRunner {
|
|
|
23
23
|
* Build the handoff prompt that hands the agent the WHOLE SPEC the claimed task
|
|
24
24
|
* belongs to, so a single agent session implements every available task for that
|
|
25
25
|
* spec with shared context — instead of one fresh session per task. The
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* harness-native ZeNorm invocation (`/zenorm` for Claude Code, `$zenorm`
|
|
27
|
+
* for Codex) triggers the dispatcher in spec mode, where `execute-task` walks
|
|
28
|
+
* every `planned`/`todo` task in order.
|
|
28
29
|
*
|
|
29
30
|
* The claim only flips THIS one task to `active` server-side (the lease that
|
|
30
31
|
* proves there is work for this repo and guards against a second daemon). The
|
|
@@ -35,14 +36,14 @@ export interface TaskRunner {
|
|
|
35
36
|
*
|
|
36
37
|
* Exported for unit testing.
|
|
37
38
|
*/
|
|
38
|
-
export declare function buildHandoffPrompt(task: SpecTaskRecord): string;
|
|
39
|
+
export declare function buildHandoffPrompt(task: SpecTaskRecord, agent: SupportedAgent): string;
|
|
39
40
|
/**
|
|
40
41
|
* The interactive (coding-agent mode) handoff prompt. Same spec-mode handoff as
|
|
41
42
|
* the headless prompt, MINUS the "work non-interactively" instruction: in this
|
|
42
43
|
* mode the agent runs as a live session the operator drives, so it SHOULD pause
|
|
43
44
|
* for input. Exported for unit testing.
|
|
44
45
|
*/
|
|
45
|
-
export declare function buildInteractiveHandoffPrompt(task: SpecTaskRecord): string;
|
|
46
|
+
export declare function buildInteractiveHandoffPrompt(task: SpecTaskRecord, agent: SupportedAgent): string;
|
|
46
47
|
/**
|
|
47
48
|
* The headless subprocess invocation for a given agent + task. Pure (no
|
|
48
49
|
* side effects) so it can be unit-tested directly without spawning anything.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work.d.ts","sourceRoot":"","sources":["../../src/commands/work.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAGV,cAAc,EAEf,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAIL,KAAK,QAAQ,EACd,MAAM,wBAAwB,CAAC;AAehC;;;;GAIG;AACH,QAAA,MAAM,gBAAgB,8BAA+B,CAAC;AACtD,KAAK,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAuCxD;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,GAAG,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAeD
|
|
1
|
+
{"version":3,"file":"work.d.ts","sourceRoot":"","sources":["../../src/commands/work.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/work.js
CHANGED
|
@@ -61,8 +61,9 @@ const AGENT_INSTALL_HINT = {
|
|
|
61
61
|
* Build the handoff prompt that hands the agent the WHOLE SPEC the claimed task
|
|
62
62
|
* belongs to, so a single agent session implements every available task for that
|
|
63
63
|
* spec with shared context — instead of one fresh session per task. The
|
|
64
|
-
*
|
|
65
|
-
*
|
|
64
|
+
* harness-native ZeNorm invocation (`/zenorm` for Claude Code, `$zenorm`
|
|
65
|
+
* for Codex) triggers the dispatcher in spec mode, where `execute-task` walks
|
|
66
|
+
* every `planned`/`todo` task in order.
|
|
66
67
|
*
|
|
67
68
|
* The claim only flips THIS one task to `active` server-side (the lease that
|
|
68
69
|
* proves there is work for this repo and guards against a second daemon). The
|
|
@@ -73,10 +74,10 @@ const AGENT_INSTALL_HINT = {
|
|
|
73
74
|
*
|
|
74
75
|
* Exported for unit testing.
|
|
75
76
|
*/
|
|
76
|
-
export function buildHandoffPrompt(task) {
|
|
77
|
+
export function buildHandoffPrompt(task, agent) {
|
|
77
78
|
const zenormCli = process.env["ZENORM_CLI"]?.trim() || "zenorm";
|
|
78
79
|
return [
|
|
79
|
-
...handoffPromptBody(task, zenormCli),
|
|
80
|
+
...handoffPromptBody(task, zenormCli, agent),
|
|
80
81
|
// Headless runs have no operator to wait on — tell the agent to drive
|
|
81
82
|
// straight through without pausing for input. The interactive variant
|
|
82
83
|
// (buildInteractiveHandoffPrompt) deliberately omits this line.
|
|
@@ -89,14 +90,14 @@ export function buildHandoffPrompt(task) {
|
|
|
89
90
|
* mode the agent runs as a live session the operator drives, so it SHOULD pause
|
|
90
91
|
* for input. Exported for unit testing.
|
|
91
92
|
*/
|
|
92
|
-
export function buildInteractiveHandoffPrompt(task) {
|
|
93
|
+
export function buildInteractiveHandoffPrompt(task, agent) {
|
|
93
94
|
const zenormCli = process.env["ZENORM_CLI"]?.trim() || "zenorm";
|
|
94
|
-
return handoffPromptBody(task, zenormCli).join("\n");
|
|
95
|
+
return handoffPromptBody(task, zenormCli, agent).join("\n");
|
|
95
96
|
}
|
|
96
97
|
/** Shared spec-mode handoff lines for both the headless and interactive prompts. */
|
|
97
|
-
function handoffPromptBody(task, zenormCli) {
|
|
98
|
+
function handoffPromptBody(task, zenormCli, agent) {
|
|
98
99
|
return [
|
|
99
|
-
|
|
100
|
+
buildSkillInvocation(agent, task.specId),
|
|
100
101
|
"",
|
|
101
102
|
`Execute ALL available tasks for ZeNorm spec ${task.specId} in THIS one`,
|
|
102
103
|
"session (spec mode — walk every `planned`/`todo` task in list order). Do not",
|
|
@@ -112,6 +113,19 @@ function handoffPromptBody(task, zenormCli) {
|
|
|
112
113
|
`\`${zenormCli} task complete <task-id>\` flow as you finish it.`,
|
|
113
114
|
];
|
|
114
115
|
}
|
|
116
|
+
/** Build the explicit skill invocation understood by the selected harness. */
|
|
117
|
+
function buildSkillInvocation(agent, specId) {
|
|
118
|
+
switch (agent) {
|
|
119
|
+
case "claude":
|
|
120
|
+
return `/zenorm ${specId}`;
|
|
121
|
+
case "codex":
|
|
122
|
+
return `$zenorm ${specId}`;
|
|
123
|
+
default: {
|
|
124
|
+
const never = agent;
|
|
125
|
+
throw new CliError(`No ZeNorm skill invocation defined for agent "${String(never)}".`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
115
129
|
/**
|
|
116
130
|
* The headless subprocess invocation for a given agent + task. Pure (no
|
|
117
131
|
* side effects) so it can be unit-tested directly without spawning anything.
|
|
@@ -125,7 +139,7 @@ function handoffPromptBody(task, zenormCli) {
|
|
|
125
139
|
* Exported for unit testing.
|
|
126
140
|
*/
|
|
127
141
|
export function buildAgentCommand(agent, task, model) {
|
|
128
|
-
const prompt = buildHandoffPrompt(task);
|
|
142
|
+
const prompt = buildHandoffPrompt(task, agent);
|
|
129
143
|
switch (agent) {
|
|
130
144
|
case "claude":
|
|
131
145
|
// stream-json requires --verbose; it does NOT make the transcript verbose
|
|
@@ -208,7 +222,7 @@ export function buildAgentCommand(agent, task, model) {
|
|
|
208
222
|
* Exported for unit testing.
|
|
209
223
|
*/
|
|
210
224
|
export function buildInteractiveAgentCommand(agent, task, model) {
|
|
211
|
-
const prompt = buildInteractiveHandoffPrompt(task);
|
|
225
|
+
const prompt = buildInteractiveHandoffPrompt(task, agent);
|
|
212
226
|
switch (agent) {
|
|
213
227
|
case "claude":
|
|
214
228
|
return {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A named CLI environment: the full bundle of endpoints the CLI talks to.
|
|
3
|
+
* Selecting one via the `ZENORM_CLI` env var switches API + Clerk together, so
|
|
4
|
+
* you never have to keep `ZENORM_API_URL`, `ZENORM_CLERK_ISSUER`, and
|
|
5
|
+
* `ZENORM_CLERK_CLIENT_ID` in sync by hand.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: only the **prod** preset is baked into this (published) module.
|
|
8
|
+
* The non-prod stacks point at an internal Clerk instance whose identifiers
|
|
9
|
+
* must NOT ship in the public npm tarball (the publish pipeline greps the
|
|
10
|
+
* package for them and blocks the release). Those stacks are launched by the
|
|
11
|
+
* non-shipped source entries (`local.ts` / `dev.ts`, run via their repo shims),
|
|
12
|
+
* which inject `ZENORM_API_URL` + `ZENORM_CLERK_*` before importing the CLI.
|
|
13
|
+
* When `ZENORM_CLI` selects a non-prod stack, this resolver reads those
|
|
14
|
+
* injected vars instead of baking the endpoints in.
|
|
15
|
+
*/
|
|
16
|
+
export interface CliEnvironment {
|
|
17
|
+
readonly name: "prod" | "dev" | "local";
|
|
18
|
+
readonly apiUrl: string;
|
|
19
|
+
readonly clerkIssuer: string;
|
|
20
|
+
readonly clerkClientId: string;
|
|
21
|
+
}
|
|
22
|
+
export declare const ZENORM_CLI_ENV_VAR = "ZENORM_CLI";
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the active CLI environment from `ZENORM_CLI`.
|
|
25
|
+
*
|
|
26
|
+
* Unset → production (the public default). A set-but-unrecognized value is a
|
|
27
|
+
* loud error rather than a silent fallback: a typo like `ZENORM_CLI=prd` must
|
|
28
|
+
* not quietly send `zenorm` commands at the wrong stack.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveEnvironment(): CliEnvironment;
|
|
31
|
+
//# sourceMappingURL=environments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environments.d.ts","sourceRoot":"","sources":["../../src/config/environments.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AASD,eAAO,MAAM,kBAAkB,eAAe,CAAC;AAyC/C;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,cAAc,CAiBnD"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { CliError } from "../util/errors.js";
|
|
2
|
+
import { PRODUCTION_API_URL, PRODUCTION_CLERK_CLIENT_ID, PRODUCTION_CLERK_ISSUER, } from "./defaults.js";
|
|
3
|
+
const PROD_ENVIRONMENT = {
|
|
4
|
+
name: "prod",
|
|
5
|
+
apiUrl: PRODUCTION_API_URL,
|
|
6
|
+
clerkIssuer: PRODUCTION_CLERK_ISSUER,
|
|
7
|
+
clerkClientId: PRODUCTION_CLERK_CLIENT_ID,
|
|
8
|
+
};
|
|
9
|
+
export const ZENORM_CLI_ENV_VAR = "ZENORM_CLI";
|
|
10
|
+
const ENV_NAMES = ["prod", "dev", "local"];
|
|
11
|
+
/**
|
|
12
|
+
* Normalize a raw `ZENORM_CLI` value to a canonical env name. Accepts the bare
|
|
13
|
+
* name (`prod`/`dev`/`local`), `production`, and each name with a `-zenorm`
|
|
14
|
+
* suffix (constructed here so the suffixed literal is never a static token).
|
|
15
|
+
*/
|
|
16
|
+
function parseEnvName(raw) {
|
|
17
|
+
const key = raw.trim().toLowerCase();
|
|
18
|
+
if (key === "production")
|
|
19
|
+
return "prod";
|
|
20
|
+
for (const name of ENV_NAMES) {
|
|
21
|
+
if (key === name || key === `${name}-zenorm`)
|
|
22
|
+
return name;
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build a non-prod environment from the endpoint env vars injected by the
|
|
28
|
+
* repo launchers. Fails loud if they are missing — that means the public
|
|
29
|
+
* `zenorm` was pointed at an internal stack without the launcher that carries
|
|
30
|
+
* its (non-shipped) endpoints, and we must not silently fall back to prod.
|
|
31
|
+
*/
|
|
32
|
+
function environmentFromInjectedVars(name) {
|
|
33
|
+
const apiUrl = process.env["ZENORM_API_URL"];
|
|
34
|
+
const clerkIssuer = process.env["ZENORM_CLERK_ISSUER"];
|
|
35
|
+
const clerkClientId = process.env["ZENORM_CLERK_CLIENT_ID"];
|
|
36
|
+
if (!apiUrl || !clerkIssuer || !clerkClientId) {
|
|
37
|
+
const shim = `${name}${"-zenorm"}`;
|
|
38
|
+
throw new CliError(`${ZENORM_CLI_ENV_VAR}=${name} needs the ${name} endpoints, which are not ` +
|
|
39
|
+
`bundled in the public CLI. Launch it with \`${shim}\` (from the repo), ` +
|
|
40
|
+
`or set ZENORM_API_URL, ZENORM_CLERK_ISSUER, and ZENORM_CLERK_CLIENT_ID.`);
|
|
41
|
+
}
|
|
42
|
+
return { name, apiUrl, clerkIssuer, clerkClientId };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the active CLI environment from `ZENORM_CLI`.
|
|
46
|
+
*
|
|
47
|
+
* Unset → production (the public default). A set-but-unrecognized value is a
|
|
48
|
+
* loud error rather than a silent fallback: a typo like `ZENORM_CLI=prd` must
|
|
49
|
+
* not quietly send `zenorm` commands at the wrong stack.
|
|
50
|
+
*/
|
|
51
|
+
export function resolveEnvironment() {
|
|
52
|
+
const raw = process.env[ZENORM_CLI_ENV_VAR];
|
|
53
|
+
if (raw === undefined || raw.trim() === "") {
|
|
54
|
+
return PROD_ENVIRONMENT;
|
|
55
|
+
}
|
|
56
|
+
const name = parseEnvName(raw);
|
|
57
|
+
if (!name) {
|
|
58
|
+
const allowed = ENV_NAMES.join(", ");
|
|
59
|
+
throw new CliError(`Invalid ${ZENORM_CLI_ENV_VAR}=${JSON.stringify(raw)}. ` +
|
|
60
|
+
`Expected one of: ${allowed} (or a "-zenorm" suffixed form).`);
|
|
61
|
+
}
|
|
62
|
+
if (name === "prod")
|
|
63
|
+
return PROD_ENVIRONMENT;
|
|
64
|
+
return environmentFromInjectedVars(name);
|
|
65
|
+
}
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
name: zenorm
|
|
3
3
|
description: >-
|
|
4
4
|
Dispatcher for explicit ZeNorm spec-driven work. Use when the user invokes
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
the harness-native ZeNorm skill command (`/zenorm ...` in Claude Code,
|
|
6
|
+
`$zenorm ...` in Codex, or `@zenorm ...` in Cursor), or explicitly asks to
|
|
7
|
+
execute, implement, run, continue, or sync a ZeNorm spec or task — e.g.
|
|
8
|
+
"implement spec AUTH-123", "run this ZeNorm spec", "continue spec Z-9",
|
|
9
|
+
"do ZeNorm task UUID", "sync zenorm",
|
|
8
10
|
"wrap up the ZeNorm session". It routes to the right focused skill:
|
|
9
11
|
full-spec execution, single-task execution, or session sync-back, and owns
|
|
10
12
|
the session id and the final outcome flush. Do NOT trigger merely because a
|
|
@@ -20,7 +22,8 @@ child skills rely on: the **session id** and the **single final sync**.
|
|
|
20
22
|
|
|
21
23
|
## Routing
|
|
22
24
|
|
|
23
|
-
`$ARGUMENTS` is everything after
|
|
25
|
+
`$ARGUMENTS` is everything after the harness-native skill invocation
|
|
26
|
+
(`/zenorm`, `$zenorm`, or `@zenorm`). Run the resolver — it strips
|
|
24
27
|
verbs, classifies the target, and resolves spec keys of ANY shape (code,
|
|
25
28
|
slug, or UUID) against the CLI so you do not guess:
|
|
26
29
|
|
|
@@ -42,7 +45,7 @@ for looking unfamiliar. The resolver already checked it against the CLI.
|
|
|
42
45
|
|
|
43
46
|
The `--dispatched` flag tells `execute-task` that *you* (the dispatcher) own
|
|
44
47
|
the final `zenorm-sync`, so it will not run its own. This keeps sync
|
|
45
|
-
single-owner: exactly one sync per
|
|
48
|
+
single-owner: exactly one sync per ZeNorm skill invocation, no double posts. (If
|
|
46
49
|
a user runs `execute-task` directly without going through you, it has no
|
|
47
50
|
`--dispatched` flag and syncs itself — that path stays correct too.)
|
|
48
51
|
|