@matteeh/osq 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/LICENSE +21 -0
- package/README.md +183 -0
- package/dist/cli/approve.d.ts +6 -0
- package/dist/cli/approve.d.ts.map +1 -0
- package/dist/cli/approve.js +26 -0
- package/dist/cli/approve.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +5 -0
- package/dist/cli/bin.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 +76 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +4 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +19 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/new.d.ts +4 -0
- package/dist/cli/new.d.ts.map +1 -0
- package/dist/cli/new.js +15 -0
- package/dist/cli/new.js.map +1 -0
- package/dist/cli/report.d.ts +10 -0
- package/dist/cli/report.d.ts.map +1 -0
- package/dist/cli/report.js +84 -0
- package/dist/cli/report.js.map +1 -0
- package/dist/cli/setup.d.ts +2 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +10 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/show.d.ts +7 -0
- package/dist/cli/show.d.ts.map +1 -0
- package/dist/cli/show.js +27 -0
- package/dist/cli/show.js.map +1 -0
- package/dist/cli/status.d.ts +7 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +23 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/watch.d.ts +9 -0
- package/dist/cli/watch.d.ts.map +1 -0
- package/dist/cli/watch.js +20 -0
- package/dist/cli/watch.js.map +1 -0
- package/dist/core/approve.d.ts +11 -0
- package/dist/core/approve.d.ts.map +1 -0
- package/dist/core/approve.js +52 -0
- package/dist/core/approve.js.map +1 -0
- package/dist/core/config.d.ts +53 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +124 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/hasher.d.ts +4 -0
- package/dist/core/hasher.d.ts.map +1 -0
- package/dist/core/hasher.js +49 -0
- package/dist/core/hasher.js.map +1 -0
- package/dist/core/init.d.ts +12 -0
- package/dist/core/init.d.ts.map +1 -0
- package/dist/core/init.js +151 -0
- package/dist/core/init.js.map +1 -0
- package/dist/core/linter.d.ts +8 -0
- package/dist/core/linter.d.ts.map +1 -0
- package/dist/core/linter.js +100 -0
- package/dist/core/linter.js.map +1 -0
- package/dist/core/lock.d.ts +17 -0
- package/dist/core/lock.d.ts.map +1 -0
- package/dist/core/lock.js +104 -0
- package/dist/core/lock.js.map +1 -0
- package/dist/core/logger.d.ts +32 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +106 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/new.d.ts +11 -0
- package/dist/core/new.d.ts.map +1 -0
- package/dist/core/new.js +79 -0
- package/dist/core/new.js.map +1 -0
- package/dist/core/parser.d.ts +32 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +87 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/core/report.d.ts +54 -0
- package/dist/core/report.d.ts.map +1 -0
- package/dist/core/report.js +475 -0
- package/dist/core/report.js.map +1 -0
- package/dist/core/show.d.ts +50 -0
- package/dist/core/show.d.ts.map +1 -0
- package/dist/core/show.js +374 -0
- package/dist/core/show.js.map +1 -0
- package/dist/core/state.d.ts +25 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +180 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/status.d.ts +10 -0
- package/dist/core/status.d.ts.map +1 -0
- package/dist/core/status.js +106 -0
- package/dist/core/status.js.map +1 -0
- package/dist/harness/agy.d.ts +31 -0
- package/dist/harness/agy.d.ts.map +1 -0
- package/dist/harness/agy.js +251 -0
- package/dist/harness/agy.js.map +1 -0
- package/dist/harness/index.d.ts +7 -0
- package/dist/harness/index.d.ts.map +1 -0
- package/dist/harness/index.js +20 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/harness/mock.d.ts +19 -0
- package/dist/harness/mock.d.ts.map +1 -0
- package/dist/harness/mock.js +62 -0
- package/dist/harness/mock.js.map +1 -0
- package/dist/harness/opencode.d.ts +39 -0
- package/dist/harness/opencode.d.ts.map +1 -0
- package/dist/harness/opencode.js +394 -0
- package/dist/harness/opencode.js.map +1 -0
- package/dist/harness/process.d.ts +26 -0
- package/dist/harness/process.d.ts.map +1 -0
- package/dist/harness/process.js +99 -0
- package/dist/harness/process.js.map +1 -0
- package/dist/harness/stream.d.ts +34 -0
- package/dist/harness/stream.d.ts.map +1 -0
- package/dist/harness/stream.js +75 -0
- package/dist/harness/stream.js.map +1 -0
- package/dist/harness/types.d.ts +53 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +10 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/watcher/archiver.d.ts +5 -0
- package/dist/watcher/archiver.d.ts.map +1 -0
- package/dist/watcher/archiver.js +66 -0
- package/dist/watcher/archiver.js.map +1 -0
- package/dist/watcher/loop.d.ts +34 -0
- package/dist/watcher/loop.d.ts.map +1 -0
- package/dist/watcher/loop.js +235 -0
- package/dist/watcher/loop.js.map +1 -0
- package/dist/watcher/runner.d.ts +70 -0
- package/dist/watcher/runner.d.ts.map +1 -0
- package/dist/watcher/runner.js +604 -0
- package/dist/watcher/runner.js.map +1 -0
- package/package.json +66 -0
- package/templates/.env.example +3 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mathias
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# osq
|
|
2
|
+
|
|
3
|
+
Strict spec queue. Spec-driven development with two kinds of agent and a human gate between them.
|
|
4
|
+
|
|
5
|
+
A smart model helps you write small, immutable task specs. A watcher hands each approved spec to a fresh, cheap coding agent that does the work, writes a result, and exits. Failures go to a dead letter queue you deal with next time you sit down. Nothing runs that you didn't approve, and nothing is marked done that the watcher didn't verify itself.
|
|
6
|
+
|
|
7
|
+
If a fresh agent could not pick up a task from the files in the repo alone, the files are wrong. Everything here follows from that.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npx @matteeh/osq init # scaffolds the folders below and configuration
|
|
13
|
+
pnpm add -D @matteeh/osq # adds osq as a devDependency (or npm i -D @matteeh/osq)
|
|
14
|
+
pnpm osq watch # start the watcher
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`init` is idempotent. Run it again after upgrading to refresh the managed block in AGENTS.md; it never touches anything else you've edited.
|
|
18
|
+
|
|
19
|
+
## What it puts in your repo
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
AGENTS.md your existing file, plus a managed block with the coding agent procedure
|
|
23
|
+
osq.config.ts limits, paths, max concurrency
|
|
24
|
+
specs/
|
|
25
|
+
_template/ copied by `osq new`
|
|
26
|
+
042-order-cancellation/
|
|
27
|
+
spec.md goal, contract, non-goals, and the delta to apply to feature docs
|
|
28
|
+
tasks.md one checkbox per task, ticked by the watcher
|
|
29
|
+
tasks/1.md the unit of work: acceptance, verify, scope, entry
|
|
30
|
+
tasks/2.md
|
|
31
|
+
.run/ approved (hash), running/, done/, dead/, results/, events/
|
|
32
|
+
archive/ finished change folders, moved whole
|
|
33
|
+
features/ one doc per feature, describes current behavior, always true of main
|
|
34
|
+
decisions/ ADRs, superseded not edited
|
|
35
|
+
.env.example OSQ_HARNESS and API keys
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
A change folder is a feature. A task is one unit of work for one agent. After approval the folder is read-only until every task is done or one is dead. State is which marker files exist under `.run/`, never a field in a document.
|
|
39
|
+
|
|
40
|
+
## The loop
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
you + smart model write change folder -> specs/042-x/ with spec.md and tasks/
|
|
44
|
+
you approve -> .run/approved
|
|
45
|
+
watcher spawn per task -> cheap agent, fresh context, one attempt
|
|
46
|
+
agent work, write result -> .run/results/1.md, exit
|
|
47
|
+
watcher verify, tick box -> .run/done/1 or .run/dead/1.md
|
|
48
|
+
watcher last task done -> apply delta to features/, archive folder
|
|
49
|
+
you next time -> osq status, look at dead
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Smart models author specs and never execute them. Cheap models execute specs and never author them.
|
|
53
|
+
|
|
54
|
+
## Change folder
|
|
55
|
+
|
|
56
|
+
`spec.md` is written for humans and the smart model:
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
---
|
|
60
|
+
title: Order cancellation
|
|
61
|
+
depends_on: [041]
|
|
62
|
+
features:
|
|
63
|
+
reads: [inventory-reservation]
|
|
64
|
+
writes: [order-state-machine]
|
|
65
|
+
---
|
|
66
|
+
## Goal
|
|
67
|
+
## Contract
|
|
68
|
+
## Non-goals
|
|
69
|
+
## Delta
|
|
70
|
+
What changes in each doc under features.writes. Applied by the watcher when the last task is done.
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`tasks/<n>.md` is what a coding agent gets:
|
|
74
|
+
|
|
75
|
+
```yaml
|
|
76
|
+
---
|
|
77
|
+
title: When a PENDING order is cancelled, its reservation is released
|
|
78
|
+
verify: pnpm test -- orders/cancel
|
|
79
|
+
scope: [src/orders/**, tests/orders/**]
|
|
80
|
+
entry: [src/orders/service.ts]
|
|
81
|
+
skills: []
|
|
82
|
+
---
|
|
83
|
+
## Acceptance
|
|
84
|
+
- [ ] each line is a test in disguise, max 7
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Tasks run in order. The agent reads its task, the parent `spec.md`, the docs under `features.reads` and `features.writes`, AGENTS.md, and a previous result file for that task if there is one. Nothing else. The delta is applied by the watcher, so the agent never edits feature docs.
|
|
88
|
+
|
|
89
|
+
Lint, run by `osq approve`:
|
|
90
|
+
|
|
91
|
+
| Check | Result |
|
|
92
|
+
|---------------------------------------------|--------|
|
|
93
|
+
| task `scope` has more than 8 patterns | reject |
|
|
94
|
+
| `features.writes` has more than 2 entries | reject |
|
|
95
|
+
| more than one table under `## Contract` | reject |
|
|
96
|
+
| task `verify` empty or chains commands | reject |
|
|
97
|
+
| `depends_on` names a missing change | reject |
|
|
98
|
+
| task acceptance longer than 7 lines | reject |
|
|
99
|
+
| `## Delta` empty while `features.writes` is not | reject |
|
|
100
|
+
| task title contains " and " | warn |
|
|
101
|
+
|
|
102
|
+
Rules the lint can't check: title reads "when X, Y happens"; slice vertically so every spec leaves `main` green on its own; no "investigate" or "decide" in a spec (that's a spike, whose output is a paragraph in a feature doc or an ADR); default to a parent with children and approve the list before writing any child in full.
|
|
103
|
+
|
|
104
|
+
## What the watcher guarantees
|
|
105
|
+
|
|
106
|
+
- State is rebuilt from `specs/` on every change. Kill it and restart it any time.
|
|
107
|
+
- One agent per spec: locks are created exclusively, stale locks (dead pid or timeout) are reaped to `dead/`.
|
|
108
|
+
- What runs is what was approved: `.run/approved` holds a hash of the folder minus `.run/` (with `tasks.md` checkbox state normalized), checked before every spawn.
|
|
109
|
+
- `done` means the watcher ran `verify` itself in a timeout-bounded process group after the agent exited. The agent's claim is not enough.
|
|
110
|
+
- Feature docs are only ever changed by the watcher applying an approved delta. Agents never touch `features/`.
|
|
111
|
+
- The agent prompt protocol restricts write paths to `.run/results/` and edits to `scope`. All markers and checkboxes are written by the watcher.
|
|
112
|
+
- An agent that exits without writing `.run/results/<n>.md` is not lost: if the adapter captured a final text message, the watcher synthesizes a result file (`synthesized: true`) from it and proceeds to verify. Only an exit with neither a result file nor final text is `dead` with `reason: no_result`. Nothing disappears silently.
|
|
113
|
+
|
|
114
|
+
Reasons emitted: `verify_red` (with `timed_out: true` if verify exceeded timeout), `spec_conflict`, `already_running`, `no_result` (no result file and no final text), `crashed`, `timeout`.
|
|
115
|
+
|
|
116
|
+
## Harnesses
|
|
117
|
+
|
|
118
|
+
`OSQ_HARNESS` picks an adapter. An adapter does two things: spawn an agent for a tier (`coding` or `smart`) and write its harness's config files (`osq setup`). Adapters translate the harness's own event stream into six events (`started`, `tokens`, `file_changed`, `verify_ran`, `result_written`, `exited`), appended to the task's `.run/events/<n>.jsonl`. Hooks are optional shims that append to the same file. The loop works without them.
|
|
119
|
+
|
|
120
|
+
Available adapters:
|
|
121
|
+
- `agy`: Antigravity harness adapter
|
|
122
|
+
- `opencode`: OpenCode harness adapter running tasks via `opencode run`
|
|
123
|
+
- `mock`: In-memory deterministic simulation for tests
|
|
124
|
+
|
|
125
|
+
Configure `opencode` in `osq.config.ts`:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { defineConfig } from '@matteeh/osq';
|
|
129
|
+
|
|
130
|
+
export default defineConfig({
|
|
131
|
+
harness: 'opencode',
|
|
132
|
+
opencode: {
|
|
133
|
+
bin: 'opencode',
|
|
134
|
+
model: 'deepseek/deepseek-flash',
|
|
135
|
+
agent: 'osq-coder',
|
|
136
|
+
variant: 'thinking', // optional
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Running `osq setup` with the `opencode` harness scaffolds `.opencode/agent/osq-coder.md` with restricted permissions (denying `webfetch` and `websearch`) and the managed `AGENTS.md` execution procedure. Note that the `--auto` flag approves any action the agent file does not deny. The agent file must have mode `all` or `primary`; a subagent cannot be selected with --agent and OpenCode silently falls back to an unrestricted default. `webfetch` and `websearch` are denied at the tool level, but `bash` is allowed and unrestricted, so the agent can reach the network through the shell. Network isolation requires a sandbox and is listed under "Not yet".
|
|
142
|
+
|
|
143
|
+
## Commands
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
osq init scaffold
|
|
147
|
+
osq setup write harness config for OSQ_HARNESS
|
|
148
|
+
osq new <name> new change folder from the template
|
|
149
|
+
osq watch run the watcher
|
|
150
|
+
osq approve <id>.. lint, hash, approve. also re-approves after fixing a dead task
|
|
151
|
+
osq status every change and task with its state
|
|
152
|
+
osq show <id> spec, tasks, results, dead markers, event timeline
|
|
153
|
+
osq report completion rate, dead by reason, cost and time per task
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`osq report` renders completion rate, failures by reason, execution durations, token usage, and file changes. Reported cost sums the `cost` values carried by harness events. Reported cost reflects the harness's internal price table rather than the invoice.
|
|
157
|
+
|
|
158
|
+
## Release Procedure
|
|
159
|
+
|
|
160
|
+
To release a new version of `osq`:
|
|
161
|
+
1. Bump `"version"` in `package.json`.
|
|
162
|
+
2. Add a corresponding release section in `CHANGELOG.md`.
|
|
163
|
+
3. Commit the changes: `git commit -am "release: v<x.y.z>"`.
|
|
164
|
+
4. Create and push the release tag: `git tag v<x.y.z> && git push --tags`.
|
|
165
|
+
5. GitHub Actions (`release.yml`) verifies the build and publishes to npm with provenance via trusted publishing.
|
|
166
|
+
|
|
167
|
+
## Not yet
|
|
168
|
+
|
|
169
|
+
Decided but deliberately unbuilt until the loop has closed on real work:
|
|
170
|
+
|
|
171
|
+
- Concurrency above 1, with a worktree per running task and merge on done (`reason: merge_conflict`).
|
|
172
|
+
- Hard OS/container sandbox confinement enforcing `scope` boundaries and filesystem write limits (`reason: scope_violation`).
|
|
173
|
+
- Pre-spawn dependency and context verification checks (`reason: missing_dep`, `reason: missing_context`).
|
|
174
|
+
- Deterministic section-level replacement for feature doc deltas (ADR 002).
|
|
175
|
+
- Containerized coding agents.
|
|
176
|
+
- A derived SQLite index under `~/.osq/` for `status` and `report` across projects. Files stay the source of truth; the index is gitignored and can be deleted at any time.
|
|
177
|
+
- Hook shims for harnesses, and `osq import` for OpenSpec change folders.
|
|
178
|
+
|
|
179
|
+
## Status
|
|
180
|
+
|
|
181
|
+
Early. Written in TypeScript so `npx` is the whole install and the glue (spawning, JSON streams, file watching, git) stays short; the filesystem protocol means the watcher could be rewritten in another language later without consumers noticing. Numbers in `osq.config.ts` are guesses until `osq report` says otherwise. See `decisions/` for why things are the way they are.
|
|
182
|
+
|
|
183
|
+
MIT.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approve.d.ts","sourceRoot":"","sources":["../../src/cli/approve.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,mBAAmB,CAAC;AAE/D,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAO,GACjD,OAAO,CAAC,IAAI,CAAC,CAuBf"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { approveSpec } from '../core/approve.js';
|
|
2
|
+
import { loadConfig } from '../core/config.js';
|
|
3
|
+
export async function approveCommand(specIds, options = {}) {
|
|
4
|
+
const cwd = options.cwd || process.cwd();
|
|
5
|
+
const config = options.config || (await loadConfig(cwd));
|
|
6
|
+
if (!specIds || specIds.length === 0) {
|
|
7
|
+
console.error('Error: specify at least one spec ID to approve (e.g. osq approve 001)');
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
for (const specId of specIds) {
|
|
11
|
+
try {
|
|
12
|
+
const result = await approveSpec(cwd, specId, config);
|
|
13
|
+
console.log(`Approved ${result.specId} (${result.folderName})`);
|
|
14
|
+
console.log(` Hash: ${result.hash}`);
|
|
15
|
+
for (const warning of result.warnings) {
|
|
16
|
+
console.warn(` Warning: ${warning}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
21
|
+
console.error(`Error approving ${specId}:\n ${message}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=approve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approve.js","sourceRoot":"","sources":["../../src/cli/approve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAkB,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/D,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAiB,EACjB,UAAgD,EAAE;IAElD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzD,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,OAAO,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAC;QACvF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;YACtD,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC;YAChE,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACtC,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACtC,OAAO,CAAC,IAAI,CAAC,cAAc,OAAO,EAAE,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO,CAAC,KAAK,CAAC,mBAAmB,MAAM,QAAQ,OAAO,EAAE,CAAC,CAAC;YAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":""}
|
package/dist/cli/bin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAElE,MAAM,OAAO,GAAG,aAAa,CAAC,qBAAqB,EAAE,CAAC,CAAC;AACvD,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,wBAAgB,qBAAqB,IAAI,MAAM,CAG9C;AAED,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAuEvD"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { approveCommand } from './approve.js';
|
|
4
|
+
import { initCommand } from './init.js';
|
|
5
|
+
import { newCommand } from './new.js';
|
|
6
|
+
import { reportCommand } from './report.js';
|
|
7
|
+
import { setupCommand } from './setup.js';
|
|
8
|
+
import { showCommand } from './show.js';
|
|
9
|
+
import { statusCommand } from './status.js';
|
|
10
|
+
import { watchCommand } from './watch.js';
|
|
11
|
+
const PACKAGE_MANIFEST_URL = new URL('../../package.json', import.meta.url);
|
|
12
|
+
export function resolvePackageVersion() {
|
|
13
|
+
const manifest = JSON.parse(readFileSync(PACKAGE_MANIFEST_URL, 'utf8'));
|
|
14
|
+
return manifest.version ?? '0.0.0';
|
|
15
|
+
}
|
|
16
|
+
export function createProgram(version) {
|
|
17
|
+
const program = new Command();
|
|
18
|
+
program
|
|
19
|
+
.name('osq')
|
|
20
|
+
.description('Strict spec queue. Spec-driven development with two kinds of agent and a human gate.')
|
|
21
|
+
.version(version ?? resolvePackageVersion());
|
|
22
|
+
program
|
|
23
|
+
.command('init')
|
|
24
|
+
.description('scaffold osq folders and configuration')
|
|
25
|
+
.action(async () => {
|
|
26
|
+
await initCommand();
|
|
27
|
+
});
|
|
28
|
+
program
|
|
29
|
+
.command('new <name>')
|
|
30
|
+
.description('new change folder from template')
|
|
31
|
+
.action(async (name) => {
|
|
32
|
+
await newCommand(name);
|
|
33
|
+
});
|
|
34
|
+
program
|
|
35
|
+
.command('approve <ids...>')
|
|
36
|
+
.description('lint, hash, and approve change folders')
|
|
37
|
+
.action(async (ids) => {
|
|
38
|
+
await approveCommand(ids);
|
|
39
|
+
});
|
|
40
|
+
program
|
|
41
|
+
.command('watch')
|
|
42
|
+
.description('run the spec watcher loop')
|
|
43
|
+
.option('-o, --once', 'run pending tasks in queue and exit')
|
|
44
|
+
.option('--verbose', 'enable verbose logging')
|
|
45
|
+
.option('-q, --quiet', 'suppress info and verbose logging')
|
|
46
|
+
.action(async (options) => {
|
|
47
|
+
await watchCommand(options);
|
|
48
|
+
});
|
|
49
|
+
program
|
|
50
|
+
.command('setup')
|
|
51
|
+
.description('configure harness environment and configuration')
|
|
52
|
+
.action(async () => {
|
|
53
|
+
await setupCommand();
|
|
54
|
+
});
|
|
55
|
+
program
|
|
56
|
+
.command('status')
|
|
57
|
+
.description('show status overview of change folders and tasks')
|
|
58
|
+
.action(async () => {
|
|
59
|
+
await statusCommand();
|
|
60
|
+
});
|
|
61
|
+
program
|
|
62
|
+
.command('show <id>')
|
|
63
|
+
.description('show detailed change information, tasks, and event timeline')
|
|
64
|
+
.action(async (id) => {
|
|
65
|
+
await showCommand(id);
|
|
66
|
+
});
|
|
67
|
+
program
|
|
68
|
+
.command('report')
|
|
69
|
+
.description('display delivery metrics and task completion report')
|
|
70
|
+
.option('--json', 'output report as raw JSON')
|
|
71
|
+
.action(async (options) => {
|
|
72
|
+
await reportCommand(options);
|
|
73
|
+
});
|
|
74
|
+
return program;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAEA,wBAAsB,WAAW,CAAC,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB/E"}
|
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { scaffoldProject } from '../core/init.js';
|
|
2
|
+
export async function initCommand(options = {}) {
|
|
3
|
+
const cwd = options.cwd || process.cwd();
|
|
4
|
+
const result = await scaffoldProject(cwd);
|
|
5
|
+
for (const dir of result.createdDirs) {
|
|
6
|
+
console.log(` created ${dir}/`);
|
|
7
|
+
}
|
|
8
|
+
for (const file of result.createdFiles) {
|
|
9
|
+
console.log(` created ${file}`);
|
|
10
|
+
}
|
|
11
|
+
for (const file of result.skippedFiles) {
|
|
12
|
+
console.log(` exists ${file}`);
|
|
13
|
+
}
|
|
14
|
+
if (result.updatedAgentsMd) {
|
|
15
|
+
console.log(' updated AGENTS.md (refreshed managed block)');
|
|
16
|
+
}
|
|
17
|
+
console.log('\nosq initialized successfully.');
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAA4B,EAAE;IAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;IAE1C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,GAAG,CAAC,CAAC;IACpC,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../src/cli/new.ts"],"names":[],"mappings":"AAEA,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAW5F"}
|
package/dist/cli/new.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createNewSpec } from '../core/new.js';
|
|
2
|
+
export async function newCommand(name, options = {}) {
|
|
3
|
+
const cwd = options.cwd || process.cwd();
|
|
4
|
+
try {
|
|
5
|
+
const result = await createNewSpec(cwd, name);
|
|
6
|
+
console.log(`Created spec ${result.specId}: ${result.folderName}`);
|
|
7
|
+
console.log(` Path: ${result.folderPath}`);
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11
|
+
console.error(`Error: ${message}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=new.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/cli/new.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY,EAAE,UAA4B,EAAE;IAC3E,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QACnE,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO,CAAC,KAAK,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type OsqConfig } from '../core/config.js';
|
|
2
|
+
export interface ReportCommandOptions {
|
|
3
|
+
cwd?: string;
|
|
4
|
+
stdout?: (msg: string) => void;
|
|
5
|
+
config?: OsqConfig;
|
|
6
|
+
json?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function serializeSortedJson(data: unknown): string;
|
|
9
|
+
export declare function reportCommand(options?: ReportCommandOptions): Promise<string>;
|
|
10
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/cli/report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,mBAAmB,CAAC;AAG/D,MAAM,WAAW,oBAAoB;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAkBD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAEzD;AA8CD,wBAAsB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBvF"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { loadConfig } from '../core/config.js';
|
|
2
|
+
import { formatMetricsReport, getMetricsReport } from '../core/report.js';
|
|
3
|
+
/** Recursively sorts object keys so serialized output is deterministic on every platform. */
|
|
4
|
+
function sortKeysDeep(value) {
|
|
5
|
+
if (Array.isArray(value)) {
|
|
6
|
+
return value.map(sortKeysDeep);
|
|
7
|
+
}
|
|
8
|
+
if (value !== null && typeof value === 'object') {
|
|
9
|
+
const record = value;
|
|
10
|
+
const sorted = {};
|
|
11
|
+
for (const key of Object.keys(record).sort()) {
|
|
12
|
+
sorted[key] = sortKeysDeep(record[key]);
|
|
13
|
+
}
|
|
14
|
+
return sorted;
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
export function serializeSortedJson(data) {
|
|
19
|
+
return JSON.stringify(sortKeysDeep(data), null, 2);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Projects the report down to the stable {@link MetricsReport} shape, dropping
|
|
23
|
+
* backward-compatibility aliases so the machine-readable output stays fixed.
|
|
24
|
+
*/
|
|
25
|
+
function toStableMetrics(report) {
|
|
26
|
+
const stable = {
|
|
27
|
+
completionRate: report.completionRate,
|
|
28
|
+
durations: {
|
|
29
|
+
totalMs: report.durations.totalMs,
|
|
30
|
+
totalSeconds: report.durations.totalSeconds,
|
|
31
|
+
avgMs: report.durations.avgMs,
|
|
32
|
+
avgSeconds: report.durations.avgSeconds,
|
|
33
|
+
formattedTotal: report.durations.formattedTotal,
|
|
34
|
+
formattedAvg: report.durations.formattedAvg,
|
|
35
|
+
},
|
|
36
|
+
failureBreakdown: { ...report.failureBreakdown },
|
|
37
|
+
fileChanges: {
|
|
38
|
+
totalChanges: report.fileChanges.totalChanges,
|
|
39
|
+
uniqueCount: report.fileChanges.uniqueCount,
|
|
40
|
+
uniqueFiles: [...report.fileChanges.uniqueFiles],
|
|
41
|
+
},
|
|
42
|
+
specs: { ...report.specs },
|
|
43
|
+
tasks: { ...report.tasks },
|
|
44
|
+
tokens: {
|
|
45
|
+
input: report.tokens.input,
|
|
46
|
+
cached_input: report.tokens.cached_input,
|
|
47
|
+
output: report.tokens.output,
|
|
48
|
+
reasoning: report.tokens.reasoning,
|
|
49
|
+
total: report.tokens.total,
|
|
50
|
+
cacheSharePercent: report.tokens.cacheSharePercent,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
if (report.cost) {
|
|
54
|
+
stable.cost = {
|
|
55
|
+
total: report.cost.total,
|
|
56
|
+
perSpec: { ...report.cost.perSpec },
|
|
57
|
+
formattedTotal: report.cost.formattedTotal,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return stable;
|
|
61
|
+
}
|
|
62
|
+
export async function reportCommand(options = {}) {
|
|
63
|
+
const cwd = options.cwd || process.cwd();
|
|
64
|
+
const config = options.config || (await loadConfig(cwd));
|
|
65
|
+
try {
|
|
66
|
+
const report = await getMetricsReport(cwd, config);
|
|
67
|
+
const output = options.json
|
|
68
|
+
? serializeSortedJson(toStableMetrics(report))
|
|
69
|
+
: formatMetricsReport(report);
|
|
70
|
+
if (options.stdout) {
|
|
71
|
+
options.stdout(output);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
console.log(output);
|
|
75
|
+
}
|
|
76
|
+
return output;
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
80
|
+
console.error(`Report error: ${message}`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/cli/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"AAGA,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAMlD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { loadConfig } from '../core/config.js';
|
|
2
|
+
import { getHarnessAdapter } from '../harness/index.js';
|
|
3
|
+
export async function setupCommand() {
|
|
4
|
+
const cwd = process.cwd();
|
|
5
|
+
const config = await loadConfig(cwd);
|
|
6
|
+
const adapter = getHarnessAdapter(config.harness);
|
|
7
|
+
await adapter.setup(cwd, config);
|
|
8
|
+
console.log(`Harness '${config.harness}' setup completed successfully.`);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,iBAAiB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"show.d.ts","sourceRoot":"","sources":["../../src/cli/show.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,mBAAmB,CAAC;AAG/D,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAO,GACjF,OAAO,CAAC,MAAM,CAAC,CAuBjB"}
|
package/dist/cli/show.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { loadConfig } from '../core/config.js';
|
|
2
|
+
import { formatSpecDetails, getSpecDetails } from '../core/show.js';
|
|
3
|
+
export async function showCommand(specId, options = {}) {
|
|
4
|
+
const cwd = options.cwd || process.cwd();
|
|
5
|
+
const config = options.config || (await loadConfig(cwd));
|
|
6
|
+
if (!specId || !specId.trim()) {
|
|
7
|
+
console.error('Error: specify a spec ID to show (e.g. osq show 001)');
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const details = await getSpecDetails(cwd, specId, config);
|
|
12
|
+
const formatted = formatSpecDetails(details);
|
|
13
|
+
if (options.stdout) {
|
|
14
|
+
options.stdout(formatted);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
console.log(formatted);
|
|
18
|
+
}
|
|
19
|
+
return formatted;
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
23
|
+
console.error(`Show error: ${message}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=show.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"show.js","sourceRoot":"","sources":["../../src/cli/show.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEpE,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAc,EACd,UAAgF,EAAE;IAElF,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzD,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;QACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1D,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,CAAC,KAAK,CAAC,eAAe,OAAO,EAAE,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/cli/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,mBAAmB,CAAC;AAG/D,wBAAsB,aAAa,CACjC,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAO,GACjF,OAAO,CAAC,MAAM,CAAC,CAkBjB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { loadConfig } from '../core/config.js';
|
|
2
|
+
import { formatStatusOverview, getStatusOverview } from '../core/status.js';
|
|
3
|
+
export async function statusCommand(options = {}) {
|
|
4
|
+
const cwd = options.cwd || process.cwd();
|
|
5
|
+
const config = options.config || (await loadConfig(cwd));
|
|
6
|
+
try {
|
|
7
|
+
const overview = await getStatusOverview(cwd, config);
|
|
8
|
+
const formatted = formatStatusOverview(overview);
|
|
9
|
+
if (options.stdout) {
|
|
10
|
+
options.stdout(formatted);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
console.log(formatted);
|
|
14
|
+
}
|
|
15
|
+
return formatted;
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
19
|
+
console.error(`Status error: ${message}`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=status.js.map
|