@jentrix/cli 0.5.13 → 0.5.15
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/claude-plugin/commands/jentrix-align.md +14 -6
- package/claude-plugin/commands/jentrix-checkpoint.md +1 -0
- package/claude-plugin/commands/jentrix-connect.md +1 -0
- package/claude-plugin/commands/jentrix-end.md +1 -0
- package/claude-plugin/commands/jentrix-plan.md +1 -0
- package/claude-plugin/commands/jentrix-review.md +1 -0
- package/claude-plugin/commands/jentrix-status.md +1 -0
- package/codex-plugin/plugins/jentrix/skills/jentrix-align/SKILL.md +8 -2
- package/codex-plugin/plugins/jentrix/skills/jentrix-checkpoint/SKILL.md +1 -0
- package/codex-plugin/plugins/jentrix/skills/jentrix-connect/SKILL.md +1 -0
- package/codex-plugin/plugins/jentrix/skills/jentrix-end/SKILL.md +1 -0
- package/codex-plugin/plugins/jentrix/skills/jentrix-plan/SKILL.md +1 -0
- package/codex-plugin/plugins/jentrix/skills/jentrix-review/SKILL.md +1 -0
- package/codex-plugin/plugins/jentrix/skills/jentrix-status/SKILL.md +1 -0
- package/dist/main.js +86 -42
- package/package.json +1 -1
- package/surface.json +0 -242
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
description: Align this session to a Jentrix work layer (workspace → project → task → owner) with a server-confirmed alignment snapshot
|
|
3
3
|
allowed-tools: Bash(jentrix align:*), Bash(jentrix session:*)
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
Run the Jentrix MVP alignment wizard for THIS session. The question set is
|
|
7
8
|
fixed — workspace, project, project name, work item, task title, owner,
|
|
@@ -28,12 +29,19 @@ and do NOT answer any question yourself.
|
|
|
28
29
|
choice is the explicit free-text escape: when picked, let the operator
|
|
29
30
|
type the id/slug. A question whose `status` is `"skipped"` was resolved
|
|
30
31
|
by detection — tell the operator what was resolved (e.g. the workspace),
|
|
31
|
-
don't re-ask it.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
don't re-ask it. When a question carries more choices than the native
|
|
33
|
+
picker can show (the work-item question can carry up to 15 tasks plus its
|
|
34
|
+
escapes), put the overflow choices — label AND id — in the prompt text and
|
|
35
|
+
say they are reachable through "Other…". Never silently drop a choice.
|
|
36
|
+
5. Re-run `--questions --json` after every answer that unlocks the next
|
|
37
|
+
question, carrying the answers so far as flags. After the WORKSPACE
|
|
38
|
+
answer: `jentrix align --questions --json --workspace <chosen>` — it
|
|
39
|
+
narrows the project list to that workspace, and without it the project
|
|
40
|
+
question stays `pending` and the flow stalls. After the PROJECT answer:
|
|
41
|
+
`jentrix align --questions --json [--workspace <chosen>] --project <chosen>`
|
|
42
|
+
loads the work-item and owner choices, then continue asking. If the
|
|
43
|
+
operator picked "Create a new project from this repo", the NEXT question
|
|
44
|
+
is `project_name` (already in the payload, `kind: "freetext"`): relay its
|
|
37
45
|
`prompt` verbatim and collect the name as free text FROM THE OPERATOR —
|
|
38
46
|
never propose name options, never fill it in yourself. Then re-run
|
|
39
47
|
`jentrix align --questions --json --new-project "<name>"` — the work-item
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
description: Write a distilled state-of-play for this session and push it as a typed Jentrix artifact
|
|
3
3
|
allowed-tools: Bash(jentrix push:*), Bash(jentrix session:*), Bash(jentrix task:*)
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
Write down what this session currently knows, and push it.
|
|
7
8
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
description: Connect this Claude Code session to a Jentrix project (session anchor + capture)
|
|
3
3
|
allowed-tools: Bash(jentrix session:*)
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
Run this exact command with the Bash tool and relay its output to the user:
|
|
7
8
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
description: End the connected Jentrix session (pushes the final artifacts, verifies capture, stores the RUN_SUMMARY)
|
|
3
3
|
allowed-tools: Bash(jentrix session:*), Bash(jentrix push:*), Bash(jentrix artifact:*), Bash(jentrix column:*), Bash(jentrix task:*), Bash(jentrix tool:*)
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
End the connected Jentrix session honestly. For an ALIGNED session (the MVP
|
|
7
8
|
pipeline), the typed artifacts ARE the record — push them before closing:
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
description: Turn the opening prompt into a goal and a small set of Jentrix tasks, confirmed by the operator before anything is created
|
|
3
3
|
allowed-tools: Bash(jentrix task:*), Bash(jentrix column:*), Bash(jentrix board:*), Bash(jentrix session:*), Bash(jentrix push:*)
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
Decompose the work the operator just asked for into a goal and a short task
|
|
7
8
|
list, **confirm both before creating anything**, then create them.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
description: Review a Jentrix session's typed artifacts by category (input → plan → decision → output → issue → gap → learning → record), check the readiness contract, and post the verdict as a task comment
|
|
3
3
|
allowed-tools: Bash(jentrix tool:*), Bash(jentrix artifact:*), Bash(jentrix session:*), Bash(jentrix comment:*)
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
Run the category walk over one session's record (docs: Reviewing a session).
|
|
7
8
|
Read-only until the final comment; never modify the tree, the board, or the
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
description: Show the connected Jentrix session for this work (capture health included)
|
|
3
3
|
allowed-tools: Bash(jentrix session:*)
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
Run `jentrix session status` with the Bash tool (append the session id if the
|
|
7
8
|
user gave one) and relay the output verbatim, including any capture-pending
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: stacks-align
|
|
3
3
|
description: Align the current Codex task to a Jentrix workspace, project, work item, and owner with an operator-confirmed server snapshot.
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
# Align this Codex task
|
|
7
8
|
|
|
@@ -12,8 +13,13 @@ description: Align the current Codex task to a Jentrix workspace, project, work
|
|
|
12
13
|
4. Present each `status:"ask"` question and its choices verbatim using Codex's
|
|
13
14
|
structured input UI when available, otherwise ask in chat. Never answer for the
|
|
14
15
|
operator. Preserve notices and the `Other…` free-text escape.
|
|
15
|
-
5. Re-run `--questions --json` after
|
|
16
|
-
|
|
16
|
+
5. Re-run `--questions --json` after every answer that unlocks the next question,
|
|
17
|
+
carrying the answers so far: `--workspace <chosen>` after the workspace (it
|
|
18
|
+
narrows the project list — without it the project question stays `pending`),
|
|
19
|
+
then `--project <chosen>`. Collect `project_name` and `task_title` as free text
|
|
20
|
+
from the operator; never propose permanent names. When the choices outnumber
|
|
21
|
+
the front end's picker, name the overflow (label + id) in the prompt rather
|
|
22
|
+
than dropping it.
|
|
17
23
|
6. Present the confirmation prompt and every disclosure verbatim.
|
|
18
24
|
7. On confirmation, submit exactly once with `jentrix align --provider codex --json
|
|
19
25
|
--yes` plus the confirmed project, task/session-level, owner, agent label, and
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: stacks-checkpoint
|
|
3
3
|
description: Distill verified session state into a typed Jentrix checkpoint that survives compaction or handoff.
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
# Write a checkpoint
|
|
7
8
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: stacks-connect
|
|
3
3
|
description: Connect the current Codex task to a Jentrix project using trusted lifecycle identity and start the session capture host.
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
# Connect this Codex task
|
|
7
8
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: stacks-end
|
|
3
3
|
description: End a connected Jentrix session after preserving approved outputs, parking completed work in review, and reporting capture honestly.
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
# End the connected session
|
|
7
8
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: stacks-plan
|
|
3
3
|
description: Turn the opening request into an operator-approved goal and small Jentrix task set, then store the prompt and goal as typed artifacts.
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
# Plan confirmed work
|
|
7
8
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: stacks-review
|
|
3
3
|
description: Review a Jentrix session's typed artifacts by category, verify the readiness contract, and post the verdict as a comment on the aligned task.
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
# Review a session's record
|
|
7
8
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: stacks-status
|
|
3
3
|
description: Show the connected Jentrix session, alignment, capture health, and honest telemetry availability for this Codex task.
|
|
4
4
|
---
|
|
5
|
+
<!-- Source of truth. The cli/{claude,codex}-plugin copy is GENERATED by cli/scripts/build.mjs (rm + cp) on every build — edits made there are deleted. Edit this file. -->
|
|
5
6
|
|
|
6
7
|
# Show session status
|
|
7
8
|
|
package/dist/main.js
CHANGED
|
@@ -10336,7 +10336,7 @@ import {
|
|
|
10336
10336
|
readFileSync as readFileSync7,
|
|
10337
10337
|
statSync as statSync3
|
|
10338
10338
|
} from "node:fs";
|
|
10339
|
-
import { homedir } from "node:os";
|
|
10339
|
+
import { homedir as homedir2 } from "node:os";
|
|
10340
10340
|
import { basename as basename2, delimiter as delimiter2, dirname as dirname4, join as join6, resolve as resolve4 } from "node:path";
|
|
10341
10341
|
import { fileURLToPath } from "node:url";
|
|
10342
10342
|
|
|
@@ -20593,8 +20593,8 @@ var ConfigError = class extends Error {
|
|
|
20593
20593
|
this.exitCode = exitCode;
|
|
20594
20594
|
}
|
|
20595
20595
|
};
|
|
20596
|
-
function configPathFor(
|
|
20597
|
-
return join(
|
|
20596
|
+
function configPathFor(homedir3) {
|
|
20597
|
+
return join(homedir3, ".config", "stacks", "config.json");
|
|
20598
20598
|
}
|
|
20599
20599
|
var PROJECT_CONFIG_DIR = ".stacks";
|
|
20600
20600
|
function projectConfigPathFor(dir) {
|
|
@@ -20610,8 +20610,8 @@ function findProjectConfigPath(startDir, exists = existsSync) {
|
|
|
20610
20610
|
dir = parent;
|
|
20611
20611
|
}
|
|
20612
20612
|
}
|
|
20613
|
-
function resolveConfigPath(cwd,
|
|
20614
|
-
return findProjectConfigPath(cwd, exists) ?? configPathFor(
|
|
20613
|
+
function resolveConfigPath(cwd, homedir3, exists = existsSync) {
|
|
20614
|
+
return findProjectConfigPath(cwd, exists) ?? configPathFor(homedir3);
|
|
20615
20615
|
}
|
|
20616
20616
|
function scaffoldProjectConfigDir(dir) {
|
|
20617
20617
|
const stacksDir = join(dir, PROJECT_CONFIG_DIR);
|
|
@@ -20882,7 +20882,10 @@ var ACCESS_TOKEN_PREFIX = "tmo_";
|
|
|
20882
20882
|
var REFRESH_TOKEN_PREFIX = "tmr_";
|
|
20883
20883
|
function parseTokenResponse(body) {
|
|
20884
20884
|
if (typeof body !== "object" || body === null) {
|
|
20885
|
-
throw new OAuthTokenError(
|
|
20885
|
+
throw new OAuthTokenError(
|
|
20886
|
+
"invalid_response",
|
|
20887
|
+
"token response is not an object"
|
|
20888
|
+
);
|
|
20886
20889
|
}
|
|
20887
20890
|
const json = body;
|
|
20888
20891
|
if (typeof json.error === "string") {
|
|
@@ -20962,7 +20965,7 @@ function refreshAccessToken(input) {
|
|
|
20962
20965
|
|
|
20963
20966
|
// src/client.ts
|
|
20964
20967
|
var CLI_NAME = "stacks-cli";
|
|
20965
|
-
var CLI_VERSION = "0.5.
|
|
20968
|
+
var CLI_VERSION = "0.5.15";
|
|
20966
20969
|
var DEAD_TOKEN_MESSAGE = "authentication failed (HTTP 401): the token is dead or expired \u2014 mint a new PAT at /account/tokens on your Jentrix server and update STACKS_TOKEN (or --token / the config file).";
|
|
20967
20970
|
var RELOGIN_MESSAGE = "OAuth session expired and could not be refreshed \u2014 run `jentrix login` to sign in again.";
|
|
20968
20971
|
function originOf2(url2) {
|
|
@@ -23840,7 +23843,7 @@ function producerFromFlags(flags) {
|
|
|
23840
23843
|
if (!label) return null;
|
|
23841
23844
|
return { label, emoji: flags.agentEmoji?.trim() || null };
|
|
23842
23845
|
}
|
|
23843
|
-
async function buildCatalog(caller,
|
|
23846
|
+
async function buildCatalog(caller, flags, detection) {
|
|
23844
23847
|
const context = await callStructured(caller, "get_token_context", {});
|
|
23845
23848
|
let pinnedWorkspace = null;
|
|
23846
23849
|
if (context.workspacePinned === true && typeof context.workspaceId === "string") {
|
|
@@ -23851,10 +23854,13 @@ async function buildCatalog(caller, deps2, flags, detection) {
|
|
|
23851
23854
|
name: match?.name ?? "pinned"
|
|
23852
23855
|
};
|
|
23853
23856
|
}
|
|
23854
|
-
const
|
|
23857
|
+
const flagWorkspaceId = flags.workspace?.trim() ? await resolveWorkspaceFlag(
|
|
23855
23858
|
caller,
|
|
23856
|
-
|
|
23857
|
-
|
|
23859
|
+
flags.workspace.trim(),
|
|
23860
|
+
pinnedWorkspace
|
|
23861
|
+
) : null;
|
|
23862
|
+
const { candidates: matched, pinned: discoveryPinned } = await resolveCandidates(caller, detection.repoOwnerName);
|
|
23863
|
+
const candidates = flagWorkspaceId ? matched.filter((candidate) => candidate.workspace.id === flagWorkspaceId) : matched;
|
|
23858
23864
|
let otherProjects = [];
|
|
23859
23865
|
let discoveryNotice = null;
|
|
23860
23866
|
if (discoveryPinned && pinnedWorkspace) {
|
|
@@ -23879,7 +23885,7 @@ async function buildCatalog(caller, deps2, flags, detection) {
|
|
|
23879
23885
|
(project) => project.id === flags.project || project.slug === flags.project
|
|
23880
23886
|
);
|
|
23881
23887
|
chosenProjectId = match?.id ?? flags.project;
|
|
23882
|
-
} else if (candidates.length === 1) {
|
|
23888
|
+
} else if (candidates.length === 1 && matched.length === 1) {
|
|
23883
23889
|
chosenProjectId = candidates[0].id;
|
|
23884
23890
|
}
|
|
23885
23891
|
let openTasks = null;
|
|
@@ -23926,7 +23932,7 @@ async function buildCatalog(caller, deps2, flags, detection) {
|
|
|
23926
23932
|
} catch {
|
|
23927
23933
|
}
|
|
23928
23934
|
}
|
|
23929
|
-
const chosenWorkspaceId = candidates.find((candidate) => candidate.id === chosenProjectId)?.workspace.id ?? pinnedWorkspace?.id ?? null;
|
|
23935
|
+
const chosenWorkspaceId = candidates.find((candidate) => candidate.id === chosenProjectId)?.workspace.id ?? flagWorkspaceId ?? pinnedWorkspace?.id ?? null;
|
|
23930
23936
|
return {
|
|
23931
23937
|
detection,
|
|
23932
23938
|
pinnedWorkspace,
|
|
@@ -24305,7 +24311,7 @@ async function runAlign(flags, deps2) {
|
|
|
24305
24311
|
provider: detected.provider
|
|
24306
24312
|
};
|
|
24307
24313
|
return await withCaller(deps2, async (caller, target) => {
|
|
24308
|
-
const catalog = await buildCatalog(caller,
|
|
24314
|
+
const catalog = await buildCatalog(caller, flags, detection);
|
|
24309
24315
|
const marker = readAlignmentMarker(
|
|
24310
24316
|
deps2.configPath,
|
|
24311
24317
|
inspection.root,
|
|
@@ -24427,14 +24433,7 @@ async function runAlign(flags, deps2) {
|
|
|
24427
24433
|
let pendingCreateProject = null;
|
|
24428
24434
|
if (flags.newTask) workItem = `__new__:${flags.newTask}`;
|
|
24429
24435
|
if (flags.newProject && flags.newProject.trim()) {
|
|
24430
|
-
|
|
24431
|
-
if (flags.workspace?.trim()) {
|
|
24432
|
-
workspaceId = await resolveWorkspaceFlag(
|
|
24433
|
-
caller,
|
|
24434
|
-
flags.workspace.trim(),
|
|
24435
|
-
catalog.pinnedWorkspace ?? null
|
|
24436
|
-
);
|
|
24437
|
-
}
|
|
24436
|
+
const workspaceId = catalog.chosenWorkspaceId ?? catalog.pinnedWorkspace?.id ?? null;
|
|
24438
24437
|
if (!workspaceId) {
|
|
24439
24438
|
throw new UsageError(
|
|
24440
24439
|
"--new-project needs a resolved workspace (a workspace-pinned token, a repo-matched candidate, or --workspace <id-or-slug>) \u2014 none resolved here"
|
|
@@ -24766,7 +24765,7 @@ function registerAlignCommand(program3, deps2, onExit2) {
|
|
|
24766
24765
|
"create a project in the resolved workspace (seeds a board + repo/board links), then align to it"
|
|
24767
24766
|
).option(
|
|
24768
24767
|
"--workspace <id-or-slug>",
|
|
24769
|
-
"workspace
|
|
24768
|
+
"the workspace question's flag twin: narrows the wizard's project list, and names where --new-project creates when neither a pinned token nor a repo match resolves one"
|
|
24770
24769
|
).option("--task <taskId>", "existing work item to align to").option(
|
|
24771
24770
|
"--new-task <title>",
|
|
24772
24771
|
"create a task (existing ops core) and align to it"
|
|
@@ -25104,7 +25103,11 @@ async function offerCardMint(kind, payload, flags, deps2, sessionId = null) {
|
|
|
25104
25103
|
board,
|
|
25105
25104
|
artifactId: payload.artifactId,
|
|
25106
25105
|
anchorTaskId: payload.taskId,
|
|
25107
|
-
noun: mintNoun(
|
|
25106
|
+
noun: mintNoun(
|
|
25107
|
+
ARTIFACT_TYPE_BY_PUSH_KIND[kind] ?? "FINDINGS"
|
|
25108
|
+
),
|
|
25109
|
+
// STA-128: the operator's own claim title, when one was given.
|
|
25110
|
+
artifactTitle: flags.title?.trim() || null,
|
|
25108
25111
|
blocks: Boolean(flags.blocks),
|
|
25109
25112
|
sessionId
|
|
25110
25113
|
});
|
|
@@ -25128,7 +25131,12 @@ async function findMintBoard(deps2, taskId) {
|
|
|
25128
25131
|
const boards = (await callStructured(caller, "list_boards", { workspaceId })).boards.filter((b) => b.kind === "BUGS" && !b.archivedAt);
|
|
25129
25132
|
const candidates = [
|
|
25130
25133
|
...boards,
|
|
25131
|
-
...task.boardId ? [
|
|
25134
|
+
...task.boardId ? [
|
|
25135
|
+
{
|
|
25136
|
+
id: String(task.boardId),
|
|
25137
|
+
name: String(task.boardName ?? "board")
|
|
25138
|
+
}
|
|
25139
|
+
] : []
|
|
25132
25140
|
];
|
|
25133
25141
|
for (const board of candidates) {
|
|
25134
25142
|
const columns = (await callStructured(caller, "list_columns", { boardId: board.id })).columns;
|
|
@@ -25147,9 +25155,11 @@ async function mintCard(deps2, input) {
|
|
|
25147
25155
|
const anchor = await callStructured(caller, "get_task", {
|
|
25148
25156
|
taskId: input.anchorTaskId
|
|
25149
25157
|
});
|
|
25158
|
+
const claim = input.artifactTitle?.trim();
|
|
25150
25159
|
const created = await callStructured(caller, "create_task", {
|
|
25151
25160
|
columnId: input.board.columnId,
|
|
25152
|
-
title
|
|
25161
|
+
// The task-title cap is 200 chars; the claim form truncates into it.
|
|
25162
|
+
title: (claim ? `${input.noun}: ${claim}` : `${input.noun} from ${String(anchor.key ?? input.anchorTaskId)}`).slice(0, 200),
|
|
25153
25163
|
description: `Minted from a ${input.noun.toLowerCase()} artifact pushed on ${String(anchor.key ?? input.anchorTaskId)}. Origin session artifact: ${input.artifactId}.`,
|
|
25154
25164
|
idempotencyKey: `mint-${input.artifactId}`
|
|
25155
25165
|
});
|
|
@@ -25225,6 +25235,8 @@ async function runMintIssue(flags, deps2) {
|
|
|
25225
25235
|
artifactId: flags.artifact,
|
|
25226
25236
|
anchorTaskId: flags.fromTask,
|
|
25227
25237
|
noun: mintNoun(type),
|
|
25238
|
+
// STA-128: the stored artifact's claim title rides onto the card.
|
|
25239
|
+
artifactTitle: typeof record3.title === "string" ? record3.title : null,
|
|
25228
25240
|
blocks: Boolean(flags.blocks),
|
|
25229
25241
|
sessionId
|
|
25230
25242
|
});
|
|
@@ -25254,7 +25266,10 @@ async function runMintIssue(flags, deps2) {
|
|
|
25254
25266
|
function registerMintIssueCommand(artifact, deps2, onExit2) {
|
|
25255
25267
|
artifact.command("mint-issue").description(
|
|
25256
25268
|
"Mint a card from a findings/gap/issue artifact: opens a card on the workspace's BUGS board (else the task's own board), links the artifact to it, and relates it back to the source task."
|
|
25257
|
-
).requiredOption(
|
|
25269
|
+
).requiredOption(
|
|
25270
|
+
"--artifact <id>",
|
|
25271
|
+
"the findings/gap/issue artifact to link"
|
|
25272
|
+
).requiredOption(
|
|
25258
25273
|
"--from-task <id>",
|
|
25259
25274
|
"the task the artifact was pushed on (the card RELATES_TO it)"
|
|
25260
25275
|
).option(
|
|
@@ -25332,7 +25347,9 @@ function statusToExit(status) {
|
|
|
25332
25347
|
}
|
|
25333
25348
|
async function runArtifactAttach(filePath, flags, deps2) {
|
|
25334
25349
|
if (!flags.workspace && !flags.task) {
|
|
25335
|
-
deps2.writeErr(
|
|
25350
|
+
deps2.writeErr(
|
|
25351
|
+
"--workspace <id> is required (or --task <id> to resolve it)"
|
|
25352
|
+
);
|
|
25336
25353
|
return EXIT_CODES.INVALID_INPUT;
|
|
25337
25354
|
}
|
|
25338
25355
|
if (flags.kind && !PUSH_KINDS.includes(flags.kind)) {
|
|
@@ -25452,7 +25469,12 @@ async function runArtifactAttach(filePath, flags, deps2) {
|
|
|
25452
25469
|
});
|
|
25453
25470
|
const artifact = result.artifact ?? {};
|
|
25454
25471
|
deps2.writeOut(
|
|
25455
|
-
flags.json ?
|
|
25472
|
+
flags.json ? (
|
|
25473
|
+
// The id at the top level: every other artifact-producing command
|
|
25474
|
+
// answers `artifactId`, and a caller should not have to know which
|
|
25475
|
+
// envelope this one came in.
|
|
25476
|
+
JSON.stringify({ artifactId: artifact.id ?? null, ...result })
|
|
25477
|
+
) : `Attached artifact ${String(artifact.id ?? "?")} \xB7 ${String(artifact.title ?? filename)} (${type}, ${body.byteLength} bytes)`
|
|
25456
25478
|
);
|
|
25457
25479
|
return 0;
|
|
25458
25480
|
} finally {
|
|
@@ -25477,7 +25499,10 @@ function registerArtifactCommand(program3, deps2, onExit2) {
|
|
|
25477
25499
|
).option("--project <id>", "link to a project").option("--task <id>", "link to a task").option("--run <id>", "link to a run").option("--work-order <id>", "link to a Work Order").option("--decision <id>", "link to a decision").option(
|
|
25478
25500
|
"--kind <kind>",
|
|
25479
25501
|
`push kind (${PUSH_KINDS.join("|")}) \u2014 files under the same ArtifactType, hence the same work layer, as \`jentrix push\``
|
|
25480
|
-
).option("--session <id>", "attribute the artifact to a connected session").option(
|
|
25502
|
+
).option("--session <id>", "attribute the artifact to a connected session").option(
|
|
25503
|
+
"--type <type>",
|
|
25504
|
+
"ArtifactType (default inferred from --kind, else the extension, else DOC)"
|
|
25505
|
+
).option("--title <title>", "artifact title (default: the file name)").option("--json", "stable JSON output").action(
|
|
25481
25506
|
async (file, flags) => onExit2(await runArtifactAttach(file, flags, deps2))
|
|
25482
25507
|
);
|
|
25483
25508
|
return artifact;
|
|
@@ -26779,7 +26804,8 @@ function mountCommandTree(program3, plan, runtime) {
|
|
|
26779
26804
|
var WHOAMI_SCOPE_NOTE = "scope/workspace restrictions are enforced server-side and not introspectable; a FORBIDDEN (exit 3) on a write means the token lacks write/admin scope.";
|
|
26780
26805
|
function redactToken(token) {
|
|
26781
26806
|
const hint = token.length > 8 ? `\u2026${token.slice(-4)}` : "\u2026";
|
|
26782
|
-
if (token.startsWith("tmo_"))
|
|
26807
|
+
if (token.startsWith("tmo_"))
|
|
26808
|
+
return { type: "oauth", display: `tmo_${hint}` };
|
|
26783
26809
|
if (token.startsWith("tm_")) return { type: "pat", display: `tm_${hint}` };
|
|
26784
26810
|
return { type: "unknown", display: hint };
|
|
26785
26811
|
}
|
|
@@ -26868,7 +26894,8 @@ async function runWhoamiCommand(flags, deps2) {
|
|
|
26868
26894
|
const scrub = (text) => {
|
|
26869
26895
|
let out = text;
|
|
26870
26896
|
for (const secret of secrets) {
|
|
26871
|
-
if (out.includes(secret))
|
|
26897
|
+
if (out.includes(secret))
|
|
26898
|
+
out = out.split(secret).join("<redacted token>");
|
|
26872
26899
|
}
|
|
26873
26900
|
return out;
|
|
26874
26901
|
};
|
|
@@ -26963,7 +26990,9 @@ async function runWhoamiCommand(flags, deps2) {
|
|
|
26963
26990
|
note: WHOAMI_SCOPE_NOTE
|
|
26964
26991
|
};
|
|
26965
26992
|
deps2.writeOut(
|
|
26966
|
-
redact(
|
|
26993
|
+
redact(
|
|
26994
|
+
flags.json === true ? stableStringify(report) : renderHuman(report)
|
|
26995
|
+
)
|
|
26967
26996
|
);
|
|
26968
26997
|
return EXIT_CODES.OK;
|
|
26969
26998
|
} finally {
|
|
@@ -27622,6 +27651,7 @@ function registerPluginCommand(program3, deps2, onExit2) {
|
|
|
27622
27651
|
// src/commands/runner.ts
|
|
27623
27652
|
import { execFile as execFile2, spawn as spawn2 } from "node:child_process";
|
|
27624
27653
|
import { constants, promises as fs } from "node:fs";
|
|
27654
|
+
import { homedir } from "node:os";
|
|
27625
27655
|
import { delimiter, isAbsolute, resolve as resolve3 } from "node:path";
|
|
27626
27656
|
var CLI_RUNNER_PROTOCOL_VERSION = 1;
|
|
27627
27657
|
var RUNNER_TIMEOUT_MS = 35e3;
|
|
@@ -27690,6 +27720,16 @@ async function resolveRunnerExecutable(env = process.env) {
|
|
|
27690
27720
|
}
|
|
27691
27721
|
return null;
|
|
27692
27722
|
}
|
|
27723
|
+
async function resolveClaudeExecutable(env = process.env, home = homedir()) {
|
|
27724
|
+
const names = platformExecutableNames("claude");
|
|
27725
|
+
const onPath = await resolveExecutableOnPath(names, env);
|
|
27726
|
+
if (onPath) return onPath;
|
|
27727
|
+
for (const name of names) {
|
|
27728
|
+
const found = await executable(resolve3(home, ".local", "bin", name));
|
|
27729
|
+
if (found) return found;
|
|
27730
|
+
}
|
|
27731
|
+
return null;
|
|
27732
|
+
}
|
|
27693
27733
|
function invokeRunnerProcess(file, args, stdin, timeoutMs = RUNNER_TIMEOUT_MS) {
|
|
27694
27734
|
if (!isAbsolute(file)) {
|
|
27695
27735
|
return Promise.reject(new Error("runner executable path must be absolute"));
|
|
@@ -28213,8 +28253,9 @@ var RUNTIMES = [
|
|
|
28213
28253
|
provider: "claude",
|
|
28214
28254
|
resolve: (deps2) => deps2.resolveClaude(),
|
|
28215
28255
|
absent: [
|
|
28216
|
-
"note: the `claude` CLI
|
|
28217
|
-
"
|
|
28256
|
+
"note: the `claude` CLI was not found \u2014 skipped its plugin.",
|
|
28257
|
+
" Installed already? This terminal's PATH may predate the install \u2014 reopen it and re-run, or run: jentrix plugin install claude",
|
|
28258
|
+
" Otherwise install Claude Code (https://claude.com/claude-code) first."
|
|
28218
28259
|
]
|
|
28219
28260
|
},
|
|
28220
28261
|
{
|
|
@@ -28732,7 +28773,8 @@ function loadSurface(json) {
|
|
|
28732
28773
|
const seen = /* @__PURE__ */ new Set();
|
|
28733
28774
|
const validated = tools.map((tool, index) => {
|
|
28734
28775
|
const where = (field) => isRecord9(tool) && typeof tool.name === "string" ? `tool "${tool.name}": ${field}` : `tools[${index}]: ${field}`;
|
|
28735
|
-
if (!isRecord9(tool))
|
|
28776
|
+
if (!isRecord9(tool))
|
|
28777
|
+
throw new SurfaceError(`tools[${index}] is not an object`);
|
|
28736
28778
|
if (typeof tool.name !== "string" || tool.name.length === 0) {
|
|
28737
28779
|
throw new SurfaceError(where("missing or empty `name`"));
|
|
28738
28780
|
}
|
|
@@ -28813,8 +28855,8 @@ function readStdin() {
|
|
|
28813
28855
|
process.stdin.on("error", reject);
|
|
28814
28856
|
});
|
|
28815
28857
|
}
|
|
28816
|
-
var CONFIG_PATH = resolveConfigPath(process.cwd(),
|
|
28817
|
-
var HOME_CONFIG_PATH = configPathFor(
|
|
28858
|
+
var CONFIG_PATH = resolveConfigPath(process.cwd(), homedir2());
|
|
28859
|
+
var HOME_CONFIG_PATH = configPathFor(homedir2());
|
|
28818
28860
|
function configFile() {
|
|
28819
28861
|
return readConfigFile(CONFIG_PATH);
|
|
28820
28862
|
}
|
|
@@ -29033,7 +29075,7 @@ var sessionDeps = {
|
|
|
29033
29075
|
closeSync3(fd);
|
|
29034
29076
|
}
|
|
29035
29077
|
},
|
|
29036
|
-
spoolRoot: join6(
|
|
29078
|
+
spoolRoot: join6(homedir2(), ".config", "stacks", "session-spool")
|
|
29037
29079
|
};
|
|
29038
29080
|
var sessionCommand = registerSessionCommand(program2, sessionDeps, onExit);
|
|
29039
29081
|
registerSnapshotCommand(sessionCommand, sessionDeps, onExit);
|
|
@@ -29047,7 +29089,9 @@ var CODEX_PLUGIN_DIR = fileURLToPath(
|
|
|
29047
29089
|
var pluginDeps = {
|
|
29048
29090
|
resolvePluginDir: () => existsSync6(PLUGIN_DIR) && isPluginMarketplaceDir(PLUGIN_DIR) ? PLUGIN_DIR : null,
|
|
29049
29091
|
resolveCodexPluginDir: () => existsSync6(CODEX_PLUGIN_DIR) && isCodexPluginMarketplaceDir(CODEX_PLUGIN_DIR) ? CODEX_PLUGIN_DIR : null,
|
|
29050
|
-
|
|
29092
|
+
// STA-133: PATH plus the native installer's ~/.local/bin, so a terminal
|
|
29093
|
+
// whose PATH predates the Claude Code install still finds the binary.
|
|
29094
|
+
resolveClaude: () => resolveClaudeExecutable(process.env),
|
|
29051
29095
|
resolveCodex: () => resolveExecutableOnPath(platformExecutableNames("codex"), process.env),
|
|
29052
29096
|
resolveRunner: () => resolveRunnerExecutable(process.env),
|
|
29053
29097
|
resolveNpm: () => resolveExecutableOnPath(platformExecutableNames("npm"), process.env),
|
|
@@ -29158,7 +29202,7 @@ registerSetupCommand(
|
|
|
29158
29202
|
}
|
|
29159
29203
|
},
|
|
29160
29204
|
login: (flags) => runLoginCommand(flags, loginDeps),
|
|
29161
|
-
codexConfigPath: () => join6(process.env.CODEX_HOME ?? join6(
|
|
29205
|
+
codexConfigPath: () => join6(process.env.CODEX_HOME ?? join6(homedir2(), ".codex"), "config.toml"),
|
|
29162
29206
|
readTextFile: (path) => existsSync6(path) ? readFileSync7(path, "utf8") : null,
|
|
29163
29207
|
appendTextFile: (path, text) => {
|
|
29164
29208
|
mkdirSync4(dirname4(path), { recursive: true });
|
|
@@ -29167,7 +29211,7 @@ registerSetupCommand(
|
|
|
29167
29211
|
copyFile: (from, to) => copyFileSync(from, to),
|
|
29168
29212
|
now: () => /* @__PURE__ */ new Date(),
|
|
29169
29213
|
cwd: () => process.cwd(),
|
|
29170
|
-
homeDir: () =>
|
|
29214
|
+
homeDir: () => homedir2(),
|
|
29171
29215
|
git: defaultGitRunner,
|
|
29172
29216
|
readLine
|
|
29173
29217
|
},
|