@johndaskovsky/nightshift 0.1.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 +263 -0
- package/bin/nightshift.js +6 -0
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +97 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/update.d.ts +3 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/commands/update.js +92 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/index.d.ts +4 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +45 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/config-merger.d.ts +14 -0
- package/dist/core/config-merger.d.ts.map +1 -0
- package/dist/core/config-merger.js +70 -0
- package/dist/core/config-merger.js.map +1 -0
- package/dist/core/scaffolder.d.ts +24 -0
- package/dist/core/scaffolder.d.ts.map +1 -0
- package/dist/core/scaffolder.js +54 -0
- package/dist/core/scaffolder.js.map +1 -0
- package/dist/core/templates.d.ts +10 -0
- package/dist/core/templates.d.ts.map +1 -0
- package/dist/core/templates.js +30 -0
- package/dist/core/templates.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/package.json +36 -0
- package/templates/agents/nightshift-dev.md +187 -0
- package/templates/agents/nightshift-manager.md +192 -0
- package/templates/agents/nightshift-qa.md +102 -0
- package/templates/commands/nightshift-add-task.md +96 -0
- package/templates/commands/nightshift-archive.md +67 -0
- package/templates/commands/nightshift-create.md +85 -0
- package/templates/commands/nightshift-start.md +78 -0
- package/templates/commands/nightshift-test-task.md +88 -0
- package/templates/commands/nightshift-update-table.md +81 -0
- package/templates/opencode.jsonc +92 -0
package/README.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
```
|
|
2
|
+
* . . . * . +
|
|
3
|
+
_ _ _ _ _ ____ _ _ __ _ *
|
|
4
|
+
| \ | (_) __ _| |__ | |_ / ___|| |__ (_)/ _| |_ .
|
|
5
|
+
| \| | |/ _` | '_ \| __| \___ \| '_ \| | |_| __| (
|
|
6
|
+
| |\ | | (_| | | | | |_ ___) | | | | | _| |_ *
|
|
7
|
+
|_| \_|_|\__, |_| |_|\__| |____/|_| |_|_|_| \__|
|
|
8
|
+
* |___/ . + . *
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Long-running unsupervised agent framework
|
|
12
|
+
|
|
13
|
+
A batch processing framework for AI agents. Define a table of items, write task instructions, and let a three-agent system (manager, dev, QA) work through them autonomously with built-in retries, self-improvement, and independent verification.
|
|
14
|
+
|
|
15
|
+
Nightshift runs inside [OpenCode](https://opencode.ai/) as a set of custom agents, commands, and skills. There is no traditional source code -- the entire framework is defined through Markdown, CSV, YAML, and JSONC configuration files.
|
|
16
|
+
|
|
17
|
+
## How It Works
|
|
18
|
+
|
|
19
|
+
A **shift** is a batch job. It contains a CSV table of items to process and one or more task definitions that describe what to do with each item. Three agents collaborate to execute the work:
|
|
20
|
+
|
|
21
|
+
- **Manager** -- reads shift state, picks the next item, delegates to dev and QA, updates statuses. The sole writer of `table.csv`.
|
|
22
|
+
- **Dev** -- executes task steps on a single item, self-validates, retries up to 3 times, and refines task steps for future items.
|
|
23
|
+
- **QA** -- independently verifies the dev's work against validation criteria. Strictly read-only.
|
|
24
|
+
|
|
25
|
+
Each item-task moves through a state machine:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
todo --> in_progress --> qa --> done
|
|
29
|
+
\ /
|
|
30
|
+
-> failed
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Prerequisites
|
|
34
|
+
|
|
35
|
+
- [OpenCode](https://opencode.ai/) AI assistant
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
Install the Nightshift CLI globally:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install -g @jdaskovsky/nightshift
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then initialize Nightshift in your project:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
cd your-project
|
|
49
|
+
nightshift init
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This creates the `.nightshift/` and `.opencode/` directories, writes agent and command files, and merges the Nightshift agent block into `opencode.jsonc`.
|
|
53
|
+
|
|
54
|
+
To regenerate framework files after upgrading the CLI:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
nightshift update
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`update` overwrites the agent and command files and re-merges `opencode.jsonc` while preserving your non-Nightshift configuration. It does not touch `.nightshift/` shift data.
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
All commands run inside the OpenCode assistant.
|
|
65
|
+
|
|
66
|
+
### 1. Create a shift
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
/nightshift-create my-batch-job
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This scaffolds `.nightshift/my-batch-job/` with a `manager.md` and an empty `table.csv`. Add data to the table. The items should have all of the metadata needed to process the tasks.
|
|
73
|
+
|
|
74
|
+
### 2. Add a task
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
/nightshift-add-task my-batch-job
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The command asks you to describe what the agent should do, what tools it needs, step-by-step instructions, and how to verify success. It creates a task file (e.g., `create-page.md`) with three sections:
|
|
81
|
+
|
|
82
|
+
```markdown
|
|
83
|
+
## Configuration
|
|
84
|
+
|
|
85
|
+
- tools: playwright
|
|
86
|
+
|
|
87
|
+
## Steps
|
|
88
|
+
|
|
89
|
+
1. Navigate to {url}
|
|
90
|
+
2. Click the "Add Page" button
|
|
91
|
+
3. Fill in the title field with {page_title}
|
|
92
|
+
4. Click "Publish"
|
|
93
|
+
|
|
94
|
+
## Validation
|
|
95
|
+
|
|
96
|
+
- Page exists at the expected URL
|
|
97
|
+
- Page title matches {page_title}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Steps use `{column_name}` placeholders that get substituted with each row's data at execution time.
|
|
101
|
+
|
|
102
|
+
### 3. Add items to the table
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
/nightshift-update-table my-batch-job
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Add rows with the metadata columns your tasks reference. The resulting `table.csv` looks like:
|
|
109
|
+
|
|
110
|
+
```csv
|
|
111
|
+
row,url,page_title,create-page
|
|
112
|
+
1,https://example.com/site1,Welcome,todo
|
|
113
|
+
2,https://example.com/site2,About Us,todo
|
|
114
|
+
3,https://example.com/site3,Contact,todo
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Each task gets a status column initialized to `todo`.
|
|
118
|
+
|
|
119
|
+
### 4. Run the shift
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
/nightshift-start my-batch-job
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The manager agent takes over: it reads the table, picks the next `todo` item, delegates to the dev agent, sends successful results to QA, updates statuses, and loops until everything is `done` or `failed`.
|
|
126
|
+
|
|
127
|
+
### 5. Test a single task (optional)
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
/nightshift-test-task my-batch-job
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Runs one task on one row through both dev and QA agents **without modifying any state**. Useful for debugging task definitions before running a full shift.
|
|
134
|
+
|
|
135
|
+
### 6. Archive a completed shift
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
/nightshift-archive my-batch-job
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Moves the shift to `.nightshift/archive/YYYY-MM-DD-my-batch-job/`.
|
|
142
|
+
|
|
143
|
+
## Commands Reference
|
|
144
|
+
|
|
145
|
+
| Command | Purpose |
|
|
146
|
+
|---------|---------|
|
|
147
|
+
| `/nightshift-create <name>` | Scaffold a new shift directory with manager.md and table.csv |
|
|
148
|
+
| `/nightshift-add-task <name>` | Add a task definition to an existing shift |
|
|
149
|
+
| `/nightshift-update-table <name>` | Add rows, update metadata, or reset failed items |
|
|
150
|
+
| `/nightshift-start <name>` | Begin or resume shift execution |
|
|
151
|
+
| `/nightshift-test-task <name>` | Dry-run a single task on a single row |
|
|
152
|
+
| `/nightshift-archive <name>` | Move a completed shift to the archive |
|
|
153
|
+
|
|
154
|
+
All commands accept a shift name as an argument, or prompt interactively if omitted.
|
|
155
|
+
|
|
156
|
+
## Shift Structure
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
.nightshift/
|
|
160
|
+
my-batch-job/
|
|
161
|
+
manager.md # Shift config: name, task order, progress counters
|
|
162
|
+
table.csv # Items and their per-task statuses
|
|
163
|
+
create-page.md # Task definition (Configuration, Steps, Validation)
|
|
164
|
+
update-cms.md # Another task definition
|
|
165
|
+
archive/
|
|
166
|
+
2026-02-08-old-job/ # Archived shift (date-prefixed)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### manager.md
|
|
170
|
+
|
|
171
|
+
Tracks task execution order and progress. Updated automatically by the manager agent.
|
|
172
|
+
|
|
173
|
+
```markdown
|
|
174
|
+
## Shift Configuration
|
|
175
|
+
|
|
176
|
+
- name: my-batch-job
|
|
177
|
+
- created: 2026-02-08
|
|
178
|
+
|
|
179
|
+
## Task Order
|
|
180
|
+
|
|
181
|
+
1. create-page
|
|
182
|
+
2. update-cms
|
|
183
|
+
|
|
184
|
+
## Progress
|
|
185
|
+
|
|
186
|
+
- Total items: 3
|
|
187
|
+
- Completed: 1
|
|
188
|
+
- Failed: 0
|
|
189
|
+
- Remaining: 2
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### table.csv
|
|
193
|
+
|
|
194
|
+
Each row is an item. Metadata columns hold the data tasks need. Status columns (one per task) track progress.
|
|
195
|
+
|
|
196
|
+
```csv
|
|
197
|
+
row,url,page_title,create-page,update-cms
|
|
198
|
+
1,https://example.com/site1,Welcome,done,in_progress
|
|
199
|
+
2,https://example.com/site2,About Us,todo,todo
|
|
200
|
+
3,https://example.com/site3,Contact,todo,todo
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Status values: `todo`, `in_progress`, `qa`, `done`, `failed`.
|
|
204
|
+
|
|
205
|
+
### Task files
|
|
206
|
+
|
|
207
|
+
Each task has three sections. Only the Steps section is modified during execution (by the dev agent's self-improvement loop).
|
|
208
|
+
|
|
209
|
+
| Section | Purpose | Mutable by Dev |
|
|
210
|
+
|---------|---------|----------------|
|
|
211
|
+
| Configuration | Declares tools and optional model | No |
|
|
212
|
+
| Steps | Numbered instructions with `{placeholder}` substitution | Yes |
|
|
213
|
+
| Validation | Independently verifiable success criteria | No |
|
|
214
|
+
|
|
215
|
+
## Execution Details
|
|
216
|
+
|
|
217
|
+
### Item processing order
|
|
218
|
+
|
|
219
|
+
The manager iterates rows in order, tasks in the order listed in `manager.md`. Tasks within a row are sequential -- task 2 cannot start until task 1 is `done`. A `failed` task blocks all subsequent tasks for that row.
|
|
220
|
+
|
|
221
|
+
### Dev agent retry loop
|
|
222
|
+
|
|
223
|
+
The dev agent gets up to 3 attempts per item (1 initial + 2 retries). On each attempt it:
|
|
224
|
+
|
|
225
|
+
1. Substitutes `{placeholder}` values from the current row
|
|
226
|
+
2. Executes steps sequentially, stopping on failure
|
|
227
|
+
3. Refines the Steps section based on what it learned
|
|
228
|
+
4. Self-validates against the Validation criteria
|
|
229
|
+
5. Retries from scratch if self-validation fails and attempts remain
|
|
230
|
+
|
|
231
|
+
Step refinements persist to the task file, so subsequent items benefit from earlier learnings.
|
|
232
|
+
|
|
233
|
+
### QA verification
|
|
234
|
+
|
|
235
|
+
After a successful dev execution, the QA agent independently checks every validation criterion using read-only tools (file reads, grep, Playwright). It reports per-criterion pass/fail with specific evidence. All criteria must pass for `done`; any failure means `failed`.
|
|
236
|
+
|
|
237
|
+
### Resumability
|
|
238
|
+
|
|
239
|
+
If a shift is interrupted, `/nightshift-start` picks up where it left off. The manager resets any stale `in_progress` or `qa` statuses back to `todo` on startup, then continues processing remaining items.
|
|
240
|
+
|
|
241
|
+
### Graceful degradation
|
|
242
|
+
|
|
243
|
+
A single item failure never stops the entire shift. The manager marks the failed item and moves on to the next one.
|
|
244
|
+
|
|
245
|
+
## Agent Permissions
|
|
246
|
+
|
|
247
|
+
| Agent | Write | Edit | Bash | Delegation | Playwright |
|
|
248
|
+
|-------|-------|------|------|------------|------------|
|
|
249
|
+
| Manager | yes | yes | denied | dev, qa only | no |
|
|
250
|
+
| Dev | yes | yes | `mkdir` only | none | yes |
|
|
251
|
+
| QA | no | no | denied | none | yes |
|
|
252
|
+
|
|
253
|
+
## Project Layout
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
night-shift/
|
|
257
|
+
opencode.jsonc # Agent definitions and permissions
|
|
258
|
+
.nightshift/ # Active and archived shifts
|
|
259
|
+
.opencode/
|
|
260
|
+
agent/ # Manager, dev, and QA agent instructions
|
|
261
|
+
command/ # Slash command definitions
|
|
262
|
+
```
|
|
263
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,wBAAgB,iBAAiB,IAAI,OAAO,CAiG3C"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import { scaffoldDirectories, writeAgentFiles, writeCommandFiles, } from "../../core/scaffolder.js";
|
|
5
|
+
import { mergeOpencodeConfig } from "../../core/config-merger.js";
|
|
6
|
+
export function createInitCommand() {
|
|
7
|
+
return new Command("init")
|
|
8
|
+
.description("Initialize Nightshift in the current project")
|
|
9
|
+
.option("-f, --force", "Overwrite all files without prompting")
|
|
10
|
+
.option("-y, --yes", "Skip confirmation prompts")
|
|
11
|
+
.action(async (options) => {
|
|
12
|
+
const targetDir = process.cwd();
|
|
13
|
+
const force = options.force ?? false;
|
|
14
|
+
const actions = [];
|
|
15
|
+
const warnings = [];
|
|
16
|
+
let hasError = false;
|
|
17
|
+
console.log(chalk.bold("\nInitializing Nightshift...\n"));
|
|
18
|
+
// Step 1: Scaffold directories
|
|
19
|
+
const dirSpinner = ora("Creating directories...").start();
|
|
20
|
+
try {
|
|
21
|
+
scaffoldDirectories(targetDir);
|
|
22
|
+
dirSpinner.succeed("Directories created");
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
dirSpinner.fail("Failed to create directories");
|
|
26
|
+
console.error(chalk.red(err instanceof Error ? err.message : String(err)));
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
// Step 2: Write agent files
|
|
30
|
+
const agentSpinner = ora("Writing agent files...").start();
|
|
31
|
+
try {
|
|
32
|
+
const result = writeAgentFiles({
|
|
33
|
+
targetDir,
|
|
34
|
+
force,
|
|
35
|
+
onWrite: (path, action) => actions.push({ path, action }),
|
|
36
|
+
});
|
|
37
|
+
agentSpinner.succeed(`Agent files written (${result.actions.length} files)`);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
agentSpinner.fail("Failed to write agent files");
|
|
41
|
+
warnings.push(`Agent files: ${err instanceof Error ? err.message : String(err)}`);
|
|
42
|
+
hasError = true;
|
|
43
|
+
}
|
|
44
|
+
// Step 3: Write command files
|
|
45
|
+
const cmdSpinner = ora("Writing command files...").start();
|
|
46
|
+
try {
|
|
47
|
+
const result = writeCommandFiles({
|
|
48
|
+
targetDir,
|
|
49
|
+
force,
|
|
50
|
+
onWrite: (path, action) => actions.push({ path, action }),
|
|
51
|
+
});
|
|
52
|
+
cmdSpinner.succeed(`Command files written (${result.actions.length} files)`);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
cmdSpinner.fail("Failed to write command files");
|
|
56
|
+
warnings.push(`Command files: ${err instanceof Error ? err.message : String(err)}`);
|
|
57
|
+
hasError = true;
|
|
58
|
+
}
|
|
59
|
+
// Step 4: Merge opencode.jsonc
|
|
60
|
+
const configSpinner = ora("Configuring opencode.jsonc...").start();
|
|
61
|
+
try {
|
|
62
|
+
const result = mergeOpencodeConfig(targetDir);
|
|
63
|
+
configSpinner.succeed(`opencode.jsonc ${result.action}`);
|
|
64
|
+
actions.push({ path: result.path, action: result.action });
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
configSpinner.fail("Failed to configure opencode.jsonc");
|
|
68
|
+
warnings.push(`Config merge: ${err instanceof Error ? err.message : String(err)}`);
|
|
69
|
+
hasError = true;
|
|
70
|
+
}
|
|
71
|
+
// Summary
|
|
72
|
+
console.log(chalk.bold("\n--- Summary ---\n"));
|
|
73
|
+
if (actions.length > 0) {
|
|
74
|
+
for (const { path, action } of actions) {
|
|
75
|
+
const relativePath = path.replace(targetDir + "/", "");
|
|
76
|
+
const icon = action === "created" ? chalk.green("+") :
|
|
77
|
+
action === "updated" ? chalk.yellow("~") :
|
|
78
|
+
chalk.dim("-");
|
|
79
|
+
console.log(` ${icon} ${relativePath} (${action})`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (warnings.length > 0) {
|
|
83
|
+
console.log(chalk.yellow("\nWarnings:"));
|
|
84
|
+
for (const w of warnings) {
|
|
85
|
+
console.log(chalk.yellow(` ! ${w}`));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
console.log(chalk.bold("\n--- Next Steps ---\n"));
|
|
89
|
+
console.log(" 1. Open your project in OpenCode");
|
|
90
|
+
console.log(" 2. Run " + chalk.cyan("/nightshift-create") + " to create your first shift");
|
|
91
|
+
console.log("");
|
|
92
|
+
if (hasError) {
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,mBAAmB,IAAI,OAAO,CA4F7C"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import { scaffoldDirectories, writeAgentFiles, writeCommandFiles, } from "../../core/scaffolder.js";
|
|
5
|
+
import { mergeOpencodeConfig } from "../../core/config-merger.js";
|
|
6
|
+
export function createUpdateCommand() {
|
|
7
|
+
return new Command("update")
|
|
8
|
+
.description("Regenerate Nightshift framework files from the current CLI version")
|
|
9
|
+
.option("-y, --yes", "Skip confirmation prompts")
|
|
10
|
+
.action(async (_options) => {
|
|
11
|
+
const targetDir = process.cwd();
|
|
12
|
+
const actions = [];
|
|
13
|
+
const warnings = [];
|
|
14
|
+
let hasError = false;
|
|
15
|
+
console.log(chalk.bold("\nUpdating Nightshift files...\n"));
|
|
16
|
+
// Ensure directories exist
|
|
17
|
+
const dirSpinner = ora("Ensuring directories...").start();
|
|
18
|
+
try {
|
|
19
|
+
scaffoldDirectories(targetDir);
|
|
20
|
+
dirSpinner.succeed("Directories verified");
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
dirSpinner.fail("Failed to verify directories");
|
|
24
|
+
console.error(chalk.red(err instanceof Error ? err.message : String(err)));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
// Overwrite agent files
|
|
28
|
+
const agentSpinner = ora("Updating agent files...").start();
|
|
29
|
+
try {
|
|
30
|
+
const result = writeAgentFiles({
|
|
31
|
+
targetDir,
|
|
32
|
+
force: true,
|
|
33
|
+
onWrite: (path, action) => actions.push({ path, action }),
|
|
34
|
+
});
|
|
35
|
+
agentSpinner.succeed(`Agent files updated (${result.actions.length} files)`);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
agentSpinner.fail("Failed to update agent files");
|
|
39
|
+
warnings.push(`Agent files: ${err instanceof Error ? err.message : String(err)}`);
|
|
40
|
+
hasError = true;
|
|
41
|
+
}
|
|
42
|
+
// Overwrite command files
|
|
43
|
+
const cmdSpinner = ora("Updating command files...").start();
|
|
44
|
+
try {
|
|
45
|
+
const result = writeCommandFiles({
|
|
46
|
+
targetDir,
|
|
47
|
+
force: true,
|
|
48
|
+
onWrite: (path, action) => actions.push({ path, action }),
|
|
49
|
+
});
|
|
50
|
+
cmdSpinner.succeed(`Command files updated (${result.actions.length} files)`);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
cmdSpinner.fail("Failed to update command files");
|
|
54
|
+
warnings.push(`Command files: ${err instanceof Error ? err.message : String(err)}`);
|
|
55
|
+
hasError = true;
|
|
56
|
+
}
|
|
57
|
+
// Re-merge opencode.jsonc
|
|
58
|
+
const configSpinner = ora("Updating opencode.jsonc...").start();
|
|
59
|
+
try {
|
|
60
|
+
const result = mergeOpencodeConfig(targetDir);
|
|
61
|
+
configSpinner.succeed(`opencode.jsonc ${result.action}`);
|
|
62
|
+
actions.push({ path: result.path, action: result.action });
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
configSpinner.fail("Failed to update opencode.jsonc");
|
|
66
|
+
warnings.push(`Config merge: ${err instanceof Error ? err.message : String(err)}`);
|
|
67
|
+
hasError = true;
|
|
68
|
+
}
|
|
69
|
+
// Summary
|
|
70
|
+
console.log(chalk.bold("\n--- Summary ---\n"));
|
|
71
|
+
if (actions.length > 0) {
|
|
72
|
+
for (const { path, action } of actions) {
|
|
73
|
+
const relativePath = path.replace(targetDir + "/", "");
|
|
74
|
+
const icon = action === "created" ? chalk.green("+") :
|
|
75
|
+
action === "updated" ? chalk.yellow("~") :
|
|
76
|
+
chalk.dim("-");
|
|
77
|
+
console.log(` ${icon} ${relativePath} (${action})`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (warnings.length > 0) {
|
|
81
|
+
console.log(chalk.yellow("\nWarnings:"));
|
|
82
|
+
for (const w of warnings) {
|
|
83
|
+
console.log(chalk.yellow(` ! ${w}`));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
console.log(chalk.bold("\nUpdate complete.\n"));
|
|
87
|
+
if (hasError) {
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../../src/cli/commands/update.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+BpC,wBAAgB,aAAa,IAAI,OAAO,CAYvC;AAED,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAGzC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve, dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { createInitCommand } from "./commands/init.js";
|
|
6
|
+
import { createUpdateCommand } from "./commands/update.js";
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
function getVersion() {
|
|
10
|
+
try {
|
|
11
|
+
// Resolve package.json relative to compiled output (dist/cli/) or source (src/cli/)
|
|
12
|
+
const candidates = [
|
|
13
|
+
resolve(__dirname, "..", "..", "package.json"),
|
|
14
|
+
resolve(__dirname, "..", "package.json"),
|
|
15
|
+
];
|
|
16
|
+
for (const candidate of candidates) {
|
|
17
|
+
try {
|
|
18
|
+
const pkg = JSON.parse(readFileSync(candidate, "utf-8"));
|
|
19
|
+
return pkg.version ?? "0.0.0";
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return "0.0.0";
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return "0.0.0";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function createProgram() {
|
|
32
|
+
const program = new Command();
|
|
33
|
+
program
|
|
34
|
+
.name("nightshift")
|
|
35
|
+
.description("CLI installer for the Nightshift AI agent orchestration framework")
|
|
36
|
+
.version(getVersion());
|
|
37
|
+
program.addCommand(createInitCommand());
|
|
38
|
+
program.addCommand(createUpdateCommand());
|
|
39
|
+
return program;
|
|
40
|
+
}
|
|
41
|
+
export async function run() {
|
|
42
|
+
const program = createProgram();
|
|
43
|
+
await program.parseAsync(process.argv);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,oFAAoF;QACpF,MAAM,UAAU,GAAG;YACjB,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;YAC9C,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC;SACzC,CAAC;QACF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;gBACzD,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,YAAY,CAAC;SAClB,WAAW,CAAC,mEAAmE,CAAC;SAChF,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAEzB,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACxC,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAE1C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,GAAG;IACvB,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAChC,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface MergeResult {
|
|
2
|
+
action: "created" | "merged";
|
|
3
|
+
path: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Create or merge opencode.jsonc in the target project.
|
|
7
|
+
*
|
|
8
|
+
* Strategy:
|
|
9
|
+
* - If no file exists: copy the full template
|
|
10
|
+
* - If file exists: parse it, merge only the three Nightshift agent entries
|
|
11
|
+
* into the "agent" block, preserving everything else including comments
|
|
12
|
+
*/
|
|
13
|
+
export declare function mergeOpencodeConfig(targetDir: string): MergeResult;
|
|
14
|
+
//# sourceMappingURL=config-merger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-merger.d.ts","sourceRoot":"","sources":["../../src/core/config-merger.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CAyDlE"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import * as jsonc from "jsonc-parser";
|
|
4
|
+
import { getTemplatePath } from "./templates.js";
|
|
5
|
+
/** The three Nightshift agent keys */
|
|
6
|
+
const NIGHTSHIFT_AGENT_KEYS = [
|
|
7
|
+
"nightshift-manager",
|
|
8
|
+
"nightshift-dev",
|
|
9
|
+
"nightshift-qa",
|
|
10
|
+
];
|
|
11
|
+
/**
|
|
12
|
+
* Create or merge opencode.jsonc in the target project.
|
|
13
|
+
*
|
|
14
|
+
* Strategy:
|
|
15
|
+
* - If no file exists: copy the full template
|
|
16
|
+
* - If file exists: parse it, merge only the three Nightshift agent entries
|
|
17
|
+
* into the "agent" block, preserving everything else including comments
|
|
18
|
+
*/
|
|
19
|
+
export function mergeOpencodeConfig(targetDir) {
|
|
20
|
+
const targetPath = join(targetDir, "opencode.jsonc");
|
|
21
|
+
const templatePath = getTemplatePath("opencode.jsonc");
|
|
22
|
+
const templateContent = readFileSync(templatePath, "utf-8");
|
|
23
|
+
if (!existsSync(targetPath)) {
|
|
24
|
+
// No existing file — write the full template
|
|
25
|
+
writeFileSync(targetPath, templateContent, "utf-8");
|
|
26
|
+
return { action: "created", path: targetPath };
|
|
27
|
+
}
|
|
28
|
+
// Existing file — merge the agent block
|
|
29
|
+
const existingContent = readFileSync(targetPath, "utf-8");
|
|
30
|
+
const templateTree = jsonc.parseTree(templateContent);
|
|
31
|
+
const existingTree = jsonc.parseTree(existingContent);
|
|
32
|
+
if (!templateTree || !existingTree) {
|
|
33
|
+
throw new Error("Failed to parse opencode.jsonc files");
|
|
34
|
+
}
|
|
35
|
+
// Extract agent definitions from template
|
|
36
|
+
const templateAgents = {};
|
|
37
|
+
for (const key of NIGHTSHIFT_AGENT_KEYS) {
|
|
38
|
+
const node = jsonc.findNodeAtLocation(templateTree, ["agent", key]);
|
|
39
|
+
if (node) {
|
|
40
|
+
templateAgents[key] = jsonc.getNodeValue(node);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Apply edits to the existing content
|
|
44
|
+
let result = existingContent;
|
|
45
|
+
// Check if "agent" key exists
|
|
46
|
+
const agentNode = jsonc.findNodeAtLocation(existingTree, ["agent"]);
|
|
47
|
+
if (!agentNode) {
|
|
48
|
+
// No "agent" block exists — add one with the Nightshift agents
|
|
49
|
+
const agentBlock = {};
|
|
50
|
+
for (const key of NIGHTSHIFT_AGENT_KEYS) {
|
|
51
|
+
agentBlock[key] = templateAgents[key];
|
|
52
|
+
}
|
|
53
|
+
const edits = jsonc.modify(result, ["agent"], agentBlock, {
|
|
54
|
+
formattingOptions: { tabSize: 2, insertSpaces: true, eol: "\n" },
|
|
55
|
+
});
|
|
56
|
+
result = jsonc.applyEdits(result, edits);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// "agent" block exists — update each Nightshift agent entry
|
|
60
|
+
for (const key of NIGHTSHIFT_AGENT_KEYS) {
|
|
61
|
+
const edits = jsonc.modify(result, ["agent", key], templateAgents[key], {
|
|
62
|
+
formattingOptions: { tabSize: 2, insertSpaces: true, eol: "\n" },
|
|
63
|
+
});
|
|
64
|
+
result = jsonc.applyEdits(result, edits);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
writeFileSync(targetPath, result, "utf-8");
|
|
68
|
+
return { action: "merged", path: targetPath };
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=config-merger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-merger.js","sourceRoot":"","sources":["../../src/core/config-merger.ts"],"names":[],"mappings":"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"}
|