ultracode-for-codex 0.2.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/README.md +182 -0
- package/ULTRACODE_INSTALL.md +133 -0
- package/dist/cli.d.ts +24 -0
- package/dist/cli.js +616 -0
- package/dist/codex/env.d.ts +2 -0
- package/dist/codex/env.js +45 -0
- package/dist/codex/subagent-backend.d.ts +72 -0
- package/dist/codex/subagent-backend.js +685 -0
- package/dist/runtime/async-queue.d.ts +10 -0
- package/dist/runtime/async-queue.js +55 -0
- package/dist/runtime/types.d.ts +61 -0
- package/dist/runtime/types.js +18 -0
- package/dist/runtime/workflow-journal.d.ts +135 -0
- package/dist/runtime/workflow-journal.js +681 -0
- package/dist/runtime/workflow-runtime.d.ts +266 -0
- package/dist/runtime/workflow-runtime.js +3280 -0
- package/dist/settings.d.ts +38 -0
- package/dist/settings.js +153 -0
- package/dist/ultracode-install-guide.d.ts +4 -0
- package/dist/ultracode-install-guide.js +22 -0
- package/docs/ultracode-p3a-journal-design.md +78 -0
- package/docs/ultracode-p3b-resume-cache.md +43 -0
- package/docs/ultracode-p3c-worktree-isolation.md +60 -0
- package/package.json +77 -0
- package/postinstall.mjs +23 -0
- package/settings.json +20 -0
- package/skills/ultracode-for-codex/SKILL.md +102 -0
- package/skills/ultracode-for-codex/agents/openai.yaml +4 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ReasoningEffort, Verbosity } from './runtime/types.js';
|
|
2
|
+
export type WorkflowExecutionMode = 'background' | 'attached';
|
|
3
|
+
export type WorkflowProgressMode = 'jsonl' | 'plain';
|
|
4
|
+
export type WorkflowPermissionPolicy = 'ask' | 'allow' | 'deny';
|
|
5
|
+
export interface UltracodeSettings {
|
|
6
|
+
readonly workflow: {
|
|
7
|
+
readonly executionMode: WorkflowExecutionMode;
|
|
8
|
+
readonly progress: WorkflowProgressMode;
|
|
9
|
+
readonly permission: WorkflowPermissionPolicy;
|
|
10
|
+
readonly retryLimit: number;
|
|
11
|
+
readonly timeoutMs: number;
|
|
12
|
+
readonly background: {
|
|
13
|
+
readonly runDir: string;
|
|
14
|
+
readonly resultFile: string;
|
|
15
|
+
readonly progressFile: string;
|
|
16
|
+
readonly metadataFile: string;
|
|
17
|
+
readonly pidFile: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
readonly codex: {
|
|
21
|
+
readonly reasoningEffort: ReasoningEffort;
|
|
22
|
+
readonly verbosity: Verbosity;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export declare function loadSettings(): UltracodeSettings;
|
|
26
|
+
export declare function workflowDefaultExecutionMode(): WorkflowExecutionMode;
|
|
27
|
+
export declare function workflowDefaultProgressMode(): WorkflowProgressMode;
|
|
28
|
+
export declare function workflowDefaultPermissionPolicy(): WorkflowPermissionPolicy;
|
|
29
|
+
export declare function workflowDefaultRetryLimit(): number;
|
|
30
|
+
export declare function workflowDefaultTimeoutMs(): number;
|
|
31
|
+
export declare function workflowBackgroundDefaults(): UltracodeSettings['workflow']['background'];
|
|
32
|
+
export declare function codexDefaultReasoningEffort(): ReasoningEffort;
|
|
33
|
+
export declare function codexDefaultVerbosity(): Verbosity;
|
|
34
|
+
export declare function isReasoningEffort(value: unknown): value is ReasoningEffort;
|
|
35
|
+
export declare function isVerbosity(value: unknown): value is Verbosity;
|
|
36
|
+
export declare function isWorkflowExecutionMode(value: unknown): value is WorkflowExecutionMode;
|
|
37
|
+
export declare function isWorkflowProgressMode(value: unknown): value is WorkflowProgressMode;
|
|
38
|
+
export declare function isWorkflowPermissionPolicy(value: unknown): value is WorkflowPermissionPolicy;
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
const SETTINGS_URL = new URL('../settings.json', import.meta.url);
|
|
3
|
+
const WORKFLOW_EXECUTION_MODES = ['background', 'attached'];
|
|
4
|
+
const WORKFLOW_PROGRESS_MODES = ['jsonl', 'plain'];
|
|
5
|
+
const WORKFLOW_PERMISSION_POLICIES = ['ask', 'allow', 'deny'];
|
|
6
|
+
const REASONING_EFFORTS = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh'];
|
|
7
|
+
const VERBOSITIES = ['low', 'medium', 'high'];
|
|
8
|
+
let cachedSettings = null;
|
|
9
|
+
export function loadSettings() {
|
|
10
|
+
if (cachedSettings)
|
|
11
|
+
return cachedSettings;
|
|
12
|
+
let parsed;
|
|
13
|
+
try {
|
|
14
|
+
parsed = JSON.parse(readFileSync(SETTINGS_URL, 'utf8'));
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
throw new Error(`Unable to read settings.json: ${errorMessage(err)}`);
|
|
18
|
+
}
|
|
19
|
+
const root = asRecord(parsed);
|
|
20
|
+
if (!root)
|
|
21
|
+
throw new Error('settings.json must contain a JSON object.');
|
|
22
|
+
const workflow = asRecord(root?.workflow);
|
|
23
|
+
if (!workflow)
|
|
24
|
+
throw new Error('settings.json must define workflow.');
|
|
25
|
+
const background = asRecord(workflow?.background);
|
|
26
|
+
if (!background)
|
|
27
|
+
throw new Error('settings.json must define workflow.background.');
|
|
28
|
+
const codex = asRecord(root?.codex);
|
|
29
|
+
if (!codex)
|
|
30
|
+
throw new Error('settings.json must define codex.');
|
|
31
|
+
cachedSettings = {
|
|
32
|
+
workflow: {
|
|
33
|
+
executionMode: readWorkflowExecutionModeSetting(workflow?.executionMode, 'workflow.executionMode'),
|
|
34
|
+
progress: readWorkflowProgressModeSetting(workflow?.progress, 'workflow.progress'),
|
|
35
|
+
permission: readWorkflowPermissionPolicySetting(workflow?.permission, 'workflow.permission'),
|
|
36
|
+
retryLimit: readNonNegativeIntegerSetting(workflow?.retryLimit, 'workflow.retryLimit'),
|
|
37
|
+
timeoutMs: readPositiveIntegerSetting(workflow?.timeoutMs, 'workflow.timeoutMs'),
|
|
38
|
+
background: {
|
|
39
|
+
runDir: readTemplateSetting(background?.runDir, 'workflow.background.runDir', true),
|
|
40
|
+
resultFile: readRelativePathSetting(background?.resultFile, 'workflow.background.resultFile'),
|
|
41
|
+
progressFile: readRelativePathSetting(background?.progressFile, 'workflow.background.progressFile'),
|
|
42
|
+
metadataFile: readRelativePathSetting(background?.metadataFile, 'workflow.background.metadataFile'),
|
|
43
|
+
pidFile: readRelativePathSetting(background?.pidFile, 'workflow.background.pidFile'),
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
codex: {
|
|
47
|
+
reasoningEffort: readReasoningEffortSetting(codex?.reasoningEffort, 'codex.reasoningEffort'),
|
|
48
|
+
verbosity: readVerbositySetting(codex?.verbosity, 'codex.verbosity'),
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
return cachedSettings;
|
|
52
|
+
}
|
|
53
|
+
export function workflowDefaultExecutionMode() {
|
|
54
|
+
return loadSettings().workflow.executionMode;
|
|
55
|
+
}
|
|
56
|
+
export function workflowDefaultProgressMode() {
|
|
57
|
+
return loadSettings().workflow.progress;
|
|
58
|
+
}
|
|
59
|
+
export function workflowDefaultPermissionPolicy() {
|
|
60
|
+
return loadSettings().workflow.permission;
|
|
61
|
+
}
|
|
62
|
+
export function workflowDefaultRetryLimit() {
|
|
63
|
+
return loadSettings().workflow.retryLimit;
|
|
64
|
+
}
|
|
65
|
+
export function workflowDefaultTimeoutMs() {
|
|
66
|
+
return loadSettings().workflow.timeoutMs;
|
|
67
|
+
}
|
|
68
|
+
export function workflowBackgroundDefaults() {
|
|
69
|
+
return loadSettings().workflow.background;
|
|
70
|
+
}
|
|
71
|
+
export function codexDefaultReasoningEffort() {
|
|
72
|
+
return loadSettings().codex.reasoningEffort;
|
|
73
|
+
}
|
|
74
|
+
export function codexDefaultVerbosity() {
|
|
75
|
+
return loadSettings().codex.verbosity;
|
|
76
|
+
}
|
|
77
|
+
export function isReasoningEffort(value) {
|
|
78
|
+
return typeof value === 'string' && REASONING_EFFORTS.includes(value);
|
|
79
|
+
}
|
|
80
|
+
export function isVerbosity(value) {
|
|
81
|
+
return typeof value === 'string' && VERBOSITIES.includes(value);
|
|
82
|
+
}
|
|
83
|
+
export function isWorkflowExecutionMode(value) {
|
|
84
|
+
return typeof value === 'string' && WORKFLOW_EXECUTION_MODES.includes(value);
|
|
85
|
+
}
|
|
86
|
+
export function isWorkflowProgressMode(value) {
|
|
87
|
+
return typeof value === 'string' && WORKFLOW_PROGRESS_MODES.includes(value);
|
|
88
|
+
}
|
|
89
|
+
export function isWorkflowPermissionPolicy(value) {
|
|
90
|
+
return typeof value === 'string' && WORKFLOW_PERMISSION_POLICIES.includes(value);
|
|
91
|
+
}
|
|
92
|
+
function readWorkflowExecutionModeSetting(value, key) {
|
|
93
|
+
if (isWorkflowExecutionMode(value))
|
|
94
|
+
return value;
|
|
95
|
+
throw new Error(`${key} must be one of ${WORKFLOW_EXECUTION_MODES.join(', ')}.`);
|
|
96
|
+
}
|
|
97
|
+
function readWorkflowProgressModeSetting(value, key) {
|
|
98
|
+
if (isWorkflowProgressMode(value))
|
|
99
|
+
return value;
|
|
100
|
+
throw new Error(`${key} must be one of ${WORKFLOW_PROGRESS_MODES.join(', ')}.`);
|
|
101
|
+
}
|
|
102
|
+
function readWorkflowPermissionPolicySetting(value, key) {
|
|
103
|
+
if (isWorkflowPermissionPolicy(value))
|
|
104
|
+
return value;
|
|
105
|
+
throw new Error(`${key} must be one of ${WORKFLOW_PERMISSION_POLICIES.join(', ')}.`);
|
|
106
|
+
}
|
|
107
|
+
function readReasoningEffortSetting(value, key) {
|
|
108
|
+
if (isReasoningEffort(value))
|
|
109
|
+
return value;
|
|
110
|
+
throw new Error(`${key} must be one of ${REASONING_EFFORTS.join(', ')}.`);
|
|
111
|
+
}
|
|
112
|
+
function readVerbositySetting(value, key) {
|
|
113
|
+
if (isVerbosity(value))
|
|
114
|
+
return value;
|
|
115
|
+
throw new Error(`${key} must be one of ${VERBOSITIES.join(', ')}.`);
|
|
116
|
+
}
|
|
117
|
+
function readNonNegativeIntegerSetting(value, key) {
|
|
118
|
+
if (typeof value === 'number' && Number.isInteger(value) && value >= 0)
|
|
119
|
+
return value;
|
|
120
|
+
throw new Error(`${key} must be a non-negative integer.`);
|
|
121
|
+
}
|
|
122
|
+
function readPositiveIntegerSetting(value, key) {
|
|
123
|
+
if (typeof value === 'number' && Number.isInteger(value) && value > 0)
|
|
124
|
+
return value;
|
|
125
|
+
throw new Error(`${key} must be a positive integer.`);
|
|
126
|
+
}
|
|
127
|
+
function readTemplateSetting(value, key, requireJobId) {
|
|
128
|
+
const text = readNonEmptyStringSetting(value, key);
|
|
129
|
+
if (requireJobId && !text.includes('{jobId}')) {
|
|
130
|
+
throw new Error(`${key} must include {jobId}.`);
|
|
131
|
+
}
|
|
132
|
+
return text;
|
|
133
|
+
}
|
|
134
|
+
function readRelativePathSetting(value, key) {
|
|
135
|
+
const text = readNonEmptyStringSetting(value, key);
|
|
136
|
+
if (text.startsWith('/') || /^[A-Za-z]:[\\/]/.test(text) || text.split(/[\\/]+/).includes('..')) {
|
|
137
|
+
throw new Error(`${key} must be a relative path without parent traversal.`);
|
|
138
|
+
}
|
|
139
|
+
return text;
|
|
140
|
+
}
|
|
141
|
+
function readNonEmptyStringSetting(value, key) {
|
|
142
|
+
if (typeof value === 'string' && value.trim())
|
|
143
|
+
return value;
|
|
144
|
+
throw new Error(`${key} must be a non-empty string.`);
|
|
145
|
+
}
|
|
146
|
+
function asRecord(value) {
|
|
147
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
148
|
+
return null;
|
|
149
|
+
return value;
|
|
150
|
+
}
|
|
151
|
+
function errorMessage(err) {
|
|
152
|
+
return err instanceof Error ? err.message : String(err);
|
|
153
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const ULTRACODE_INSTALL_GUIDE_FILENAME = "ULTRACODE_INSTALL.md";
|
|
2
|
+
export declare function readUltracodeInstallGuide(): string;
|
|
3
|
+
export declare function ultracodeInstallGuidePath(): string;
|
|
4
|
+
export declare function renderUltracodeInstallGuideNotice(guide?: string): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
export const ULTRACODE_INSTALL_GUIDE_FILENAME = 'ULTRACODE_INSTALL.md';
|
|
5
|
+
export function readUltracodeInstallGuide() {
|
|
6
|
+
return readFileSync(ultracodeInstallGuidePath(), 'utf8');
|
|
7
|
+
}
|
|
8
|
+
export function ultracodeInstallGuidePath() {
|
|
9
|
+
const distDir = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
return join(distDir, '..', ULTRACODE_INSTALL_GUIDE_FILENAME);
|
|
11
|
+
}
|
|
12
|
+
export function renderUltracodeInstallGuideNotice(guide = readUltracodeInstallGuide()) {
|
|
13
|
+
return [
|
|
14
|
+
'',
|
|
15
|
+
'=== ultracode-for-codex ULTRACODE INSTALL GUIDE START ===',
|
|
16
|
+
guide.trimEnd(),
|
|
17
|
+
'=== ultracode-for-codex ULTRACODE INSTALL GUIDE END ===',
|
|
18
|
+
'',
|
|
19
|
+
`Re-read later with: ultracode-for-codex --llm-guide`,
|
|
20
|
+
'',
|
|
21
|
+
].join('\n');
|
|
22
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Ultracode Journal Contract
|
|
2
|
+
|
|
3
|
+
This is the current journal contract for the local command-owned workflow runtime.
|
|
4
|
+
It is implemented in `src/runtime/workflow-journal.ts` and
|
|
5
|
+
`src/runtime/workflow-runtime.ts`.
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Every launched workflow writes a durable
|
|
10
|
+
`<transcriptDir>/journal.jsonl` ledger. The journal is the canonical runtime
|
|
11
|
+
artifact for future replay/cache behavior; CLI progress is only a
|
|
12
|
+
projection.
|
|
13
|
+
|
|
14
|
+
P3-A is done when:
|
|
15
|
+
|
|
16
|
+
- `workflow.run.started` is durably written before `taskId` and `runId` are
|
|
17
|
+
acknowledged to the caller;
|
|
18
|
+
- agent started/completed/failed entries use deterministic start-order call
|
|
19
|
+
keys;
|
|
20
|
+
- exactly one terminal run entry is durable before terminal workflow events and
|
|
21
|
+
result projection;
|
|
22
|
+
- journal write failures fail closed with `workflow_journal_write_failed`;
|
|
23
|
+
- journal readers validate schema, ordering, sequence, hash chain, task/run
|
|
24
|
+
identity, agent pairing, terminal uniqueness, and trailing partial-line
|
|
25
|
+
recovery.
|
|
26
|
+
|
|
27
|
+
## Authority Model
|
|
28
|
+
|
|
29
|
+
| Concept | Authority | Rule |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| `journal.jsonl` | runtime canonical artifact | Future replay/cache reads this ledger. |
|
|
32
|
+
| `WorkflowEvent` | runtime projection | CLI progress consumes this stream. |
|
|
33
|
+
| `taskId`, `runId`, `seq`, `recordedAt` | runtime | Never accepted from workflow script or model output. |
|
|
34
|
+
| `journalPath` | runtime internal state | Never printed in CLI output. |
|
|
35
|
+
| `scriptPath`, `workflowSource`, `scriptHash` | runtime resolver | Captured in `workflow.run.started`. |
|
|
36
|
+
| agent return value | runtime executor | Raw text or validated structured output. |
|
|
37
|
+
|
|
38
|
+
The CLI defaults to OS background execution from `settings.json`, writing result
|
|
39
|
+
and progress files under the configured background run directory. Attached
|
|
40
|
+
execution prints progress to stderr as JSONL and final result JSON to stdout.
|
|
41
|
+
Journal contents and `journalPath` remain internal runtime state.
|
|
42
|
+
|
|
43
|
+
## Ordering
|
|
44
|
+
|
|
45
|
+
Launch ordering:
|
|
46
|
+
|
|
47
|
+
1. Normalize and validate workflow input.
|
|
48
|
+
2. Resolve source, permission, script hash, and transcript directory.
|
|
49
|
+
3. Create the transcript directory.
|
|
50
|
+
4. Append and durably flush `workflow.run.started`.
|
|
51
|
+
5. Register the task, emit `workflow.started`, and start execution.
|
|
52
|
+
|
|
53
|
+
Agent ordering:
|
|
54
|
+
|
|
55
|
+
1. Reserve `agentIndex`, `agentId`, `previousAgentCallKey`, and `agentCallKey`
|
|
56
|
+
before the first await.
|
|
57
|
+
2. Append and flush `workflow.agent.started`.
|
|
58
|
+
3. Emit `workflow.agent.started`.
|
|
59
|
+
4. Execute Codex subagent with stall retry and structured-output validation.
|
|
60
|
+
5. Append and flush exactly one agent final entry.
|
|
61
|
+
6. Emit the matching final event.
|
|
62
|
+
|
|
63
|
+
Terminal ordering:
|
|
64
|
+
|
|
65
|
+
1. Create or reuse one terminal finalizer.
|
|
66
|
+
2. Normalize result or failure payload.
|
|
67
|
+
3. Append and flush terminal run entry.
|
|
68
|
+
4. Emit `workflow.completed` or `workflow.failed`.
|
|
69
|
+
5. Write/project the terminal result when successful.
|
|
70
|
+
|
|
71
|
+
## Verification
|
|
72
|
+
|
|
73
|
+
- `test/workflow-journal.test.mjs` verifies stable JSON, writer durability, and
|
|
74
|
+
reader validation.
|
|
75
|
+
- `test/workflow-runtime.test.mjs` verifies direct runtime launch, failure,
|
|
76
|
+
retry, cancel, resume/cache, and worktree paths.
|
|
77
|
+
- `scripts/e2e-installed-ultracode-for-codex.mjs` verifies the packaged CLI
|
|
78
|
+
against a fake Codex app-server boundary.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Ultracode Resume Cache Contract
|
|
2
|
+
|
|
3
|
+
This is the current same-session resume/cache contract. It builds on
|
|
4
|
+
`docs/ultracode-p3a-journal-design.md`.
|
|
5
|
+
|
|
6
|
+
## Scope
|
|
7
|
+
|
|
8
|
+
Resume/cache is runtime-internal and same-session. User-facing recovery uses
|
|
9
|
+
same-run retry or explicit CLI reruns.
|
|
10
|
+
|
|
11
|
+
## Rules
|
|
12
|
+
|
|
13
|
+
- `resumeFromRunId` accepts only workflow `runId` values already known by the
|
|
14
|
+
current `WorkflowTaskRegistry`.
|
|
15
|
+
- Running source runs fail with `workflow_resume_running`.
|
|
16
|
+
- Completed agent prefixes are reused only when the current `agentCallKey`
|
|
17
|
+
matches the source journal prefix.
|
|
18
|
+
- The first changed or new agent call and every suffix call reruns.
|
|
19
|
+
- Cache hits emit `workflow.agent.completed` with `cached: true` and zero usage
|
|
20
|
+
for the current run.
|
|
21
|
+
- Resumed runs write their own journal entries.
|
|
22
|
+
|
|
23
|
+
`agentCallKey` is derived from previous key, prompt, and stable semantic opts:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
sha256(previousAgentCallKey + "\0" + prompt + "\0" + stableJson(opts))
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Semantic opts include schema, model, effort, isolation, and agent type. Display
|
|
30
|
+
values such as label and phase stay outside the cache identity.
|
|
31
|
+
|
|
32
|
+
## Verification
|
|
33
|
+
|
|
34
|
+
- `test/workflow-runtime.test.mjs` covers exact cache hits and retry/cancel
|
|
35
|
+
interactions.
|
|
36
|
+
- `test/workflow-journal.test.mjs` validates the journal reader used to derive
|
|
37
|
+
cache entries.
|
|
38
|
+
|
|
39
|
+
Realization:
|
|
40
|
+
|
|
41
|
+
- `mock`: direct runtime tests use a fake subagent backend.
|
|
42
|
+
- `boundary_stub`: packaged CLI E2E uses a fake Codex app-server for CLI
|
|
43
|
+
execution paths.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Ultracode Worktree Isolation Contract
|
|
2
|
+
|
|
3
|
+
This is the current contract for `agent(..., { isolation: "worktree" })`.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Worktree isolation is an opt-in local isolation mode for subagents that need
|
|
8
|
+
workspace writes while keeping the source checkout reviewable.
|
|
9
|
+
|
|
10
|
+
P3-C is done when:
|
|
11
|
+
|
|
12
|
+
- the runtime creates a detached git worktree before the backend call;
|
|
13
|
+
- the backend turn runs with that worktree as its workspace;
|
|
14
|
+
- unchanged isolated worktrees are removed after the agent finishes;
|
|
15
|
+
- changed or unsafe-to-clean worktrees are preserved for review;
|
|
16
|
+
- `semanticOpts.isolation` participates in the agent cache key.
|
|
17
|
+
|
|
18
|
+
## Authority Model
|
|
19
|
+
|
|
20
|
+
| Concept | Authority | Rule |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| isolation request | workflow script | Only `isolation: "worktree"` is accepted. |
|
|
23
|
+
| worktree path | runtime | Runtime creates paths outside the source repo working tree. |
|
|
24
|
+
| backend cwd | runtime request packet | Subagent backend executes the turn in `worktreePath`. |
|
|
25
|
+
| changed/unchanged decision | runtime git status | `git status --porcelain --untracked-files=all --ignored=matching` decides preserve vs cleanup. |
|
|
26
|
+
| preserved path projection | runtime event | Changed or unsafe-to-clean worktrees are surfaced on agent final events. |
|
|
27
|
+
|
|
28
|
+
The accepted isolation values are `"none"` and `"worktree"`; any other value
|
|
29
|
+
fails as invalid workflow input.
|
|
30
|
+
|
|
31
|
+
## Worktree Location
|
|
32
|
+
|
|
33
|
+
Worktrees are created as siblings of the git root:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
<parent-of-git-root>/.ultracode-for-codex-worktrees/<repo-slug>-<repo-hash>/<runId>/<agentId>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The worktree is detached at `HEAD`; uncommitted source repo changes are not
|
|
40
|
+
copied into the isolated worktree.
|
|
41
|
+
|
|
42
|
+
## Lifecycle
|
|
43
|
+
|
|
44
|
+
1. Validate `agent()` options and include `isolation: "worktree"` in
|
|
45
|
+
`semanticOpts`.
|
|
46
|
+
2. Append `workflow.agent.started`.
|
|
47
|
+
3. Create a detached git worktree for the agent.
|
|
48
|
+
4. Pass `worktreePath` to the subagent backend and append path-free worktree
|
|
49
|
+
context to the backend prompt.
|
|
50
|
+
5. Inspect worktree status after the attempt settles.
|
|
51
|
+
6. Remove clean worktrees.
|
|
52
|
+
7. Preserve changed, stalled, aborted, status-unavailable, or cleanup-failed
|
|
53
|
+
worktrees and surface them on the agent final event.
|
|
54
|
+
|
|
55
|
+
## Verification
|
|
56
|
+
|
|
57
|
+
- `test/codex-isolation.test.mjs` verifies Codex backend request projection.
|
|
58
|
+
- `test/workflow-runtime.test.mjs` verifies changed worktree preservation.
|
|
59
|
+
- `scripts/e2e-installed-ultracode-for-codex.mjs` verifies packaged CLI workflow
|
|
60
|
+
execution through the fake Codex app-server boundary.
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ultracode-for-codex",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Run local Codex-backed workflows from a command-owned CLI runtime.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"codex",
|
|
7
|
+
"workflow",
|
|
8
|
+
"cli",
|
|
9
|
+
"automation",
|
|
10
|
+
"subagents"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/kangminlee-maker/ultracode-for-codex#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/kangminlee-maker/ultracode-for-codex/issues"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/kangminlee-maker/ultracode-for-codex.git"
|
|
19
|
+
},
|
|
20
|
+
"license": "UNLICENSED",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"bin": {
|
|
23
|
+
"ultracode-for-codex": "dist/cli.js"
|
|
24
|
+
},
|
|
25
|
+
"exports": {
|
|
26
|
+
".": "./dist/cli.js",
|
|
27
|
+
"./ultracode-install-guide": "./dist/ultracode-install-guide.js",
|
|
28
|
+
"./settings": "./dist/settings.js"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"ULTRACODE_INSTALL.md",
|
|
32
|
+
"postinstall.mjs",
|
|
33
|
+
"dist/cli.js",
|
|
34
|
+
"dist/cli.d.ts",
|
|
35
|
+
"dist/ultracode-install-guide.js",
|
|
36
|
+
"dist/ultracode-install-guide.d.ts",
|
|
37
|
+
"dist/settings.js",
|
|
38
|
+
"dist/settings.d.ts",
|
|
39
|
+
"dist/codex/",
|
|
40
|
+
"dist/runtime/",
|
|
41
|
+
"skills/ultracode-for-codex/",
|
|
42
|
+
"settings.json",
|
|
43
|
+
"README.md",
|
|
44
|
+
"docs/ultracode-p3a-journal-design.md",
|
|
45
|
+
"docs/ultracode-p3b-resume-cache.md",
|
|
46
|
+
"docs/ultracode-p3c-worktree-isolation.md"
|
|
47
|
+
],
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "rm -rf dist && tsc -p tsconfig.json && chmod 755 dist/cli.js",
|
|
50
|
+
"verify:runtime-boundary": "node scripts/verify-runtime-boundary.mjs",
|
|
51
|
+
"prepack": "npm run build && npm run verify:runtime-boundary",
|
|
52
|
+
"postinstall": "node postinstall.mjs",
|
|
53
|
+
"prepublishOnly": "npm run test:all",
|
|
54
|
+
"pack:ultracode-for-codex": "node scripts/package-ultracode-for-codex.mjs",
|
|
55
|
+
"publish:dry-run": "npm publish --dry-run --access public",
|
|
56
|
+
"publish:npm": "npm publish --access public",
|
|
57
|
+
"publish:npm:provenance": "npm publish --access public --provenance",
|
|
58
|
+
"test:e2e:ultracode-for-codex": "node scripts/e2e-installed-ultracode-for-codex.mjs",
|
|
59
|
+
"test:all": "npm test && npm run test:e2e:ultracode-for-codex",
|
|
60
|
+
"test": "npm run build && npm run verify:runtime-boundary && node --test --test-concurrency=2 test/*.test.mjs",
|
|
61
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
62
|
+
"run": "npm run build && node dist/cli.js run --accept-llm-guide=v1"
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@types/node": "^22.0.0",
|
|
67
|
+
"typescript": "^5.9.3"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=22.0.0"
|
|
71
|
+
},
|
|
72
|
+
"publishConfig": {
|
|
73
|
+
"access": "public",
|
|
74
|
+
"registry": "https://registry.npmjs.org/"
|
|
75
|
+
},
|
|
76
|
+
"packageManager": "npm@11.12.1"
|
|
77
|
+
}
|
package/postinstall.mjs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
const packageRoot = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const guide = readFileSync(join(packageRoot, 'ULTRACODE_INSTALL.md'), 'utf8');
|
|
9
|
+
process.stdout.write([
|
|
10
|
+
'',
|
|
11
|
+
'=== ultracode-for-codex ULTRACODE INSTALL GUIDE START ===',
|
|
12
|
+
guide.trimEnd(),
|
|
13
|
+
'=== ultracode-for-codex ULTRACODE INSTALL GUIDE END ===',
|
|
14
|
+
'',
|
|
15
|
+
'Re-read later with: ultracode-for-codex --llm-guide',
|
|
16
|
+
'',
|
|
17
|
+
].join('\n'));
|
|
18
|
+
} catch (err) {
|
|
19
|
+
process.stderr.write(
|
|
20
|
+
`ultracode-for-codex postinstall failed to read ULTRACODE_INSTALL.md: ${err instanceof Error ? err.message : String(err)}\n`,
|
|
21
|
+
);
|
|
22
|
+
process.exitCode = 1;
|
|
23
|
+
}
|
package/settings.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"workflow": {
|
|
3
|
+
"executionMode": "background",
|
|
4
|
+
"progress": "jsonl",
|
|
5
|
+
"permission": "ask",
|
|
6
|
+
"retryLimit": 0,
|
|
7
|
+
"timeoutMs": 180000,
|
|
8
|
+
"background": {
|
|
9
|
+
"runDir": ".ultracode-for-codex/background/{jobId}",
|
|
10
|
+
"resultFile": "result.json",
|
|
11
|
+
"progressFile": "progress.jsonl",
|
|
12
|
+
"metadataFile": "metadata.json",
|
|
13
|
+
"pidFile": "pid"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"codex": {
|
|
17
|
+
"reasoningEffort": "xhigh",
|
|
18
|
+
"verbosity": "medium"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ultracode-for-codex
|
|
3
|
+
description: Operate, package, validate, or update the Ultracode for Codex npm runtime. Use when installing or running local workflows, checking runtime boundaries, packaging release tarballs, updating docs, or maintaining the companion Codex skill.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ultracode for Codex
|
|
7
|
+
|
|
8
|
+
## Core Rule
|
|
9
|
+
|
|
10
|
+
Treat the npm package as the runtime artifact. This skill is only a companion
|
|
11
|
+
guide for Codex agents. Runtime authority remains in the `ultracode-for-codex`
|
|
12
|
+
binary, tests, package exports, journal layer, and workflow runtime code.
|
|
13
|
+
|
|
14
|
+
Workflow execution runs through the local CLI command. Progress,
|
|
15
|
+
cancellation, permission review, retry, and result projection stay in that
|
|
16
|
+
command process. `settings.json` defaults runs to OS background execution.
|
|
17
|
+
Attached runs stream stderr JSONL for Codex-readable status, while stdout
|
|
18
|
+
remains the final workflow result JSON.
|
|
19
|
+
|
|
20
|
+
## Install And Run
|
|
21
|
+
|
|
22
|
+
Use the npm package for consumer installs.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install --save-dev ultracode-for-codex
|
|
26
|
+
npm exec -- ultracode-for-codex --llm-guide
|
|
27
|
+
npm exec -- ultracode-for-codex run \
|
|
28
|
+
--accept-llm-guide=v1 \
|
|
29
|
+
--cwd /path/to/project \
|
|
30
|
+
--script-file .codex/workflows/review.js \
|
|
31
|
+
--args '{"prompt":"review the current change"}'
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
For source-checkout validation before publish:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm run pack:ultracode-for-codex
|
|
38
|
+
npm install --save-dev ./artifacts/ultracode-for-codex-0.2.0.tgz
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
CLI behavior:
|
|
42
|
+
|
|
43
|
+
- default execution is `background`; stdout contains a launch record with
|
|
44
|
+
`jobId`, `pid`, `resultPath`, `progressPath`, `metadataPath`, and `pidPath`;
|
|
45
|
+
- attached execution is available with `--execution attached`;
|
|
46
|
+
- attached progress prints to stderr as JSONL by default;
|
|
47
|
+
- attached final workflow result prints as JSON to stdout;
|
|
48
|
+
- JSONL records include `kind`, `version`, `event`, `status`, and `summary`,
|
|
49
|
+
with agent identity and label fields on agent records;
|
|
50
|
+
- `Ctrl-C` cancels the active attached workflow;
|
|
51
|
+
- `--retry-limit <n>` retries failed workflows inside the same process;
|
|
52
|
+
- `--permission ask|allow|deny` handles project/user/plugin/scriptPath reviews.
|
|
53
|
+
- `--progress plain` switches to human-readable progress lines.
|
|
54
|
+
- background file locations are controlled by `workflow.background` in
|
|
55
|
+
`settings.json`.
|
|
56
|
+
|
|
57
|
+
## Runtime Boundaries
|
|
58
|
+
|
|
59
|
+
- Use Codex app-server over stdio as the production backend.
|
|
60
|
+
- Keep direct provider credentials out of Codex child process environments.
|
|
61
|
+
- Keep workflow execution local and command-owned; settings default to OS
|
|
62
|
+
background execution and `--execution attached` keeps the process connected
|
|
63
|
+
until completion.
|
|
64
|
+
- Keep `journalPath`, `journal.jsonl`, and journal contents out of CLI output.
|
|
65
|
+
- Treat `.ultracode-for-codex` workflow state as sensitive local data.
|
|
66
|
+
- Keep `resumeFromRunId` runtime-internal unless cross-process resume
|
|
67
|
+
gets an explicit durable design.
|
|
68
|
+
- Use `isolation: "worktree"` only inside a git repo with at least one commit;
|
|
69
|
+
changed or unsafe worktrees are intentionally preserved for review.
|
|
70
|
+
|
|
71
|
+
## Packaging And Verification
|
|
72
|
+
|
|
73
|
+
For source checkout changes, run the narrowest relevant check first, then a
|
|
74
|
+
release-level check before handoff:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm test
|
|
78
|
+
npm run test:e2e:ultracode-for-codex
|
|
79
|
+
npm run test:all
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Build an installable artifact with:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm run pack:ultracode-for-codex
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Check the npm publish payload with:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npm run publish:dry-run
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Publish after npm login with:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npm run publish:npm
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
When architecture, runtime boundaries, package exports, or release scope
|
|
101
|
+
changes, update active docs such as `README.md`, `ULTRACODE_INSTALL.md`, and
|
|
102
|
+
`IMPLEMENTATION_MAP.html`.
|